findings: a Stop gate on knowledge that would otherwise die with the session - #163
Merged
Conversation
…session A session routinely learns things worth more than the diff it shipped — a tool that reports something untrue, a rule contradicted by observed behaviour, a prerequisite nobody had verified. The only place those land today is prose in a closing message, and the next session does not read transcripts. stop-hook-git-check.sh already establishes the posture for unsaved code: refuse to stop, name what is outstanding. This is the same gate on unsaved knowledge. The hook cannot introspect, so the evidence is a file rather than a question. Prompting at every Stop would fire in sessions where nothing was learned, and check-session-scope.sh states that objection precisely: a block that fires when nothing is wrong teaches the reader to skim past the one that matters. So the session appends (findings.sh), the hook reads, and the repair stays the model's or the human's act — the same declaration/detector/repair triad. Fail directions are split deliberately. Found something ⇒ exit 2. Could not run (no jq, unreadable buffer) ⇒ exit 0, because a Stop gate that cannot be satisfied is a session that can never end. For the same reason a malformed line is dischargeable rather than terminal: it is counted as outstanding, so without that path one stray keystroke would trap the session. What this does not do is bound the RECORDING of findings, only their discharge. An empty buffer is not evidence nothing was learned. That residual is stated in the hook header rather than left to be inferred. Two negative tests exist because the property is the refusal, not the run: an open finding must exit 2, and a no-op discharge must not report success. Both caught real defects — jq -r's trailing newline made every discharge grow a blank line and made an out-of-range number look successful. Closes #162. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sxsnt4gA1dViEK64uFQJrY
…machine
The schema job enumerates its test files, and the previous commit did not add
this one. So CI went green on that push having never executed the suite —
including the two negative tests that are the only evidence the Stop gate
refuses at all.
org-defaults.yml already documents this exact failure mode ("a test file that is
never listed here runs only on the author's machine and green CI says nothing
about it — which is how this one shipped in the first place"). It caught a
second file the same way.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sxsnt4gA1dViEK64uFQJrY
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #162.
The gap
A session routinely learns things worth more than the diff it shipped: a tool that reports something untrue, a documented rule contradicted by observed behaviour, a prerequisite nobody had verified. The only place those land today is prose in a closing message, and the next session does not read transcripts.
This PR was itself motivated by two such findings —
.github-private#457 (a search qualifier returning silent false negatives, which nearly caused duplicate filings in the session that found it) and the unconfirmability of board placement from inside a session. Both existed only as sentences.The shape
stop-hook-git-check.shalready establishes the posture for unsaved code: refuse to stop, name what is outstanding. This is the same gate on unsaved knowledge.findings.jsonlWhy the evidence is a file, not a question
A hook cannot introspect. The obvious alternative — prompt at every Stop, "did you learn anything?" — fires in every session including the ones where nothing was learned, and
check-session-scope.shstates that objection precisely: "a block that fires when nothing is wrong teaches the reader to skim past the one that matters."So the session appends as it goes, the hook reads the buffer, and the repair stays irreducibly the model's or the human's act. Same declaration / detector / repair triad
check-session-scope.shdescribes in its own header.Fail directions, split deliberately
exit 2, block the stopjq, unreadable buffer) ⇒exit 0, fail openThe second is
check-session-scope.sh's rule, and it matters more here than there: a Stop gate that cannot be satisfied is a session that can never end. For the same reason a malformed line is dischargeable rather than terminal — the hook counts one as outstanding, so without that path a single stray keystroke would trap the session. Every message names an escape; "not worth keeping" is discharged with a reason instead of a URL.What this does NOT do
It bounds the discharge of recorded findings, not their recording. An empty buffer is not evidence that nothing was learned; it is evidence that nothing was written down. The honor-system core narrows from "remember to file it" to "remember to append it" — it is not eliminated.
That is stated in the hook header rather than left to be inferred, per hygiene rule 3. Related:
.github-private#456 is the same failure class one level up.Two negative tests, and what they caught
The property is the refusal, not the run — so the exit-2 paths are asserted on status codes. Both negative tests caught real defects rather than confirming a design:
jq -rappends a trailing newline thejoinhad already accounted for, so every discharge silently grew a blank line, and a no-op rewrite still differed — meaning an out-of-range entry number reported success.claude-box#248 is the cautionary case this follows: a&&guard that had never been observed failing closed turned out not to be one.Checks
node --test .claude/*.test.mjs— 146 pass, 0 fail (+15 new)node --test *.test.mjs— 70 pass, 0 failNot in scope
.github-private#427) — it currently defaults under$HOME/.claude/, which is per-container. Carry-forward capture: a Stop-hook gate on findings that would otherwise die with the session #162 tracks this as an open question..github's own.claude/settings.jsononly.🤖 Generated with Claude Code
https://claude.ai/code/session_01Sxsnt4gA1dViEK64uFQJrY
Generated by Claude Code