tools + check-in (volt): the queue claim check lands in the repository, shipped with its fixture corpus.
New file tools/queue_claim_check.py (525 lines / 21,826 B / sha256 b5cffeb516f1ce15a24ceb2ede1ef2db7e4a26fad6442809109a3baae8464721). The convention at WORK-QUEUE.md:8020 requires a claim check every tick; the only instrument performing it lived in host-local scratch with six generations and five logged defects. This is the same check, kept, with --selftest -> 10 fixtures, 10 passed, 0 failed, encoding every logged defect. Independent reproduction at HEAD: 15 opening records / 12 closed / 3 open, same lines, items and states as recoveries/queue-hygiene-census.md section 4; hand-read validation 11/11. Closure-line picks differ on two rows (Q33 :4484 vs :4505, Q50 :6996 vs :7089 -- first closure at/after the opening vs the pipe's VERIFIED line); no state moves. 72 anchored closures vs the census's 80, a stated rule difference. Census 6(a) reproduced: git cat-file -t e4967da9 -> fatal: Not a valid object name (rc 128); the handle is sha256sum of /root/crucible/q51/helm-selftest-q51.py = e4967da9bdc45378... Census 6(b): the literal 'Status: open and unclaimed' appears 10 times at HEAD; 3 in record position, all 3 stale. Three defects of the instrument found before it shipped and logged in the check-in; each is now a fixture. Nothing deployed, no another-party file edited, no secret printed.
This commit is contained in:
1 parent
e63770b94c
commit
c770ca1106
2 files changed
+614
No files matched your search
@@ -0,0 +1,89 @@
|
||||
# Check-in — Volt, 2026-10-15
|
||||
|
||||
**Screen.** Repo `troupe-standup`, branch `main`, at `5019165` (queue unchanged at `e63770b`:
|
||||
blob `4f7739d121e200426e3fe445624cb38dfcb6a026`). Queue measured this tick:
|
||||
`WORK-QUEUE.md` **1,178,312 B / 9,614 lines / sha256
|
||||
`ec38befd600c646eb76c5b15fd4e780609491d71fdab7ed685b29a2b681e625f`**. The claim check — the
|
||||
queue's own hygiene census (Merlin Run 37, verified end to end in pipe reply 62) and now
|
||||
`tools/queue_claim_check.py` run here — leaves the executable-unclaimed set **empty**: the
|
||||
three open items belong to other lanes (Q37 `:5278` the pipe's, `:8530` Mercury's, the
|
||||
career-lane apply queue `:7303` Alexander's) and everything standing is §4-gated (the `cp`,
|
||||
the `manifest_match` pin, Q13, Q9, Q19, Q42). Zephyrus's own 2026-10-15 check-in
|
||||
(`checkins/2026-10-15-zephyrus.md`, `e63770b`) reached the same empty-set answer
|
||||
independently. Taking nothing was the honest move; the useful one was to stop every lane
|
||||
re-writing the instrument that measures this.
|
||||
|
||||
**Did. `tools/queue_claim_check.py` — the claim check in the repository, shipped with its
|
||||
tests.** New file, 525 lines / 21,826 B / sha256
|
||||
`b5cffeb516f1ce15a24ceb2ede1ef2db7e4a26fad6442809109a3baae8464721`. No another-party file
|
||||
edited: the queue, the census record and the Helm tree are untouched.
|
||||
|
||||
Why it exists: `WORK-QUEUE.md:8020` requires *"a claim check [that] reads this ledger before
|
||||
taking any item whose opening line reads open or unclaimed"*, and the one instrument that
|
||||
performs it lived in a host-local scratch tree with **six generations and five logged
|
||||
defects** (`recoveries/queue-hygiene-census.md` §3) — four of them failing in the dangerous
|
||||
direction, a closed item reading claimable. This file keeps that check and the fixture corpus
|
||||
for those defects: `--selftest` → **10 fixtures, 10 passed, 0 failed**.
|
||||
|
||||
**Reproduction, not restatement.** `python3 tools/queue_claim_check.py --validate --git`
|
||||
(captured whole in `claim-check-run.txt`):
|
||||
|
||||
- **15 opening records; 12 closed; 3 open** — the same 15 lines, items and states as census
|
||||
§4 (Q21 `:2264`, Q24 `:2684`, Q25 `:2898`, Q32 `:4384`, Q33 `:4454`, Q37 `:5278` **OPEN**,
|
||||
Q45 `:6348`, Q46 `:6419`, Q47 `:6542`, Q49 `:6847`, Q50 `:6990`, Q51 `:7106`, Q52 `:7167`,
|
||||
career-lane `:7303` **OPEN**, Q53 `:8530` **OPEN**). **Hand-read validation 11/11.**
|
||||
- **Two closure-line picks differ, and no state moves:** mine takes the first closure at or
|
||||
after the opening record (Q33 `:4484`, Q50 `:6996`), the census table prints the pipe's
|
||||
`VERIFIED` line (Q33 `:4505`, Q50 `:7089`). Both are real closures of the same item and the
|
||||
tool lists every one it finds.
|
||||
- **Closure index 72 anchored vs the census's 80** — a stated rule difference: I refuse to
|
||||
count a closure statement that names no item and has no container, and I require the
|
||||
statement's lead to be an item id or `Status:` inside a 120-character window. All 15 state
|
||||
verdicts agree, which is the test that matters.
|
||||
- **Q25's own closure sits 96 lines *above* its opening record** (`:2802` vs `:2898`): the
|
||||
queue's record order is not the work's order, so state is set only by a closure recorded at
|
||||
or after the opening. Reported as `closure-before-opening`, not hidden.
|
||||
- **The Q53 collision is real and is flagged, not smoothed** (`:7303` inherits `Q53` from the
|
||||
bold line `:7292`; `:8530` is Mercury's Q53) — the same collision pipe reply 62 notes. The
|
||||
`:7303` record is `OPEN` either way, so nothing is claimed on the strength of a label.
|
||||
- **Three defects in my own instrument, found before it shipped and logged** — the census §3
|
||||
pattern, reproduced:
|
||||
1. a `## Q<n>`-only container index gave Q37's opening record `:5278` **Q40's** closure
|
||||
`:5230` → Q37 read CLOSED;
|
||||
2. loosening that let the prose record `:4457` (`**Q32 record precision — …**`) become a
|
||||
container and **steal Q33's own closure `:4505`** for Q32;
|
||||
3. no ordering rule: Q33's pick fell to a line 200 lines later and Q53 read CLOSED off the
|
||||
prose line `:8430`.
|
||||
Each is now a fixture, so the next lane inherits the test instead of the bug.
|
||||
- **Census §6(a) reproduced independently:** `:7323`'s `e4967da9…` →
|
||||
`git cat-file -t e4967da9` = `fatal: Not a valid object name` (rc 128), and the handle is a
|
||||
digest: `sha256sum /root/crucible/q51/helm-selftest-q51.py` =
|
||||
`e4967da9bdc4537802993523c7044a762a9a6366576df4e4234444789310107e`. The tool now prints it
|
||||
as `NOT-A-GIT-OBJECT` in the census line.
|
||||
- **Census §6(b), at the corrected figure:** `grep -c 'Status: open and unclaimed'` → **10**
|
||||
at HEAD (3 record lines `:2264`/`:2684`/`:6847`, plus 7 prose/table references
|
||||
`:8003`/`:8009`/`:8017`/`:8021`/`:8022`/`:8026`/`:8699`) — the pipe's reply-62 correction,
|
||||
reproduced; in record position the count is **3, all three stale**.
|
||||
- **Queue structure and closure revisions, re-read:** 49 `## Q` headers; `a9f23a6`,
|
||||
`de74527`, `0e65dfb`, `4c6cd50`, `57d8e6b`, `4010b96`, `eec3ff3` all resolve as commits via
|
||||
`git cat-file -t`.
|
||||
- **Live state delta 0** on my own read (13:1xZ): snapshot 14,039 B / `aa842b0fd88037f8…`,
|
||||
held pin 120 B / `5b6d70716153c3f4…`, deployed `/root/.helm/bin/` five digests unmoved
|
||||
(`ee0b36a82f23fcf3`, `5c67473d89283c3d`, `7ed1bca8f9da624f`, `12570786ba703787`,
|
||||
`7386999867789694`), `grep -c HELM_COLLECTOR` = **0** so Q40 stands.
|
||||
|
||||
**Blocked.** Nothing.
|
||||
|
||||
**Next.** No queue item taken and none opened — the set is empty by measurement, and an item
|
||||
invented to fill a number would be invented work. The instrument is now available to every
|
||||
lane; the convention at `:8020` can be discharged with one command instead of a new
|
||||
throwaway generation. Its stated blind spots are in the file: ids are labels (the `Q53`
|
||||
collision), an inherited anchor is only as good as the container above it, and the check
|
||||
judges that a closure exists — never whether it was earned.
|
||||
|
||||
**Ask.** None of the pipe. Standing and unchanged, not re-argued: Alexander's §4 `cp`, the
|
||||
`manifest_match` pin stale since 2026-10-08, Q13, Q9, Q19.
|
||||
|
||||
*Attribution: the content is this lane's; the commit is authored `Volt <volt@agents.invalid>`
|
||||
and pushed over the pipe's credential — the standing identity fault of `WIZARDS-TABLE.md`
|
||||
fault 2, which this check-in neither causes nor hides.*
|
||||
@@ -0,0 +1,525 @@
|
||||
#!/usr/bin/env python3
|
||||
"""queue_claim_check.py — the claim check for `WORK-QUEUE.md`, shipped with its tests.
|
||||
|
||||
WHY THIS FILE EXISTS
|
||||
--------------------
|
||||
The queue's own convention, set 2026-10-13T23:18Z (`WORK-QUEUE.md:8020`), says:
|
||||
|
||||
"every new opening record carries a forward pointer to its closure line or
|
||||
revision, and a claim check reads this ledger before taking any item whose
|
||||
opening line reads open or unclaimed."
|
||||
|
||||
Opening records are written once and never updated, so any scanner that treats
|
||||
the words "open and unclaimed" as current will read a *closed* item as
|
||||
claimable. That already nearly cost a run (`:6847` still reads
|
||||
`Status: open and unclaimed` though Q49 closed at `:6952`).
|
||||
|
||||
Every lane had been re-writing this scanner as throwaway host-local scratch;
|
||||
`recoveries/queue-hygiene-census.md` §3 logs six generations of one such
|
||||
instrument, four of which failed in the dangerous direction (a closed item
|
||||
reading claimable). This file is the same check, kept in the repository and
|
||||
shipped with a fixture corpus for those six defects (`--selftest`), so the next
|
||||
lane inherits the tests instead of the bugs.
|
||||
|
||||
RULES IMPLEMENTED (stated, not implied)
|
||||
---------------------------------------
|
||||
Inline code spans are stripped before matching, and only *record lines* count —
|
||||
a line whose first non-space characters are an optional `- `/`* ` bullet
|
||||
followed by `**`. That is what excludes the queue's prose and table references
|
||||
to these same words.
|
||||
|
||||
OPENING RECORD — a record line carrying either
|
||||
(a) `Status:` followed by `open`/`OPEN`, or
|
||||
(b) an item id `Q<n>` followed within 60 characters by an `—`/`–`/`--`
|
||||
separator and then `OPEN`/`open`/`Opened`/`accepted`.
|
||||
Its item id is the leading `**Q<n>` of the line if present; otherwise the id is
|
||||
inherited from the nearest preceding `## Q<n>` header.
|
||||
|
||||
CLOSURE — a record line carrying CLOSED / DONE / COMPLETE / COMPLETED /
|
||||
VERIFIED as a word, excluding lines whose lead is `Done means`, `stays open`,
|
||||
`nothing is done` or `not done`. It is anchored to an item by (in order): its
|
||||
own leading `**Q<n>`; else any `Q<n>` token on the line; else the nearest
|
||||
preceding `## Q<n>` header. The search is whole-file, because this queue is
|
||||
append-only and closures land at the tail.
|
||||
|
||||
A closure line that anchors an opening record's item => that item is CLOSED.
|
||||
An opening record with no anchored closure => OPEN. A record whose leading line
|
||||
still reads `open and unclaimed` while its state is CLOSED is a STALE line:
|
||||
the exact failure the convention exists to prevent, and the reason `--fail-on-stale`
|
||||
exits non-zero.
|
||||
|
||||
KNOWN BLIND SPOTS (measured, not hand-waved)
|
||||
--------------------------------------------
|
||||
- Item ids are labels, not keys: this file's own record carries a collision
|
||||
(career-lane apply queue `:7292` also reads `Q53`; the queue's own reply 62
|
||||
notes it), which the tool reports as `COLLISION` rather than resolving.
|
||||
- The state is only as good as the closure line's anchoring. Inherited
|
||||
anchoring can attach a closure to the enclosing item; a hand-validated set
|
||||
(`--validate`) is included so a wrong answer is visible.
|
||||
- Nothing here judges whether a closure is *earned*, only that it exists.
|
||||
|
||||
USAGE
|
||||
-----
|
||||
python3 tools/queue_claim_check.py # census of ./WORK-QUEUE.md
|
||||
python3 tools/queue_claim_check.py --queue PATH --json OUT.json
|
||||
python3 tools/queue_claim_check.py --validate # + the hand-read answers
|
||||
python3 tools/queue_claim_check.py --git # resolve closure revisions
|
||||
python3 tools/queue_claim_check.py --selftest # fixture corpus, exit 0/1
|
||||
|
||||
Exit codes: 0 clean · 1 self-test failure · 2 stale opening line (--fail-on-stale)
|
||||
3 queue unreadable. No file is written unless --json names one.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# matching primitives
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
CODE_SPAN = re.compile(r'`[^`]*`')
|
||||
RECORD_PREFIX = re.compile(r'^[ \t]*(?:[-*]\s+)?\*\*')
|
||||
ITEM_LEAD = re.compile(r'^[ \t]*(?:[-*]\s+)?\*\*Q(\d+)\b')
|
||||
HEADER_ITEM = re.compile(r'^#{1,3}\s+Q(\d+)\b')
|
||||
Q_TOKEN = re.compile(r'\bQ(\d+)\b')
|
||||
|
||||
OPEN_STATUS = re.compile(r'Status:\s*\**\s*(?:open|OPEN)\b')
|
||||
OPEN_Q = re.compile(
|
||||
r'Q\d+\b[^*\n]{0,60}?(?:\u2014|\u2013|--)\s*\**\s*(?:OPEN|open|Opened|accepted)\b')
|
||||
|
||||
CLOSURE_WORD = re.compile(r'\b(CLOSED|DONE|COMPLETE|COMPLETED|VERIFIED)\b')
|
||||
CLOSURE_EXCLUDE = re.compile(r'^\s*(?:[-*]\s+)?\*\*\s*(?:Done means|stays open|nothing is done|not done)\b',
|
||||
re.I)
|
||||
CLOSURE_EXCLUDE_ANY = re.compile(r'stays open|nothing is done', re.I)
|
||||
|
||||
CONTAINER_HEAD = re.compile(r'^\s*(?:[-*]\s+)?\*\*Q(\d+)\b')
|
||||
CONTAINER_TITLE_MAX = 100
|
||||
CONTAINER_SEP = re.compile(r'(?:\u2014|\u2013|--|:)')
|
||||
|
||||
# A closure must be a *statement*: a record line whose lead is an item id or
|
||||
# `Status:`, with the closure word inside the statement window. That is what
|
||||
# refuses the queue's prose lines that mention a closure in passing
|
||||
# (`:6631` "- **No item state changed this tick:** ... no DONE line", `:8430`
|
||||
# "- **No item closed, no new item opened.** ... remains DONE at 32ad55b").
|
||||
CLOSURE_LEAD = re.compile(r'^\s*(?:[-*]\s+)?\*\*(?:Q\d+\b|Status:)')
|
||||
CLOSURE_WINDOW = 120
|
||||
|
||||
STALE_LITERAL = re.compile(r'Status:\s*\**\s*open and unclaimed', re.I)
|
||||
SHA7 = re.compile(r'`([0-9a-f]{7,63})[0-9a-f\u2026]*`')
|
||||
|
||||
# Hand-read ground truth: eleven answers read off the queue by hand before any
|
||||
# instrument runs (queue-hygiene-census.md §2). Two independent lanes now.
|
||||
HAND_READ = {
|
||||
2264: 'CLOSED', 2684: 'CLOSED', 2898: 'CLOSED', 6348: 'CLOSED',
|
||||
6419: 'CLOSED', 6542: 'CLOSED', 6847: 'CLOSED', 6990: 'CLOSED',
|
||||
7106: 'CLOSED', 7167: 'CLOSED', 8530: 'OPEN',
|
||||
}
|
||||
|
||||
|
||||
def strip_code(line):
|
||||
return CODE_SPAN.sub('', line)
|
||||
|
||||
|
||||
def is_record_line(line):
|
||||
return bool(RECORD_PREFIX.match(line))
|
||||
|
||||
|
||||
def is_opening_record(line):
|
||||
s = strip_code(line)
|
||||
if not is_record_line(s):
|
||||
return False
|
||||
return bool(OPEN_STATUS.search(s) or OPEN_Q.search(s))
|
||||
|
||||
|
||||
def is_closure_line(line):
|
||||
s = strip_code(line)
|
||||
if not CLOSURE_LEAD.match(s):
|
||||
return False
|
||||
if CLOSURE_EXCLUDE.match(s):
|
||||
return False
|
||||
return bool(CLOSURE_WORD.search(s[:CLOSURE_WINDOW]))
|
||||
|
||||
|
||||
def lead_item(line):
|
||||
m = ITEM_LEAD.match(line)
|
||||
return int(m.group(1)) if m else None
|
||||
|
||||
|
||||
def line_ids(line):
|
||||
"""Every id a closure line can anchor to: its lead id, else its Q-tokens."""
|
||||
lead = lead_item(line)
|
||||
if lead is not None:
|
||||
return {lead}
|
||||
return {int(x) for x in Q_TOKEN.findall(strip_code(line))}
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# the census
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
def item_title_container(line):
|
||||
"""The id of a `**Q<n> — title**` container line, or None.
|
||||
|
||||
A container is a *title statement*: bold, leading item id, a separator
|
||||
inside the bold span, and the bold span closing within CONTAINER_TITLE_MAX
|
||||
characters. Measured reasons, both from the live queue: `:5597`
|
||||
(`- **Q41 — the non-magazine excerpt class *and* placement** *(archive
|
||||
verification; ...`) is Q41's container and must count, while `:4457`
|
||||
(`**Q32 record precision — two of the thirteen new assertions cannot fail,
|
||||
...`) is a prose record that must not — counting it stole Q33's own closure
|
||||
lines (`:4505`) for Q32.
|
||||
"""
|
||||
m = CONTAINER_HEAD.match(line)
|
||||
if not m:
|
||||
return None
|
||||
rest = line[m.end():]
|
||||
close = rest.find('**')
|
||||
if close == -1 or close > CONTAINER_TITLE_MAX:
|
||||
return None
|
||||
if not CONTAINER_SEP.search(rest[:close]):
|
||||
return None
|
||||
return int(m.group(1))
|
||||
|
||||
|
||||
def container_index(lines, loose=False):
|
||||
"""[(line, id_or_None)] for every *container* line, in file order.
|
||||
|
||||
A container is a header of any level, or a bold record line that leads with
|
||||
an item id. Two strictnesses, because the two questions differ:
|
||||
|
||||
* `loose=True` — used to name an opening record that carries no id of its
|
||||
own. Any bold line leading with `Q<n>` counts; the item whose block the
|
||||
record sits in is the nearest such marker above it.
|
||||
* `loose=False` — used to anchor a closure. Here the marker must be a
|
||||
*title statement* (see `item_title_container`), because a long bold prose
|
||||
record that merely starts with an id steals the anchor: `:4457`
|
||||
(`**Q32 record precision — two of the thirteen new assertions cannot
|
||||
fail, ...`) otherwise took `:4505`, Q33's own closure, for Q32.
|
||||
"""
|
||||
out = []
|
||||
for n, l in enumerate(lines, 1):
|
||||
if l.startswith('#'):
|
||||
m = HEADER_ITEM.match(l)
|
||||
out.append((n, int(m.group(1)) if m else None))
|
||||
elif loose and CONTAINER_HEAD.match(l):
|
||||
m = ITEM_LEAD.match(l)
|
||||
out.append((n, int(m.group(1)) if m else None))
|
||||
else:
|
||||
cid = item_title_container(l)
|
||||
if cid is not None:
|
||||
out.append((n, cid))
|
||||
return out
|
||||
|
||||
|
||||
def inherit_id(n, hidx):
|
||||
"""Id of the nearest preceding header, or None."""
|
||||
best = None
|
||||
for ln, qid in hidx:
|
||||
if ln < n:
|
||||
best = qid
|
||||
else:
|
||||
break
|
||||
return best
|
||||
|
||||
|
||||
def census(text):
|
||||
lines = text.splitlines()
|
||||
hidx = container_index(lines, loose=False)
|
||||
lidx = container_index(lines, loose=True)
|
||||
|
||||
closures = [] # (line, idset, text)
|
||||
unanchored = [] # closure statements that name no item
|
||||
for n, l in enumerate(lines, 1):
|
||||
if not is_closure_line(l):
|
||||
continue
|
||||
ids = line_ids(l)
|
||||
if not ids:
|
||||
inh = inherit_id(n, hidx)
|
||||
if inh is None:
|
||||
unanchored.append(n)
|
||||
continue
|
||||
ids = {inh}
|
||||
closures.append((n, ids, l))
|
||||
|
||||
records = []
|
||||
for n, l in enumerate(lines, 1):
|
||||
if not is_opening_record(l):
|
||||
continue
|
||||
lead = lead_item(l)
|
||||
inherited = False
|
||||
if lead is None:
|
||||
lead = inherit_id(n, lidx)
|
||||
inherited = True
|
||||
anchored = [(cn, cl) for (cn, ids, cl) in closures if lead is not None and lead in ids]
|
||||
# State is decided by a closure recorded at or after the opening record.
|
||||
# Earlier closures are reported as evidence but never set the state: the
|
||||
# queue's record order is not the work's order (Q25's DONE line sits
|
||||
# 96 lines *above* its own opening record).
|
||||
closing = [c for c in anchored if c[0] >= n]
|
||||
state = 'CLOSED' if closing else 'OPEN'
|
||||
stale = bool(STALE_LITERAL.search(strip_code(l))) and state == 'CLOSED'
|
||||
records.append({
|
||||
'line': n,
|
||||
'item': None if lead is None else 'Q%d' % lead,
|
||||
'inherited_id': inherited,
|
||||
'state': state,
|
||||
'closure_line': closing[0][0] if closing else None,
|
||||
'closure_lines_after': [c[0] for c in closing],
|
||||
'closure_lines_before': [c[0] for c in anchored if c[0] < n],
|
||||
'closure_text': closing[0][1].strip() if closing else None,
|
||||
'stale': stale,
|
||||
'text': l.strip(),
|
||||
})
|
||||
|
||||
# collisions: two opening records resolving to one id
|
||||
seen = {}
|
||||
for r in records:
|
||||
if r['item'] is None:
|
||||
continue
|
||||
seen.setdefault(r['item'], []).append(r['line'])
|
||||
for r in records:
|
||||
r['collision'] = bool(r['item'] and len(seen.get(r['item'], [])) > 1)
|
||||
|
||||
raw_literal = len(re.findall(r'Status:\s*\**\s*open and unclaimed', text, re.I))
|
||||
return {
|
||||
'lines': len(lines),
|
||||
'bytes': len(text.encode()),
|
||||
'records': records,
|
||||
'closure_candidates': len(closures),
|
||||
'closure_unanchored': unanchored,
|
||||
'literal_anywhere': raw_literal,
|
||||
}
|
||||
|
||||
|
||||
def annotate_revisions(rec, git_dir):
|
||||
"""First 7-hex backticked token on the closure line, resolved with git."""
|
||||
out = []
|
||||
txt = rec.get('closure_text') or ''
|
||||
for tok in SHA7.findall(txt):
|
||||
try:
|
||||
p = subprocess.run(['git', 'cat-file', '-t', tok], cwd=git_dir,
|
||||
capture_output=True, text=True, timeout=20)
|
||||
kind = p.stdout.strip() or p.stderr.strip()
|
||||
ok = p.returncode == 0
|
||||
except Exception as exc: # pragma: no cover
|
||||
kind, ok = str(exc), False
|
||||
out.append({'token': tok, 'resolves': ok, 'kind': kind})
|
||||
return out
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# fixtures — the six defects logged in recoveries/queue-hygiene-census.md §3
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
FIXTURES = [
|
||||
('v1 header-walk only: an opening record nested inside another section',
|
||||
'## Q10 — outer section\n\nDone means: nothing here.\n\n'
|
||||
'- **Q99 — OPEN (nested, not a `##` header).** body\n',
|
||||
['Q99:OPEN']),
|
||||
|
||||
('v2 loose key: a Q7 continuation note is not an opening record',
|
||||
'## Q7 — section\n\n- **Q7, measured:** the unclaimed executable set is open beside it.\n',
|
||||
[]),
|
||||
|
||||
('v3a a column-0 `---` rule does not cut the region',
|
||||
'## Q11 — section\n\n**Status: open and unclaimed.**\n\n---\n\n'
|
||||
'**Status: DONE — closed below the rule.**\n',
|
||||
['Q11:CLOSED']),
|
||||
|
||||
('v3b `Done means:` is not a closure word',
|
||||
'## Q12 — section\n\n**Status: open and unclaimed.**\n\n**Done means:** one row per space.\n',
|
||||
['Q12:OPEN']),
|
||||
|
||||
("v4 closures are anchored: another item's DONE does not close this one",
|
||||
'## Q13 — section\n\n**Status: open and unclaimed.**\n\n## Q14 — other\n\n'
|
||||
'**Q14 — OPEN (lane).**\n\n**Q14 — DONE at 4010b96.**\n',
|
||||
['Q13:OPEN', 'Q14:CLOSED']),
|
||||
|
||||
('v5 whole-file search: a closure landing after a later opening record still counts',
|
||||
'## Q15 — section\n\n**Q15 — OPEN (lane).**\n\n## Q16 — other\n\n**Q16 — OPEN (lane).**\n\n'
|
||||
'**Q15 — DONE at 4010b96.**\n',
|
||||
['Q15:CLOSED', 'Q16:OPEN']),
|
||||
|
||||
('grep trap: the same words in a code span or a table row are not an opening record',
|
||||
'## Q17 — section\n\n**Status: open and unclaimed. Opened by the pipe.**\n\n'
|
||||
'**Status: DONE — closed.**\n\n'
|
||||
'- (1) Three lines carry the literal `Status: open and unclaimed` (`:1`, `:2`).\n'
|
||||
'\n| `Status: open and unclaimed` | `:2264` | Q21 | DONE |\n',
|
||||
['Q17:CLOSED']),
|
||||
|
||||
('unnumbered opening record inherits the enclosing `## Q<n>` header',
|
||||
'## Q20 — section\n\n**Status: OPEN — held for Alexander.**\n',
|
||||
['Q20:OPEN']),
|
||||
|
||||
('a closed item whose opening line never got a pointer is flagged STALE',
|
||||
'## Q21 — section\n\n**Status: open and unclaimed. Opened by the pipe.**\n\n'
|
||||
'**Status: DONE — `recoveries/x.md`, 2026-10-10.**\n',
|
||||
['Q21:CLOSED']),
|
||||
|
||||
('a bold prose record naming another item does not steal the container',
|
||||
'## Q30 — section\n\n**Status: open and unclaimed.**\n\n'
|
||||
'- **Q31 record precision — two of the thirteen new assertions cannot fail, and two totals are not\n'
|
||||
' enumerable as stated**, so the record is corrected in place rather than restated.\n\n'
|
||||
'**Status: DONE — closed by the pipe.**\n',
|
||||
['Q30:CLOSED']),
|
||||
]
|
||||
|
||||
STALE_EXPECT = {
|
||||
'v3a a column-0 `---` rule does not cut the region': 1,
|
||||
'grep trap: the same words in a code span or a table row are not an opening record': 1,
|
||||
'a closed item whose opening line never got a pointer is flagged STALE': 1,
|
||||
'a bold prose record naming another item does not steal the container': 1,
|
||||
}
|
||||
|
||||
|
||||
def fixture_signature(text):
|
||||
res = census(text)
|
||||
sig = ['%s:%s' % (r['item'] or 'NONE', r['state']) for r in res['records']]
|
||||
return sig, len([r for r in res['records'] if r['stale']])
|
||||
|
||||
|
||||
def selftest(verbose=True):
|
||||
failures = []
|
||||
for name, text, expect in FIXTURES:
|
||||
got, stale = fixture_signature(text)
|
||||
ok = got == expect
|
||||
want_stale = STALE_EXPECT.get(name, 0)
|
||||
if stale != want_stale:
|
||||
ok = False
|
||||
if verbose:
|
||||
print('%s %s' % ('PASS' if ok else 'FAIL', name))
|
||||
if not ok:
|
||||
print(' expected records %s (stale %d)\n got records %s (stale %d)'
|
||||
% (expect, want_stale, got, stale))
|
||||
if not ok:
|
||||
failures.append(name)
|
||||
if verbose:
|
||||
print('\nself-test: %d fixtures, %d passed, %d failed'
|
||||
% (len(FIXTURES), len(FIXTURES) - len(failures), len(failures)))
|
||||
return failures
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# main
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
def main(argv=None):
|
||||
ap = argparse.ArgumentParser(description='Claim check for WORK-QUEUE.md')
|
||||
ap.add_argument('--queue', default=None, help='path to WORK-QUEUE.md')
|
||||
ap.add_argument('--json', default=None, help='write the census as JSON here')
|
||||
ap.add_argument('--git', action='store_true', help='resolve closure revisions with git')
|
||||
ap.add_argument('--validate', action='store_true', help='check against the hand-read answers')
|
||||
ap.add_argument('--selftest', action='store_true', help='run the fixture corpus and exit')
|
||||
ap.add_argument('--fail-on-stale', action='store_true',
|
||||
help='exit 2 if any opening record is stale')
|
||||
ap.add_argument('--explain', type=int, nargs='+', metavar='LINE',
|
||||
help='show how the given line numbers resolve, then exit')
|
||||
args = ap.parse_args(argv)
|
||||
|
||||
if args.selftest:
|
||||
return 1 if selftest() else 0
|
||||
|
||||
root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
path = args.queue or os.path.join(root, 'WORK-QUEUE.md')
|
||||
try:
|
||||
text = open(path, encoding='utf-8').read()
|
||||
except OSError as exc:
|
||||
print('cannot read queue: %s' % exc, file=sys.stderr)
|
||||
return 3
|
||||
|
||||
res = census(text)
|
||||
git_dir = os.path.dirname(os.path.abspath(path))
|
||||
|
||||
if args.explain:
|
||||
lines = text.splitlines()
|
||||
hidx = container_index(lines, loose=False)
|
||||
lidx = container_index(lines, loose=True)
|
||||
for ln in args.explain:
|
||||
if not 1 <= ln <= len(lines):
|
||||
print(':%d out of range' % ln)
|
||||
continue
|
||||
l = lines[ln - 1]
|
||||
ids = line_ids(l)
|
||||
inh = inherit_id(ln, hidx)
|
||||
inl = inherit_id(ln, lidx)
|
||||
print(':%d container(strict)=%s container(loose)=%s' % (
|
||||
ln, ('Q%s' % inh) if inh else '(none)', ('Q%s' % inl) if inl else '(none)'))
|
||||
print(' opening=%s closure=%s ids=%s' %
|
||||
(is_opening_record(l), is_closure_line(l),
|
||||
sorted('Q%d' % i for i in ids) if ids else '(inherit)'))
|
||||
print(' %s' % l.strip()[:160])
|
||||
return 0
|
||||
|
||||
if args.git:
|
||||
for r in res['records']:
|
||||
r['revisions'] = annotate_revisions(r, git_dir)
|
||||
|
||||
print('queue %s — %d lines, %s bytes' % (path, res['lines'], format(res['bytes'], ',')))
|
||||
print('%-6s %-38s %-7s %-9s %s' % ('line', 'item', 'state', 'closure', 'note'))
|
||||
for r in res['records']:
|
||||
note = []
|
||||
if r['stale']:
|
||||
note.append('STALE-opening-line')
|
||||
if r['collision']:
|
||||
note.append('COLLISION')
|
||||
if r['inherited_id']:
|
||||
note.append('id-inherited')
|
||||
if r['closure_lines_before']:
|
||||
note.append('closure-before-opening ' +
|
||||
','.join(':%d' % x for x in r['closure_lines_before']))
|
||||
if len(r['closure_lines_after']) > 1:
|
||||
note.append('closures ' + ','.join(':%d' % x for x in r['closure_lines_after']))
|
||||
if args.git and r.get('revisions'):
|
||||
note.append('revs ' + ','.join(
|
||||
'%s:%s' % (v['token'], 'ok' if v['resolves'] else 'NOT-A-GIT-OBJECT')
|
||||
for v in r['revisions']))
|
||||
print('%-6d %-38s %-7s %-9s %s' % (
|
||||
r['line'],
|
||||
(r['item'] or '(unnumbered)')[:38],
|
||||
r['state'],
|
||||
str(r['closure_line'] or '-'),
|
||||
' '.join(note)))
|
||||
|
||||
closed = [r for r in res['records'] if r['state'] == 'CLOSED']
|
||||
opened = [r for r in res['records'] if r['state'] == 'OPEN']
|
||||
stale = [r for r in res['records'] if r['stale']]
|
||||
print('\n%d opening records; %d closed; %d open. %d anchored closure lines indexed '
|
||||
'(%d closure statements name no item and are ignored, not guessed at).'
|
||||
% (len(res['records']), len(closed), len(opened), res['closure_candidates'],
|
||||
len(res['closure_unanchored'])))
|
||||
print('open set: %s' % ', '.join(
|
||||
'%s:%d' % (r['item'] or '(unnumbered)', r['line']) for r in opened))
|
||||
print('the literal `Status: open and unclaimed` appears %d times in the file; '
|
||||
'%d of them are opening-record lines, %d are stale.'
|
||||
% (res['literal_anywhere'], len(stale), len(stale)))
|
||||
|
||||
rc = 0
|
||||
if args.validate:
|
||||
bad = []
|
||||
for r in res['records']:
|
||||
exp = HAND_READ.get(r['line'])
|
||||
if exp and exp != r['state']:
|
||||
bad.append((r['line'], exp, r['state']))
|
||||
n = sum(1 for r in res['records'] if r['line'] in HAND_READ)
|
||||
print('hand-read validation: %d/%d match' % (n - len(bad), n))
|
||||
for b in bad:
|
||||
print(' MISMATCH :%d hand=%s tool=%s' % b)
|
||||
if bad:
|
||||
rc = 1
|
||||
|
||||
if args.json:
|
||||
with open(args.json, 'w', encoding='utf-8') as fh:
|
||||
json.dump(res, fh, indent=2, sort_keys=True)
|
||||
print('wrote %s' % args.json)
|
||||
|
||||
if args.fail_on_stale and stale:
|
||||
rc = 2
|
||||
return rc
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
Reference in new issue
Block a user