FOOP — Writing and Planning
This skill covers creating FOOPs: writing the specification and constructing the plan. For finding, executing, backburnering, or cancelling existing FOOPs, use the foop-use-maintain skill.
Authoritative source:
foop.mdat the repository root. When this skill andfoop.mdappear to disagree,foop.mdwins. Readfoop.mdbefore creating or editing any FOOP.
What a FOOP Is
A FOOP (Foolish Optimization Process) is the Foolish equivalent of Python's PEP or Rust's RFC. It proposes, discusses, and tracks changes to the Foolish language and its reference implementations.
A FOOP progresses through statuses: Draft → Brewing (ready for BDFL review) → Final (accepted) → Implementing (active coding) → complete. Each FOOP is assigned to a phase (phase-1 through phase-7, or meta for process documents).
The Two Files of a FOOP
Every FOOP is expressed as (up to) two separate files that share the same FOOP-<NUMBER> stem:
| File | Purpose | Answers |
|------|---------|---------|
| FOOP-#.md | Specification — the proposal, motivation, design, semantics, discussion. | What and why |
| FOOP-#.plan.md | Plan — a checkboxed, sequentially-executed breakdown of the work. (Note the lowercase .plan.md extension.) | How and in-what-order |
Executing a FOOP requires reading BOTH files. The plan assumes the context of the specification; do not act on FOOP-#.plan.md without first reading FOOP-#.md. The plan is meant to be executed sequentially from top to bottom.
FOOP Numbering — Little-Endian (Critical)
FOOP numbering is little-endian: the filename digits ARE the identifier, but they sort in reverse. Chronological order (oldest → newest):
FOOP-1, FOOP-2, FOOP-3, ... FOOP-9, FOOP-01, FOOP-11, FOOP-21, FOOP-31, FOOP-41, FOOP-51, FOOP-61, ...
- FOOP-9 is the one before FOOP-01.
- The digits in
FOOP-01are0then1— read as "ten" when reversed, so its sort key is10. - FOOP-21 → sort key 12. FOOP-51 → sort key 15.
| Filename | Identifier | Sort key (frontmatter only) |
|----------|------------|-----------------------------|
| FOOP-9.md | FOOP-9 | 9 |
| FOOP-01.md | FOOP-01 | 10 |
| FOOP-21.md | FOOP-21 | 12 |
| FOOP-51.md | FOOP-51 | 15 |
The filename digits ARE the identifier. The foop: frontmatter field is a separate numeric sort key (the digits reversed). Do NOT use the sort-key value as the identifier in prose.
Naming Convention
| Context | Form | Example |
|---------|------|---------|
| Filename, code, formal citation | FOOP-<NUMBER> (dash) | FOOP-01.md |
| Prose / sentences | FOOP <NUMBER> (space) | "FOOP 01 and FOOP 11 are pre-teen FOOPs." |
The space form in prose reduces digit-reversal errors: writing "FOOP 01" makes it harder to accidentally type "FOOP 10".
File Locations
| What | Path |
|------|------|
| FOOP specs & plans | docs/foop/FOOP-<NUMBER>.md and docs/foop/FOOP-<NUMBER>.plan.md |
| Index | docs/foop/INDEX.md (canonical list, sorted by number) |
| Template | docs/foop/FOOP-template.md |
| Helper script | docs/foop/scripts/foop_check.py |
| Meta-FOOP (defines the process itself) | docs/foop/FOOP-1.md |
The Numbering Helper Script
Use docs/foop/scripts/foop_check.py to manage FOOP numbering. Run it before creating a new FOOP and periodically to catch drift.
python3 docs/foop/scripts/foop_check.py check # verify consecutive numbering
python3 docs/foop/scripts/foop_check.py get_last # most recent FOOP
python3 docs/foop/scripts/foop_check.py gen_next # filename for next FOOP
python3 docs/foop/scripts/foop_check.py list # all FOOPs in chronological order
When creating a new FOOP, ALWAYS run gen_next first to get the correct filename and identifier. The script handles the little-endian encoding for you.
Task: Create a New FOOP Specification
Step 1 — Get the next FOOP number
python3 docs/foop/scripts/foop_check.py gen_next
Output: FOOP-<NUMBER>\tFOOP-<NUMBER>.md\t(sort key <N>)
Remember the <NUMBER> — you will substitute it everywhere below.
Step 2 — Copy the template
cp docs/foop/FOOP-template.md docs/foop/FOOP-<NUMBER>.md
Step 3 — Fill in the frontmatter
Edit docs/foop/FOOP-<NUMBER>.md. The frontmatter must be:
---
foop: D<NUMBER>
title: <SHORT TITLE — one line, no trailing period>
author: <Name> <email@example.com>
status: Draft
type: Standards
created: <YYYY-MM-DD>
phase: <phase-1 | phase-2 | phase-3 | phase-4 | phase-5 | phase-6 | phase-7 | meta>
supersedes: []
begun: [ ]
---
Frontmatter field rules:
| Field | Rule |
|-------|------|
| foop | The sort key. Two accepted forms (see "The foop: field" below). |
| title | Short title, one line, no trailing period. |
| status | Start as Draft. Lifecycle: Draft → Brewing → Final → Implementing → complete. |
| type | Typically Standards. |
| created | Date in YYYY-MM-DD format. |
| phase | One of phase-1 through phase-7, or meta for process documents. |
| supersedes | List of FOOP identifiers this one replaces. Empty list [] if none. |
| begun | [ ] (not yet started). Changed to [x] when work begins (see foop-use-maintain skill). |
The foop: field — two accepted forms:
Dprefix (big-endian decimal):foop: D<NUMBER>— theDmeans "the filename digits reversed as a big-endian decimal." Sofoop: D42= sort key 42 = fileFOOP-24.md.- Direct value:
foop: <NUMBER>(noD) — the literal sort-key value directly.
In all cases, the filename (FOOP-<NUMBER>.md) is the ultimate identifier and the right numbering. The foop: field is only a sort key for tooling.
Step 4 — Fill in the body
The body follows this structure (from the template):
# FOOP-<NUMBER>: <TITLE>
FOOP numbering is little-endian; the full rules live in `foop.md` at the
repository root — **read it before creating or editing a FOOP.**
## Abstract
One paragraph. What does this FOOP propose? Read this and you should know
whether to read the rest.
## Motivation
Why does this matter? What's the problem being solved? What does the world
look like today, and what does it look like after this FOOP is implemented?
## Specification
The design itself. Be precise. If a feature has syntax, give the grammar
fragment. If it adds an FIR variant, give the Rust struct and its `Fir` enum
arm. If it changes a step rule, give the before/after.
Use code blocks for anything formal.
## FIR Impact
If this FOOP doesn't touch FIR, write "None." and move on.
Otherwise: list every new FIR variant, every state-machine change, every
serialization implication. Include the YAML/JSON shape for any new variant.
## UBC Step Impact
If this FOOP doesn't touch the evaluator, write "None."
Otherwise: list every new step rule. State the before/after. Note any
interaction with existing step rules (especially around constanic
coordination).
## Test Plan
How is this verified?
- New unit tests in `<file>` covering ...
- New `.foo` approval tests at ...
- Existing tests that need updating ...
If a feature can't be cleanly tested, say so explicitly and explain why.
## Plan of Execution for Plan
How this FOOP's plan gets executed, and by whom. **Plan the execution based on
complexity**: assign each phase to an agent with sufficient capability for it,
rather than sizing the whole FOOP to one model. A table of phase → character →
needs, plus what makes the small-model phases safe, plus what must never be
delegated at any size. See "Planning the execution of the plan" below.
## Rejected Alternatives
At least one alternative MUST be listed, even if it's just "do nothing" with
an explanation of why doing nothing is worse.
### A. <Alternative name>
Description and reason for rejection.
### B. <Alternative name>
Description and reason for rejection.
## Open Questions
Things still to decide. List them as bullets. As they're resolved, edit the
FOOP body and remove from this section. When this section is empty and the
FOOP is `Implementing`, the design is frozen.
- ?
## References
- Prior FOOPs: ...
- External docs: ...
- Code locations: ...
Task: Create the Plan File
A FOOP is not actionable without a plan. Write docs/foop/FOOP-<NUMBER>.plan.md (lowercase .plan.md). Read FOOP-<NUMBER>.md first — the plan is derived from the specification; the spec exists before the plan, so you can name a concrete short_description and decompose the spec into ordered tasks.
Planning the execution of the plan
Every FOOP spec carries a "Plan of Execution for Plan" section (between Test Plan and Rejected Alternatives). It answers: which agent executes which phase?
The rule: plan the execution based on complexity. Phases differ sharply in what they demand. Assign each to an agent with sufficient capability for that phase — sizing every phase to the hardest one wastes capability on mechanical work, and sizing them all to the easiest puts judgment calls in the wrong hands.
Sort phases into two kinds:
- Judgment phases → a larger model. The deliverable IS a decision: resolving
an open question whose answer changes the design; predicting expected output
from the specification; every
output→checkedpromotion review. - Execution phases → a smaller model. There is a fixed target to hit: code given verbatim in the plan, a hand-written expectation to match, a mechanical diff to verify. An agent that cannot judge "is this right?" can still answer "does this match the thing a human wrote?" — a different and much easier question.
Harness-specific names as of writing: Claude — Opus / Sonnet for judgment, Sonnata for execution; Codex — GPT-terra; local — Qwen3.8-27B.
Three properties make the small-model phases safe. Build them into the plan deliberately, and say in the section that you have:
- Facts inline, not referenced. Carry trait shapes, verified behavior, file sizes, tuple arities and exact command forms in the plan itself, so an executing agent spends its context on the work rather than on rediscovery. Mark each fact verify, don't re-derive.
- A fixed target per phase. After a hand-written expectation exists, or after an adapter switch, "done" is checkable without judgment.
- Named stop conditions. Say explicitly what a wrong result looks like and that the answer is to STOP and report. A small model should not have to recognize trouble unaided; it should match a stated condition.
What must never be delegated, at any model size (AGENTS.md §"The agent is
responsible for correctness"): every output → checked promotion; any
hand-written expectation the design depends on; any decision to change a crate
the FOOP promised not to touch; marking any Verified-tier test #[ignore].
Plan Construction Rules
Build the plan so that:
- Tasks are listed in the order they must be executed (top to bottom).
- Each task is concrete and trackable on its own.
- Worktree lifecycle tasks (create / verify / merge / cleanup) appear as explicit checkboxes at the appropriate points (see "Worktree Branch Tracking" below).
- Tasks that prove larger than expected split into indented sub-tasks (see "Sub-Tasks" below).
- All RHS variables should be expanded and literally placed into the plan file as the plan is being created. No
${WORKTREE_*}placeholders remain when work begins — fill them with real paths. - If the spec has research/experimentation (web search, historic docs, prototyping), those should be clearly documented in the FOOP file, and the plan steps shall, where needed, contain section or sub-section header pointers into the FOOP file. A large todo with sub-tasks may have several "read section X of FOOP-<NUMBER>.md" as its first few checkboxes.
- Sanity-check sub-tasks may be installed where ambiguity exists — e.g. "[ ] sub-agent please consult with primary agent or human regarding the current approach to..." These can be installed or removed by the planning agent as specification, clarification, design, and planning progresses.
- Once work begins on a FOOP, all updates — including to the foop folder — MUST be written ONLY to the worktree. This continues until merge time.
- Every phase ends with a test-gate checkbox. The last checkbox of every implementation phase (and a final one right before the merge STOP) is, verbatim:
A phase is not complete until this box is checked. The contract behind it (a failing einmo test is broken code, not a stale baseline; never- [ ] Run all tests — old and new — and make sure they all pass correctly.promoteover a foreign FOOP's divergent baseline; the threeoutput/checked/verifiedstages are a contract) lives inrust_instructions.md§"Phase-by-phase testing discipline" — the plan installs the checkbox;rust_instructions.mdexplains why. - Every
output→checkedpromotion gets its own review-gate block. Promotion is a correctness claim made by the agent, never a bookkeeping step, and it is never installed as a bare one-line checkbox. Wherever a phase produces new or changed einmo output, the plan installs the full Promotion Review Gate block below (see "Promotion Review Gate"), with one[ ]sub-task per einmo case to be promoted, named individually. A block that names no cases is not a gate; a gate whose per-case boxes are checked faster than the cases could be read is a falsified record. - Every sub-section (and every undivided phase) starts with the "Establish relevant tests" checkbox — the small test subset for that sub-section: the old unit tests and einmo cases its work must not break, plus the new tests written for it. The checkbox names the REAL cases (expand every placeholder) and links to
README.md§"Running specific tests", the central command reference. The plan names CASES, never command forms — so einmo CLI evolution touches only the README section:
The implementer runs this subset frequently while the sub-section is developed (after each increment, each new test — adding each new test to the list), and runs ALL tests when the sub-section completes (rule 9's gate covers the phase boundary). Test invocations should go through subagents in parallel where available — the agent equivalent of a human opening several terminals.- [ ] Establish relevant tests for this sub-section. Use [these instructions](../../README.md#running-specific-tests) to run einmo tests: <case_1>, foop/<NUMBER>/<case_2>; run unit tests: <crate>::<test_a>, <crate>::<test_b>.
Promotion Review Gate
einmo promote output to checked writes the frozen expected-output contract. Checking a promotion box asserts: "I read this case's OUTPUT statement by statement, and I can say in my own words why each line is what the specification requires." It does not assert "the suite is green" and it does not assert "the evaluator produced this."
Install this block — expanded with the real case names, one sub-task each — before any einmo promote in the plan:
- [ ] Review and promote `output` → `checked` for FOOP-<NUMBER>'s einmo cases
- [ ] Confirm the rest of the suite is green — no foreign-FOOP baseline diverges
(a foreign divergence is a regression I introduced: fix the code, do not promote)
- [ ] Confirm no case below has a `verified/` twin (if one does: STOP, ask the human)
- [ ] Re-read the in-force specification for each feature under test:
FOOP-<NUMBER>.md §Specification, plus README.md §"The Unknown" for any NK result
- [ ] Review `foop/<NUMBER>/<case_1>` — every OUTPUT statement justified
- [ ] Review `foop/<NUMBER>/<case_2>` — every OUTPUT statement justified
(…one checkbox per case; name them all, never "…and the rest")
- [ ] Write the justification summary into the plan or commit message:
for each case, what it demonstrates and why its result is spec-correct
- [ ] Report ALL accumulated doubts to the human in ONE statement — or record
"no doubts". Blocking doubts stop here; non-blocking ones are reported alongside.
- [ ] Run `einmo promote output to checked foolish-ubca/einmo_suite`
- [ ] Re-run `cargo test -p foolish-ubca --lib -- einmo_gate_checked` — must exit 0
What "every OUTPUT statement justified" requires, per case — this is the work, not a formality:
- Statement by statement. Read each OUTPUT line against the INPUT statement that produced it. State why that value is what the specification mandates. "The evaluator emitted it" is the thing being tested, not a justification for it.
- Be skeptical of
NK. A search settling NK is the narrow, exceptional outcome. Name which legitimate case applies (seeREADME.md§"The Unknown" and FOOP-23 §Specification — anchored miss ⇒ NK, unanchored miss ⇒ ECONSTANIC). If you cannot name it, trace it with thefoolish-debuggingskill; do not promote. - Statement names are specification.
hit = ?…asserts the search finds its target;miss = ?…asserts it does not. Ahitthat yields NK is a contradiction between the test's own claim and its result — resolve it (fix the bug the name predicted, or rename with a comment explaining why) before promoting, never by promoting past it. - Coherence, not just conformance. Ask whether the result fits the rest of Foolish: does an analogous existing case behave the same way? Would a Foolisher reading only the spec predict this output? A result that is locally defensible but inconsistent with a sibling feature is a design bug worth raising, not a baseline to freeze.
- Step counts and alarms count too. They are part of the OUTPUT contract. A step count that jumped sharply for a feature that should not have changed cost is a signal to investigate.
If any case fails review, do not promote any of them. Fix the code (or revise the test's input/statement names, which is its own reviewable change) and re-run the gate.
Checkbox Format
When an item is checked off, always place a timestamp (to the minute) on the next line with indent into the bulleted list:
- [ ] Task not yet done
- [x] Task completed ← bad (no timestamp)
- [x] Task completed ← good
(2026-05-06 13:11) ← timestamped properly
This gives both agents and humans a clear view of how work is progressing over time.
Sub-Tasks
If a task proves larger than expected and splits into multiple sub-tasks, indent them under the parent. Use completed sub-tasks to justify why the split occurred:
- [ ] Merge `foop-<NUMBER>-<SHORT_DESCRIPTION>` to `jia`
- [ ] Run all tests — old and new — and make sure they all pass correctly.
- [ ] Check and make sure current foop has, and passes, a "comprehensive" snaptest. Input name: `input/foop/<NUMBER>/comprehensive.foo` (reserved for this foop). Agent generates and verifies; human gives final signed approval.
- [ ] Run all tests — old and new — and make sure they all pass correctly.
- [x] Detected complex merge situation requiring additional work
(2026-05-06 14:00)
- [ ] Update `foop-<NUMBER>-<SHORT_DESCRIPTION>` to follow new coding style
- [ ] Update `foop-<NUMBER>-<SHORT_DESCRIPTION>` to use new API call convention
- [x] Merged breaking changes from `jia`
(2026-05-06 14:31)
- [ ] Repair ALL tests in `jia` in /home/<USER>/foolish-rust
- [ ] STOP! STOP!! STOP!!! ASK HUMAN to check this box before continuing. UNDER NO CIRCUMSTANCES will Agent continue past this point automatically!!
- [ ] Present human with the `cd $(pwd)/../foolish_worktrees/foop-<NUMBER>-<SHORT_DESCRIPTION>` command and ask them to review snapshots BEFORE checking the parent checkbox.
- [ ] Cleanup $(pwd)/../foolish_worktrees/foop-<NUMBER>-<SHORT_DESCRIPTION>
- [ ] Check that .plan.md has all but Cleanup checkboxes completed
- [ ] Remove $(pwd)/../foolish_worktrees/foop-<NUMBER>-<SHORT_DESCRIPTION>
- [ ] This is the last sub-task checkbox to be checked in this block
This pattern is common because Foolish uses git merge (not rebase), so merge conflicts on jia may trigger follow-up repair work.
Worktree Branch Tracking
If a worktree branch is used for implementation, the plan must document the lifecycle of that worktree as explicit, separate checkbox tasks placed at appropriate points in the plan.
Variables (expand to literals before the plan is finalized):
WORKTREE_ORIGIN_BRANCH=jia
WORKTREE_ORIGIN_PATH=$(pwd)
WORKTREE_BRANCH_NAME=foop-<NUMBER>-<SHORT_DESCRIPTION>
WORKTREE_FULL_FS_PATH=$(pwd)/../foolish_worktrees/foop-<NUMBER>-<SHORT_DESCRIPTION>
The short_description in the path should be generated as part of the .plan.md generation. It is possible because the specification is already made. The foop-<NUMBER> suffix should match the name of the foop file as well as the plan file. Once set, this path name is fixed.
Worktree creation command (from the ${WORKTREE_ORIGIN_BRANCH} branch and path):
# cd $WORKTREE_ORIGIN_PATH ## User normally starts in this directory
# git checkout $WORKTREE_ORIGIN_BRANCH ## Already on this branch normally
git worktree add -b "$WORKTREE_BRANCH_NAME" "$WORKTREE_FULL_FS_PATH"
cd "$WORKTREE_FULL_FS_PATH"
# Now commence work here.
Permission scope: An agent with permission to work on the main foolish directory also has permission to work on a worktree added from the foretias directory. If asking for permission, ask once for the entire worktree branch path ($WORKTREE_FULL_FS_PATH), not a subdirectory.
Worktree checkboxes in the plan (all variables expanded to literals):
- [ ] Create worktree at $(pwd)/../foolish_worktrees/foop-<NUMBER>-<SHORT_DESCRIPTION> with branch `foop-<NUMBER>-<SHORT_DESCRIPTION>`
...
(implementation tasks here)
...
- [ ] Verify all work is complete in $(pwd)/../foolish_worktrees/foop-<NUMBER>-<SHORT_DESCRIPTION> and committed to `foop-<NUMBER>-<SHORT_DESCRIPTION>`
- [ ] Merge `foop-<NUMBER>-<SHORT_DESCRIPTION>` to `jia`
Branch naming (no prefix): The branch name is
foop-<NUMBER>-<SHORT_DESCRIPTION>— bare, with nofoop/prefix — and must be identical toWORKTREE_BRANCH_NAMEand to the worktree directory's basename. Thefoop-<NUMBER>stem must match the FOOP filename.Current practice (FOOP-13, FOOP-23, FOOP-33, FOOP-54, FOOP-84):
foop-23-value-search,foop-54-einmo. Afoop/-prefixed form appears in older pre-2026-06 plans (foop/12-alarms) and in somefoop.mdexamples — do not copy it. Mixing the two within one plan is a real hazard: the create checkbox makes one branch while the merge checkbox names another that does not exist.
Variable expansion: The branch names and paths in the plan should reflect expanded
$HOME,$WORKTREE_BRANCH_NAMEand$WORKTREE_ORIGIN_BRANCH. Fillers such as the literal$WORKTREE_ORIGIN_BRANCHshould be replaced with real values before starting work on the plan.${HOME}should be the full path when the plan is generated.
Comprehensive FOOP Test
Every FOOP has the right — and the obligation — to generate a comprehensive snapshot test that thoroughly exercises the new feature interacting with existing features.
| Attribute | Value |
|-----------|-------|
| Input file | foolish-ubca/einmo_suite/input/foop/<NUMBER>/comprehensive.foo |
| Name | Reserved for this FOOP alone |
| Purpose | Coverage of high-value feature combinations and edge cases that per-phase approval tests may not reach. Slight repetition of earlier tests is acceptable if it serves coverage. |
| Scope | Mix new features with old — value search inside nested branes, contexted operators chained with dot access, expression patterns referencing ancestral names, combined name+value with head/tail, etc. The test should be large enough to exercise at least one path through every new operator or predicate variant. |
| Process | The agent generates the .foo input, runs it through the approval test suite, and reviews the output statement by statement via the Promotion Review Gate before promoting. Final approval requires human review and formal signed acceptance. |
| Placement in plan | A checkbox task "Write and verify input/foop/<NUMBER>/comprehensive.foo" should appear in the plan, after all implementation phases and before the merge STOP. |
Minimal Plan Skeleton
# FOOP-<NUMBER>.plan — <SHORT_DESCRIPTION>
- [ ] Begin work: commit FOOP-<NUMBER>.md and FOOP-<NUMBER>.plan.md to origin, check `begun: [x]` in frontmatter
(YYYY-MM-DD HH:MM)
- [ ] Create worktree at $(pwd)/../foolish_worktrees/foop-<NUMBER>-<SHORT_DESCRIPTION> with branch `foop-<NUMBER>-<SHORT_DESCRIPTION>`
- [ ] (read §<SECTION> of FOOP-<NUMBER>.md)
- [ ] Establish relevant tests for this phase. Use [these instructions](../../README.md#running-specific-tests) to run einmo tests: <case_1>, foop/<NUMBER>/<case_2>; run unit tests: <crate>::<test_a>, <crate>::<test_b>. Run this subset frequently while implementing; add new tests to this list as they are written.
- [ ] <implementation task 1>
- [ ] <implementation task 2>
- [ ] Run all tests — old and new — and make sure they all pass correctly.
- [ ] Review and promote `output` → `checked` for FOOP-<NUMBER>'s einmo cases
(expand the full Promotion Review Gate block, one sub-task per named case)
- [ ] Write and verify `foolish-ubca/einmo_suite/input/foop/<NUMBER>/comprehensive.foo`
- [ ] Run all tests — old and new — and make sure they all pass correctly.
- [ ] Review and promote `output` → `checked` for `foop/<NUMBER>/comprehensive`
(expand the full Promotion Review Gate block)
- [ ] Verify all work is complete in $(pwd)/../foolish_worktrees/foop-<NUMBER>-<SHORT_DESCRIPTION> and committed to `foop-<NUMBER>-<SHORT_DESCRIPTION>`
- [ ] Merge `foop-<NUMBER>-<SHORT_DESCRIPTION>` to `jia`
- [ ] Cleanup worktree at $(pwd)/../foolish_worktrees/foop-<NUMBER>-<SHORT_DESCRIPTION>
Quick Reference — All Creation Commands
# ── Numbering ──
python3 docs/foop/scripts/foop_check.py gen_next # get next FOOP number
python3 docs/foop/scripts/foop_check.py check # verify no gaps
# ── Create spec ──
cp docs/foop/FOOP-template.md docs/foop/FOOP-<NUMBER>.md
$EDITOR docs/foop/FOOP-<NUMBER>.md # fill frontmatter + body
# ── Create plan ──
$EDITOR docs/foop/FOOP-<NUMBER>.plan.md # write from spec, expand all variables
# ── Worktree setup (for plan construction reference) ──
git worktree add -b foop-<NUMBER>-<SHORT_DESCRIPTION> \
$(pwd)/../foolish_worktrees/foop-<NUMBER>-<SHORT_DESCRIPTION>
Safety Invariants
- Read
foop.mdbefore creating or editing any FOOP. This skill is a cookbook;foop.mdis the authority. - Always run
gen_nextbefore creating a new FOOP. Never guess the next number. - All variables must be expanded to literals in a finalized plan. No
${WORKTREE_*}placeholders remain when work begins. - At least one Rejected Alternative must be listed in the spec, even if it's "do nothing."
- Never auto-accept snapshots. Do not run
cargo insta accept/INSTA_UPDATE=always. Human review required for all snapshot tests. - Never start Phase+ work when tests are broken. Fix or disable (with human permission) first.
- Never commit from inside this skill unless the user explicitly asks.
- Never promote over a foreign FOOP's divergent einmo baseline. A failing einmo test (output≠checked) is broken code, not a stale baseline — fix your code so the pre-existing baseline passes again.
einmo promote output→checkedis only for your own FOOP's new tests, after the rest of the suite is green; never a remedy for a failing test. If the divergent baseline has averified/twin, it is frozen — do not touch it without a human reviewer's key. Seerust_instructions.md§"Phase-by-phase testing discipline." - Never promote a case you have not read. "It is my own FOOP's test" makes promotion permissible, not justified — the justification is a statement-by-statement reading against the in-force specification. Every plan that promotes installs the Promotion Review Gate with one named sub-task per case; a plan with a bare
- [ ] einmo promote …line is malformed, and a gate whose per-case boxes were checked without the cases being read is a false record of work. - Every sub-section (and undivided phase) starts with the "Establish relevant tests" checkbox — the small subset of old + new unit tests and einmo cases, named by case, linking to
README.md§"Running specific tests". The subset runs frequently during the sub-section; ALL tests run when it completes. Plans name cases, never command forms — the commands live only in the README section.
Last Updated
Date: 2026-09-02
Updated By: Claude Code / claude-opus-5
Changes: Added the "Plan of Execution for Plan" spec section (between Test Plan and
Rejected Alternatives) and a new §"Planning the execution of the plan": every FOOP states which
agent executes which phase, planned by complexity rather than sizing the whole FOOP to one
model. Judgment phases (resolving an open question, predicting expected output from the spec,
every output → checked promotion review) go to a larger model; execution phases, which have
a fixed target to hit, go to a smaller one. Records the three properties that make small-model
phases safe (facts inline not referenced; a fixed target per phase; named stop conditions) and
what must never be delegated at any size. Mirrored into docs/foop/FOOP-template.md and
AGENTS.md §FOOP. Prior entry: plan-construction rule 11, safety invariant 10, and the
per-sub-section "Establish relevant tests" checkbox linking to README.md §"Running specific
tests" — plans name CASES, never command forms.