Skip to content

claude-lane-incident: the counting rule is vacuously satisfiable when the index names nothing, and the recovery instruction covers causes with nothing to recover #360

Description

@kyle-sexton

Three items left over from #350's rework list, all in .github/scripts/claude-lane-incident.cjs and its body-budget test. Verified at #350's merged head 9681fee, which is byte-identical to main for both files. Filed separately from #357 because these are product copy changes that grow the rendered issue body, where #357 is a test-only change that does not — and growing this copy is exactly what makes item 3 load-bearing.

1. The counting rule is vacuously satisfiable when the index names nothing

claude-lane-incident.cjs:704-706 is byte-identical to the pre-rework snapshot 484e765. The rendered body tells the operator a cycle counts when "it polled every repository the incident TRACKS, and the tracked index accounts for every repository the incident has SEEN". When the index names nothing, both clauses are vacuously true — polling the empty set, an empty index accounting for zero seen — while coverageGap holds and no cycle can count.

The state is reachable, not theoretical. A hand-edited state block with "repositories":{} parses successfully:

parseStateBlock succeeded: true
  repositories: {}
  repositoriesSeen: 5
  coverageGap: {"unobserved":[],"unlisted":0,"namesNothing":true}
  body still asserts the counting rule verbatim: true

9681fee fixed the permanent-hold enumeration (cjs:717-720 now names the unreadable-index cause), which answers a different question — what holds an incident open forever, not what makes a cycle count. The operator reading the counting rule in this state is still told cycles are counting exactly while the gate withholds every one of them.

Fix direction: make the copy state the real condition — a gate that holds when the index names nothing blocks the count — rather than deleting the clauses.

2. "once you have confirmed recovery" covers causes with nothing to recover

claude-lane-incident.cjs:713-715 says the three permanent-hold causes are "each closed by hand once you have confirmed recovery". Two of the three have nothing to recover: a repository that is gone (archived, or removed from the installation) is not coming back, and an index this watchdog can no longer read is a corrupted record, not an outage.

9681fee made this marginally worse rather than better: it widened the third cause from a STATE_SCHEMA_VERSION bump to any hand-edited or older-schema index, so the recovery instruction now covers strictly more causes that have no recovery step.

Raised as a LOW nit in comment 5181967118 item 3, offered as "fix or consciously decline". Neither happened — #350 carries 7 issue comments, 0 review comments, and 0 reviews, and nothing after the addendum mentions it.

3. The body-budget test does not drive the true worst case

the body stays inside GitHub's limit at the longest repository name that can exist drives three shapes — 40×10, 120×40, 200×300 — and none is near the real bound. Sweeping the reachable parameter space at longest legal owner/name:

shape rendered length headroom of 65536
40×20, pulls from 1 65418 118
54×14, nine-digit pull numbers 65487 49
120×40 (in the suite) 64526 1010
200×300 (in the suite) 64515 1021

Two independent sweeps agree on the 40×20 figure; the 54×14 case is tighter because it crosses the shape with nine-digit pull numbers, so 49 characters is the binding number. The suite's shapes sit roughly 1000 characters clear of the limit, so a copy edit can consume 20× the real headroom with a green suite.

This is not currently a live defect — fixedLength (cjs:737-742) deducts the actual prologue and epilogue length from the table allowance, so copy growth converts to dropped rows rather than an over-limit body, with a last-resort minimal body at cjs:759. The bound is structural. But it means the test proves far less than it appears to, and items 1 and 2 above both grow this copy. Order matters: tighten the budget test first, then edit the copy against a test that would actually catch a regression.

Recorded for completeness: the addendum's "65433 at 120×10, 103 characters of headroom" did not reproduce in either sweep (120×10 measures 64364, or 64786 at nine-digit pull numbers). Hostile statusCounts crossed with that shape was not swept, so the figure is unconfirmed rather than contradicted — and the sweeps found a tighter bound regardless, so nothing depends on resolving it.

Source

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions