Skip to content

The fix lock: <state-dir>/fix_scope.json ​

The per-dispatch lock a nacharbeit fix pass holds while it edits other plugins' files. hooks/nacharbeit_guard.py reads it on every Write, Edit, MultiEdit and Bash call and is inert when it does not exist. It answers the one question a PreToolUse payload cannot: is this edit one the in-flight pass was given? (see docs/orchestration/references/hazards.md, "Why a hook cannot tell whose edit it is").

Shape ​

json
{
  "runStamp": "2026-09-09T05:12:52Z",
  "createdAt": "2026-09-09T05:12:52Z",
  "reviewRunStamp": "2026-09-08T17:57:42Z",
  "stateDir": "analysis/nacharbeit",
  "writeRoots": ["plugins/", "tools/"],
  "files": {
    "plugins/andon/skills/andon-loop/SKILL.md": "sonnet",
    "plugins/andon/references/okf-ledger-schema.md": "haiku",
    "plugins/zirkel/workflows/solve.js": "opus",
    "plugins/andon/skills/andon-preflight/SKILL.md": "human"
  }
}
fieldmeaning
runStampthe fix run this lock belongs to (also the fix-args.json stamp)
createdAtUTC time the lock was opened; nacharbeit-status calls a lock older than six hours stale
reviewRunStampthe review run whose findings the pass applies
stateDirwhere the pass keeps its state; writes under it are always allowed (the session must save the workflow's return value)
writeRootspath prefixes the pass may write under; the workflow refuses any item outside them
filesevery file the review touched, repo-relative, with its highest fix tier. haiku and sonnet entries may be edited; opus and human entries are listed deliberately so a denial can say "opus-tier, never auto-applied" instead of "unknown file"

Lifecycle ​

momentactoreffect
openscripts/build_fix_args.pyrefuses unless the guard is present and denies; writes the lock, snapshots each touched plugin to <state-dir>/pre-fix/<plugin>/, deletes any old heartbeat and fix-run.js; refuses when a lock is already open. Does not bake fix-run.js
confirmscripts/build_fix_args.py --confirm-guardbakes fix-run.js only if <state-dir>/guard-heartbeat.json names this lock's runStamp; otherwise deletes the lock and exits 1
heldhooks/nacharbeit_guard.pydenies edits outside files, edits to opus/human entries, edits with no determinable target, and git state changes
releasescripts/post_fix_check.py --release-lockdeletes the lock (and the heartbeat) only when every post-check passed and contract_diff found nothing lost or moved; --force overrides with a stated reason
stalenacharbeit-statusreports a lock older than six hours with the release command; the guard itself never expires a lock — a stale lock denies, which is the safe direction

The heartbeat: <state-dir>/guard-heartbeat.json ​

json
{ "lockRunStamp": "2026-09-09T05:12:52Z", "seenAt": "2026-09-09T05:13:01Z", "tool": "Bash" }

Written by hooks/nacharbeit_guard.py on every Write, Edit, MultiEdit or Bash call while a lock exists — allowed or denied — and never when the lock is absent or NACHARBEIT_DISABLE_GUARD=1. lockRunStamp binds it to one lock, so a heartbeat left by an earlier pass confirms nothing. It is the only evidence that the runtime registered the hook: the files being present is not. A failed write changes no decision; it only leaves the pass unconfirmed, which refuses to start.

What the lock is not ​

  • Not a pattern list: matching is exact path equality after normalisation, never a glob.
  • Not repo-level state: it exists only between build_fix_args.py and post_fix_check.py.
  • Not a substitute for the other guards: takt, lehre, andon, zeugnis, befund and cupertino keep arbitrating the same edits by their own markers, and a denial from one of them during a pass is reported as "blocked by that plugin", never bypassed.