Skip to content

feat(standards): one hook that reports after a commit - #18

Merged
yuema137 merged 2 commits into
mainfrom
feat/standards-preset
Sep 10, 2026
Merged

yuema137 merged 2 commits into
mainfrom
feat/standards-preset

Conversation

@yuema137

Copy link
Copy Markdown
Owner

What this is

The declared checks run automatically at one moment: PostToolUse after a direct git 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 Stop line.

Cut: the Stop handler, the pr hook 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 pr onto an event that does not mean pr. 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 explicit standards.py run at 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. subprocess terminates 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 commit at 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); --check PASS on all five checks.

Process group a stub spawns a grandchild that would outlive it; after the timeout its recorded pid is gone. Reverting to terminating only the child fails it.
Handler reports on a matching trigger; silent on a non-matching trigger, a chained command, another tool, a redirected cwd, a foreign cwd, and a malformed payload
Coexistence all three presets install as six groups with SessionStart once; removing any one leaves the others' events intact
Native, Codex six hooks parsed, postToolUse bound to standards.py at timeoutSec=60, the rest at 12

The 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 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.

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

yuema137 and others added 2 commits September 9, 2026 21:51
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
@yuema137
yuema137 merged commit 88860e6 into main Sep 10, 2026
4 checks passed
@yuema137
yuema137 deleted the feat/standards-preset branch September 10, 2026 02:08
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.

1 participant