Skip to content

fix: generate the autonomy ledger skeleton instead of parsing live bytes - #124

Merged
Jammy2211 merged 2 commits into
mainfrom
feature/spawn-autonomy-log-generated
Aug 4, 2026
Merged

fix: generate the autonomy ledger skeleton instead of parsing live bytes#124
Jammy2211 merged 2 commits into
mainfrom
feature/spawn-autonomy-log-generated

Conversation

@Jammy2211

Copy link
Copy Markdown
Collaborator

Closes #123.

Overview

autonomy_log_body() copied lines from the live autonomy_log.md until one started with |---. Same "trust the live file's shape" pattern that made empty_body() stamp live registry entries into the public templates (#118) — it just had not fired yet.

Pre-existing; surfaced by the independent review of #118. Sibling half of #121.

Both failure modes reproduced against the real ledger

hazard old behaviour
a task row inserted above the separator that row copied into the template
separator reformatted to | --- | — a cosmetic edit any markdown formatter makes the break never fires: 231 live task records copied

The canary scan is no backstop. A leaked row containing no dataset or person token returns zero hits — verified directly. The full-ledger case is caught today only because some rows happen to mention slacs1430 / B1938 / cosmos_web_ring. That is luck, and it is exactly the reasoning that made the #118 leak invisible.

The fix

Same shape as #118: the header is a constant asset and the source is never opened. Byte-identical to what the parse produced for the current ledger, so it introduces no template driftautonomy_log.md does not appear in the post-merge --check preview at all; only the two files changed here do.

Spec rule 5 was also internally contradictory: it listed autonomy_log.md with the EMPTY ledgers and said each title lives in EMPTY_TITLES, but the ledger is SPECIAL, absent from that map, and carries prose and a table rather than a title plus schema-pointer comment. Now split into rule 5 (the six EMPTY ledgers) and rule 5b (the autonomy ledger).

The trade-off this creates, and how it is covered

Generating instead of parsing removes the leak — but it also removes the feedback that kept the header current. Nothing else would notice if the live ledger grew a column and the template kept shipping the old table.

So one test reads the real file and asserts the live ledger still starts with the constant. If the schema moves, that fails and tells you to update the constant — explicitly, not to re-add parsing.

Independent review (Codex)

Two findings, both fixed:

  • The tests were partly circular. autonomy_log_body(src) == AUTONOMY_LOG_TEMPLATE compares the helper with what the helper returns, and the only independent format check was endswith("|") — a stale or malformed constant would have left every test and --check green. Added the live-schema guard above, an assertion on the emitted template file rather than the helper's return value, and an independent shape check (H1 present, exactly a header row plus separator, matching column counts). Control-tested both ways: a stale column fails the live-schema guard; a short separator fails that and the shape guard.
  • The spec contradiction above.

Codex separately confirmed what I had audited: the constant is byte-identical to the live ledger's first 11 lines (both SHA-256 6cad53f7…), the signature is safe at the single production call site, no other handler parses live bytes with shape assumptions, and there is no feedback loop — feeding the template back through spawn returns the same constant, and no shipped workflow regenerates it.

Verification

  • 81 tests pass. 7 fail against the old implementation — the missing-source case, five hostile ledger shapes, and the end-to-end generated-tree test.
  • Ledger shapes covered: well-formed, row-above-separator, reformatted separator, no separator, prose-then-table. Plus a nonexistent source file, which is the strongest form of "never opens it".
  • Post-merge --check preview: 2 drifts, both the files changed here. Memory OK.

API Changes

None. spawn.py is a repo-local generator with no importers outside this repo. autonomy_log_body(src)autonomy_log_body(src=None); src is accepted and ignored so the generator's dispatch stays uniform. Single production call site.

Ship notes

Heart YELLOW (score 70, red_reasons: []) on the same two reasons acknowledged earlier today — workspace validation, tenant-firewall manifest drift. Neither relates to this change.

Post-merge: /spawn --apply. Expect autonomy_log.md itself to be unchanged in the published template.

🤖 Generated with Claude Code

Jammy2211 and others added 2 commits August 4, 2026 20:09
`autonomy_log_body()` copied lines from the live `autonomy_log.md` until one
started with `|---`. That is the same "trust the live file's shape" pattern
that made `empty_body()` stamp live registry entries into the public templates
(#118) — it just had not fired yet.

Both failure modes reproduced against the real ledger:

  * a task row inserted ABOVE the separator is copied verbatim;
  * a cosmetically reformatted separator (`| --- |`, which any markdown
    formatter may produce) means the break never fires — 231 live task records
    into a public repo.

The canary scan is no backstop: a leaked row containing no dataset or person
token returns zero hits, verified. The full-ledger case happens to be caught
today only because some rows mention slacs1430 / B1938 / cosmos_web_ring. That
is luck, and it is exactly the reasoning that made the #118 leak invisible.

The header is now a constant asset and the source is never opened. It is
byte-identical to what the parse produced for the current ledger, so this
introduces no template drift — `autonomy_log.md` does not appear in the
post-merge --check preview at all.

Tests cover all five ledger shapes (well-formed, row-above-separator,
reformatted separator, no separator, prose-then-table) plus a nonexistent
source file, which is the strongest form of "never opens it". The end-to-end
generated-tree fixture now plants live markers on BOTH sides of a reformatted
separator. Control-tested: 7 tests fail against the old implementation.

Closes #123

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…le 5b

Independent review (Codex) findings.

1. The tests were partly circular: `autonomy_log_body(src) == AUTONOMY_LOG_TEMPLATE`
   compares the helper with what the helper returns, and the only independent
   format check was `endswith("|")`. A stale or malformed constant would have
   left every test AND `--check` green.

   Generating instead of parsing removes the leak, but it also removes the
   feedback that kept the header current — nothing else notices if the live
   ledger grows a column and the template keeps shipping the old table. Adds:
     * the live ledger must still START WITH the constant (the one test here
       that reads the real file, skipped where no live ledger exists);
     * the EMITTED template file must equal the constant, not just the helper's
       return value;
     * an independent shape check (H1 present, exactly a header row plus a
       separator, matching column counts).

   Control-tested both ways: a stale column fails the live-schema guard, and a
   short separator fails both that and the shape guard.

2. Spec rule 5 was internally contradictory — it listed `autonomy_log.md` with
   the EMPTY ledgers and said each title lives in `EMPTY_TITLES`, but the
   ledger is SPECIAL, absent from that map, and carries prose and a table
   rather than a title plus schema-pointer comment. Split into rule 5 (the six
   EMPTY ledgers) and rule 5b (the autonomy ledger), each stating its own
   generate-never-parse reasoning.

Refs #123

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Jammy2211 Jammy2211 added the pending-release Awaiting coordinated release label Aug 4, 2026
@Jammy2211
Jammy2211 merged commit 3cdc755 into main Aug 4, 2026
2 checks passed
@Jammy2211
Jammy2211 deleted the feature/spawn-autonomy-log-generated branch August 4, 2026 19:33
Jammy2211 pushed a commit that referenced this pull request Aug 4, 2026
Prompt folded manually: the issue was created directly rather than via
create_issue, so the file never advanced draft/ -> active/ and
`lifecycle.py record --prompt` had nothing to fold.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pending-release Awaiting coordinated release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: SPECIAL:autonomy_log parses live bytes and can leak the whole ledger

1 participant