feat: reconcile upstream runtime and fork behavior - #9
Merged
Conversation
…uid#821) * feat(herdr): correct all-home child presentation topology Inherit the presentation opt-in to secondmate homes, label new projected spaces with the approved corner format, insert each child under its owning parent under one session-scoped lock, and keep flat non-destructive fallback. * no-mistakes(review): Exclude secondmates from Herdr presentation projection * no-mistakes(review): Harden shared Herdr locks and ambiguous child ordering * no-mistakes(review): Use adjacency-only Herdr child ownership * no-mistakes(review): Reject foreign legacy projections safely * no-mistakes(review): Validate Herdr session sockets before projection * no-mistakes(test): Fix Herdr teardown fixture session socket metadata * fix(herdr): canonicalize presentation lock socket paths Always resolve the session socket parent directory so symlink parents such as /tmp -> /private/tmp cannot split the shared cross-home lock identity. Refuse relative socket paths. Clarify lock-unavailable warnings. * no-mistakes(test): Fix Bash-compatible GitLab merge request URL parsing * no-mistakes(document): Document all-home Herdr child topology * no-mistakes(lint): Quote fallback provenance string for ShellCheck
* fix(no-mistakes): drop full-suite local Test override Local no-mistakes Test is intent-targeted; CI Behavior keeps the broad tests/*.test.sh suite. Keep commands.lint on bin/fm-lint.sh and add a focused contract test so the override cannot silently return. * no-mistakes(lint): Make CI contract assertion ShellCheck-clean
* feat(test): add canonical timed suite runner and honest CI timeout Introduce bin/fm-test-run.sh as the single serial owner for selecting one script, a family, a conservative changed-file set, or the explicit complete suite, with per-script timing markers and a JSON artifact. Wire CI Behavior through the runner, raise the hang-tripwire timeout to 25 minutes, and document entry points without restoring a full-suite local no-mistakes Test command. * no-mistakes(review): Captain: fix changed selection and empty summaries * no-mistakes(review): Captain: fail closed on unmapped changed sources * no-mistakes(document): Document canonical timed test entry points
* fix: disclose main-home orphan and unstructured inventory gaps Main Bearings could report an empty fleet while structured in-flight rows lacked meta or current backlog rows were free-form. Emit main_inventory from the fleet snapshot, map it into Bearings omitted surfaces and a Charted Next gate, and keep meta as the only live Underway source. * no-mistakes(document): Document Bearings inventory-integrity projection * no-mistakes: apply CI fixes
* feat: add concurrent test isolation proof for Phase 2 Prove an audited portable candidate set passes under concurrent workers with private mode-0700 temp roots, without enabling production CI sharding or fm-test-run --jobs. * no-mistakes(review): Pin isolation proof to audited candidate manifest
* feat(secondmate): parent-owned guards for missed status reports Marked parent-to-secondmate requests now create a durable pending-reply expectation with a privacy-safe correlation id before delivery. Transport success never resolves it; only a correlated parent status or document pointer does. After a completed turn with no report, the parent sends one recovery repost and escalates once if that turn is also missed, without scraping the secondmate conversation or looping. * no-mistakes(review): Deduplicate wrong-home pending-reply sightings * no-mistakes(review): Harden pending-reply recovery and escalation guards * no-mistakes(review): Bound pending-reply backend polling * no-mistakes(review): Cache pending-reply status scans * no-mistakes(review): Protect undelivered pending-reply records from scans * no-mistakes(review): Close pending-reply delivery durability gaps * no-mistakes(review): Separate pending-reply transport outcomes * no-mistakes(review): Escalate stalled pending-reply deliveries once * no-mistakes(review): Resolve attempted deliveries from correlated reports * no-mistakes(review): Resolve late reports after delivery escalation * no-mistakes(document): Document pending-reply grace and ownership * no-mistakes(lint): Silence intentional pending-reply test fixture lint warnings
* feat: add required pinned Herdr CI lane Install exact Herdr 0.7.4 and Treehouse 2.0.1 with official assets and SHA-256 pins, run the real-herdr-gated family serially through fm-test-run with hard-fail on herdr-not-found, and keep portable Behavior free of claimed Herdr coverage. * no-mistakes(document): Consolidate real-Herdr CI documentation ownership * no-mistakes: apply CI fixes * no-mistakes: apply CI fixes * no-mistakes: apply CI fixes
…guid#841) * feat: shard portable CI tests after isolation proof Balance the Phase 2 proven-isolated set into two LPT portable parallel lanes from Phase 1 timing evidence, keep stateful work in a required portable serial lane, exclude real Herdr to its dedicated required lane, and prove complete inventory coverage with a deterministic guard. Add bounded local --jobs only for the proven set, per-lane timing plus aggregate artifacts, and reduce the interim portable hang tripwire now that the serial remainder owns the long wall-clock path. * no-mistakes(review): Captain, fix CI contracts and completion-order worker scheduling * no-mistakes(review): Captain, preserve stderr gate-skip detection in parallel tests * no-mistakes(document): Document portable sharding and timing aggregation * no-mistakes: apply CI fixes
) * feat: fence primary-session delegation outside the fleet A firstmate primary that delegates through Claude Code's built-in delegation tools creates work with no state/<id>.meta. Because fm-supervision-lib.sh counts *.meta and fm-turnend-guard.sh exits silently at zero, such work does not merely go unsupervised: it makes the whole guard stack structurally inert, and it dies with the primary session. On 2026-07-22 that cost two workers mid-flight and left supervision down for 73 minutes unnoticed. Layer 1, the primary fix: a permissions.deny list in .claude/settings.json removes the 18 delegation, scheduling, worktree, and task-tracking tools from the model's schema, so they are never offered. This is removal rather than interception, so there is no call to intercept and no fail-open path. The list is flat and in one file so its width stays reviewable; the captain owns that width. Layer 2, bin/fm-subagent-pretool-check.sh: a deny list is fail-open against tools that do not exist yet, and permissions.allow is a pre-approval list rather than an availability list, so there is no fail-closed allowlist to use instead. This backstop classifies the tool NAME by shape rather than against a fixed list, so a delegation tool that ships before the deny list is updated is still refused. It excludes mcp__* names and observe-or-stop operations, scopes itself to a genuine primary home via the shared fm_primary_scope_matches predicate so a crewmate's task worktree is unaffected, and offers one deliberate FM_ALLOW_SUBAGENT=1 escape hatch that must be set at launch. Verified live against Claude Code 2.1.217, including a deny-key A/B with a nonsense-name control, layer 2 denying an un-denied Workflow call, the same call allowed in a linked worktree, and the escape hatch. Corrects a prior finding: both Task and Agent work as deny keys, so both are pinned. Codex 0.144.1 verified to expose no delegation tool; grok, opencode, and pi are inspected and documented as not wired because those binaries are absent from this host and the repo requires live validation before trusting a harness hook. Evidence in docs/subagent-guard.md. * no-mistakes(review): Ship scoped Claude delegation guard * no-mistakes(test): Ship Claude delegation deny list * no-mistakes(document): Clarify PreToolUse guard ownership * no-mistakes(lint): Keep Claude deny list local
Reproduction: portable-parallel-2 completed successfully without tasks-axi while fm-decision-hold-lifecycle emitted a gate skip in 30 ms. The pre-shard lane installed tasks-axi and exercised the test fully. Installing tasks-axi is the smallest counterfactual and makes the representative shard execute the test with gate_skip=false in about 20 seconds. Both parallel jobs receive symmetric setup, while the exact 91-test inventory and coverage guard remain unchanged.
* feat: make dispatch profiles quota aware * no-mistakes(review): Fix quota window and Grok product scoping * no-mistakes(document): Document implicit quota-aware dispatch accurately
…guid#875) * fix: preserve mixed Bearings projections * no-mistakes(review): Enforce strict invalidity precedence for partial snapshots * no-mistakes(review): Enforce ownership for unknown child metadata * no-mistakes(document): Document partial structured Bearings projections * no-mistakes: apply CI fixes * no-mistakes: apply CI fixes
* Add session-local Pi calm mode * no-mistakes(review): Preserve Pi HTML exports during calm mode * no-mistakes(review): Preserve calm exports across submit bindings and share * no-mistakes(document): Document calm-mode feasibility across supported harnesses
* fix(pi): limit watcher arm tool to recovery * no-mistakes(review): Strengthen Pi live re-arm regression coverage * no-mistakes(document): Document Pi first-cycle and recovery-only watcher arming
* fix(pi): clean up Calm transcript rendering * no-mistakes(review): Captain, preserve Calm exports and classify Pi launch briefs * no-mistakes(review): Captain, eliminate Calm gaps and verify exported conversations * no-mistakes(review): Restore Calm rows received while active * no-mistakes(review): Preserve diagnostics during Calm restoration * no-mistakes(document): Clarify Calm transcript behavior and injection paths
* fix: execute every PR body compliance event * no-mistakes(document): Document independent PR compliance events
…#899) * fix: distinguish operational input in ahoy * no-mistakes(review): Handle legacy Ahoy operational boundaries * no-mistakes(review): Narrow legacy Ahoy boundaries with live regressions * no-mistakes(document): Document Ahoy operational marker ownership * no-mistakes(lint): Suppress intentional literal fixture lint warnings
…nguid#909) * fix: type canonical operational inputs * no-mistakes(document): Correct canonical operational-input documentation ownership
* fix: avoid generic secondmate acknowledgements * no-mistakes(document): Document sparse secondmate acknowledgement behavior * no-mistakes: apply CI fixes
* fix(pi): preserve calm presentation across sessions * no-mistakes(review): Fix Calm home fallback persistence * no-mistakes(document): Clarify Calm gapless and export contracts * no-mistakes: apply CI fixes
…nguid#932) * fix: retire merged PR polls after notification * no-mistakes(review): Decouple PR retirement recovery from template updates * no-mistakes(review): Recover pending PR retirements before poll migration * no-mistakes(document): Document merged PR poll retirement contracts
* Clarify intake evidence and overlap handling * no-mistakes(review): Align scout guard with intake classification * no-mistakes(document): Clarify scout documentation and intake ownership
* fix(pi): preserve operational follow-up semantics in Calm * no-mistakes(document): Correct Calm operational-row visibility documentation
* perf(lint): shrink shell source graph * no-mistakes(review): Ensure lint workers terminate fully on cancellation * no-mistakes(document): Repair stale lint documentation ownership
* fix(pi): remove Calm hidden-block gaps * no-mistakes(review): Validate Calm geometry against current viewport * no-mistakes(review): Synchronize Calm geometry checks with reload completion
* fix: escalate ask-user contract expansion * no-mistakes(document): Point project management to authority owner
* fix(pi): hide Calm operational user rows * no-mistakes(review): Narrow Calm operational input suppression * no-mistakes(review): Avoid Calm replay classifier subprocesses * no-mistakes(document): docs: point Pi verification to Calm owner
* fix(session-start): relaunch missing second mates * fix(test): detect completed parallel workers * no-mistakes(review): Isolate session-start recovery test cleanup * no-mistakes(review): Complete backend-safe secondmate session recovery * no-mistakes(review): Resolve Zellij task ownership before recovery * no-mistakes(review): Recover relocated Zellij ghost tabs safely * no-mistakes(review): Restore conservative Zellij recovery boundary * no-mistakes(review): Reject malformed tmux recovery targets * no-mistakes(document): Align secondmate recovery documentation * no-mistakes: apply CI fixes
…kunchenguid#1928) * feat(stow): cascade the internal /stow to every registered secondmate Invoked in a primary home, /stow now sweeps every registered secondmate after the primary's own required pass, enforcing the same startup-memory threshold in each home against that home's own allowance rather than a fleet total. bin/fm-stow-cascade.sh owns the mechanical inputs: it enumerates each registered secondmate exactly once from data/secondmates.md, reports that home's own budget accounting, and resolves how the sweep reaches it. A live agent sweeps its own home so its uncaptured session knowledge is captured too; a local home without one is curated in place; a remote home without one is accounted read-only and deferred, because there is no generic remote write path for a home's own memory files. Every host- crossing step and each home's accounting runs under one hard bound, so a slow or unreachable home reports an exception and the sweep continues. Nothing changes until /stow is invoked: no new notification, digest section, or background work. The public skills/stow skill is untouched. * no-mistakes(review): fix(stow): extend cascade --help range to include full exit-code contract
…nguid#1927) 29 fm-remote-job-worker.sh processes were found running at ppid 1, 1-2 days old, each still polling and appending to a log inside a no-mistakes gate worktree that had already been returned. Three things combined to make that possible: - The recorded worker.pid is the serving child, not the restart supervisor above it, so a teardown that stops that one pid only makes the supervisor respawn. The Linux start path also left the worker tree in the launching command's process group, so there was no group to signal instead. - Neither the serving loop nor the supervisor ever rechecked whether its configured FM_ROOT still existed, so a worker launched from a worktree outlived that worktree indefinitely. - The supervisor restarted a failing child with a fixed 0.1s delay and no bound, which is what grew the logs (~66MB/day measured). The Linux start path now puts the worker tree in its own process group, and fm_remote_job_stop_worker_tree signals that whole group - refusing any group whose leader is not itself a worker, so a worker from an older build or from launchd's own session is still stopped safely as a single process. The worker stops itself once its code root stops being a Firstmate checkout, confirmed across a grace window so an ordinary transient cannot stop a healthy worker. The supervisor backs off and gives up rather than restarting forever. bin/fm-remote-job-reap-orphans.sh is the belt-and-suspenders sweep for workers already orphaned that way, wired into fm-teardown.sh. Its reap condition is exactly "the code root named in the worker's own command line is gone", which is why the account's healthy LaunchAgent worker and every live remote secondmate worker are never candidates. The two suites that leaked these in the first place now stop the worker tree rather than the recorded pid alone.
…henguid#1925) * fix(bin): lint only the changed shard locally, full lint in CI Two ships hitting fm-lint.sh at once could spike CPU to 190% and load to 8.58 on a captain's Mac, even though each run finishes quickly. fm-lint.sh now defaults to linting only the canonical-set files changed since the merge-base with origin/main (including uncommitted edits) on an ordinary local branch, using plain local git with no network calls. It still lints the full canonical set in CI (GITHUB_ACTIONS=true or CI=true), on the main branch, or whenever no merge-base can be found, so CI coverage never depends on a local diff. Explicit paths keep bypassing this selection entirely. * no-mistakes: apply CI fixes
* feat(bin): add deterministic agent lifecycle control Separate firstmate's data plane from its control plane. bin/fm-send.sh is the data plane: conversational text, always routing-marked for a kind=secondmate target. That marking is right for a message and wrong for a lifecycle command - a marked "/quit" arrives as ordinary chat the agent reasons about instead of executing. bin/fm-control.sh is the control plane: allowlisted interrupt, exit, and transactional relaunch verbs addressed to an exact task id, with per-harness mechanics owned by the executable bin/fm-control-lib.sh rather than improvised in agent prose, and a verified postcondition for every action. There is no arbitrary-text and no raw-key entry point. relaunch runs as a transaction with a durable journal: it resolves the profile, proves the work it must preserve is recoverable, records the required progress note, stops the old agent, then delegates the launch to its single owner, bin/fm-spawn.sh --relaunch, which adopts the recorded endpoint and worktree instead of creating either. A refusal before the stop leaves the record and instructions byte-identical; a failure after it reports the concrete state rather than claiming an agent that is not running. Teardown and discard stay separate and explicit. exit and relaunch require a backend with a recovery-grade agent-state classifier, so zellij, orca, and cmux are refused rather than reported as successful blind. A remotely placed secondmate is refused by name, because its agent runs on a host where none of these postconditions can be read. * fix(control): resolve a recorded harness to its adapter before retiring wiring fm-spawn arms per-task harness wiring on prefixes, because a task launched from a raw command records that command's basename rather than the exact adapter name. The control plane's retirement tables are keyed by the exact adapter, so a task recorded as `grok-2` had its turn-end token, private registry entry, and worktree hook pointer armed and never retired - leaving a registry entry that outlived the agent that owned it. State the prefix rule once, in the capability owner, and resolve the recorded value through it before every table lookup. bin/fm-send.sh's composer-clear lookup reads the same owner instead of keeping its own copy of which adapters need one. * test(control): pin muse session-binding retirement across a harness switch * no-mistakes(review): Resolve prefixed harnesses across lifecycle control verbs * no-mistakes(review): Report interrupt delivery without fabricating cancellation state * no-mistakes(review): Clear disabled relaunch trace context atomically * no-mistakes(review): Clarify control interrupts and restore legacy send state * no-mistakes(review): Refuse ambiguous relaunches and report exit delivery * no-mistakes(review): Revalidate interrupts and accept interrupt-stopped exits * no-mistakes(review): Lock descendant tasks before forced recursive teardown * no-mistakes(document): Align lifecycle adapter documentation with control plane * no-mistakes: apply CI fixes * fix(bin): serialize fresh task publication with forced teardown Forced secondmate teardown enumerated a home's task set, locked what it found, then re-enumerated while removing. A fresh spawn takes only its own per-task lock, so a record published inside that window was invisible to the preflight and visible to the cleanup: it was destructively processed while never lifecycle-locked. Reproduced with real agents. A record published 0.249s after teardown began was removed, its window closed, and its worktree returned to the pool - while both commands reported success. A per-task lock cannot protect a task that does not exist yet. Add a per-home task-set lock guarding WHICH tasks a home has, as opposed to the metadata lock guarding one task's record. Teardown takes it per home, parent before child, before enumerating and holds it through cleanup. A fresh spawn takes it before its own per-task locks and holds it through publication; a relaunch is exempt, because it republishes an existing task already covered by that task's control lock. Either the spawn publishes first and the teardown's preflight covers it, or the teardown owns the set and the spawn refuses. Both directions fail closed, and both are pinned by tests that hold the lock rather than racing on timing. * no-mistakes(review): Serialize remote secondmate publication with forced teardown * no-mistakes(review): Preserve remote spawn routing and state initialization * no-mistakes(review): Serialize teardown when descendant state is absent * no-mistakes(review): Cover symlinked descendant state refusal * no-mistakes(document): Document task-set serialization safeguards * no-mistakes(lint): Isolate task-set lock path resolution * no-mistakes: apply CI fixes
* feat(stow): tiered decaying memory with captain-gated offload to local excluded skills Implement the captain-adopted /stow redesign from the v2 tiering report as amended by the adoption decision: - Per-entry trailing HTML-comment markers with three tiers named for their handling: pinned (no clock, no eviction), aging (stale after 30 days), perishable (stale after 7 days, mandatory checkable expiry condition). - File-scoped defaults (captain.md and captain-shared.md pinned, learnings.md aging) with a self-describing legend line per file header. - Reinforcement requires session evidence; re-reading memory never counts. - Archive-not-delete: stale and budget-evicted entries move with provenance to the never-injected data/memory-archive.md; prune always means the cold tier, and a stale unique fact is never deleted. - Captain-gated over-budget offload: staleness evaluated before scope, the sweep runs only when still over budget after decay and consolidation, proposals go through the receipt plus one durable captain-held backlog item, migration runs through the destination's normal path, and the memory entry leaves only once the destination is live. - Offload destination per the adoption decision: a user-owned skill under .agents/skills/<freeform-name>/ excluded via the local .git/info/exclude, with the hard rule that stow never creates or writes a tracked skill. - Five graduation moves, receipt verbs archived and proposed-offload, and the one-time non-destructive migration of unmarked legacy entries. The public skills/stow/SKILL.md mirrors the generic parts (markers, decay, archive exit, user-approved on-demand offload exit, migration) with no firstmate-specific paths. The load-bearing assumption that a git-excluded skill is still discovered was verified empirically against Claude Code 2.1.226 (direct .git/info/exclude scratch-repo test plus an in-repo ignored-probe test); the dated evidence is recorded in docs/verification/stow-memory.md. The graduation list's deletion move is deliberately narrowed to duplicates already preserved by a stronger owner, reconciling the v2 report's retained 'deletion of a stale entry' wording with its own prune-always-archives rule. * no-mistakes(review): Persist legacy migration grace across stow passes * no-mistakes(review): Enforce archival invariants and exempt default-pinned legacy entries * no-mistakes(review): Clarify offload scope, archive placement, and marker boundaries * no-mistakes(review): Enforce aging fallback and verify excluded skill loading * no-mistakes(review): Fix stow decay, pinned offload, and archival safeguards * no-mistakes(review): Preserve pinned entries, approvals, and archive provenance * no-mistakes(review): Restrict stow mutations to editable memory files * no-mistakes(review): Clarify skill destinations, collision checks, and migration legends * no-mistakes(review): Resolve exclude paths for linked worktrees * no-mistakes(review): Secure per-home excluded skill migration * no-mistakes(test): Require explicit tier markers on new stow entries * no-mistakes(test): Route missing shared legends to primary owner * no-mistakes(document): Align stow documentation with tiered memory * fix(stow): converge the pass on an over-budget home (dogfood D1-D3) The dogfood run against a copy of the real over-budget home showed the pass increasing the deficit from 624 to 1,107 estimated tokens and the relief ladder provably unable to reach budget. Three skill-text fixes: - D1: markers become single-token spellings (<!--a:DATE-->, <!--p:DATE-->, <!--P-->, <!--g-->), entries matching a pinned file default carry no marker, the per-file policy legend collapses to a one-line pointer naming the stow skill as the scheme owner, and marker/pointer bytes are explicitly counted content - roughly 76% less metadata cost on the dogfooded home's first installment. - D2: the eviction rung gains a convergence precondition - total the eligible pool first, and when archiving all of it cannot reach budget, skip eviction entirely, archive nothing for budget reasons, and report the exempt pinned floor as the concrete inability in the final step. - D3: budget eviction considers only dated aging entries; <!--g--> legacy-grace entries are ineligible until their grace cycle resolves, so eviction cannot cancel promised grace or invert against validation. Public skill mirrors the D1 marker/pointer changes; D2/D3 are internal because the public skill has no budget ladder. * no-mistakes(test): Enforce evidence-only reinforcement during stow migration * no-mistakes(document): Clarify stow receipt marker actions
* docs: add firstmate vision * no-mistakes(test): Classify VISION.md as public product documentation * no-mistakes(document): Restore approved one-file vision diff * no-mistakes: apply CI fixes
* fix(spawn): force regular Pi TUI for crews * no-mistakes(document): Documented Pi regular TUI launch mode
* fix(cmux): classify borderless Claude composer * no-mistakes(review): Normalize cmux NBSP prompts across locales * no-mistakes(document): Document cmux borderless Claude composer classification
…nchenguid#2091) The public installer-facing stow skill scoped its classify-then-replace discipline to TODO/BACKLOG items only, so findings routed to a memory file had no stated rule against a blind append or a wholesale overwrite. Step 6 now classifies every finding against the destination's current contents as new, duplicate, superseding, or obsolete, and states the considered replacement each classification implies. The outcomes follow the tiered-memory contract already in the file: an obsolete entry is refreshed, archived, or replaced in a way that preserves its fact, a duplicate folds into the entry that already carries it, and a superseded body worth keeping leaves through step 7's existing exits rather than a second recovery mechanism.
* fix(watcher): resurface durable work after downtime * no-mistakes(review): Make watcher rearm recovery durable and cursor-safe * no-mistakes(review): Persist safe recovery markers across migration lock recovery * no-mistakes(review): Retain stale lock when recovery marker publication fails * no-mistakes(review): Preserve delivery-gap recovery and quarantine malformed markers * no-mistakes(review): Serialize recovery consumption and report acknowledgment failures * no-mistakes(review): Centralize recovery publication before clearing watcher evidence * no-mistakes(review): Guarantee recovery evidence across queue and lock handoffs * no-mistakes(review): Publish recovery evidence before durable wake commits * no-mistakes(review): Replace recovery marker Perl dependency with Node * no-mistakes(review): Keep interrupted wakes durable until handling acknowledgment * no-mistakes(review): Add post-handling durable wake acknowledgements * no-mistakes(review): Enforce post-handling acknowledgement across recovery and AFK return * no-mistakes(review): Bind wake acknowledgements to recovery generations * no-mistakes(review): Align wake regressions with generation-bound acknowledgements * no-mistakes(document): Document durable re-arm recovery semantics * no-mistakes(lint): Resolve ShellCheck warnings in recovery and watcher tests * no-mistakes: apply CI fixes * test(watcher): assert post-handling wake replay * no-mistakes(review): Prevent successor loops and adopt legacy wake generations * no-mistakes(review): Rearm durable wakes without recursive successor recovery * no-mistakes(review): Align recovery tests with handling marker state * no-mistakes(review): Delay handling transition until successor launch is established * no-mistakes(review): Confirm wake handling only after successful prompt delivery * no-mistakes(review): Acknowledge AFK wakes only after evidence publication * no-mistakes(review): Prevent AFK wake loss before post-handling acknowledgement * no-mistakes(document): Document durable wake acknowledgement semantics * no-mistakes(lint): Suppress false positive for recovery action output * no-mistakes: apply CI fixes * no-mistakes: apply CI fixes
* ci: add Windows Herdr automation spike * ci: run Windows spike on its pull request * fix: wait for Windows Herdr command output * fix: run ANSI probe in pane shell * ci: keep Windows Herdr spike manually triggered * docs: clarify Windows Herdr spike verdict
* Add guided ahoy decision flow * no-mistakes(document): Document guided Ahoy decision flow
Integrates 145 upstream commits since the last sync (remote secondmates, process-event supervision, public-followup replies, trace context propagation, muse/pi-signed harness support, deterministic agent lifecycle control, and extensive documentation/test evolution) while preserving all fork-original behavior: hosted Codex seatbelt session-lock fallback, the Herdr broken-pipe capability-probe fix, the Claude launch session-identity sanitize, the metadata-publication abort-on-failure fix, and the review-diff branch-resolution behavior. Every conflict was resolved against current contracts and tests rather than a wholesale ours/theirs choice. Where upstream's implementation superseded a fork fix with an equivalent or stronger mechanism (the Herdr teardown preflight system, harness ancestry walking, session-lock ownership), upstream's version was adopted; where fork's fix had no upstream equivalent (Codex hosted-session fallback, the SIGPIPE fix, the Claude transcript sanitize), it was preserved on top of upstream's evolved surrounding code. Two real merge-time defects were found via targeted and full-suite test execution (not caught by textual diffing alone) and fixed: - bin/fm-teardown.sh was missing its entire Herdr presentation-journal retirement block, silently dropped during conflict resolution. Confirmed as a real regression via a live Herdr E2E test (passed on pristine upstream, failed consistently on the merged branch); root-caused via upstream's two-block journal-retirement structure and fixed by restoring the missing block verbatim from upstream. - bin/fm-herdr-lab.sh silently kept fork's stale pre-squash content instead of merging upstream's evolved fleet-state tripwire check, with no conflict markers raised. A harmless duplicate BRIEF_REAL assignment in fm-spawn.sh was also found and removed. Comprehensive repo-wide scans (content and line-count comparisons against upstream) found no further instances of either defect class. Validation: bin/fm-doc-audience-check.sh and bin/fm-lint.sh both pass clean. Every touched test file was run individually and passes, including all real Herdr E2E suites that exercise the fixed teardown path. Two pre-existing upstream-wide defects were found and confirmed reproducible on unmodified upstream/main (not fixed, out of scope for this reconciliation): a set -e/EXIT-trap interaction in fm-teardown.sh that silently swallows a missing-adapter preflight refusal's exit code, and a duplicate `ln -s fm-wake-lib.sh` in tests/fm-gotmp.test.sh's fixture that blocks that whole test file from running. tests/fm-calm-pi-extension.test.sh also has one pre-existing failing assertion, reproduced identically on unmodified upstream/main.
…cope reap() edit The pipeline's CI auto-fix rounds for serial-4 left tests/fm-pi-watch-extension.test.sh's hung-successor tests marginal against the production plugin's retry/retire timing under loaded CI runners, and separately made an unrequested change to tests/fm-watch-triage.test.sh's reap() helper unrelated to that finding. Revert the reap() edit (restores the original bare kill+wait) and bump the test-only FM_WATCH_ARM_RETIRE_TIMEOUT_MS override from 5000 to 8000 in the two hung-successor test functions (Pi and OpenCode variants). The production default (1000ms, used whenever the env var is unset) is untouched.
… edit The pipeline's CI auto-fix rounds for the recurring serial-2 timeout substituted an unrequested tests/fm-watch-triage.test.sh reap() change instead of the approved fix, twice. Revert that edit back to the original bare kill+wait, and apply the actually-approved correction: raise tests-portable-serial's timeout-minutes from 15 to 20 (observed shard durations run roughly 8-16 minutes, exceeding the old 15-minute cap under loaded runners) and correct the adjacent stale duration comment to match. No shard count, matrix composition, or test distribution changes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intent
Guarded fork reconciliation: merge kunchenguid/firstmate:main (upstream, 145 commits ahead) into rega10/firstmate:main (fork) via a normal history-preserving merge (git merge --no-ff), with both branch tips present as merge-commit parents. No rebase, no force-push, no reset, no squash. Every conflict resolved semantically, preserving intentional fork-original behavior unless upstream demonstrably superseded it, adopting upstream's evolution everywhere the fork had no deliberate divergence. Two genuine merge-produced regressions found via live E2E testing and fixed (missing Herdr presentation-journal-retirement block in bin/fm-teardown.sh; stale pre-merge content in bin/fm-herdr-lab.sh). Three pre-existing upstream-only defects found, confirmed reproducible on unmodified upstream/main, deliberately left unfixed as out of scope.
Captain-decided ask-user findings applied during validation: (1) hosted Codex lock-owner tokens (codex:) are now treated as opaque identities through the shared session-lock boundary consistently across bin/fm-trace-context-lib.sh, bin/fm-session-start.sh, bin/fm-sessionstart-run.sh, bin/fm-sessionstart-nudge.sh, bin/fm-startup-network.sh, bin/fm-bootstrap.sh, with two source-text-parsing tests replaced by observable behavioral coverage; (2) a fifth obsolete test file, tests/no-mistakes-required-workflow.test.sh, was deleted to match upstream commit a2d5f26's 'replace source assertions with behavioral coverage' refactor, consistent with four other files already removed for the same reason.
Four fork-original test files were found stale against contracts this merge correctly adopted from upstream (not code regressions), and fixed: tests/fm-claude-session-env.test.sh needed --mode/--yolo added to a ship-spawn invocation now required by fm-spawn.sh; tests/fm-install-herdr.test.sh and tests/fm-nm-test-contract.test.sh asserted a literal single-lane CI string superseded by upstream's stronger 4-way sharded portable-serial matrix and now parse the workflow YAML semantically instead (with a resulting shellcheck SC2016 false-positive suppressed per existing repo precedent); tests/fm-pi-watch-extension.test.sh's OpenCode lock-owner-race assertion was made deterministic (waits on the plugin's own coordinator promise instead of a fixed sleep), and its separate hung-successor-recovery flake was root-caused to a marginal test-only FM_WATCH_ARM_RETIRE_TIMEOUT_MS=5000 override against the production plugin's real retry/kill timing under loaded CI runners (production default 1000ms untouched) and fixed by raising it to 8000 in exactly the two named test functions, verified by a full local 30/30 pass under the repository's hard-bound timeout helper after a genuinely wedged watcher process from an earlier attempt was recovered by exact-PID termination.
Separately, Behavior portable serial 2 twice hit the CI job's 15-minute hard timeout (a recurring, upstream-inherited shard-timing-margin issue unrelated to this merge's content - none of the four shards run anywhere near upstream's own documented ~4.8min/shard estimate, observed range is roughly 8-16 minutes). The captain approved a minimal, scoped correction: raise .github/workflows/ci.yml's tests-portable-serial job timeout-minutes from 15 to 20 and correct its adjacent stale duration comment, with no shard-balancing, matrix, or test-distribution changes. The pipeline's own CI auto-fix agent twice substituted an unrelated, unauthorized change to tests/fm-watch-triage.test.sh's reap() helper instead of making that approved change, and the resulting validation response became genuinely wedged (quiet for over a day after logging CI success); this was resolved by aborting that stuck run and manually applying the exact approved two-file correction (verified via full diff review, clean shellcheck lint, and YAML validity check) now committed as 00bbd67.
This fresh run validates the current head under review/test/document/lint/push/pr/ci from scratch. The PR (#9) must target rega10/firstmate:main only, never kunchenguid/firstmate, and must not be merged without the captain's explicit authorization.
What Changed
Risk Assessment
Testing
Merge ancestry, typed CI configuration, hosted Codex lock handling, session startup/bootstrap, deferred network authorization, guarded Herdr lab lifecycle, and presentation-journal retirement were exercised end to end and passed after the focused lock-boundary fix; the broader teardown attempt encountered only the explicitly out-of-scope inherited upstream fixture defect.
Evidence: Hosted Codex lock CLI behavior
Evidence: Merge ancestry and typed CI contract
Evidence: Herdr presentation-journal teardown
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
🔧 **Review** - 1 issue found → auto-fixed ✅
bin/fm-session-lock-lib.sh:161- The shared lock reader does not reject an unterminated trailing fragment because Bashreadreturns nonzero at EOF even after consuming data. A lock containingcodex:thread\njunkis therefore accepted ascodex:thread, allowingfm_session_lock_owner_matchesto authorize deferred mutations although the lock record changed. After the second read, reject any captured fragment so malformed or torn records fail closed.🔧 Fix: Reject malformed trailing session-lock fragments
✅ Re-checked - no issues remain.
✅ **Test** - passed
✅ No issues found.
bin/fm-test-run.sh tests/fm-lock.test.sh tests/fm-trace-context-lib.test.sh tests/fm-sessionstart-nudge.test.sh tests/fm-startup-network.test.shbin/fm-test-run.sh tests/fm-session-start.test.sh tests/fm-bootstrap.test.shFocused execution oftest_herdr_projection_teardown_retires_journal_only_after_confirmed_close,test_herdr_projection_teardown_preserves_state_when_journal_removal_fails, andtest_herdr_projection_teardown_uses_single_absence_confirmationRealbin/fm-lock.sh statusCLI checks with a validcodex:<thread-id>owner and an interrupted trailing-fragment lock fileRuby typed YAML validation of the four-shard portable-serial matrix and 20-minute timeoutgit show --format='%H%n%P' --no-patch ed48aa1plusgit merge-base --is-ancestorfor both required merge parentsFulltests/fm-teardown.test.shattempt reproduced the inherited upstream missing-adapter fixture defect; merge-specific journal cases were then isolated and passed✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.