LearnNewsExamplesServices
Frontmatter
id13659
titleAdd a dedicated stopHookDecision.spec.mjs for the shared no-hold decision helper (#13657 follow-up)
stateClosed
labels
aitestingmodel-experience
assigneesneo-opus-grace
createdAtJun 20, 2026, 10:28 PM
updatedAtJun 20, 2026, 11:03 PM
githubUrlhttps://github.com/neomjs/neo/issues/13659
authorneo-opus-grace
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtJun 20, 2026, 11:03 PM

Add a dedicated stopHookDecision.spec.mjs for the shared no-hold decision helper (#13657 follow-up)

Closed v13.1.0/archive-v13-1-0-chunk-4 aitestingmodel-experience
neo-opus-grace
neo-opus-grace commented on Jun 20, 2026, 10:28 PM

Context

#13657 extracted the shared no-hold Stop-hook decision primitives into ai/scripts/lifecycle/stopHookDecision.mjs — now the cross-harness source-of-authority consumed by both the Claude (.claude/hooks/laneStateStopHook.mjs) and Codex (.codex/hooks/codex-lane-state-stop.mjs) hooks. My cross-family review of #13657 flagged this as a non-blocking follow-up.

The gap

The helper is the explicit anti-drift mechanism ("Claude and Codex cannot drift again") — yet it has no dedicated spec; it's covered only indirectly through the two hooks' specs. Its blockInjectionSupported:false + blockUnsupportedReason-suffix branches are exercised only via the Codex path. A future harness wiring the helper with blockInjectionSupported:false, or a refactor of either hook, could silently drift the shared layer with the hook-level tests still green. The shared primitive should be tested directly.

AC

  • A dedicated test/playwright/unit/hooks/stopHookDecision.spec.mjs exercises the three pure functions directly (no hook, no I/O):
    • parseOutcomeToVerdict: malformed / absent / valid / invalid-with-violations / invalid-no-violations.
    • isOperatorInLoop: stopHookActive forced-continuation, empty-prompt fail-closed, [WAKE] autonomous, genuine operator → true.
    • decideStopHookAction: operatorInLoop→allow; enforce+block-supported→block; enforce+block-unsupported(+suffix / bare)→would-block; dry-run→would-block; defaults→would-block.
  • Green under npm run test-unit.

Out of scope

  • Any behavior change to the helper or either hook (this is test-only hardening).

Refs #13657, #13655.