Skip to content

fix(promote): keep a decision-log entry's metadata block out of the Proposal - #189

Merged
kendrick merged 4 commits into
mainfrom
issue-176
Nov 10, 2026
Merged

kendrick merged 4 commits into
mainfrom
issue-176

Conversation

@kendrick

Copy link
Copy Markdown
Owner

ltm promote --entry now reads the metadata block that working-memory-kit plans to put under each decision-log heading. Without this, promoting an entry that has status: and date: lines publishes them as Record body text, and a stamp written as - promoted_to: or **promoted_to:** lets the same entry be promoted twice.

The block is the run of lines straight under the heading whose key is one of status, date, superseded_by, or promoted_to, in plain, list (- key:), or bold (**key:** ) form. readEntry in src/core/promote.ts now leaves the block out of the Proposal body, looks for an existing stamp only inside it, and writes the new stamp as the block's last line. An entry with no block behaves as before. The key set is the contract with working-memory-kit, so owner: x in the same position stays body text.

Closes #176

Where to Look

Start with readEntry in src/core/promote.ts, about 20 lines. The part I'm least sure about is how loose META_LINE is. Plain and list forms match with no space after the colon, so a hand-typed promoted_to:acme:x still refuses a second promotion, as it did before this change. The bold form still needs the space after **promoted_to:**. The old parser never saw a bold stamp in any spacing, so that case is no worse than before.

fixtures/working-memory-kit/decisionLog.md is a byte copy of working-memory-kit's template/_working-memory/decisionLog.md at 19a9a80, with SOURCE.txt naming the SHA. A new CLI test promotes from it.

Verification

An independent reproducer ran each claim from the build report. Each claim below is marked by where its last run happened: caller drove the ltm promote CLI path (from src/cli/promote.ts:69 through promoteSource into readEntry), and seam ran a test or check with no production caller in front of it.

  • The pinned-template test catches the bug (caller). With src/core/promote.ts reset to the base commit, bun test test/cli/promote.test.ts -t 'pinned working-memory-kit' fails at test/cli/promote.test.ts:201 with Expected to not contain: "status: accepted", so the metadata block was in the Proposal body. At HEAD it passes.
  • test/cli/promote.test.ts passes in full (caller): bun test test/cli/promote.test.ts gives 15 pass, 0 fail. The new test spawns ltm promote <log> --entry 'Cap retries at three per request' --bank <clone> --json.
  • readEntry builds body, stamp lookup, and stamp position from the block (caller): same CLI run as above, plus a direct read of the diff.
  • A no-space stamp still refuses (caller). A real ltm promote log.md --entry A on an entry holding promoted_to:acme:x exits 1 with ltm promote: log.md already has promoted_to 'acme:x' and leaves the repo clean. The test a promoted_to line with no space after the colon, or no value, still gives existing fails on the previous META_LINE and passes at HEAD, 62 pass in test/core/promote.test.ts.
  • The bold, list, and plain forms all read as a stamp (caller): calling the real readEntry returns acme:x for "**promoted_to:** acme:x" and for "- promoted_to:acme:x", and null for "**promoted_to:**acme:x".
  • The new readEntry metadata block tests and the bold-form refusal pass (seam): bun test test/core/promote.test.ts gives 61 pass, 0 fail in that round. The tests call readEntry and promoteSource directly. The bold-form refusal was not driven through the CLI.
  • The fixture matches upstream (seam): cmp fixtures/working-memory-kit/decisionLog.md <(gh api 'repos/kendrick/working-memory-kit/contents/template/_working-memory/decisionLog.md?ref=19a9a80a3c021b6b105bfa155b60fd60c3fb8ac2' -H 'Accept: application/vnd.github.raw') prints identical. A fixture has no production caller.
  • guide/promotion.md documents the block (seam): grep -n 'metadata block' guide/promotion.md hits lines 66 and 94. Docs have no production caller.

The full suite, bun run typecheck && bun test, passed in this worktree at the commit being pushed: tsc --noEmit clean, then 1746 pass, 3 skip, 0 fail across 1749 tests in 89 files.

Not Independently Verified

  • The reproducer didn't run the full suite in the real tree for the final repair round, so I ran it myself, as shown above. Its earlier full run used a copy with .git removed, where test/cli/discover.test.ts "discover leaves the repo byte-identical" fails on git status alone.
  • No command checks the wording of the two new code comments in src/core/promote.ts.

Plan Concerns

  • The plan put the contract comment on promote.ts:102, but that line belongs to datedTitle. The comment went on the new META_LINE constant above readEntry instead.
  • The plan's SOURCE.txt was hard-wrapped. It ships as one line per paragraph.
  • The "Commit the Stamp" sentence in the guide covers ADR files as well as entries, so it was reworded to keep both cases rather than replaced.

Left Out

  • When prose sits directly under the heading or the block, the stamp lands with no blank line before the prose. That matches the behavior before this change, which the acceptance criteria asked to keep.
  • META_LINE runs three times per block line and spells the key list twice. All three forms are tested, so a key added to one alternation and missed in the other fails a test.
  • Reading status or superseded_by into the Proposal's frontmatter, H3 entries, and the promoted_from anchor slug are out of scope per the issue's Non-Goals.

…roposal

working-memory-kit plans to put `status:`, `date:` and `superseded_by:` lines directly under each entry heading. `readEntry` copied the whole section into the Proposal body, so those lines would land in the Bank. It also took any `promoted_to:` line anywhere in the section as the stamp, which missed `- promoted_to:` and `**promoted_to:**` and refused an entry whose prose happened to start a line with the key.

`readEntry` now reads the run of lines under the heading whose key is one of `status`, `date`, `superseded_by` or `promoted_to`, in plain, list or bold form. That block stays out of the body, a stamp counts only inside it, and the new stamp goes in as the block's last line so the blank line before the prose survives. The key set is closed, so `owner: x` stays body text. An entry with no block reads and stamps as before. Refs #176.
…sionLog template

Nothing tested `ltm promote --entry` against the log working-memory-kit ships, only a hand-written fixture. `fixtures/working-memory-kit/decisionLog.md` is a byte-identical copy of its `template/_working-memory/decisionLog.md`, with the source commit in `SOURCE.txt` beside it so a refresh can be checked with a plain diff. The CLI test confirms the template's fenced example isn't an entry, then fills in an entry with a metadata block and checks that the block stays out of the Record body and the stamp lands after `date:`. With the old `readEntry` the test fails on `status: accepted` in the body. Refs #176.
…osition

The guide said the stamp goes right under the heading and that any `promoted_to` refuses an entry, which stopped being true with the metadata block. It now names the four keys and three line forms, says the block stays out of the Proposal, and shows the stamp as the block's last line. It also says which headings `--entry` matches by bare title. Refs #176.
…colon

The metadata-block rewrite made META_LINE require `: ` after the key. Before it, readEntry found the stamp with `startsWith("promoted_to:")`, so a hand-typed `promoted_to:acme:x` under the heading blocked a second Promotion. The plain and list forms now accept the key with or without a space, and a new readEntry test covers the no-space and bare-key cases. Also rewords the META_LINE and fence comments.
@kendrick

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 10, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-10T21:56:29.100714Z 5ac0a8f Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. You're on a roll.

Reviewed commit: 5ac0a8f599

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@kendrick
kendrick merged commit 5b9e128 into main Nov 10, 2026
16 checks passed
@kendrick
kendrick deleted the issue-176 branch November 10, 2026 21:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ltm promote --entry: metadata lines under a decision-log heading leak into the Proposal, and a styled stamp allows a second promotion

1 participant