Skip to content

sync: merge upstream kunchenguid/firstmate main (11 commits) - #32

Merged
knowttl merged 12 commits into
mainfrom
fm/fm-upstream-sync-13
Aug 21, 2026
Merged

sync: merge upstream kunchenguid/firstmate main (11 commits)#32
knowttl merged 12 commits into
mainfrom
fm/fm-upstream-sync-13

Conversation

@knowttl

@knowttl knowttl commented Aug 21, 2026

Copy link
Copy Markdown
Owner

Merge the 11 commits this fork was behind kunchenguid/firstmate main (merge base b57c4d6), preserving the fork's own work.
Done as a merge commit, not a squash, so upstream ancestry stays intact for the next sync.
After the merge, git rev-list --count fm/fm-upstream-sync-13..upstream/main is 0.

Absorbed upstream commits

Conflict resolution (14 conflicts)

A. decision-hold -> captain-hold restructure (kunchenguid#2728): adopted upstream

The fork had no original work in the decision-hold files beyond its lavish->atelier rename (verified per file with git log b57c4d6..main).

  • Accepted upstream's deletion of docs/decision-hold-lifecycle.md and tests/fm-decision-hold-lifecycle.test.sh.
  • Took upstream's pointer stub for .agents/skills/decision-hold-lifecycle/SKILL.md.
  • Adopted upstream's new captain-hold files (bin/fm-captain-hold.sh, docs/captain-hold-lifecycle.md, the skill, and its test) wholesale.
  • Updated the fork's own codev-session skill to point at captain-hold-lifecycle directly rather than the deprecated pointer, so nothing dangles.
  • bin/fm-decision-hold.sh remains as upstream's one-release compatibility shim; the remaining decision-hold/fm-decision-hold.sh references (the shim, its docs, the compat code in fm-captain-hold.sh, and the tests exercising the shim) are all intentional.

B. lavish-axi -> atelier-axi fork policy (PR #25, df367d7): reapplied to every new upstream lavish reference

Renamed all new upstream lavish code introduced by this sync using df367d7's mapping: lavish-axi -> atelier-axi, Lavish -> Atelier, fm-procevent-lavish.sh -> the fork's fm-procevent-atelier.sh, .lavish/ -> .atelier/, source-id prefix lavish- -> atelier-, /bearings lavish -> /bearings atelier, procevent lavish -> procevent atelier.
This covers the interactive bearings board (bin/fm-bearings-board.sh, .agents/skills/bearings/assets/board-template.html, the bearings skill), the captain-hold docs/tests, and docs/scripts.md.
Zero lavish references remain in the tree.

atelier-axi compatibility verified against the installed atelier-axi 0.3.3 (not assumed): bare atelier-axi <file> opens/resumes a session, atelier-axi poll long-polls, and the browser runtime injects window.atelier.queuePrompt() with the data-atelier-question attribute (README lines 193-194 and dist/chrome-client.js).
So the board template's window.lavish -> window.atelier and data-lavish-question -> data-atelier-question renames were required for the board to function, not cosmetic.

C. Supervision infrastructure: kept BOTH sides' fixes

bin/fm-watch.sh, bin/fm-supervise-daemon.sh, docs/architecture.md, and the AGENTS.md state-dir line.
The fork's crew-provably-working wedge deferral (#7, #14, #18: .wedge-verified-* marker, FM_WEDGE_WORKING_ESCALATE_SECS, via wedge_escalation_deferred) and upstream's worktree-write deferral (kunchenguid#2524: .writing-* markers, via crew_worktree_written_since) are complementary, not competing - they answer different questions (is the busy/run signal still live vs is the filesystem being written).
At the threshold both now run in sequence.
wedge_timer_check's 5th arg is unified to <task> with an optional 6th nodefer; nodefer suppresses only the fork's crew re-verification on the busy-turn path (whose busy signal is already discredited past BUSY_TURN_MAX_SECS), while the worktree-write probe reads the filesystem and still runs there.
No fork fix was dropped, and upstream superseded none of them.
The away-mode daemon keeps the fork's provably-working deferral (.subsuper-wedge-verified-*) but has no worktree-write deferral of its own, and now names which human a declared/captain-held wait is on (kunchenguid#2748).
clear_pause_tracking adopted upstream's key-based signature (every caller passes a key) and still clears the fork's .wedge-verified marker.
The AGENTS.md state-dir line lists both .wedge-verified-* and .writing-* because the merged code writes both.

Other fork supervision fixes preserved and confirmed by tests: #8, #9, #12, #16, #21 (see the fork's original commits in b57c4d6..main).

D. Ordinary both-sides conflicts

bin/fm-brief.sh, docs/documentation-audiences.json, docs/verification/process-event-sources.md, .agents/skills/bearings/SKILL.md, .agents/skills/process-event-sources/SKILL.md, bin/fm-procevent-atelier.sh, tests/fm-watch-triage.test.sh.
Resolved surgically, keeping the fork's feature changes (brief-context-first #6, per-secondmate model/effort pin #20, the /firstmate-sync and codev-session skill entries, the atelier-axi policy) and upstream's changes in the same file, with the section-B rename applied to any lavish text pulled in.
The verification record keeps the fork's honest atelier-axi 0.3.3 Linux fact and updates its deleted test reference to tests/fm-captain-hold-lifecycle.test.sh; upstream's macOS/lavish-axi 0.1.45 dates were not adopted because they are not facts about this fork's tool.

Verification

  • bin/fm-lint.sh: passes (ShellCheck 0.11.0 pinned, actionlint 1.7.12 pinned, 3 workflow files valid).
  • bin/fm-doc-audience-check.sh: passes (72 surfaces, 264 local links valid).
  • Test suites (bin/fm-test-run.sh), 14 suites, 0 failures:
    • Conflict-touched: fm-watch-triage, fm-supervision-events, fm-daemon, fm-classify-decision-key, fm-brief, fm-procevent, fm-composer-lib, fm-turnend-guard, fm-bootstrap, fm-bearings-snapshot.
    • New upstream files: fm-captain-hold-lifecycle, fm-bearings-board, fm-claude-stop-autoarm, fm-watch-recovery-loop.
  • Not run here (require a live harness/credentials this environment lacks; byte-identical to upstream or exercising the auto-merged herdr change, so they run in CI): fm-herdr-submit-confirm-live-e2e, fm-claude-stop-autoarm-live-e2e.

CI note

This sync itself adds .github/workflows/no-mistakes-required.yml (kunchenguid#2710).
If that required check cannot be produced by a straight upstream-merge PR, this PR should not be force-merged around it - flag it for a decision.

kunchenguid and others added 12 commits August 19, 2026 13:16
Added guidelines for decision communication to the captain.
Clarify communication protocols with crewmates regarding task delegation and reporting.
* fix(herdr): confirm local steers that native agent-state misses

Herdr can leave agent_status idle for a landed Claude turn and can keep
queued Enter text visible while busy, so fm-send was reporting false
swallows. Confirm those cases through the shared queued-Enter verdict
and a cleared composer, and keep a genuine idle pending composer as
unconfirmed.

* no-mistakes(review): Stop Herdr Enter retries on unreadable composers

* no-mistakes(review): Reject queued delivery when all Herdr Enter sends fail

* no-mistakes(review): Prevent confirmation after failed Herdr Enter

* no-mistakes(review): Pace Herdr retries and clarify submit fallback

* no-mistakes(review): Align Herdr submit docs with idle fallback

* no-mistakes(document): Correct Herdr submit-confirmation documentation
* feat(bin): accept any-origin decision bindings with full-identity keys

An aggregation surface (the bearings board) carries captain answers for holds
across origins, but a binding was one-origin-per-source and the Lavish adapter
capped question keys at 64 chars while real full hold identities measure 69-81.

- fm-decision-hold.sh: bind <source-id> --any-origin records the (any) marker;
  binding prints it verbatim and answers accepts it, so the runner's feed seam
  carries an any-origin source with no runner change. In any-origin mode each
  key is a full hold identity <origin>-decision-<key>, split at its first
  -decision-; a key with no separator (merge/dispatch instructions) is skipped
  and feeds nothing, keeping non-decision answers out of the hold ledger by
  construction. Every existing close guard applies unchanged.
- fm-procevent-lavish.sh: raise the question-key cap 64 -> 128 so a full hold
  identity fits; the slug-shape security property is unchanged.
- tests: cross-origin closure through the real runner seam, an 81-char
  identity through the adapter, cap and shape refusals, routed-work skips,
  nonexistent-identity skips, and idempotent replay.

* feat(bearings): add the /bearings lavish interactive fleet board

/bearings lavish renders the bearings snapshot onto a shipped, reusable board
template and arms it as a Lavish process-event source, so the captain answers
Captain's Call items on the board and firstmate is woken by an ordinary check
wake - no conversational turn ever blocks on a poll.

- .agents/skills/bearings/assets/board-template.html: the shipped template
  (myfirstmate design system inlined, one fm-bearings-board.v1 JSON slot,
  fail-closed schema guard that renders an error card instead of an empty
  fleet). Per-invocation agent work is composing the payload only.
- bin/fm-bearings-board.sh: build/refresh owner - fail-closed payload
  validation, slot injection with a round-trip check and \u003c escaping,
  stable board path, any-origin bind ALWAYS before arm, arm-if-absent.
- bearings SKILL.md: the lavish invocation option, board composition rules,
  board-wake handling, and the captain-ruled merge-click authorization with
  its mandatory safeguards (PR resolved from the task's own meta record,
  wake-time green re-verification, never a red or changed PR, merges only
  through bin/fm-pr-merge.sh, chat echo with the full PR URL).
- process-event-sources SKILL.md: one-line board-wake routing trigger.
- tests: payload refusals, injection round-trip, bind-before-arm, idempotent
  re-arm, and template slot integrity.

Fleet pickup: homes receive this after merge plus a firstmate self-update;
landing timing is coordinated with the main firstmate.

* no-mistakes(review): Harden bearings board validation and wake handling

* no-mistakes(review): Require HTTPS for bearings board PR links

* no-mistakes(review): Fail closed and bound bearings board answers

* no-mistakes(review): Enforce UTF-8 byte limits for board answers

* no-mistakes(review): Serve bearings board before arming and reject empty actions

* no-mistakes(review): Prove bind-before-arm ordering through live answer consumption

* no-mistakes(document): Document bearings board and cross-origin answers
…enguid#2707)

* fix(bearings): always show decision options and a close/drop control

Freeform-only Captain's Call cards hid the option buttons the board was designed around, and there was no way to drop a stale hold without inventing an answer. Require selectable options, keep freeform as a supplement, and route the reserved __drop__ answer through decline so the hold leaves Captain's Call.

* no-mistakes(review): Fix drop closure and decision-only option validation

* no-mistakes(review): Preserve answerability for non-decision cards

* no-mistakes(document): Clarify decision drop documentation
Signature-only PRs can hide skipped review, test, or document steps. Fail unless no-mistakes >= 1.46.0 attests those three steps completed.
…#2728)

* feat(captain-hold): collapse the decisions concept into tasks held for the captain

A decision is no longer a separate type: it is an ordinary backlog task held
for the captain, identified by its task id. bin/fm-captain-hold.sh owns the
surviving behaviors - guarded hold creation, the recorded-answer close
(answer/answers with a release mode for captain-gated work), the source
bindings, and the investigation completion gate - and bin/fm-decision-hold.sh
becomes a one-release compatibility shim over it.

The fleet snapshot now parses hold-until and computes captain_actionable as
queued + captain-held + unblocked + due, independent of row kind, plus a
presentation-only deferred_marker for prose-deferred rows. Bearings renders
every due captain-held task in Captain's Call, date-deferred holds as dated
Charted Next gates, suppresses prose-deferred rows from default views with an
omitted disclosure, and excludes from Recently Landed anything that closed
while still held for the captain.

Legacy compatibility: pre-collapse <origin>-decision-<key> rows are already
plain task ids and keep working; short keys in recorded metadata, concrete
origin bindings, chat --resolve-key fallbacks, and old resolution records all
resolve in place.

* no-mistakes(review): Fix captain answer replay and body preservation

* no-mistakes(review): Fix captain hold idempotency and legacy replay

* no-mistakes(review): Validate card close modes and compatibility routing

* no-mistakes(review): Enforce release replay mode matching

* no-mistakes(review): Prevent duplicate decision cards and released replay mismatches

* no-mistakes(review): Preserve answer columns and legacy resolve replays

* no-mistakes(document): Document strict replay and legacy compatibility

* no-mistakes(lint): Quote done literals to satisfy ShellCheck

* no-mistakes: apply CI fixes

* fix(rebase): keep collapsed captain hold board semantics
…id#2733)

* fix(watch): announce recovery once per generation and keep successors supervising

A lost Pi/OpenCode handling handshake re-announced the same recovery
generation on every cycle and spent the successor's first ~55s blind, so
a real crew event could be ignored and then dropped. Record the
announcement in the durable marker, confirm the handshake before the
follow-up without swallowing failure, and enter the poll loop immediately.

* no-mistakes(review): Tighten recovery event timing regression

* no-mistakes(document): Document recovery-loop supervision guarantees
* fix(bin): signal a captain call resolved in the log but still held

A captain call has two records and closing one has never closed the
other: a `resolved [key=...]` line closes the status-log fold, while the
backlog task held for the captain closes only through
`fm-captain-hold.sh answer`. Answering on the status side alone left no
trace of the disagreement - the fold went quiet, the durable record kept
saying the captain owed an answer, and nothing warned. The defect was
never the separation; it was the silence.

Add `fm-captain-hold.sh diverged`, a read-only report of that
contradiction, and print it from `fm-wake-drain.sh` as a bounded RECORD
DIVERGENCE section beside OPEN DECISIONS on every drain. It flags one
condition: a task still open and still carrying the captain-hold
annotations whose key was closed on the status side by the resolve verb,
under the collapsed identity or the legacy derived one.

It closes nothing, ever. A captain call closed wrongly leaves review
entirely, which is worse than the noise, so both reconciliation
directions stay human-owned and the printed hint names both - a
resolution is not proof the captain ruled, since a call can dissolve on a
false premise or turn out to have been a question of fact.

Three states are deliberately not divergence: a `captain-held` close is
the verified transfer `complete` writes, a still-open keyed decision
belongs to the OPEN DECISIONS fold, and a captain call with no routed
work item is legitimate rather than incomplete, so routed work is no part
of the test.

`fm-classify-lib.sh` gains `status_key_closing_verb`, which reports how
the status side currently reads one key by replaying the existing
`_fm_decision_fold_line` rule rather than re-deriving it, so the two
closing verbs stay distinguishable in one place. The per-wake cost is one
`tasks-axi list`, one key scan per status log, and the precise per-key
fold only for a key that already names a still-open task; the call is
hard-bounded so a slow backlog tool can never delay wake presentation.

* fix(document): Correct divergence lifecycle documentation

* fix(document): Neutralize divergence lifecycle prose
…escalation while a worktree is written (kunchenguid#2524)

* fix(watch): re-arm supervision after an abandoned auto-arm claim

A Claude auto-arm cycle that armed, delivered one rewake, and exited left
its single-flight lock behind. Both Stop-event participants then deferred
to that lock forever, because its recorded pid was still live: the
turn-end guard read it as recovery under way and allowed the stop, and the
next Stop firing treated it as another owner and declined to arm. On
2026-08-14 a home with two tasks in flight lost supervision for about 40
minutes with no watcher process and no watcher lock, its beacon frozen at
the one delivery, and both crewmates' finished reports sat in the durable
queue until an operator drained it by hand.

Abandonment is now proven from the epoch ledger instead of inferred from
pid liveness. A lock whose holder pid matches the ledger's own owner_pid
while the recorded outcome is anything other than arming has already
finished its decision, so that claim is reclaimed under the lock's steal
mutex, stops counting as recovery ownership in the guard, and is cleared
by the guard's terminal check rather than deferred to. A failed clear
re-blocks instead of allowing a blind stop, and an arming entry stays in
flight however old it is, because its owner foregrounds the arm for the
whole watcher cycle.

Issue kunchenguid#2251's PR kunchenguid#2263 does not cover this failure. It is closed and
unmerged, lives entirely in bin/fm-watch-arm.sh, and retires the stalled
watcher and matching stale watcher lock of an arm that is currently
running. Here no arm and no watcher were running and no watcher lock
existed, so it has nothing to retire and the home stays blind.

tests/fm-claude-stop-autoarm.test.sh covers the reclaim, the still-arming
and unnamed-owner cases that must keep the gate closed, and the failed
clear. tests/fm-turnend-guard.test.sh covers the guard side of the same
boundary. Both fail without this change.

* fix(watch): defer a wedge escalation while the task worktree is written

The wedge detector had two inputs, rendered pane quietness and the run
step, and neither can see a crew that is writing source, then tests, then
documentation behind a static pane. On 2026-08-14 one crewmate produced
eight consecutive possible-wedge escalations in a single afternoon, three
of them demanding deep inspection, while it was demonstrably working and
then committed. Every one of them cost a supervision turn to disprove by
hand.

Add write activity inside the crew's own recorded worktree as a third
liveness input. crew_worktree_written_since compares the worktree against
the caller's existing idle-window timer file, so -newer needs no clock
arithmetic, no temp file, and no portable mtime write. The probe runs only
inside the branch that was about to escalate, which bounds it to one
pruned, depth-bounded walk per window per FM_STALE_ESCALATE_SECS and
leaves the per-poll stale sweep exactly as cheap as before.

Positive evidence defers rather than cancels. The idle timer restarts so
the next window probes again, the escalation counter is neither advanced
nor reset so a later genuine wedge keeps the demand-deep-inspection
history it earned, and a .writing-since marker ages the whole deferral
chain so the pane still re-surfaces once per FM_PAUSE_RESURFACE_SECS,
through the same throttle shape a declared pause already uses, labeled as
a recheck rather than a wedge. This can only reduce false positives: every
absence of evidence, including no recorded worktree, a torn-down worktree,
a missing anchor, and a failed walk, falls through to the unchanged
escalation schedule, so a crew that writes nothing still escalates on the
existing timetable.

What the signal cannot see, by design or by construction:

- CPU burn with no writes, such as a long compaction, is invisible. That
  case keeps the old behavior exactly.
- A commit-only phase writes only .git, which is pruned first so that
  firstmate's own read-only git commands against the worktree can never
  make the probe self-fulfilling.
- Writes under the pruned generated trees, or deeper than
  FM_WORKTREE_WRITE_MAXDEPTH, do not count.
- The probe cannot attribute a write to the crew, so a background build or
  another process touching the tree looks the same. The hourly re-surface
  is what bounds that, and a churny file cannot buy silence.
- The away-mode daemon's own escalation path is deliberately untouched.

tests/fm-watch-triage.test.sh covers the classifier including the .git
prune, both halves of the live case on one fixture (quiet plus writing
defers, quiet plus silent still escalates and counts), and the bounded
re-surface. All three fail without this change.

* no-mistakes(review): prove autoarm claims by identity; skip mate-home write probe

* no-mistakes(document): document away-mode wedge boundary and probe filesystem limit

* no-mistakes(document): qualify turn-end recovery condition for abandoned auto-arm claims

* fix(watch): keep a write deferral scoped to its own idle window

Two consistency gaps in the worktree write probe, both found while reviewing
the wedge-deferral change on this branch.

A write deferral is a bounded chain: its .writing-since marker ages the whole
chain so a churning worktree still re-surfaces once per resurface window. That
is only sound while the chain belongs to the current quiet stretch, so every
path that restarts the idle-window timer has to drop it too. Two did not: the
corrupt-timer repair in wedge_timer_check, and both first-sight branches for a
captain-relevant status. A chain left over from an earlier quiet stretch made
the first deferral of the new window re-surface immediately instead of after a
full fresh window.

FM_WORKTREE_WRITE_PRUNE is a skip list, so clearing it reads as "skip nothing"
and is the obvious way to widen the probe to the whole depth-bounded tree.
Instead an empty list reported no evidence at all, quietly costing the wedge
detector its third liveness input on a home that meant to widen the walk. An
empty list now widens the walk, and the header says so.

Neither change alters when a stall that writes nothing escalates.

Regressions in tests/fm-watch-triage.test.sh cover all three paths and each
one fails on the pre-fix code.

* no-mistakes(review): honor an empty write-prune, bound the probe, share window_key

* no-mistakes(document): align probe knob count and guard regression-coverage ownership

* no-mistakes(lint): silence deliberate single-quote SC2016 in write-prune env test
…lared pause (kunchenguid#2748)

* fix(bin): give a captain hold the same bounded pause cadence as a declared pause

Two supervisors read a finished task's last status line and disagreed about which
declarations mean an idle endpoint is expected. bin/fm-inactive-reconcile.sh
suppresses its inactive-outcome scan only on `captain-held`, while the away-mode
daemon's wedge path gated deferral on `paused` alone. Both read the LAST line, so
the two verbs are mutually exclusive and no finished task waiting on a person
could satisfy both at once. Marking 11 such tasks `captain-held:` silenced the
900s outcome scan and immediately produced five possible-wedge escalations in one
batch, because the 240s wedge detector no longer saw a pause verb.

fm-classify-lib.sh's status_is_paused_or_captain_held already owns the combined
question, and bin/fm-watch.sh's ordinary-crew wedge path already asked it. This
extends that same answer to the paths still asking the narrower one:

- bin/fm-supervise-daemon.sh, all six sites, which form one subsystem and have to
  move together. classify_stale returns the pause action, reconcile_pause_tracking
  and migrate_watcher_pause_markers record and migrate the marker, and
  housekeeping defers the wedge and then re-surfaces the recheck. Changing only
  the stale-persistence gate would defer the escalation while
  reconcile_pause_tracking recorded nothing, so the wedge marker would persist and
  the sweep would `continue` past it forever: quiet, but never re-surfacing.
- bin/fm-watch.sh's secondmate stale gate, whose downstream owner
  pause_state_class already treats both declarations identically.
- bin/fm-push-transition-lib.sh's absorb, where either declaration already names
  the human the transition would report and the wait is already durably recorded.

Quieting alone would be half a fix, so the bounded re-surface had to reach a hold
too. A hold has no current-state mapping, unlike `paused`, so authoritative crew
state reports it as unknown and pause_state_class received `none`. An ordinary
crew recovers pause classification from that state through confirmed agent death,
which proves no live decision gate is being silenced. A secondmate's endpoint
liveness is deliberately never read there, because an idle mate is healthy by
design, so that confirmation is unavailable by construction and cannot be
required: without recovering the classification for a mate, every caller silenced
a held mate outright and its hold would rot invisibly. That promotion is bounded
by the declared-wait guard at the top of the function, so it can only reclassify a
task that already declared a wait and shows no positive working evidence.

Two narrow `status_is_paused` calls are deliberately left alone.
bin/fm-crew-state.sh's map_log_state is a current-state reporting contract, not a
wedge path; reporting a hold as `paused` would erase the distinction
status_key_closing_verb and fm-captain-hold.sh depend on, where a `captain-held`
close is a verified durable transfer and a `resolved` close claims outright
settlement. fm-classify-lib.sh's call inside status_is_captain_relevant needs no
change because that function's own case list already returns non-relevant for
`captain-held`.

bin/fm-inactive-reconcile.sh keeps its `captain-held` suppression as it is. Its
guard exists because a finished task's crew state still reports done from a
higher-priority source than the log, and a declared pause needs no such guard: the
scan only reports done or failed, and nothing else reaches its record path.
Widening it would change a separate subsystem's reporting contract, which this
defect does not require.

Coverage extends the existing colocated patterns for these predicates and asserts
both halves. tests/fm-daemon.test.sh covers the classification, the wedge marker
converting to pause tracking with no escalation, the bounded re-surface with its
window reset, and the boundary case where an answered hold stops claiming the
cadence. tests/fm-watch-triage.test.sh covers a held secondmate re-surfacing on
the same bounded cadence without being labeled a wedge.
tests/fm-supervision-events.test.sh covers the absorbed push transition. Every one
of these fails on the pre-fix code except the answered-hold boundary case, which
is there to pin that the quieting was not widened too far.

The `paused:` workaround appended to those 11 tasks is live supervision state and
is untouched here. It can be retired once this lands.

* no-mistakes(review): name the captain in a held task's bounded recheck

* no-mistakes(document): extend declared-wait supervision docs to captain-held holds
Absorb the 11 commits the fork was behind upstream/main (merge base b57c4d6),
preserving the fork's own work. Merge, not squash, so upstream ancestry stays
intact for the next sync.

Absorbed upstream commits:
- 3d125ad fix(bin): give a captain hold the same bounded pause cadence as a declared pause (kunchenguid#2748)
- 4d2cb0c fix(bin): re-arm after an abandoned auto-arm claim and defer a wedge escalation while a worktree is written (kunchenguid#2524)
- d3342dc fix(bin): surface captain-call record divergence (kunchenguid#2744)
- 3f03533 fix: bound recovery announcements and preserve supervision (kunchenguid#2733)
- 99b21d8 feat: collapse decisions into tasks held for the captain (kunchenguid#2728)
- a0cec26 ci: require no-mistakes pipeline step attestation (kunchenguid#2710)
- b96dba1 fix(bearings): restore decision options and add close controls (kunchenguid#2707)
- 1cb900c feat(bearings): add interactive Lavish fleet board (kunchenguid#2659)
- 87681a4 fix(bin): reliably confirm herdr steer submission (kunchenguid#2647)
- 7b38a2f doc: Update task delegation and communication guidelines
- f242264 doc: Enhance communication guidelines for decision-making

Conflict resolution (14 conflicts):

A. decision-hold -> captain-hold restructure (kunchenguid#2728): adopted upstream wholesale.
   The fork had no original work in the decision-hold files beyond its
   lavish->atelier rename, verified with git log b57c4d6..main per file.
   - Accepted upstream's deletion of docs/decision-hold-lifecycle.md and
     tests/fm-decision-hold-lifecycle.test.sh (git rm).
   - Took upstream's pointer stub for .agents/skills/decision-hold-lifecycle/SKILL.md.
   - Adopted upstream's new captain-hold files (bin/fm-captain-hold.sh,
     docs/captain-hold-lifecycle.md, the skill, and its test) wholesale.
   - Updated the fork's own codev-session skill to point at captain-hold-lifecycle
     directly rather than the deprecated pointer, so nothing dangles.
   - bin/fm-decision-hold.sh remains as upstream's one-release compatibility shim.

B. lavish-axi -> atelier-axi fork policy (PR #25, df367d7): reapplied to every new
   upstream lavish reference this sync introduced, using df367d7's mapping.
   The new interactive bearings board, fm-bearings-board.sh, board-template.html,
   the captain-hold docs/tests, and docs were all renamed: lavish-axi -> atelier-axi,
   Lavish -> Atelier, fm-procevent-lavish.sh -> the fork's fm-procevent-atelier.sh,
   .lavish/ -> .atelier/, source-id prefix lavish- -> atelier-, /bearings lavish ->
   /bearings atelier, procevent lavish -> procevent atelier. Zero lavish references
   remain in the tree.
   Verified against the installed atelier-axi 0.3.3 that it backs the new board:
   bare `atelier-axi <file>` opens/resumes a session, `atelier-axi poll` long-polls,
   and the browser runtime injects `window.atelier.queuePrompt()` with
   `data-atelier-question` (README lines 193-194 and dist/chrome-client.js), so the
   board template's window.lavish/data-lavish-question renames were required, not
   cosmetic.

C. supervision infrastructure (bin/fm-watch.sh, bin/fm-supervise-daemon.sh,
   docs/architecture.md, AGENTS.md state-dir line): kept BOTH sides' fixes.
   The fork's crew-provably-working wedge deferral (#7, #14, #18, using the
   .wedge-verified-* marker and FM_WEDGE_WORKING_ESCALATE_SECS via
   wedge_escalation_deferred) and upstream's worktree-write deferral (kunchenguid#2524, using
   the .writing-* markers via crew_worktree_written_since) are complementary, not
   competing: they answer different questions (is the busy/run signal still live vs
   is the filesystem being written). At the threshold both now run in sequence.
   wedge_timer_check's 5th arg is unified to <task> with an optional 6th `nodefer`;
   `nodefer` suppresses ONLY the fork's crew re-verification on the busy-turn path
   (whose busy signal is already discredited past BUSY_TURN_MAX_SECS), while the
   worktree-write probe reads the filesystem and still runs there. No fork fix was
   dropped; upstream superseded none of them. The away-mode daemon keeps the fork's
   provably-working deferral (.subsuper-wedge-verified-*) but has no worktree-write
   deferral of its own, and now names which human a declared/captain-held wait is on
   (kunchenguid#2748). clear_pause_tracking adopted upstream's key-based signature (every caller
   passes a key) and still clears the fork's .wedge-verified marker. The AGENTS.md
   state-dir line lists both .wedge-verified-* and .writing-* because the merged code
   writes both.

D. Ordinary both-sides conflicts (bin/fm-brief.sh, docs/documentation-audiences.json,
   docs/verification/process-event-sources.md, .agents/skills/bearings/SKILL.md,
   .agents/skills/process-event-sources/SKILL.md, bin/fm-procevent-atelier.sh,
   tests/fm-watch-triage.test.sh): resolved surgically, keeping the fork's feature
   changes (brief-context-first #6, per-secondmate model/effort pin #20, the
   /firstmate-sync and codev-session skill entries, the atelier-axi policy) AND
   upstream's changes in the same file, with the section-B rename applied to any
   lavish text pulled in. The verification record keeps the fork's honest
   atelier-axi 0.3.3 Linux fact and updates its deleted test reference to
   tests/fm-captain-hold-lifecycle.test.sh; upstream's macOS/lavish-axi 0.1.45 dates
   were not adopted because they are not facts about this fork's tool.

Verification: bin/fm-lint.sh passes (shellcheck 0.11.0, actionlint 1.7.12, 3
workflow files valid). bin/fm-doc-audience-check.sh passes. Affected and new-upstream
suites pass (see PR body for the list).
@knowttl
knowttl merged commit 63524bd into main Aug 21, 2026
12 of 13 checks passed
@knowttl
knowttl deleted the fm/fm-upstream-sync-13 branch August 21, 2026 20:15
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