Skip to content

serve silently drops the dev repo's pre-agent deterministic steps, so nets that exist and are tested never run #44

Description

@genesis-dev-bot

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

  1. 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.
  2. 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.
  3. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs:evolverFramework-level improvement for genesis's own evolver to evaluate

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions