genesis serve disables every genesis-* workflow and launches claude -p directly. It never runs anything under the dev repo's .genesis/scripts/:
$ grep -rn "\.genesis/scripts" --include="*.py" src/
src/genesis/scaffold.py:20:# here. A dev repo's `.genesis/scripts/activate.sh` then copies them into that
src/genesis/github.py:108: with `.genesis/scripts/activate.sh` once the credentials are in place.
Both hits are comments. Nothing executes.
That is fine for the scripts a dev repo runs because it is in Actions (retain-transcript.sh copies an Actions-only artifact; run-outcome.sh/escalate.sh post-mortem a dead runner). Serve has its own, arguably better, answers for those: --resume continuations with a judge, plus an operator watching the terminal.
It is not fine for the class of step that has nothing to do with Actions and everything to do with the loop: a deterministic check that must run before the agent gets its first turn.
The concrete case
MaKlaude has .genesis/scripts/nudge-gates.sh. It exists because a needs:human gate that nobody answers is the one failure mode with no safety net — every orchestrator run correctly does nothing while a gate is open, so a dropped gate produces no failing run, no red CI and no signal at all. Milestone 4's plan gate sat 21 days across ~85 scheduled ticks that way. The fix was to stop asking an agent to notice: "is this gate older than N days" needs no judgment, so it became a script wired as a workflow step placed deliberately before the agent step.
Under serve that step does not exist. All six genesis-* workflows read disabled_manually, so the check does not run, and noticing a stale gate is back to being an agent's judgment call — exactly the thing the script replaced. The net was written, tested, correctly placed, and silently does not execute in the mode the project actually runs in.
MaKlaude is patching this locally by hanging the same script off a SessionStart hook in .claude/settings.json, which is the one seam both modes share (verified: a headless claude -p in the repo fires the project hook). That works, but it is a per-repo workaround for a framework-level property, and every dev system that learns this same lesson will have to rediscover the same seam.
Why this is worth a framework answer
The scaffold teaches dev systems to build deterministic nets and wire them into workflow YAML — that is the right instinct and this repo's own templates encourage it. Serve then changes the safety posture of the system it is running, without saying so. A dev repo's tests can assert the workflow step is present and green (MaKlaude's do) while the step never fires.
Possible shapes, roughly in order of preference
- Serve runs the dev repo's pre-agent hook. A conventional path —
.genesis/scripts/pre-session.sh if present — invoked before each claude -p launch, bounded and non-fatal. Cheap, explicit, and the natural counterpart to the workflow step.
- Scaffold the SessionStart hook. Seed
.claude/settings.json with a session-nets.sh-shaped entry so new dev systems get a mode-independent seam by default. Works today with no serve change, but leaves the wiring as something each repo must remember.
- Serve declares the gap. At startup, log which workflow steps it is not running. Weakest, but strictly better than silence.
Whatever the shape, the property worth holding is that a net a dev system built to be deterministic stays deterministic across execution modes — otherwise "we made this a script so nobody has to remember it" is only true in CI.
genesis servedisables everygenesis-*workflow and launchesclaude -pdirectly. It never runs anything under the dev repo's.genesis/scripts/:Both hits are comments. Nothing executes.
That is fine for the scripts a dev repo runs because it is in Actions (
retain-transcript.shcopies an Actions-only artifact;run-outcome.sh/escalate.shpost-mortem a dead runner). Serve has its own, arguably better, answers for those:--resumecontinuations with a judge, plus an operator watching the terminal.It is not fine for the class of step that has nothing to do with Actions and everything to do with the loop: a deterministic check that must run before the agent gets its first turn.
The concrete case
MaKlaude has
.genesis/scripts/nudge-gates.sh. It exists because aneeds:humangate that nobody answers is the one failure mode with no safety net — every orchestrator run correctly does nothing while a gate is open, so a dropped gate produces no failing run, no red CI and no signal at all. Milestone 4's plan gate sat 21 days across ~85 scheduled ticks that way. The fix was to stop asking an agent to notice: "is this gate older than N days" needs no judgment, so it became a script wired as a workflow step placed deliberately before the agent step.Under serve that step does not exist. All six
genesis-*workflows readdisabled_manually, so the check does not run, and noticing a stale gate is back to being an agent's judgment call — exactly the thing the script replaced. The net was written, tested, correctly placed, and silently does not execute in the mode the project actually runs in.MaKlaude is patching this locally by hanging the same script off a
SessionStarthook in.claude/settings.json, which is the one seam both modes share (verified: a headlessclaude -pin the repo fires the project hook). That works, but it is a per-repo workaround for a framework-level property, and every dev system that learns this same lesson will have to rediscover the same seam.Why this is worth a framework answer
The scaffold teaches dev systems to build deterministic nets and wire them into workflow YAML — that is the right instinct and this repo's own templates encourage it. Serve then changes the safety posture of the system it is running, without saying so. A dev repo's tests can assert the workflow step is present and green (MaKlaude's do) while the step never fires.
Possible shapes, roughly in order of preference
.genesis/scripts/pre-session.shif present — invoked before eachclaude -plaunch, bounded and non-fatal. Cheap, explicit, and the natural counterpart to the workflow step..claude/settings.jsonwith asession-nets.sh-shaped entry so new dev systems get a mode-independent seam by default. Works today with no serve change, but leaves the wiring as something each repo must remember.Whatever the shape, the property worth holding is that a net a dev system built to be deterministic stays deterministic across execution modes — otherwise "we made this a script so nobody has to remember it" is only true in CI.