ForgeKitFORGEKIT

Build Log

Concurrency detection replaced with an atomic ownership claim

August 22, 2026

Heartbeat-freshness concurrency detection — which caused a real false positive within the first minutes of this session — was replaced with an atomic filesystem ownership claim serialized by a transition-lock mutex, hardened across five adversarial review passes into something the reviewer finally called boring enough to trust.

OSGatesConcurrencyHooks
5
adversarial review passes, each finding a real gap the previous pass missed
40
concurrency-gate tests, all passing, now wired into test:os
3
successive designs for release/force-release before landing on a real mutex

What shipped

.claude/hooks/lib/checkout-ownership.mjs (new — atomic-claim ownership model: acquireMainOwnership, releaseMainOwnership, forceReleaseMainOwnership, inspectMainOwnership, all serialized through a transition-lock mutex)

.claude/hooks/session-start.mjs, session-end.mjs (new — SessionEnd releases ownership if held; SessionStart deliberately does nothing in V1)

.claude/hooks/gate-dispatch.mjs (mutating-call classifier rewritten from a heartbeat/HEAD-drift check to an ownership-claim check; default-deny shell classifier covering Bash/PowerShell; shrunk read-only allowlist)

.claude/hooks/lib/concurrency.mjs (heartbeat code removed entirely — now HEAD-drift-only, defense-in-depth alongside ownership)

forgekit-os/scripts/checkout-ownership-cli.mjs (new — npm run checkout:status / checkout:force-release, the human-facing auditable force-release entry point)

forgekit-os/scripts/check-concurrent-session.mjs (rewritten into a pure ownership diagnostic — never creates a worktree or mutates the claim itself)

forgekit-os/scripts/retro.mjs (added a best-effort ownership-release step at the real session-close moment, not just on Claude Code's SessionEnd)

.claude/settings.json (PowerShell and NotebookEdit added to the PreToolUse matcher — previously unrestricted alternate writers)

.claude/hooks/tests/concurrency-gate.test.mjs (rewritten and extended to 40 real tests — acquire/block/release, default-deny shell classifier, shrunk allowlist, a real 12-process acquisition race, deterministic non-owner-release invariants)

.claude/hooks/tests/fixtures/ownership-race-child.mjs (new — the real multi-process race fixture)

package.json (wired concurrency-gate.test.mjs into test:os — it had never actually been included since its 2026-08-17 creation)

forgekit-os/scripts/hard-rule-baseline.json (line numbers re-synced after ignition-core.md's §1 ORIENT section was rewritten)

.claude/rules/concurrency.md, ignition-core.md (rewritten to describe the real mechanism and its full three/five-pass hardening history)

Both major redesigns this session followed the same arc: a clever fix for a real gap that, on closer adversarial inspection, turned out to open a narrower version of the same gap. The eventual fix in both cases was structurally simpler than the fix it replaced.

This session's own insights