Skip to content

Upstream catch-up: merge kunchenguid/firstmate main (22 commits)#11

Merged
Bre77 merged 23 commits into
mainfrom
fm/fm-upstream-integrate-r9
Jul 11, 2026
Merged

Upstream catch-up: merge kunchenguid/firstmate main (22 commits)#11
Bre77 merged 23 commits into
mainfrom
fm/fm-upstream-integrate-r9

Conversation

@Bre77

@Bre77 Bre77 commented Jul 11, 2026

Copy link
Copy Markdown
Owner

Intent

Upstream integration: merge kunchenguid/firstmate main (22 commits) into the fork.

Conflict resolution (8 files)

All eight conflicts were resolved by integrating both sides; nothing from the fork was dropped.

  • .gitignore - both sides added a new config-ignore line (clickstack-webhook.env and wedge-alarm); kept both.
  • AGENTS.md - one conflict in section 11 (crewmate briefs): kept the fork's --fork-only and --amend brief flags, added upstream's new --herdr-lab flag, and adopted upstream's more complete secondmate usage line ({<project>...|--no-projects}). The fork's clickstack-alert-response skill pointer (section 13) and the --fork-only write-exception language (section 1) merged in cleanly with no conflict.
  • CONTRIBUTING.md - upstream's doc-consolidation commit (fix: complete brief help and consolidate documentation kunchenguid/firstmate#438) deleted the 54-row per-test inventory in favor of each test file's own header comment; took upstream's shorter version since the fork's copy was purely the pre-consolidation content with nothing fork-specific in it.
  • bin/fm-bootstrap.sh - one-line install_cmd conflict: fork had added python3 (for the ClickStack listener), upstream added git (fix: detect Git and centralize backend configuration kunchenguid/firstmate#445); merged to include both. The rest of the ClickStack webhook wiring (bootstrap detection, shim generation) merged in cleanly.
  • bin/fm-brief.sh - four conflicts, all fork additions (--fork-only, --amend) interleaved with upstream additions (--herdr-lab, --no-projects) at the same insertion points; combined the usage comment, variable declarations, and argument-parsing cases from both sides. The larger fourth conflict placed the fork's early-exit --amend brief block ahead of upstream's HERDR_SECTION setup, since the two are mutually exclusive branches that both fall through to the same later brief-assembly code.
  • bin/fm-watch.sh - two conflicts in the stale-detection loop. Upstream's paused-external-wait feature (feat(watcher): add paused external-wait supervision kunchenguid/firstmate#421) needed to run for secondmates too (not just skip them outright), so the fork's blanket secondmate -> continue was replaced with upstream's pause-aware guard, while keeping the fork's own semantic busy-state resolution (fm_backend_busy_state/stale_signal, a fork-local herdr dedup fix) as the hash source. Where upstream's new pause-transition logic called the older window_is_busy, it was adapted to call the fork's busy_state_is_busy "$bs" "$tail40" instead, since window_is_busy no longer exists post-refactor.
  • docs/scripts.md - same upstream doc-consolidation shortening as CONTRIBUTING.md; took upstream's table structure and re-added the fork-only fm-fork-deliver.sh row (with a --fork-only mention on the fm-brief.sh row) that upstream's table has no equivalent for. fm-pi-watch-extension.sh's row was dropped along with the script itself, which upstream deleted (superseded by .pi/extensions/fm-primary-pi-watch.ts).
  • tests/fm-backend.test.sh - upstream deliberately retired the byte-identical old-vs-new spawn conformance test as part of fix(spawn): make tmux window handling robust under non-default config kunchenguid/firstmate#134 (tmux window handling is now intentionally different, not a pure extraction), replacing it with an explanatory comment and pointing at its replacement coverage in fm-tangle-guard.test.sh and fm-backend-tmux-smoke.test.sh; took upstream's side since the fork's copy of this test pre-dates fix(spawn): make tmux window handling robust under non-default config kunchenguid/firstmate#134 and would fail against the now-merged spawn behavior.

Validation

  • shellcheck -x bin/*.sh bin/backends/*.sh tests/*.sh - clean.
  • Full tests/*.sh suite (62 files) - 60 pass; 2 pre-existing failures in fm-backlog-handoff.test.sh and fm-secondmate-lifecycle-e2e.test.sh are an environment gate (tasks-axi 0.1.2 installed, 0.2.2+ required by upstream's fix: delegate backlog handoffs to tasks-axi kunchenguid/firstmate#411 delegated-handoff path), confirmed identical on a clean origin/main checkout with no merge involved.
Full narrative / original brief

Catch the Bre77 fork up with its upstream (kunchenguid/firstmate) again. Twenty-two upstream commits landed after the previous catch-up (PR #10). Merged origin/main into fork/main-based branch fm/fm-upstream-integrate-r9, resolving 8 conflicting files by integrating both sides.

kunchenguid and others added 23 commits July 9, 2026 14:13
* docs(readme): reformat Quick Start and recommend Grok equally with Claude Code

* no-mistakes(review): Captain: align harness launch guidance

* no-mistakes(review): Captain, clarify Pi supervised launch

* no-mistakes(review): Captain, document Pi first-launch bridge

* feat(pi): track primary watcher extension for plain-pi launch

Move Pi's primary watcher bridge from a generated state/ file to a
tracked .pi/extensions/fm-primary-pi-watch.ts, matching how the turn-end
guard extension already works: self-hashing version, project-local
auto-discovery after one-time Pi trust. This drops the state/-generation
step and dual -e requirement from the happy path, so Pi's Quick Start
launch becomes plain 'pi', the same friction class as 'claude' and
'grok --trust'.

- bin/fm-pi-watch-extension.sh is removed; nothing generates the
  extension anymore since it is committed.
- fm-session-start.sh and fm-supervision-instructions.sh resolve the
  watcher extension path from FM_ROOT instead of state/, and the
  session-start diagnostic now points at restarting plain pi after
  trust, with -e as a documented fallback.
- fm-spawn.sh points Pi secondmate launches at the tracked extension
  path in the secondmate home instead of generating a state/ copy.
- README Quick Start Pi block is now just 'pi' plus a trust note.
- Tests, docs, and the harness-adapters skill updated to match.

* fix(pi): drop backticks from session-start diagnostic to satisfy shellcheck SC2016
* feat(supervision): add PreToolUse seatbelt against watcher-arm anti-patterns

Adds bin/fm-arm-pretool-check.sh, a shared PreToolUse-style checker that
denies a primary shell command backgrounding, piping, or bundling the
watcher arm/checkpoint, or force-killing the watcher process broadly -
the exact shapes that silently took Grok's supervision down. Wires it
into all five verified harnesses (grok, claude, codex, opencode, pi),
each validated empirically against the real harness.

Also fixes a grok 0.2.93 regression discovered during that validation: the
existing turnend-guard Stop hook's bare root variable broke grok's own
variable pre-substitution and silently no-op'd the hook.

* no-mistakes(review): Harden watcher arm validation

* no-mistakes(review): Harden arm guard metacharacter checks

* no-mistakes(review): Harden nested shell arm guard

* fix(lint): rewrite SC2015 guards in fm-arm-pretool-check.sh as if/then

A && B || C is not if-then-else; C can run when A is true. Replace both
occurrences of the quote-state early-continue with an explicit if/then.
* fix Pi primary supervision lifecycle

* no-mistakes(document): Synchronize Pi primary extension documentation
…#398)

* fix secondmate backlog guidance

* no-mistakes(review): Require reasons for captain backlog holds

* no-mistakes(test): Document secondmate handoff skill requirement

* fix secondmate teardown reminder

* no-mistakes(document): sync teardown reminder docs to work-items-only backlog contract
…kunchenguid#401)

* fix(backlog-handoff): move full item blocks including indented bodies

fm-backlog-handoff only moved the checklist header line, so multi-line
item bodies were left orphaned in the source backlog and never reached
the secondmate. Move the full block (header plus indented body lines)
atomically, treating body membership by indentation so lines like
## Intent stay with the item, and add regression coverage.

* no-mistakes(review): Captain: preserve EOF handoff terminators

* no-mistakes(review): treat blank lines inside item bodies as movable body

* no-mistakes(document): sync backlog-handoff docs with full-block move behavior
…kunchenguid#402)

* guard Herdr lab lifecycle in briefs

* no-mistakes(review): Fix Herdr lab helper and provisioning safety

* no-mistakes(review): Captain, harden Herdr lab lifecycle safety

* no-mistakes(review): fix Herdr lab test cleanup ordering and brief help range

* no-mistakes(review): reject leading options in Herdr lab run guard

* no-mistakes(review): strip leading non-alnum in Herdr lab name generator

* no-mistakes(document): document Herdr lab helper and --herdr-lab brief flag

* no-mistakes(lint): add shellcheck disable for deliberate SC2016 literals in fm-brief herdr-lab

* no-mistakes: apply CI fixes
…nchenguid#403)

* fix watcher arm command policy

* no-mistakes(review): Harden watcher command policy parsing

* no-mistakes(review): Captain: harden watcher policy parsing

* no-mistakes(review): harden watcher policy for expanded paths, direct-watch, and sound prefilter

* no-mistakes(review): close prefilter and classifier locale/ANSI-C watcher-path decode gaps

* no-mistakes(review): fail closed on loop-wrapped broad watcher kills

* no-mistakes(document): sync docs for watcher-arm command-position policy
* fix(agents-md): inject self-governance section into existing AGENTS.md

fm-ensure-agents-md.sh only appended the canonical "## Maintaining this
file" section on skeleton create or CLAUDE.md promotion, so an existing
AGENTS.md that lacked it exited unchanged and forced hand-copying the
wording during a rollout across existing projects. Call the already-
idempotent ensure_maintenance_section on the existing-AGENTS.md paths and
report whether the file changed; a re-run and an already-complete file stay
byte-identical.

Also fixes kunchenguid#389: refuse a case-variant real memory file (e.g. a lowercase
agents.md) instead of silently emitting a CLAUDE.md symlink whose uppercase
literal target dangles once the tree lands on a case-sensitive filesystem.

Tests extend tests/fm-ensure-agents-md.test.sh; skeleton-create and
CLAUDE.md-promotion regressions still pass. Docs updated to match.

* no-mistakes(review): Captain: preserve CRLF maintenance-section idempotency

* no-mistakes(review): Preserve CRLF during maintenance-section injection

* no-mistakes(review): Captain: harden dangling-symlink regression coverage

* no-mistakes(document): Document agent-memory injection outcomes
* feat(secondmate): support project-less homes via --no-projects

fm-brief.sh --secondmate and fm-home-seed.sh now accept an explicit
--no-projects signal to scaffold, seed, and register a secondmate home
whose subject is the firstmate repo itself (no clones). The signal is
mutually exclusive with a project list; omitting both still fails loudly
so an accidental omission is never a silent project-less seed. The
registry line renders an empty projects: field, which spawn and the
snapshot already tolerate. Docs updated in the secondmate-provisioning
skill and both script headers.

* no-mistakes(review): Captain: document project-less secondmate flow

* no-mistakes(review): Captain: refuse project-less reseeding of populated homes

* fix(seed): fail closed on unreadable project data

* no-mistakes(review): Captain: reject stale projectful charters

* no-mistakes(review): Captain: fail closed on unsafe project paths

* no-mistakes(review): Captain: validate project-less charter clone sections

* no-mistakes(document): Document project-less secondmate seeding
* wip(handoff): record verified delegation design + tasks-axi mv blocker

No production code changed yet. tasks-axi mv (v0.2.1) cannot atomically
move a blocked-by-linked item set across backlogs (deadlocks both orders,
no batch/--force), which fm-secondmate-lifecycle-e2e requires. Parked
pending a tasks-axi connected-set mv enhancement; note captures the
verified design, semantics, test/CI/doc changes, and resume checklist.

* refactor(handoff): delegate the item move to tasks-axi mv

fm-backlog-handoff.sh's two-pass awk was a second parser of the backlog
format and the source of the PR kunchenguid#401 body-orphaning drift. Delete it and
delegate the move to `tasks-axi mv <id>... --to <dest>` (v0.2.2 atomic
multi-id), the single owner of the format: a connected set (blocker plus
dependents) moves together with blocked-by preserved, item blocks stay
byte-exact, and destination section placement holds. The helper keeps only
the fleet-level validation tasks-axi cannot know - secondmate-home
resolution, the seeded-home safety checks, the In-flight refusal, and
idempotent per-key reporting - and is atomic: on any move failure nothing
moves.

Tests: fm-backlog-handoff.test.sh keeps PR kunchenguid#401's regression matrix but now
exercises the delegated path and skips cleanly when tasks-axi is absent; the
two whole-file fixtures move to tasks-axi's canonical whitespace. The
lifecycle-e2e and safety move-cases gain the same skip guard. CI installs
tasks-axi so the delegated path is exercised. Docs state that
config/backlog-backend=manual governs firstmate's own hand-editing, not this
validated helper, which delegates fleet-wide because bootstrap requires
tasks-axi on PATH.

Remove the now-redundant WIP design note.

* no-mistakes(review): Captain: harden atomic backlog handoffs

* no-mistakes(review): Captain: enforce queued-only backlog handoffs

* no-mistakes(review): Captain: harden handoff section parsing

* no-mistakes(document): Document delegated backlog handoffs

* no-mistakes(lint): Silence ShellCheck source diagnostics
* fix: gitignore the secondmate home marker

bin/fm-home-seed.sh writes an untracked .fm-secondmate-home marker into
every seeded secondmate home. A secondmate home is a worktree of the
firstmate repo, so any plain `git status --porcelain` dirtiness check
counted the untracked marker and the home read as dirty forever:
fleet-sync reported it STUCK and the local fast-forward convergence
sweeps risked leaving it stale on firstmate updates.

Add .fm-secondmate-home to the tracked .gitignore so the marker is
invisible to every dirtiness check uniformly, without weakening
fleet-sync's deliberate untracked-counting for project clones.

Convergence chicken-and-egg: existing homes predate the fix and it only
arrives by fast-forward. The already-present marker-tolerant ff-skip
(ignore_seed_marker=yes, used by the bootstrap sweep, /updatefirstmate,
and spawn pre-launch) advances such a home past the fix commit, after
which .gitignore takes over - no hand intervention.

Tests in tests/fm-secondmate-sync.test.sh cover a freshly seeded home
reading clean, an existing marker-only home converging then reading
clean, and a genuinely dirty home still skipping.

* no-mistakes(review): Captain: document standalone-clone update path

* no-mistakes(document): Document secondmate marker migration
* fix(composer): stop reading dead-shell prompts as empty agent composers

Consolidate composer empty/pending/unknown classification into one shared
owner, bin/fm-composer-lib.sh's fm_composer_classify_content, delegated to by
all four backend adapters (tmux via fm-tmux-lib.sh, herdr, orca, cmux). This
replaces four drifting copies of the glyph decision.

Safety fix: a bare shell prompt glyph (> $ % #) on an unstructured row is now
classified unknown (a dead shell, unsafe for injection), not empty. It is only
empty inside a bordered composer box (the harness's own prompt). Agent glyphs
❯ (claude) and › (codex) read empty either way. The away-mode injector
(inject_msg) now requires an affirmatively-empty composer, deferring on pending
or unknown, so an escalation can never be typed into (or executed by) a pane
whose agent exited to its login shell.

Regression coverage: new tests/fm-composer-lib.test.sh pins the shared owner;
per-backend dead-shell tests in fm-daemon (tmux + injector), orca, and the
existing herdr/cmux suites. shellcheck clean; herdr incident regressions stay
green.

* no-mistakes(review): Captain: harden composer safety checks

* no-mistakes(test): Stabilize Herdr prune safety setup

* no-mistakes(document): Document composer injection safety

* no-mistakes(lint): Clean composer safety lint

* no-mistakes: apply CI fixes
* feat(watcher): add paused/awaiting-external crew state

A crew (or firstmate steering it) can declare a deliberate wait on a known
external dependency with a paused: <reason> status. Both the always-on watcher
and the away-mode daemon absorb such an idle pane through shared fm-classify-lib.sh
vocabulary instead of tripping the possible-wedge stale escalation, and re-surface
it for a recheck only on a long bounded cadence (FM_PAUSE_RESURFACE_SECS) so a
forgotten pause cannot rot invisibly. fm-crew-state.sh reports state: paused
distinctly. A crew that goes idle without declaring a pause classifies exactly as
before. Docs and brief scaffold state lists updated; tests colocated.

* no-mistakes(review): Captain: fix paused-state transitions

* init

* no-mistakes(review): Captain: fix paused-state supervision transitions

* no-mistakes(review): Captain: fix paused supervision handoffs

* no-mistakes(review): Reconcile paused supervision markers

* no-mistakes(review): Captain: prioritize paused states over captain relevance

* no-mistakes(review): Captain: preserve paused-working wedge timer

* no-mistakes(review): Captain: honor configured pause verb in briefs

* no-mistakes(test): Captain: fix AFK paused watcher handoff

* no-mistakes(document): Document declared external waits

* no-mistakes(lint): Clean paused-state lint

---------

Co-authored-by: fmtest <fmtest@example.invalid>
* fix(x-mode): make follow-up platform splitting immune to link ordering

A ~470-char Discord follow-up posted as a (1/2)(2/2) thread split at ~280
chars because fm-x-link only learned the platform from the inbox payload,
and the fmx-respond ack path can drain that inbox file before the task is
linked. A link recorded after cleanup silently lost the platform and the
splitter defaulted to the X 280-char budget.

Make platform resolution ordering-proof:

- fm-x-link now resolves the platform AUTHORITATIVELY by request_id via a
  new fmx_request_relay_context helper (POST /connector/request-context)
  when neither the inbox payload nor carry flags carry it. The request_id
  survives the inbox drain, so a post-cleanup link still learns the right
  split budget. Best-effort: no token/curl or a non-2xx relay degrades to
  the loud warning below rather than a silent X default.
- fm-x-link warns loudly when no platform source resolves, so the loss is
  never silent.
- The fmx-respond procedure now orders link-before-inbox-cleanup so the
  fast local path stays correct without a relay round-trip.

Colocated regression tests: a Discord follow-up >280 <2000 posts as ONE
message even when linked after inbox cleanup, and an unresolvable platform
warns loudly instead of splitting silently. docs/configuration.md documents
the request-context lookup.

The relay endpoint is the companion durable change (see done status); until
it ships, the link-before-cleanup reorder keeps the normal path correct.

* no-mistakes(document): Document X-mode platform recovery
* fix(composer): one ANSI-aware ghost owner covers claude dim + grok truecolor

Away-mode injection wedged all night on the primary claude-on-herdr pane:
the herdr composer classifier never stripped generic dim ghost text (only a
narrow codex bold-wrapped byte-pattern check), so claude's rotating
prompt-suggestion ghost - a bare "❯" then SGR-2 dim text, which herdr's ANSI
pane read preserves - read as real pending input and every escalation deferred
(6524 lifetime "pending input (non-empty composer)" defers; wedge 30623s).

Consolidate ghost extraction into one fleet-wide ANSI-aware owner,
fm_composer_strip_ghost (bin/fm-composer-lib.sh), that drops every
de-emphasised run - dim/faint (SGR 2: claude, codex) AND a dark/muted truecolor
foreground (grok's placeholder, luminance below FM_COMPOSER_GHOST_LUMA_MAX,
default 128, dark-theme assumption). Both ANSI-capable backends route through
it: fm_tmux_composer_state (fm_tmux_strip_ghost is now a thin adapter) and
fm_backend_herdr_composer_state. The herdr-only faint byte-pattern check is
removed and fm_backend_herdr_strip_ansi reduced to a thin adapter over the
shared fm_composer_strip_ansi. Bordered detection now reads the plain row so a
dark box border dropped with the ghost does not lose the composer shape.

This also closes the documented grok TRUECOLOR placeholder gap by the same
mechanism (harness-adapters skill note updated).

Empirical evidence (read-only live capture + isolated tmux, no herdr lifecycle)
and the incident write-up are in docs/herdr-backend.md; deterministic
regressions feed the exact captured bytes through the real classifiers
(tests/fm-backend-herdr.test.sh, tests/fm-composer-ghost.test.sh). Two prior
ghost-test fixtures that used a near-black 38;2;1;2;3 as "real" colored text
(never a realistic real-input color) are corrected to a bright 38;2;224;222;244,
preserving the truecolor payload-skip parser intent.

* no-mistakes(review): Preserve dark shell prompt safety

* no-mistakes(review): Harden erased shell prompt classification

* no-mistakes(document): Document shared composer ghost extraction

* no-mistakes(lint): Normalize tmux comment punctuation
…enguid#432)

* fix(session-start): isolate harness env markers in suite runner

Neutralize CLAUDECODE, PI_CODING_AGENT, and GROK_AGENT in
run_session_start so ambient interactive shells cannot override the
suite's fake ps harness (local-vs-CI split on the pi supervision case).

* no-mistakes(document): Correct Pi marker documentation
…nchenguid#435)

* fix(teardown): retry treehouse return on transient index.lock

Killed crew git ops can leave a short-lived worktree index.lock that
makes treehouse return fail. Retry on that error signature with a
bounded wait (env-overridable), never force-delete a live lock, and
only then fall back to the existing provably-stale cleanup path.

* no-mistakes(review): Harden teardown retry configuration

* no-mistakes(document): Document teardown index-lock retry behavior

* no-mistakes(lint): Fix empty shell variable assignments
* docs: de-feature the scripts.md and CONTRIBUTING test inventories

Slice 1 of the documentation redundancy cleanup wave (firstmate scope).

docs/scripts.md: every row is now one purpose clause; script headers
are the declared owner of behavior, flags, and contracts. Coverage
stays 61/61 scripts; bytes drop 19,922 -> 7,958.

CONTRIBUTING.md: the 54-row per-test inventory is gone; contributors
discover tests by listing tests/*.test.sh and reading each script's
own header, and gated tests print their own skip gates. The run
commands, symlink assertions, and watcher smoke line are unchanged.
Lines drop 135 -> 84 (18,797 -> 7,831 bytes).

Two facts that existed only as inventory rows moved into their
owners' headers first: fm-brief.sh's paused-vs-blocked scaffold
distinction and fm-session-start.sh's Pi extension-loaded check.

No instruction-surface or behavior change; AGENTS.md untouched.

* no-mistakes(review): Captain, fix brief help and Grok test discovery

* no-mistakes(review): Captain: document Grok lock-holder test coverage
* docs: consolidate universal backend contracts into configuration.md

Slice 2 of the documentation redundancy cleanup wave (firstmate scope).

docs/configuration.md is now the declared single owner of three
universal contracts, each with an explicit ownership sentence:
- the universal toolchain list (Toolchain), now also carrying the
  per-tool purpose clauses that previously lived only in the tmux guide;
- the task-selector vocabulary (Runtime backend);
- the tasks-axi compatibility definition (Backlog backend).

The five backend guides' prerequisites replace their verbatim
universal-requirements parentheticals (5 full copies) with a pointer
plus only backend-specific items; zellij/cmux selector restatements
and architecture.md's partial copy become pointers or are dropped;
CONTRIBUTING's compatibility sentence becomes a pointer; two
near-verbatim orca-bootstrap restatements (configuration.md Runtime
backend, orca guide) collapse into the Toolchain owner copy.

Backend-specific setup, behavior, target-string shapes, and every
empirical verification record are untouched. AGENTS.md untouched
(slice 3).

* docs: include git and GitHub auth in the toolchain owner list

The review flagged that the new universal-toolchain owner omitted git
and GitHub authentication while every backend guide now defers its
prerequisites here; bootstrap's NEEDS_GH_AUTH check makes them real
universal requirements.

* no-mistakes(review): Detect Git in bootstrap toolchain

* no-mistakes(document): Clarify GitHub CLI and centralize selector documentation
* feat(daemon): backend-independent active alert for the wedge alarm

When away-mode injection wedges past max-defer, inject_wedge_alarm only
actively signalled via the tmux status-line, which is skipped on non-tmux
backends. A wedged claude-on-herdr primary left only the passive
state/.subsuper-inject-wedged marker (2026-07-10 overnight incident).

Add a config-gated active alert (config/wedge-alarm, local/gitignored;
FM_WEDGE_ALARM_CHANNEL) that reaches the captain even when every pane and
its status-line is unreadable: an OS-level macOS notification (osascript),
a herdr notification, or a captain-supplied command. Default-on (auto) so
the alarm is never silent; each channel best-effort, degrading to the next
and never crashing the daemon loop. The tmux flash and durable marker stay.

The OS notifiers route through a single FM_WEDGE_ALARM_EXEC seam. When the
daemon is sourced (only tests do this; production execs it) the seam
defaults to "discard", and tests/wake-helpers.sh points it at a recorder,
so it is structurally impossible for any test to post a real notification.

Channels verified once manually on macOS 26.5.2 / herdr 0.7.3; see
docs/wedge-alarm.md.

* no-mistakes(review): Bound wedge alarm notifier execution

* no-mistakes(review): Captain: harden wedge alarm notifier safety

* no-mistakes(review): Captain: harden wedge alarm test notifier isolation

* no-mistakes(review): Captain: harden wedge alarm throttling

* no-mistakes(review): Redact wedge alarm directive logs

* no-mistakes(review): Harden wedge alarm notifier safety

* no-mistakes(review): Track notifier process groups through cleanup

* no-mistakes(document): Document wedge-alarm active alert behavior
* docs(agents): extract conditional AGENTS.md material to owned homes

Slice 3 of the documentation redundancy cleanup wave (firstmate scope):
the always-loaded instruction surface drops from 941 lines / 116,733
bytes (~29k tokens per session per fleet member) to 785 / 91,353
(~22.8k tokens), moving only audit-identified conditional and
situational material while preserving every load-bearing invariant at
its trigger point via the inline-stub pattern.

Moves, each to one declared owner plus an inline stub:
- section 3's bootstrap output-line handbook (~44 lines) -> new
  agent-only bootstrap-diagnostics skill, added to the section 13
  trigger index; the detect-consent-install rule and the
  do-not-dispatch gate stay inline as safety-critical.
- section 4's crew-dispatch JSON schema and field semantics ->
  docs/configuration.md 'Crew dispatch profiles' (pointer direction
  flipped); the intake procedure, precedence, backstop, and
  never-select-unverified rules stay inline.
- section 4's quota-balanced algorithm -> bin/fm-dispatch-select.sh
  header (now the declared owner; usage() converted to the dynamic
  header extraction pattern PR kunchenguid#438 established for fm-brief.sh).
- section 7's spawn resolution narrative and example sprawl ->
  bin/fm-spawn.sh header; the isolated-worktree assertion, refusal-is-
  a-blocker rule, and post-spawn duties stay inline.
- section 7's teardown landed-work mechanics -> bin/fm-teardown.sh
  header (section 1's containment pointer retargeted); the fork benign
  case and never-force rule stay inline.
- section 8's watcher classification narrative -> docs/architecture.md
  'Event-driven supervision' (already the owner); every operative rule
  (one live cycle, no turn ends blind, drain first, wake ladder,
  never-pkill, guard responses) stays inline.
- sections 3/4/6/7 secondmate sync, propagation, schema, and handoff
  restatements -> secondmate-provisioning skill, now the declared
  owner including the literal-file inheritance nuance.
- section 14's X-mode cadence mechanism -> docs/configuration.md
  'X mode (.env)', closing issue kunchenguid#363; activation semantics, the
  fmx-respond trigger, and the terminal-wake final-follow-up duty
  stay inline.

CLAUDE.md stays a symlink; no behavior or test change.

* no-mistakes(document): Centralize contract-owner documentation
…ate-r9

# Conflicts:
#	.gitignore
#	AGENTS.md
#	CONTRIBUTING.md
#	bin/fm-bootstrap.sh
#	bin/fm-brief.sh
#	bin/fm-watch.sh
#	docs/scripts.md
#	tests/fm-backend.test.sh

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 04182357da

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if (hasUnclassifiableProtectedExpansion(position.command, context.root)) unclassifiableProtected = true;
const commandName = basename(executable);
const args = position.words.slice(position.index + 1);
if (commandName === "pkill" && args.some((word) => /fm-watch/.test(word.value) || wordReferencesAny(word, nodeContext.watcherPatterns))) broadKill = true;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Block regex-obfuscated watcher kill patterns

When the model uses a regex pattern such as pkill -f '[f]m-watch' (a common form that avoids matching the pkill process itself), this check returns allow because it searches only for the literal substring fm-watch; pkill --help confirms -f uses the full process name to match a pattern, so the command still targets every fm-watch.sh watcher and bypasses the broad-kill seatbelt. Please either parse/deny regex patterns that can match fm-watch or fail closed for pkill patterns that are not a safe literal.

Useful? React with 👍 / 👎.

@Bre77 Bre77 merged commit 3cd6c69 into main Jul 11, 2026
3 of 4 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.

3 participants