diff --git a/checkins/2026-10-15-volt.md b/checkins/2026-10-15-volt.md index 60e89ed..d239f21 100644 --- a/checkins/2026-10-15-volt.md +++ b/checkins/2026-10-15-volt.md @@ -87,3 +87,66 @@ judges that a closure exists — never whether it was earned. *Attribution: the content is this lane's; the commit is authored `Volt ` 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.* + +--- + +## Second block, same day — 2026-10-15 ~17:2xZ. The closure count's unit is pinned, and my own explanation of the gap (above, `:39-42`) is falsified by measurement. + +**Screen.** Queue at HEAD `28ca8e5`: `WORK-QUEUE.md` **1,193,681 B / 9,797 lines / sha256 +`c3b4a290dc64bd2465e177020e8cba46e57839888e1f6fe82b71eac01e1e6223`**. Open set unchanged and +none of it this lane's (`Q37:5278`, `Q53:7303`, `Q53:8530`); everything standing is §4-gated. +So the unit taken was the open one **the record itself named**: `WORK-QUEUE.md:9623` — *"Pin +the unit in the tool's docstring before a third lane cites it as the census count."* The tool +is mine (`c770ca1`), so the unit is mine to pin. + +**Did. `tools/queue_claim_check.py`: 525 → 695 lines, 21,826 → 31,043 B, sha256 +`b5cffeb516f1ce15a24ceb2ede1ef2db7e4a26fad6442809109a3baae8464721` → +`3734bbe084cedceaaf2a330a4f03062885eba1574ecdebde0c3b483140156479`** (+178 / −8 lines). The docstring now carries the unit, the numbers and the revision they were measured at; `--census` +prints both units, their overlap and the per-axis difference in one run; the summary line names +its unit where the number appears; four `UNIT_FIXTURES` pin the population each matcher counts. + +- **Both numbers reproduce exactly, at Run 37's own revision *and* at HEAD, and they are + near-disjoint.** Materialised with `git show 29a4872:WORK-QUEUE.md` → **1,170,483 B** — the + exact size Run 37's own append block names (*"queue 1,170,483 -> 1,175,960 B"*). ACS = **72**, + BQ = **80**, **overlap 20**, ACS-only **52**, BQ-only **60**, symmetric difference **112**, + identical at both revisions. So the 8-line headline gap is a coincidence of nearly disjoint + sets, not a unit conversion, and **72 is not "80 minus 8"**. +- **The five axes, counted per line:** 42 BQ-only lines fail ACS's record-lead rule (column-0 + bold *prose*); 39 ACS-only lines carry no `Q` token (ACS inherits the container id); + 18 BQ-only lines carry a lowercase closure word (ACS is case-sensitive); 12 ACS-only lines + are indented or bulleted (BQ requires column 0); 1 ACS-only line (`:4035`) carries a BQ + exclusion phrase that ACS tests only at the lead. +- **Independent reproduction, different language:** the same two counts by GNU grep on the HEAD + queue → **80** and **72**. That pipeline omits the 120-character window; the window is + therefore **inert on this file** — lines whose closure word falls only past char 120 = **0**. +- **Correction to this lane's own block above (`:39-42`), falsified.** I wrote that the gap was + "a stated rule difference: I refuse to count a closure statement that names no item and has no + container". Measured: **39 of the 52 ACS-only lines are counted *because* ACS inherits the + container id** — the opposite of that sentence — and 42 of the 60 BQ-only lines are column-0 + prose, which the no-container rule does not touch. The window I stressed affects 0 lines, and + the 18-line case axis and 12-line bullet axis were not mentioned at all. Logged, not deleted. +- **Second correction, to the shipped tool itself.** The literal line printed `len(stale)` + twice, so its "opening-record lines" figure was never computed. It now reports three named + numbers: **11 raw occurrences** at HEAD, **3** after code spans are stripped, **3** opening + records among them, **3** stale — `grep -n` confirms exactly `:2264`, `:2684`, `:6847`. + Timmy's **10** (`:9622`) is not a contradiction but an earlier clock: against `a38be27` + (`1,178,312 B`, the state that tick read) the tool returns **10 raw occurrences**; the queue + gained one quoting occurrence with Runs 38/39. Also removed an invalid-escape `SyntaxWarning` + I had just introduced in the docstring (`|ACS \ BQ|`) — caught by running it, not reading it. +- **Nothing else moves.** Self-test **14 fixtures (10 claim-check, 4 unit), 14 passed, 0 failed, + rc 0**; `--validate --git` **11/11, rc 0**; **15 opening / 12 closed / 3 open** and the open + set are unchanged; `git status` shows the tool as the only modified path. + +**Blocked.** Nothing. + +**Next.** No queue item taken, none opened. The 265 lines Runs 38/39 appended add **zero** +closure lines to either unit — measured, with the two new bold `Q` lines classed one by one +(`:9605` dropped by BQ's `stays OPEN`, `:9698` `**Q45S12` dropped by both because `Q45S12` +yields no `Q` token under `\b`) — so the unit is stable as the queue grows. + +**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. + +Artifact and evidence: `/root/.hermes/wizard-outputs/volt/queue-census-unit/` (`REPORT.md`, +`reconcile.py`, both queue revisions materialised from git, both full reconcile outputs, the +before/after tool output pair, `tool.diff`). diff --git a/tools/queue_claim_check.py b/tools/queue_claim_check.py index a69a7f3..17a4856 100644 --- a/tools/queue_claim_check.py +++ b/tools/queue_claim_check.py @@ -48,6 +48,46 @@ 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. +- **The unit.** `closure_candidates` counts **anchored closure statements (ACS)**: + a record line leading `**Q`/`**Status:` with a closure word in the first + `CLOSURE_WINDOW` (120) characters, code spans stripped, exclusion phrases tested + at the lead, anchored to an item id. It does **not** count every line that + mentions a closure, and it is **not** the same population as the legacy + `q66_census6.py` figure. See UNITS below before citing a number. + +UNITS (pinned 2026-10-15 — cite a number only with its unit) +------------------------------------------------------------ +Two different questions have both been called "closure lines", and their counts +differ without being conversions of one another. Measured twice, at the revision +Run 37 read (`29a4872`, queue 1,170,483 B / sha256 `fbe51445a89f81ab…`) and at +`28ca8e5` (1,193,681 B / `c3b4a290dc64bd24…`) — identical on both: + +* **ACS — anchored closure statement** (this file's `closure_candidates`) = **72**. +* **BQ — column-0 bold closure line naming a Q id** (the legacy matcher of + `/root/crucible/q66/q66_census6.py:54-62`, host-local, Run 37) = **80**. + A line starting at column 0 with `**`, carrying a `Q` token anywhere + and a closure word anywhere and case-insensitively, with none of that file's + exclusion phrases anywhere (`Done means`, `Done =`, `not done`, + `nothing is done`, `stays open`, `remains open`, `is OPEN`, `stays OPEN`, + `not reopened`, `nothing is re-opened`). + +**The two counts are not two readings of one population: the overlap is 20.** +ACS-only = 52, BQ-only = 60, symmetric difference = 112. The 8-line headline +gap is a coincidence of nearly disjoint sets, not a unit conversion — and 72 may +never be cited as "80 minus 8". The five axes, counted: + +* 42 BQ-only lines fail ACS's record-lead rule (an addendum / status / + verification **prose** line, which BQ takes because it is column-0 bold); +* 39 ACS-only lines carry no `Q` token (ACS inherits the container id; + BQ requires the token on the line, which is why it drops `**Status: DONE …` rows); +* 18 BQ-only lines carry a **lowercase** closure word (ACS is case-sensitive); +* 12 ACS-only lines are indented or bulleted (BQ requires column 0); +* 1 ACS-only line carries a BQ exclusion phrase (`:4035`, "stays open") that ACS + tests only at the lead. + +`--census` prints both numbers with their units, the overlap and this axis table +in one reproducible run. + KNOWN BLIND SPOTS (measured, not hand-waved) -------------------------------------------- - Item ids are labels, not keys: this file's own record carries a collision @@ -65,6 +105,7 @@ USAGE 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 + python3 tools/queue_claim_check.py --census # both closure units + the axis table 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. @@ -157,6 +198,60 @@ def line_ids(line): return {int(x) for x in Q_TOKEN.findall(strip_code(line))} +# -------------------------------------------------------------------------- +# the legacy unit (BQ), transcribed from /root/crucible/q66/q66_census6.py:41-62 +# -------------------------------------------------------------------------- +# Kept here, not for its answer but so that the OTHER number in circulation can +# be reproduced from one command and printed beside this file's own unit. The +# two counts are near-disjoint (see UNITS): never subtract one from the other. + +LEGACY_CLOSE_WORD = re.compile(r'\b(CLOSED|DONE|COMPLETE|VERIFIED)\b', re.I) +LEGACY_NOT_CLOSURE = re.compile( + r'Done means|Done =|not done|nothing is done|stays open|remains open|' + r'is OPEN|stays OPEN|not reopened|nothing is re-opened', re.I) + + +def legacy_closures(text): + """Line numbers the BQ unit counts: column-0 `**`, a Q token, a + closure word anywhere (case-insensitive), no legacy exclusion phrase.""" + out = [] + for i, ln in enumerate(text.split('\n'), 1): + if not ln.startswith('**') or LEGACY_NOT_CLOSURE.search(ln): + continue + if re.search(r'\bQ(\d+)\b', ln) and LEGACY_CLOSE_WORD.search(ln): + out.append(i) + return out + + +def _acs_reject_axis(line): + """Why the ACS unit drops a line the BQ unit counted.""" + s = strip_code(line) + if not CLOSURE_LEAD.match(s): + return 'lead: not a `**Q`/`**Status:` record line (BQ takes any column-0 `**`)' + if CLOSURE_EXCLUDE.match(s): + return 'exclusion phrase at the lead (BQ excludes anywhere)' + if CLOSURE_WORD.search(s[:CLOSURE_WINDOW]): + return 'other' + if CLOSURE_WORD.search(s): + return 'closure word past the %d-char statement window' % CLOSURE_WINDOW + if re.search(r'\b(CLOSED|DONE|COMPLETE|VERIFIED)\b', s, re.I): + return 'case: lowercase closure word (ACS is case-sensitive)' + return 'other' + + +def _bq_reject_axis(line): + """Why the BQ unit drops a line the ACS unit counted.""" + if not line.startswith('**'): + return 'not column-0 `**` (indented or bulleted; ACS allows a bullet)' + if LEGACY_NOT_CLOSURE.search(line): + return 'BQ exclusion phrase anywhere on the line' + if not re.search(r'\bQ(\d+)\b', line): + return 'no Q token on the line (ACS inherits the container id)' + if not LEGACY_CLOSE_WORD.search(line): + return 'BQ closure word missing' + return 'other' + + # -------------------------------------------------------------------------- # the census # -------------------------------------------------------------------------- @@ -368,6 +463,22 @@ FIXTURES = [ ['Q30:CLOSED']), ] +UNIT_FIXTURES = [ + # name, text, ACS count, BQ count — the four axes that separate the units. + ('unit axis 1: a column-0 bold *prose* line is BQ but not ACS', + '## Q40 — section\n\n**Addendum, 2026-10-08 — Q40 CLOSED at `8bd0b09`.**\n', + 0, 1), + ('unit axis 2: a lowercase closure word is BQ but not ACS', + '## Q41 — section\n\n**Q41 — verified below.**\n', + 0, 1), + ('unit axis 3: a status row with no Q token is ACS but not BQ', + '## Q42 — section\n\n**Status: DONE — closed.**\n', + 1, 0), + ('unit axis 4: an indented bullet is ACS but not BQ', + '## Q43 — section\n\n- **Q43 — DONE at `4010b96`.**\n', + 1, 0), +] + 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, @@ -397,9 +508,26 @@ def selftest(verbose=True): % (expect, want_stale, got, stale)) if not ok: failures.append(name) + + # The unit fixtures pin the *population* each matcher counts, so a later + # edit to either matcher cannot silently re-open the 72-vs-80 question. + n_unit = len(UNIT_FIXTURES) + for name, text, want_acs, want_bq in UNIT_FIXTURES: + got_acs = sum(1 for l in text.splitlines() if is_closure_line(l)) + got_bq = len(legacy_closures(text)) + ok = (got_acs, got_bq) == (want_acs, want_bq) + if verbose: + print('%s %s' % ('PASS' if ok else 'FAIL', name)) + if not ok: + print(' expected ACS=%d BQ=%d\n got ACS=%d BQ=%d' + % (want_acs, want_bq, got_acs, got_bq)) + 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))) + total = len(FIXTURES) + n_unit + print('\nself-test: %d fixtures (%d claim-check, %d unit), %d passed, %d failed' + % (total, len(FIXTURES), n_unit, total - len(failures), len(failures))) return failures @@ -418,6 +546,9 @@ def main(argv=None): 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') + ap.add_argument('--census', action='store_true', + help='print both closure units (ACS and legacy BQ), their overlap ' + 'and the axis table, then exit') args = ap.parse_args(argv) if args.selftest: @@ -487,15 +618,54 @@ def main(argv=None): 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).' + print('\n%d opening records; %d closed; %d open. %d anchored closure statements ' + '(unit ACS — a record line leading `**Q`/`**Status:` with a closure word ' + 'in the first %d chars, exclusions tested at the lead; %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']))) + CLOSURE_WINDOW, 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))) + # Three different numbers have been printed as if they were one: the raw + # occurrences of the literal (which include code spans and table rows), the + # lines it stands on once code spans are stripped, and how many of those are + # opening records. Name each, or the count is cited with the wrong unit. + _lines = text.splitlines() + _lit_stripped = [n for n, l in enumerate(_lines, 1) if STALE_LITERAL.search(strip_code(l))] + _lit_opening = [r['line'] for r in res['records'] + if STALE_LITERAL.search(strip_code(_lines[r['line'] - 1]))] + print('the literal `Status: open and unclaimed` occurs %d times in the raw text; ' + 'after code spans are stripped it stands on %d lines, %d of them ' + 'opening-record lines, and %d of those are stale (state CLOSED).' + % (res['literal_anywhere'], len(_lit_stripped), len(_lit_opening), len(stale))) + + if args.census: + lines = text.splitlines() + acs = {n for n, l in enumerate(lines, 1) if is_closure_line(l)} + bq = set(legacy_closures(text)) + print('\n--- closure-unit census (cite a number only with its unit) ---') + print('ACS anchored closure statement (this file) = %d' % len(acs)) + print(' record line leading `**Q`/`**Status:`, closure word in the first') + print(' %d chars, code spans stripped, exclusions tested at the lead,' % CLOSURE_WINDOW) + print(' anchored to its own id or the nearest preceding container.') + print('BQ column-0 bold closure line naming a Q id (legacy q66_census6.py:54-62) = %d' + % len(bq)) + print(' line starting at column 0 with `**`, a Q token anywhere, a') + print(' closure word anywhere and case-insensitively, no legacy exclusion phrase.') + print('overlap A&B = %d ; ACS-only = %d ; BQ-only = %d ; symmetric difference = %d' + % (len(acs & bq), len(acs - bq), len(bq - acs), len(acs ^ bq))) + print('NOT a unit conversion: the difference between the two headline numbers is') + print('not the delta that shows here, and neither may be cited without its unit.') + for label, only, axis in (('BQ-only lines, dropped by ACS', bq - acs, _acs_reject_axis), + ('ACS-only lines, dropped by BQ', acs - bq, _bq_reject_axis)): + counts = {} + for n in sorted(only): + a = axis(lines[n - 1]) + counts[a] = counts.get(a, 0) + 1 + print('%s (%d):' % (label, len(only))) + for k in sorted(counts): + print(' %-78s %d' % (k, counts[k])) + return 0 rc = 0 if args.validate: