Skip to content

The scaffolded .gitignore enumerates genesis serve's runtime files, so each new one dirties every dev repo #40

Description

@genesis-dev-bot

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:

.genesis/.*

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.

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