Skip to content

ci: rebalance portable serial tests across seven shards - #253

Merged
dnth merged 9 commits into
mainfrom
fm/fm-ci-serial-shard3-timeout
Oct 15, 2026
Merged

dnth merged 9 commits into
mainfrom
fm/fm-ci-serial-shard3-timeout

Conversation

@dnth

@dnth dnth commented Oct 15, 2026 •

Copy link
Copy Markdown
Owner

Intent

Fix Firstmate CI where portable serial shard 3 grew toward its 20-minute timeout. Diagnose per-script timing across cancelled PR attempts, passing main attempts, and green artifacts; do not rebalance to hide a hang. Keep the 20-minute cap and serial-lane partition, preserve assertions, and root-cause a slow or hanging test if evidence shows one. If aggregate growth is the cause, rebalance measured work toward the documented ~14-minute per-shard budget; if both causes exist, address both. Include cancelled and passing shard-3 timing evidence, run the partition guard, and document membership. Firstmate directed the initial six-shard rebalance and then the seventh shard because 166 scripts measured 91m01.112s total and 15m10.185s average across six. Firstmate, not the captain, approved an inconclusive local Test step for the prior run only. For this PR, all seven actual serial job wall times must be at most 15 minutes with no cancellations; otherwise do not approve CI. Firstmate later authorized keeping the correlation-ID test fix: run 38770719267 failed the merged-PR reply assertion without it, while run 38785253557 passed with it. The test previously selected by result-file sequence, which did not reliably identify the reply for that request. Do not rewrite history or drop commits; add a follow-up that reverses restoration commit 3ae8c75, add a Required test fix section explaining the sequence selection and CI evidence, then revalidate. Keep scope limited to these approved changes.

What Changed

  • Rebalance portable serial CI across seven shards using refreshed measurements for 166 scripts, preserving serial execution and the 20-minute timeout.
  • Select the remote secondmate lifecycle test’s pending acknowledgement by correlation ID instead of result-file sequence, preserving existing assertions.
  • Document cancelled and passing timing evidence, shard membership, the required test fix, and the requirement that all seven jobs finish within 15 minutes without cancellation.

Risk Assessment

✅ Low: The bounded changes preserve serial execution, coverage, assertions, and the timeout while implementing the authorized rebalance and correlation-based reply selection.

Testing

Focused runner tests, live membership checks, the partition guard, and adversarial lane requests passed; a CLI transcript was captured. No full suite, lint, or CI was run. Actual CI durations and the lifecycle test remain unverified.

  • Live validation: ⚠️ inconclusive - 2 of 4 scenarios driven live against the product
Scenario Result Live Evidence
List seven serial shards and obtain nonempty, disjoint memberships covering all 166 serial scripts ✅ pass live Live runner partition and refusal transcript
Request obsolete shard counts or invalid indexes and receive exit 2 without a test selection ✅ pass live Live runner partition and refusal transcript
Complete all seven actual CI serial jobs within 15 minutes without cancellations ⏸️ untested no Actual GitHub Actions execution belongs exclusively to the outer executor. Local execution cannot establish GitHub job wall times, and this phase is explicitly prohibited from executing CI. The outer…
Select the lifecycle reply by request correlation rather than result-file sequence ⏸️ untested no This change modifies an existing test's selection logic and has no independent runtime product surface. The lifecycle test was not executed here; its validation remains for CI.
Evidence: Live runner partition and refusal transcript
$ bin/fm-test-run.sh --list --lane portable-serial 
exit: 0
tests/fm-afk-inject-e2e.test.sh
tests/fm-afk-pi-herdr-return-e2e.test.sh
tests/fm-afk-return.test.sh
tests/fm-ask-user-authority.test.sh
tests/fm-backend-cmux-smoke.test.sh
tests/fm-backend-cmux.test.sh
tests/fm-backend-herdr-focus-flash-e2e.test.sh
tests/fm-backend-herdr-server-detach.test.sh
tests/fm-backend-orca.test.sh
tests/fm-backend-tmux-smoke.test.sh
tests/fm-backend-zellij-smoke.test.sh
tests/fm-backend-zellij.test.sh
tests/fm-backend.test.sh
tests/fm-backlog-atomicity.test.sh
tests/fm-backlog-handoff.test.sh
tests/fm-bearings-snapshot.test.sh
tests/fm-boat-lane-runner.test.sh
tests/fm-boat-lifecycle.test.sh
tests/fm-boat-omp-auth.test.sh
tests/fm-boat-routing.test.sh
tests/fm-bootstrap.test.sh
tests/fm-busy-adapter-wiring.test.sh
tests/fm-busy-state.test.sh
tests/fm-calm-pi-extension.test.sh
tests/fm-check-unregister.test.sh
tests/fm-classify-decision-key.test.sh
tests/fm-claude-stop-autoarm-live-e2e.test.sh
tests/fm-claude-stop-autoarm.test.sh
tests/fm-cleanup-skill.test.sh
tests/fm-codex-continuity-live-e2e.test.sh
tests/fm-daemon.test.sh
tests/fm-devin-adapter.test.sh
tests/fm-devin-herdr-live-e2e.test.sh
tests/fm-dispatch-resolve.test.sh
tests/fm-documentation-audiences.test.sh
tests/fm-ext-ask.test.sh
tests/fm-ext-bridge.test.sh
tests/fm-ext-decision.test.sh
tests/fm-ext-plugin-dest.test.sh
tests/fm-ext-thread-context.test.sh
tests/fm-ext-topic-threads.test.sh
tests/fm-ext-waiting-pin.test.sh
tests/fm-ext-watch.test.sh
tests/fm-fleet-snapshot-view.test.sh
tests/fm-fleet-sync.test.sh
tests/fm-gate-refuse.test.sh
tests/fm-gitignore-config.test.sh
tests/fm-gotmp.test.sh
tests/fm-grok-continuity-live-e2e.test.sh
tests/fm-grok-stop-live-e2e.test.sh
tests/fm-guard-stale-banner.test.sh
tests/fm-herdr-session-cleanup.test.sh
tests/fm-herdr-submit-confirm-live-e2e.test.sh
tests/fm-hermes-harness.test.sh
tests/fm-hermes-live-e2e.test.sh
tests/fm-idle-sleep-watch.test.sh
tests/fm-idle-sleep.test.sh
tests/fm-inbox-result.test.sh
tests/fm-inbox.test.sh
tests/fm-kimi-harness.test.sh
tests/fm-landing.test.sh
tests/fm-main-ci-watch.test.sh
tests/fm-merge-local.test.sh
tests/fm-omp-branch-bash.test.sh
tests/fm-omp-branch-live-e2e.test.sh
tests/fm-omp-branch-supervision.test.sh
tests/fm-omp-branch-types.test.sh
tests/fm-omp-compact-adviser-live-e2e.test.sh
tests/fm-omp-compact-adviser.test.sh
tests/fm-omp-fleet-hooks.test.sh
tests/fm-omp-harness.test.sh
tests/fm-omp-herdr-exit-live-e2e.test.sh
tests/fm-omp-herdr-live-e2e.test.sh
tests/fm-omp-primary-live-e2e.test.sh
tests/fm-omp-primary.test.sh
tests/fm-omp-relaunch-guard.test.sh
tests/fm-omp-relaunch-tmux-live-e2e.test.sh
tests/fm-omp-secondmate-live-e2e.test.sh
tests/fm-omp-secondmate.test.sh
tests/fm-omp-task-inbox-doorbell.test.sh
tests/fm-omp-update.test.sh
tests/fm-omp-worker-tmux-live-e2e.test.sh
tests/fm-on.test.sh
tests/fm-opencode-primary-live-e2e.test.sh
tests/fm-operational-input.test.sh
tests/fm-pending-reply-sleep-reconcile.test.sh
tests/fm-pending-reply.test.sh
tests/fm-pi-compatible-family.test.sh
tests/fm-pi-primary-live-e2e.test.sh
tests/fm-pi-watch-extension.test.sh
tests/fm-pr-check-handoff.test.sh
tests/fm-pr-check-security.test.sh
tests/fm-prepush-guard.test.sh
tests/fm-procevent-when.test.sh
tests/fm-procevent.test.sh
tests/fm-public-followup.test.sh
tests/fm-quota-array-dispatch-live-e2e.test.sh
tests/fm-receipt-check.test.sh
tests/fm-receipt.test.sh
tests/fm-reflect-skill.test.sh
tests/fm-remote-backlog-handoff.test.sh
tests/fm-remote-doctor.test.sh
tests/fm-remote-entrypoint.test.sh
tests/fm-remote-job.test.sh
tests/fm-remote-omp-update-restart-live-e2e.test.sh
tests/fm-remote-reply.test.sh
tests/fm-remote-secondmate-lifecycle-e2e.test.sh
tests/fm-remote-secondmate-parent-binding.test.sh
tests/fm-remote-secondmate-sleep-reconcile.test.sh
tests/fm-remote-secondmate-trace-context.test.sh
tests/fm-remote-secondmate-update-restart.test.sh
tests/fm-runpod-lifecycle.test.sh
tests/fm-runpod-omp-auth.test.sh
tests/fm-runpod-pod-boot.test.sh
tests/fm-runpod-routing.test.sh
tests/fm-secondmate-harness.test.sh
tests/fm-secondmate-lifecycle-e2e.test.sh
tests/fm-secondmate-liveness.test.sh
tests/fm-secondmate-restart.test.sh
tests/fm-secondmate-safety.test.sh
tests/fm-secondmate-sync.test.sh
tests/fm-send-inbox-doorbell-live-e2e.test.sh
tests/fm-send-inbox.test.sh
tests/fm-send-resolve-key.test.sh
tests/fm-send-secondmate-marker-herdr-e2e.test.sh
tests/fm-send-secondmate-marker.test.sh
tests/fm-send-turn-start.test.sh
tests/fm-session-lock-ancestry.test.sh
tests/fm-session-start.test.sh
tests/fm-sessionstart-nudge.test.sh
tests/fm-shared-captain-inheritance.test.sh
tests/fm-spawn-dispatch-profile.test.sh
tests/fm-spawn-pool-base-freshen.test.sh
tests/fm-spawn-relaunch-dead-endpoint.test.sh
tests/fm-spawn-worktree-settle.test.sh
tests/fm-stall-recovery.test.sh
tests/fm-startup-memory-budget.test.sh
tests/fm-subagent-pretool-check.test.sh
tests/fm-supervision-events.test.sh
tests/fm-tangle-guard.test.sh
tests/fm-task-delivery.test.sh
tests/fm-task-inbox.test.sh
tests/fm-tasks-axi.test.sh
tests/fm-teardown-endpoint-safety.test.sh
tests/fm-teardown.test.sh
tests/fm-test-fixture-cleanup.test.sh
tests/fm-test-isolation-proof.test.sh
tests/fm-todo-project.test.sh
tests/fm-trace-context-lib.test.sh
tests/fm-trace-context-spawn.test.sh
tests/fm-treehouse-orphan-recovery.test.sh
tests/fm-treehouse-status-read-only.test.sh
tests/fm-treehouse-sweep.test.sh
tests/fm-turnend-guard.test.sh
tests/fm-update.test.sh
tests/fm-vendor-auth-probe.test.sh
tests/fm-wake-daemon-lifecycle-e2e.test.sh
tests/fm-wake-drain-open-decisions-cursor.test.sh
tests/fm-wake-drain-open-decisions.test.sh
tests/fm-wake-drain-unread-status.test.sh
tests/fm-wake-queue.test.sh
tests/fm-watch-arm.test.sh
tests/fm-watch-checkpoint.test.sh
tests/fm-watch-recovery-loop.test.sh
tests/fm-watch-triage.test.sh
tests/fm-watcher-lock.test.sh
$ bin/fm-test-run.sh --list --lane portable-serial-1of7 
exit: 0
tests/fm-remote-secondmate-lifecycle-e2e.test.sh
tests/fm-wake-drain-unread-status.test.sh
tests/fm-pi-watch-extension.test.sh
tests/fm-backend-orca.test.sh
tests/fm-pr-check-handoff.test.sh
tests/fm-gate-refuse.test.sh
tests/fm-todo-project.test.sh
tests/fm-secondmate-lifecycle-e2e.test.sh
tests/fm-treehouse-sweep.test.sh
tests/fm-omp-compact-adviser.test.sh
tests/fm-runpod-omp-auth.test.sh
tests/fm-test-isolation-proof.test.sh
tests/fm-operational-input.test.sh
tests/fm-cleanup-skill.test.sh
tests/fm-send-inbox-doorbell-live-e2e.test.sh
tests/fm-omp-branch-live-e2e.test.sh
tests/fm-omp-herdr-exit-live-e2e.test.sh
$ bin/fm-test-run.sh --list --lane portable-serial-2of7 
exit: 0
tests/fm-watch-triage.test.sh
tests/fm-omp-harness.test.sh
tests/fm-runpod-lifecycle.test.sh
tests/fm-main-ci-watch.test.sh
tests/fm-task-inbox.test.sh
tests/fm-secondmate-restart.test.sh
tests/fm-backend.test.sh
tests/fm-procevent-when.test.sh
tests/fm-task-delivery.test.sh
tests/fm-wake-drain-open-decisions.test.sh
tests/fm-tangle-guard.test.sh
tests/fm-ext-plugin-dest.test.sh
tests/fm-guard-stale-banner.test.sh
tests/fm-busy-state.test.sh
tests/fm-receipt.test.sh
tests/fm-gotmp.test.sh
tests/fm-check-unregister.test.sh
tests/fm-ask-user-authority.test.sh
tests/fm-afk-pi-herdr-return-e2e.test.sh
tests/fm-omp-branch-bash.test.sh
tests/fm-omp-branch-types.test.sh
tests/fm-omp-secondmate-live-e2e.test.sh
tests/fm-omp-primary-live-e2e.test.sh
$ bin/fm-test-run.sh --list --lane portable-serial-3of7 
exit: 0
tests/fm-spawn-dispatch-profile.test.sh
tests/fm-ext-topic-threads.test.sh
tests/fm-bearings-snapshot.test.sh
tests/fm-backlog-atomicity.test.sh
tests/fm-secondmate-safety.test.sh
tests/fm-trace-context-spawn.test.sh
tests/fm-runpod-routing.test.sh
tests/fm-fleet-sync.test.sh
tests/fm-boat-routing.test.sh
tests/fm-remote-backlog-handoff.test.sh
tests/fm-tasks-axi.test.sh
tests/fm-fleet-snapshot-view.test.sh
tests/fm-inbox.test.sh
tests/fm-ext-watch.test.sh
tests/fm-omp-relaunch-guard.test.sh
tests/fm-send-secondmate-marker.test.sh
tests/fm-remote-secondmate-sleep-reconcile.test.sh
tests/fm-supervision-events.test.sh
tests/fm-calm-pi-extension.test.sh
tests/fm-treehouse-orphan-recovery.test.sh
tests/fm-backend-cmux-smoke.test.sh
tests/fm-codex-continuity-live-e2e.test.sh
tests/fm-remote-omp-update-restart-live-e2e.test.sh
$ bin/fm-test-run.sh --list --lane portable-serial-4of7 
exit: 0
tests/fm-secondmate-harness.test.sh
tests/fm-ext-waiting-pin.test.sh
tests/fm-watch-arm.test.sh
tests/fm-bootstrap.test.sh
tests/fm-session-start.test.sh
tests/fm-remote-job.test.sh
tests/fm-public-followup.test.sh
tests/fm-runpod-pod-boot.test.sh
tests/fm-omp-branch-supervision.test.sh
tests/fm-afk-inject-e2e.test.sh
tests/fm-omp-task-inbox-doorbell.test.sh
tests/fm-kimi-harness.test.sh
tests/fm-shared-captain-inheritance.test.sh
tests/fm-on.test.sh
tests/fm-herdr-session-cleanup.test.sh
tests/fm-watch-checkpoint.test.sh
tests/fm-backlog-handoff.test.sh
tests/fm-afk-return.test.sh
tests/fm-documentation-audiences.test.sh
tests/fm-classify-decision-key.test.sh
tests/fm-pending-reply-sleep-reconcile.test.sh
tests/fm-pi-compatible-family.test.sh
tests/fm-herdr-submit-confirm-live-e2e.test.sh
tests/fm-grok-stop-live-e2e.test.sh
tests/fm-grok-continuity-live-e2e.test.sh
$ bin/fm-test-run.sh --list --lane portable-serial-5of7 
exit: 0
tests/fm-pr-check-security.test.sh
tests/fm-omp-secondmate.test.sh
tests/fm-wake-queue.test.sh
tests/fm-wake-daemon-lifecycle-e2e.test.sh
tests/fm-boat-omp-auth.test.sh
tests/fm-remote-secondmate-trace-context.test.sh
tests/fm-boat-lifecycle.test.sh
tests/fm-send-inbox.test.sh
tests/fm-teardown-endpoint-safety.test.sh
tests/fm-remote-secondmate-update-restart.test.sh
tests/fm-prepush-guard.test.sh
tests/fm-remote-secondmate-parent-binding.test.sh
tests/fm-pending-reply.test.sh
tests/fm-inbox-result.test.sh
tests/fm-backend-zellij.test.sh
tests/fm-remote-doctor.test.sh
tests/fm-backend-cmux.test.sh
tests/fm-session-lock-ancestry.test.sh
tests/fm-omp-update.test.sh
tests/fm-devin-adapter.test.sh
tests/fm-remote-entrypoint.test.sh
tests/fm-stall-recovery.test.sh
tests/fm-backend-herdr-focus-flash-e2e.test.sh
tests/fm-devin-herdr-live-e2e.test.sh
tests/fm-omp-compact-adviser-live-e2e.test.sh
tests/fm-omp-herdr-live-e2e.test.sh
$ bin/fm-test-run.sh --list --lane portable-serial-6of7 
exit: 0
tests/fm-ext-decision.test.sh
tests/fm-boat-lane-runner.test.sh
tests/fm-ext-bridge.test.sh
tests/fm-procevent.test.sh
tests/fm-omp-primary.test.sh
tests/fm-daemon.test.sh
tests/fm-landing.test.sh
tests/fm-secondmate-sync.test.sh
tests/fm-spawn-relaunch-dead-endpoint.test.sh
tests/fm-send-resolve-key.test.sh
tests/fm-turnend-guard.test.sh
tests/fm-busy-adapter-wiring.test.sh
tests/fm-spawn-worktree-settle.test.sh
tests/fm-send-turn-start.test.sh
tests/fm-dispatch-resolve.test.sh
tests/fm-idle-sleep-watch.test.sh
tests/fm-omp-fleet-hooks.test.sh
tests/fm-trace-context-lib.test.sh
tests/fm-subagent-pretool-check.test.sh
tests/fm-reflect-skill.test.sh
tests/fm-treehouse-status-read-only.test.sh
tests/fm-send-secondmate-marker-herdr-e2e.test.sh
tests/fm-gitignore-config.test.sh
tests/fm-pi-primary-live-e2e.test.sh
tests/fm-opencode-primary-live-e2e.test.sh
tests/fm-omp-relaunch-tmux-live-e2e.test.sh
$ bin/fm-test-run.sh --list --lane portable-serial-7of7 
exit: 0
tests/fm-hermes-harness.test.sh
tests/fm-teardown.test.sh
tests/fm-remote-reply.test.sh
tests/fm-watch-recovery-loop.test.sh
tests/fm-claude-stop-autoarm.test.sh
tests/fm-ext-ask.test.sh
tests/fm-watcher-lock.test.sh
tests/fm-vendor-auth-probe.test.sh
tests/fm-spawn-pool-base-freshen.test.sh
tests/fm-wake-drain-open-decisions-cursor.test.sh
tests/fm-secondmate-liveness.test.sh
tests/fm-ext-thread-context.test.sh
tests/fm-idle-sleep.test.sh
tests/fm-update.test.sh
tests/fm-startup-memory-budget.test.sh
tests/fm-receipt-check.test.sh
tests/fm-backend-herdr-server-detach.test.sh
tests/fm-merge-local.test.sh
tests/fm-test-fixture-cleanup.test.sh
tests/fm-sessionstart-nudge.test.sh
tests/fm-backend-tmux-smoke.test.sh
tests/fm-hermes-live-e2e.test.sh
tests/fm-backend-zellij-smoke.test.sh
tests/fm-claude-stop-autoarm-live-e2e.test.sh
tests/fm-omp-worker-tmux-live-e2e.test.sh
tests/fm-quota-array-dispatch-live-e2e.test.sh
Observed seven nonempty disjoint selections exactly covering portable-serial: 166 scripts
$ bin/fm-test-run.sh --check-coverage 
exit: 0
FM_TEST_COVERAGE ok total=201 parallel=24 serial=166 serial_shards=7 herdr=11
$ bin/fm-test-run.sh --list --lane portable-serial-1of6 
exit: 2
fm-test-run: lane 'portable-serial-1of6' asks for 6 portable serial shards but this runner is configured for 7 (see --list-lanes)
$ bin/fm-test-run.sh --list --lane portable-serial-0of7 
exit: 2
fm-test-run: lane 'portable-serial-0of7' shard index is outside 1..7 (see --list-lanes)
$ bin/fm-test-run.sh --list --lane portable-serial-8of7 
exit: 2
fm-test-run: lane 'portable-serial-8of7' shard index is outside 1..7 (see --list-lanes)
Obsolete shard count and invalid indexes refused without emitting test selection
- Outcome: ⚠️ 1 warning across 1 run (1m35s)

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

✅ **Review** - passed

✅ No issues found.

⚠️ **Test** - 1 warning
  • ⚠️ live validation verdict: inconclusive (2 of 4 scenarios were driven live against the product); untested: Complete all seven actual CI serial jobs within 15 minutes without cancellations, Select the lifecycle reply by request correlation rather than result-file sequence
  • Live validation: ⚠️ inconclusive - 2 of 4 scenarios driven live against the product
Scenario Result Live Evidence
List seven serial shards and obtain nonempty, disjoint memberships covering all 166 serial scripts ✅ pass live Live runner partition and refusal transcript
Request obsolete shard counts or invalid indexes and receive exit 2 without a test selection ✅ pass live Live runner partition and refusal transcript
Complete all seven actual CI serial jobs within 15 minutes without cancellations ⏸️ untested no Actual GitHub Actions execution belongs exclusively to the outer executor. Local execution cannot establish GitHub job wall times, and this phase is explicitly prohibited from executing CI. The outer…
Select the lifecycle reply by request correlation rather than result-file sequence ⏸️ untested no This change modifies an existing test's selection logic and has no independent runtime product surface. The lifecycle test was not executed here; its validation remains for CI.
  • TMPDIR="$PWD/.test-phase-tmp" bash tests/fm-test-run.test.sh
  • bin/fm-test-run.sh --check-coverage
  • Executed all seven --list --lane portable-serial-Nof7 selections and compared their combined membership against portable-serial.
  • Executed invalid lane requests: portable-serial-1of6, portable-serial-0of7, and portable-serial-8of7.
  • Removed disposable test files and confirmed a clean working tree.
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

dnth added 6 commits October 15, 2026 23:59
Refresh all serial shard weights from green-run timing artifacts, diagnose the 20-minute cancellations as aggregate runtime, and document the balanced membership.
…selecting each acknowledgement by correlation token. The original failing case and later cleanup cases now pass. Partition guard, focused ShellCheck, and whitespace checks pass. Full lifecycle verification stopped later because existing /tmp/fm-fallback-omp permissions are unsafe; that directory was left untouched
Use the measured 91-minute serial suite from PR run 38774522507 to distribute work across seven CI runners. Refresh the duration hints and membership snapshot while keeping each shard serial and the 20-minute timeout unchanged.
@dnth dnth changed the title ci: rebalance portable serial shards with measured timings ci: rebalance portable serial tests across seven shards Oct 15, 2026
dnth added 3 commits October 16, 2026 02:41
Keep this PR limited to the portable serial shard rebalance. The correlation-ID behavior change is a separate fix and can be reviewed on its own.
@dnth
dnth merged commit 197f71a into main Oct 15, 2026
18 checks passed
@dnth
dnth deleted the fm/fm-ci-serial-shard3-timeout branch October 15, 2026 23:43
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