feat(standards): one hook that reports after a commit - #18
Merged
Merged
Conversation
subprocess terminates the child it started and leaves that child's own children running. Under an explicit command an operator would notice an orphaned tree; under a hook, which is where this is heading, nothing would. The recorded debt from the previous step is paid before anything runs unattended. Each tool now starts in its own session, so a timeout can end the group rather than one process. end_group sends SIGTERM, waits a bounded grace period, then SIGKILL, and treats an already-gone group as done rather than as an error. The test builds the case that actually matters: a stub spawns a background grandchild that would outlive it, records its pid, and sleeps past the timeout. After execute returns, that pid is gone. A second stub traps SIGTERM and is still ended, within the grace budget instead of hanging. A third confirms an ordinary tool is unaffected by running in its own session. Mutation check: restoring the previous behaviour of terminating only the child fails the grandchild test. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016cCs5DdUjLDkP8V3Z5x6XH
The declared checks now run without anyone remembering to ask, at the one moment a host really offers: PostToolUse after a direct git commit. The commit already exists when the report arrives, which is the simpler behaviour the operator asked for over a blocking check the agent would have to retry around. Scope was cut after the freeze, on the operator's judgement that the plan was over-engineered for what this project is. The Stop handler, the pr hook trigger and the unchanged-inputs skip are gone. That skip existed only to suppress noise from an event firing more often than the trigger it stood in for; it was a patch on a patch, and the root cause was mapping pr onto an event that does not mean pr. trigger: "pr" now has no hook, and the documentation says why rather than leaving a reader to infer it. The registered budget is 60 seconds instead of 12, because a check is slower than a notice. Both hosts were measured to honour a larger value, and Codex reports timeoutSec=60 for this hook while the others stay at 12. The host is blocked while it runs, so the documentation says to keep the commit trigger to fast checks. The handler acts only on a recognized direct git commit at the exact repository root, reuses the recognition and cwd discipline the other presets already apply, and fails open: any error returns an empty result and a diagnostic on stderr. A hook that advises must never block the host it advises. The existing coexistence test assumed exactly two presets, in its pairing, its group count and its two-way event assertion. It is generalised rather than extended, so a fourth preset would not need it rewritten again. Not observed: a PostToolUse actually firing under an authenticated turn. Registration, parsing and the budget are measured; delivery of this specific event is recorded as unverified rather than claimed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016cCs5DdUjLDkP8V3Z5x6XH
This was referenced Sep 10, 2026
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.
What this is
The declared checks run automatically at one moment:
PostToolUseafter a directgit commit. The commit already exists when the report arrives. Nothing is blocked.Scope was cut after the freeze
The operator judged the plan over-engineered for what this project is — a lightweight prompt specification with a recommended hook. Re-reading it with that in mind, they were right about the
Stopline.Cut: the
Stophandler, theprhook trigger, and the unchanged-inputs skip.That skip existed only to suppress noise from an event that fires more often than the trigger it was standing in for. It was a patch on a patch, and its root cause was mapping
pronto an event that does not meanpr. Removing the mapping removed the need for the state.trigger: "pr"therefore has no hook, deliberately. Neither host has a PR-completed event. That granularity stays an explicitstandards.py runat review time, and the documentation says so rather than leaving a reader to infer it from behaviour.Five commits became two.
What is here
Process-group cleanup, the debt recorded in the previous step.
subprocessterminates the child and leaves its descendants running — under an explicit command an operator notices, under a hook nothing does. Each tool now runs in its own session and a timeout ends the group.The handler. Acts only on a recognized direct
git commitat the exact repository root, reusing the recognition and cwd discipline the other presets already apply. Fails open on any error: a hook that advises must never block the host it advises.The registration. A 60-second budget instead of 12, because a check is slower than a notice. Both hosts were measured to honour a larger value.
Validation
Ran 163 tests ... OK (skipped=1);--checkPASS on all five checks.SessionStartonce; removing any one leaves the others' events intactpostToolUsebound tostandards.pyattimeoutSec=60, the rest at 12The existing coexistence test assumed exactly two presets — in its pairing, its hardcoded group count and its two-way event assertion. It is generalised rather than extended, so a fourth preset will not need it rewritten again.
Not observed
A
PostToolUseactually firing under an authenticated turn. Registration, parsing and the budget are measured; delivery of this specific event is recorded as unverified rather than claimed.Still open
Whether Step 3's approval machinery should be reduced to reporting. It is the heaviest thing shipped so far, and it does not enforce who approves, which its shape implies. Deliberately not acted on here.
🤖 Generated with Claude Code
https://claude.ai/code/session_016cCs5DdUjLDkP8V3Z5x6XH