Skip to content

feat(followups): mutation + ex-ante creation verbs (CHARTER-01 — closes #355, #360) - #376

Merged
montfort merged 1 commit into
mainfrom
feat/followups-mutation-and-creation-verbs
Jul 26, 2026
Merged

feat(followups): mutation + ex-ante creation verbs (CHARTER-01 — closes #355, #360)#376
montfort merged 1 commit into
mainfrom
feat/followups-mutation-and-creation-verbs

Conversation

@montfort

Copy link
Copy Markdown
Contributor

Closes #355. Closes #360. Executes CHARTER-01 (declared in #374) — the repo's first executed Charter since self-adoption.

Ships as fw-4.37.0 / cli-3.39.0.

The three verbs

Verb What it closes
followups note FU-NNN "<text>" [--source ID] Recording a partial mitigation without changing status meant hand-editing a CLI-parsed file. Notes is single-line by parser contract, so annotations compose onto the existing value; --source keeps them attributable instead of undated prose.
followups set-status FU-NNN <status> The edit-then-recount two-step desyncs the moment you forget the second half, leaving the counters quietly lying about the backlog. Now one step.
followups new --title … --origin "CHARTER-NN §Scope" No path existed to create an entry at Charter-declaration time — both older paths assume an ex-post origin.

set-status refuses rather than damages: an out-of-vocabulary status is rejected, because the parser is lenient — a typo would not fail, it would silently drop the entry from every counter. promoted redirects to followups promote, which also writes the TDE that gives the status something to point at.

Why new is a correctness fix, not ergonomics: ids are minted max(existing) + 1 at extraction time, so the adopter's forward-referenced FU-011 (cited three times in CHARTER-06 with nothing reserving it) would have been handed to an unrelated entry by the next drift --apply, silently repointing those citations. new assigns the id atomically and prints it, so by the time the Charter cites it the entry exists.

All three refuse to write a registry with parse warnings — a surgical edit against a structure the parser mis-read can corrupt neighbouring entries.

Two findings worth flagging

The schema needed no change — and that reframes #360. The Charter declared the schema conditionally ("confirm the ex-ante shape validates; adjust $defs.entry if not"). It validates unchanged: origin_class already carries ex-ante-planning in its enum, and source_hash is not a schema property at all. The vocabulary for the ex-ante case already existed; only the creation path was missing.

Two pre-existing defects in shared write helpers (R6, new — not in Charter). set_entry_field collapsed an entry's trailing "\n\n" to "\n", gluing the edited entry to the next ## Bucket: heading, and insert_into_bucket appended entries with no blank line before the following section. Both are shared, so every drift --apply / promote / verify write had been reshaping the registry a little. Making field writes routine via note is what exposed them. Fixed at the root with regression tests rather than worked around.

What stayed deferred

Both options the Charter declared out of scope remain so, and nothing in execution argued otherwise: no id-reservation primitive (#360 Option C — atomic new closes the window by construction; a reservation registry would be a second source of truth for id assignment) and no Charter-scanning in drift (Option B — its dedup is keyed on an AILOG-derived Source-hash).

Verification

  • cargo test: 38 groups green. 10 new integration tests + 5 new unit tests, including the byte-identical-file assertion after a refused write, the "a following recount finds nothing to do" assertion, and the R4 new-then-drift no-duplicate check.
  • cargo clippy -p straymark-cli: no warnings in the three new modules. gen_codex_skills --check: in sync.
  • End-to-end with the built binary, running the flow the Charter declared: newnoteset-statusrecount reports "already in sync"listdrift does not re-extract. Then the guard: a malformed ### FU- heading makes note exit 1 with nothing written.
  • straymark validate . --include-charters → 0 errors. charter drift CHARTER-01 → 1 omission + 14 expansions, all four causes documented in ## Closing notes in this same PR (the atomic-update convention), including the one worth naming: the Charter declared the Gemini/.agent skill mirrors as "regenerated by the same generator" — gen_codex_skills only emits .codex, so those two are hand-maintained and were hand-synced. The reconnaissance discipline ([adopter] Charter scope declared against assumed (un-read) code — propose a code-reconnaissance gate at charter creation #210) had been applied to the files but not to the mechanism that maintains them.

Framework: pattern doc + template + /straymark-followups skill (all 4 agent surfaces) ×3 locales. Docs: CLI-REFERENCE ×3 locales, CLAUDE.md, version tables, CHANGELOG.md. Governed AILOG: AILOG-2026-07-26-001.

🤖 Generated with Claude Code

…#360)

Closes the last flows that required hand-editing the CLI-parsed registry.

- `followups note FU-NNN "<text>" [--source ID]` — dated annotation appended to
  Notes. The field is single-line by parser contract, so annotations compose
  onto the existing value; --source records what motivated it.
- `followups set-status FU-NNN <status>` — status AND counters in one step,
  closing the desync window recount exists to clean up after. An
  out-of-vocabulary status is refused, not written: the parser is lenient, so a
  typo would not fail — it would silently drop the entry from every counter.
  `promoted` redirects to `followups promote`, which writes the TDE the status
  points at.
- `followups new --title … --origin "CHARTER-NN §Scope"` — an entry declared
  ex-ante, before any AILOG exists. The id is assigned atomically and printed,
  so a Charter cites an entry that EXISTS: ids are minted max+1 at extraction,
  so the adopter's forward-referenced FU-011 would have been handed to an
  unrelated entry by the next `drift --apply`.

All three refuse to write a registry with parse warnings — a surgical edit
against a mis-read structure can corrupt neighbouring entries. One shared
recompute-and-write path (`write_recounted`) backs every mutating verb, and
`recount` now uses it too, so it stays the idempotent check on their arithmetic
rather than a second implementation.

The schema needed no change: `origin_class` already carried `ex-ante-planning`
and `source_hash` is not a schema property. The vocabulary for the ex-ante case
existed; only the creation path was missing.

Also fixes two pre-existing shape defects in the shared write helpers, which
every drift/promote/verify write had been degrading the registry with:
`set_entry_field` collapsed an entry's trailing blank line (gluing it to the
next `## Bucket:` heading) and `insert_into_bucket` omitted the blank line
before the following section. Documented as R6 (new, not in Charter).

fw-4.37.0 / cli-3.39.0. CHARTER-01 closed; AILOG-2026-07-26-001.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@montfort
montfort merged commit 50a5bc5 into main Jul 26, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant