Found while running the orchestrator in Sayfan-AI/MaKlaude. Fixed locally there in MaKlaude PR #153; filing here because the source is the scaffold, so every genesis-created dev repo has it.
What happens
The scaffolded .gitignore carries a section for genesis serve's local control-plane state, written as an enumeration:
# genesis local control plane runtime state (genesis serve)
.genesis/.disabled-by-genesis
.genesis/.orchestrator.lock
.genesis/.poll-etag
.genesis/.poll-highwater
genesis serve also writes .genesis/.trigger-state (an event-poll cursor — MaKlaude's held {"ci_failure_seen": "2026-08-02T02:18:15Z"}). It isn't in the list, so it sits untracked in the working tree after every local-mode run.
Why it's worth fixing rather than appending the name
It's quiet, not harmless, and appending leaves the shape that produced the miss.
- Dev-system agents read
git status to decide what a run changed; a stray ?? line is noise they have to look past every run, and any git add -A path sweeps per-machine state into the repo.
- It is invisible to CI by construction — a fresh checkout has no runtime state — so
main stays green and nothing ever reports it. Same invisible-nothing-happened class MaKlaude has been draining one member at a time.
- The list is opt-in per filename, and the opt-in is done by whoever remembers. That's the recurring shape in MaKlaude's learnings (the concurrency group the evolver never joined; the turn-budget floors that moved per-runner; the escalate step that needed a membership test): when a property depends on every member of a set being listed, the next member silently defaults to unsafe.
Suggested fix
Replace the enumeration with a pattern over the class, which encodes the convention genesis already follows — a dotfile directly under .genesis/ is per-machine runtime state; tracked content (config.toml, onboarding.md, design/, scripts/) is never a dotfile:
Already-tracked files are unaffected by gitignore, and a future .genesis dotfile that genuinely belongs in the repo can still be added with git add -f.
One trap worth carrying over, if you guard it with a test
MaKlaude PR #153 pins this with git check-ignore rather than by reparsing .gitignore. The negative control — "tracked .genesis content must NOT be ignored", which is what stops an over-broad .genesis/* from swallowing the dev system's own scripts — passed against a deliberately broken tree on the first attempt. git check-ignore consults the index by default and reports every tracked path as un-ignored, since gitignore has no effect on tracked files. True, and useless there: every path the control asks about is tracked, so it answered "not ignored" no matter how broad the pattern got. --no-index measures the pattern instead of the index, which is both the property under test and the case that matters in practice — the next new file under .genesis/scripts/ isn't tracked yet.
Scope check
Only the scaffold template needs to change; the fix doesn't reach repos already created from it (MaKlaude's own copy needed its own PR). If genesis has a drift-check or re-scaffold path for existing dev repos, this is a candidate for it.
Found while running the orchestrator in
Sayfan-AI/MaKlaude. Fixed locally there in MaKlaude PR #153; filing here because the source is the scaffold, so every genesis-created dev repo has it.What happens
The scaffolded
.gitignorecarries a section forgenesis serve's local control-plane state, written as an enumeration:genesis servealso writes.genesis/.trigger-state(an event-poll cursor — MaKlaude's held{"ci_failure_seen": "2026-08-02T02:18:15Z"}). It isn't in the list, so it sits untracked in the working tree after every local-mode run.Why it's worth fixing rather than appending the name
It's quiet, not harmless, and appending leaves the shape that produced the miss.
git statusto decide what a run changed; a stray??line is noise they have to look past every run, and anygit add -Apath sweeps per-machine state into the repo.mainstays green and nothing ever reports it. Same invisible-nothing-happened class MaKlaude has been draining one member at a time.Suggested fix
Replace the enumeration with a pattern over the class, which encodes the convention genesis already follows — a dotfile directly under
.genesis/is per-machine runtime state; tracked content (config.toml,onboarding.md,design/,scripts/) is never a dotfile:Already-tracked files are unaffected by gitignore, and a future
.genesisdotfile that genuinely belongs in the repo can still be added withgit add -f.One trap worth carrying over, if you guard it with a test
MaKlaude PR #153 pins this with
git check-ignorerather than by reparsing.gitignore. The negative control — "tracked.genesiscontent must NOT be ignored", which is what stops an over-broad.genesis/*from swallowing the dev system's own scripts — passed against a deliberately broken tree on the first attempt.git check-ignoreconsults the index by default and reports every tracked path as un-ignored, since gitignore has no effect on tracked files. True, and useless there: every path the control asks about is tracked, so it answered "not ignored" no matter how broad the pattern got.--no-indexmeasures the pattern instead of the index, which is both the property under test and the case that matters in practice — the next new file under.genesis/scripts/isn't tracked yet.Scope check
Only the scaffold template needs to change; the fix doesn't reach repos already created from it (MaKlaude's own copy needed its own PR). If genesis has a drift-check or re-scaffold path for existing dev repos, this is a candidate for it.