Skip to content

feat(triage-bot): let every flow capture debug-journal findings - #4949

Merged
springfall2008 merged 2 commits into
mainfrom
feat/triage-bot-journal-updates
Sep 6, 2026
Merged

feat(triage-bot): let every flow capture debug-journal findings#4949
springfall2008 merged 2 commits into
mainfrom
feat/triage-bot-journal-updates

Conversation

@springfall2008

Copy link
Copy Markdown
Owner

Why

The triage bot reads debug-journal.md before every investigation, but has never been able to maintain it. Sessions tried repeatedly across the last fortnight (#3078, #4804, #4840, #4845, #4931) and each reported the edit denied — but permissions were only half the story. sync_repo() runs git reset --hard origin/main and git clean -fd before every flow, so an edit inside the clone is destroyed before anything could pick it up. And no skill ever asked for a journal entry, so what upkeep happened was self-motivated and rare.

Capture

A queue directory outside the clone, writable by all five flows through one Edit grant in the shared base allowlist, plus JOURNAL_CAPTURE_PROMPT appended to every flow's system prompt. An appended system prompt is the only lever that reaches /code-review, whose SKILL.md this repo doesn't own — same reasoning as the existing GH_API_ENDPOINT_FIRST_PROMPT.

The prompt asks for what was verified and how, and explicitly asks for silence when a run learned nothing that generalises — an empty queue is the expected outcome for most runs.

Flush

Once a day, if anything is queued, /journal-update folds the queue in and opens a PR. It does two things:

  1. Re-checks each candidate against current main before accepting it — fold in, rewrite, or drop with a reason.
  2. Re-checks the existing journal against what has merged since.

The second half is the point. A file that only ever grows becomes wrong, and a wrong entry is worse than a missing one because every later run trusts it. Two entries were found stale this week; one asserted a credential leak that #4910 had already fixed, which would have had a triage run tell a reporter to rotate keys that never leaked.

Blast radius

The flush is the only flow that can both edit a file and push, so it's deliberately the narrowest:

  • Edits only the journal and the cspell dictionary, named by exact path — not the clone.
  • Cannot write the queue it reads (read access comes from --add-dir).
  • Cannot push outside bot/debug-journal-*, so it cannot push to main even though that's where the file lives.
  • Cannot merge its own PR. The human merge is the review gate.
  • Force-push denials match the PR flow's.

Consumed candidates move to processed/ rather than being deleted: no rm grant needed anywhere, a re-run can't double-count, and a dropped finding stays auditable.

Tests

36 new, 190 total, tools/test_triage_daemon.py (pre-commit gated). Cover the queue living outside the clone, capture reaching all five flows, the once-a-day gate, the permission set's carve-outs, and archive-on-success / keep-on-failure.

Two pre-existing tests asserted the appended prompt was exactly GH_API_ENDPOINT_FIRST_PROMPT; it's now that plus the capture text, so they assert containment. The property they protect — the #4758 gh-api steer still reaching review and cleanup — is unchanged and now covered directly too.

Worth a look before merging

Edit(<clone>/**) has been granted since 2026-08-24 yet those sessions still reported Edit denied, which points at ** not crossing the .claude path segment. This PR sidesteps it (the queue is elsewhere, and the journal rule is an exact path rather than a glob), but I have not proved that hypothesis — the first real flush run will confirm whether the exact-path Edit rule fires. If it doesn't, that's the thing to look at.

🤖 Generated with Claude Code

The bot has never been able to maintain the journal it reads before every
investigation. Sessions tried repeatedly and reported the edit denied; the
deeper reason is that sync_repo() runs `git reset --hard origin/main` and
`git clean -fd` before every flow, so an edit inside the clone is destroyed
before anything can pick it up. No skill asked for a journal entry either,
so what upkeep happened was self-motivated and rare.

Capture: a queue directory outside the clone, writable by all five flows
via a single Edit grant in the shared base allowlist, and
JOURNAL_CAPTURE_PROMPT appended to every flow's system prompt. The prompt
is the only lever that reaches /code-review, whose SKILL.md this repo does
not own. It asks for what was verified and how, and explicitly for silence
when a run learned nothing that generalises.

Flush: once a day, if anything is queued, /journal-update folds the queue
in and opens a PR. It re-checks each candidate against current main before
accepting it, and re-checks the existing journal against what has merged
since - a file that only ever grows becomes wrong, and a wrong entry is
worse than a missing one because every later run trusts it. Two entries
were found stale this week; one asserted a credential leak that had been
fixed, which would have had a triage run tell a reporter to rotate keys
that never leaked.

That flow is the only one that can both edit a file and push, so it has
the narrowest edit scope of any: the journal and the cspell dictionary,
named by exact path. It cannot write the queue it reads, cannot push to
any branch outside bot/debug-journal-*, and cannot merge its own PR - the
human merge is the review gate. Force-push denials match the PR flow's.

Consumed candidates are moved to processed/ rather than deleted, so no rm
grant is needed anywhere, a re-run cannot double-count, and a dropped
finding stays auditable.

Two pre-existing tests asserted the appended system prompt was exactly
GH_API_ENDPOINT_FIRST_PROMPT; it is now that plus the capture text, so
they assert containment instead. The property they protect - that the
#4758 gh-api steer still reaches review and cleanup - is unchanged and now
also covered directly.

36 new tests.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@springfall2008
springfall2008 marked this pull request as ready for review September 6, 2026 07:58
Copilot AI lite review requested due to automatic review settings September 6, 2026 07:58

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The new queue archiving logic can silently overwrite already-archived findings with the same filename, which undermines the stated auditability of processed/dropped entries.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR extends the triage-bot daemon to (1) append a system prompt that asks each flow to capture verified “debug journal” findings into a queue directory outside the repo clone, and (2) run a once-per-day /journal-update flow that folds queued entries into the debug journal via a PR with narrowly scoped edit/push permissions.

Changes:

  • Add a shared journal-queue/ (outside the clone) and append JOURNAL_CAPTURE_PROMPT to all flows so findings survive sync_repo() resets.
  • Add a daily gated flush_journal() path that runs /journal-update under a dedicated allow/deny set and archives consumed queue entries.
  • Add the new /journal-update skill plus expanded unit tests for queue capture/gating/permissions; update the debug journal instructions and cspell dictionary.
File summaries
File Description
tools/triage_daemon.py Adds queue + capture prompt, journal flush flow, and a dedicated permission set for /journal-update.
tools/test_triage_daemon.py Adds unit coverage for queue behavior, prompt propagation to all flows, daily flush gating, and permission carve-outs.
.claude/skills/journal-update/SKILL.md Introduces the /journal-update skill contract (verify candidates, re-check journal, run pre-commit, open PR).
.claude/skills/issue-triage/references/debug-journal.md Updates contributor guidance to write findings to the queue under the triage bot.
.cspell/custom-dictionary-workspace.txt Adds “oneline” to satisfy cspell in new docs/instructions.
Review details
  • Files reviewed: 5/5 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread tools/triage_daemon.py
Comment on lines +701 to +702
for entry in entries:
entry.replace(archive_dir / entry.name)
Comment thread tools/triage_daemon.py Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@springfall2008
springfall2008 merged commit ecbd879 into main Sep 6, 2026
2 checks passed
@springfall2008
springfall2008 deleted the feat/triage-bot-journal-updates branch September 6, 2026 08:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants