ForgeKitFORGEKIT

Build Log

Gate Enforcement Architecture

July 17, 2026

Set out to build a Deploy Gate transcript layer; found and fixed a real fail-open bug in the mechanical gate system already policing every session

OSGatesSecurity
2
real bugs found in the existing gate system (fail-open on relative hook paths, self-referential tree-hash)
1
real vulnerability found + fixed in new code (marker-ordering spoof)
27
fixture/integration tests written, all passing
4
historical confirmed-bypass instances found in bounded audit
3
Crucible/Council rounds, each finding a real problem with the prior design

Timeline

Start
Charge: correct gate docs, extend hook architecture with judgment-gate ledger
Crucible round 1
Council rejects the pending-ledger design outright
Unproven detection precision would relocate, not fix, the marker-trust problem
Design pivot 1
Notes-keyword 'substantive validator' built, then caught as theater before shipping
Checked Claude's own prose, not a real fact
Investigation
Empirically verified PostToolUse does not fire on failing Bash commands
2 controlled tests with a temporary debug hook
Crucible round 2-3
Transcript-capture design proposed, found to have a context-binding hole, fixed with tree-hashing, re-approved
Build
Deploy Gate transcript layer built, tested, live-demonstrated
Real discovery
5 Whys traced a live test failure to MODULE_NOT_FOUND fail-open in the PRE-EXISTING gate-check.mjs
Relative hook paths break when Bash cd's into a subdirectory
Hardening
Zeb's review named 6 concrete gaps; all fixed, tested, and proven live
CLAUDE_PROJECT_DIR, fail-closed, SHA-256 tree-hash w/ untracked coverage, shell-free wrapper, nested-dir integration test, spoofing test
Audit
Bounded historical blast-radius audit found 4 real historical confirmed bypasses
1 prior session, low-severity content, corrected in that session's own JSON
Done
3-tier model documented, coverage table generated from config, Crucible interactivity rule hardened

What shipped

Deploy Gate transcript layer — a PostToolUse hook (gate-transcript-capture.mjs) captures real command exit codes for products/forge's `npm run gate:typecheck`, independent of anything Claude writes in a marker's notes field

Tree-hash binding (computeScopedTreeHash in marker.mjs) — SHA-256 over unstaged + staged + untracked file contents, so a transcript captured before a later edit is correctly treated as stale

Fail-closed boundary in gate-check.mjs — any unexpected crash now exits 2 (blocking) instead of silently letting the gated action through; proven with real injected-failure tests, not just review

Fixed 2 real bugs in the PRE-EXISTING gate system: relative hook command paths in settings.json (fail-open on subdirectory cwd, now uses ${CLAUDE_PROJECT_DIR}), and markerPath()/transcriptPath() resolving against process.cwd() instead of repo root

Fixed 1 real vulnerability found by writing an adversarial test: a spoofed echo could win over a real command's own marker in stdout if printed first — fixed by using the last match, not the first

27 fixture/integration tests across 3 test files (marker-transcript, fail-closed, real-hook-invocation) — a test harness that did not exist before this session

Generated gate-coverage table script (npm run gate:coverage) reading directly from gates.config.mjs — replaces the informal '13 gates' hand-count

3-tier enforcement model documented in triggers.md and gates.md — present-state-first, both historical bugs named with evidence

Bounded historical blast-radius audit — 225 fail-open events classified, 4 real historical confirmed bypasses found and corrected in the originating session's own JSON

Hardened ignition.md §1.6 Crucible interactivity rule after two live lapses this session (batching without Zeb's turn, presenting summary instead of raw output)

The session's biggest success is not the transcript hook. It is that your pressure transformed a plausible mechanism into one that discovered a defect in the system already policing it.

Zeb, mid-session review