fix(chat): fire redaction notice on the 7 exception-path assistant persists - #8318
Conversation
Design Review (Fable 5) — ✅ PASSDesign-level review of Design-Verdict: PASS Root-cause fix: the notice tail is now coupled to the persisted artifact via one helper, not to Suggestions
[DESIGN-REVIEWED] 607351b |
Opus 4.8 Review — ✅ no blocking findingsReviewed Verdict parsed from the review's SHA-scoped output markers for commit False positive or not applicable? A repository writer can comment: |
First Principles Review (Fable 5) — 🟡 CONCERNSPremise-level review of All verification is done. The 7 exception-path sites and the First-Principles-Verdict: CONCERNS The fix is real and rightly shaped, but by its own harvested rule three redact-then-persist sites in the same file still opt out of the notice. What this change shipsIntent: a user whose turn ends in an error/cancel is now told when a credential in the salvaged reply was replaced — a FIX.
Watch
[FIRST-PRINCIPLES-REVIEWED] 607351b |
GPT 5.6 Review — ✅ no blocking findingsGPT 5.6 completed its review of This comment is updated in place on each push. Review detailsNo findings. False positive or not applicable? A repository writer can comment: |
Open PR relationship auditThis is a consolidated, point-in-time code-level audit note. It compares complete merge-base diffs and current/merged code; it does not treat a shared topic as duplication or partial coverage as completion. Relationship findings
No PR, Issue, label, branch, or review state was changed by the relationship-note portion of this audit. |
d9d1a78 to
97988ab
Compare
…rsists Extract the count-tags-and-append-notice tail of _flush_segment into a shared _append_redaction_notice helper and call it from all seven exception/teardown persists (CancelledError, AcpAuthRequired, AcpProcessDied, PromptBusyExhausted, AcpError transient, post-token transient recovery, terminal else) so all eight persists share one credential-redaction notice contract (issue #8311). The notice lands immediately after the assistant row and before any subsequent error/retry cards, mirroring _flush_segment. Redaction behavior is unchanged; scope stays credentials-only. Adds unit coverage for the helper and a driven AcpProcessDied turn that proves the notice now fires on an exception persist. Co-authored-by: Kiro Crew <noreply@kirodotdev.github.io>
97988ab to
607351b
Compare
Legitimate, and the evidence is in this same file. The docstring listed three passes that could have put a redaction tag into the persisted body, one of them the Root cause of the wrong clause: the sentence is the dashboard analogue of the same enumeration in The fix drops the wire pass from the list and says why it is excluded, rather than silently shortening the enumeration — an unexplained omission would read as an oversight to the next reader and invite the same clause back. Comment-only: no behaviour change, no call-site change, and the counting contract (read the tag in the persisted artifact, not the redactor's warning list) is untouched. Re-verified after the edit: black gate, isort, flake8, mypy |
Verified rather than taken on trust, and the count is right. I ran my own census of every Deferred rather than fixed here, for three reasons. First, it is a different surface: these are synchronous slash-command outputs, not the LLM The census also turned up something this finding does not cover and a mechanical sweep would have papered over. A fourth uncovered site, One thing #8435 must decide before any code, which is why it is a task and not a chore: the notice text says "Any command shown above will not work if you paste it as-is". That reads correctly under a salvaged assistant reply. Whether it reads correctly under a |
Agreed on the diagnosis, and it is stated accurately: extracting the tail makes the contract nameable in one place, but it does not make forgetting it impossible. That is why the The half I am putting to a maintainer rather than answering myself is the seam question, because it is a sequencing call and not a code judgment. Open PR #8291 adds the exfiltration-URL notice to this same file. Two orderings are available and they are not equivalent: Land this PR first and #8291 rebases onto I have deliberately not picked. This PR is self-contained and composes either way — it adds a helper and eight call sites and touches no URL-notice code, and the current head is rebased onto Nothing here blocks readiness: this review's verdict is advisory CONCERNS, and both halves are answered — the coupling is acknowledged and tracked, and the seam question is on the record for whoever merges. |
chenmingwei23
left a comment
There was a problem hiding this comment.
Approving: PR Readiness green (the repo's only required check), no failing lanes, MERGEABLE.
Fixes #8311.
Summary
The credential-redaction notice (#6189 / PR #8109) previously fired only on the
_flush_segmentpath. Seven exception/teardown branches insrc/kiro_crew/dashboard/chat_runner.pypersist the pasteable assistant body directly viaslot.append("assistant", …, "msg msg-a"), bypassing_flush_segment— so a segment finalized through one of those branches got its credentials redacted correctly but never told the user.This change extracts the count-tags-and-append-notice tail of
_flush_segmentinto a module-level helper_append_redaction_notice(slot, redacted)and calls it from all 8 assistant-body persist sites so they share one notice contract.Changes
_append_redaction_notice(slot, redacted): sums occurrences ofCREDENTIAL_REDACTION_TAGSin the exact persisted bytes and appends a singlemsg msg-infonotice row when non-zero. Counting is artifact-based (reads tags in the persisted text, not the redactor's warning list), so it composes with the per-chunk redaction already done on the streaming paths._flush_segmenttail replaced with a call to the helper (behavior unchanged; notice still lands after the assistant row and before the trailing stop card)._redactedlocal so the helper sees the exact persisted bytes; the 3_safesites reuse the existing local.Scope / divergence from the issue
At the current repo HEAD only the credential notice (#8109) has landed. The exfiltration-URL notice (#8291) referenced in the issue is not present in the file, so this fix propagates the existing credential-only notice and does not invent a URL notice. The issue's line numbers (from
bbbf77567) are stale; sites were located by pattern, confirming the count of 7. The URL-exfiltration notice remains the separately-tracked #8291 concern.Testing
python -m pytest test/test_chat_runner_coverage.py→ 277 passed (Python 3.12.13).black --check/isort --check-only/flake8clean on both changed files.test/test_chat_runner_coverage.py:TestAppendRedactionNotice(helper units: clean → no notice, single/multiple credentials, encoded-credential tag counted, no secret leak) andTestExceptionPathRedactionNotice(drives a real AcpProcessDied turn, asserts ordering assistant → notice → error card).TestExceptionPathRedactionNoticefail with "expected a redaction notice row", then restored.Sequencing note
The issue triage suggested sequencing this after open PR #8291 (URL notice, same file) merges to avoid a conflict. This change is self-contained (adds a helper + call sites) and should compose cleanly with a later URL-notice landing, but reviewers may want to reconcile ordering with #8291 before merging.
Pattern harvest
Rule candidate: semgrep
Pattern: a user-visible side effect is implemented inline in one persist path while sibling persist paths write the same artifact directly. Concretely: an
slot.append("assistant", ..., "msg msg-a")call inchat_runner.pythat is not followed by the shared_append_redaction_notice(...)call. The defect class is a cross-cutting notice/audit tail coupled to one function body instead of to the artifact it describes, so every later branch that persists that artifact silently opts out. Extracting the tail into a named helper is what makes the rule expressible at all -- before this change there was no single symbol a linter could require.