crickets developer safety - alexherrero/crickets GitHub Wiki

Note

LAUNCHED (lifted 2026-06-24, AG Phase 3; originally approved 2026-06-23) Β· locked 2026-06-28 (final AG design sweep). child-design β€” the developer-safety capability (the control + safety layer: the recoverability gate, operator-control hooks, engineering-norm snippets + their enforcement). status: launched (lifted into tracked wiki/designs/ 2026-06-24, AG Phase 3). Points up at the crickets HLD.

developer-safety

Objective

developer-safety is the control + safety layer for an autonomous agent session β€” the recoverability gate that decides what proceeds vs. what stops, the operator-control hooks (halt, redirect, snapshot), and the engineering-norm snippets. It keeps the developer- prefix as a deliberate control-layer marker (the standing naming exception). It declares [recoverability].

Overview

Nobody runs developer-safety β€” it is ambient. There is no /safety command; the capability wraps every session from underneath, through three channels that engage on their own:

  • standing doctrine β€” the recoverability gate is always-load context the agent consults before any side-effecting action, and the same doctrine is inlined verbatim at the head of the phase commands (/work Β· /bugfix Β· /release);
  • always-on hooks β€” kill-switch and steer run on every tool call (acting only when the operator drops a signal file), and commit-on-stop fires automatically at every turn-end;
  • the mechanical floor β€” the host permission allowlist that deterministically gates a push regardless of the model's reasoning.

The operator touches it in exactly two ways: dropping a control file (.harness/STOP to halt, .harness/STEER.md to redirect) into a running session, or answering a confirmation prompt when an action the agent judged unrecoverable reaches the gate. Otherwise it is invisible.

It engages the phase loop one-directionally β€” developer-safety enhances development-lifecycle; the loop never requires or calls it. The doctrine reaches the phases by inlining; the hooks engage host-automatically when the plugin is installed and graceful-skip when it isn't (the loop runs unchanged, on the standing doctrine alone). All primitives are delivered.

Primitive Kind Engages What it does
recoverability skill standing doctrine The always-load classification β€” recoverable β†’ proceed announced; unrecoverable β†’ stop for confirmation. Inlined verbatim into /work Β· /bugfix Β· /release.
kill-switch hook Β· PreToolUse operator drops .harness/STOP Blocks the next tool call until the file is removed β€” the operator's out-of-band halt.
steer hook Β· UserPromptSubmit operator writes .harness/STEER.md Surfaces the file as additionalContext on the operator's next submitted prompt to redirect mid-run (no restart), then archives it.
commit-on-stop hook Β· Stop automatic β€” every dirty turn-end Snapshots the working tree to a hidden side ref refs/auto-save/<ts> β€” no work is ever lost.
commit-no-coauthor snippet composed by other plugins No Co-Authored-By agent trailer β€” the operator is the sole author of intent.
coauthor-guard hook Β· prepare-commit-msg automatic β€” every commit in every repo, once installed machine-wide Deterministically strips every agent Co-Authored-By trailer before the commit is written (human co-authors stay) β€” commit-no-coauthor made mechanical, on every host.
worktrees-operator-initiated snippet composed by other plugins Worktrees only on operator initiation β€” never an autonomous spawn.

The developer-safety control + safety layer as three gates over a norm layer: the control gate (operator drops STOP/STEER β†’ kill-switch / steer hooks), the judgment gate (every outward action passes a recoverability gate β€” recoverable proceeds announced, unrecoverable stops for operator confirmation, backstopped by the settings allowlist), and the preservation gate (turn-end commit-on-stop auto-snapshots a dirty tree to refs/auto-save); ambient and never invoked

The operator's only manual touch-points are dropping a STOP / STEER file (halt / redirect) and answering a confirmation when an unrecoverable action hits the gate. Everything else is ambient β€” the judgment gate runs on every outward action (backstopped mechanically by the settings allowlist), and commit-on-stop snapshots a dirty tree automatically at every turn-end.

The safety gates, in principle

An autonomous session runs without a human watching each action. Three of the things a human-in-the-loop would otherwise catch each need a gate β€” and developer-safety is those three gates plus the engineering norms that ride underneath them.

  1. The judgment gate β€” before an action that can't be taken back, stop. An agent left to run will eventually reach for something irreversible; the gate is the discipline of recognizing that case before acting and pre-announcing it for a human.
  2. The control gate β€” the operator must be able to halt or redirect a running session right now, out of band, without waiting for it to finish or restarting it. Autonomy is only safe if it can be interrupted.
  3. The preservation gate β€” if the session stops, crashes, or is halted, no uncommitted work evaporates. The cost of stopping has to be near zero, or the operator won't stop it.

The central invariant β€” recoverability is reversibility, not blast-radius. The judgment gate turns on one question: can this be undone? A large-blast-radius but reversible action (a force-push to your own un-shared branch, deleting a still-reachable branch) proceeds, announced; a tiny but irreversible one (a force-push that rewrites published shared history, a sole-ref delete of unmerged work, an immutable deploy) stops and pre-announces. When uncertain, it is treated as unrecoverable. This is what makes autonomous-by-default safe: the agent runs freely on everything it can undo, and only the genuinely-irreversible stops it.

The gates, mapped to the parts

The judgment gate β†’ the recoverability doctrine + the mechanical floor. The doctrine is a standing always-load skill β€” no command, no trigger; it governs every session β€” that classifies each side-effecting action recoverable Β· unrecoverable Β· unresolved-decision. The same text is inlined byte-identically at the head of /work, /bugfix, and /release (a drift test enforces the byte-identity), so inside the loop it is surfaced before every outward action and at each task's safety pre-check. The doctrine is the judgment; the host permission allowlist is the mechanical floor underneath it. Recoverable push refspecs auto-proceed; force / delete / tag-overwrite refspecs route through a confirmation prompt β€” deterministically, regardless of the model's reasoning. The allowlist can't read intent (a force-push to a private branch and one to shared history match the same pattern), so it gates the whole destructive family conservatively and lets the prose draw the line. Prose decides; the floor can't be bypassed.

The control gate β†’ kill-switch + steer. The two hooks act on different events, each only on an operator signal. kill-switch is a PreToolUse hook: the operator drops a .harness/STOP file and the very next tool call is blocked (the hook exits non-zero) until the file is removed β€” a halt with no restart, regardless of what the operator types. steer is a UserPromptSubmit hook: the operator writes .harness/STEER.md, and on the operator's next submitted prompt its contents are injected into the agent's context as additionalContext to redirect the upcoming action, after which the file is archived to an audit trail β€” a redirect, never a halt, that waits for the next prompt rather than pre-empting the next tool call. The two hooks no longer share an event, so there is no install-order dependency between them.

The preservation gate β†’ commit-on-stop. A Stop hook that fires at every turn-end, fully automatic, self-gating on a dirty tree. When there is uncommitted work it snapshots the full working state (tracked + staged + untracked) to a hidden side ref refs/auto-save/<ts> β€” without touching HEAD, the index, or the working tree, and without ever pushing β€” then prints the recovery command. Stopping costs nothing.

The norm layer β†’ the two snippets, one of them deterministically enforced. commit-no-coauthor (no Co-Authored-By agent trailer β€” the operator is the sole author of intent) and worktrees-operator-initiated (a worktree is only ever an operator act, never an autonomous spawn). Both are emitted as snippets and inlined into the phase commands that compose them β€” but commit-no-coauthor does not stop at doctrine: a prepare-commit-msg git hook (coauthor-guard) deterministically strips any agent Co-Authored-By trailer before the commit is written, so the norm holds even when the agent forgets or a host injects the trailer on its own. That gives the norm the same belt-and-suspenders shape as the judgment gate (doctrine over a mechanical floor) and the same deterministic git-layer enforcement as privacy's pre-push guard. It strips rather than blocks β€” the trailer simply never lands, so the autonomous loop is never interrupted. worktrees-operator-initiated stays doctrine-only: it is an authority / intent rule no hook can mechanize. developer-safety owns both standards; conventions and the phase loop cite them. (The coauthor-guard hook is as-built β€” shipped in Wave C via PLAN-wave-c-design-and-conventions / PR #160: src/developer-safety/hooks/coauthor-guard/ carries the prepare-commit-msg strip on both .sh and .ps1.) It installs once per machine, not per repo: install-global.sh points git's global core.hooksPath at a crickets-managed directory whose dispatcher runs the strip on every commit and hands every other hook on to the repo's own .git/hooks. The dispatcher also stands in for Git LFS: git-lfs installs its hooks into the directory core.hooksPath names and cannot get past the dispatcher there, so a repo that uses LFS and has no hook of its own gets git-lfs's four hooks from the dispatcher. A commit made off the machine (a cloud session, a collaborator, a server-side merge) is out of its reach.

Host portability

The gates are not uniform across hosts. commit-on-stop is a pure side-effect, so it is effective on both Claude Code and Antigravity β€” as is coauthor-guard, which runs in git itself and so holds on every host (it even catches a host that injects the trailer automatically). kill-switch depends on the host honoring a hook's exit code β€” which Antigravity does not β€” so there it fires but cannot halt; it ships as a no-harm, Claude-Code-effective primitive, with an always-on manual rule as the Antigravity fallback. steer doesn't even get that far: Antigravity has no UserPromptSubmit-equivalent event, so the generator skips registering it there entirely, and the hook never fires at all (previously, on the old PreToolUse mechanism, it fired but couldn't inject β€” the net effect for the Antigravity operator is unchanged, only the reason changed). The recoverability doctrine, being standing context, is host-portable.

The boundary

developer-safety is orthogonal to privacy β€” a control layer and a data-protection layer are different concerns; pii was deliberately not folded here. The recoverability doctrine names privacy's pre-push PII guard as a mandatory carve-out, but it does not own it (the same goes for worktree-spawn authority and the PR-merge-on-green step that closes a plan β€” carve-outs the doctrine names but other plugins own).

Opinions

developer-safety doesn't consume an opinion so much as provide one of the standards the others rest on: the recoverable-by-default autonomy doctrine is part of what how-we-engineer means. The arrow is one-way β€” how-we-engineer and the development-lifecycle's finalize step cite this capability's gate; it cites nothing above it.

Dependencies

  • standalone β€” requires: []; ships alone.
  • enhances development-lifecycle β€” one-directionally (the loop never requires it): the recoverability doctrine is inlined verbatim at the head of /work Β· /bugfix Β· /release (byte-identity drift-tested), and its hooks engage automatically when the plugin is installed, graceful-skip when not.
  • cited by conventions β€” the commit-no-coauthor + worktree-authority + recoverability standards live here; conventions cites them.
  • Points up at the crickets HLD; the requires/enhances mechanics are in crickets-composition.

Risks & open questions

  • All delivered, including the coauthor-guard enforcement hook β€” the capability name is settled (recoverability, declared v0.3.1); the deterministic commit-no-coauthor strip (a prepare-commit-msg git hook) shipped in Wave C (PR #160). It strips rather than blocks, so a silent strip never surfaces why a trailer appeared β€” acceptable, since the doctrine's stance is "strip it" regardless of cause.
  • Cite-don't-duplicate β€” the snippets are the source of truth for their conventions; conventions cites, never re-owns them.
  • Re-audit triggers: reconsider strip-vs-block if a silent coauthor-guard strip ever needs to be visible; keep the recoverability doctrine in sync with the global push-and-confirmation policy (~/.claude/CLAUDE.md); confirm conventions cites rather than copies the snippets at the conventions lift.

References

  • crickets src/developer-safety/ β€” hooks (kill-switch, steer, commit-on-stop, coauthor-guard Β· .sh + .ps1) Β· recoverability skill Β· snippets (commit-no-coauthor, worktrees-operator-initiated); declares [recoverability]
  • Doctrine: ~/.claude/CLAUDE.md (push + worktree policy; worktrees-operator-initiated)
  • Up: crickets HLD Β· composition Β· conventions (cites the snippets)

2026-10-03 β€” the dispatcher runs Git LFS's hooks (0.6.4). git-lfs installs its hooks into the directory core.hooksPath names. It found the dispatcher there and wrote nothing, so an LFS repo cloned or created on the machine pushed its pointer files, left the objects in the local clone, and exited 0. The dispatcher now runs git lfs <hook> for git-lfs's four hooks (pre-push, post-checkout, post-commit, post-merge) when the repo has no hook of its own under that name and uses LFS. A repo uses LFS when its LFS store holds an object (a glob under lfs/objects/), which git-lfs writes only when it stores or downloads a file. pre-push also counts a root .gitattributes naming filter=lfs, so a clone that stored nothing still refuses to push LFS history it lacks, and a configured lfs.storage. A repo's own pre-push that never mentions git-lfs is warned about on each push. The dispatcher's first lines, which git-lfs prints when it refuses, say never to run git lfs update --force or git lfs install --force: either overwrites four dispatcher copies for every repo on the machine. A dispatcher-version number lets the SessionStart check warn when the install is older than the plugin, since a plugin update never touches the copies. Why not a command that writes the hooks into a repo's .git/hooks: the failure is silent, so a fix someone has to remember per clone leaves every fresh clone broken until they do. Why not the presence of lfs/, or any local lfs.* key: read-only queries (git lfs env, ls-files, status) leave both, and git-lfs would then run on every commit in a repo with no LFS content and block its pushes wherever git-lfs is off PATH. Why not run git-lfs in every repo: about 50ms a call, on every commit. Re-audit triggers: git-lfs changes where it installs hooks or what it stores where, or installs a fifth hook; a second tool needs hooks it cannot install past the dispatcher; a repo with its own pre-push is found pushing without its objects despite the warning.

2026-10-02 β€” a third review round (0.6.3). Include files are now scanned without evaluating conditions (recursively, depth 10), and such values block only a first install, never a refresh or a still-named uninstall. The pwsh installer's environment restore no longer turns GIT_DIR into an empty string. Why not evaluate conditions: a gitdir: condition depends on the repo, which an installer run from outside every repo can't know. Re-audit trigger: a refusal the operator wants to override.

2026-10-02 β€” a second review round (0.6.2). [includeIf]-set hook paths now block the install, --check checks against a reference copy kept in the install (an older plugin copy no longer reads a newer install as stale), and a deleted hooks directory under a symlinked ~/.config can be reinstalled. Re-audit trigger: the operator wants the guard in repos a conditional include points elsewhere.

2026-10-02 β€” the machine-wide install, hardened before release (0.6.1). An adversarial review of 0.6.0 found the dispatcher made git slow (a shell per reference-transaction / post-index-change call: a 40-commit rebase took 8s), so those two hooks are no longer installed and a repo's own versions of them stop running while the install is active. The installer now reads core.hooksPath through system, global and [include]d config, --check compares file contents, and uninstall never strands git on a deleted directory. The strip also runs on applypatch-msg (git am, rebase --apply) and matches product names in the name only. Why not keep the two hooks with a faster stub: even a builtin-only shell costs a process per call, hundreds per rebase or fetch. Re-audit trigger: a repo the operator uses needs its own reference-transaction or post-index-change hook.

2026-10-01 β€” coauthor-guard installs machine-wide and strips agents only. The hook had no installer, so it ran only where someone had copied it into .git/hooks: on the operator's machine, 2 repos of 16, while the global floor was prose plus a host setting. install-global.{sh,ps1} now sets the global core.hooksPath to ~/.config/crickets/git-hooks/, filled with a POSIX-sh dispatcher under every hook name; it runs the repo's own hook (args, stdin, exit code intact) and, for prepare-commit-msg, the strip after it. A read-only SessionStart check warns if the install breaks. By the operator's call the strip now removes only trailers naming an AI agent (vendor domain, [bot], product name), so a human pair's trailer survives. Why not the alternatives: init.templateDir covers new clones only and freezes a copy; a SessionStart installer is Claude-only and reaches a repo only after a first visit; a backfill misses every future repo; commit-msg is skipped by --no-verify, which prepare-commit-msg is not. The PII pre-push stays per-repo: its scanner is tuned for public repos and would block private ones. Re-audit triggers: a hook manager the operator adopts needs .git/hooks (pre-commit, git-lfs) or sets its own core.hooksPath (husky); an agent trailer is found that the pattern missed; the PII scan is wanted globally.

2026-07-07 — coauthor-guard flipped designed→as-built (G12 AG close-out sweep). The prepare-commit-msg strip hook shipped in Wave C (src/developer-safety/hooks/coauthor-guard/{coauthor-guard.sh,coauthor-guard.ps1,hook.md}, PLAN-wave-c-design-and-conventions / PR #160), but this design's body still marked it [PENDING-IMPL] in three places — the norm-layer parenthetical, the "all delivered except" Risks bullet, and the References "to build" note. That plan reconciled crickets-design + crickets-conventions but left developer-safety's own markers stale. Flipped all three to as-built. The strip-rather-than-block behavior and the host-portability note (it runs in git itself, so it holds on every host, even one that injects the trailer) are unchanged — they were already accurate. Re-audit trigger: reconsider strip-vs-block only if a silent strip ever needs to surface why a trailer appeared.

2026-07-06 β€” carve-out language updated to name the authority pair directly, now that the commands it used to cite are gone (PLAN-worktree-native-flow task 8, docs sweep). The worktrees-operator-initiated carve-out previously named the explicit commands /spawn-worker and /integrate-worker as the two authority paths; both commands are deleted outright (not migrated). The carve-out now names the authority pair itself, independent of any specific command: an explicit operator instruction to spawn a worktree, or the durable isolation.mode: worktree-per-plan config opt-in in .harness/project.json (ADR 0028's channel). Auto-spawn, when authorized, now calls the host's own worktree primitive (Claude Code EnterWorktree; Antigravity New-Worktree-Mode / invoke_subagent) directly rather than a bespoke script, and a plan's close-out is a PR opened by finalize_unit.py with auto-merge armed, not an operator-invoked integrate step. This is a wording reconciliation only β€” the authority doctrine and the auto-merge-on-green call are already recorded in the entry below; this entry closes the stale command-name references the body carried past that amendment.

2026-07-06 β€” worktree-native flow: the worktrees-operator-initiated and /integrate-worker carve-outs retire, for agentm + crickets only (operator-ratified 2026-07-06; ROADMAP-MASTER Β§ Ratified 2026-07-06; adjudicated via the W1 verdict memo + Fable judgment pass).

Carve-out retired: worktree-spawn no longer requires per-invocation operator authority in agentm + crickets. The recoverability skill's carve-out β€” "a worktree spawn is not a recoverable action the agent can self-authorize" β€” is superseded, for these two repos only, by the standing isolation.mode: worktree-per-plan config opt-in (ADR 0028's own authority channel, now actually exercised via a local .harness/project.json). Every other repo keeps the carve-out as written; the plugin's own default is unchanged. Replacement contract: no bespoke spawn-announcement β€” both hosts' own UI already surfaces child-session/worktree creation, so building one would duplicate an existing signal. No concurrent-worktree ceiling for now β€” deferred pending evidence, not rejected. What does replace the carve-out: an orphan-worktree lifecycle β€” a periodic sidecar (cadence: a few days, running on the scheduler Wave B ships rather than growing its own) that finds worktrees whose branch never merged and whose session is gone and surfaces/reclaims them, extended per the Fable judgment pass to stalled-PR shepherding (gh pr update-branch on an armed PR left behind when a sibling plan's PR merges first, loud surfacing of conflicts) β€” plus a soft, non-blocking doctrine snippet in developer-workflows' standard /setup scaffold reminding a session to close out its own worktree. This is the direct answer to the adoring-vaughan stranded-fix failure mode. Build lands in PLAN-worktree-native-flow, including a spike on GitHub's native merge queue as a cleaner replacement for the shepherd if available on these repos.

/integrate-worker's carve-out retires with it. Replacement: auto-merge-on-green. Once the PR's required checks (check-all.sh on the merge commit, branch protection) pass, the PR merges with no operator invocation β€” armed explicitly via gh pr merge --auto at PR-open time (a PLAN-worktree-native-flow build item, not automatic from the repo setting alone), with "Allow auto-merge" enabled once per repo. This is a named, deliberate loosening of the recoverability doctrine's usual posture β€” a merge to main is less trivially undone than a push, and the operator has explicitly traded that reduced recoverability for a hands-off loop. The doctrine's central invariant (reversibility, not blast-radius, decides what stops) is unchanged; what moved is where "proceeds, announced" ends, one step further down the pipeline than before. The global ~/.claude/CLAUDE.md push-and-confirmation doctrine gains a matching line: a required-checks-green PR merge is a "recoverable β†’ announce + proceed" action, alongside the existing push examples.

Unchanged: the PII pre-push carve-out and the no-Co-Authored-By carve-out are untouched by this amendment β€” they are orthogonal to worktree mechanics. Why not go further (drop the carve-outs everywhere, not just agentm/crickets): the plugin serves installs with no config opt-in and no host worktree maturity to lean on; the doctrine's default has to stay conservative for those, which is exactly what "authority = explicit command OR durable config opt-in" already expresses β€” this amendment exercises a path the doctrine already named rather than rewriting it. Re-audit triggers: the orphan/shepherd sidecar is ever found insufficient; a stalled-PR is discovered that the shepherd didn't catch; GitHub's native merge queue becomes available on these repos (supersedes the hand-rolled shepherd); either carve-out's retirement is ever extended beyond agentm + crickets.

2026-07-05 — steer migrated PreToolUse→UserPromptSubmit. Decision: steer now fires on UserPromptSubmit and emits additionalContext JSON, Claude Code's real injection contract, because a live-verify with headless claude -p sessions proved the original PreToolUse+stdout mechanism never actually delivered injected content to the model (the assumption was false since the hook's original release, not a regression). Why not keep PreToolUse: there is no host-side stdout-into-context contract on that event; UserPromptSubmit is the only event Claude Code actually wires to additionalContext. Consequence: steer decouples from kill-switch's event (no more install-order dependency); on Antigravity the generator now skips registering UserPromptSubmit for steer entirely (previously it fired there uselessly; behavior is unchanged for the operator, only the reason changed). Re-audit trigger: if Claude Code's UserPromptSubmit injection contract or event surface changes again. See src/developer-safety/hooks/steer/hook.md's own "Mechanism history" section for the full reasoning.

2026-06-28 β€” lock-down sweep (operator review). Converted the three-gates mermaid to a house-style hand-SVG (diagrams/crickets-developer-safety.svg). Confirmed the three gates (judgment Β· control Β· preservation) over the norm layer, the ambient never-invoked stance, and the central invariant β€” recoverability is reversibility, not blast-radius. The folded ADR 0003/0028/0022 records (0009 re-homed to code-review) and the newest-first log are unchanged. Locked as a v5–v8 guidepost.

2026-06-24 β€” folded ADRs 0003 / 0028 / 0022 into this design (AG Phase 4, move-and-retire); ADR 0009 re-homed to code-review (AG settle-sweep, 2026-06-25).

0003 β€” Base operator-control hooks: kill-switch, steer, commit-on-stop (2026-05-14; amended 2026-05-30). Ship three Claude-Code-only hooks per-repo. commit-on-stop originally branched to a safety branch; amended to write a hidden refs/auto-save/<ts> ref via git commit-tree β€” never touching HEAD, index, or working tree; prunes to 10 snapshots. Why not keep the branch design: working-tree mutation and branch switching are inherent to stash+checkout; the redesign had to drop checkout entirely. Hidden refs (not refs/heads/) keep snapshots invisible to git branch. git stash create rejected for awkward tree-shape and untracked-file handling; git commit rejected because it honors commit.gpgsign and hangs the non-interactive hook. Re-audit triggers: commit-tree/update-ref/GIT_INDEX_FILE semantics change; Claude Code changes hook-event registration or PreToolUse exit 2 semantics; Stop event fires after tree is wiped; refs/auto-save/ gains other writers.

0028 β€” Worktree authority broadened: config opt-in is operator authority (2026-06-14; partially supersedes 0022). isolation.mode: worktree-per-plan in .harness/project.json counts as operator authority for auto-spawn, equal to an explicit /spawn-worker. Prohibition restated: "Never spawn without operator authority" (authority = explicit command OR durable config opt-in). Why not keep explicit-command-only: defeats the worktree-per-plan design β€” requiring /spawn-worker every time defeats the one-time-config intent. An additional per-invocation flag was rejected as equivalent-cost to a command; a durable config opt-in expressing the same intent is sufficient authority. Re-audit triggers: any agent or installer ever sets isolation.mode without explicit operator instruction; any code path calls spawn_worker.py outside the two authorized paths.

0022 β€” Retire worktrees-never-auto: worktrees first-class but operator-initiated (2026-06-13; partially superseded by 0028). Rename worktrees-never-auto β†’ worktrees-operator-initiated and reframe: worktrees are first-class (coordinator creates them via /spawn-worker); autonomous creation stays prohibited. Why not delete the rule outright: would license autonomous spawn. Why not keep with an exception list: brittle β€” every future sanctioned workflow needs a carve-out; reframing on the initiation axis covers all future operator-initiated workflows without enumeration. Re-audit trigger: any drift toward autonomous worktree creation.

2026-06-23 β€” authored, reviewed, and finalized. Authored from the seeded stub and grounded against the live wiring (crickets src/developer-safety + the agentm phase loop + global config). developer-safety is the control + safety layer for an autonomous session, framed as three gates over a norm layer β€” judgment (the recoverability doctrine + the settings-allowlist mechanical floor; reversibility, not blast-radius), control (kill-switch halt + steer redirect, operator-signal-driven), preservation (commit-on-stop, automatic snapshot to refs/auto-save/<ts>), and the norms (commit-no-coauthor + worktrees-operator-initiated). It is ambient, never invoked: it enhances development-lifecycle one-way (the loop inlines the doctrine byte-identically across /workΒ·/bugfixΒ·/release and graceful-skips the hooks), and the operator touches it only by dropping a STOP/STEER file or answering an unrecoverable-action confirmation. kill-switch/steer are Claude-Code-effective; commit-on-stop and the git-layer enforcement hold on every host. Boundary: orthogonal to privacy (not folded). ### Opinions: it provides the recoverability standard how-we-engineer cites, one-way.

On review the commit-no-coauthor norm gained deterministic enforcement β€” a prepare-commit-msg git hook (coauthor-guard) that strips any agent Co-Authored-By trailer before the commit lands (strip-not-block, so the loop is never interrupted), mirroring privacy's pre-push floor ([PENDING-IMPL] β€” the only greenfield piece). Re-audit: build coauthor-guard; keep the doctrine in sync with the global push-and-confirmation policy; confirm conventions cites rather than copies the snippets at the conventions lift.

⚠️ **GitHub.com Fallback** ⚠️