Plan file contract — host and container¶
Nyia's plan tooling has two sides that never call each other:
| Side | Runs where | Does |
|---|---|---|
nyia (the orchestrator) |
on the host, only | reads and renders: nyia todo, nyia plans status, nyia plans decisions; one-time store operations: nyia plans migrate, nyia plans status-backfill |
| Skills and system prompts | inside the assistant container | read the files below, and write exactly two things: a plan's Status: line and entries in its decisions.md |
nyia is never installed, mounted, or shimmed into the container — that is the product: the AI CLI lives in the
box, control stays outside. A skill or prompt that says "run nyia …" is a bug (tests/bats/test_skills_host_boundary.bats
fails the build on it). The boundary is files, in the formats below.
1. todo.md — generated by the host, read by everyone¶
.nyiakeeper/todo.md is the plan inventory: one line per plan, worst status first, with a GENERATED header.
The host regenerates it at launch (right after the plan-layout gate) and after the container exits;
nyia todo --write regenerates it on demand. The regeneration is quiet and best-effort: it never fails a launch
and it never overwrites a file that does not carry the GENERATED header (a hand-written todo.md is left alone,
with a one-line hint at launch; archive it, or NYIA_TODO_FORCE=1 nyia todo --write).
Inside the container: read it, never write it. If it is missing or older than the plans, derive the same view by
reading each plans/NNN-slug/plan.md Status: line — read-only; the host writes the file on the next launch or exit.
2. plan.md — the Status: line¶
Line 2 of every plans/NNN-slug/plan.md (the line after the # Plan: title) is:
Exactly one token from that enum. Skills (nyia-make-a-plan, nyia-implement-plan, nyia-run-plans, nyia-code-review, nyia-kickoff, nyia-checkpoint)
edit this line to move a plan on the board; the inventory is generated from it. Optional, near the top:
Roadmap: <label> — a free-text grouping axis read by nyia-plan-status.
3. decisions.md — the decision log¶
plans/NNN-slug/decisions.md is an append-only log. One entry per decision, a blank line between entries:
## D-<YYYYMMDD>-<HHMMSS>-<n> (<YYYY-MM-DD>) · decided-by: <user|llm|<assistant-name>>
Topic: <one line>
Question: <one line>
Options: <one line — alternatives considered, "|"-separated>
Decision: <one line — what was decided and why>
Supersedes: <optional — an earlier decision id>
Rules for a writer (a skill, or a person in an editor):
- The header is the only line that starts with
##; the id isD-+ date + time + a random number (unique in the file); the date in parentheses is ISO;decided-byisuserwhen the person decided,llm(or the assistant's name) when the assistant did. - Every field is one line — fold newlines into spaces. Do not put secrets in a decision (the host viewer redacts
KEY=,TOKEN=,PASSWORD=patterns on display, but the file is the record). Topic,QuestionandDecisionare required;OptionsandSupersedesare optional.- Append; never rewrite or delete an earlier entry. A changed mind is a new entry with
Supersedes:.
The host viewer (nyia plans decisions [N] [--by X] [--since DATE]) is tolerant: it renders what parses and prints
warning: malformed entry at <file>:<line> for what does not. Inside the container, nyia-show-decisions reads the same
files directly.
4. Reviews¶
plans/NNN-slug/reviews/ holds plan-review-*, code-review-* and pair-review-* files. Those file-name
prefixes are parsed by the migrator and the resolvers — they are not skill names and do not change when skills
are renamed.
5. Shipped skills are Nyia-owned¶
The built-in skills under docker/shared/skills/ are refreshed (overwritten) on every upgrade, at every seeding
target; every shipped copy carries a .nyia-builtin <version> marker. Never edit a shipped copy in place — it will
be replaced. To customize a built-in under its own name, use a team source or the project's
.nyiakeeper/shared/skills/ (an override copied from there is user-owned: the marker is stripped, and it is never
replaced); to change the image itself, use an overlay (TEAM_SHARING.md). A skill you write
under another name in your global skills directory is never touched. Inside the box, the image's copy of a
built-in replaces the host-propagated copy only when the image is at least as new — rebuild the image
(nyia-<assistant> --build) to refresh skills in the session after a dist-only upgrade. When a shipped skill is
renamed (the nyia- prefix, Plan 338), every directory under the old name inside Nyia's skill directories — global,
per-assistant, persona profiles — is deleted on upgrade and again at every launch, whatever its content or marker: those
directories only ever receive Nyia's copies, and a CLI's own commands never live there.