Exhaustive triage of the closed tracker ersatztv#237, whose 111 comments server-management#642 excludes from MemPalace ingestion (over the per-file cap) and #520 removes from startup. #524's premise was that facts living only in those comments would be orphaned and need curating into lifecycle records. Result: zero decision-shaped orphans. Every durable decision-shaped fact is already held by the decision corpus or by the individual issue the comment narrates -- which the exporter does ingest. The tracker was always the lossy copy, because the session protocol required the fuller closing record on the worked issue first. - docs/decisions.md: new active record docs.tracker-comment-retrofit, leading with the reusable rule (check the worked issue BEFORE the decision corpus) and the consequence for #642's benchmark row, which has no valid subject. - docs/tracker-retrofit-triage-237.md: the audit trail -- method, per-comment classification of all 111, totals, and the one candidate raised and disproved (#497's Guids/Directors scope, stated more fully on its own issue). - docs/handoffs/chicorytv-issue-queue.md: sweeps the two genuinely orphaned LORE facts the triage surfaced (e2e-local.sh readiness probe hanging on a reused config dir; troubleshooting playback cannot exercise channel branding). - docs/README.md: index the new doc. The claim is deliberately narrow: no *decision-shaped* orphans. The lore bucket was classified but not coverage-checked, and it was not empty -- hence the sweep. fixes #524
12 KiB
Tracker-retrofit triage — ersatztv#237 (#524)
Evidence for the decision record docs.tracker-comment-retrofit (docs/decisions.md). That record
states the rule; this file is the audit trail — what was examined, by what method, and how every one
of the 111 comments was classified, so the result can be spot-checked or re-derived rather than taken
on trust.
Deliberately not under docs/decisions/: files there are parsed as decision records by
scripts/decisions_lib.py, and this is evidence, not a record. It is also not in
docs/decisions/migration-map.md, whose scope is the #521 legacy-heading→key migration.
Why the triage happened
server-management#642 exports Gitea issue history into MemPalace but excludes #237 — at 111 comments it exceeds the miner's per-file cap. #520 separately removed #237 from session startup. The worry behind #524: a durable fact living only in a #237 comment would become unretrievable, and should be curated into lifecycle records.
Method
- Corpus. All 111 comments pulled verbatim from the Gitea API with per-comment provenance (id,
author, timestamp, canonical URL). Nothing sampled — every comment read. The corpus itself is not
committed (that would be the 1:1 dump #524 rejects); to re-derive or spot-check a row below,
re-pull from the permanent primary source:
GET /api/v1/repos/timothy/ersatztv/issues/237/comments(returns all 111 in one call). - Three-way split (from #524), applied to each content item, since one session comment
routinely mixes all three:
- decision — a durable "we do X rather than Y because Z" that governs future behavior;
- issue-work — per-session/per-issue narration (closures, PR/CI numbers, review loops, claims), which already lives on the individual Gitea issue and is ingested from there;
- lore — a cross-cutting workflow/orchestration lesson, belonging in the handoff lore or MEMORY.
- Coverage test, applied to decision-shaped items only. Semantic candidate discovery against the
ErsatzTV-Decisions/ErsatzTV-Decisions-ArchiveMemPalace wings, then verification against canonical Markdown by exact search (docs/decisions.md,docs/decisions/*.md,docs/decisions/archive/*.md,docs/handoffs/chicorytv-issue-queue.md, convention docs). No item was declared covered on a semantic hit alone — MemPalace was discovery, never authority. - Worked-issue check. For any item surviving step 3, the issue the comment narrates was read live. This is the step that disposes of most apparent orphans.
Result
Decision-shaped orphans: zero. No durable decision-shaped fact exists only in #237.
Item-level counts from the coverage pass (units are items, so totals exceed 111 — one comment commonly carries a decision restatement, its issue-work narration, and a lore aside):
| Slice | Only-in-#237 | Already covered | Issue-work | Lore |
|---|---|---|---|---|
| comments 1–28 | 0 | 14 | 12 | 2 |
| comments 29–56 | 0 | 18 | 22 | 14 |
| comments 57–84 | 0 | 12 | 14 | 8 |
| comments 85–111 | 0 (1 raised, disproved) | 16 | 21 | 9 |
| total | 0 | 60 | 69 | 33 |
Comment-level primary class (units are comments; sums to exactly 111): decision 14, issue-work 88, lore 9.
The one candidate raised, and its disproof
Comment [106/111] (issues/237#issuecomment-14077) recorded #497's deliberate scope exclusions —
Guids (not eager-loaded by GetOrAdd, so reconciling would duplicate-insert every scan) and
Directors (not add-persisted for MusicVideoMetadata). Absent from the decision corpus, so it
survived the corpus check. It fails the worked-issue check: issue #497's comments 14013 and
14072 state both exclusions with fuller reasoning, and the exporter ingests that issue as
497.md. Category 2, not a retrofit target. (scan.musicvideo-reconciliation covers #494's
neighbouring reconcile decision but not this field-scope question — distinct records.)
Spot-checks verified independently
- #357, parked player-owned-playback architecture options — preserved on issue #357 itself (8
comments carrying the three paths and the no-go). Note the docs copy is not on
main:docs/player-owned-playback-poc.mdand thedocs/decisions.mdverdict entry exist only on the unmergedfeat/357-player-owned-playbackbranch, so the issue is currently the sole reachable home. Category 2 holds, but by a narrower margin than the other items. - #235 / PR #267, the
Unlock: isLastbatch fix — this was a fresh design decision (Fable prescribed lock-once-per-provider batching because the naive per-source fix would starve the 2nd+ media source; now live atSchedulerService.cs), not merely an application oflocking.entitylocker-atomic-flags. It is still category 2: issue #235 comment 8880 and PR #267 comment 8794 both carry the rationale.
Known gap in this triage
The 33 lore-class items were classified but not coverage-checked. That bucket is not empty: two
facts from comment [109/111] have no home anywhere and were swept into
docs/handoffs/chicorytv-issue-queue.md by this PR — the e2e-local.sh readiness-probe hang on a
reused config dir, and the troubleshooting playback API's inability to exercise channel branding. A
future tracker triage should coverage-check the lore bucket too, or state that it didn't.
Per-comment classification
primary-class is the single dominant class of each comment; durable home names where that
content lives (the worked issue, a doc, or ephemeral for queue bookkeeping with no durable home).
| # | primary-class | durable home |
|---|---|---|
| 1 | issue-work | #231–#235 filed (triage session); ephemeral |
| 2 | decision | triage-verdict rule (Blocker/High ⇒ priority pickup); decisions.md 2026-07-11 |
| 3 | issue-work | #213, #215, #217–#221, #236 |
| 4 | decision | tracker "Pending adversarial reviews" section; ephemeral (protocol) |
| 5 | issue-work | #126, #207, #212 closed; #230, #238 filed |
| 6 | issue-work | #231, #232 via PR #241 |
| 7 | lore | re-review the fix commit, not just the initial PR diff |
| 8 | issue-work | #244 via PR #249 (ChannelsScreen extraction) |
| 9 | issue-work | #233, #234 via PR #250; #235 remainder |
| 10 | issue-work | audit sweep filing #251–#254 |
| 11 | issue-work | arc/backlog triage for #251–#254; ephemeral |
| 12 | issue-work | #251, #252 via PR #258 |
| 13 | issue-work | adversarial-reviewer#24; #255–#257 triage |
| 14 | issue-work | #238, #243, #246 body edits; ephemeral |
| 15 | decision | #253 concurrency contract (Version, ETag, 412); optimistic-concurrency.md |
| 16 | decision | #204 redirect matcher; decisions.md spa.legacy-redirect-matcher |
| 17 | decision | #205, #206 auth posture + rollback tag; blazor-route-parity.md §5 |
| 18 | issue-work | #202, #255–#257 via PR #262; #264 filed |
| 19 | issue-work | #253 PR1 landed; #265 filed |
| 20 | lore | api-conventions.md §7a/§7b section-number collision on parallel merge |
| 21 | issue-work | #235 via PR #267 (F9 gate closed) |
| 22 | issue-work | #91b SPA pre-work PR #272; #271 filed |
| 23 | lore | Codex "interrupted" status unreliable; trust the rollout transcript |
| 24 | issue-work | #253 PR3 landed; #269 filed |
| 25 | decision | #91b Blazor removal; rollback tag blazor-final = bb310f6b |
| 26 | decision | #253 PR4 phasing — collapse Phase-1/428 flip, no separate soak release |
| 27 | issue-work | #197 decomposed into #280–#289; PR #279 Phase-0 |
| 28 | decision | #197 Bundle A — single API key, RequireKeyForReads default true |
| 29 | issue-work | #283, #294 |
| 30 | issue-work | #289 (PR #76) — ERSATZTV_ALLOW_WRITES posture |
| 31 | issue-work | #295 design ratified (OIDC + local login) |
| 32 | issue-work | #245–#247 (App.tsx modularization plan review) |
| 33 | issue-work | #287, #288 (Bundle C) |
| 34 | issue-work | #271 |
| 35 | issue-work | ephemeral (arc/tracker bookkeeping correction) |
| 36 | lore | queue-status-in-prose drift; docs/handoffs/chicorytv-issue-queue.md |
| 37 | issue-work | #295 PR1, #299, #301 |
| 38 | issue-work | #269 (500-exposure slice); #303 H6/H10 |
| 39 | decision | #303 H9 append-only + H3 root-png guard; release-ci-governance.md |
| 40 | decision | #303 H10 review-verdict merge gate; release-ci-governance.md |
| 41 | issue-work | #269 closed; #308 filed |
| 42 | decision | #311 H11 rebase-not-merge + scoped format gate; contributing.md |
| 43 | issue-work | #303 close-out; #314, #315 filed |
| 44 | issue-work | #317 merge-gate double-prompt fix; #314, #319 |
| 45 | issue-work | #295 PR2, #301; #320 filed |
| 46 | lore | merge-gate needs ETV_GITEA_BASICAUTH in env; #315 |
| 47 | issue-work | #319 (CSP / Permissions-Policy) |
| 48 | issue-work | #253, #265 closed |
| 49 | issue-work | #238 (TopBar primary action) |
| 50 | issue-work | #286 (/api/v1 versioning); #289 superseded |
| 51 | issue-work | #172 (scheduling API hardening); #327 filed |
| 52 | issue-work | #314 (authenticated security scan); #330 filed |
| 53 | issue-work | #197 go-live gate closed; #334 filed |
| 54 | issue-work | #335, #336 (v26.8.0 go-live) |
| 55 | issue-work | #237 tracker closure summary; ephemeral |
| 56 | issue-work | #330 (CORP header); #340 filed |
| 57 | issue-work | #333, #344–#346 |
| 58 | issue-work | #344, #347 |
| 59 | issue-work | #245, #350, #352 |
| 60 | issue-work | #354–#357 |
| 61 | issue-work | #357, #358 (player-owned playback PoC) |
| 62 | issue-work | #246, #359, #360 |
| 63 | issue-work | #243, #247 (modularization epic) |
| 64 | issue-work | #299, #363 |
| 65 | issue-work | #99, #333, #334 (queue-selection correction) |
| 66 | issue-work | #345, #357, #367 |
| 67 | issue-work | #99, #368 |
| 68 | decision | selector tier order; docs/handoffs/chicorytv-issue-queue.md |
| 69 | issue-work | #354, server-management#599 |
| 70 | decision | selector eligibility — exclude parked / closed milestones; #334, #371, #372 |
| 71 | lore | coverlet.runsettings Migrations exclusion (exit-137 OOM); #15, #373 |
| 72 | issue-work | #333, #344, #345 |
| 73 | lore | Gitea ?milestones= filter no-ops on :/+ names |
| 74 | issue-work | #70, #73, #77, #383–#386 |
| 75 | issue-work | #383–#386, #397 |
| 76 | issue-work | #72, #164, #401, #405, #414, #415, #417 |
| 77 | issue-work | #70, #376, #383, #385, #386, #419, #421 |
| 78 | issue-work | #176, #320, #385, #386, #423 |
| 79 | issue-work | #385, #386, #425, #428 |
| 80 | issue-work | #164, #385, #425, #430, #431 |
| 81 | issue-work | #386, #425, #432 |
| 82 | issue-work | #386, #425, #439 |
| 83 | issue-work | #308, #441; optimistic-concurrency.md |
| 84 | issue-work | #176, #293, #395, #425, #442 |
| 85 | issue-work | #363, #444, #445, #447 |
| 86 | issue-work | #350, #446 |
| 87 | issue-work | #381, #383, #395, #396, #448 |
| 88 | issue-work | #310, #327, #338, #367, #401, #409, #410, #447, #458 |
| 89 | issue-work | #350, #403, #404, #412, #431 |
| 90 | issue-work | #398, #420 (upload-artifact@v3 pin) |
| 91 | issue-work | #409, #458, #460 |
| 92 | issue-work | v26.10.0 release; #350, #461 (selector script) |
| 93 | issue-work | #404, #463, #464 |
| 94 | issue-work | #431, #469 |
| 95 | issue-work | #363, #444 |
| 96 | issue-work | #350, #472–#474, server-management#630 |
| 97 | lore | verify the emitter is deployed before trusting an empty log |
| 98 | issue-work | #350, #412, server-management#604 |
| 99 | issue-work | #476, #478 |
| 100 | issue-work | #478, server-management#631 |
| 101 | issue-work | #474, #477, #480, #484, #485 |
| 102 | issue-work | #350, #472 |
| 103 | issue-work | #381, #395, #480 |
| 104 | issue-work | #474, #487, #488, #491, #494 |
| 105 | issue-work | #494, #496 |
| 106 | issue-work | #497, #498, #500 (the raised-and-disproved candidate) |
| 107 | issue-work | #25, #67, #68, #502, #503 |
| 108 | issue-work | #350, #474, #487, #510, #511 |
| 109 | issue-work | #502, #510, #511 (+ the two lore facts swept to the handoff) |
| 110 | decision | retire #237 from startup; #520, #521, server-management#642 |
| 111 | lore | independent caps don't compose into a budget; #511 |