Reported issues for thread-keeper
Pod holds 22 of 39 problems reported by people outside the maintainer team. Issues filed by the project's own owners, members and collaborators are excluded entirely — a maintainer's release checklist is not a warning to a prospective user.
Back to thread-keeper.
Most discussed
Recoverable lesson removal (soft-delete/tombstone) before curator destructive-by-default prunes unrecoverably
Problem
Lesson removal is irreversible, and the curator is moving to destructive-by-default, so a wrongly-pruned lesson is silently lost with no recovery path.
lesson_remove()(threadkeeper/tools/lessons.py:187) callsremove_lesson()(threadkeeper/lessons.py:135-152), which rewriteslessons.mdto physically drop the<!-- LESSON:BEGIN ... -->section.- The only trace left is an
eventsrow recording the slug + source, not the body (`threadkeeper/tools/lessons.py
Read the thread · 2026-06-17 · closed · 5 comments
Security: de-privilege & sanitize autonomous GitHub-writing daemons (bypassPermissions, prompt injection, body redaction)
Problem
thread-keeper's autonomous daemons can write to a public GitHub repo and run an unsandboxed code agent, and the only barrier protecting that surface is a single prompt-level instruction. Three coupled weaknesses:
- Output side — no mechanical redaction. The evolve reviewer (
threadkeeper/evolve_daemon.py:216-230) and the evolve appliers (threadkeeper/evolve_applier.pyroadmap-issue path ~:859, evolve-format path ~:1056) runpermission_mode="bypassPermissions"with `Bas
Read the thread · 2026-06-14 · closed · 4 comments
Cross-CLI ingest verification in production
Status
Cross-CLI ingest is wired (see `scripts/tk_verify_ingest.py` for the contract test) but has only been exercised on a single dev machine. Need real-world verification across all four CLIs.
What's needed
- Run `scripts/tk_verify_ingest.py` on a fresh machine that has been actively using Claude Code + Codex + Gemini + Copilot for at least a week
- Confirm `dialog_messages.source` ends up with rows from all four adapters
- Confirm `shadow_review_run --dry-run` sees cross-ad
Read the thread · 2026-05-14 · closed · 4 comments
Bug: CLI MCP/hook config writes are non-atomic full-file rewrites — a crash or concurrent write can truncate ~/.claude.json (entire Claude Code state)
Problem
Every adapter and the installer mutate CLI config with the pattern cfg = json.loads(path.read_text()) then path.write_text(json.dumps(cfg)) — a truncate-then-write with no temp-file + os.replace atomic swap (e.g. threadkeeper/adapters/claude_code.py:146,159; threadkeeper/adapters/gemini.py; threadkeeper/adapters/copilot.py; the hook helper; threadkeeper/_setup.py instruction writes).
Two failure modes:
- Crash/partial write → corruption.
write_texttruncates th
Read the thread · 2026-06-17 · closed · 3 comments
Add retention/GC for the tasks table and TASK_LOG_DIR spool files (unbounded growth + sensitive prompt/log persistence)
Problem
The tasks table and the TASK_LOG_DIR spool directory grow without bound — nothing ever prunes either.
- Every
spawn(...)inserts a permanent row intotasks(threadkeeper/db.pyschema:id, pid, parent_cid, spawned_cid, cwd, prompt TEXT NOT NULL, started_at, ended_at, return_code). There is noDELETE FROM tasksanywhere in the package, andconsolidate()only dedups notes/verbatim and demotes/releases threads (threadkeeper/tools/consolidate.py) — it never touches `tas
Read the thread · 2026-06-17 · closed · 3 comments
Curator destructive-by-default: pre-mutation snapshot/restore + structured prune/consolidate telemetry
Problem
THREADKEEPER_CURATOR_DESTRUCTIVE now defaults to 1 (CHANGELOG [Unreleased]; config.py curator_destructive: bool = True). Once the curator daemon is enabled, the spawned LLM child writes REPORT-<isodate>.md and then applies its own PRUNE / CONSOLIDATE / PATCH directly via lesson_remove and skill_manage(action='delete') — autonomously, no human review.
The destruction has no recovery path:
lessons.remove_lesson()(threadkeeper/lessons.py) is a plain in-place
Read the thread · 2026-06-17 · closed · 3 comments
Telemetry dashboard for shadow_review production validation
Goal
Once `MEMORY_PARTNER_SHADOW_REVIEW_INTERVAL_S` is enabled in production (15min default), we need visibility into:
- How often the daemon fires (interval ticks)
- How many ticks resulted in `no_window` / `too_short` / `spawned`
- How many spawned children produced a `MATERIALIZED:` outcome vs `SKIP:`
- Skill writes attributable to shadow_review (via `write_origin='shadow_review'` in skill_usage)
- Aggregate cost: total Claude-spawn time spent on shadow evaluation
Surfac
Read the thread · 2026-05-14 · closed · 3 comments
Lessons: add surgical lesson_patch and same-slug shadow edit exemption
Problem
lesson_append() only supports wholesale same-slug overwrite, and shadow-authored lessons still hit the word-count cap. That leaves older long lessons effectively frozen: a one-line correction forces a full re-transcription, but the long-body guard blocks the only existing in-place edit path.
Proposed direction
Add a surgical lesson_patch(slug, old_string, new_string) tool, mirroring skill_manage(action='patch'), and allow same-slug overwrites that do not grow the lesson body
Read the thread · 2026-06-20 · closed · 2 comments
Most recent
Security: split Curator web research from destructive memory mutation
Problem
A destructive Curator child is currently granted both untrusted web-research capabilities (WebSearch and WebFetch) and tools that patch or delete durable lessons, skills, and concepts. The prompt also requires external research for every skill. Snapshot recovery and per-pass mutation caps limit damage, but they do not prevent web content from steering mutations inside the same model context.
Issue #79 established this separation for the Evolve reviewer, but the equivalent Curato
Read the thread · 2026-08-24 · open · 0 comments
Port ThreadKeeper to MCP SDK 2.x before lifting the compatibility cap
Problem
The package currently pins mcp>=1.10.0,<2 because MCP SDK 2.0 removed the mcp.server.fastmcp import path and renamed FastMCP to MCPServer. Before the cap was added, fresh installs could resolve 2.0 and fail at import time.
The compatibility cap restores installations, but the codebase still imports the 1.x API directly in the MCP singleton, elicitation helper, and dialectic tool. The changelog explicitly says the 2.x port remains open, yet no roadmap issue tracks the migrati
Read the thread · 2026-08-10 · open · 0 comments
Security: untrusted GitHub comments can spoof Evolve applier claims
Problem
The Evolve applier treats any issue comment containing <!-- thread-keeper:evolve-applier-claim --> as an active claim for 24 hours. _issue_comment_is_active_claim checks only the marker and timestamp; _fetch_issue_comments does not preserve or validate the comment author's association or login.
Because the repository is public, an unrelated commenter can copy the visible marker into a comment on a trusted roadmap issue. The automatic applier then skips that issue until the cla
Read the thread · 2026-08-10 · open · 0 comments
Learning-loop skills: no load-time re-screen for loop-authored SKILL.md after write-time gate
Problem
Issue #76 added a write-time injection screen (review_prompts.screen_injection_markers, wired into lesson_append in threadkeeper/tools/lessons.py) so loop-synthesized memory can't carry imperative-override / remote-exec idioms at the moment it's created. That screen only runs once, at the lesson_append / skill_manage(create/patch) call site.
Once a SKILL.md is materialized under a loop-authored created_by_origin (shadow_review, candidate_review, background_review, etc.)
Read the thread · 2026-07-31 · open · outside contributor · 0 comments
Security: mechanically confine evolve web-research writes to the assigned digest
Problem
The evolve web-research phase is intentionally separated from the privileged audit phase, but it still receives a generic file-write capability. The single-digest write restriction exists only in prompt text, not as an enforced destination policy. Untrusted web content can therefore try to steer the research worker into altering repository or state files, creating an integrity and availability bridge into the later privileged audit.
Proposed direction
Replace generic research-phas
Read the thread · 2026-07-25 · open · 0 comments
Security: curator-report applier executes any REPORT-*.md as instructions with destructive memory tools — no provenance link to the curator pass that wrote it
Problem
apply_curator_report (threadkeeper/evolve_applier.py:1353) consumes a curator report selected purely by filesystem state: _latest_complete_curator_report globs CURATOR_REPORTS_DIR.glob("REPORT-*.md"), sorts by mtime, and accepts the newest file that contains the completeness marker CURATOR_PASS_COMPLETE (CURATOR_REPORT_MARKER, line 140) and is not yet recorded as applied (threadkeeper/evolve_applier.py:521-541). It then reads the report's full text, inlines it into the
Read the thread · 2026-06-17 · closed · 2 comments
Bug: memory/skill nudge counters inflated by daemon *_pass and render_brief's own *_hint_shown events — nudges fire early
Problem
nudges._count_events_since (threadkeeper/nudges.py:92-118) counts every events row for the session with id > since_id, excluding only _NONCOUNTING_KINDS = ("thread_hint_shown","shadow_review_pass") plus the caller's reset kinds. But background daemons write per-tick *_pass events under the foreground session id (identity._session_id):
ingest_pass— the live ingester ticks every few seconds; non-empty passes are always recorded (threadkeeper/ingest.py:48-65,
Read the thread · 2026-06-17 · closed · 2 comments
Security/data-loss: destructive curator's PROTECTED guarantee is prompt-only — foreground lessons never classified protected; skill_manage(delete) has no provenance/tier guard
Problem
The curator now runs destructive-by-default (THREADKEEPER_CURATOR_DESTRUCTIVE=1, shipped in e4c4dcf): after writing its REPORT, the spawned child autonomously applies PRUNE/CONSOLIDATE via lesson_remove and skill_manage(action='delete') (threadkeeper/curator.py:467-494). The prompt claims two safety nets, but neither is enforced at the server layer for the entries they are meant to protect.
Lessons — the protected class is unreachable. Both the [PROTECTED] in
Read the thread · 2026-06-17 · closed · 1 comment
Curator destructive-by-default: [PROTECTED] invariant is prompt-only for skill patch/delete & lesson force-remove — enforce server-side
Problem
Flipping the curator to destructive-by-default (THREADKEEPER_CURATOR_DESTRUCTIVE
now defaults to 1, branch merge/curator-to-main) hands the autonomous curator
child a write toolset (lesson_append, lesson_remove, skill_manage) under
permission_mode="auto". The curator marks user/pinned/foreground entries
[PROTECTED] in the inventory and the prompt says "NEVER touch [PROTECTED]" /
"NEVER pass force=True". But that contract is only partially backed by
server-side
Read the thread · 2026-06-17 · closed · 1 comment
Hot-config reload is Claude-Code-only: config_watcher hardcodes ~/.claude/settings.json, no-ops on Codex/Gemini/Antigravity/VS Code hosts
Problem
Hot-config reload (#2, shipped) lets the running MCP server pick up edited THREADKEEPER_* env knobs without a host restart. But threadkeeper/config_watcher.py resolves the watched file as a hardcoded ~/.claude/settings.json (_settings_path()), with only a single global THREADKEEPER_CONFIG_WATCH_PATH override.
thread-keeper ships adapters for several hosts, and each stores its MCP-server env in a different file and format:
- Claude Code —
~/.claude/settings.json(JSO
Read the thread · 2026-06-17 · closed · 1 comment
Dockerfile installs threadkeeper unpinned — non-reproducible Glama eval build
Problem
Dockerfile:13 installs the package unpinned:
RUN pip install --no-cache-dir threadkeeper
The image is the artifact Glama builds to compute the MCP Quality score (it
speaks MCP over stdio and inspects the tool schema — see the Dockerfile header
and glama.json). An unpinned install means every rebuild silently pulls
whatever threadkeeper (and transitive mcp / pydantic / pyyaml) is newest
on PyPI, so:
- The score is non-reproducible — a rebuild can e
Read the thread · 2026-06-17 · closed · 1 comment
Adapter MCP-config writes are non-atomic + lock-free — interrupted/concurrent write can corrupt the host CLI's whole config (e.g. ~/.claude.json)
Problem
Every CLI adapter writes the host CLI's own config file via a plain,
non-atomic write_text(json.dumps(...)) after a read-modify-write. Examples:
threadkeeper/adapters/claude_code.py:146,159threadkeeper/adapters/claude_desktop.py:97,113threadkeeper/adapters/gemini.py:120,133threadkeeper/adapters/copilot.py:139,154threadkeeper/adapters/vscode.py:112,128threadkeeper/adapters/codex.py:266,284,296threadkeeper/adapters/antigravity.py:105,121
Two fa
Read the thread · 2026-06-17 · closed · 1 comment
Privacy: selective erasure ("forget this session/topic") — no way to delete one conversation from dialog + derived stores short of rm-ing the whole DB
Problem
thread-keeper auto-ingests transcripts from all connected CLIs by default and
fans them out into many derived stores: dialog_messages (+ FTS5 + dialog_vec),
notes / notes_vec, the lessons.md store, dialectic observations/claims,
verbatim, materialized skills, and per-task log files under TASK_LOG_DIR.
The only documented way to remove sensitive content — e.g. a session where credentials, a client's confidential data, or a regretted exchange were discussed — is the RE
Read the thread · 2026-06-17 · closed · 1 comment
Candidate-reviewer 'max 2 new skills per pass' cap is prompt-only — enforce server-side in skill_manage
Problem
threadkeeper/candidate_reviewer.py:122-123 states the "max 2 new skills per pass" cap only in the LLM prompt, and skill_manage(action='create') is in the child's extra_allowed_tools (lines 368-376) with no per-pass create counter or server-side gate (no LIMIT check in tools/skills.py). The reviewer child's input is harvested user/agent dialog content, so it is prompt-injection-exposed; a confused or injected child can call create N times and pollute the auto-trigger skill
Read the thread · 2026-06-17 · closed · 1 comment
The remaining reports are on the project's issue tracker.