Skip to content

Coalesce four finalize hooks into mpl-finalize-gate (#257)#260

Merged
KyubumShin merged 3 commits into
mainfrom
feat/257-finalize-gate-batch
May 30, 2026
Merged

Coalesce four finalize hooks into mpl-finalize-gate (#257)#260
KyubumShin merged 3 commits into
mainfrom
feat/257-finalize-gate-batch

Conversation

@KyubumShin

Copy link
Copy Markdown
Owner

Summary

Closes #257.

Replaces four separately-registered PreToolUse hooks on finalize_done=true writes — mpl-require-e2e, mpl-require-e2e-authenticity, mpl-require-finalize-artifacts, mpl-require-whole-goal-closure — with one coalesced gate mpl-finalize-gate. The four delegates' files remain on disk and unchanged; only their hooks.json registration is removed.

What

  • hooks/mpl-finalize-gate.mjs (new): detects finalize_done=true writes, spawns each delegate as a subprocess, captures every block, clears the per-child envelopes that recorded along the way, and emits one envelope with retry_context.failures[] listing each originating validator's hookId, code, reason, resume_instruction, and retry_context. Per-validator ENFORCEMENT_DEFAULTS (warn/block/off) are honored — each child resolves its own rule action.
  • hooks/hooks.json: drop the four individual registrations, register the gate with a 15s timeout to cover four sequential children.
  • hooks/lib/mpl-config.mjs: new finalize_gate_failures: 'block' default; lets workspaces opt the whole gate off via .mpl/config.json without touching delegate config.
  • hooks/lib/mpl-recover.mjs: new inspectRecovery route for finalize_gate_failures — returns requires_user_action with the structured failures[] list. No auto-fix; user must resolve each entry before retrying.
  • skills/mpl-recover/SKILL.md: handler doc.
  • hooks/lib/mpl-hook-trace.mjs: PURPOSES + STATE_FOCUS entries for the gate.
  • docs/design.md: one row replacing the four, count updated 41 → 38.
  • Tests: new mpl-finalize-gate.test.mjs covers detection, summarization, multi-finding aggregation, child-envelope leak prevention, and the recover handler. Two existing hook-trace tests + one count-assertion test updated to reflect the registry change.

Why

Per docs/findings/2026-05-29-over-enforcement-audit.md and harness-evaluation follow-up: each of the four hooks individually verified a distinct property (scenario execution, scenario authenticity, declared-artifact presence, AC/AX coverage), but registering them separately produced a cascading-block UX — fix one, retry, hit the next. Same pattern PR #233 endorses for the relaxation work: wire to existing policy + surface better, don't add new constraint surface. No new check, just one envelope instead of four sequential ones.

Test plan

  • node --test hooks/__tests__/*.test.mjs → 1556/1556 pass
  • Multi-failure scenario produces single block envelope with ≥2 entries in retry_context.failures[]
  • Each failures[] entry preserves originating hookId + code + reason
  • state.blocked_by_hook === 'mpl-finalize-gate' after delegation — no leaked child envelope
  • inspectRecovery routes finalize_gate_failuresrequires_user_action with failures[]
  • Non-finalize writes pass through unchanged
  • Existing per-validator semantics preserved (verified via subprocess delegation of the original files)

🤖 Generated with Claude Code

KyubumShin and others added 3 commits May 30, 2026 15:33
The four finalize_done=true validators (mpl-require-e2e,
mpl-require-e2e-authenticity, mpl-require-finalize-artifacts,
mpl-require-whole-goal-closure) were registered as separate PreToolUse hooks
on Edit|Write|MultiEdit. Each blocked on its own violation, so resolving them
required a fix → retry → next-block → retry treadmill.

mpl-finalize-gate runs the four as delegated subprocesses, captures every
violation, and emits a single block envelope with retry_context.failures[]
preserving each originating validator's hookId, code, reason, and per-finding
retry_context. Per-validator ENFORCEMENT_DEFAULTS (warn/block/off) are honored
because each child resolves its own rule action independently.

The four hook files remain on disk and unchanged; only their hooks.json
registration is removed. A new ENFORCEMENT_DEFAULTS entry
(finalize_gate_failures: 'block') wires the gate through the standard
enforcement resolver so workspaces can opt the whole gate off via
.mpl/config.json without touching the delegate config.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Updates the discoverability layer so the new coalesced gate appears wherever
the registry-aware tooling expects it:

- hook-trace PURPOSES: label for mpl-finalize-gate.
- hook-trace STATE_FOCUS: include the gate because it reads state.json (via
  its delegates) and rewrites the blocked_hook envelope.
- docs/design.md Hook System table: one row replacing the four individual
  finalize-validator rows, plus the registered-hook count (41 → 38).
- hook-trace tests: state-category and file-category D3 tests now assert the
  gate replaces mpl-require-e2e and mpl-require-e2e-authenticity in traces.
- issue-251 runtime test: assert the new 38 count in design.md.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds an inspectRecovery handler for the new finalize_gate_failures block code
emitted by mpl-finalize-gate. The handler returns requires_user_action with
the structured retry_context.failures[] list — each entry carries the
originating validator's hookId, code, and reason, so the orchestrator can
present every failure at once without re-parsing block_reason.

There is no auto-fix path: the four delegate concerns (E2E execution, E2E
authenticity, declared artifacts, AC/AX closure) are independent and the
user must resolve each one before retrying the finalize_done=true write. The
gate re-runs every delegate on the next write, so partial fixes surface as a
smaller failures[] list on the next block envelope.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@KyubumShin

Copy link
Copy Markdown
Owner Author

Scheduled Hermes PR Review

Scope: lightweight diff review of PR #260 at 93b1040.

Findings

  • ✅ Looks Good — The PR cleanly replaces the four separate finalize validators in hooks/hooks.json with mpl-finalize-gate, while leaving the delegate validators in place and covered by subprocess delegation.
  • ✅ Looks Good — The new gate preserves each child finding's hookId, code, reason, resume_instruction, and retry_context in retry_context.failures[], which matches the intended non-cascading recovery UX.
  • ✅ Looks Good — finalize_gate_failures is wired into ENFORCEMENT_DEFAULTS and recovery inspection, so workspace-level warn/off behavior should still flow through emitBlockedHook.
  • ✅ Looks Good — Focused and full hook test runs passed locally: node --test hooks/__tests__/*.test.mjs reported 1556/1556 passing.

Notes / Caveats

  • This was a lightweight scheduled diff review with minimal contextual checks; I did not perform a manual end-to-end Claude hook runtime session.
  • I did not find high-confidence blocking issues in the reviewed diff.

Automated periodic review by Hermes Agent.

@KyubumShin KyubumShin merged commit 566014a into main May 30, 2026
@KyubumShin KyubumShin deleted the feat/257-finalize-gate-batch branch May 30, 2026 07:27
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.

[Over-enforcement audit] Finalize gate cascading-block — batch report 4 finalize hooks together

1 participant