Skip to content

test(kanban): pin that carrying a ref does not cost the bundle beside it - #866

Merged
Kyzcreig merged 4 commits into
mainfrom
daedalus/t_e41d3dd4-mixed-carry-pin
Sep 23, 2026
Merged

Kyzcreig merged 4 commits into
mainfrom
daedalus/t_e41d3dd4-mixed-carry-pin

Conversation

@Kyzcreig

@Kyzcreig Kyzcreig commented Sep 22, 2026 •

Copy link
Copy Markdown
Collaborator

Stacked on #845 (which is stacked on #842). Test-only.

The carded defect is already fixed — this closes the GATE, not the bug

t_e41d3dd4 was written against PR #842 head 3ca4b600a3, where the cleanup
carry-forward read only previous["refs"]. Two commits landed on #842 after that
base — a61dda5edf ("close FleetReview's 2xP1 on the cleanup relaxation") added
carried_bundles and gated the ref-completion branch on not bundles, and
7afe1b9e37 re-keyed the coverage test on absent.

Re-measured on pristine #845 head 7c3e0b6db1 (impl blob 370e5b30f1, byte-identical
to this branch's): both shapes the card measured — bundle-only partial loss and mixed
partial loss — already carry the bundle and record kind: "bundle". The bundle-only
shape is additionally pinned by the pre-existing
test_a_bundle_vouched_missing_repo_is_carried_into_the_rewritten_survivor.
No behaviour change is needed. Zero lines of hermes_cli/kanban_survivor.py in this diff.

What IS missing: the MIXED shape has no gate

carried (refs) and carried_bundles are two independent collections read out of the
SAME recorded survivor. Every pre-existing partial-loss test loses a repository vouched
for by exactly ONE of them, so nothing in the suite exercises "carry a ref and a bundle
at once".

Mutant M2 — keep the bundle carry-forward, but disable it whenever a ref is also carried:

carried_bundles = [] if carried else [b for b in (previous or {}).get("bundles") or () ...]

Under M2 the mixed shape silently drops the bundle and relabels the record kind: "ref",
telling an operator the work is pushed when its only copy is an orphaned bundle attachment.

Measured (my run, isolated --basetemp, four survivor files)

arm result
HEAD, full four-file suite 96 passed (95 before this test)
M2 vs the 95 pre-existing tests (new test deselected) 95 passed, 0 failed — SURVIVES
M2 vs the new test alone 1 failed — killed only by this pin

Mutant applied in a throwaway detached worktree, reverted, worktree removed; both
checkouts git status --porcelain clean afterwards. ruff check clean on the test file
and on kanban_survivor.py.

Verify:

.venv/bin/python -m pytest \
  tests/hermes_cli/test_kanban_survivor.py \
  tests/hermes_cli/test_kanban_survivor_stale_bases.py \
  tests/hermes_cli/test_kanban_survivor_authority.py \
  tests/hermes_cli/test_kanban_external_survivor.py \
  --basetemp=/tmp/<isolated> -q

Notes


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

@Kyzcreig

Copy link
Copy Markdown
Collaborator Author

FleetReview

Reviewed with 2 of 3 model families — openai unavailable.

Confidence: 4/5

Findings

  • P1 hermes_cli/kanban_survivor.py:496 — Carried refs are merged with previous["refs"] again on the elif claimed: arm, duplicating recovery-index entries when repos exist but capture nothing
  • P3 tests/hermes_cli/test_kanban_survivor_stale_bases.py:1046 — Set-based refs assertion in the new test cannot see a duplicated carried ref

FleetReview provenance · models: C=claude-code-opus-5, D=grok-4.6, G=grok-4.6 · cost: $4.23 · duration: 11m 05s · rounds: 1 · files examined: 1

@Kyzcreig
Kyzcreig force-pushed the daedalus/t_e41d3dd4-mixed-carry-pin branch from 1e6339b to f14afd1 Compare September 22, 2026 17:33
@Kyzcreig
Kyzcreig changed the base branch from survivor/t_1bcd8aec-cleanup-coverage to main September 22, 2026 17:33
daedalus-opus and others added 4 commits September 23, 2026 03:40
…t shape

`missing <= _vouched_repositories(previous)` is the whole of what lets a reaper
DELETE a workspace whose recorded repository is gone. It was pinned for ONE cell
of its input matrix: a ref-shaped survivor under total loss. Three mutations of
the other cells left all 78 survivor tests green.

Tests only; no production change (`git diff hermes_cli/` is empty).

  ref/partial  -- `if repos: carried = [...]` (kanban_survivor.py:380-386) was
    reached by exactly one PRE-EXISTING test, which reads the COMPLETION
    snapshot and passes with the branch disabled. Disabled, the surviving repo
    satisfies the completion alone and the RECORDED ref for the vanished repo is
    silently dropped -- preserve() SUCCEEDS with the lost work pointed at
    nothing. Now driven through the CLEANUP path, asserting both refs survive.

  patch/any -- `_vouched_repositories()` excludes `repositories` because a patch
    is keyed by base SHA against a checkout. That was prose in a docstring.
    Widening the helper to collect `repositories` turns a fail-CLOSED hold into
    a reapable workspace whose only survivor is a patch against a base SHA that
    exists nowhere. Now pinned, with a direct shape assertion as teeth.

  bundle/total -- no test exercised a bundle-shaped recorded survivor at
    cleanup, so dropping `bundles` from the helper converts every bundle-backed
    reclamation into a permanent HOLD. Verified: that mutant passes 78/78 on the
    unmodified suite. Now pinned.

Verified (CPython 3.11.15, pytest 9.1.1, isolated --basetemp, four survivor
files):

  baseline #842 head 3ca4b60 : 78 passed
  with these tests             : 81 passed
  mutant if repos -> if False  : 1 failed, 80 passed (ref/partial test)
  mutant +repositories         : 1 failed, 80 passed (patch test)
  mutant -bundles              : 1 failed, 80 passed (bundle test)
  mutant -bundles, no new tests: 78 passed  <- the gap was real

Each mutant is caught by exactly its own test and nothing else. ruff clean.

Scope note: enumerating the matrix surfaced a LIVE defect, not a test gap --
bundle-shaped survivor under PARTIAL loss. `carried` collects only
`previous["refs"]` while `_vouched_repositories()` accepts bundles too, so the
bundle passes the coverage test, carries nothing, gets erased from the record,
and the hold clears. Measured on pristine code; carded as t_e41d3dd4 rather than
fixed here, and deliberately NOT pinned -- pinning it would freeze the bug.

Refs: t_1bcd8aec (found by argus on t_a49e8a28 / PR #842 round 1)
(cherry picked from commit 8272cd4)
…exits

Round-2 class sweep on the cleanup relaxation, re-derived over THREE axes
(survivor shape x loss extent x workspace content) after #842 r2 landed both
FleetReview P1 fixes. The omitted axis in round 1 was workspace content.

Measured, not inferred: a 30-cell probe over
{ref,bundle,patch,mixed,none} x {total,partial,partial-dirty} x {clean,loose}
at 3afa908 found one unpinned family -- the `_loose_files()` guard is
exercised only by REF-shaped survivors. Waiving it for `kind == "bundle"` at
the relaxation's own exit flips four cells (bundle|partial|loose,
mixed|partial|loose, and both partial-dirty variants) from fail-closed HOLD to
a reclaim that rmtree's `qa-output/verdict.md`, and leaves all 84 tests GREEN.

Two tests added:
  test_partial_loss_holds_for_loose_evidence_under_a_bundle_shaped_survivor
  test_a_bundle_shaped_survivor_does_not_buy_a_delete_for_loose_evidence

Verified: 86 passed across the four survivor files. Mutation gate
(mutation_gate_r2.sh) -- M4 (relaxation exit waived for bundles) kills the
partial test; M5 (the `elif claimed:` arm waived) is an EQUIVALENT mutant,
traced with sys.settrace: the total-loss bundle+loose case raises at the first
site and never reaches the second, so it flips zero cells alone. The combined
M4+M5 mutant kills both tests. Source restored pristine after every arm.

Test-only: zero lines of hermes_cli/kanban_survivor.py changed.
(cherry picked from commit 7d7f223)
…ping

`missing <= vouched` is the whole of what lets a reaper delete a workspace
whose recorded repository is gone. Every existing test loses exactly ONE
repository, where the subset test and an intersection test agree — so
relaxing it to `missing & vouched` (or `missing & vouched or missing <=
vouched`) left all 86 tests on the four survivor files, and all 151 on the
wider survivor surface, GREEN while flipping two cells from fail-closed HOLD
to reapable.

Adds the discriminating shape: two recorded repos gone, the recorded survivor
vouching for only one. The unvouched repo has no ref, no bundle and no patch
anywhere, so under the mutant its unpushed work is pointed at nothing after
the reap. Pinned on both the total and partial loss arms, plus a full-coverage
teeth case proving the assertion keys on TOTAL coverage rather than on
multi-repo loss itself.

Measured (mutation_gate_r3.sh, source restored + `git diff --quiet` verified
after every arm):
  P1 `missing & vouched or missing <= vouched` -> 2 failed, 1 passed
  P2 `missing & vouched`                       -> 2 failed, 1 passed
  P0 pristine                                   -> 3 passed
Four survivor files: 89 passed. Wider surface (7 files): 154 passed.
ruff clean. Zero lines of kanban_survivor.py changed — behaviour untouched.

(cherry picked from commit 7c3e0b6)
The carded defect (t_e41d3dd4) is ALREADY FIXED: #842 r2 (a61dda5) added
`carried_bundles` and gated the ref branch on `not bundles`. Both shapes the
card measured -- bundle-only partial loss and mixed partial loss -- were driven
through `preserve(cleanup=True)` on pristine #845 head and both already carry
the bundle and record `kind: "bundle"`. No behaviour change is needed.

What IS missing is a gate. `carried` and `carried_bundles` are independent
collections read from the same recorded survivor, and every existing
partial-loss test loses a repository vouched for by exactly ONE of them. So a
mutant that keeps the bundle carry-forward but disables it whenever a ref is
also carried -- `carried_bundles = [] if carried else [...]` -- leaves all 30
tests on this file GREEN while the MIXED shape drops the bundle and relabels
the record `kind: "ref"`, telling an operator the work is pushed when its only
copy is an orphaned bundle attachment.

Measured on this tree, pre-existing suite only:
  M1 `carried_bundles = []`                -> 3 failed / 28 passed (already gated)
  M2 `[] if carried else [...]`            -> 0 failed / 30 passed  SURVIVES
With this test: M1 3 failed, M2 1 failed (killed only by the new test).

Test-only: zero lines of hermes_cli/kanban_survivor.py; impl byte-identical to
the pristine base. Four survivor files at this head: 95 passed before, 96 after.

(cherry picked from commit 1e6339b)
@Kyzcreig
Kyzcreig force-pushed the daedalus/t_e41d3dd4-mixed-carry-pin branch from f14afd1 to 7dbd9ed Compare September 23, 2026 10:42
@Kyzcreig

Copy link
Copy Markdown
Collaborator Author

apollo/merge-pass 2026-09-23: rebased onto current main (clean, own commits only, no content change) so CI runs against the post-#915 main; landing via fleet-merge on the FR-pause lane once green.

@Kyzcreig

Copy link
Copy Markdown
Collaborator Author

🤖 merged-by: apollo · lane: fr-pause-0922 · gate: BYPASS: FR PAUSED by Ace ruling 2026-09-22 (state/fleetreview-pause-20260922.md); t_e41d3dd4 approved · why: argus APPROVED (card done); rebased clean onto main; FR paused; CI green; Apollo merge pass 2026-09-22

@Kyzcreig
Kyzcreig added this pull request to the merge queue Sep 23, 2026
Merged via the queue into main with commit d4ee4fb Sep 23, 2026
54 checks passed
@Kyzcreig
Kyzcreig deleted the daedalus/t_e41d3dd4-mixed-carry-pin branch September 23, 2026 11:13
@Kyzcreig Kyzcreig added the fleetreview:post-merge Ask FleetReview to review this MERGED pull (merge commit vs first parent) label Sep 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fleetreview:post-merge Ask FleetReview to review this MERGED pull (merge commit vs first parent)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant