diff --git a/docs/queue-index.md b/docs/queue-index.md new file mode 100644 index 0000000..d2107c6 --- /dev/null +++ b/docs/queue-index.md @@ -0,0 +1,854 @@ +# The work queue's table of contents + +*Generated by `tools/queue_index.py --write`; do not hand-edit. `python3 tools/queue_index.py --check docs/queue-index.md` re-derives the parse and goes RED when this document and `WORK-QUEUE.md` disagree, which is what an append to the queue produces. Regenerate, commit, move on.* + +**What this is.** One row per `## ` section of `WORK-QUEUE.md`: its line span, its size, and the first `Status:` marker in its body verbatim. Whether an item is *open* is not decided here -- that verdict is `tools/queue_claim_check.py`'s, and this document does not overrule it. + +**What it exposes.** The queue is two-tier. 49 sections carry 45 ids because 4 ids own a second, continuation section. A further **13 ids have no heading at all** and are opened as records nested inside another item's section -- so grepping `^## Q41` finds nothing while Q41 is a live item the file names 24 times. Those are listed in their own table below. + +| # | id | lines | bytes | role | title | first `Status:` | +|---|---|---|---|---|---|---| +| 1 | Q1 | 9-54 | 3139 | opening | Recover the fifteen silent rooms *(highest value; no dependencies)* | CLOSED — 0/15 recovered, and every public avenue is now exhausted with receipts. | +| 2 | Q2 | 55-72 | 1085 | opening | Chase the one dead link | DONE — recovered. | +| 3 | Q3 | 73-105 | 2333 | opening | Independent audit of the published ledger | DONE — both passes complete (`2ebcbe7` pass 1, `6f31cde` pass 2, zephyrus-sparkborn; material `2118b3e`). | +| 4 | Q4 | 106-132 | 2006 | opening | Falsification pass | DONE — `recoveries/falsification-pass.md` (`f50f052`, zephyrus-sparkborn), all three verdicts re-verified | +| 5 | Q8 | 133-453 | 40998 | opening | Summary fidelity: does each entry's machine-written summary overstate its article? | pilot CLOSED at `dcf91a1` (zephyrus-sparkborn), corrected at `9cc6879`; the full sweep is CLOSED at `f1fe38d` (2026-10-06) — 91/91, 1,101 rows, verified independently from the published file. The one … | +| 6 | Q5 | 454-488 | 2764 | opening | Issue triage in the organisation's public repos | re-scoped 2026-09-26 (reply 08 §5) after a census, because as written it invites busywork. | +| 7 | Q6 | 489-581 | 8337 | opening | A public guide to checking this archive | DONE at `8bd0b09` (Merlin, 2026-10-08T17:28:47Z) — verified from this host figure by figure; | +| 8 | Q7 | 582-655 | 6898 | opening | Propose the next items | satisfied for the 2026-09-25 cycle. | +| 9 | Q9 | 656-692 | 3028 | opening | Independent QC of the sovereign music candidates *(second opinion, no listening required)* | unclaimed. Pre-verified by Timmy against the delivered files — record `recoveries/music-master-verification.md` | +| 10 | Q10 | 693-948 | 24339 | opening | Summary fidelity for the non-magazine entries | COMPLETE 2026-10-10 (verified from raw sources; closure record below). Originally claimed by zephyrus-sparkborn at `3c9ad5b` (2026-10-07, `checkins/2026-10-07-zephyrus.md` §3). | +| 11 | Q11 | 949-984 | 2699 | opening | Close the eight pass-2 date mismatches | DONE — `recoveries/ledger-date-closure.md`, Merlin, 2026-10-09. 8 / 8 resolved, all | +| 12 | Q12 | 985-1110 | 12049 | opening | Are the evidence tiers earned? | DONE — `recoveries/evidence-tier-audit.md`, Merlin, 2026-10-10. 18/18 `justified`, 0 `too | +| 13 | Q13 | 1111-1145 | 2788 | opening | Publish the archive-side wording fixes, one at a time | -- | +| 14 | Q14 | 1146-1264 | 11043 | opening | Put the Inbox on `alexanderwhitestone.com` behind a gate | DONE — finished by `616794c` (2026-09-26), then re-measured by a later run the same day instead of being | +| 15 | Q15 | 1265-1395 | 14450 | opening | Stop the monitor from waking on the pipe's own writes *(pipe; no dependencies)* | DONE — 2026-09-26 ≈19:05 UTC. | +| 16 | Q16 | 1396-1552 | 14833 | opening | Per-platform ATS gate census *(career lane; opened 2026-10-01 by reply 21 §3, drafted by zephyrus)* | draft v0.1 landed `f29425a` (`career-lane/ats-gate-census-draft.md`, 2026-10-01; check-in `2f83e08`); | +| 17 | Q17 | 1553-1590 | 8975 | opening | Employer-side AI-authorship refusal gates *(career lane; opened 2026-10-03 by reply 26, per Q7)* | -- | +| 18 | Q18 | 1591-1767 | 50438 | opening | Employer-side lead intake must be deduped against the prepared index *(career lane; opened 2026-10-04 by reply 28, per the G2i miss)* | -- | +| 19 | Q19 | 1768-2111 | 80477 | opening | Three proposed members (local Mac wizard, Grok, Mistral) *(wizard onboarding; opened 2026-10-06 by zephyrus-sparkborn, `checkins/2026-10-06-zephyrus-wizards*.md` + `JOIN-THE-TROUPE.md`)* | RESOLVED — provisional (2026-10-06). | +| 20 | Q20 | 2112-2232 | 10988 | opening | Excerpt fidelity and placement for the 91 magazine entries *(archive verification; opened 2026-10-08 by the pipe, per Q7's never-dry obligation)* | DONE — `recoveries/excerpt-placement-audit.md` (12,914 B) and `recoveries/excerpt-placement-rows.md` | +| 21 | Q21 | 2233-2366 | 12569 | opening | Does every quoted excerpt resolve at the URL it is printed against? | open and unclaimed. Opened by the pipe 2026-10-10 | +| 22 | Q22 | 2367-2476 | 9703 | opening | Do the named-but-unlinked sources resolve? *(proposed 2026-10-10 by Merlin, per Q7; not yet opened)* | DONE — `recoveries/named-source-liveness.md`, Merlin, 2026-10-10. 28 fetches → 27 distinct | +| 23 | Q23 | 2477-2620 | 14827 | opening | The prose-named sources the `@` token rule cannot see *(archive verification; opened 2026-10-10 by the pipe, per Q7's never-dry obligation)* | DONE 2026-10-10 — `recoveries/named-source-liveness-unlinked.md` (Merlin), verified from the published file this tick (control plane). | +| 24 | Q24 | 2621-2765 | 12679 | opening | The Q13 fix pack: stage every verified live-page fix as one reviewable approval *(archive-side; opened 2026-10-10 by the pipe, per Q7's never-dry obligation)* | open and unclaimed. Opened by the pipe 2026-10-10 | +| 25 | Q25 | 2766-3057 | 26281 | opening | A prose-anchor convention for the ledger build *(archive-side; proposed 2026-10-10 by Merlin, per Q7's never-dry obligation — Q24 closed and the unclaimed executable set fell to zero)* | DONE — `recoveries/prose-anchor-convention.md`, Merlin, 2026-10-10. Designed, prototyped and | +| 26 | Q26 | 3058-3254 | 17936 | opening | Make the silent token classes loud, and measure the fifth slot *(archive-side; opened 2026-10-10 by the pipe, per Q7's never-dry obligation — Q25 closed at `a9f23a6` and the unclaimed executable set … | DONE — `recoveries/prose-anchor-hardening.md`, Merlin, 2026-10-10→11 (~00:20–00:24 UTC). | +| 27 | Q27 | 3255-3387 | 14425 | opening | A fixture at every render site: make the token sentinel's coverage total *(archive-side; opened 2026-10-11 by the pipe, per Q7's never-dry obligation — Q26 closed at `f05179e`)* | DONE 2026-10-11 — `recoveries/render-site-coverage.md` (Merlin). 32 sites: 28 covered by fixture, 4 structural exclusions; 35 fixtures, every negative writes 0 files; the scope clause closed with … | +| 28 | Q28 | 3388-3572 | 15313 | opening | The channels the entry walk cannot see: state the guarantee, then cover what it cannot reach | DONE 2026-10-11 — `recoveries/render-site-channels.md` (Merlin), scratch tree | +| 29 | Q29 | 3573-3746 | 19748 | opening | The guards that are not the walk: what protects a page when the value is not a token | DONE 2026-10-11 — `recoveries/render-site-guards.md` (Merlin), scratch tree `/root/crucible/q29/`. 22 fixtures plus the control, every negative or escape claim measured against the emitted byte; the … | +| 30 | Q30 | 3747-3961 | 17595 | opening | The renderer's entry point: the one path no gate executes | DONE 2026-10-11 — `recoveries/helm-cli-entry-point.md` (Merlin), all four steps of the | +| 31 | Q31 | 3962-4367 | 37235 | opening | The collector's entry point: the sibling path no gate has executed either | DONE at Merlin's Run 6 (2026-10-11 ≈13:19–13:35Z) — deliverable `recoveries/helm-collector-run.md`; the collector's entry point is executed, its destination is gated, and G8 asserts it. | +| 32 | Q32 | 4368-4437 | 7043 | opening | The G8 two-run agreement check is nondeterministic: `age_seconds` is not scrubbed *(Helm lane; opened 2026-10-11 by the pipe, per Q7's never-dry obligation — Q31's adoption is blocked behind it)* | OPEN — named to the Helm lane | +| 33 | Q33 | 4438-4551 | 11472 | opening | Split the newest-file pointer out of `evidence.source_path` *(collector-side; opened 2026-10-11 by the pipe, per Q7's never-dry obligation — Q32 closed at `7e12c82`)* | OPEN — named to the Helm lane. | +| 34 | Q34 | 4552-4727 | 18123 | opening | Gate the pointer's null case *(collector-side gate; opened 2026-10-11 by the pipe, per Q7's never-dry obligation — Q33 closed at `8bb88da`)* | DONE 2026-10-12 — `recoveries/helm-null-gate.md` (Merlin), scratch tree `/root/crucible/q34/`. | +| 35 | Q35 | 4728-4895 | 16334 | opening | The gate must refuse a subject collector that carries no destination rule *(Helm lane; opened 2026-10-12 by the pipe, per Q7's never-dry obligation — Q34 verified and closed at `ac98bfb`)* | DONE — `recoveries/helm-precondition-gate.md` (8,261 B / sha256 | +| 36 | Q36 | 4896-4984 | 7447 | opening | The monitor cannot see a counterparty write that carries this pipe's identity *(pipe tooling; opened 2026-10-12 by the pipe, per Q7's never-dry obligation — Q20 verified and closed at `9a1de9a` and … | DONE — the rule holds, and the predicted transition arrived exactly as stated. | +| 37 | Q37 | 4985-5017 | 2795 | opening | The identity rule closes one class, not the class *(pipe tooling; opened 2026-10-12 by the pipe, per Q7's never-dry obligation — Q36 closed at `9584984` and the unclaimed executable set fell to … | -- | +| 38 | Q38 | 5018-5134 | 11073 | opening | The behavioural destination guard *(Helm lane; opened 2026-10-12 by Merlin — Q36's candidate 2, on Alexander's direction to continue the Helm lane)* | DONE — `recoveries/helm-behavioural-guard.md` (10,232 B), Merlin, 2026-10-12T06:5x–07:1xZ, this host. | +| 39 | Q38 | 5135-5150 | 6035 | cont: The | The behavioural destination guard *(Helm lane; opened 2026-10-12 by Merlin — Q36's candidate 2, on Alexander's direction to continue the Helm lane; **direction unrecorded in this repo — Testimony … | the deliverable is verified and the item is DONE, and its protective claim is FALSIFIED. It is not adoptable as it stands. | +| 40 | Q39 | 5151-5203 | 8753 | opening | The Q38 guard's admission admits a writer, and its legs run the unrewritten subject *(Helm lane; opened 2026-10-12 by the pipe, per Q7's never-dry obligation — Q38 is closed and verified and its … | DONE — `recoveries/helm-destination-guard-fix.md` (12,253 B), Merlin, 2026-10-12, scratch tree `/root/crucible/q39/`. Fixed gate `9b09f328…` (1,412 lines / 77,704 B, +309/−38 against Q38's … | +| 41 | Q39 | 5204-5216 | 4002 | cont: closure | closure — verified from this host by the pipe (2026-10-12, reply 22) | DONE. Finished by `fad802c` (record + queue block + Run 5 check-in) and `4e87c8f` (landing evidence); verified by `replies/2026-10-12-to-merlin-22.md` with my own harness `/root/troupe-verify/q39/inde … | +| 42 | Q40 | 5217-5275 | 4156 | opening | the deployed gate admits all five negatives, and has no admission path at all *(Helm lane; opened 2026-10-12 by Merlin, discharging the Q39 closure's C26)* | DONE — `recoveries/helm-deployed-gate-admission.md` (5,633 B / sha256 | +| 43 | Q37 | 5276-6102 | 84293 | cont: claimed | claimed by this pipe; first increment landed (the v1.3 census) | OPEN and claimed by the pipe (Merlin's §Next 2 read it correctly; it is this lane's tooling). Landed this tick: | +| 44 | Q43 | 6103-6140 | 7242 | opening | The legs run the rewritten subject: prevention, not detect-and-restore *(Helm lane; opened 2026-10-12 by Merlin, per the Q39 closure's own recommendation — "open the stronger form (the legs against a … | DONE — `recoveries/helm-leg-sandbox.md` (13,561 B), Merlin, 2026-10-12. Scratch tree `/root/crucible/q43/`; nothing deployed, no `cp` into `/root/.helm/bin/`, `decisions/` and `inbox/` untouched. | +| 45 | Q43 | 6141-6161 | 5562 | cont: closure | closure — verified from this host by the pipe (2026-10-12, reply 27) | VERIFIED AND CLOSED from the pipe, 2026-10-12 18:5x–19:1xZ. | +| 46 | Q44 | 6162-6705 | 88496 | opening | The pin class: redirect every constant composed from the live data root, not only `SNAPSHOT_PATH` *(Helm lane; opened 2026-10-12 by the pipe, per Q43's own closing sentence — "extend the redirect … | DONE 2026-10-12 — `recoveries/helm-root-redirect.md` (Merlin, Helm lane); the closure block is at the foot | +| 47 | Q48 | 6706-6825 | 15123 | opening | The runtime-resolution redirect: close the class Q45's literals cannot see | DONE 2026-10-13 — `recoveries/helm-runtime-redirect.md` (Merlin, Helm lane), scratch tree | +| 48 | Q49 | 6826-7593 | 92050 | opening | The runtime-resolved root's live write path: name it, reproduce it, then close it | open and unclaimed. Opened by the pipe 2026-10-13 | +| 49 | Q45 | 7594-9901 | 323266 | opening | §9 follow-on — the home-directory channel *(Helm lane; Merlin)* | -- | + +## Ids with no `## ` heading + +Opened as records inside another section. `section` is the heading whose body holds the first mention; `shape` is the form of that line. + +| id | first line | refs | inside section | shape | +|---|---|---|---|---| +| Q41 | 5597 | 24 | Q37 | BULLET+BOLD | +| Q42 | 5673 | 57 | Q37 | BULLET+BOLD | +| Q46 | 6412 | 60 | Q44 | BULLET+BOLD | +| Q47 | 6485 | 43 | Q44 | BULLET+BOLD | +| Q50 | 6990 | 44 | Q49 | BULLET+BOLD | +| Q51 | 7106 | 35 | Q49 | BULLET+BOLD | +| Q52 | 7167 | 88 | Q49 | BULLET+BOLD | +| Q53 | 7246 | 85 | Q49 | PLAIN+BOLD | +| Q54 | 7991 | 4 | Q45 | PLAIN | +| Q55 | 8081 | 7 | Q45 | PLAIN | +| Q56 | 8319 | 6 | Q45 | BULLET+BOLD | +| Q58 | 8731 | 1 | Q45 | BULLET+BOLD | +| Q62 | 9227 | 1 | Q45 | PLAIN | + +## Ids referenced outside the queue entirely + +Id tokens in the 317 tracked `*.md` files under the prose corpus (`tools/` excluded: its fixtures carry synthetic ids by construction) that `WORK-QUEUE.md` never mentions. + +| id | files | +|---|---| +| Q63 | `recoveries/helm-pill-both-directions.md` | + +## The pinned parse + +The block below is the parse this document renders, byte for byte. `--check` compares a fresh parse against it. + + +```json +{ + "ids_multi_section": { + "Q37": [ + 4985, + 5276 + ], + "Q38": [ + 5018, + 5135 + ], + "Q39": [ + 5151, + 5204 + ], + "Q43": [ + 6103, + 6141 + ] + }, + "ids_with_section": [ + "Q1", + "Q2", + "Q3", + "Q4", + "Q5", + "Q6", + "Q7", + "Q8", + "Q9", + "Q10", + "Q11", + "Q12", + "Q13", + "Q14", + "Q15", + "Q16", + "Q17", + "Q18", + "Q19", + "Q20", + "Q21", + "Q22", + "Q23", + "Q24", + "Q25", + "Q26", + "Q27", + "Q28", + "Q29", + "Q30", + "Q31", + "Q32", + "Q33", + "Q34", + "Q35", + "Q36", + "Q37", + "Q38", + "Q39", + "Q40", + "Q43", + "Q44", + "Q45", + "Q48", + "Q49" + ], + "nested": [ + { + "id": "Q41", + "line": 5597, + "refs": 24, + "section": "Q37", + "shape": "BULLET+BOLD" + }, + { + "id": "Q42", + "line": 5673, + "refs": 57, + "section": "Q37", + "shape": "BULLET+BOLD" + }, + { + "id": "Q46", + "line": 6412, + "refs": 60, + "section": "Q44", + "shape": "BULLET+BOLD" + }, + { + "id": "Q47", + "line": 6485, + "refs": 43, + "section": "Q44", + "shape": "BULLET+BOLD" + }, + { + "id": "Q50", + "line": 6990, + "refs": 44, + "section": "Q49", + "shape": "BULLET+BOLD" + }, + { + "id": "Q51", + "line": 7106, + "refs": 35, + "section": "Q49", + "shape": "BULLET+BOLD" + }, + { + "id": "Q52", + "line": 7167, + "refs": 88, + "section": "Q49", + "shape": "BULLET+BOLD" + }, + { + "id": "Q53", + "line": 7246, + "refs": 85, + "section": "Q49", + "shape": "PLAIN+BOLD" + }, + { + "id": "Q54", + "line": 7991, + "refs": 4, + "section": "Q45", + "shape": "PLAIN" + }, + { + "id": "Q55", + "line": 8081, + "refs": 7, + "section": "Q45", + "shape": "PLAIN" + }, + { + "id": "Q56", + "line": 8319, + "refs": 6, + "section": "Q45", + "shape": "BULLET+BOLD" + }, + { + "id": "Q58", + "line": 8731, + "refs": 1, + "section": "Q45", + "shape": "BULLET+BOLD" + }, + { + "id": "Q62", + "line": 9227, + "refs": 1, + "section": "Q45", + "shape": "PLAIN" + } + ], + "preamble": { + "bytes": 284, + "end_line": 8, + "line": 1 + }, + "queue_bytes": 1215527, + "queue_lines": 9901, + "queue_sha256": "92d682a0ae8400cda424ed1aeefd3ffe7a672137c2515d4a90d33817c50b12bb", + "sections": [ + { + "bytes": 3139, + "end_line": 54, + "id": "Q1", + "line": 9, + "lines": 46, + "role": "opening", + "role_detail": "", + "status": "CLOSED \u2014 0/15 recovered, and every public avenue is now exhausted with receipts.", + "status_line": 42, + "title": "Recover the fifteen silent rooms *(highest value; no dependencies)*" + }, + { + "bytes": 1085, + "end_line": 72, + "id": "Q2", + "line": 55, + "lines": 18, + "role": "opening", + "role_detail": "", + "status": "DONE \u2014 recovered.", + "status_line": 64, + "title": "Chase the one dead link" + }, + { + "bytes": 2333, + "end_line": 105, + "id": "Q3", + "line": 73, + "lines": 33, + "role": "opening", + "role_detail": "", + "status": "DONE \u2014 both passes complete (`2ebcbe7` pass 1, `6f31cde` pass 2, zephyrus-sparkborn; material `2118b3e`).", + "status_line": 89, + "title": "Independent audit of the published ledger" + }, + { + "bytes": 2006, + "end_line": 132, + "id": "Q4", + "line": 106, + "lines": 27, + "role": "opening", + "role_detail": "", + "status": "DONE \u2014 `recoveries/falsification-pass.md` (`f50f052`, zephyrus-sparkborn), all three verdicts re-verified", + "status_line": 115, + "title": "Falsification pass" + }, + { + "bytes": 40998, + "end_line": 453, + "id": "Q8", + "line": 133, + "lines": 321, + "role": "opening", + "role_detail": "", + "status": "pilot CLOSED at `dcf91a1` (zephyrus-sparkborn), corrected at `9cc6879`; the full sweep is CLOSED at `f1fe38d` (2026-10-06) \u2014 91/91, 1,101 rows, verified independently from the published file. The one \u2026", + "status_line": 154, + "title": "Summary fidelity: does each entry's machine-written summary overstate its article?" + }, + { + "bytes": 2764, + "end_line": 488, + "id": "Q5", + "line": 454, + "lines": 35, + "role": "opening", + "role_detail": "", + "status": "re-scoped 2026-09-26 (reply 08 \u00a75) after a census, because as written it invites busywork.", + "status_line": 463, + "title": "Issue triage in the organisation's public repos" + }, + { + "bytes": 8337, + "end_line": 581, + "id": "Q6", + "line": 489, + "lines": 93, + "role": "opening", + "role_detail": "", + "status": "DONE at `8bd0b09` (Merlin, 2026-10-08T17:28:47Z) \u2014 verified from this host figure by figure;", + "status_line": 498, + "title": "A public guide to checking this archive" + }, + { + "bytes": 6898, + "end_line": 655, + "id": "Q7", + "line": 582, + "lines": 74, + "role": "opening", + "role_detail": "", + "status": "satisfied for the 2026-09-25 cycle.", + "status_line": 587, + "title": "Propose the next items" + }, + { + "bytes": 3028, + "end_line": 692, + "id": "Q9", + "line": 656, + "lines": 37, + "role": "opening", + "role_detail": "", + "status": "unclaimed. Pre-verified by Timmy against the delivered files \u2014 record `recoveries/music-master-verification.md`", + "status_line": 674, + "title": "Independent QC of the sovereign music candidates *(second opinion, no listening required)*" + }, + { + "bytes": 24339, + "end_line": 948, + "id": "Q10", + "line": 693, + "lines": 256, + "role": "opening", + "role_detail": "", + "status": "COMPLETE 2026-10-10 (verified from raw sources; closure record below). Originally claimed by zephyrus-sparkborn at `3c9ad5b` (2026-10-07, `checkins/2026-10-07-zephyrus.md` \u00a73).", + "status_line": 707, + "title": "Summary fidelity for the non-magazine entries" + }, + { + "bytes": 2699, + "end_line": 984, + "id": "Q11", + "line": 949, + "lines": 36, + "role": "opening", + "role_detail": "", + "status": "DONE \u2014 `recoveries/ledger-date-closure.md`, Merlin, 2026-10-09. 8 / 8 resolved, all", + "status_line": 962, + "title": "Close the eight pass-2 date mismatches" + }, + { + "bytes": 12049, + "end_line": 1110, + "id": "Q12", + "line": 985, + "lines": 126, + "role": "opening", + "role_detail": "", + "status": "DONE \u2014 `recoveries/evidence-tier-audit.md`, Merlin, 2026-10-10. 18/18 `justified`, 0 `too", + "status_line": 1012, + "title": "Are the evidence tiers earned?" + }, + { + "bytes": 2788, + "end_line": 1145, + "id": "Q13", + "line": 1111, + "lines": 35, + "role": "opening", + "role_detail": "", + "status": null, + "status_line": 1129, + "title": "Publish the archive-side wording fixes, one at a time" + }, + { + "bytes": 11043, + "end_line": 1264, + "id": "Q14", + "line": 1146, + "lines": 119, + "role": "opening", + "role_detail": "", + "status": "DONE \u2014 finished by `616794c` (2026-09-26), then re-measured by a later run the same day instead of being", + "status_line": 1210, + "title": "Put the Inbox on `alexanderwhitestone.com` behind a gate" + }, + { + "bytes": 14450, + "end_line": 1395, + "id": "Q15", + "line": 1265, + "lines": 131, + "role": "opening", + "role_detail": "", + "status": "DONE \u2014 2026-09-26 \u224819:05 UTC.", + "status_line": 1277, + "title": "Stop the monitor from waking on the pipe's own writes *(pipe; no dependencies)*" + }, + { + "bytes": 14833, + "end_line": 1552, + "id": "Q16", + "line": 1396, + "lines": 157, + "role": "opening", + "role_detail": "", + "status": "draft v0.1 landed `f29425a` (`career-lane/ats-gate-census-draft.md`, 2026-10-01; check-in `2f83e08`);", + "status_line": 1411, + "title": "Per-platform ATS gate census *(career lane; opened 2026-10-01 by reply 21 \u00a73, drafted by zephyrus)*" + }, + { + "bytes": 8975, + "end_line": 1590, + "id": "Q17", + "line": 1553, + "lines": 38, + "role": "opening", + "role_detail": "", + "status": null, + "status_line": null, + "title": "Employer-side AI-authorship refusal gates *(career lane; opened 2026-10-03 by reply 26, per Q7)*" + }, + { + "bytes": 50438, + "end_line": 1767, + "id": "Q18", + "line": 1591, + "lines": 177, + "role": "opening", + "role_detail": "", + "status": null, + "status_line": null, + "title": "Employer-side lead intake must be deduped against the prepared index *(career lane; opened 2026-10-04 by reply 28, per the G2i miss)*" + }, + { + "bytes": 80477, + "end_line": 2111, + "id": "Q19", + "line": 1768, + "lines": 344, + "role": "opening", + "role_detail": "", + "status": "RESOLVED \u2014 provisional (2026-10-06).", + "status_line": 1770, + "title": "Three proposed members (local Mac wizard, Grok, Mistral) *(wizard onboarding; opened 2026-10-06 by zephyrus-sparkborn, `checkins/2026-10-06-zephyrus-wizards*.md` + `JOIN-THE-TROUPE.md`)*" + }, + { + "bytes": 10988, + "end_line": 2232, + "id": "Q20", + "line": 2112, + "lines": 121, + "role": "opening", + "role_detail": "", + "status": "DONE \u2014 `recoveries/excerpt-placement-audit.md` (12,914 B) and `recoveries/excerpt-placement-rows.md`", + "status_line": 2149, + "title": "Excerpt fidelity and placement for the 91 magazine entries *(archive verification; opened 2026-10-08 by the pipe, per Q7's never-dry obligation)*" + }, + { + "bytes": 12569, + "end_line": 2366, + "id": "Q21", + "line": 2233, + "lines": 134, + "role": "opening", + "role_detail": "", + "status": "open and unclaimed. Opened by the pipe 2026-10-10", + "status_line": 2264, + "title": "Does every quoted excerpt resolve at the URL it is printed against?" + }, + { + "bytes": 9703, + "end_line": 2476, + "id": "Q22", + "line": 2367, + "lines": 110, + "role": "opening", + "role_detail": "", + "status": "DONE \u2014 `recoveries/named-source-liveness.md`, Merlin, 2026-10-10. 28 fetches \u2192 27 distinct", + "status_line": 2389, + "title": "Do the named-but-unlinked sources resolve? *(proposed 2026-10-10 by Merlin, per Q7; not yet opened)*" + }, + { + "bytes": 14827, + "end_line": 2620, + "id": "Q23", + "line": 2477, + "lines": 144, + "role": "opening", + "role_detail": "", + "status": "DONE 2026-10-10 \u2014 `recoveries/named-source-liveness-unlinked.md` (Merlin), verified from the published file this tick (control plane).", + "status_line": 2532, + "title": "The prose-named sources the `@` token rule cannot see *(archive verification; opened 2026-10-10 by the pipe, per Q7's never-dry obligation)*" + }, + { + "bytes": 12679, + "end_line": 2765, + "id": "Q24", + "line": 2621, + "lines": 145, + "role": "opening", + "role_detail": "", + "status": "open and unclaimed. Opened by the pipe 2026-10-10", + "status_line": 2684, + "title": "The Q13 fix pack: stage every verified live-page fix as one reviewable approval *(archive-side; opened 2026-10-10 by the pipe, per Q7's never-dry obligation)*" + }, + { + "bytes": 26281, + "end_line": 3057, + "id": "Q25", + "line": 2766, + "lines": 292, + "role": "opening", + "role_detail": "", + "status": "DONE \u2014 `recoveries/prose-anchor-convention.md`, Merlin, 2026-10-10. Designed, prototyped and", + "status_line": 2802, + "title": "A prose-anchor convention for the ledger build *(archive-side; proposed 2026-10-10 by Merlin, per Q7's never-dry obligation \u2014 Q24 closed and the unclaimed executable set fell to zero)*" + }, + { + "bytes": 17936, + "end_line": 3254, + "id": "Q26", + "line": 3058, + "lines": 197, + "role": "opening", + "role_detail": "", + "status": "DONE \u2014 `recoveries/prose-anchor-hardening.md`, Merlin, 2026-10-10\u219211 (~00:20\u201300:24 UTC).", + "status_line": 3124, + "title": "Make the silent token classes loud, and measure the fifth slot *(archive-side; opened 2026-10-10 by the pipe, per Q7's never-dry obligation \u2014 Q25 closed at `a9f23a6` and the unclaimed executable set \u2026" + }, + { + "bytes": 14425, + "end_line": 3387, + "id": "Q27", + "line": 3255, + "lines": 133, + "role": "opening", + "role_detail": "", + "status": "DONE 2026-10-11 \u2014 `recoveries/render-site-coverage.md` (Merlin). 32 sites: 28 covered by fixture, 4 structural exclusions; 35 fixtures, every negative writes 0 files; the scope clause closed with \u2026", + "status_line": 3310, + "title": "A fixture at every render site: make the token sentinel's coverage total *(archive-side; opened 2026-10-11 by the pipe, per Q7's never-dry obligation \u2014 Q26 closed at `f05179e`)*" + }, + { + "bytes": 15313, + "end_line": 3572, + "id": "Q28", + "line": 3388, + "lines": 185, + "role": "opening", + "role_detail": "", + "status": "DONE 2026-10-11 \u2014 `recoveries/render-site-channels.md` (Merlin), scratch tree", + "status_line": 3468, + "title": "The channels the entry walk cannot see: state the guarantee, then cover what it cannot reach" + }, + { + "bytes": 19748, + "end_line": 3746, + "id": "Q29", + "line": 3573, + "lines": 174, + "role": "opening", + "role_detail": "", + "status": "DONE 2026-10-11 \u2014 `recoveries/render-site-guards.md` (Merlin), scratch tree `/root/crucible/q29/`. 22 fixtures plus the control, every negative or escape claim measured against the emitted byte; the \u2026", + "status_line": 3653, + "title": "The guards that are not the walk: what protects a page when the value is not a token" + }, + { + "bytes": 17595, + "end_line": 3961, + "id": "Q30", + "line": 3747, + "lines": 215, + "role": "opening", + "role_detail": "", + "status": "DONE 2026-10-11 \u2014 `recoveries/helm-cli-entry-point.md` (Merlin), all four steps of the", + "status_line": 3858, + "title": "The renderer's entry point: the one path no gate executes" + }, + { + "bytes": 37235, + "end_line": 4367, + "id": "Q31", + "line": 3962, + "lines": 406, + "role": "opening", + "role_detail": "", + "status": "DONE at Merlin's Run 6 (2026-10-11 \u224813:19\u201313:35Z) \u2014 deliverable `recoveries/helm-collector-run.md`; the collector's entry point is executed, its destination is gated, and G8 asserts it.", + "status_line": 4035, + "title": "The collector's entry point: the sibling path no gate has executed either" + }, + { + "bytes": 7043, + "end_line": 4437, + "id": "Q32", + "line": 4368, + "lines": 70, + "role": "opening", + "role_detail": "", + "status": "OPEN \u2014 named to the Helm lane", + "status_line": 4384, + "title": "The G8 two-run agreement check is nondeterministic: `age_seconds` is not scrubbed *(Helm lane; opened 2026-10-11 by the pipe, per Q7's never-dry obligation \u2014 Q31's adoption is blocked behind it)*" + }, + { + "bytes": 11472, + "end_line": 4551, + "id": "Q33", + "line": 4438, + "lines": 114, + "role": "opening", + "role_detail": "", + "status": "OPEN \u2014 named to the Helm lane.", + "status_line": 4454, + "title": "Split the newest-file pointer out of `evidence.source_path` *(collector-side; opened 2026-10-11 by the pipe, per Q7's never-dry obligation \u2014 Q32 closed at `7e12c82`)*" + }, + { + "bytes": 18123, + "end_line": 4727, + "id": "Q34", + "line": 4552, + "lines": 176, + "role": "opening", + "role_detail": "", + "status": "DONE 2026-10-12 \u2014 `recoveries/helm-null-gate.md` (Merlin), scratch tree `/root/crucible/q34/`.", + "status_line": 4568, + "title": "Gate the pointer's null case *(collector-side gate; opened 2026-10-11 by the pipe, per Q7's never-dry obligation \u2014 Q33 closed at `8bb88da`)*" + }, + { + "bytes": 16334, + "end_line": 4895, + "id": "Q35", + "line": 4728, + "lines": 168, + "role": "opening", + "role_detail": "", + "status": "DONE \u2014 `recoveries/helm-precondition-gate.md` (8,261 B / sha256", + "status_line": 4751, + "title": "The gate must refuse a subject collector that carries no destination rule *(Helm lane; opened 2026-10-12 by the pipe, per Q7's never-dry obligation \u2014 Q34 verified and closed at `ac98bfb`)*" + }, + { + "bytes": 7447, + "end_line": 4984, + "id": "Q36", + "line": 4896, + "lines": 89, + "role": "opening", + "role_detail": "", + "status": "DONE \u2014 the rule holds, and the predicted transition arrived exactly as stated.", + "status_line": 4945, + "title": "The monitor cannot see a counterparty write that carries this pipe's identity *(pipe tooling; opened 2026-10-12 by the pipe, per Q7's never-dry obligation \u2014 Q20 verified and closed at `9a1de9a` and \u2026" + }, + { + "bytes": 2795, + "end_line": 5017, + "id": "Q37", + "line": 4985, + "lines": 33, + "role": "opening", + "role_detail": "", + "status": null, + "status_line": null, + "title": "The identity rule closes one class, not the class *(pipe tooling; opened 2026-10-12 by the pipe, per Q7's never-dry obligation \u2014 Q36 closed at `9584984` and the unclaimed executable set fell to \u2026" + }, + { + "bytes": 11073, + "end_line": 5134, + "id": "Q38", + "line": 5018, + "lines": 117, + "role": "opening", + "role_detail": "", + "status": "DONE \u2014 `recoveries/helm-behavioural-guard.md` (10,232 B), Merlin, 2026-10-12T06:5x\u201307:1xZ, this host.", + "status_line": 5036, + "title": "The behavioural destination guard *(Helm lane; opened 2026-10-12 by Merlin \u2014 Q36's candidate 2, on Alexander's direction to continue the Helm lane)*" + }, + { + "bytes": 6035, + "end_line": 5150, + "id": "Q38", + "line": 5135, + "lines": 16, + "role": "continuation", + "role_detail": "The", + "status": "the deliverable is verified and the item is DONE, and its protective claim is FALSIFIED. It is not adoptable as it stands.", + "status_line": 5137, + "title": "The behavioural destination guard *(Helm lane; opened 2026-10-12 by Merlin \u2014 Q36's candidate 2, on Alexander's direction to continue the Helm lane; **direction unrecorded in this repo \u2014 Testimony \u2026" + }, + { + "bytes": 8753, + "end_line": 5203, + "id": "Q39", + "line": 5151, + "lines": 53, + "role": "opening", + "role_detail": "", + "status": "DONE \u2014 `recoveries/helm-destination-guard-fix.md` (12,253 B), Merlin, 2026-10-12, scratch tree `/root/crucible/q39/`. Fixed gate `9b09f328\u2026` (1,412 lines / 77,704 B, +309/\u221238 against Q38's \u2026", + "status_line": 5159, + "title": "The Q38 guard's admission admits a writer, and its legs run the unrewritten subject *(Helm lane; opened 2026-10-12 by the pipe, per Q7's never-dry obligation \u2014 Q38 is closed and verified and its \u2026" + }, + { + "bytes": 4002, + "end_line": 5216, + "id": "Q39", + "line": 5204, + "lines": 13, + "role": "continuation", + "role_detail": "closure", + "status": "DONE. Finished by `fad802c` (record + queue block + Run 5 check-in) and `4e87c8f` (landing evidence); verified by `replies/2026-10-12-to-merlin-22.md` with my own harness `/root/troupe-verify/q39/inde \u2026", + "status_line": 5206, + "title": "closure \u2014 verified from this host by the pipe (2026-10-12, reply 22)" + }, + { + "bytes": 4156, + "end_line": 5275, + "id": "Q40", + "line": 5217, + "lines": 59, + "role": "opening", + "role_detail": "", + "status": "DONE \u2014 `recoveries/helm-deployed-gate-admission.md` (5,633 B / sha256", + "status_line": 5230, + "title": "the deployed gate admits all five negatives, and has no admission path at all *(Helm lane; opened 2026-10-12 by Merlin, discharging the Q39 closure's C26)*" + }, + { + "bytes": 84293, + "end_line": 6102, + "id": "Q37", + "line": 5276, + "lines": 827, + "role": "continuation", + "role_detail": "claimed", + "status": "OPEN and claimed by the pipe (Merlin's \u00a7Next 2 read it correctly; it is this lane's tooling). Landed this tick:", + "status_line": 5278, + "title": "claimed by this pipe; first increment landed (the v1.3 census)" + }, + { + "bytes": 7242, + "end_line": 6140, + "id": "Q43", + "line": 6103, + "lines": 38, + "role": "opening", + "role_detail": "", + "status": "DONE \u2014 `recoveries/helm-leg-sandbox.md` (13,561 B), Merlin, 2026-10-12. Scratch tree `/root/crucible/q43/`; nothing deployed, no `cp` into `/root/.helm/bin/`, `decisions/` and `inbox/` untouched.", + "status_line": 6125, + "title": "The legs run the rewritten subject: prevention, not detect-and-restore *(Helm lane; opened 2026-10-12 by Merlin, per the Q39 closure's own recommendation \u2014 \"open the stronger form (the legs against a \u2026" + }, + { + "bytes": 5562, + "end_line": 6161, + "id": "Q43", + "line": 6141, + "lines": 21, + "role": "continuation", + "role_detail": "closure", + "status": "VERIFIED AND CLOSED from the pipe, 2026-10-12 18:5x\u201319:1xZ.", + "status_line": 6143, + "title": "closure \u2014 verified from this host by the pipe (2026-10-12, reply 27)" + }, + { + "bytes": 88496, + "end_line": 6705, + "id": "Q44", + "line": 6162, + "lines": 544, + "role": "opening", + "role_detail": "", + "status": "DONE 2026-10-12 \u2014 `recoveries/helm-root-redirect.md` (Merlin, Helm lane); the closure block is at the foot", + "status_line": 6184, + "title": "The pin class: redirect every constant composed from the live data root, not only `SNAPSHOT_PATH` *(Helm lane; opened 2026-10-12 by the pipe, per Q43's own closing sentence \u2014 \"extend the redirect \u2026" + }, + { + "bytes": 15123, + "end_line": 6825, + "id": "Q48", + "line": 6706, + "lines": 120, + "role": "opening", + "role_detail": "", + "status": "DONE 2026-10-13 \u2014 `recoveries/helm-runtime-redirect.md` (Merlin, Helm lane), scratch tree", + "status_line": 6718, + "title": "The runtime-resolution redirect: close the class Q45's literals cannot see" + }, + { + "bytes": 92050, + "end_line": 7593, + "id": "Q49", + "line": 6826, + "lines": 768, + "role": "opening", + "role_detail": "", + "status": "open and unclaimed. Opened by the pipe 2026-10-13", + "status_line": 6847, + "title": "The runtime-resolved root's live write path: name it, reproduce it, then close it" + }, + { + "bytes": 323266, + "end_line": 9901, + "id": "Q45", + "line": 7594, + "lines": 2308, + "role": "opening", + "role_detail": "", + "status": null, + "status_line": null, + "title": "\u00a79 follow-on \u2014 the home-directory channel *(Helm lane; Merlin)*" + } + ] +} +``` + diff --git a/tools/queue_index.py b/tools/queue_index.py new file mode 100644 index 0000000..99d50e1 --- /dev/null +++ b/tools/queue_index.py @@ -0,0 +1,583 @@ +#!/usr/bin/env python3 +"""queue_index.py -- the work queue's table of contents, and the gate that it is current. + +WHY THIS EXISTS + `WORK-QUEUE.md` is 1.2 MB and 9,901 lines long. It has no table of contents. + Every lane cites it by line number ("Q37 `:5278`"), so a reader who wants one + item has to grep for its heading and then find where that section ends -- and a + reader who wants the shape of the whole queue has nothing to read at all. + + The shape is not obvious even after a grep. Measured on 2026-10-17 at HEAD + (`de0b1fd`): 49 `## ` sections carry 45 distinct ids, because four ids (Q37, + Q38, Q39, Q43) own a second section each -- a `closure` or `claimed` block + appended under a fresh heading. And 13 further ids (Q41, Q42, Q46, Q47, Q50, + Q51, Q52, Q53, Q54, Q55, Q56, Q58, Q62) have NO heading of their own: they are + opened as bold or prose records nested inside somebody else's section. So + "read the queue" is two different operations depending on which item you want, + and nothing said so. + +WHAT IT MEASURES -- structural facts only, one row per `## ` section + id, line, end_line, role, title, lines, bytes, first `Status:` marker. + role=opening the first section carrying that id + role=continuation a later section for an id already seen (a closure or + claim block); its `role_detail` names the first word + lines/bytes the section's own extent, header line to last line + status the first `Status:` line in the section, VERBATIM and + truncated. NOT adjudicated: whether an item is open is + `tools/queue_claim_check.py`'s verdict, not this tool's. + Two instruments disagreeing about closure is a bug this + tool declines to introduce. + + Plus the two legs that make the structure visible: + nested ids first seen inside a section that has no heading of their own, + with the section that contains them and the shape of that line + outside ids referenced by the prose corpus (checkins/ replies/ recoveries/ + audits/ access/ decisions/ docs/ and the top-level documents) but + absent from the queue entirely -- `tools/` is excluded on purpose: + its fixtures carry synthetic ids by construction, and this tool's + own selftest is one of them. + + Headings inside a fenced block are never sections; a heading whose id is + followed by `closure --` or `§9 follow-on --` is still that id's section. + +WHAT IT DOES NOT DO + It does not grade the work, does not adjudicate closure, and does not read + git history. It answers "what is in this file and where", and it keeps the + generated copy of that answer honest: `--write` renders `docs/queue-index.md` + with the parse pinned inside a machine block, and `--check` re-derives the + parse and goes RED the moment the document and the queue disagree. + + `--check` exit codes: 0 current, 1 stale (with the delta printed), 2 the + document carries no parseable pinned block. + + READ-ONLY except `--json PATH` and `--write PATH`. + +USAGE + python3 tools/queue_index.py --index # 49 sections, one screen each side + python3 tools/queue_index.py --json out.json + python3 tools/queue_index.py --write docs/queue-index.md + python3 tools/queue_index.py --check docs/queue-index.md + python3 tools/queue_index.py --selftest # fixture corpus, 32 cells + + Re-run `--write` after any append to the queue. The document carries the + queue's sha256, so a stale copy is a RED check, never a silent lie. +""" + +import argparse +import hashlib +import json +import os +import re +import subprocess +import sys +import tempfile + +QUEUE_DEFAULT = "WORK-QUEUE.md" +BEGIN = "" +END = "" + +HEADING = re.compile(r"^##\s+(Q\d{1,2})\b(.*)$") +ID_TOKEN = re.compile(r"\bQ\d{1,2}\b") +STATUS = re.compile(r"^\s*\*{0,2}Status:\s*(.*)$") +FENCE = re.compile(r"^(```|~~~)") + +ROLE_WORDS = ("closure", "claimed", "§", "status") + + +def split_lines(text): + """Lines without their terminators, plus the byte length each line occupies.""" + lines = text.split("\n") + trailing_newline = text.endswith("\n") + if trailing_newline: + lines = lines[:-1] + return lines, trailing_newline + + +def line_bytes(line, is_last, trailing_newline): + if is_last and not trailing_newline: + return len(line.encode("utf-8")) + return len(line.encode("utf-8")) + 1 + + +def parse(text): + """Return the structural parse: sections, nested ids, id map, byte accounting.""" + lines, trailing_newline = split_lines(text) + n = len(lines) + sizes = [line_bytes(ln, i == n - 1, trailing_newline) for i, ln in enumerate(lines)] + + headings = [] # (line_no, id, rest) + in_fence = False + for i, ln in enumerate(lines, 1): + if FENCE.match(ln.strip()): + in_fence = not in_fence + continue + if in_fence: + continue + m = HEADING.match(ln) + if m: + headings.append((i, m.group(1), m.group(2).strip())) + + sections = [] + seen = {} + for idx, (line_no, qid, rest) in enumerate(headings): + end = headings[idx + 1][0] - 1 if idx + 1 < len(headings) else n + role = "opening" if qid not in seen else "continuation" + detail = "" + if role == "continuation": + words = rest.lstrip("—- ").split() + detail = words[0].strip("*(") if words else "" + seen.setdefault(qid, line_no) + body = lines[line_no:end] + status = None + status_line = None + for off, ln in enumerate(body, start=line_no + 1): + m = STATUS.match(ln) + if m: + raw = m.group(1).strip() + if "**" in raw: # a bold status marker closes mid-line + raw = raw.split("**", 1)[0] + status = raw.strip() + status_line = off + break + title = rest.lstrip("—- ").strip() + sections.append({ + "id": qid, + "line": line_no, + "end_line": end, + "role": role, + "role_detail": detail, + "title": clip(title, 200), + "lines": end - line_no + 1, + "bytes": sum(sizes[line_no - 1:end]), + "status": clip(status, 200) if status else None, + "status_line": status_line, + }) + + ids: dict = {} + for s in sections: + ids.setdefault(s["id"], []).append(s["line"]) + + # every id token in the queue, with its first occurrence and shape. + # Fenced lines are skipped: an id in a code block is quoted text, not a record. + mentioned = {} + in_fence = False + for i, ln in enumerate(lines, 1): + if FENCE.match(ln.strip()): + in_fence = not in_fence + continue + if in_fence: + continue + for m in ID_TOKEN.finditer(ln): + qid = m.group(0) + hit = mentioned.get(qid) + if hit is None: + mentioned[qid] = [i, ln, 1] + else: + hit[2] += 1 + nested = [] + for qid in sorted(mentioned, key=lambda s: int(s[1:])): + if qid in ids: + continue + line_no, raw, refs = mentioned[qid] + owner = None + for s in sections: + if s["line"] <= line_no <= s["end_line"]: + owner = s["id"] + break + stripped = raw.lstrip() + if stripped.startswith("#"): + shape = "HEADING" + elif stripped.startswith(("-", "*", "|")): + shape = "BULLET" + elif stripped.startswith("**"): + shape = "BOLD" + else: + shape = "PLAIN" + if shape != "HEADING" and "**" in raw: + shape += "+BOLD" + nested.append({"id": qid, "line": line_no, "section": owner, "shape": shape, + "refs": refs}) + + first = headings[0][0] if headings else n + 1 + preamble_end = first - 1 + payload = { + "queue_lines": n, + "queue_bytes": sum(sizes), + "queue_sha256": hashlib.sha256(text.encode("utf-8")).hexdigest(), + "preamble": {"line": 1, "end_line": preamble_end, + "bytes": sum(sizes[:preamble_end])}, + "sections": sections, + "ids_with_section": sorted(ids, key=lambda s: int(s[1:])), + "ids_multi_section": {k: v for k, v in sorted( + ids.items(), key=lambda kv: int(kv[0][1:])) if len(v) > 1}, + "nested": nested, + } + return payload + + +def outside_ids(repo, queue_path, exclude_tools=True): + """Ids referenced by the prose corpus that the queue never mentions.""" + try: + out = subprocess.check_output(["git", "-C", repo, "ls-files"], text=True) + except (subprocess.CalledProcessError, FileNotFoundError): + return None + files = [f for f in out.split("\n") if f.endswith(".md")] + if exclude_tools: + files = [f for f in files if not f.startswith("tools/")] + queue_abs = os.path.normpath(os.path.join(repo, queue_path)) + queue_text = open(queue_abs, encoding="utf-8", errors="replace").read() + in_queue = set(ID_TOKEN.findall(queue_text)) + hits = {} + for f in files: + path = os.path.join(repo, f) + try: + txt = open(path, encoding="utf-8", errors="replace").read() + except OSError: + continue + for m in ID_TOKEN.finditer(txt): + hits.setdefault(m.group(0), set()).add(f) + return {k: sorted(v) for k, v in sorted( + hits.items(), key=lambda kv: int(kv[0][1:])) if k not in in_queue}, len(files) + + +def summary(payload): + secs = payload["sections"] + cont = [s for s in secs if s["role"] == "continuation"] + return ("index: %d sections, %d ids, %d continuation sections, %d nested ids, " + "%d lines / %d bytes" % (len(secs), len(payload["ids_with_section"]), + len(cont), len(payload["nested"]), + payload["queue_lines"], payload["queue_bytes"])) + + +def clip(text, limit): + """Cut at a word boundary and say so; a silent mid-word cut reads as data loss.""" + if len(text) <= limit: + return text + cut = text[:limit] + if " " in cut[max(0, limit - 20):]: + cut = cut.rsplit(" ", 1)[0] + return cut.rstrip(" ,;:—-") + " …" + + +def build_doc(payload, outside, corpus_files): + lines = [] + lines.append("# The work queue's table of contents") + lines.append("") + lines.append("*Generated by `tools/queue_index.py --write`; do not hand-edit. `python3 " + "tools/queue_index.py --check docs/queue-index.md` re-derives the parse and " + "goes RED when this document and `WORK-QUEUE.md` disagree, which is what an " + "append to the queue produces. Regenerate, commit, move on.*") + lines.append("") + lines.append("**What this is.** One row per `## ` section of `WORK-QUEUE.md`: its line " + "span, its size, and the first `Status:` marker in its body verbatim. " + "Whether an item is *open* is not decided here -- that verdict is " + "`tools/queue_claim_check.py`'s, and this document does not overrule it.") + lines.append("") + lines.append("**What it exposes.** The queue is two-tier. %d sections carry %d ids because " + "%d ids own a second, continuation section. A further **%d ids have no heading " + "at all** and are opened as records nested inside another item's section -- so " + "grepping `^## %s` finds nothing while %s is a live item the file names %d times. " + "Those are listed in their own table below." + % (len(payload["sections"]), len(payload["ids_with_section"]), + len(payload["ids_multi_section"]), len(payload["nested"]), + payload["nested"][0]["id"], payload["nested"][0]["id"], + payload["nested"][0]["refs"])) + lines.append("") + lines.append("| # | id | lines | bytes | role | title | first `Status:` |") + lines.append("|---|---|---|---|---|---|---|") + for i, s in enumerate(payload["sections"], 1): + status = (s["status"] or "--").replace("|", "\\|") + title = s["title"].replace("|", "\\|") + role = s["role"] if s["role"] == "opening" else "cont: %s" % (s["role_detail"] or "?") + lines.append("| %d | %s | %d-%d | %d | %s | %s | %s |" + % (i, s["id"], s["line"], s["end_line"], s["bytes"], role, title, status)) + lines.append("") + lines.append("## Ids with no `## ` heading") + lines.append("") + lines.append("Opened as records inside another section. `section` is the heading whose " + "body holds the first mention; `shape` is the form of that line.") + lines.append("") + lines.append("| id | first line | refs | inside section | shape |") + lines.append("|---|---|---|---|---|") + for r in payload["nested"]: + lines.append("| %s | %d | %d | %s | %s |" + % (r["id"], r["line"], r["refs"], r["section"] or "--", r["shape"])) + lines.append("") + if outside is not None: + lines.append("## Ids referenced outside the queue entirely") + lines.append("") + lines.append("Id tokens in the %d tracked `*.md` files under the prose corpus " + "(`tools/` excluded: its fixtures carry synthetic ids by construction) " + "that `WORK-QUEUE.md` never mentions." % corpus_files) + lines.append("") + if outside: + lines.append("| id | files |") + lines.append("|---|---|") + for qid, files in outside.items(): + lines.append("| %s | %s |" % (qid, ", ".join("`%s`" % f for f in files[:4]) + + (" +%d" % (len(files) - 4) if len(files) > 4 else ""))) + else: + lines.append("None: every id the prose corpus cites is mentioned in the queue.") + lines.append("") + lines.append("## The pinned parse") + lines.append("") + lines.append("The block below is the parse this document renders, byte for byte. " + "`--check` compares a fresh parse against it.") + lines.append("") + lines.append(BEGIN) + lines.append("```json") + lines.append(json.dumps(payload, indent=1, sort_keys=True)) + lines.append("```") + lines.append(END) + lines.append("") + return "\n".join(lines) + + +def extract_pinned(text): + if BEGIN not in text or END not in text: + return None + body = text.split(BEGIN, 1)[1].split(END, 1)[0] + m = re.search(r"```json\n(.*?)\n```", body, re.S) + if not m: + return None + try: + return json.loads(m.group(1)) + except ValueError: + return None + + +def delta(pinned, fresh): + """A short, specific account of how a pinned parse and a fresh one differ.""" + out = [] + p_sec = {(s["id"], s["line"]): s for s in pinned.get("sections", [])} + f_sec = {(s["id"], s["line"]): s for s in fresh.get("sections", [])} + added = [k for k in f_sec if k not in p_sec] + removed = [k for k in p_sec if k not in f_sec] + moved = [k for k in f_sec if k in p_sec and + (f_sec[k]["end_line"], f_sec[k]["bytes"], f_sec[k]["status"]) + != (p_sec[k]["end_line"], p_sec[k]["bytes"], p_sec[k]["status"])] + if added: + out.append("%d new section(s): %s" % (len(added), ", ".join("%s@%d" % k for k in added))) + if removed: + out.append("%d gone section(s): %s" % (len(removed), ", ".join("%s@%d" % k for k in removed))) + if moved: + out.append("%d section(s) changed extent or status, e.g. %s@%d %d->%d lines" + % (len(moved), moved[0][0], moved[0][1], + p_sec[moved[0]]["end_line"] - p_sec[moved[0]]["line"] + 1, + f_sec[moved[0]]["end_line"] - f_sec[moved[0]]["line"] + 1)) + p_nest = [r["id"] for r in pinned.get("nested", [])] + f_nest = [r["id"] for r in fresh.get("nested", [])] + if p_nest != f_nest: + out.append("nested id set changed: +%s -%s" + % (",".join(sorted(set(f_nest) - set(p_nest), key=lambda s: int(s[1:]))), + ",".join(sorted(set(p_nest) - set(f_nest), key=lambda s: int(s[1:]))))) + if pinned.get("queue_sha256") != fresh.get("queue_sha256"): + out.append("queue sha256 %s -> %s" % (str(pinned.get("queue_sha256"))[:12], + str(fresh.get("queue_sha256"))[:12])) + return "; ".join(out) if out else "no structural difference (byte-level drift only)" + + +def run(args): + repo = args.repo + queue_path = os.path.join(repo, args.queue) + with open(queue_path, encoding="utf-8") as fh: + text = fh.read() + payload = parse(text) + + if args.check: + doc = open(args.check, encoding="utf-8").read() + pinned = extract_pinned(doc) + if pinned is None: + print("UNVERIFIABLE: no parseable pinned block in %s" % args.check) + return 2 + if json.dumps(pinned, sort_keys=True) == json.dumps(payload, sort_keys=True): + print("index current: %s" % summary(payload)) + return 0 + print("STALE: %s" % delta(pinned, payload)) + print("regenerate with: python3 tools/queue_index.py --repo %s --write %s" + % (repo, args.check)) + return 1 + + outside, corpus = outside_ids(repo, args.queue) + if args.write: + with open(args.write, "w", encoding="utf-8") as fh: + fh.write(build_doc(payload, outside, corpus or 0)) + print("wrote %s (%d bytes)" % (args.write, os.path.getsize(args.write))) + return 0 + + if args.json: + with open(args.json, "w", encoding="utf-8") as fh: + json.dump(payload, fh, indent=2, sort_keys=True) + fh.write("\n") + + if args.index: + print("%-4s %-5s %-11s %-7s %-13s %-22s %s" + % ("#", "id", "lines", "bytes", "role", "status", "title")) + for i, s in enumerate(payload["sections"], 1): + print("%-4d %-5s %-11s %-7d %-13s %-22s %s" + % (i, s["id"], "%d-%d" % (s["line"], s["end_line"]), s["bytes"], + s["role"][:13], clip((s["status"] or "--"), 22), clip(s["title"], 72))) + print() + for r in payload["nested"]: + print("nested %-4s line %-5d refs %-3d inside %-4s %s" + % (r["id"], r["line"], r["refs"], r["section"] or "--", r["shape"])) + if outside is not None and outside: + print("outside the queue: %s" % ", ".join(outside)) + print() + + print(summary(payload)) + return 0 + + +# -------------------------------------------------------------------------- +# selftest: a fixture corpus, one cell per stated rule +# -------------------------------------------------------------------------- + +FIXTURE = """# Work queue + +Preface text. + +## Q1 — First item *(lane)* + +**Status: DONE at `abc1234`.** Body. + +``` +## Q9 — not a section, this is fenced +``` + +## Q2 — Second item + +Body with no status marker. + +## Q1 — closure — verified + +**Status: OPEN.** A continuation section for Q1. + +## Q3 §9 follow-on — odd shape + +- **Q41 — OPEN (pipe, 2026-10-13, unclaimed).** A nested record. +- prose mentioning Q42 in passing. +""" + + +def selftest(): + cells = [0] + bad = [0] + + def check(name, got, want): + cells[0] += 1 + if got != want: + bad[0] += 1 + print("FAIL %s\n got %r\n want %r" % (name, got, want)) + + p = parse(FIXTURE) + secs = p["sections"] + check("four sections parsed", len(secs), 4) + check("ids in order", [s["id"] for s in secs], ["Q1", "Q2", "Q1", "Q3"]) + check("fenced heading is not a section", "Q9" in p["ids_with_section"], False) + check("second Q1 is a continuation", secs[2]["role"], "continuation") + check("continuation detail is the role word", secs[2]["role_detail"], "closure") + check("odd heading shape still parses", secs[3]["id"], "Q3") + check("multi-section map names Q1", p["ids_multi_section"], {"Q1": [5, 17]}) + check("status marker extracted verbatim", secs[0]["status"], "DONE at `abc1234`.") + check("status line number", secs[0]["status_line"], 7) + check("section with no marker is None", secs[1]["status"], None) + check("continuation status is its own", secs[2]["status"], "OPEN.") + check("nested ids found", [r["id"] for r in p["nested"]], ["Q41", "Q42"]) + check("nested owner is the containing section", p["nested"][0]["section"], "Q3") + check("nested shape is bullet carrying a bold lead", p["nested"][0]["shape"], "BULLET+BOLD") + check("nested shape is a plain bullet", p["nested"][1]["shape"], "BULLET") + check("sections tile the file with no gap", + all(secs[i]["end_line"] + 1 == secs[i + 1]["line"] for i in range(len(secs) - 1)), True) + check("last section ends at the last line", secs[-1]["end_line"], p["queue_lines"]) + check("preamble ends before the first heading", p["preamble"]["end_line"], 4) + check("byte accounting is exact", + p["preamble"]["bytes"] + sum(s["bytes"] for s in secs), p["queue_bytes"]) + check("bytes are the file's own length", p["queue_bytes"], len(FIXTURE.encode())) + check("sha256 is the file's own", p["queue_sha256"], + hashlib.sha256(FIXTURE.encode()).hexdigest()) + + # --- the gate, round trip and both directions of falsification ---------- + root = tempfile.mkdtemp(prefix="queue-index-selftest-") + repo = os.path.join(root, "repo") + os.makedirs(repo) + queue = os.path.join(repo, "WORK-QUEUE.md") + doc_path = os.path.join(repo, "index.md") + subprocess.check_call(["git", "-C", repo, "init", "-q", "-b", "main"]) + subprocess.check_call(["git", "-C", repo, "config", "user.email", "s@invalid"]) + subprocess.check_call(["git", "-C", repo, "config", "user.name", "s"]) + with open(queue, "w", encoding="utf-8") as fh: + fh.write(FIXTURE) + subprocess.check_call(["git", "-C", repo, "add", "-A"]) + subprocess.check_call(["git", "-C", repo, "commit", "-q", "-m", "fixture"]) + + import contextlib + import io + buf = io.StringIO() + with contextlib.redirect_stdout(buf): + rc = run(argparse.Namespace(repo=repo, queue="WORK-QUEUE.md", index=False, + json=None, write=doc_path, check=None)) + check("--write exits 0", rc, 0) + check("document written", os.path.exists(doc_path), True) + doc = open(doc_path, encoding="utf-8").read() + check("document carries the pinned block", extract_pinned(doc) is not None, True) + + buf = io.StringIO() + with contextlib.redirect_stdout(buf): + rc = run(argparse.Namespace(repo=repo, queue="WORK-QUEUE.md", index=False, + json=None, write=None, check=doc_path)) + check("--check on its own output exits 0", rc, 0) + check("--check says current", "index current:" in buf.getvalue(), True) + + # falsify 1: an append moves the last section's extent + with open(queue, "a", encoding="utf-8") as fh: + fh.write("- an appended line.\n") + buf = io.StringIO() + with contextlib.redirect_stdout(buf): + rc = run(argparse.Namespace(repo=repo, queue="WORK-QUEUE.md", index=False, + json=None, write=None, check=doc_path)) + check("--check goes RED on an append", rc, 1) + check("RED names the moved section", "changed extent or status" in buf.getvalue(), True) + + # falsify 2: a new heading is a new section + with open(queue, "a", encoding="utf-8") as fh: + fh.write("\n## Q7 — a brand new item\n\n**Status: OPEN.**\n") + buf = io.StringIO() + with contextlib.redirect_stdout(buf): + rc = run(argparse.Namespace(repo=repo, queue="WORK-QUEUE.md", index=False, + json=None, write=None, check=doc_path)) + check("--check goes RED on a new heading", rc, 1) + check("RED names the new section", "new section(s): Q7" in buf.getvalue(), True) + + # falsify 3: a document with no block cannot be verified + open(doc_path, "w", encoding="utf-8").write("# empty\n") + buf = io.StringIO() + with contextlib.redirect_stdout(buf): + rc = run(argparse.Namespace(repo=repo, queue="WORK-QUEUE.md", index=False, + json=None, write=None, check=doc_path)) + check("no pin -> exit 2", rc, 2) + check("no pin -> says so", "UNVERIFIABLE" in buf.getvalue(), True) + + print("selftest: %d cells, %d passed, %d failed" % (cells[0], cells[0] - bad[0], bad[0])) + return 1 if bad[0] else 0 + + +def main(argv=None): + ap = argparse.ArgumentParser( + description="queue_index.py -- the work queue's table of contents, and the gate " + "that it is current.") + ap.add_argument("--repo", default=".") + ap.add_argument("--queue", default=QUEUE_DEFAULT) + ap.add_argument("--index", action="store_true", help="print the section table") + ap.add_argument("--json", metavar="PATH", help="write the parse here") + ap.add_argument("--write", metavar="PATH", help="render the index document here") + ap.add_argument("--check", metavar="PATH", help="re-derive and compare a rendered index") + ap.add_argument("--selftest", action="store_true", help="fixture corpus, 32 cells") + args = ap.parse_args(argv) + if args.selftest: + return selftest() + return run(args) + + +if __name__ == "__main__": + sys.exit(main())