Skip to content

fix: support concurrent Boat QA lanes within a shared spend cap - #255

Merged
dnth merged 4 commits into
mainfrom
fm/fm-boat-lanes-parallel-fixes
Oct 16, 2026
Merged

dnth merged 4 commits into
mainfrom
fm/fm-boat-lanes-parallel-fixes

Conversation

@dnth

@dnth dnth commented Oct 16, 2026

Copy link
Copy Markdown
Owner

Intent

Fix bin/fm-boat-lanes.py so release QA lanes from one run can start concurrently under one total spend cap when the cap allows. Add optional per-lane ttl_seconds overrides applied to reservation, cap check, Boat box creation, and TTL update. Add optional top-level known_product_failures case IDs so listed failed product cases retain failure status and evidence and are never retried, while every other failure keeps the existing retry behavior. Add optional lane continue_on_failure, default false, so ordered later selections run after an earlier selection reports failed or blocked cases and produce the maintenance selection result and artifacts; preserve default stop-after-non-pass and stopping on harness errors, timeouts, cancellation, or invalid evidence. Existing specs must behave unchanged; document the new fields in the runner header and --help. Extend tests/fm-boat-lane-runner.test.sh and offline fake-provider cases only; create no live Boat boxes and incur no spend. Create the private gitignored ~/Desktop/firstmate/config/boat-lanes/infoconnect-release.v1.json from the read-only v0.5.15 specs and report evidence, with one maintenance pilot, the main shards, one correctly ordered resilience-then-maintenance lane, and qualification. Size per-lane TTLs from measured v0.5.15 lane durations with real margin, list QA-QUAL-RANKING-01 as a known failure, and keep total worst-case reservation at or below $1.00 so all eight post-pilot lanes can start together with --jobs 8. Validate the recipe offline against the runner without creating boxes; keep it out of the commit and PR. The external config write is the single approved exception to the task worktree boundary; touch nothing else outside the worktree. Use report.md Recipe problems and next-run fixes item 3; leave report items 4 and 5 (InfoConnect scripts) unchanged. The diagnostics startup-persistence test fails on clean origin/main commit bb51ff2; record that as pre-existing with evidence and do not fix it in this task. Record receipts for AC1-AC5. Do not merge a PR.

What Changed

  • Apply optional per-lane TTLs to spend reservations, cap checks, box creation, and TTL updates so concurrent lanes can fit within one total cap.
  • Add known_product_failures case IDs that retain failure status and evidence without retries, preserving existing retry behavior for other cases.
  • Add opt-in continue_on_failure for later ordered selections while retaining earlier failures and harness stop conditions; document the fields and extend offline fake-provider coverage.

Risk Assessment

✅ Low: The changes are bounded, and source tracing confirms consistent TTL accounting, known-failure preservation, and continued-selection verdict aggregation.

Testing

All 23 targeted runner cases passed through the real CLI with an isolated fake provider. Saved product reports and ledgers; the clean-base diagnostics check also passed. No real Boat calls or spend occurred. Private recipe validation remains unavailable within the permitted paths.

  • Live validation: ⚠️ inconclusive - 8 of 9 scenarios driven live against the product
Scenario Result Live Evidence
Run a successful pilot followed by eight concurrent lanes under a $1 total reservation cap ✅ pass live product-transcript.txt: test_eight_post_pilot_lanes_concurrent_under_one_dollar
Override lane TTLs and observe matching reservations, creation TTLs, updates, and cap refusals ✅ pass live targeted-tests.log; saved create_intent ledgers and fake-provider state
Protect listed failures across supported statuses and overlapping retry evidence while retrying eligible cases ✅ pass live targeted-tests.log: known_product_failure_ids_are_not_retried and known_failures_survive_overlapping_retry_evidence_and_stop_fanout
Continue from failed or blocked selections into maintenance, request artifacts, and retain the strongest failure verdict ✅ pass live product-transcript.txt: continuation and failure-precedence cases
Omit new options and preserve default stopping, pilot gating, retry behavior, and cleanup ✅ pass live targeted-tests.log: lane_still_stops_after_nonpass_by_default and existing retry cases
Enable continuation and verify guard failures, invalid evidence, timeouts, and cancellation still stop later selections ✅ pass live adversarial-tests.log; product-transcript.txt
Reject malformed new fields before creation and expose their documentation through --help ✅ pass live targeted-tests.log; runner-help.txt
Run the diagnostics startup-persistence case against clean base bb51ff2 ✅ pass live baseline-startup-persistence.log
Validate the actual private release recipe, measured TTL margins, lane ordering, and AC1–AC5 receipts ⏸️ untested no The private recipe, v0.5.15 source specs, duration report, and receipts are not supplied inside this worktree. This phase forbids reading another project checkout. A disposable nine-lane recipe exerci…
Evidence: Runner ledgers and generated reports

=== test_eight_post_pilot_lanes_concurrent_under_one_dollar ===
{"event": "create_intent", "lane": "pilot", "name": "fm-lanes-5697126cb8d4-pilot", "reserved_usd": 0.011999999999999999, "sequence": 2, "time": 1792142867.9066267, "ttl_seconds": 600, "version": 1}
{"box": "bx_1", "event": "lane_result", "lane": "pilot", "sequence": 10, "signature": null, "time": 1792142868.3642223, "verdict": "pass", "version": 1}
{"event": "create_intent", "lane": "lane1", "name": "fm-lanes-5697126cb8d4-lane1", "reserved_usd": 0.074, "sequence": 14, "time": 1792142868.5056329, "ttl_seconds": 3700, "version": 1}
{"event": "create_intent", "lane": "lane2", "name": "fm-lanes-5697126cb8d4-lane2", "reserved_usd": 0.07599999999999998, "sequence": 16, "time": 1792142868.5408142, "ttl_seconds": 3800, "version": 1}
{"event": "create_intent", "lane": "lane3", "name": "fm-lanes-5697126cb8d4-lane3", "reserved_usd": 0.07799999999999999, "sequence": 18, "time": 1792142868.6141968, "ttl_seconds": 3900, "version": 1}
{"event": "create_intent", "lane": "lane4", "name": "fm-lanes-5697126cb8d4-lane4", "reserved_usd": 0.08, "sequence": 20, "time": 1792142868.6557403, "ttl_seconds": 4000, "version": 1}
{"event": "create_intent", "lane": "lane5", "name": "fm-lanes-5697126cb8d4-lane5", "reserved_usd": 0.082, "sequence": 22, "time": 1792142868.7724073, "ttl_seconds": 4100, "version": 1}
{"event": "create_intent", "lane": "lane6", "name": "fm-lanes-5697126cb8d4-lane6", "reserved_usd": 0.08399999999999999, "sequence": 24, "time": 1792142868.8097842, "ttl_seconds": 4200, "version": 1}
{"event": "create_intent", "lane": "lane7", "name": "fm-lanes-5697126cb8d4-lane7", "reserved_usd": 0.086, "sequence": 26, "time": 1792142868.859661, "ttl_seconds": 4300, "version": 1}
{"event": "create_intent", "lane": "lane8", "name": "fm-lanes-5697126cb8d4-lane8", "reserved_usd": 0.08799999999999998, "sequence": 28, "time": 1792142868.9003897, "ttl_seconds": 4400, "version": 1}
{"box": "bx_2", "event": "lane_result", "lane": "lane1", "sequence": 71, "signature": null, "time": 1792142869.5666614, "verdict": "pass", "version": 1}
{"box": "bx_8", "event": "lane_result", "lane": "lane7", "sequence": 73, "signature": null, "time": 1792142869.5973206, "verdict": "pass", "version": 1}
{"box": "bx_3", "event": "lane_result", "lane": "lane2", "sequence": 75, "signature": null, "time": 1792142869.6139014, "verdict": "pass", "version": 1}
{"box": "bx_5", "event": "lane_result", "lane": "lane4", "sequence": 79, "signature": null, "time": 1792142869.626667, "verdict": "pass", "version": 1}
{"box": "bx_4", "event": "lane_result", "lane": "lane3", "sequence": 80, "signature": null, "time": 1792142869.6272852, "verdict": "pass", "version": 1}
{"box": "bx_9", "event": "lane_result", "lane": "lane8", "sequence": 83, "signature": null, "time": 1792142869.6645339, "verdict": "pass", "version": 1}
{"box": "bx_7", "event": "lane_result", "lane": "lane6", "sequence": 85, "signature": null, "time": 1792142869.6663408, "verdict": "pass", "version": 1}
{"box": "bx_6", "event": "lane_result", "lane": "lane5", "sequence": 88, "signature": null, "time": 1792142869.6947455, "verdict": "pass", "version": 1}
{"cleanup_incomplete": false, "event": "summary", "reserved_usd": 0.0, "results": [{"box": "bx_1", "lane": "pilot", "signature": null, "verdict": "pass"}, {"box": "bx_2", "lane": "lane1", "signature": null, "verdict": "pass"}, {"box": "bx_8", "lane": "lane7", "signature": null, "verdict": "pass"}, {"box": "bx_3", "lane": "lane2", "signature": null, "verdict": "pass"}, {"box": "bx_5", "lane": "lane4", "signature": null, "verdict": "pass"}, {"box": "bx_4", "lane": "lane3", "signature": null, "verdict": "pass"}, {"box": "bx_9", "lane": "lane8", "signature": null, "verdict": "pass"}, {"box": "bx_7", "lane": "lane6", "signature": null, "verdict": "pass"}, {"box": "bx_6", "lane": "lane5", "signature": null, "verdict": "pass"}], "sequence": 102, "signal": 0, "spent_usd": 0.009, "systemic_halt": false, "time": 1792142870.0288897, "version": 1}
# Boat lane results

Overall case totals: {"pass": 9}
Spend so far: $0.009000; reserved: $0.000000

## pilot / 0 / initial

Box: bx_1; profile: default; duration: 0.099s
Counts: {"pass": 1}; verdict: None


## pilot / lane / complete

Box: bx_1; profile: default; duration: 0.595s
Counts: {"pass": 1}; verdict: pass


## lane1 / 0 / initial

Box: bx_2; profile: default; duration: 0.253s
Counts: {"pass": 1}; verdict: None


## lane7 / 0 / initial

Box: bx_8; profile: default; duration: 0.278s
Counts: {"pass": 1}; verdict: None


## lane2 / 0 / initial

Box: bx_3; profile: default; duration: 0.385s
Counts: {"pass": 1}; verdict: None


## lane4 / 0 / initial

Box: bx_5; profile: default; duration: 0.291s
Counts: {"pass": 1}; verdict: None


## lane3 / 0 / initial

Box: bx_4; profile: default; duration: 0.252s
Counts: {"pass": 1}; verdict: None


## lane8 / 0 / initial

Box: bx_9; profile: default; duration: 0.226s
Counts: {"pass": 1}; verdict: None


## lane6 / 0 / initial

Box: bx_7; profile: default; duration: 0.353s
Counts: {"pass": 1}; verdict: None


## lane5 / 0 / initial

Box: bx_6; profile: default; duration: 0.164s
Counts: {"pass": 1}; verdict: None


## lane1 / lane / complete

Box: bx_2; profile: default; duration: 1.263s
Counts: {"pass": 1}; verdict: pass


## lane3 / lane / complete

Box: bx_4; profile: default; duration: 1.398s
Counts: {"pass": 1}; verdict: pass


## lane4 / lane / complete

Box: bx_5; profile: default; duration: 1.411s
Counts: {"pass": 1}; verdict: pass


## lane6 / lane / complete

Box: bx_7; profile: default; duration: 1.413s
Counts: {"pass": 1}; verdict: pass


## lane8 / lane / complete

Box: bx_9; profile: default; duration: 1.43s
Counts: {"pass": 1}; verdict: pass


## lane7 / lane / complete

Box: bx_8; profile: default; duration: 1.449s
Counts: {"pass": 1}; verdict: pass


## lane2 / lane / complete

Box: bx_3; profile: default; duration: 1.461s
Counts: {"pass": 1}; verdict: pass


## lane5 / lane / complete

Box: bx_6; profile: default; duration: 1.519s
Counts: {"pass": 1}; verdict: pass



=== test_lane_ttl_is_used_by_cap_check ===
{"event": "create_intent", "lane": "pilot", "name": "fm-lanes-ede0da21f119-pilot", "reserved_usd": 0.072, "sequence": 2, "time": 1792142826.3421962, "ttl_seconds": 3600, "version": 1}
{"box": "bx_1", "event": "lane_result", "lane": "pilot", "sequence": 10, "signature": null, "time": 1792142826.755621, "verdict": "pass", "version": 1}
{"event": "create_intent", "lane": "lane1", "name": "fm-lanes-ede0da21f119-lane1", "reserved_usd": 0.288, "sequence": 14, "time": 1792142827.0126302, "ttl_seconds": 14400, "version": 1}
{"event": "refused", "lane": "lane2", "new_usd": 0.072, "reason": "projected spend exceeds cap", "reserved_usd": 0.288, "sequence": 16, "spent_usd": 0.001, "time": 1792142827.072692, "version": 1}
{"box": null, "event": "lane_result", "lane": "lane2", "sequence": 18, "signature": "create:refused", "time": 1792142827.0747921, "verdict": "harness_failure", "version": 1}
{"box": "bx_2", "event": "lane_result", "lane": "lane1", "sequence": 19, "signature": null, "time": 1792142827.3406014, "verdict": "interrupted", "version": 1}
{"cleanup_incomplete": false, "event": "summary", "reserved_usd": 0.0, "results": [{"box": "bx_1", "lane": "pilot", "signature": null, "verdict": "pass"}, {"box": null, "lane": "lane2", "signature": "create:refused", "verdict": "harness_failure"}, {"box": "bx_2", "lane": "lane1", "signature": null, "verdict": "interrupted"}], "sequence": 22, "signal": 0, "spent_usd": 0.002, "systemic_halt": false, "time": 1792142827.4649634, "version": 1}
# Boat lane results

Overall case totals: {"pass": 1}
Spend so far: $0.002000; reserved: $0.000000

## pilot / 0 / initial

Box: bx_1; profile: default; duration: 0.097s
Counts: {"pass": 1}; verdict: None


## pilot / lane / complete

Box: bx_1; profile: default; duration: 0.666s
Counts: {"pass": 1}; verdict: pass


## lane2 / lane / complete

Box: None; profile: default; duration: 0.063s
Counts: {}; verdict: harness_failure


## lane1 / lane / complete

Box: bx_2; profile: default; duration: 0.451s
Counts: {}; verdict: interrupted



=== test_known_failures_survi

... [10590 bytes truncated] ...

1}; verdict: None

- case-0: failed, product, retry=never, requests=

## lane1 / 1 / initial

Box: bx_2; profile: default; duration: 0.094s
Counts: {"failed": 1}; verdict: None

- case-1: failed, uncertain, retry=skipped-no-fresh-stack-selection, requests=

## lane1 / 2 / initial

Box: bx_2; profile: default; duration: 0.155s
Counts: {"pass": 1}; verdict: None


## lane1 / lane / complete

Box: bx_2; profile: default; duration: 1.451s
Counts: {"failed": 2, "pass": 1}; verdict: product_failure

- case-0: failed, product, retry=never, requests=
- case-1: failed, uncertain, retry=skipped-no-fresh-stack-selection, requests=

## lane2 / 0 / initial

Box: bx_3; profile: default; duration: 0.113s
Counts: {"failed": 1}; verdict: None

- case-0: failed, product, retry=never, requests=

## lane2 / 1 / initial

Box: bx_3; profile: default; duration: 0.176s
Counts: {"dependency-blocked": 1}; verdict: None

- case-1: dependency-blocked, known-harness-defect, retry=never, requests=

## lane2 / 2 / initial

Box: bx_3; profile: default; duration: 0.119s
Counts: {"pass": 1}; verdict: None


## lane2 / lane / complete

Box: bx_3; profile: default; duration: 1.262s
Counts: {"failed": 1, "dependency-blocked": 1, "pass": 1}; verdict: product_failure

- case-0: failed, product, retry=never, requests=
- case-1: dependency-blocked, known-harness-defect, retry=never, requests=

## lane3 / 0 / initial

Box: bx_4; profile: default; duration: 0.115s
Counts: {"failed": 1}; verdict: None

- case-0: failed, uncertain, retry=skipped-no-fresh-stack-selection, requests=

## lane3 / 1 / initial

Box: bx_4; profile: default; duration: 0.098s
Counts: {"failed": 1}; verdict: None

- case-1: failed, product, retry=never, requests=

## lane3 / 2 / initial

Box: bx_4; profile: default; duration: 0.125s
Counts: {"pass": 1}; verdict: None


## lane3 / lane / complete

Box: bx_4; profile: default; duration: 1.037s
Counts: {"failed": 2, "pass": 1}; verdict: product_failure

- case-0: failed, uncertain, retry=skipped-no-fresh-stack-selection, requests=
- case-1: failed, product, retry=never, requests=

## lane4 / 0 / initial

Box: bx_5; profile: default; duration: 0.132s
Counts: {"failed": 1}; verdict: None

- case-0: failed, uncertain, retry=skipped-no-fresh-stack-selection, requests=

## lane4 / 1 / initial

Box: bx_5; profile: default; duration: 0.146s
Counts: {"dependency-blocked": 1}; verdict: None

- case-1: dependency-blocked, known-harness-defect, retry=never, requests=

## lane4 / 2 / initial

Box: bx_5; profile: default; duration: 0.116s
Counts: {"pass": 1}; verdict: None


## lane4 / lane / complete

Box: bx_5; profile: default; duration: 1.219s
Counts: {"failed": 1, "dependency-blocked": 1, "pass": 1}; verdict: uncertain_failure

- case-0: failed, uncertain, retry=skipped-no-fresh-stack-selection, requests=
- case-1: dependency-blocked, known-harness-defect, retry=never, requests=

## lane5 / 0 / initial

Box: bx_6; profile: default; duration: 0.108s
Counts: {"dependency-blocked": 1}; verdict: None

- case-0: dependency-blocked, known-harness-defect, retry=never, requests=

## lane5 / 1 / initial

Box: bx_6; profile: default; duration: 0.095s
Counts: {"failed": 1}; verdict: None

- case-1: failed, product, retry=never, requests=

## lane5 / 2 / initial

Box: bx_6; profile: default; duration: 0.092s
Counts: {"pass": 1}; verdict: None


## lane5 / lane / complete

Box: bx_6; profile: default; duration: 0.904s
Counts: {"dependency-blocked": 1, "failed": 1, "pass": 1}; verdict: product_failure

- case-0: dependency-blocked, known-harness-defect, retry=never, requests=
- case-1: failed, product, retry=never, requests=

## lane6 / 0 / initial

Box: bx_7; profile: default; duration: 0.075s
Counts: {"dependency-blocked": 1}; verdict: None

- case-0: dependency-blocked, known-harness-defect, retry=never, requests=

## lane6 / 1 / initial

Box: bx_7; profile: default; duration: 0.111s
Counts: {"failed": 1}; verdict: None

- case-1: failed, uncertain, retry=skipped-no-fresh-stack-selection, requests=

## lane6 / 2 / initial

Box: bx_7; profile: default; duration: 0.09s
Counts: {"pass": 1}; verdict: None


## lane6 / lane / complete

Box: bx_7; profile: default; duration: 0.853s
Counts: {"dependency-blocked": 1, "failed": 1, "pass": 1}; verdict: uncertain_failure

- case-0: dependency-blocked, known-harness-defect, retry=never, requests=
- case-1: failed, uncertain, retry=skipped-no-fresh-stack-selection, requests=


=== test_continue_stops_on_invalid_evidence ===
{"event": "create_intent", "lane": "pilot", "name": "fm-lanes-5c0e44288f9e-pilot", "reserved_usd": 0.011999999999999999, "sequence": 2, "time": 1792142870.1821592, "ttl_seconds": 600, "version": 1}
{"box": "bx_1", "event": "lane_result", "lane": "pilot", "sequence": 10, "signature": "selection:refused", "time": 1792142870.5759468, "verdict": "harness_failure", "version": 1}
{"cleanup_incomplete": false, "event": "summary", "reserved_usd": 0.0, "results": [{"box": "bx_1", "lane": "pilot", "signature": "selection:refused", "verdict": "harness_failure"}], "sequence": 13, "signal": 0, "spent_usd": 0.001, "systemic_halt": false, "time": 1792142870.8327231, "version": 1}
# Boat lane results

Overall case totals: {}
Spend so far: $0.001000; reserved: $0.000000

## pilot / lane / complete

Box: bx_1; profile: default; duration: 0.649s
Counts: {}; verdict: harness_failure



=== test_continue_stops_on_guard_failure ===
{"event": "create_intent", "lane": "pilot", "name": "fm-lanes-df9f1e85e87a-pilot", "reserved_usd": 0.011999999999999999, "sequence": 2, "time": 1792142871.027164, "ttl_seconds": 600, "version": 1}
{"box": "bx_1", "event": "lane_result", "lane": "pilot", "sequence": 12, "signature": "guard:refused", "time": 1792142871.7636044, "verdict": "harness_failure", "version": 1}
{"cleanup_incomplete": false, "event": "summary", "reserved_usd": 0.0, "results": [{"box": "bx_1", "lane": "pilot", "signature": "guard:refused", "verdict": "harness_failure"}], "sequence": 15, "signal": 0, "spent_usd": 0.001, "systemic_halt": false, "time": 1792142872.0908554, "version": 1}
# Boat lane results

Overall case totals: {"pass": 1}
Spend so far: $0.001000; reserved: $0.000000

## pilot / 0 / initial

Box: bx_1; profile: default; duration: 0.16s
Counts: {"pass": 1}; verdict: None


## pilot / lane / complete

Box: bx_1; profile: default; duration: 1.062s
Counts: {"pass": 1}; verdict: harness_failure



=== test_continue_stops_on_timeout ===
{"event": "create_intent", "lane": "pilot", "name": "fm-lanes-00d7e686497c-pilot", "reserved_usd": 0.011999999999999999, "sequence": 2, "time": 1792142872.2293475, "ttl_seconds": 600, "version": 1}
{"box": "bx_1", "event": "lane_result", "lane": "pilot", "sequence": 9, "signature": "selection:timeout", "time": 1792142872.8257062, "verdict": "timeout", "version": 1}
{"cleanup_incomplete": false, "event": "summary", "reserved_usd": 0.0, "results": [{"box": "bx_1", "lane": "pilot", "signature": "selection:timeout", "verdict": "timeout"}], "sequence": 12, "signal": 0, "spent_usd": 0.001, "systemic_halt": false, "time": 1792142873.0135787, "version": 1}
# Boat lane results

Overall case totals: {}
Spend so far: $0.001000; reserved: $0.000000

## pilot / lane / complete

Box: bx_1; profile: default; duration: 0.782s
Counts: {}; verdict: timeout



=== test_continue_stops_on_cancellation ===
{"event": "create_intent", "lane": "pilot", "name": "fm-lanes-a547d7b69021-pilot", "reserved_usd": 0.011999999999999999, "sequence": 2, "time": 1792142873.1203008, "ttl_seconds": 600, "version": 1}
{"box": "bx_1", "event": "lane_result", "lane": "pilot", "sequence": 8, "signature": null, "time": 1792142873.9097044, "verdict": "interrupted", "version": 1}
{"cleanup_incomplete": false, "event": "summary", "reserved_usd": 0.0, "results": [{"box": "bx_1", "lane": "pilot", "signature": null, "verdict": "interrupted"}], "sequence": 11, "signal": 15, "spent_usd": 0.001, "systemic_halt": false, "time": 1792142874.096881, "version": 1}
# Boat lane results

Overall case totals: {}
Spend so far: $0.001000; reserved: $0.000000

## pilot / lane / complete

Box: bx_1; profile: default; duration: 0.974s
Counts: {}; verdict: interrupted
Evidence: Targeted acceptance results
test_success_pilot_gate_and_cleanup (cases.Cases) ... ok
test_lane_ttl_overrides_creation_reservation_and_runs_concurrently (cases.Cases) ... ok
test_lane_ttl_is_used_by_cap_check (cases.Cases) ... ok
test_invalid_lane_ttl_is_refused_before_box_creation (cases.Cases) ... ok
test_continue_on_failure_runs_later_selection_and_fetches_artifacts (cases.Cases) ... ok
test_continued_lane_verdict_preserves_failure_precedence_in_both_orders (cases.Cases) ... ok
test_lane_still_stops_after_nonpass_by_default (cases.Cases) ... ok
test_invalid_continue_on_failure_is_refused_before_box_creation (cases.Cases) ... ok
test_known_product_failure_ids_are_not_retried (cases.Cases) ... ok
test_known_failures_survive_overlapping_retry_evidence_and_stop_fanout (cases.Cases) ... ok
test_invalid_known_product_failure_ids_are_refused_before_box_creation (cases.Cases) ... ok
test_case_retry_excludes_known_defect (cases.Cases) ... ok
test_infra_retried_once_product_never_retried (cases.Cases) ... ok
test_overlapping_fallback_never_reruns_attempted_case (cases.Cases) ... ok
test_retry_reclassification_protects_unattempted_case (cases.Cases) ... ok
test_smallest_selection_retries_all_targets_once (cases.Cases) ... ok
test_concurrent_budget_includes_active_box_reservations (cases.Cases) ... ok
test_help_owns_schema_and_exits (cases.Cases) ... ok

----------------------------------------------------------------------
Ran 18 tests in 25.556s

OK
Evidence: Adversarial results
test_eight_post_pilot_lanes_concurrent_under_one_dollar (__main__.Live) ... ok
test_continue_stops_on_invalid_evidence (__main__.Live) ... ok
test_continue_stops_on_guard_failure (__main__.Live) ... ok
test_continue_stops_on_timeout (__main__.Live) ... ok
test_continue_stops_on_cancellation (__main__.Live) ... ok

----------------------------------------------------------------------
Ran 5 tests in 6.315s

OK
Evidence: Clean-base diagnostics result
Clean base bb51ff2110fe9cc6536b927e00433efe045eb948, extracted bin/ and tests/ without edits.
test_diagnostics_persist_while_running_and_survive_signal (baseline_cases.Cases) ... ok

----------------------------------------------------------------------
Ran 1 test in 8.342s

OK
Evidence: Runner help
usage: fm-boat-lanes.py [-h] --spec SPEC --run-dir RUN_DIR --cap-usd CAP_USD
                        [--pilot-only] [--jobs JOBS]

Run versioned, disposable Boat QA lanes (POSIX workstation only).

Usage: fm-boat-lanes.py --spec lanes.json --run-dir NEW_DIRECTORY --cap-usd .50
       [--pilot-only] [--jobs 2]

This header and --help own the mechanics. No project recipe is built in.
The runner imports fm-boat.py's client, credentials and hourly rates; FM_BOAT_BIN,
FM_BOAT_CONFIG_FILE and FM_BOAT_CURL_BIN retain that client's meanings.
Boat personal/base creation always uses --no-env and an explicit TTL. No existing
box can be supplied. Each created ID is fsynced before customization; the provider
name is fm-lanes-<random run token>-<lane>. Never adopt or delete another run's box.

Spec v1 is JSON: {"version":1,"size":"large","ttl_seconds":1800,
 "pilot":"pilot","known_product_failures":["case-id"],"env":{"NAME":"value"},
 "prepare":[STEP],"guard":STEP,"lanes":[{"id":"pilot","ttl_seconds":3600,
 "continue_on_failure":true,"selections":[STEP]}],"artifacts":["/tmp/results"],
 "finish":[STEP]}.
STEP is {"command":"shell script","timeout_seconds":60} or, in prepare only,
 {"upload":"/absolute/local/file","destination":"/tmp/file","timeout_seconds":60}.
size accepts fm-boat.py's RATES keys; spec-wide ttl_seconds is an integer from
60 through 86400. Lane IDs are unique and match [a-zA-Z0-9][a-zA-Z0-9_-]{0,31};
pilot names one of them. Each lane has nonempty selections, an optional
report-only profile (default "default"), and an optional ttl_seconds override
using the same range; omitted lane TTLs inherit the spec value.
continue_on_failure is an optional boolean that defaults to false. When true,
later selections continue after a selection reports failed or blocked cases;
harness errors, timeouts, cancellation, and invalid evidence still stop the lane.
prepare, finish, artifacts and env default to empty; known_harness_defects and
infra_signatures are optional top-level lists of nonempty literal substrings.
known_product_failures is an optional top-level list of unique case IDs matching
[A-Za-z0-9_.:-]{1,120}; failed matching cases are product failures and are never retried.
Dependency-blocked evidence keeps its existing classification.
Every step needs a positive finite timeout_seconds. env maps shell variable
names to strings without NUL; FM_ names are reserved. Artifact and evidence
paths must be beneath /tmp without '..' components. Upload sources must exist.
 Keep project recipes private under the operator home's config/boat-lanes/;
 tests/boat-lane-runner-cases.py contains offline fake-provider spec examples.
 Commands execute remotely in bash with strict errors,
 VIRTUAL_ENV unset, spec env plus FM_LANE, FM_RUN_TOKEN and FM_BOX_ID. Uploads use
 Boat scp. guard is mandatory and runs after prepare and before each selection;
 it must assert the project's source/module/cwd/credential agreement. Selections
 must include the real test invocation AND case evidence; exit zero and all
 cases passing are both required. All commands are trusted operator input, never echoed.
 Docker inventory must be empty before prepare. Preparation/guard/transport
 failures are harness_failure; a nonzero selection exit is a failure observation.
 Passing evidence with a nonzero exit is a harness contradiction; a permitted
 retry may recover failed cases but never erases the initial failure observation.
 Artifacts must exclude credentials; only explicitly declared paths are fetched.
 Each nonempty case failure signature is registered when evidence is read,
 independently of case IDs, other failures and later retry success. A signature
 observed on two distinct lanes stops new creation before reports or downloads;
 already created lanes finish and clean up. Missing or empty case signatures use
 phase+exit at the same boundary, for initial and retry evidence, even if a later
 retry passes. A failing case with exit zero uses exit 1 for fallback matching.

Required selection evidence_file is a /tmp JSON object with cases:[{id,status,
 failure_signature,request_ids,kind,dependency_blocked}]. cases must be nonempty.
 Only id and status are required: IDs are unique within the evidence file and
 match [A-Za-z0-9_.:-]{1,120}; status matches [A-Za-z_-]{1,32} and only exact
 "pass" passes. Optional failure_signature defaults to "" and must be a string,
 dependency_blocked defaults to false and must be a boolean, request_ids defaults
 to [] and must contain strings matching [A-Za-z0-9_.:-]{1,160}, and kind
 (when present) must be infra or product.
 Malformed fields are harness evidence errors. Non-pass cases classify as known-harness-defect when
 dependency_blocked=true, status="dependency-blocked", or matching a literal
 substring in spec known_harness_defects;
 then infra when kind=infra or matching infra_signatures (e.g. a known outage);
 then product when kind=product; otherwise uncertain. Passes classify as pass.
 Only infra/uncertain are eligible for one retry. Selection retry_reset:[STEP]
 must rebuild a fresh stack; retry_cases:{case_id:STEP} provides case re-selection,
 else retry_selection:STEP must be the smallest containing selection, and is
 skipped when it would rerun an attempted case or a currently known-defect/product case. Never rerun
 all lane selections. Missing reset/selection records a skipped retry. Retry
 commands must write updated evidence_file; known defects/product are never
 retry targets. Newly reported retry cases remain in verdicts and reports; newly
 observed infra/uncertain cases are recorded without adding retry targets.
 Infra transport/timeout failures without readable case evidence
 remain harness/timeout verdicts, not invented product cases.

Stack-up steps must declare role:"stack_up" and observe:STEP. The observer is a
 streaming diagnostic command started and acknowledged BEFORE the main command.
 After establishing its capture/watchers, the observer itself must flush the exact
 line FM_OBSERVER_READY on stdout or stderr within 15 seconds. The wrapper never
 emits readiness. The observer must remain running until runner cancellation;
 any premature completion (including exit zero) fails the step and pilot.
 Its timeout must exceed the main deadline by at least 5s. Put project-specific
 Compose service discovery/log-follow commands in the observer spec. Follow logs
 from container creation, since project startup may remove failed services before
 returning. On step completion, the observer's process tree is terminated and its
 bounded, redacted tail is persisted incrementally mode0600 under artifacts/<lane>/diagnostics before
 finish/delete. Observer commands must redact generated secrets at their source;
 the runner additionally removes credential assignments, bearer tokens and URI
 passwords. Sensitive env values are never echoed. This also applies to retry
 stack resets marked stack_up. No diagnostics command may operate another box.
 Observers may emit JSON lines {"service":"redis","line":"redacted log line"};
 these retain a separate last-200-line tail per service, so another service's
 output cannot bury an earlier startup error. At most 32 services are retained;
 excess services use the plain tail. Each tail is at most 32768 characters,
 and input lines over 65536 bytes are discarded. Other output uses one bounded tail.

Command-step stdout/stderr is drained and discarded. Consumers of provider JSON,
 Docker inventory and case evidence retain complete responses
 up to 1MiB per stream; larger responses fail closed rather than being truncated.

report.jsonl appends a fsynced record immediately after each selection/retry and
 lane finish: lane, box, profile, duration_seconds, cases (id/status/classification/
 request_ids/retry_decision), case_counts, failures and spend_so_far_usd. Each
 append recomputes overall_totals from the latest case result per lane/selection/id
 (retries replace their original result). report.md is atomically replaced after
 every append and readable mid-run. Only structural evidence is recorded, never
 arbitrary request bodies, credentials, commands or remote stdout/stderr.

The pilot completes its real selections and cleanup before any other box is
 created. --pilot-only omits fan-out; --jobs (positive integer, default 2) bounds
 subsequent concurrent lanes. cap-usd must be positive and finite; BOAT_ORG is
 refused to keep creation in personal workstation scope. Before every create, spent usage plus ALL
 outstanding TTL reservations plus the new full-TTL reservation must fit cap-usd.
 Unknown usage retains the full reservation; unsuccessful cleanup is a run failure.
 A spend cap bounds projected compute only, using fm-boat.py's hourly rates; it is
 not a provider billing limit. A TTL is mandatory even if the workstation crashes.

The NEW run directory is mode 0700. ledger.jsonl is append-only, mode 0600, fsynced
 JSON events with version=1, sequence, time, event and event-specific fields.
 create_intent records lane/name/TTL/reserved_usd before the API call; created
 records box immediately; usage records the full provider object BEFORE delete;
 deleted records explicit info 404 proof. lane_result records verdict/signature;
 summary records spent_usd/reserved_usd/systemic_halt/results. Private artifact
 directories contain declared downloads and observer diagnostics. A crash leaves IDs (and in-flight
 create names) in the ledger for manual account inspection/cleanup. No automatic
 recovery or ledger reuse is supported; never delete by an unverified name alone.

Each process has its own session. Stdin is delivered incrementally without
 blocking output draining, deadline enforcement or cancellation. Deadlines terminate its group AND remembered
 descendants, then kill after a grace period even if the leader already exited.
 Remote scripts also have a provider-side timeout; timeout prevents the next
 selection. SIGINT/SIGTERM stop scheduling, reap commands, capture usage, delete
 every owned box, and verify info's explicit 404. Cleanup continues across errors.
 SIGKILL/power loss cannot run cleanup: use the ledger and provider TTL.

 Exit codes: 0 all selected lanes pass with deletion proofs; 2 invalid spec/input;
 3 harness failure/refusal (including budget); 4 selection failure; 5 systemic halt;
 6 cleanup incomplete; 124 lane timeout; 130 SIGINT; 143 SIGTERM.
 When outcomes overlap, precedence is cleanup, signal, systemic halt, timeout,
 harness failure, selection failure, then success.

options:
  -h, --help         show this help message and exit
  --spec SPEC
  --run-dir RUN_DIR
  --cap-usd CAP_USD
  --pilot-only
  --jobs JOBS
- Outcome: ⚠️ 1 warning across 1 run (3m9s)

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 3 issues found → auto-fixed (2) ✅
  • 🚨 bin/fm-boat-lanes.py:675 - The requirement says listed failed product cases are “never retried,” but the new condition requires status == "fail". The public evidence contract also accepts "failed" and other non-pass statuses. A listed case with status="failed" and no kind becomes uncertain, enters retry targets at bin/fm-boat-lanes.py:766, and executes its retry at :798. The same classification boundary processes retry evidence at :799. Apply known-product protection consistently to supported failure statuses while preserving dependency-blocked handling.
  • ⚠️ bin/fm-boat-lanes.py:896 - With continue_on_failure=true, a product failure in selection 0 followed by a dependency-blocked failure in selection 1 changes the lane verdict to known_harness_defect, despite the retained product failure. Each failing selection overwrites the previous verdict. Aggregate classifications across selections using the existing selection precedence at bin/fm-boat-lanes.py:821; the resulting verdict must remain consistent in lane_result (:921) and the complete report (:939).
  • ⚠️ tests/boat-lane-runner-cases.py:183 - This concurrent test assumes lane1 acquires the creation lock before lane2. ThreadPoolExecutor does not guarantee that order: lane2 can reserve $.072 first, causing lane1's $.288 reservation to be refused instead. Then assertions at :185-190 fail despite correct cap enforcement. Coordinate admission explicitly or assert the reservation/refusal arithmetic independently of lane identity.

🔧 Fix applied.
3 issues (2 errors, 1 warning) still open:

  • 🚨 bin/fm-boat-lanes.py:675 - The requirement says listed failed product cases are “never retried,” but the new condition requires status == "fail". The public evidence contract also accepts "failed" and other non-pass statuses. A listed case with status="failed" and no kind becomes uncertain, enters retry targets at bin/fm-boat-lanes.py:766, and executes its retry at :798. The same classification boundary processes retry evidence at :799. Apply known-product protection consistently to supported failure statuses while preserving dependency-blocked handling.
  • ⚠️ bin/fm-boat-lanes.py:896 - With continue_on_failure=true, a product failure in selection 0 followed by a dependency-blocked failure in selection 1 changes the lane verdict to known_harness_defect, despite the retained product failure. Each failing selection overwrites the previous verdict. Aggregate classifications across selections using the existing selection precedence at bin/fm-boat-lanes.py:821; the resulting verdict must remain consistent in lane_result (:921) and the complete report (:939).
  • 🚨 bin/fm-boat-lanes.py:675 - Round 1 fixed retry targeting but left the retry-merge sibling unprotected. The requirement says listed failed cases must “retain failure status and evidence.” Start with listed K failing and eligible E failing; E's supported per-case retry can return additional rows, including K=pass. The unconditional merge at bin/fm-boat-lanes.py:811-817 replaces K's status, request IDs, and signature hash, then :818 updates reported totals and :819-824 can return success, allowing pilot fan-out. Additional protected rows in per-case retry evidence are already exercised by tests/boat-lane-runner-cases.py's test_infra_retried_once_product_never_retried. Preserve previously observed listed failures at the shared retry-merge boundary, including their retry report and final verdict representations.

🔧 Fix applied.
✅ Re-checked - no issues remain.

⚠️ **Test** - 1 warning
  • ⚠️ live validation verdict: inconclusive (8 of 9 scenarios were driven live against the product); untested: Validate the actual private release recipe, measured TTL margins, lane ordering, and AC1–AC5 receipts
  • Live validation: ⚠️ inconclusive - 8 of 9 scenarios driven live against the product
Scenario Result Live Evidence
Run a successful pilot followed by eight concurrent lanes under a $1 total reservation cap ✅ pass live product-transcript.txt: test_eight_post_pilot_lanes_concurrent_under_one_dollar
Override lane TTLs and observe matching reservations, creation TTLs, updates, and cap refusals ✅ pass live targeted-tests.log; saved create_intent ledgers and fake-provider state
Protect listed failures across supported statuses and overlapping retry evidence while retrying eligible cases ✅ pass live targeted-tests.log: known_product_failure_ids_are_not_retried and known_failures_survive_overlapping_retry_evidence_and_stop_fanout
Continue from failed or blocked selections into maintenance, request artifacts, and retain the strongest failure verdict ✅ pass live product-transcript.txt: continuation and failure-precedence cases
Omit new options and preserve default stopping, pilot gating, retry behavior, and cleanup ✅ pass live targeted-tests.log: lane_still_stops_after_nonpass_by_default and existing retry cases
Enable continuation and verify guard failures, invalid evidence, timeouts, and cancellation still stop later selections ✅ pass live adversarial-tests.log; product-transcript.txt
Reject malformed new fields before creation and expose their documentation through --help ✅ pass live targeted-tests.log; runner-help.txt
Run the diagnostics startup-persistence case against clean base bb51ff2 ✅ pass live baseline-startup-persistence.log
Validate the actual private release recipe, measured TTL margins, lane ordering, and AC1–AC5 receipts ⏸️ untested no The private recipe, v0.5.15 source specs, duration report, and receipts are not supplied inside this worktree. This phase forbids reading another project checkout. A disposable nine-lane recipe exerci…
  • python3 ~/.no-mistakes/evidence/01M51ZHGR7Y9C9B4RWVRHTT81E/drive.py — 18 selected executable acceptance cases
  • python3 ~/.no-mistakes/evidence/01M51ZHGR7Y9C9B4RWVRHTT81E/adversarial.py — eight-lane concurrency and four continuation stop boundaries
  • python3 ~/.no-mistakes/evidence/01M51ZHGR7Y9C9B4RWVRHTT81E/baseline.py — clean-base diagnostics startup-persistence case
  • python3 bin/fm-boat-lanes.py --help
  • Captured runner ledgers, reports, specifications, and isolated provider state; removed disposable worktree data and confirmed clean git status.
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

dnth added 4 commits October 16, 2026 17:07
Use each lane TTL for spend reservation and box creation. Avoid retries for listed failed case IDs and allow opted-in ordered lanes to continue after non-pass selections without changing legacy defaults.
@dnth
dnth merged commit 412b879 into main Oct 16, 2026
18 checks passed
@dnth
dnth deleted the fm/fm-boat-lanes-parallel-fixes branch October 16, 2026 13:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant