Skip to content

sync: merge upstream kunchenguid/firstmate main (8 commits) - #40

Merged
knowttl merged 9 commits into
mainfrom
fm/fm-upstream-sync-18
Aug 29, 2026
Merged

sync: merge upstream kunchenguid/firstmate main (8 commits)#40
knowttl merged 9 commits into
mainfrom
fm/fm-upstream-sync-18

Conversation

@knowttl

@knowttl knowttl commented Aug 29, 2026

Copy link
Copy Markdown
Owner

Summary

Absorbs the 8 commits the fork was behind, preserving the fork's 78 commits ahead.

Absorbed commits:

Conflict resolution

Merge base: 7ee0c192e9d664b022361bd4609303bebfc7de14. 58 files changed, ~5394 insertions. Only one real conflict.

bin/fm-crew-state.sh — confined to the header comment's one-line summary of the authoritative state source. Both sides independently added non-overlapping code that auto-merged cleanly on its own:

  • Fork's side: a CI-green override for a terminal failed run (PR fix(backends): scope zellij tabs by firstmate home kunchenguid/firstmate#252-class incident fix), plus an agent-presence-absent check (fm_backend_agent_confirmed_absent) ahead of the busy-state fallback.
  • Upstream's side: an attribution refactor pointing at bin/fm-nm-run-lib.sh as the single owner, plus a head-resolvable check and a pipeline-owned-active exemption.

Only the summary sentence needed reconciling, to name both facts: attribution is now owned by bin/fm-nm-run-lib.sh (upstream), and the fallback is the semantic busy-state contract, not the pane busy-signature (fork, since fork changed that fallback). Verified every referenced function (fm_nm_head_resolvable, fm_nm_run_is_pipeline_owned_active, fm_backend_agent_confirmed_absent, nm_ci_checks_state) exists and both sides' logic blocks coexist with no duplication.

tests/fm-public-followup.test.sh — the file the brief flagged as risky auto-merged cleanly. The fork's PR #39 clock-relative expiry fix (iso_offset_days, replacing the hardcoded 2026-08-28T01:12:00Z time-bomb) and upstream's independent PF_TEST_NOW/FMX_NOW_OVERRIDE clock-pinning are complementary, not conflicting. Verified the hardcoded date is absent from the merged file and both seed_repro_commitment and test_expiry_escalation_uses_now_override use the dynamic derivation.

No feature loss. Diffed every touched file in both directions from the merge base. Fork-only files (.agents/skills/codev-session/, .agents/skills/firstmate-sync/) are untouched and still present; every fork-added function in fm-crew-state.sh survives alongside every upstream-added one.

Verification

Captain directed: skip CI/CD, local sanity pass only.

  • bin/fm-lint.sh: clean — shellcheck 0.11.0 full extended analysis passed. actionlint isn't installed locally, so that sub-check was skipped (not a failure).
  • Targeted bin/fm-test-run.sh on the touched subsystems + new upstream test files (20 scripts): 4 failed, all verified pre-existing or environment-specific rather than merge regressions:
    • fm-test-run.test.sh--check-coverage's comm calls omit LC_ALL=C and this host's default locale isn't C; the same comm-without-LC_ALL pattern already exists at the merge base (pre-existing), and the guard passes cleanly under LC_ALL=C.
    • fm-calm-pi-extension.test.sh — one real-Pi e2e test (duplicate captain answer) fails against the locally installed Pi 0.84.3; upstream's own maintainer-verification evidence for this exact fix was gathered against Pi 0.84.1 and 0.84.4, not 0.84.3.
    • fm-on.test.sh — the new upstream PATH-portability test's OPTIONAL_DIRS list doesn't enumerate this host's Nix Home Manager store path; this file was untouched by any conflict.
    • fm-session-start.test.sh — the MISSING-diagnostic test assumes no real node on the minimal test PATH, but this host has a system /usr/bin/node; this test predates the merge base unchanged.

Test plan

  • bin/fm-lint.sh clean
  • Targeted subsystem tests run, all failures traced to root cause and confirmed pre-existing/environment-specific
  • Captain review and merge (CI skipped per captain instruction)

kunchenguid and others added 9 commits August 27, 2026 13:12
* fix(pi): stop reporting one merge to the captain twice

The supervision branch's captain-outcome note told main, unconditionally,
that the note "is not your own earlier output" and to relay it now. When
main had already reported the same event, that assertion was false and the
order turned the correct response - saying nothing new - into a mechanical
re-report, so the captain saw one merge reported twice in 16 seconds.

Two independent changes, both needed:

- The relay instruction is now conditional. It still names itself as a
  supervision outcome so main cannot mistake it for its own earlier answer
  (the silent loss that instruction exists to prevent), and it now lets
  main stay quiet about an outcome it has already given the captain.

- The merge case is closed at its source rather than left to that judgment.
  One merge reaches a home on two independent paths by design - main's own
  permanently main-owned merge poll, and the branch's task-local status
  wake - and main's captain-facing text only reaches the branch's mirror at
  main's turn end, so the branch can escalate before it could possibly see
  the captain was already told. bin/fm-pr-merge-notified.sh answers that
  question from bin/fm-pr-lib.sh's canonical merge-notification marker, so
  the answer holds regardless of mirror timing. A captain outcome naming an
  already-published merge is delivered as the ordinary rendered note
  instead of opening a follow-up turn: still appended, still visible, still
  recorded with the verdict the branch decided, minus the wasted turn.

Any error, timeout, or unreadable state relays the outcome. A duplicate
announces itself; a lost outcome does not.

Regression coverage drives the real delivery path in both directions: a new
outcome must still reach the captain in exactly one follow-up turn even
beside an unrelated published merge, and an already-published merge must
open no second turn while a different PR in the same task still does. The
merge path's real producer and this new consumer are exercised end to end
in tests/fm-pr-merge.test.sh.

Pi-only by construction: the delivery path lives in .pi/extensions, so no
other harness loads it, and the new script only reads existing markers.

* no-mistakes(review): Document accepted latest-marker suppression residual

* no-mistakes(review): Recheck ownership before merge outcome delivery

* no-mistakes(document): Document merge-outcome suppression exception

* refactor(pi): drop the source-level merge suppression, keep the envelope fix

The captain reviewed this branch and judged the source-level duplicate
suppression overly complicated for the problem it solved, and asked for
the change to be reduced to the envelope wording alone.

Remove the mergeIntoMain downgrade path, bin/fm-pr-merge-notified.sh, and
every test and document that existed only for it. What remains is the
conditional captain-outcome instruction: main is told to stay quiet about
an outcome it has already reported and to relay anything else, which
covers the duplicate without a second mechanism.

The silent-loss protection is untouched - the note is still typed,
self-describing, and delivered as one invisible follow-up turn - and the
behavioral tests still assert that, now requiring both halves of the
conditional instruction.

* no-mistakes(ci): Clarified in code comments and owned documentation that this is intentionally an M1-only, model-facing conditional relay fix—not source-level suppression—addressing Greptile’s mistaken scope expectation without changing runtime behavior. Net diff remains 3 files and 27 insertions. Verified with fm-pi-branch-extension tests, fm-lint, doc audience check, and git diff --check; all passed

* no-mistakes(ci): Strengthened the runtime delivery test to verify the captain outcome retains its required self-description and outcome text. Verified with `bash tests/fm-pi-branch-extension.test.sh`, `bin/fm-lint.sh`, `bin/fm-doc-audience-check.sh`, and `git diff --check`; all passed. The outer pipeline can now commit and attest the new head
* fix(bin): bind the live pipeline-owned run instead of a superseded failed row

fm-crew-state.sh bound a superseded FAILED no-mistakes run to a task instead
of the LIVE replacement run: the live run's pipeline-owned lane head is not a
git object in the task worktree, so head-equality attribution rejected it and
the coarse runs-list fallback silently continued past the RUNNING row onto an
older failed row whose head equalled the stale worktree HEAD. The home summary
then flipped invalid and Bearings hid the home's live work (F10).

Attribution precedence now follows the daemon's own identity:
- An ACTIVE run for the task's branch binds without head equality while
  branch_sync.state is pipeline_owned (fm_nm_run_is_pipeline_owned_active);
  the pipeline owning the branch is itself the attribution.
- A genuinely failed run with no later run on the branch still reports failed
  through the unchanged head-equality path - real failures are not hidden.
- In the coarse runs scan, an unresolvable head is unknown attribution and
  stops the scan (fm_nm_head_resolvable) instead of falling through to an
  older row; a resolvable-but-mismatched head keeps the historical
  reused-branch skip.

The exemption never applies to a terminal run and requires pipeline_owned
specifically, both pinned by negative-control tests. Fixture shape verified
against the live incident run's real axi status output.

* no-mistakes(document): Updated run-attribution documentation ownership
…unchenguid#3211)

* fix(pi): surface requested supervision outcomes

* no-mistakes(review): Mirror in-flight captain requests before branch dispatch

* no-mistakes(review): Exercise real branch ownership and main outcome access

* no-mistakes(review): Preserve request tails and align verdict guidance

* no-mistakes(review): Preserve complete current captain requests

* no-mistakes(review): Require visible requested outcomes and realistic classification

* no-mistakes(document): Align supervision outcome documentation

* no-mistakes(ci): Fixed Greptile’s runtime-ordering finding. The extension now stages Pi’s authoritative `before_agent_start` prompt before SessionManager persistence and suppresses the later duplicate entry. Updated docs and behavioral regression to reproduce real Pi ordering and verify each prompt is mirrored exactly once. Passed branch-extension tests, supervision tests, strict Pi typecheck, full lint, and diff checks

* no-mistakes(review): Use canonical operational input classification

* no-mistakes(review): Filter legacy operational inputs canonically

* no-mistakes(document): Clarify captain request mirroring boundary

* no-mistakes(ci): Fixed the CI time-boundary failure in tests/fm-public-followup.test.sh by pinning its clock, including context-registry setup. This prevents follow-up fixtures from expiring based on wall time. Verified the full regression suite passes, project-owned lint passes, and git diff checks are clean

* no-mistakes(document): Clarify captain-visible supervision outcome documentation
…#3210)

* feat(bin): per-home remote transport lanes with cancellation, bounded send, and closed stdin

All remote commands for every home on one host used to serialize through one
single-job-at-a-time worker on one shared queue: a timed-out caller abandoned a
staged job that kept running, retries convoyed behind it, fm-send's remote leg
had no time bound, and staging captured the caller's stdin to EOF so any
fm-on.sh caller with an open stdin wedged staging indefinitely.

- The worker now serves one lane per staged home: same-home jobs run strictly
  FIFO in a new staging-sequence order while different homes run concurrently,
  each lane as its own top-level worker process (a backgrounded subshell does
  not reliably reap dead children, so a zombie group leader kept a finished
  command's process group signalable). Long-poll preemption is lane-scoped.
- A caller that disconnects or times out cancels its job: the entrypoint marks
  the record on any post-staging exit and probes its parent so a dead ssh
  channel cancels without a signal; the worker skips cancelled queued jobs,
  terminates a running cancelled job's process group, and reaps the record.
- fm-send's remote leg is bounded by FM_SEND_REMOTE_BUDGET (default 30s) and a
  bound hit exits through the existing unconfirmed-delivery contract, which
  stays idempotent because the remote enqueue deduplicates.
- fm-on.sh defaults the remote command's stdin to /dev/null; the three payload
  callers pass the new --stdin flag. Abandoned .stage.* litter is age-reaped.
- The job execution deadline no longer loses up to a second to clock
  truncation.

* no-mistakes(review): Protect live stages and validate send budgets early

* no-mistakes(review): Preserve sequence lock ownership during stale recovery

* no-mistakes(review): Allocate job sequences at publication boundary

* no-mistakes(review): Bound remote keys and extend stale lock recovery

* no-mistakes(document): Document bounded remote transport behavior

* no-mistakes(lint): Suppress intentional deferred-expansion lint warning

* no-mistakes(ci): Fixed stale sequence-lock recovery by reconciling the counter against published job records before allocating the next sequence, preventing duplicate sequences and same-home FIFO violations. Added a behavioral regression test reproducing displacement after publication and verifying execution order. Passed fm-remote-transport-lanes.test.sh, fm-remote-job.test.sh, fm-lint.sh, and git diff --check

* no-mistakes(review): Use atomic sequence claims and lossless lane keys

* no-mistakes(review): Recover regressed sequence hints and rate-limit claim reaping

* no-mistakes(review): Restrict worker heartbeats to serving loop

* no-mistakes(review): Verify supervisor identity before lane recovery signals

* no-mistakes(review): Verify tracked lane and claim owner identities

* no-mistakes(document): Clarify remote lane and transport contracts

* no-mistakes(ci): Fixed the CI time-boundary failure by pinning fm-public-followup tests to a deterministic clock, including context-registry setup. Verified tests/fm-public-followup.test.sh, tests/fm-remote-transport-lanes.test.sh, shellcheck, and git diff --check

* no-mistakes(review): Preserve assigned lane ownership of queued jobs

* no-mistakes(review): Reserve homes owned by foreign queued lanes

* no-mistakes(review): Preserve completed results during crash recovery

* no-mistakes(review): Harden claim cleanup, expiry, and cancellation races

* no-mistakes(review): Verify process groups and reap abandoned results

* no-mistakes(review): Stop leaderless groups and reap cancelled publications

* no-mistakes(document): Correct remote transport lifecycle documentation

* no-mistakes(lint): Quote done state comparisons for ShellCheck
* fix(tests): make the changed-file map select per script and stabilize a budget flake

The changed-file map's bin/ fallback resolved a direct test reference to that
test's whole FAMILY. bin/fm-push-transition-lib.sh is named by exactly one
real-Herdr E2E, so a one-line change to it selected all 12 real-herdr-gated
scripts, including a 341s presentation E2E with no dependency on it.

Resolve direct test references per script, and keep resolving consumer bin/
scripts through the curated map so recorded family-level coupling survives.

Also fix a load-sensitive flake: the tool-update budget deadline is whole-second
granular, so a test budget of 1 left headroom anywhere in (0, 1] seconds and the
first budget check could already read as exhausted.

* feat(bin): make suite wall clock a result and let a family's concurrency be proven

--max-wall-ms fails a run whose wall clock exceeds the caller's budget, after
reporting the per-script results. A suite that stays green while outgrowing its
caller's invocation budget is the regression that got an agent killed mid-run
and retried invisibly, so duration has to be a result rather than a log note.

--pool on the isolation-proof harness runs the same concurrent proof over a
whole family, so 'is this family safe to parallelize?' is answered by a command
instead of a guess. Measured watcher-wake-lock and refused it: 3 of 18 scripts
fail under concurrency on wall-clock assertions about reaching the next poll.

* perf(bin): schedule the changed suite concurrently, longest first

The watcher-wake-lock family is proven concurrent-safe (two clean runs, 18
candidates, 0 failures at 4 workers; docs/fm-test-isolation-proof.md), so
--changed now schedules its proven-concurrent scripts with bounded parallelism
and runs any unproven remainder serially afterwards, never beside them.

Concurrent runs are ordered longest-hint-first. Workers are handed scripts in
order, so alphabetical order started the 193s fm-watch-triage last and stranded
it running alone: 395s wall against a 205s balanced four-worker sum.

An explicit --jobs keeps its strict refusal, so every CI lane is unchanged.

* fix(bin): bound a hung test instead of letting it hang the suite

tests/fm-calm-pi-extension.test.sh was observed running 17+ minutes against a
464ms recorded hint, and the suite had no per-script bound to stop it. An
unbounded suite is precisely what silently outruns a caller's invocation budget,
and --max-wall-ms is evaluated after the run so it cannot end one that never
finishes.

--per-script-timeout-secs terminates a script that outruns it and records exit
124, so the run still completes, accounts for the script, and fails. The
auto-concurrent --changed path applies 900s, far above the slowest real script
(the 341s Herdr presentation E2E), so it only ever converts a hang.

* no-mistakes(review): Enforce safe concurrency and descendant timeouts

* no-mistakes(review): Validate empty runs and isolation proof pools

* no-mistakes(review): Measure selection time in wall budget

* no-mistakes(review): Reap interrupted workers and bound finalization

* no-mistakes(review): Contain shutdown descendants and watchdog finalization

* no-mistakes(review): Honor remaining budget and close launch races

* no-mistakes(review): Restore timeout helper and simplify runner cleanup

* no-mistakes(review): Record isolation pool admission metadata

* no-mistakes(review): Bound Chrome reap and scope proof admission

* no-mistakes(review): Align proof scheduling and preserve budget summaries

* no-mistakes(review): Remove unreliable finalization watchdog

* no-mistakes(review): Freeze budget duration and enforce admission caps

* no-mistakes(document): Refresh test runner concurrency documentation

* no-mistakes(lint): Fix ShellCheck findings in test runner scripts

* no-mistakes(ci): Fixed Greptile’s concurrency-consent finding. `--changed` now remains serial by default; `--changed --jobs auto` explicitly opts into bounded concurrency and the automatic hang timeout. Updated documentation and added behavioral coverage proving serial default behavior, explicit concurrent scheduling, and refusal of `--jobs auto` outside `--changed`. Verified with `bash tests/fm-test-run.test.sh`, `bin/fm-lint.sh`, and `git diff --check`

* no-mistakes(review): Restore automatic changed-suite concurrency and timeout

* no-mistakes(review): Correct changed-suite contributor guidance

* no-mistakes(review): Reject gate-skipped isolation proofs

* no-mistakes(review): Correct automatic concurrency evidence

* no-mistakes(review): Isolate nested runner process groups

* no-mistakes(review): Remove unreliable signal cleanup machinery

* no-mistakes(test): Narrow changed-suite selection to executable contract owners

* no-mistakes(document): Document isolation proof skip and artifact semantics

* no-mistakes(ci): Fixed Greptile’s concurrency-consent finding. `--changed` now remains serial by default; bounded concurrency requires explicit `--jobs auto`. Updated behavioral coverage, contributor guidance, and isolation-proof commands accordingly. Verified with `tests/fm-test-run.test.sh`, `bin/fm-doc-audience-check.sh`, `bin/fm-lint.sh`, Bash syntax checks, and `git diff --check`; all passed

* no-mistakes(review): Restore plain changed-suite automatic concurrency

* no-mistakes(review): Record resolved changed-suite worker count

* fix(bin): keep a runner change selecting its whole curated family

A pipeline fix round narrowed the curated changed-file map so bin/fm-test-run.sh
and bin/fm-test-isolation-proof.sh selected only their own two contract tests,
and the documentation surfaces only the audience test. That cut this branch's
own changed selection from 33 scripts to 5.

The runner executes every pure-contract-unit script, so its contract test
passing proves its logic is right, not that the suite it drives still runs.
Narrowing it also makes any wall-clock claim about the changed suite trivially
true by not running the work.

Only the unmapped bin/* grep fallback resolves per script; curated mappings keep
their recorded family coupling.

* perf(bin): admit the pure-contract-unit family to bounded concurrency

A runner-file change selects pure-contract-unit, so that family decides the
changed suite's wall clock. With only watcher-wake-lock admitted, 14 of its 33
selected scripts fell to the serial tail and the selection measured 327.3s
against a 300s budget: the concurrent group was 19 scripts totalling 273.4s
while the tail alone was 215.7s.

bin/fm-test-isolation-proof.sh --pool pure-contract-unit --jobs 4 passes twice,
32 candidates, 0 failures, so the family is admitted on recorded evidence.

Full 33-script plain --changed: 327.3s -> 181.8s / 178.5s / 172.7s, 0 failures,
inside a 300000ms budget. Also states the per-script guard's derivation.

* no-mistakes(review): Align contract-unit concurrency cap with recorded proof

* no-mistakes(document): Record final changed-suite performance evidence

* fix(bin): keep an empty changed selection clean on stock macOS Bash

Under set -u, bash 3.2 treats "${arr[@]}" on an EMPTY array as an
unbound-variable error, while bash 4.4+ makes it a harmless no-op. The
concurrency work removed the early exit for an empty selection, so execution
fell through to the unguarded existence loop: on stock /bin/bash 3.2.57 a
contributor who changes only documentation and runs --changed got

  bin/fm-test-run.sh: line 1713: SCRIPTS[@]: unbound variable

with exit 1 and no summary, instead of a clean total=0 pass.

Restore the early exit, and guard every remaining array expansion reachable
with an empty selection. The reported duration is real elapsed invocation
time rather than a hardcoded zero, so a selection phase that outran
--max-wall-ms still fails.

Verified on this host with /bin/bash 3.2.57: exit 1 with the unbound-variable
error before, exit 0 with FM_TEST_SUMMARY total=0 after.

* no-mistakes(document): Document shell-bound changed-suite performance

---------

Co-authored-by: Kun Chen <kun-1@kunchenguid.com>
* feat(bin): publish per-home summary ledger

* no-mistakes(review): Bound and schedule home summary publication

* no-mistakes(review): Prove recurring watcher summary refresh cadence

* no-mistakes(review): Bound refresh workers and publish durable spawns

* no-mistakes(review): Fix atomic kill process-group coverage

* no-mistakes(review): Bound state initialization within refresh timeout

* no-mistakes(document): Document recurring bounded home-summary publication

* no-mistakes(review): Bound and log all best-effort refresh failures

* no-mistakes(review): Harden cadence and timeout regression coverage

* no-mistakes(document): Document home-summary runtime tuning

* no-mistakes(lint): Fix direct exit-code check in refresh test

* no-mistakes(ci): Fixed remote secondmate retirement recreating the deleted home: teardown now skips side-band summary refresh when its overridden state directory was removed. Verified with remote lifecycle E2E, teardown tests, home-summary tests, ShellCheck, and git diff checks

* no-mistakes(document): Clarify atomic home-summary publication guarantee
* fix(pi): gate first call on startup context

* no-mistakes(document): Correct Pi startup prerequisite verification date

* no-mistakes(review): Captain, fix startup process-group retirement after leader exit

* no-mistakes(review): Captain, release reload exit listeners on shutdown

* no-mistakes(review): Captain, complete startup exit lifecycle ownership

* no-mistakes(review): Captain, release empty startup process-group ownership promptly

* no-mistakes(review): Captain, supervise startup ownership and restore failure fallback

* no-mistakes(review): Captain, restore live Pi supervisor execution

* no-mistakes(document): docs: clarify Pi startup prerequisite delivery
* fix(pi): restore 0.84.4 adapter compatibility

* no-mistakes(review): Restore Pi collapsed and expanded outcome parity

* no-mistakes(review): Preserve Pi stock previews through capability probing

* no-mistakes(document): Document Pi 0.84.4 renderer compatibility
Absorbs the 8 commits the fork was behind, preserving the fork's ahead work:

  f66be0f fix(pi): restore Pi 0.84.4 renderer compatibility (kunchenguid#3261)
  52b59a1 fix(pi): gate first provider call on startup context (kunchenguid#3158)
  a390659 feat(bin): publish per-home summary ledgers (kunchenguid#3222)
  4207214 fix(bin): accelerate and bound changed test runs (kunchenguid#3250)
  1fd7ea2 feat(bin): add concurrent bounded remote transport lanes (kunchenguid#3210)
  c651b59 fix(pi): surface requested outcomes without replaying fleet events (kunchenguid#3211)
  bca584a fix(bin): prioritize active pipeline-owned crew runs (kunchenguid#3194)
  4f89f5b fix(pi): prevent duplicate captain outcome reports (kunchenguid#3184)

Conflict resolution (evidence: git log/diff against merge-base 7ee0c19
for both sides of every touched file):

- bin/fm-crew-state.sh: only real conflict, confined to the header comment's
  one-line summary of the state source. The fork's side and upstream's side
  each independently added non-overlapping code (fork: a CI-green override
  for a terminal failed run plus an agent-presence-absent check before the
  busy-state fallback; upstream: an attribution refactor pointing at
  bin/fm-nm-run-lib.sh plus a head-resolvable check and a pipeline-owned-active
  exemption), all of which merged cleanly on their own. Only the summary
  sentence needed reconciling to name both facts: attribution is now owned by
  bin/fm-nm-run-lib.sh (upstream), and the fallback is the semantic busy-state
  contract, not the pane busy-signature (fork). Verified every referenced
  function (fm_nm_head_resolvable, fm_nm_run_is_pipeline_owned_active,
  fm_backend_agent_confirmed_absent, nm_ci_checks_state) exists and both
  sides' logic blocks are present with no duplication.

- tests/fm-public-followup.test.sh: auto-merged cleanly. The fork's PR #39
  clock-relative expiry fix (iso_offset_days, replacing the hardcoded
  2026-08-28 date) and upstream's independent PF_TEST_NOW/FMX_NOW_OVERRIDE
  pinning are complementary, not conflicting - both are present and verified
  intact (grepped the merged file for the hardcoded date: absent; confirmed
  seed_repro_commitment and test_expiry_escalation_uses_now_override both use
  the dynamic derivation).

No feature loss: diffed every file in both directions from the merge base:
fork-only files (.agents/skills/codev-session, .agents/skills/firstmate-sync)
are untouched by the merge and still present; every fork-added function in
fm-crew-state.sh survives alongside every upstream-added one.

Verification (captain directed: skip CI, quick local pass only):
- bin/fm-lint.sh: clean (shellcheck 0.11.0 full extended analysis passed;
  actionlint not installed locally, so its check was skipped - not a failure).
- Targeted bin/fm-test-run.sh on the touched subsystems and new upstream test
  files (20 scripts): 4 failed, all verified pre-existing/environment-specific,
  not merge regressions:
    - fm-test-run.test.sh: --check-coverage's comm calls omit LC_ALL=C and
      this host's default locale isn't C; the same comm-without-LC_ALL pattern
      already existed at the merge base, and the guard passes cleanly under
      LC_ALL=C.
    - fm-calm-pi-extension.test.sh: one real-Pi e2e test (duplicate captain
      answer) fails against the locally installed Pi 0.84.3; upstream's own
      maintainer-verification evidence for this exact fix was gathered against
      0.84.1 and 0.84.4, not 0.84.3 - a version this environment doesn't have.
    - fm-on.test.sh: the new upstream PATH-portability test's OPTIONAL_DIRS
      list doesn't enumerate this host's Nix Home Manager store path; the file
      itself was untouched by any conflict.
    - fm-session-start.test.sh: the MISSING-diagnostic test assumes no real
      node in the minimal test PATH, but this host has a system /usr/bin/node;
      this test predates the merge base unchanged.
@knowttl
knowttl merged commit 6c7b7c3 into main Aug 29, 2026
10 of 12 checks passed
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.

4 participants