Skip to content

diu-stop: a file citation buys silence only when it is backed - #784

Merged
mergify[bot] merged 2 commits into
mainfrom
stack/EdbertChan/reflect/subagent-decisions-slot-20260922/diu-stop-file-citation-buys-silence-only-backed--9ece412c
Sep 24, 2026
Merged

mergify[bot] merged 2 commits into
mainfrom
stack/EdbertChan/reflect/subagent-decisions-slot-20260922/diu-stop-file-citation-buys-silence-only-backed--9ece412c

Conversation

@EdbertChan

@EdbertChan EdbertChan commented Sep 22, 2026 •

Copy link
Copy Markdown
Owner

Summary

The gate that demands evidence for a claim goes quiet as soon as the paragraph contains anything shaped like a file and a line number.

It never checks that the file exists, that it was opened this session, or which version was read.

A made-up path silences it just as well as a real one.

A citation now only buys silence when it names the version it was read at, or the transcript shows that file being read this turn.

Review Claim

A file reference silences the evidence gate only when it is backed by a version or by a real read.

Review Lane

behavior

Review Unit

engine-runtime

Safety Invariant

When the transcript cannot be read, the gate does not go quiet: an unverifiable citation is treated as no citation, so the gate asks rather than assuming. This is the rule the repo already states in prose and enforced nowhere.

Slice Rationale

One condition in one gate, plus its fixtures. Independent of the other gate change in this stack, which is about telling a mention from a use.

Non-goals

Does not change which sentences the gate considers claims, and does not alter its message.

Test Plan

Test Plan

Run at this commit:

python3 engine/hooks/diu-stop/tests/test_hooks.py
Ran 87 tests in 1.204s
OK

Pinned cases: the claim alone blocks; the claim plus a real backed citation passes; the claim plus a fabricated path must block, which is what fails on main.

Revert Plan

Revert Plan

Revert this commit. Any file-shaped string silences the gate again, as on main.


Note

Medium Risk
Changes Stop-hook enforcement behavior for agents citing files; stricter blocking when transcripts are missing, but scoped to the unverified-claim path only.

Overview
The diu-stop unverified-claim gate no longer treats a bare path:line as evidence. A paragraph only skips the check when the citation includes a read ref (path:line @ origin/main), or the session transcript shows a tool call that named that path (parsed from transcript_path JSONL tool_use inputs).

If the transcript cannot be read, citations are unchecked rather than assumed valid: the block still fires and the message names the paths and asks for an @ <ref> citation. Block copy now states that bare file:line is not output, aligned with prove-it guidance.

README documents the three citation outcomes. Tests cover backed reads, fabricated paths, ref-only silence, unreadable transcripts, and Grep-shaped tool inputs.

Reviewed by Cursor Bugbot for commit 717a1f8. Bugbot is set up for automated code reviews on this repo. Configure here.

@EdbertChan

EdbertChan commented Sep 22, 2026 •

Copy link
Copy Markdown
Owner Author

This pull request is part of a Mergify stack:

# Pull Request Link
1 wrong-check-reflect: one shot per reply, and scope the reflect scan to the turn #796
2 reflect: catch an evidence-order correction from the ledger transition, not from wording #782
3 unverified-tag-ledger: gate the next-prompt reminder, default to stale only #783
4 diu-stop: a file citation buys silence only when it is backed #784 👈
5 diu-stop: the marker gate reads prose, so a mention is not a use #785
6 cat-mode: show the escape-hatch tag in the form the gate accepts #786
7 principle-subagent-inherits-scope: a brief carries decisions, not just facts and a question #788
8 llm-judge: claude is the only default runner #793

@EdbertChan

EdbertChan commented Sep 22, 2026 •

Copy link
Copy Markdown
Owner Author

Revision history

# Type Changes Reason Date
1 initial e409545 2026-09-22 18:38 UTC
2 rebase e409545 → d01a1ea (rebase only) 2026-09-22 18:38 UTC
3 content d01a1ea → 2166b85 (raw) 2026-09-23 14:06 UTC

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit d01a1ea. Configure here.

Comment thread engine/hooks/diu-stop/claude_stop_check.py
@EdbertChan
EdbertChan force-pushed the stack/EdbertChan/reflect/subagent-decisions-slot-20260922/diu-stop-file-citation-buys-silence-only-backed--9ece412c branch from d01a1ea to 2166b85 Compare September 23, 2026 14:06
@EdbertChan
EdbertChan force-pushed the stack/EdbertChan/reflect/subagent-decisions-slot-20260922/unverified-tag-ledger-gate-next-prompt-reminder--4f465761 branch from 4d9f2f3 to 1d1cad5 Compare September 23, 2026 14:06
…e only

The reminder was re-read to the user on every prompt for every outstanding
claim, including claims whose proof had already been pasted. There was no way
to turn it down: CATSTACK_HOOK_MODE_UNVERIFIED_TAG_LEDGER=off still printed
the full list at exit 0, because this hook is not on run_hook and nothing
reads the registry mode for it.

CATSTACK_UNVERIFIED_TAG_REMINDER is read in-hook through _flags/flags.py, the
one pattern proven to work here (wrong-check-reflect/detect.py:12-15, 191):

  stale  default, and what unset means: only claims already 3+ turns old
  all    the old behaviour
  off    no injection

Unset resolves to stale on purpose. A flag whose unset value is the old
behaviour changes nothing for the person who asked for less. reminder() at
:138 already computed that exact subset and threw it away at :145.

The gate is on the injection only. Emission and recording are untouched on
every setting, so `off` buys silence without emptying the ledger it is mined
from. A value nobody understands, and an .env candidate that cannot be read,
are both named on stderr instead of passing as "not set".

hooks.toml gains an enabled_by line naming the flag. Nothing reads that field
-- registry.py:15 is its only mention in the repo -- so it is documentation
beside the gate, not the gate.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Change-Id: I4f465761ee7f84489616ba1e83c54eb98ceefff0
@EdbertChan
EdbertChan force-pushed the stack/EdbertChan/reflect/subagent-decisions-slot-20260922/unverified-tag-ledger-gate-next-prompt-reminder--4f465761 branch from 1d1cad5 to ba95eda Compare September 24, 2026 08:02
FILE_LINE_RE matched a path and a line number and the paragraph loop skipped
the paragraph. Nothing checked that the file existed, that anyone had read it,
or at what ref. Pasting a made-up path beside the same claim turned the gate
off just as well:

  $ printf '%s' '{"last_assistant_message":"The problem was the old value at
    totally-made-up-file-that-does-not-exist.ts:99999.", ...}' \
      | python3 engine/hooks/diu-stop/claude_stop_check.py
    exit=0

A citation now counts when it names the ref it was read at
(`path:line @ origin/main`), or when the session transcript shows a tool call
that named that path. That is the rule corpus/CLAUDE.learned.md already ships
in prose and nothing enforced.

Third outcome, pinned: when the transcript cannot be read, the citation is
unchecked rather than clear. It does not buy silence, the reason goes to
stderr, and the block names the path it could not check.

tests/test_hooks.py:412 asserted the old behaviour. Its own PR, #479, listed
the file:line exemption under Non-goals -- it carried the exemption over so
fence normalization would not regress it, and did not decide that an unread
path should silence anything. The test keeps that intent and now backs its
citation with a read.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Change-Id: I9ece412c527ac3272bf86d31d8089db65fd685f5
@EdbertChan
EdbertChan force-pushed the stack/EdbertChan/reflect/subagent-decisions-slot-20260922/diu-stop-file-citation-buys-silence-only-backed--9ece412c branch from 2166b85 to 717a1f8 Compare September 24, 2026 08:29
@cursor

cursor Bot commented Sep 24, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_814cd18b-a5dd-4720-bef9-d00e0421862a)

@EdbertChan
EdbertChan changed the base branch from stack/EdbertChan/reflect/subagent-decisions-slot-20260922/unverified-tag-ledger-gate-next-prompt-reminder--4f465761 to main September 24, 2026 09:00
@mergify

mergify Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Queued — the merge queue status continues in this comment ↓.

@EdbertChan

Copy link
Copy Markdown
Owner Author

@Mergifyio queue

@mergify

mergify Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Merge Queue Status

This pull request spent 31 minutes 21 seconds in the queue, including 30 minutes 31 seconds running CI.

Required conditions to merge
  • check-success = lint
  • check-success = test
  • check-success = validate

@mergify mergify Bot added the queued label Sep 24, 2026
@mergify mergify Bot mentioned this pull request Sep 24, 2026
6 tasks done
@mergify
mergify Bot merged commit 99a36bd into main Sep 24, 2026
8 checks passed
@mergify mergify Bot removed the queued label Sep 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant