Skip to content

feat: route crew by quota pace and rearm Pi watchers - #10

Merged
knowttl merged 3 commits into
mainfrom
fm/fm-upstream-sync-3
Jul 28, 2026
Merged

knowttl merged 3 commits into
mainfrom
fm/fm-upstream-sync-3

Conversation

@knowttl

@knowttl knowttl commented Jul 28, 2026

Copy link
Copy Markdown
Owner

Intent

Sync the captain's knowttl/firstmate fork with its upstream template kunchenguid/firstmate, bringing in the two upstream commits the fork lacked: fa0d85d 'feat: route crew dispatch using quota-window pace (kunchenguid#1172)' and 9ea1a1a 'fix(pi): rearm watcher across session transitions (kunchenguid#1166)'. (A third pinned commit, b29621b pi-signed runtime adapter, was already present via the earlier sync merge 94ca122, so it is correctly absent from this diff.) This is deliberately a MERGE, never a rebase or reset of main: the fork is ahead with its own commits (PRs #6/#7/#8 and prior sync merges) and ALL of them are kept, matching how prior syncs were done (see merge 3827d75). The merge-commit shape is a hard requirement - if the pipeline's rebase step cannot preserve it, that must stop and escalate rather than silently flatten the merge. The captain explicitly scoped this as a sync-only PR under standing scope discipline. Exactly one conflict occurred, in docs/watcher-continuity.md's regression-coverage list: our PR #8 rewrote the fm-watcher-lock and fm-claude-stop-autoarm coverage lines while upstream 9ea1a1a inserted a new fm-pi-watch-extension coverage line immediately above them. The two sides are additive and describe different layers, not contradictory, so the resolution deliberately KEEPS BOTH, ordering each sentence adjacent to the suite it describes. This was verified against shipped code, not just prose: our delivering-close prints the wake reason on its own line and the Pi extension's actionableLine/classifyClose classifies a close by that reason line, while upstream's change only gates which session generation may act and never touches classification - so a delivering close still restores continuity correctly under the new generation ownership. Verification already done locally: bin/fm-lint.sh clean at exact CI parity with the pinned ShellCheck 0.11.0, and green across the watch, wake, watcher-lock, turn-end guard, claude-stop-autoarm, bootstrap, spawn, doc-audience, instruction-owners and the new quota-array-dispatch suites. KNOWN AND DELIBERATELY OUT OF SCOPE: tests/fm-pi-watch-extension.test.sh has one PRE-EXISTING failing subtest ('OpenCode watch plugin must arm only when this session owns the fleet lock') that fails identically on origin/main before this merge and was never touched by upstream. Root cause is the OpenCode plugin's module-level launchInFlight single-flight coalescing a second session.idle event into the still-in-flight lock-refused launch so the lock is never rechecked - it fails deterministically at the test's 120ms gap and passes deterministically at a 2000ms gap. The captain decided (option A) to land this sync as-is and file that plugin defect separately as a captain-gated item, so do NOT fix the OpenCode plugin or that test in this PR - a sync merge stays a sync merge. Deliverable is a PR on the captain's own repo with base named explicitly (knowttl/firstmate, base main), never upstream.

What Changed

  • Route crew-dispatch profile arrays using quota-window pace, headroom, uncertainty, and tie handling through the new quota-array-dispatch skill, with updated guidance and schema fixtures.
  • Rearm Pi watchers across same-process session replacements using generation-scoped ownership, preventing stale callbacks from mutating the replacement cycle while retaining shutdown cleanup and regression coverage.

Risk Assessment

⚠️ Medium: Captain, the required merge structure and normal transition behavior are intact, but repeated Pi reloads introduce a bounded-yet-growing listener leak worth fixing before or shortly after merge.

Testing

Inspected the merge topology, ancestry, first-parent scope, and read-only merge preview; ran the focused quota suite; ran the Pi suite through its explicitly accepted untouched OpenCode failure; reran the imported Pi transition and cleanup scenarios cleanly; exercised real delivering-close and stale-receipt watcher flows with reviewer-visible logs; and confirmed a clean worktree. The sync satisfies the tested intent, with no actionable failures.

Evidence: Merge topology, retained ancestry, conflict scope, and resolved documentation
Target merge object
commit=c73b8a1d719c5780c708a90a41a16d846e2506ce
parents=94ca1223198af3ace94d5978769dc08bf4c08c58 fa0d85d00be145196d60eee5dffcbe18ab3af901
subject=Merge upstream kunchenguid/firstmate main (fa0d85d) into knowttl fork

Upstream commits newly reachable from the fork base
9ea1a1a fix(pi): rearm watcher across session transitions (#1166)
fa0d85d feat: route crew dispatch using quota-window pace (#1172)

Required retained ancestry
fa0d85d00be145196d60eee5dffcbe18ab3af901 retained=yes
9ea1a1ab4f0071748558b3ef220ba2bc06fdde9d retained=yes
b29621b retained=yes
de81d3e retained=yes
16c9e67 retained=yes
a45035b retained=yes
c4ec3b2 retained=yes
3827d75 retained=yes

Files whose merge result differs from upstream tip among newly imported paths
docs/documentation-audiences.json
docs/watcher-continuity.md

Resolved regression-coverage lines
`tests/fm-pi-watch-extension.test.sh` checks Pi's first-cycle-or-explicit-repair tool metadata and ownership-based redundant-call no-ops, then simulates actionable and empty child closes against the actual Pi and OpenCode close handlers, blocks prompt delivery to prove the successor launches first, verifies single-flight behavior, changes the session lock before close to prove ownership is rechecked, and hangs each successor arm to prove bounded fallback delivery includes the typed restoration failure.
The same suite covers ordinary same-process session replacement for `/new`, `/resume`, and `/fork`, same-instance shutdown-plus-start, stale prior-generation callbacks, repeated transitions with exactly one live cycle, disappearance of the shutting-down refusal after a valid replacement activates, and terminal quit still refusing late rearm.
`tests/fm-watcher-lock.test.sh` covers verified-successor attach, the typed self-eviction failure, bounded and successor-linked lifecycle rows, a SIGSTOP counterfactual that distinguishes a live PID from a stale beacon before classifying termination, an attached cycle whose watcher delivered its wake closing successfully, and a stale delivery receipt that still reports the typed failure.
`tests/fm-subagent-pretool-check.test.sh` proves Claude retains only the non-status Bash seatbelts.
`tests/fm-claude-stop-autoarm.test.sh` covers the auto-arm's scope, stale and live session owners, unchanged AFK and need boundaries, single-flight, exit-2 translation, and the fork-frugal session-lock ancestry resolution its claim latency depends on.
`FM_CLAUDE_LIVE_E2E=1 tests/fm-claude-stop-autoarm-live-e2e.test.sh` starts with the reproduced stale-lock state, runs session start first, completes two tokenless cycles, and checks the competing-live-owner negative control.

OpenCode plugin changed by this merge
no

Read-only merge preview conflict paths
docs/watcher-continuity.md
conflict_marker_count=1
Evidence: Quota dispatch acceptance scenarios
ok - quota-array-dispatch has one conditional owner and a concise always-loaded boundary
ok - quota-array-dispatch owns the full pace procedure and acceptance scenarios
ok - cross-references point at the single procedure owner
ok - sanitized schemaVersion 3 fixture preserves producer pace shape without private details
ok - case higher-raw-ahead-vs-lower-raw-sustainable -> B (prefer sustainable pace over higher raw headroom with conservation pressure)
ok - case mixed-effective-with-ahead-bound -> B (mixed with aheadWindowIds is conservation pressure)
ok - case both-ahead-least-negative-reserve -> B (among pressured candidates prefer least-negative worst reserve)
ok - case ahead-bounding-window-overrides-neutral-effective-summary -> B (an ahead applicable bounding window creates conservation pressure even when the effective summary is neutral)
ok - case known-sustainable-vs-unknown -> A (prefer known sustainable evidence over unknown pace)
ok - case all-tight-strongest-reasoning -> A (preserve strongest-reasoning class when every candidate is tight)
ok - case genuine-tie-captain-choice -> genuine tie requires captain choice (report genuine ties instead of selecting by array order or harness identity)
ok - case genuine-tie-reversed-array-order -> genuine tie requires captain choice (reversing a genuine tie must still require captain choice)
ok - case schema-v2-absent-pace -> A (absent pace degrades to raw headroom without fabricating pace health)
ok - AGENTS.md does not duplicate the pace procedure body
Evidence: Focused imported Pi watcher behaviors
ok - Pi actionable close starts one successor before wake delivery settles
ok - Pi session transitions use a generation owner across /new /resume /fork, stale callbacks, and quit
ok - Pi process-exit cleanup listener remains singular across session replacement
ok - Pi process-exit cleanup stops the attached arm child
Evidence: Full Pi watcher suite including accepted OpenCode failure
ok - Pi primary watcher extension is tracked, self-hashing, and self-locating
ok - Pi secondmate launch wiring includes both tracked primary extensions
ok - Pi extension reports external healthy watcher output
ok - Pi custom tool exposes repair-only metadata and returns automatic-continuation guidance
ok - Pi redundant tool call returns ownership guidance and spawns no second child
ok - Pi scheduled retry remains extension-owned after another tool call
ok - Pi actionable close starts one successor before wake delivery settles
ok - Pi hung successor falls back to one typed actionable wake
ok - Pi unretired successor falls back without an overlapping retry
ok - Pi late unretired closes resume classified supervision
ok - Pi clean empty close triggers a bounded continuity retry
ok - Pi established clean closes stop at the configured retry limit
ok - Pi close handler verifies session-lock ownership before successor launch
ok - Pi watcher arm distinguishes all session lock ownership states
ok - Pi session transitions use a generation owner across /new /resume /fork, stale callbacks, and quit
ok - Pi process-exit cleanup listener remains singular across session replacement
ok - Pi process-exit cleanup stops the attached arm child
ok - OpenCode primary watcher plugin has the verified TUI wake wiring
ok - OpenCode plugins have an explicit ESM boundary even under a typeless parent package
ok - OpenCode watcher plugin uses the effective FM_HOME state
ok - OpenCode watcher plugin sources the effective config
not ok - OpenCode watch plugin must arm only when this session owns the fleet lock: expected exit 0, got 1
Evidence: Delivering-close arm output with repeated signal reason
watcher: attached pid=541168 (beacon 0s)
watcher: closed pid=541168 (wake delivered by its owning arm)
signal: /tmp/fm-watcher-lock-tests.EnQjDA/arm-delivered-close/state/task.status
Evidence: Delivering-close lifecycle ledger
arm_pid=541269	watcher_pid=541168	origin=attached	started_at=1785256976	ended_at=1785256980	exit_code=unknown	signal=unknown	reason=attached-cycle-delivered	beacon_age=3	lock_before=pid:541168|identity:linux-starttime=43631197 cmdline-hex=62617368002f686f6d652f62727974746f6e2f2e6e6f2d6d697374616b65732f776f726b74726565732f3337636134643136653863352f30314b594d5334485736415a414b37354a564754345746424e512f62696e2f666d2d77617463682e736800	lock_after=pid:none|identity:none	successor=none

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

⚠️ **Review** - 1 warning
  • ⚠️ .pi/extensions/fm-primary-pi-watch.ts:212 - This module-scope exit listener leaks across real Pi /reload cycles. Pi clears the extension cache and reloads extensions with moduleCache: false (loader source), so each reload reevaluates this line. session_shutdown stops the old generation but no longer removes its listener, causing repeated reloads to retain stopped generations and eventually emit MaxListenersExceededWarning. Register the fallback per factory runtime, remove it on shutdown, and re-register it on same-instance session_start. The current test reuses one module import and therefore misses this path.
✅ **Test** - passed

✅ No issues found.

  • git show -s --format='%H%n%P%n%s' c73b8a1d719c5780c708a90a41a16d846e2506ce and targeted commit graph/diff inspection
  • git merge-tree $(git merge-base 94ca1223198af3ace94d5978769dc08bf4c08c58 fa0d85d00be145196d60eee5dffcbe18ab3af901) 94ca1223198af3ace94d5978769dc08bf4c08c58 fa0d85d00be145196d60eee5dffcbe18ab3af901 conflict-path inspection
  • tests/fm-quota-array-dispatch.test.sh
  • tests/fm-pi-watch-extension.test.sh through the documented pre-existing OpenCode lock-recheck failure; every imported Pi scenario before it passed
  • Focused test_pi_actionable_close_starts_single_successor_before_delivery, test_pi_session_transition_generation_owner, test_pi_process_exit_cleanup_listener_lifecycle, and test_pi_process_exit_cleanup_stops_arm_child execution
  • Focused test_attached_arm_accounts_delivered_close_as_success and test_attached_arm_rejects_stale_delivery_receipt execution with preserved arm and lifecycle outputs
  • git diff --quiet 94ca1223198af3ace94d5978769dc08bf4c08c58..c73b8a1d719c5780c708a90a41a16d846e2506ce -- .opencode/plugins/fm-primary-watch-arm.js
  • git status --short after testing
✅ **Document** - passed

✅ No issues found.

⚠️ **Lint** - 1 warning
  • ⚠️ linter found issues (exit code 127)
✅ **Push** - passed

✅ No issues found.

kunchenguid and others added 3 commits July 27, 2026 22:30
* fix(pi): rearm watcher across same-process session transitions

Pi emits session_shutdown for ordinary /new, /resume, and /fork replacement
as well as terminal quit. The primary watcher extension latched a module-level
stopping flag on every shutdown, so a replacement session in the same process
could not arm monitoring until Pi restarted.

Own arm authority per session generation so only the active live generation
may start, stop, or rearm the child. Replacement sessions can arm again without
restarting Pi, stale prior-generation callbacks cannot mutate the active cycle,
and real quit still blocks late rearm.

* no-mistakes(review): Preserve Pi generation isolation and exit cleanup

* no-mistakes(document): Correct Pi watcher transition documentation
* Consume quota-axi pace signals in dispatch profile array selection.

Add quota-array-dispatch as the single owner of the pace-aware candidate
choice, keep AGENTS.md to the intake boundary and load trigger, and cover
the acceptance cases with sanitized schemaVersion 3 fixtures.

* no-mistakes(review): Stop and report genuine quota dispatch ties

* no-mistakes(document): Document quota pace freshness and uncertainty
Brings in the two upstream commits this fork lacked:
- fa0d85d feat: route crew dispatch using quota-window pace (kunchenguid#1172)
- 9ea1a1a fix(pi): rearm watcher across session transitions (kunchenguid#1166)

(b29621b, the pi-signed runtime adapter, was already present via the
earlier sync merge 94ca122.)

One conflict, in docs/watcher-continuity.md's regression-coverage list:
our #8 rewrote the fm-watcher-lock and fm-claude-stop-autoarm lines while
upstream inserted a new fm-pi-watch-extension line immediately above them.
The two sides are additive, not contradictory - they describe different
layers - so both survive, each sentence kept adjacent to the suite it
describes.
@knowttl

knowttl commented Jul 28, 2026

Copy link
Copy Markdown
Owner Author

Two facts discovered after the PR body was generated.

1. Review finding approved, deliberately not fixed (upstream defect, carried verbatim)

The review step raised one warning in .pi/extensions/fm-primary-pi-watch.ts: upstream's 9ea1a1a moved the process-exit cleanup to module scope and dropped the process.off("exit", cleanupOnProcessExit) that session_shutdown previously performed. Because Pi reloads extensions with moduleCache: false, each /reload re-evaluates that line and leaves the prior generation's listener registered, eventually emitting MaxListenersExceededWarning.

The finding is real, but it was approved rather than fixed, on two grounds:

  • The file is byte-identical to upstream fa0d85d (git diff fa0d85d HEAD -- .pi/extensions/fm-primary-pi-watch.ts is empty), and this fork has never edited it - only prior sync merges touch it. Patching it here would introduce fork drift in a file the fork deliberately mirrors.
  • This PR is scoped sync-only; fixing it would turn a merge into a behavior change.

It is a candidate to raise upstream, tracked separately.

2. No CI checks ran on this PR - Actions is disabled on this fork

repos/knowttl/firstmate/actions/permissions reports enabled: false, and the fork has zero workflow runs. The pipeline's CI step therefore returned checks-passed vacuously: there were no checks to pass, not green checks. Treat the local verification in the body as the evidence for this change, not a green CI badge.

Relatedly, the pipeline's lint step reported ShellCheck missing (exit 127) and passed vacuously for the same reason. The lint evidence in the body comes from a separate run against the pinned ShellCheck 0.11.0 fetched for exact CI parity, which was clean.

@knowttl
knowttl merged commit 1ac0c5b into main Jul 28, 2026
@knowttl
knowttl deleted the fm/fm-upstream-sync-3 branch August 26, 2026 20:14
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.

2 participants