docs(PENDING): resolve pending-168-count-unit-declared; PENDING-182 ADDENDUM 1
The deferral is stamped RESOLVED against REVIEWED-138, checked by reading the ruling against the deferral's own discriminator rather than inferred from the needle firing. Resolving it also retires the false fire REVIEWED-138 warned of: a later ruling on PENDING-168's open remedy would have tripped the same needle. PENDING-182 ADDENDUM 1 places tick_id's allocation in fire_tick, as an OS-random int carried to the child in argv, withdraws the concurrent-panes premise (no two of 746 ticks share a second), and records that the body's selftest writes tick records into the live occurrence log (floor of two). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PJM5fwqp456LDGzqiZXsgu
This commit is contained in:
co-authored by
Claude Opus 5
parent
aa9fb24cb5
commit
43d5249d4f
+45
@@ -4311,6 +4311,7 @@ chk(f"a real session reads as WRAPPED end-to-end [{_v.count('wrapped')} of {len
|
||||
needle and cannot be OR-ed, so the majority form is chosen and the gap is
|
||||
disclosed. Silence from this trigger is therefore weak evidence, not proof the
|
||||
ruling has not landed.
|
||||
resolved: 2026-09-11 — REVIEWED-138 (placed in commit 7c096a2, REVIEWED.md) declares its unit before its number, in both units, and names this deferral satisfied. Checked by reading the ruling against the discriminator, not inferred from the needle firing: REVIEWED-138 itself records that the needle detects a ruling ON PENDING-168, not a declared unit, so a later ruling on PENDING-168's still-open remedy would have fired it again and reported a declaration that had not occurred. Resolving it here retires that false fire too. Cited by heading and commit, not by line, per REVIEWED-139.
|
||||
-->
|
||||
|
||||
**Awaiting:** Steward authorization. ⚠ **Nothing is blocked:** the control's vacuity costs nothing today, because the behaviour it failed to guard was verified behaviourally in the same sitting. What is time-sensitive is only that PENDING-168 not be ruled on an evidence base this item changes.
|
||||
@@ -4488,6 +4489,50 @@ A partition with no exceptions in either direction. **One opaque integer written
|
||||
|
||||
**Related:** REVIEWED-137 §3 (the instrument designed, not authorized) · REVIEWED-136 AMD 1 condition G · PENDING-180 ADDENDUM 1 §6 (the assertion-versus-file gap) · PENDING-152.
|
||||
|
||||
### PENDING-182 — ADDENDUM 1: `tick_id` is minted in `fire_tick` as an OS-random integer and carried to the child in argv — and the body's selftest has been writing ticks into the live log
|
||||
**Date:** 2026-09-11 · filed before any ruling, so it is read with the parent.
|
||||
|
||||
**The premise the open design point rested on does not hold in the record.** The parent says the allocation site *"determines whether it can distinguish two panes drawing in the same minute — which is the whole question it exists to answer."* Measured over `tarbuckle-draws.jsonl`, 746 tick records, 2026-08-25 → 2026-09-11: **no two ticks in the same second, and every short gap after construction day is a test artifact (below).** Panes do coexist — two or more session keys rendered in the same minute in 145 of 8,928 minutes of `tarbuckle-invocations.jsonl` — but the clock is machine-global and `fire_tick` writes it before it draws, so a double-fire needs two renders inside one process's read-to-write window. None is observed. **The question the id exists to answer is judgment 2's: which tick a child's terminal record belongs to** (REVIEWED-137 §2). ⚠ The pane framing was the drafting hand's, and so is this correction.
|
||||
|
||||
**The allocation, and why each alternative fails:**
|
||||
|
||||
| site / form | verdict | why |
|
||||
|---|---|---|
|
||||
| **inside `fire_tick`, before the draw, an OS-random 63-bit integer** | **recommended** | Every consumed tick carries it, **including the 73% that draw silent**, which are the denominator. Minted per process, so even an unobserved race yields two ids, never one. Drawn independently of `draw()`, which stays argument-free (D1). |
|
||||
| derived from the clock file's value | ✘ | Two firings in one second collide by construction, and the seam rewrites the same file, so the value is not the tick's alone. |
|
||||
| a monotonic counter file | ✘ | A value the next tick reads back is **state** — the body's own determination: *"A conserved draw is a budget, and a budget is memory."* Racy across panes as well. |
|
||||
| minted in the mumble child | ✘ | Too late: silent ticks spawn no child, so the denominator would carry no id, and the join needs the id at **both** ends. |
|
||||
| per-session clock (key `last-tick` by pane) | not proposed | Changes cadence — n panes, n× the rate — which is behaviour, not instrumentation. |
|
||||
|
||||
**Carriage: `argv[3]`, not the environment.** The child is spawned as `[sys.executable, MUMBLE, kind, transcript]`. An environment variable would ride into the child's own `claude -p` and every hook that fires inside it — `Stop` fires for `claude -p`, which is why `7948c09` exists — so the id's scope would exceed the one process that needs it. A manual run of the generator carries no `argv[3]` and records `tick_id: null`, which is true: no tick caused it.
|
||||
|
||||
**Type: `int`, and condition A is decided here for this field.** A uuid string would be a string parameter. ⚠ **And condition A as drafted is false of the function it extends:** *"the logger takes no string parameter at all"* — but `log_event(surface, outcome)` already takes two strings, and mumble selftest A6 asserts exactly that pair. The property that can hold: **every parameter is an `int` or drawn from a closed set** — `surface`, `outcome`, `reason_category` closed; `tick_id`, `words` ints. FORM B should then assert the declared parameter tuple **and its annotations**, so a widened type fails as surely as an added name. ⚠ A proposed reading of the condition, not a ruling on it; A6 must be replaced by FORM B in the same change or it fails on the first added field.
|
||||
|
||||
**Two writers, not one.** The tick record is written by the body's local `_log_draw` — kept local *"so the body needs nothing importable to tick"* — and the child's by `log_event`. Both must carry the field, and **FORM B as drafted guards only `log_event`**: `_log_draw` would be the unguarded writer of the same file. Both signatures are asserted, or the control covers half the join.
|
||||
|
||||
**Synchronous surfaces: nothing minted.** Seam, wrap and invoke generate in-process and write one record per occasion. There is no detached boundary and nothing to join; they write `tick_id: null`. Minting there would be uniformity rather than fit, and would make a non-tick occasion read as a tick.
|
||||
|
||||
**What the id buys, bounded:** `child.t − tick.t`, exactly, per draw — the lag's total. **Not its decomposition.** Spawn-to-start, pre-generation and generation cannot be separated without a child-start record, which this item does not propose. Judgment 2 notes `claude -p` carries `timeout=120`; if lags run far past that, the id will show *that* the time is spent outside the generator, not *where*.
|
||||
|
||||
#### The finding: the body's selftest writes tick records into the live occurrence log
|
||||
|
||||
`_log_draw` hardcodes `~/.claude/state/tarbuckle-draws.jsonl`. The selftest rebinds `LAST_TICK` and `SLOT` and not that path, and D5 calls `fire_tick(now + 9999, "")`. **Every selftest run — and every mutant copy of the body run for mutation testing — appends one real-time `tick` record to the live log.** The D block's own comment reads *"Run against a temp dir, never live state."* The code does not do what its comment says.
|
||||
|
||||
Established three ways, none resting on another:
|
||||
1. **Census of writers:** one writer of `"surface": "tick"` (`_log_draw`), one caller (`fire_tick`), two call sites — `main()` and selftest D5. Nothing else in `~/dotfiles` or the hook configuration.
|
||||
2. **The clock's invariant:** `fire_tick` writes the clock before it draws, and both writers of the clock write the current time, so two live ticks are ≥ 1200 s apart. `2026-09-10T18:52:40` sits between two ticks 1204 s apart that each spawned a child — a child needs a transcript and the selftest passes none — so it cannot be live. For 09-09, `21:31:33` / `21:48:39` / `21:52:11` cannot all be live; the invariant alone does not say which is not.
|
||||
3. **Time-matched runs in the transcripts:** a loop running all five Tarbuckle selftests at 21:48:36 local on 09-09, 3 s before `21:48:39` — which decides 09-09 — and a mutant copy of the body at 18:52:39 on 09-10, 1 s before `18:52:40`. ⚠ A first search for the literal `tarbuckle-body.py --selftest` found neither: the loop and the mutant never spell it. Caught before reporting.
|
||||
|
||||
**Magnitude: a floor of two, and the record cannot give more.** Both identified strays are `silent`. A selftest tick landing in an idle stretch leaves no short gap, so the gap method sees only strays that fall near live ticks. A census was attempted and **failed its own negative control**: `main()` writes an invocation record just before it can tick and the selftest writes none — but during active work the status line renders every few seconds, and the known stray at 18:52:40 has an invocation within 2 s too. **Nothing in a tick record distinguishes a live tick from a test tick.** Past records separate only by time-matching against transcripts, one at a time. Effect on the fortnight report's rate: not re-assessed; the identified strays are two of ~740.
|
||||
|
||||
**Why it matters more to this item than to the rate:** with `tick_id` in place, a selftest tick that draws `aside` becomes **a tick with an id and no child** — the signature of the orphaned or lagging draw this instrument exists to detect. It would be born reporting false positives of its own target class.
|
||||
|
||||
**Recommended precondition — a `[FIX]`, separable from this item's ruling:** hoist the draws path to a module global in `tarbuckle-body.py`, rebind it in the D block beside `LAST_TICK`/`SLOT`, and add a control that the live log is byte-unchanged across the selftest, shown to fail by mutation (delete the rebinding). ⚠ **Not done** — surfaced, not taken. The stray records stay in the log; a log is not corrected by deletion.
|
||||
|
||||
**Correction to the parent, by joining:** its *Files affected* says *"the four seam call sites"* and names five (`body`, `mumble`, `seam`, `wrap`, `invoke`). Under this addendum the id is minted in one (`body`), carried into one (`mumble`), and written as null by three.
|
||||
|
||||
**Awaiting:** unchanged — steward authorization and a jurist design gate on the parent. This addendum narrows the open design point to a recommendation with its alternatives; it decides nothing the parent left to them.
|
||||
|
||||
## PENDING-183 — CARRIER: the eleven open items from the verdicts sitting, named so they can be aimed at
|
||||
**Date:** 2026-09-10
|
||||
**Tag:** [HARDENING]
|
||||
|
||||
Reference in New Issue
Block a user