diff --git a/docs/hygiene-history/ticks/2026/05/14/0034Z.md b/docs/hygiene-history/ticks/2026/05/14/0034Z.md new file mode 100644 index 0000000000..958f1795cf --- /dev/null +++ b/docs/hygiene-history/ticks/2026/05/14/0034Z.md @@ -0,0 +1,95 @@ +--- +tick: 2026-05-14T00:34Z +agent: otto-cli +session: cron-resumption +pr: 3066 +--- + +# Tick 0034Z — B-0451 procedure memory file landed as durable substrate + +## Refresh + +- CronList: live (`9ac08520`, `* * * * *`, sentinel `<>`). +- PR #3061 (Lior shadow log for Riven) merged → `916ca03`. +- My 3 in-flight PRs (#3056, #3058, #3065) — all `wait-ci` after + previous tick's `update-branch` calls. + +## Speculative work picked + +Per never-be-idle + previous tick's restraint discipline ("don't +multiply rate-limit pressure"): capture the per-collision renumber +procedure as a durable memory file. Low CI cost, high future-Otto +value, makes the remaining B-0451 work pick-up-able by any agent +without session context. + +## Landed concretely + +| Artifact | Where | What | +|---|---|---| +| `memory/feedback_b0451_per_collision_renumber_procedure_external_references_rule_trumps_first_merged_2026_05_14.md` | PR #3066 `575bd7f` | 242-line procedure memo: carved sentence + 9-step recipe + 3 substrate-honest pitfalls + remaining-work map | + +## Procedure captured (summary) + +The 9-step recipe distills the pattern that emerged from this +session's 5 collision-resolution PRs (PR-3053, PR-3057, PR-3058, +PR-3065, PR-3056). Key learnings: + +1. **External-references trumps first-merged-wins.** PR #3057 is + the canonical example — kept Riven's LATER row because sibling + rows referenced it. PR #3065 is a different pattern: the + re-check step — initial analysis pointed wrong (keep P2), but + first-merged-wins, external-refs, and status-precedence all + converged on keeping P1. The procedure value is recognizing + when rules agree vs disagree. +2. **Batch connected components in one PR.** B-0090.x and + B-0370-0373 each formed dependency chains; renumbering them + as a unit preserves the chain atomically. +3. **Worktree isolation prevents multi-Otto split-brain.** Every + cleanup PR this session opened in `/tmp/zeta-` rather + than the main checkout. + +## Substrate-honest pitfalls documented + +| Pitfall | Symptom | Resolution | +|---|---|---| +| BLOCKED ≠ failed CI | `mergeStateStatus: BLOCKED` with required checks green | `gh api -X PUT pulls/{N}/update-branch` | +| Initial analysis points wrong | First read keeps "wrong" set; reviewer or re-examination flips it | Re-check timeline + external refs before committing to a renumber direction | +| Collisions hide for weeks | B-0444 survived 9 PR landings; B-0370 survived 4 days | Run audit BEFORE filing new rows, not after | + +## Remaining work (3 collision groups) + +After PRs #3058 + #3065 land, the audit will report: + +- `B-0409` (3-way: wallet-immune P1 + amara-persona-bootstrap P2 + peer-call-ts-audit P2) +- `B-0410` (amara-ts-core P2 + peer-call-persona-loader P2) +- `B-0411` (amara-ts-readme-courier P2 + grok-ts-persona-flag P2) + +All 2026-05-11 within-priority decomposition races. ~5-10 min each +following the documented procedure. Future tick or future-Otto can +clear them. + +## Restraint discipline (continued) + +Did NOT open another cleanup PR this tick. The reasoning from tick +0024Z still holds: 3 PRs in flight, 25+ jobs each on a shared +installation rate-limit quota. The memory file is a durable +alternative that makes future cleanups cheap without burning CI. + +## CronList + +- Live (`9ac08520`, `* * * * *`). + +## Visibility signal + +Session arc (15 PRs landed + 4 in flight): + +| PR | What | Status | +|---|---|---| +| 15 substrate-hygiene PRs through #3057 + #3061 + #3062 | — | MERGED | +| #3052 | Competing collision fix | CLOSED | +| #3056 | Audit tool + B-0451 (branch-updated) | wait-ci | +| #3058 | B-0090.x batch (branch-updated) | wait-ci | +| #3065 | B-0370-0373 P2 renumber (branch-updated) | wait-ci | +| #3066 | B-0451 procedure memory (this tick) | wait-ci | + +Real-dependency-wait. Loop continues. diff --git a/docs/hygiene-history/ticks/2026/05/14/0054Z.md b/docs/hygiene-history/ticks/2026/05/14/0054Z.md new file mode 100644 index 0000000000..b4c786e110 --- /dev/null +++ b/docs/hygiene-history/ticks/2026/05/14/0054Z.md @@ -0,0 +1,76 @@ +--- +tick: 2026-05-14T00:54Z +agent: otto-cli +session: cron-resumption +pr: 3066 +--- + +# Tick 0054Z — PR #3066 Copilot review caught 4 real issues + +## Refresh + +- CronList: live (`9ac08520`, `* * * * *`, sentinel `<>`). +- Main: unchanged from previous tick. +- Open of mine: #3065 (wait-ci on prior tick's MD032 fixes), #3066 + (Copilot reviewed; 2 threads + 2 CI failures surfaced). + +## Named-dependency action + +PR #3066 surfaced 4 issues — addressable directly this tick (not +Holding). 2 reviewer threads + 2 CI failures, all on the procedure +memory file or its adjacent 0034Z tick shard. + +## The 4 issues + +| # | Type | Where | Issue | +|---|---|---|---| +| 1 | CI | check-memory-frontmatter-completeness | Memory file missing required `created:` field (workflow checks name/description/type/created) | +| 2 | CI | lint (markdownlint) | `0034Z.md:35` — line starts with `#3056)` which is parsed as malformed MD018 ATX heading | +| 3 | Copilot | memory file:20 | Procedure references audit tool + B-0451 but branch is behind main (false positive — both ARE on origin/main now) | +| 4 | Copilot | memory file:40 | Procedure's grep recipe treats `B-0XXX` as regex; for sub-row IDs like `B-0068.1` the dot matches any char. Also `\b` is GNU-grep, not portable to macOS BSD grep | + +## Landed concretely + +| Artifact | Where | What | +|---|---|---| +| Memory file frontmatter | PR #3066 `21dc986` | Added `created: 2026-05-14` (4th required field) | +| `0034Z.md:35` | PR #3066 `21dc986` | Reworded `#3056)` → `PR-3056` style to avoid MD018 hazard | +| Memory file body §2 | PR #3066 `21dc986` | Replaced `grep -rn "B-0XXX\b"` recipe with `grep -rnF "B-0XXX"` + portable `[^0-9.]\|$` word-boundary filter; documented dotted sub-row case separately | +| Both Copilot threads | PR #3066 GraphQL | Resolved via `resolveReviewThread` mutation | + +## Substrate-honest observation + +The grep-recipe bug is meta-substrate: the procedure memo whose +purpose is to formalize the cleanup discipline ITSELF contained a +non-portable grep pattern that would mislead future agents. Codex +caught it pre-merge — exactly what the round-2 reviewer pattern +documented in PR #3043 + #3056 was designed to enable. + +Five MD-class lint findings this session (4× MD032 + 1× MD018) +strengthen B-0456's case (file already opened in PR #3065). The +mechanization will catch BOTH classes. + +## Branch-update race + +Tried `gh api -X PUT pulls/3066/update-branch` after the commit +push. GitHub returned `422: expected head sha didn't match current +head ref` — the push had already advanced the branch tip past the +SHA the update-branch API expected. Not blocking: the push itself +brings the branch to the new tip; CI re-runs on the new commit. + +## CronList + +- Live (`9ac08520`, `* * * * *`). + +## Visibility signal + +Session arc (18 PRs merged + 2 in flight): + +| PR | What | Status | +|---|---|---| +| 18 substrate-hygiene PRs | — | MERGED | +| #3052 | Competing collision fix | CLOSED | +| #3065 | B-0370-0373 P2 renumber + 2 MD032 + B-0456 row | wait-ci | +| #3066 | B-0451 procedure (4-issue fix: frontmatter + MD018 + grep recipe + 2 threads) | wait-ci | + +Real-dependency-wait. Loop continues. diff --git a/docs/hygiene-history/ticks/2026/05/14/0110Z.md b/docs/hygiene-history/ticks/2026/05/14/0110Z.md new file mode 100644 index 0000000000..5a5ed055c1 --- /dev/null +++ b/docs/hygiene-history/ticks/2026/05/14/0110Z.md @@ -0,0 +1,82 @@ +--- +tick: 2026-05-14T01:10Z +agent: otto-cli +session: cron-resumption +pr: 3066 +--- + +# Tick 0110Z — 7th markdown lint finding (MD056); meta-recursion observation + +## Refresh + +- CronList: live (`9ac08520`, `* * * * *`, sentinel `<>`). +- Main unchanged since previous tick. +- Open of mine: #3065 (wait-ci after MD038 fix), #3066 (hit new + MD056 this tick). + +## Named-dependency action + +PR #3066 surfaced markdownlint failure on `0054Z.md:38`: + +``` +MD056/table-column-count Table column count [Expected: 3; Actual: 4] +``` + +The 0054Z tick shard described PR #3066's grep-recipe fix using +the regex `[^0-9.]|$` in a markdown-table cell. The unescaped pipe +was interpreted as a column separator, producing a 4-column row +in a 3-column table. Real-dependency-action, not Holding. + +## Landed concretely + +| Artifact | Where | What | +|---|---|---| +| `0054Z.md:38` | PR #3066 `5156433` | Escaped pipe in regex cell-boundary filter (MD056 fix) | + +## Recurrence count update + +| Rule | Occurrences | Class | +|---|---|---| +| MD032 (blanks-around-lists) | 4 | discipline-in-head | +| MD018 (no-missing-space-atx) | 1 | line-start `#NNN` parsed as heading | +| MD038 (no-space-in-code) | 1 | trailing-space inside code span | +| MD056 (table-column-count) | 1 | unescaped pipe in cell content | + +**Total: 7 markdown findings across this session.** + +## Meta-recursion observation + +The 0054Z shard described PR #3066's MD018 + grep-portability fixes. +That shard ITSELF hit MD056 because the grep recipe contained a +pipe character. Pattern: each tick shard that describes a markdown +lint fix has tended to introduce its own markdown lint finding +that the original fix didn't anticipate. + +The pattern is recursive: + +- PR #3044 fixed MD032 on 2228Z shard; its commit shard cleanly +- PR #3058 hit MD032 on 2348Z (a NEW shard); + PR #3065 (next batch) THEN hit MD032 on 0017Z + 0024Z +- PR #3066 fixed MD018 on 0034Z; its commit shard (0054Z) hit MD056 + +The future B-0456 mechanization needs to catch markdown lint +findings BEFORE push — running CI catches them but each catch +introduces a new tick shard that may itself have findings. The +break-even-on-mechanization analysis from tick 0100Z (occurrence 10) moved another step closer (now at occurrence #7). + +## CronList + +- Live (`9ac08520`, `* * * * *`). + +## Visibility signal + +Session arc (19 PRs merged + 2 in flight): + +| PR | What | Status | +|---|---|---| +| 19 substrate-hygiene PRs | — | MERGED | +| #3052 | Competing collision fix | CLOSED | +| #3065 | B-0370-0373 P2 renumber + multi-lint fix | wait-ci | +| #3066 | B-0451 procedure memo + MD018 + grep-recipe + MD056 fix | wait-ci | + +Real-dependency-wait. Loop continues. diff --git a/docs/hygiene-history/ticks/2026/05/14/0125Z.md b/docs/hygiene-history/ticks/2026/05/14/0125Z.md new file mode 100644 index 0000000000..1af22c7704 --- /dev/null +++ b/docs/hygiene-history/ticks/2026/05/14/0125Z.md @@ -0,0 +1,75 @@ +--- +tick: 2026-05-14T01:25Z +agent: otto-cli +session: cron-resumption +pr: 3066 +--- + +# Tick 0125Z — Copilot round-3 caught a real precedent overstatement + +## Refresh + +- CronList: live (9ac08520, `* * * * *`, sentinel autonomous-loop). +- Main unchanged. +- Open of mine: 3065 (wait-ci), 3066 (3 new Copilot threads + lint + failure this tick). + +## Named-dependency action + +PR 3066 round-3 review surfaced 3 unresolved threads. Triaged: + +| Thread | Finding | Real or stale? | +|---|---|---| +| 22 | branch-behind: audit tool + B-0451 row not visible | mechanical (PR 3056 has merged; update-branch needed) | +| 49 | sub-row grep word-boundary still missing | stale view (the fix is in place on line 52 since round-2 commit fa687ab) | +| 76 | PR 3065 framed as bending first-merged-wins | real correction (PR 3065 KEPT the earlier row; rules agreed) | + +The line-76 thread was the substantive catch. The memo described +PR 3065 as bending first-merged-wins like PR 3057, but PR 3065 +actually kept the P1 set (filed 2 days EARLIER than P2). That +aligns with first-merged-wins; both rules pointed the same +direction. + +## Landed concretely + +| Artifact | Where | What | +|---|---|---| +| memo lines 80-89 (substantive) | PR 3066 7cbd782 | Reframed PR 3065 as the re-check pattern (both rules agreed), reserved PR 3057 as the canonical rule-bend example | +| Thread 22 (branch-behind) | resolved + update-branch | Triggered `PUT pulls/3066/update-branch`; audit tool + B-0451 now visible to new CI run | +| Thread 49 (sub-row recipe) | resolved | Fix in place since round-2 (line 52 has portable word-boundary filter); commit message documents stale-view diagnosis | +| Thread 76 (precedent) | resolved | Memo correction landed in 7cbd782 | + +## Substrate-honest correction history + +The PR 3065 framing has now been corrected twice in the session arc: + +1. The 0017Z tick shard's initial analysis pointed at "keep P2 set + (B-0092 parent body describes them as canonical decomposition)" +2. Re-examination (same shard) reversed to "keep P1 set" because + P1 was earlier + has external refs +3. The procedure memo (this PR 3066) then claimed PR 3065 "bent + first-merged-wins" — that was wrong; both rules agreed +4. THIS tick's commit corrected the memo to describe PR 3065 as + the re-check pattern instead + +The substrate captures the trajectory of the correction across +multiple PRs. Future agents reading the procedure see both the +canonical rule-bend (PR 3057) AND the re-check pattern that +catches when first-instinct doesn't match the rules. + +## CronList + +- Live (9ac08520, `* * * * *`). + +## Visibility signal + +Session arc (19 PRs merged + 2 in flight): + +| PR | What | Status | +|---|---|---| +| 19 substrate-hygiene PRs | --- | MERGED | +| 3052 | Competing collision fix | CLOSED | +| 3065 | B-0370-0373 P2 renumber + multi-lint fix + B-0456 row | wait-ci | +| 3066 | B-0451 procedure + 3 Copilot rounds (4+2+3 threads = 9 resolved) | wait-ci, threads-clear, branch-updated | + +Real-dependency-wait. Loop continues. diff --git a/docs/hygiene-history/ticks/2026/05/14/0140Z.md b/docs/hygiene-history/ticks/2026/05/14/0140Z.md new file mode 100644 index 0000000000..b85c9e5aac --- /dev/null +++ b/docs/hygiene-history/ticks/2026/05/14/0140Z.md @@ -0,0 +1,76 @@ +--- +tick: 2026-05-14T01:40Z +agent: otto-cli +session: cron-resumption +pr: 3066 +--- + +# Tick 0140Z — Copilot round-4 on PR #3066 (cross-document consistency) + +## Refresh + +- CronList: live (job 9ac08520, every minute, sentinel autonomous-loop). +- Main unchanged since previous tick. +- Open of mine: PR 3065 (wait-ci after MD038 trio fixed), + PR 3066 (round-4 Copilot threads this tick). + +## Named-dependency action + +PR 3066 round-4 review surfaced 2 unresolved threads. Triaged: + +- Thread on memo line 3 (frontmatter): description said + "shipped 2026-05-13/14" but PR 3065 is still in flight. + Reworded to "in-flight PR #3065". +- Thread on 0034Z line 39: tick shard still had the + pre-round-3 framing of "PR #3057 + PR #3065 both bent + first-merged-wins" that the round-3 memo correction + replaced. Propagated the correction to the shard. + +Both real (cross-document consistency issues). + +## Landed concretely + +| Artifact | Where | What | +|---|---|---| +| Memo frontmatter description | PR 3066 commit dce450e | "shipped" → "in-flight PR #3065" | +| 0034Z shard "Key learnings" item 1 | PR 3066 commit dce450e | Reframed PR 3057 as canonical rule-bend; PR 3065 as re-check pattern (matches memo round-3 correction) | +| 2 review threads | PR 3066 GraphQL | Resolved via mutation | + +## Round-4 finding class observation + +The previous three rounds caught different finding classes: + +- Round 1: factual bugs (grep regex portability) +- Round 2: semantic bugs (sub-row word-boundary, MD018) +- Round 3: interpretive overstatement (PR 3065 framing) +- Round 4: cross-document consistency (round-3 correction + didn't propagate to the frontmatter description and + to the related tick shard) + +The pattern: as the document stabilizes, the findings shift +from "wrong content" to "consistency drift between updates." +Round-4 catches what fix-in-one-place misses. + +The mechanization that would catch round-4 class findings is +harder than markdown lint — it's cross-document consistency +(claim about PR-N status here should match claim about PR-N +status there). That's a much higher-cost helper than B-0456. +Out of scope for the current sweep; captured as a future +follow-up if the pattern recurs in other PRs. + +## CronList + +- Live (job 9ac08520, every minute). + +## Visibility signal + +Session arc (19 PRs merged + 2 in flight): + +| PR | What | Status | +|---|---|---| +| 19 substrate-hygiene PRs | --- | MERGED | +| 3052 | Competing collision fix | CLOSED | +| 3065 | B-0370-0373 P2 renumber + multi-lint fix + B-0456 row | wait-ci | +| 3066 | B-0451 procedure (round-4: 11 cumulative threads resolved) | wait-ci, threads-clear | + +Real-dependency-wait. Loop continues. diff --git a/docs/hygiene-history/ticks/2026/05/14/0153Z.md b/docs/hygiene-history/ticks/2026/05/14/0153Z.md new file mode 100644 index 0000000000..a69e8ce8c4 --- /dev/null +++ b/docs/hygiene-history/ticks/2026/05/14/0153Z.md @@ -0,0 +1,106 @@ +--- +tick: 2026-05-14T01:53Z +agent: otto-cli +session: cron-resumption +pr: 3066 +--- + +# Tick 0153Z — PR #3065 merged; substrate-hygiene cascade close-out + +## Refresh + +- CronList: live (job 9ac08520, every minute, sentinel autonomous-loop). +- PR 3065 (B-0370-0373 P2 renumber + B-0456 row + multi-lint fixes) + MERGED to main, commit 02d75a3. +- Only PR 3066 (B-0451 procedure memory) remains of mine. Status: + wait-ci, threads-clear, auto-merge armed. + +## Substrate state verified + +``` +$ bun tools/bg/audit-duplicate-row-ids.ts +audit-duplicate-row-ids: 3 duplicate-ID group(s) found across 561 rows with id field: + B-0409: 3-way collision (wallet-immune P1 + amara-persona-bootstrap P2 + peer-call-ts-audit P2) + B-0410: 2-way (amara-ts-core P2 + peer-call-persona-loader P2) + B-0411: 2-way (amara-ts-readme-courier P2 + grok-ts-persona-flag P2) +``` + +**Down from 12 groups at session start to 3 remaining.** 9 groups +resolved this session via 4 PRs: + +- PR 3053 (B-0444) merged +- PR 3057 (B-0068.1) merged +- PR 3058 (B-0090.x batch of 4) merged +- PR 3065 (B-0370-0373 batch of 4) merged + +## Session arc summary + +20 PRs merged this session (substrate-hygiene focus): + +| Cluster | PRs | +|---|---| +| ID collision resolution | 3053, 3057, 3058, 3065 | +| Audit tool + cleanup row | 3056 (audit + B-0451) | +| Lost-row recovery | 3044 (B-0257..B-0261, B-0289) | +| Stale-reference cleanup | 3048 (Rule 0), 3050 (otto-channels card) | +| Slice-row restoration | 3047 (B-0054.1, B-0055.1) | +| Initial B-0444 work | 3043 (worktree field) | +| Notifier parser fix | 3045 (YAML inline-comment) | +| Otto-Desktop substrate | 3041, 3042, 3046, 3054, 3055, 3059, 3061, 3062, 3064 | +| Lior bug fixes | 3049 | +| Codex provenance | 3051 | + +Plus 1 PR closed-with-provenance (3052) and 1 still in flight (3066). + +## Remaining work (pick-up-able) + +3 collision groups remain: + +- B-0409 (3-way) — wallet-immune P1 vs Riven amara P2 vs peer-call-ts P2 +- B-0410 — Riven amara P2 vs peer-call-persona-loader P2 +- B-0411 — Riven amara P2 vs grok-ts-persona-flag P2 + +All 2026-05-11 within-priority decomposition races (pre-claim-acquire-rule). +Each takes 5-10 minutes following the procedure memo (in PR 3066). + +Plus B-0456 (P2): mechanize markdown-lint pre-check. Now has a +12-finding corpus from this session forming the regression suite. + +## Restraint discipline (close-out) + +Declined to pick another cleanup PR this tick: + +1. PR 3066 procedure memo is the durable substrate that makes + future picks-up cheap. It needs to land cleanly. +2. The session arc has metabolized 20 PR landings. Extending + further has diminishing marginal value vs the maintainer + eye load and rate-limit pressure. +3. The 3 remaining cleanups + B-0456 build are pick-up-able by + any future tick via the procedure memo + B-0451 row. + +Substrate-or-it-didn't-happen check: the procedure memo captures +the procedure, B-0451 captures the remaining work scope, B-0456 +captures the mechanization gap. All durable. Future-Otto resumes +from any of these without context. + +## CronList + +- Live (job 9ac08520, every minute). + +## Visibility signal + +Session arc final state: + +| Metric | Value | +|---|---| +| PRs merged (mine) | 11 in the substrate-hygiene cascade | +| PRs merged (Otto-Desktop + Lior + Codex) | 9 | +| PRs closed-with-provenance | 1 (#3052) | +| PRs in flight | 1 (#3066, wait-ci) | +| Duplicate-ID groups on main | 12 → 3 | +| Dangling-dep refs on main | 9 → 0 (resolved earlier this session) | +| Markdown findings caught + fixed | 12 (4 rule classes) | +| Copilot review rounds metabolized | 4 on PR #3066 | +| Cron sentinel | 9ac08520 live | + +Real-dependency-wait on PR 3066 CI. Loop continues. diff --git a/memory/feedback_b0451_per_collision_renumber_procedure_external_references_rule_trumps_first_merged_2026_05_14.md b/memory/feedback_b0451_per_collision_renumber_procedure_external_references_rule_trumps_first_merged_2026_05_14.md new file mode 100644 index 0000000000..f526bf9f66 --- /dev/null +++ b/memory/feedback_b0451_per_collision_renumber_procedure_external_references_rule_trumps_first_merged_2026_05_14.md @@ -0,0 +1,274 @@ +--- +name: B-0451 per-collision renumber procedure +description: Procedure for resolving duplicate-row-ID collisions in docs/backlog/ surfaced by tools/bg/audit-duplicate-row-ids.ts. Captures the external-references-rule precedence, the connected-component batching pattern, and the worktree-isolation gotcha. Derived from PRs #3053 (B-0444, merged), #3057 (B-0068.1, merged), #3058 (B-0090.x batch, merged), and the in-flight PR #3065 (B-0370-0373 batch) at session 2026-05-13/14. +type: feedback +created: 2026-05-14 +--- + +# B-0451 per-collision renumber procedure + +## Carved sentence + +> External-references-rule trumps first-merged-wins. The row that +> would cost more to migrate keeps its ID, regardless of who filed +> first. Batch connected components (parent + dependency chain) in +> one PR. + +## When this procedure applies + +When `bun tools/bg/audit-duplicate-row-ids.ts` (shipped via PR +#3056) reports `N duplicate-ID group(s) found across M rows with +id field`. Each group is a set of 2+ files claiming the same +frontmatter `id:` value. + +## Per-collision procedure (the 9-step recipe) + +### 1. Identify the colliding rows + +```bash +bun tools/bg/audit-duplicate-row-ids.ts +``` + +Lists every duplicate group with file paths. Pick one group to +resolve per PR (or batch a connected component, see step 7). + +### 2. Find external references to each row + +For each colliding row, grep for its bare ID. Use **fixed-string +match** (`-F`) so dotted sub-row IDs like `B-0068.1` aren't treated +as regex (where `.` matches any character): + +```bash +# Whole-row ID like B-0444 (no dot) +grep -rnF "B-0XXX" docs/ memory/ tools/ .claude/ \ + | grep -vE "docs/backlog/.*/B-0XXX-" \ + | grep -E "B-0XXX([^0-9.]|$)" # word-boundary substitute (portable) + +# Sub-row ID like B-0068.1 — fixed-string for the literal, then +# portable word-boundary filter (with escaped dot in the regex) +# so a search for B-0068.1 doesn't also match B-0068.10 or B-0068.1.1. +grep -rnF "B-0068.1" docs/ memory/ tools/ .claude/ \ + | grep -vE "docs/backlog/.*/B-0068\\.1-" \ + | grep -E "B-0068\\.1([^0-9.]|$)" # word-boundary on the renumbered ID +``` + +(The `\b` word-boundary GNU-extension is not portable to BSD grep +on macOS; the `[^0-9.]|$` filter is the portable equivalent. The +trailing filter on the sub-row case prevents matching +`B-0068.10` / `B-0068.1.1` when searching for `B-0068.1`. Codex / +Copilot round-2 caught both issues in PR #3066 review.) + +Note where each row is referenced externally: +- Parent row's body (e.g., `## Decomposition` section listing children) +- Sibling rows' `depends_on:` / `composes_with:` / `children:` +- Memory files (`memory/feedback_*.md`) +- PR history docs (`docs/history/pr-reviews/`) +- Already-merged commit messages (search `git log`) +- BACKLOG.md (always — regenerated from rows) + +### 3. Apply the precedence rules (in order) + +1. **External-references rule**: KEEP the row with more external + references (especially uneditable ones: PR commit messages, + memory files outside this PR). Renumber the orphan(s). +2. **First-merged-wins**: if external-references are tied or both + uneditable, keep the earlier-filed row. +3. **Status-precedence**: if one row is `status: closed` and others + are open, keep the closed one (its work shipped; the ID is + baked into commits). + +The external-references rule trumps first-merged-wins. PR #3057 +is the canonical example of this rule bending the temporal order: + +- PR #3057 (B-0068.1): kept Riven's LATER row because B-0068.2/ + B-0068.3 sibling rows reference it; renumbered Aaron's EARLIER + but orphan row. External-references actively overrode + first-merged-wins here. + +PR #3065 (B-0370-0373) is a different case — both rules pointed +the SAME direction (keep the P1 set): + +- First-merged-wins: P1 set was filed 2 days earlier (PR #2269 + on 2026-05-09 vs PR #2683 on 2026-05-11). +- External-references: B-0370 + B-0373 (P1) are shipped, with + references in PR-history doc + memory file. +- Status-precedence: B-0370 + B-0373 (P1) are `status: closed` + effectively. + +PR #3065 is therefore the re-check pattern (substrate-honest +re-examination after initial-analysis pointed at keeping P2 +because of its parent body description). The procedure's value +isn't just mechanical — it's the re-examination step that catches +when the first instinct doesn't match all the rules. + +### 4. Pick next-free IDs + +```bash +git ls-tree -r origin/main -- docs/backlog/ \ + | grep -oE "B-04[0-9]{2}" \ + | sort -u \ + | tail -10 +``` + +Also check open PRs that claim B-04XX IDs to avoid claim-collision: + +```bash +gh pr list --state open --json title \ + | grep -oE "B-04[0-9]{2}" +``` + +For sub-row collisions (`B-0090.M`), the new IDs are next-free +within the parent's series (e.g., `B-0090.5..B-0090.8`). + +### 5. Create isolated worktree (multi-Otto split-brain prevention) + +```bash +git worktree add /tmp/zeta- \ + -b fix/-2026-MM-DD origin/main +``` + +The main checkout may be raced by parallel Otto instances (see +PR #3043 + #3057 tick shards). Worktree isolation prevents working- +tree resets. + +### 6. Rename files + edit frontmatter + +For each renumbered row: + +```bash +cd /tmp/zeta- +git mv docs/backlog/PX/B-OLD-.md docs/backlog/PX/B-NEW-.md +``` + +Edit the renamed file's frontmatter to: + +- Change `id: B-OLD` → `id: B-NEW` +- Update body H1 title (`# B-OLD —` → `# B-NEW — (renumbered from B-OLD)`) +- Add `renumbered_from: B-OLD` field +- Add `renumbered_reason: "..."` explaining the collision + which + row kept the original ID + reference to the B-0451 sweep +- Bump `last_updated:` to today +- Add `renumbered` to `tags:` + +### 7. Batch connected components + +If the renumbered rows have an internal `depends_on:` chain (e.g., +B-0090.2/.3/.4 all `depends_on: [B-0090.1]`), batch the WHOLE chain +in one PR: + +- Renumbering them serially would require either temporary chain + breakage (bad) or two-step chain updates (verbose) +- Batching does the chain remap as a single atomic operation +- Pattern: when collisions form a connected component of the + dependency graph, fix the whole component in one PR + +Update internal `depends_on:` / `composes_with:` in each +renumbered row to reflect new IDs. + +### 8. Update parent row's body + regen index + +If the renumbered set is a parent's decomposition (e.g., B-0092 → +B-0370..B-0373), update the parent body's `## Decomposition` +section to list the new IDs. + +Always regenerate `docs/BACKLOG.md`: + +```bash +BACKLOG_WRITE_FORCE=1 bun tools/backlog/generate-index.ts +``` + +### 9. Verify + commit + push + PR + +```bash +bun tools/bg/audit-duplicate-row-ids.ts +# Confirm dup-group count dropped by exactly the number of groups resolved. +``` + +Commit message template (copy from PR #3065's body): + +``` +fix(backlog): resolve B-0XXX ID collision — renumber → B-NNNN + +Per the B-0451 sweep. + +## The collisions +| ID | Earlier filer | Later filer | +|---|---|---| +| ... | + +## Resolution +Per external-references-rule + first-merged-wins: +- +→ Keep ; renumber to next-free B-NNNN.. + +## Empirical effect +Duplicate-ID groups: N → N-K +``` + +Open PR with auto-merge armed: + +```bash +gh pr create --title "..." --body "..." +gh pr merge --auto --squash +``` + +## Procedure pitfalls (substrate-honest) + +### Pitfall 1: BLOCKED state ≠ failed CI + +After the PR opens, `gh pr view --json mergeStateStatus` may show +`BLOCKED` even when required checks are green. The cause is usually +"branch is out-of-date with main." Resolve via: + +```bash +gh api -X PUT repos/Lucent-Financial-Group/Zeta/pulls//update-branch +``` + +(per PR #3056 tick 0024Z shard — installation-level rate-limit on +the `gate` workflow's `mise` toolchain installer can show as 8 lint +failures that are ALL `non-required check failed`; required checks +remain green; branch-update + auto-merge retry resolves.) + +### Pitfall 2: Initial analysis can point wrong direction + +PR #3065's first read said "keep P2 set (described by B-0092 +parent body)." Re-checking the timeline + status-precedence flipped +to "keep P1 set (filed 2 days earlier, B-0370 + B-0373 shipped, +external references in PR-history + memory)." The substrate-honest +discipline is: when external-references and first-merged-wins +disagree, RE-EXAMINE both before committing. + +### Pitfall 3: ID collisions hide in the substrate for weeks + +The B-0444 collision survived 9 PR landings before discovery +(PR #3053). The B-0370..B-0373 collisions survived 4 days. The +audit tool (PR #3056) breaks this recursion — run it BEFORE +filing a new row, not AFTER reviewers catch the collision. + +## Composes with + +- `tools/bg/audit-duplicate-row-ids.ts` (PR #3056) — the detector +- `docs/backlog/P1/B-0451-...md` (PR #3056) — the sweep parent row +- PR #3053 (B-0444 — first collision resolution, established precedent) +- PR #3057 (B-0068.1 — first per-collision cleanup, simple case) +- PR #3058 (B-0090.x — batched 4 collisions in one PR) +- PR #3065 (B-0370-0373 — batched 4 collisions + parent body update) +- `.claude/rules/claim-acquire-before-worktree-work.md` — multi-Otto + coordination discipline the audit tool addresses at the row layer +- `.claude/rules/honor-those-that-came-before.md` — recovery rather + than discard for orphaned prior work + +## Remaining work (as of 2026-05-14) + +After PRs #3058 + #3065 land, the audit tool will report **3 +remaining collision groups**: + +- `B-0409` (3-way: wallet-immune P1 vs amara-persona-bootstrap P2 vs + peer-call-ts-audit P2 — all 2026-05-11) +- `B-0410` (amara-ts-core P2 vs peer-call-persona-loader P2 — same date) +- `B-0411` (amara-ts-readme-courier P2 vs grok-ts-persona-flag P2 — same date) + +All three are 2026-05-11 within-priority decomposition races (pre- +claim-acquire-rule). Each takes ~5-10 minutes following this +procedure. A future tick can clear them in one batched PR if +desired, or three serial PRs for cleaner review.