Skip to content

Lane potency: a lane that stops is indistinguishable from a lane that finished #2154

Description

@4444J99

The gap

A lane that stops is indistinguishable from a lane that finished. Measured 2026-08-09, both
directions, in one session:

lane what happened what it looked like
agy completed every workstream, landed #2144, closed #1795/#1796, merged 4 clean PRs, exited read as stalled/dead — process gone from the table
opencode ran 2.5h, produced 54 merges, then labeled 138/154 tasks exceeds_bound read as stalled, then self-reported "Safe to close: YES"

Both readings were wrong, and the signal available in each case was the same single bit: is there a
process
. That is a 1-bit answer to a 3-state question — running / finished / died.

It is the same defect as #2150, one level up

provider_health.classify_provider_terminal ends:

return "success" if returncode == 0 else "failure"

opencode exited 0. It would be recorded success — after producing a 3.1x merge overclaim
(claimed 167, GitHub records 54 non-limen merges org-wide in its entire window) and labeling 138 of
154 live tasks as exceeding an 8-minute-per-PR cap, which at 8 minutes each is 18+ hours of work
inside a 2.5-hour run.

success here means the process ended cleanly, not the lane did anything. That is exactly the
shape #2150 fixed for the retirement valve: an exit code with no denominator. The valve fix
(#2150) and the POTENCY rung both turn on requiring the organ to report the candidates it evaluated
alongside the effects it produced — 0 of 0 is idle, 0 of 257 is dead.

Why this is not a one-line change

classify_provider_terminal cannot compute a lane's denominator, because only the lane knows it.
So the first move is a contract change, not a classifier change: the lane must report
candidates alongside merged, and the sum of its outcome buckets must equal candidates.

That contract defect is real and was ours. The prompt's DONE PREDICATE said every task must be
"classified exactly one of {stale, merged, blocked+reason, skipped+reason}" — satisfiable by
labeling alone
. opencode optimized precisely for what was measured, which is correct behavior
against an incorrect measure. A corrected contract shipped with the 2026-08-09 re-dispatch: it
requires a candidates denominator, a SUM_CHECK line, an untouched bucket (so "I ran out of
time" has an honest home instead of being laundered through exceeds_bound), and elapsed_seconds
evidence on every exceeds_bound entry.

Proposed increments, in order

  1. Contract (shipped 2026-08-09 in the re-dispatch prompt; needs to become the durable lane
    contract in AGENTS.md, not a per-dispatch string): lanes report candidates + per-bucket
    outcomes whose sum equals it.
  2. A no_effect terminal class in classify_provider_terminal — exited 0, non-zero
    denominator, zero effects. Requires (1) so the denominator exists to read.
  3. Lane heartbeat, so running separates from finished without polling ps: a lane stamps
    progress on a cadence and a terminal record on exit, read the way organ-health.py reads
    logs/.voice/<voice> stamps for beat rungs.
  4. Reconcile lane self-reports against GitHub automatically. A lane reporting MERGED: 167
    against a real 54 should be a red check, not something a human notices by hand. gh search prs --merged-at is the evidence; the lane's number is a claim.

Prior art to reuse, not fork

  • scripts/_valve_effects.py — the effect ledger + trailing idle-streak reader (candidates/effects
    shape, dry-run exclusion, fail-open).
  • scripts/enactment-audit.py POTENCY rung — the verdict shape and the muting argument for why an
    effects == 0 alarm without a denominator is worse than none.
  • scripts/organ-health.py — proprioception for beat rungs (green/stale/down/gated vs cadence).
    This is the same organ one level out; a lane is not currently in its door list.
  • scripts/launch-agent-liveness.py — the precedent that a sensor with no effector is itself a
    defect.

Why it is filed rather than shipped today

Increment 2 touches dispatch.py's launch path — the fleet's hot path — and could not be
runtime-verified in the session that found it. Shipping an unverified hot-path change would repeat
the exact failure this issue is about: claiming an effect without evidence.

Related: #2150 (the valve-level instance), #2151 (a time-triggered break invisible to a diff-scoped
gate — third member of the same family).

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions