Skip to content

Merge main into feat/omp-adaptor (omp adapter) - CI verification - #1144

Open
Boxyboxy wants to merge 44 commits into
kunchenguid:mainfrom
Boxyboxy:feat/omp-adaptor
Open

Merge main into feat/omp-adaptor (omp adapter) - CI verification#1144
Boxyboxy wants to merge 44 commits into
kunchenguid:mainfrom
Boxyboxy:feat/omp-adaptor

Conversation

@Boxyboxy

Copy link
Copy Markdown

Draft for the clean-environment CI verdict only. NOT for merge (standing ruling: feat/omp-adaptor is not merged to main).

Contents: main merged into feat/omp-adaptor with all conflicts resolved, two real merge bugs fixed (session-lock lib anchored-regex matching for pi/omp; omp busy signature relocation), two pre-existing bash-3.2/hermeticity test fixes, and omp re-verified live on 17.1.5.

Expected: the 'Require no-mistakes' check fails by design (it requires a no-mistakes-originated PR body). Read the verdict from the CI workflow jobs - especially the stock macOS bash snapshot job.

Boxyboxy added 29 commits July 8, 2026 19:25
Add omp as a sixth verified harness adapter alongside claude, codex,
opencode, pi, and grok. omp is Pi-derived but distinct: it sets OMPCODE=1
(and CLAUDECODE=1, so detection must check OMPCODE first), runs as
process `omp`, needs --auto-approve for autonomy, uses --thinking for
effort, and exposes an active session_stop hook that forces a
continuation by returning {continue:true} - making omp a direct-blocking
turn-end harness (class with claude/codex).

- detection: fm-harness.sh prefers OMPCODE over CLAUDECODE, plus ancestry
- lock: fm-lock.sh HARNESS_RE recognizes ^omp$
- spawn: launch template (--auto-approve, -e turn-end signal ext),
  --model/--thinking flags (omit max), crewmate turn_end extension
- busy detection: the busy-only "esc" hint token in fm-watch.sh and
  fm-tmux-lib.sh (omp's Working... uses a unicode ellipsis)
- liveness: backends/tmux.sh classifies an omp pane as alive
- bootstrap: crew-dispatch verified()/effort_ok, secondmate-liveness set
- session-start: load-health check for the auto-discovered guard
- teardown: state/<id>.omp-ext.ts cleanup at both sites
- primary guard: tracked auto-discovered
  .omp/extensions/fm-primary-turnend-guard.ts (session_stop)
- supervision: docs/supervision-protocols/omp.md + repair_line arm
- knowledge: harness-adapters skill omp section + expanded new-adapter
  wiring checklist so future adapters are fully wired
- tests: new fm-omp-harness.test.sh + fm-lock.test.sh and coverage in
  bootstrap, teardown, turnend-guard, supervision, liveness; tests/lib.sh
  now clears ambient harness markers so detection is hermetic

Verified live on omp 16.3.12.
# Conflicts:
#	.agents/skills/harness-adapters/SKILL.md
#	README.md
#	bin/fm-spawn.sh
#	docs/turnend-guard.md
# Conflicts:
#	.agents/skills/harness-adapters/SKILL.md
#	AGENTS.md
#	README.md
#	docs/turnend-guard.md
#	tests/fm-teardown.test.sh
#	tests/lib.sh
Add omp as a sixth verified harness adapter alongside claude, codex,
opencode, pi, and grok. omp is Pi-derived but distinct: it sets OMPCODE=1
(and CLAUDECODE=1, so detection must check OMPCODE first), runs as
process `omp`, needs --auto-approve for autonomy, uses --thinking for
effort, and exposes an active session_stop hook that forces a
continuation by returning {continue:true} - making omp a direct-blocking
turn-end harness (class with claude/codex).

- detection: fm-harness.sh prefers OMPCODE over CLAUDECODE, plus ancestry
- lock: fm-lock.sh HARNESS_RE recognizes ^omp$
- spawn: launch template (--auto-approve, -e turn-end signal ext),
  --model/--thinking flags (omit max), crewmate turn_end extension
- busy detection: the busy-only "esc" hint token in fm-watch.sh and
  fm-tmux-lib.sh (omp's Working... uses a unicode ellipsis)
- liveness: backends/tmux.sh classifies an omp pane as alive
- bootstrap: crew-dispatch verified()/effort_ok, secondmate-liveness set
- session-start: load-health check for the auto-discovered guard
- teardown: state/<id>.omp-ext.ts cleanup at both sites
- primary guard: tracked auto-discovered
  .omp/extensions/fm-primary-turnend-guard.ts (session_stop)
- supervision: docs/supervision-protocols/omp.md + repair_line arm
- knowledge: harness-adapters skill omp section + expanded new-adapter
  wiring checklist so future adapters are fully wired
- tests: new fm-omp-harness.test.sh + fm-lock.test.sh and coverage in
  bootstrap, teardown, turnend-guard, supervision, liveness; tests/lib.sh
  now clears ambient harness markers so detection is hermetic

Verified live on omp 16.3.12.
… architecture guide

(cherry picked from commit ecdd6bb)
Combines the 24-commit omp (Oh My Pi) harness-adapter branch with the 50
newer commits on main. All 14 conflicts resolved additively: every omp
adapter surface is preserved and every main improvement is kept. omp
support is ported into structures main added after the branch diverged:
the fm-dispatch-select.sh verified-harness and effort tables, the
fm_backend_tmux_agent_state five-state recovery classifier, the
agent_state-based secondmate liveness sweep, the operational-input
launch-brief encoder in every omp launch template, and the reworked
fm-watch-arm successor contract in the omp supervision protocol.
… inside tmux)

Three pre-existing main failures surfaced by running the full suite on
macOS with system bash 3.2 from inside a tmux session; none were caused
by the omp merge:

- fm-brief.sh: assign the no-mistakes definition-of-done block with
  IFS= read -r -d '' instead of DOD=$(cat <<EOF ...). bash 3.2's
  command-substitution scanner cannot parse heredoc content holding an
  unbalanced quote character (the apostrophe in "firstmate's"), which
  made the whole script unparseable on macOS (kunchenguid#958).
- fm-spawn.sh: make a failed task-metadata publication explicitly fatal.
  bash 3.2 does not apply errexit to a redirection failure on a compound
  command, so an unwritable metadata path let the spawn report success.
- tests/fm-session-start.test.sh: clear TMUX for the tmux
  secondmate-recovery runner so the respawn under test resolves the
  static firstmate session instead of the host session the suite may be
  running inside.
Bring main's Kimi adapter, quota-aware agent-owned dispatch (drops
fm-dispatch-select.sh), Claude Stop asyncRewake auto-arm, shared
session-lock lib, per-harness tmux busy signatures, and the
guidance/verification doc split into the omp adapter branch.

Conflict resolution kept both sides: every verified-harness list now
carries kimi and omp; the omp and kimi adapter sections, launch cases,
teardown cleanup, and turn-end integrations coexist. omp's busy
signature moved into the per-harness FM_TMUX_OMP_BUSY_REGEX_DEFAULT and
its lock identity into fm-session-lock-lib.sh (FM_HARNESS_RE gains
^omp$).

Fixes exposed by the merge:
- fm-session-lock-lib.sh: match the command name and interpreter script
  basename separately instead of grepping the whole args string, so
  anchored short names (^pi$, ^omp$) resolve; restores pi/omp lock
  recognition that the lib refactor had regressed.
- fm-omp-harness.test.sh: assert omp's busy signature through the new
  per-harness matcher plus a behavioral fm_pane_is_busy check.
- docs/documentation-audiences.json: classify docs/supervision-protocols/omp.md.
…hine

- fm-session-start.test.sh: the concurrent-lock test used $BASHPID, which is
  unset on macOS system bash 3.2, so every acquirer got an empty harness pid
  and none won. Derive each subshell's own pid portably via
  $(exec sh -c 'echo $PPID').
- fm-spawn-batch.test.sh: run_spawn left FM_CONFIG_OVERRIDE='' so config
  resolved to the repo's own config/, letting a captain's live
  config/crew-dispatch.json trip the dispatch backstop. Point it at an
  isolated empty config dir so the hermetic test is independent of local files.
The merge moved omp's ⟦esc⟧ into the per-harness FM_TMUX_OMP_BUSY_REGEX_DEFAULT
only, but the harness-agnostic composer/submit path (fm_tmux_composer_row_state,
behind fm-send's submit acknowledgement and the away-mode composer read) uses
only FM_TMUX_BUSY_REGEX_DEFAULT. Without ⟦esc⟧ there, a steer to a busy omp pane
on the no-box fallback could false-report a swallowed Enter. ⟦esc⟧ is omp-unique
and unambiguous (unlike the claude/kimi spinners main deliberately scoped
per-harness), so it is safe in the shared default; restored in fm-tmux-lib.sh and
fm-watch.sh (kept byte-identical) and pinned by two omp-harness assertions.

Live smoke on omp 17.1.5 re-verified: SMOKE_OK print run, busy token ⟦esc⟧
present-busy/absent-idle, pane process name omp, launch flags, OMPCODE=1, and the
session_stop forced-continuation contract. Stamps updated accordingly.
@Boxyboxy
Boxyboxy marked this pull request as ready for review July 27, 2026 18:15
Brings the branch up to date with 34 commits of firstmate main and keeps
the omp (Oh My Pi) adapter working against main's current contracts.

Resolved 30 conflicts against stated intent rather than by side. Verified
harness enumerations became the union claude, codex, opencode, pi,
pi-signed, grok, kimi, omp: main's pi-signed and the branch's omp both
survive everywhere they appear.

The substantive resolution is busy state. Main replaced rendered-text busy
heuristics with the semantic lifecycle contract in bin/fm-busy-lib.sh, so
omp now reports through that contract instead of a pane signature. omp
diverges from Pi's wiring because live probes on omp 17.2.1 showed it never
emits agent_settled: per turn it emits agent_start, turn_end, session_stop,
agent_end, and ctx.isIdle() reads false at agent_end and flips true within
250ms. omp's per-task extension therefore takes agent_end as its idle edge
and re-checks ctx.isIdle() after a short delay, keeping main's protection
against auto-retries, compaction, tool loops, and queued continuations
rather than dropping the guard. fm_busy_sources_for_harness gained an omp
arm so those records are trusted, and fm-spawn arms the gen for omp so the
extension is not dead code.

The rendered-text omp token in bin/fm-tmux-lib.sh is deliberately kept: it
is a delivery guard for fm-send submit acknowledgement and the away-mode
pane read, not a task-state source, and removing it would leave omp the
only harness without that guard.

Test expectations changed deliberately, never weakened:
- fm-omp-harness.test.sh: the fm-watch.sh busy-default parity assertion is
  dropped because main removed that default outright; every omp token
  assertion is kept and the test is rescoped to the delivery guard.
- fm-turnend-guard, arm-pretool, cd-pretool, sessionstart-nudge: the
  branch's omp source-substring assertions are converted to behavioral
  coverage driving the real tracked extensions, following main's move away
  from asserting implementation-source bytes. Each replacement was
  mutation-verified.
- fm-busy-adapter-wiring.test.sh: new omp semantic lifecycle test covering
  the deferred isIdle re-check in both directions.

Verified live on omp 17.2.1: a real crewmate launched from the command
fm-spawn builds moved busy -> idle through the real classifier, and a
session_stop return-value block forced one continuation with its one-shot
release intact.
@kunchenguid

Copy link
Copy Markdown
Owner

Automated reminder: thanks for the PR! This branch currently has a merge conflict with the base branch.

When you get a chance, please rebase onto (or merge) the latest base branch, resolve the conflict, and push. After that, checks will re-run and the PR will get looked at again.

Noted for firstmate#1144 at 8fca698a.

Boxyboxy added 14 commits July 31, 2026 15:06
Each rule already cost the fleet real turns, and each existed only as prose.

fm-send: refuse text over 400 bytes before any send or pending-reply record.
Two ~1,400-byte steers to one busy pane both reported an unconfirmed send and
never arrived, while a ~230-byte pointer to the same pane landed first try.
The refusal names the working pattern (append to the brief, send a pointer)
and --allow-long is the explicit opt-out. The --key path is untouched.

fm-brief: bake the absolute firstmate path into both the ship and the scout
scaffold for any report or evidence output, and say plainly that a path inside
the worktree does not survive cleanup. Three crewmates wrote evidence to a bare
relative data/<id>/ path and lost it when the worktree was returned.

fm-pr-merge: read the PR's check state and refuse a red PR, naming the failing
checks. No configured checks and pending checks are deliberately not red; a
check state that cannot be read is a refusal, because "not red" must be
established rather than assumed. --allow-red-checks is the captain-authorized
exception and records merge_checks_override= in the task's metadata, above the
canonical pr= line so the metadata identity parse still accepts the file.

Tests are colocated in the existing suites for the three scripts.
…m one

Merging origin/main brought in a corrected `tests/fixtures/*/*` family rule.
The older `tests/fixtures/*` rule added on the other side survived the textual
auto-merge and still matched a direct child of `tests/fixtures/`, resolving it
to the literal reference "fixtures" instead of letting it fall through to
`__unmapped__` as the upstream fix intends.

Neither side had this defect alone; combining them created it. Removing the
superseded rule restores the merged-in behavior exactly.
Stale observations were reaching the captain and workers with the confidence
they had the day they were filed: decisions already shipped, retired, or
explicitly out of scope were re-presented, and briefs generated from them sent
workers to implement against dead premises. Three separate gaps allowed it, so
each is closed in its own existing owner rather than by new machinery.

Wrong authority order. AGENTS.md section 6 now states that an initiative whose
plan the captain maintains outside firstmate outranks every firstmate-derived
record of it, and that a hold contradicting that source is stale by definition.
The rule is generic and the concrete locations stay home-local in data/, since
this repo is a shared template.

No verification gate on surfacing. Every ship and scout scaffold now carries a
premise check: confirm the task text's central factual claims against current
code, and report rather than implement when one no longer holds. It lives in
the scaffold because the failure mode is firstmate not remembering to add it.
decision-hold-lifecycle requires the same re-verification before a hold is
relayed or briefed, which is the step it previously only required at creation.

No expiry semantics. Open decisions now carry age_days, the whole days since
the filing date tasks-axi already records, so an aged hold reads as a
hypothesis rather than a fact. It is null when undated rather than guessed at,
and the secondmate home summary passes the filing date through so cross-home
decisions carry the same age.
Six omp-on-herdr defects, two behavioral and four documentation accuracy.

The herdr composer classifier could not see omp's input box. omp draws its
composer as a bottom-anchored rounded pair - a status row `╭── π > ... ▶───╮`
directly above an input row `╰─ <typed text> ─╯` - and neither row starts and
ends with the same side-border glyph, so the bordered test never matched either
one and an unrelated row higher up the capture won instead. An idle omp holding
unsubmitted text classified `empty`, which is the away-mode injector's
go-signal, so an escalation could be typed on top of half-typed text and the
concatenation submitted.

Add a rounded shape that locates the bottom-most adjacent arc pair and accepts
it only when herdr's native identity is omp and its status is idle, done, or
blocked, mirroring the existing Pi separator-pair conjunction. Structure alone
is deliberately insufficient: omp closes ordinary transcript boxes with the same
`╰────╯` glyph, so shape without identity would let a leftover output box pass
as a ready composer. The new branch is last in the chain, so every existing
refusal keeps precedence and this can only add a recognized shape.

Tests run over four raw styled captures recorded from live omp 17.2.2 panes
under herdr 0.7.5, fed in verbatim because content extraction reads SGR styling.

omp supervision also lost its wake path every five minutes: the watcher arm is
started as an omp background async job, and omp's bash tool applies a 300s
command deadline by default, so on a quiet fleet nothing woke the arm inside
that window and it was killed. The watcher child survives, but the arm is the
wake delivery path. Record that the arm must be started with the command
deadline disabled, and say why, in the protocol, the emitted repair and
ordinary-wake lines, and the away-mode daemon's own entry points.

Also name omp as a native tracked background host in the away-mode guidance,
add the missing omp ordinary-wake case so it no longer falls back to generic
wording, correct the claim that arming reports `watcher: healthy` to its real
`started`/`attached`/`FAILED` forms, and refresh the omp version stamps. Every
restamped claim was re-probed on 17.2.2 rather than restamped blind: launch
flags, the `--no-extensions` suppression of both `-e` and `--hook`, the
lifecycle event set with its deferred `ctx.isIdle()` edge, the `session_stop`
forced continuation, the `tool_call` block contract, and the process name.
Facts that were not re-run are left dated at the version that verified them.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants