Skip to content

feat(bootstrap): surface fork upstream drift at session start - #801

Closed
Amplify-Logic wants to merge 13 commits into
kunchenguid:mainfrom
Amplify-Logic:fm/firstmate-upstream-update-detect-n8
Closed

Amplify-Logic wants to merge 13 commits into
kunchenguid:mainfrom
Amplify-Logic:fm/firstmate-upstream-update-detect-n8

Conversation

@Amplify-Logic

Copy link
Copy Markdown
Contributor

Intent

Detect and surface new upstream Firstmate releases at session start so forked homes stop silently drifting behind origin-only updates.

Captain authorized detection, not automatic merging: automatically merging a third party's changes into a system running unattended is not acceptable; the valuable half is knowing promptly and deciding deliberately.

Requirements implemented:

  1. Cheap read-only detection when the configured upstream remote has commits this home lacks, suitable for every session start.
  2. Surface through the existing session-start bootstrap diagnostic path (bin/fm-bootstrap.sh + bootstrap-diagnostics skill), not a parallel channel.
  3. Report usefully: commit count plus subjects for urgency judgment.
  4. Never fetch-and-merge, never rewrite refs destructively, never touch projects/.
  5. Fail quietly when no upstream remote, offline, or not a fork (origin URL equals upstream).
  6. Keep it bounded/cheap for every fleet member session start.

Scope decision stated and followed: firstmate-only. Do NOT also check no-mistakes/treehouse releases here — those already surface their own version gaps; expanding would be scope creep and startup cost.

Prior rebase ask-user finding about unpushed main commits was a verified false positive (stale remote-tracking ref); origin/main and local main are both ff1015f with this branch only one feature commit ahead. Approve any repeat of that false positive.

Tests cover behind-with-subjects, no-upstream, offline, same-URL non-fork, current tip, secondmate silence, and bootstrap wiring.

Restarting validation after a prior run failed on a network connection drop during the test step, not on the changes themselves. Commits intact; no implementation redo.

What Changed

  • Added bin/fm-upstream-lib.sh, a read-only upstream drift detector wired into bin/fm-bootstrap.sh: when the configured upstream remote has commits this fork lacks, session-start diagnostics emit an UPSTREAM line with the commit count and recent subjects (clipped to 72 chars, bounded by FM_UPSTREAM_SUBJECT_LIMIT). It stays silent when there is no upstream remote, the origin URL matches upstream (not a fork), the fork is at the upstream tip, or the network is unreachable — and it never fetches, merges, or rewrites refs.
  • Updated the bootstrap-diagnostics skill to instruct agents to report upstream drift and wait for the captain's decision rather than merging.
  • Added tests/fm-upstream.test.sh covering behind-with-subjects, no-upstream, offline, same-URL non-fork, current-tip, secondmate silence, and bootstrap wiring cases, and documented the FM_UPSTREAM_* env knobs in docs/configuration.md and the new library in docs/scripts.md.

Risk Assessment

✅ Low: A well-bounded, read-only, fail-quiet detection feature that matches every stated intent constraint (no merging, existing diagnostic path, timeout-bounded probes, secondmate/non-fork/offline silence) with comprehensive tests and no substantive defects found.

Testing

Baseline full suite plus the targeted upstream/bootstrap tests all pass, and a live end-to-end run of bin/fm-bootstrap.sh against a synthetic fork 3 commits behind its upstream printed the expected UPSTREAM diagnostic with commit subjects, left all local refs untouched, and stayed silent in the non-fork, no-remote, offline, and up-to-date cases.

Evidence: Bootstrap UPSTREAM diagnostic (fork 3 commits behind)

$ FM_ROOT_OVERRIDE=.../fork FM_BOOTSTRAP_DETECT_ONLY=1 bin/fm-bootstrap.sh UPSTREAM: 3 commits behind upstream/main (/tmp/fm-upstream-demo.Ar1WI3/upstream) - docs: describe upstream drift detection; fix(spawn): stop double-registering herdr workers; feat: add fleet-wide quota telemetry exit=0

$ FM_ROOT_OVERRIDE=/tmp/fm-upstream-demo.Ar1WI3/fork FM_BOOTSTRAP_DETECT_ONLY=1 bin/fm-bootstrap.sh   # fork 3 commits behind upstream
UPSTREAM: 3 commits behind upstream/main (/tmp/fm-upstream-demo.Ar1WI3/upstream) - docs: describe upstream drift detection; fix(spawn): stop double-registering herdr workers; feat: add fleet-wide quota telemetry
exit=0
Evidence: Read-only guarantee and silent cases via real bootstrap

fork HEAD/refs/heads/main unchanged after detection; silent (empty output, exit 0) for: origin==upstream URL, no upstream remote, unreachable upstream, fork at upstream tip

== read-only guarantee: fork local branch/HEAD unchanged after detection ==
e583f250c052a52fe2f59ccc8cdcd8a2a72a850a
e583f250c052a52fe2f59ccc8cdcd8a2a72a850a
## main...origin/main

== silent: origin URL == upstream URL (not a fork) ==
output: [] exit=0

== silent: no upstream remote ==
output: [] exit=0

== silent: upstream unreachable (offline) ==
output: [] exit=0

== silent: fork already at upstream tip ==
output: [] exit=0

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

⚠️ **Rebase** - 1 warning

Push main to origin, or rebase your branch onto origin/main, before gating.

⚠️ **Review** - 2 infos
  • ℹ️ bin/fm-upstream-lib.sh:118 - When drift exceeds FM_UPSTREAM_SUBJECT_LIMIT (default 8), the UPSTREAM line lists only the newest 8 subjects with no explicit '…and N more' marker; the leading commit count does convey the total, so this is cosmetic.
  • ℹ️ bin/fm-upstream-lib.sh:118 - Upstream commit subjects are third-party-controlled text surfaced into the agent's session-start digest (mild prompt-injection surface). Inherent to the required 'report subjects' behavior; mitigated by the 72-char clip, bounded subject count, and the bootstrap-diagnostics skill's explicit never-merge/wait-for-captain instruction.
✅ **Test** - passed

✅ No issues found.

  • command -v tmux >/dev/null || { echo "tmux is required for e2e tests" >&2; exit 1; }; tmux -V; rc=0; for t in tests/*.test.sh; do echo "== $t =="; bash "$t" || rc=1; done; exit "$rc"
  • Baseline configured suite: for t in tests/*.test.sh; do bash "$t"; done (ran successfully before this session)
  • bash tests/fm-upstream.test.sh — behind-with-subjects, no-upstream, offline, same-URL non-fork, current tip, secondmate silence, bootstrap wiring (all ok)
  • bash tests/fm-bootstrap.test.sh — full bootstrap diagnostic suite (all ok)
  • Manual e2e: built a synthetic upstream bare repo + fork 3 commits behind, ran FM_ROOT_OVERRIDE=<fork> FM_BOOTSTRAP_DETECT_ONLY=1 bin/fm-bootstrap.sh and captured the UPSTREAM line with 3 subjects
  • Manual e2e safety check: verified fork HEAD/refs/heads/main and working tree unchanged after detection (read-only guarantee)
  • Manual e2e silent cases through the real bootstrap: origin==upstream URL, no upstream remote, unreachable upstream, and fork at upstream tip — all produced no output
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

Amplify-Logic and others added 13 commits July 19, 2026 01:19
…efuse ambiguous delimiter shapes (#1)

* fix(herdr-lab): place --session before the child-argv delimiter

`agent start ... -- <child argv...>` always got a trailing --session
appended after --, so Herdr never consumed it and the child process
received it instead - meaning Herdr could fall through to the live
default session for agent-start calls. fm_herdr_lab_raw now places
exactly one --session immediately before -- for that shape and still
appends it for every ordinary call. fm_herdr_lab_cli rejects multiple
delimiters, a delimiter on any command other than agent start, and
agent start without a delimiter plus non-empty child command, before
any Herdr call is made. Updated the brief scaffold and backend doc
wording that claimed the selector is always trailing.

* no-mistakes(document): note run's delimiter-shape refusals in herdr-backend doc

* no-mistakes(review): refuse option before child-argv delimiter; collapse raw invocation

* no-mistakes(review): only treat first -- as delimiter; child argv untouched
…uid#685 vocabulary (#2)

Commit 1182883 (kunchenguid#685) intentionally changed the secondmate charter
scaffold's role declaration from 'a persistent domain supervisor' to
'a persistent second mate' and updated the captain-translation-contract
test, but missed the assertion in tests/fm-brief.test.sh. The stale
expectation left the secondmate-charter case failing. Point the
role-declaration assertion at the current wording so it verifies the
charter still declares its role without weakening the check or reverting
the intended vocabulary.
…er to doc indexes (#3)

* feat(primary): add guarded runtime profiles and Kimi K3 support

* no-mistakes(review): make Kimi registry merge atomic and clean temp files

* no-mistakes(document): add Kimi protocol and fm-primary launcher to doc indexes
* feat: add human-readable Herdr worker presentation

* no-mistakes(review): capability-gate herdr presentation and skip secondmate worker rename
…4.5) (#6)

* feat(harness): add verified cursor worker adapter (Cursor CLI + Grok 4.5)

Adds `cursor` (Cursor CLI `agent` 2026.07.16-899851b) as a verified WORKER
adapter, after empirical verification on 2026-07-19. Worker only: primary
support was not verified and is not claimed, the mirror of the Kimi
primary-only boundary. Kimi stays primary-only; no Kimi worker support here.

Verification found two defects in shared composer monitoring that would have
made an adapter monitoring cannot trust, both fixed in the shared owners:

- Reverse-video cursor cell. cursor draws the terminal cursor as SGR 7 over the
  idle placeholder's FIRST character. Reverse video is neither dim/faint nor a
  dark foreground, so fm_composer_strip_ghost kept it and an idle composer
  reduced to a lone "A" -> `pending`, deferring every away-mode escalation
  forever. A plain FM_COMPOSER_IDLE_RE does not fix this, because the idle regex
  was only matched against ghost-stripped content; it is now matched against the
  plain row too, which is styling-independent.
- #{cursor_y} does not point at cursor's composer (it parks the cursor in the
  bottom status area, observed 5 rows off). Reading that row found an empty
  status line, so a composer holding real unsubmitted text classified as
  `empty` - a FALSE-EMPTY, the dangerous direction, since the away-mode injector
  picks targets by emptiness and would type over pending input. The tmux path
  now locates the composer structurally, matching herdr's existing scan.

Other integration surfaces:

- fm-spawn: launch template (--yolo autonomy, --workspace pinned to the task
  worktree, and deliberately no -w so cursor never allocates a second worktree),
  plus a per-task gitignored .cursor/hooks.json `stop` hook verified to fire
  once per turn with no extra hook-trust grant.
- Effort axis: cursor has no effort flag - effort is a SUFFIX on the model id -
  so cursor_model_with_effort folds it in. xhigh/max cap at high, an explicit
  tiered model is never retiered, and -fast variants are never implicit.
- fm-harness: CURSOR_AGENT=1 detection, tested BEFORE CLAUDECODE because cursor
  does not clear an inherited CLAUDECODE=1 and a cursor worker under a
  claude-hosted firstmate would otherwise be steered with claude's vocabulary.
- Busy signature `ctrl+c to stop` in both owners. The spinner VERB is
  deliberately not matched: it flips Working -> Running mid-turn, so matching it
  reads a tool-executing pane as idle.
- tmux liveness: cursor's wrapper execs node, but the versioned cursor-agent
  bundle path survives exec -a in argv, so a `node` COMM resolves to `alive`.
  Any other bare node stays `unknown`, never dead; pi's gap is unchanged.

Backends reviewed rather than assumed: tmux verified; herdr composer-safe by
construction (structural scan + shared idle default) with liveness unverified;
zellij/orca/cmux not exercised with cursor. Recorded in docs/cursor-harness.md.

Also documents that cursor executes claude-format hooks (.claude/settings*.json,
Stop -> stop), so cursor must never be launched from the firstmate primary
checkout, and that --model mutates the account-global default.

A live fm-spawn dispatch was NOT run: it allocates a real pooled worktree and
writes live fleet state outside the task worktree. Every underlying behaviour
was verified by raw launches and the integrated path (launch, turn-end hook,
composer, liveness) was confirmed together on a real pane; the first supervised
fm-spawn --harness cursor run remains firstmate's gate.

Evidence: docs/cursor-harness.md. Regressions: tests/fm-cursor-adapter.test.sh
(20 assertions). bin/fm-lint.sh clean; composer/tmux/herdr/orca/cmux suites pass.

* no-mistakes(review): scope cursor composer-row scan to identified cursor panes

* no-mistakes(document): document cursor worker adapter in config, architecture, and indexes
* fix(spawn): refuse missing harness binaries before launch

Resolve and probe verified launch executables before creating task endpoints, preventing absent CLIs from leaving dead worker shells.

Co-authored-by: Cursor <cursoragent@cursor.com>

* no-mistakes(review): add stdin-closed timeout-bounded version probe with override knob

* no-mistakes(review): escalate timed-out version probe to SIGKILL after grace

* no-mistakes(review): kill probe process group so timed-out wrapper descendants die

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Define one fleet-status contract for guarded Claude, Pi, and Kimi surfaces while preserving Cursor's worker-only boundary.
* test(watcher): wait for healthy peer readiness

Wait for the fixture's SIGTERM handler before exercising restart so startup scheduling cannot turn the healthy-peer case into the child-owned path.

Co-authored-by: Cursor <cursoragent@cursor.com>

* no-mistakes(review): clean up fixture processes on healthy-peer assertion failures

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Keep startup dispatch validation and secondmate liveness aligned with the verified worker adapter set, including Cursor's model-suffixed effort range.

Co-authored-by: Cursor <cursoragent@cursor.com>
…alation (#11)

* fix(herdr): do not escalate busy cursor panes as waiting-on-human

Herdr can report agent_status=blocked while a cursor worker is mid-turn
(ctrl+c to stop footer). Corroborate blocked/idle against that busy
signature before immediate stale escalation or poll-path idle.

Co-authored-by: Cursor <cursoragent@cursor.com>

* no-mistakes(test): fix jq-1.6 and preflight fixture gaps in failing tests

* no-mistakes(docs): corroborate herdr idle/blocked in architecture summary

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* no-mistakes(lint): silence SC2329 on indirectly-invoked test stub

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Detect when a configured upstream remote has commits this home lacks, and
report count plus subjects through the existing bootstrap diagnostic path
so forks stop silently drifting behind without auto-merging third-party
changes.

Co-authored-by: Cursor <cursoragent@cursor.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@Amplify-Logic

Copy link
Copy Markdown
Contributor Author

Closing this PR — it targeted the wrong repository by mistake (gh's default repo was set incorrectly) and is not an intended contribution upstream. Sorry for the noise.

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.

1 participant