Skip to content

feat(send): route secondmate replies for firstmate requests#93

Merged
kunchenguid merged 2 commits into
mainfrom
fm/sm-request-marker-h4
Jun 26, 2026
Merged

feat(send): route secondmate replies for firstmate requests#93
kunchenguid merged 2 commits into
mainfrom
fm/sm-request-marker-h4

Conversation

@kunchenguid

Copy link
Copy Markdown
Owner

Intent

Make a secondmate distinguish a request from the MAIN FIRSTMATE (its supervisor) from a request typed directly by the CAPTAIN, and route its response accordingly.

Problem: a secondmate is itself a firstmate, so it treats incoming fm-send/tmux messages as if from its captain and answers conversationally in its own chat - which the main firstmate never reads. The only main<-secondmate channel is the terse status file, so a detailed secondmate reply to a main-firstmate request strands unseen.

Design: when a request comes from the main firstmate, fm-send marks it so the secondmate recognizes it and responds via the STATUS/ESCALATION path instead of chat. This mirrors the existing afk FM_INJECT_MARK mechanism (which marks daemon->firstmate escalations) but in the OTHER direction: firstmate marks its requests TO a secondmate.

Key decision - a DISTINCT marker, not a reuse of the afk FM_INJECT_MARK. New helper bin/fm-marker-lib.sh defines FM_FROMFIRST_MARK as the human-readable label "[fm-from-firstmate]" followed by ASCII unit separator 0x1f. It reuses the afk untypable-0x1f concept (a byte a human cannot type, so a captain-typed message is never mistaken for a firstmate request) but is deliberately distinct from the afk daemon's BARE leading 0x1f. The afk-exit contract keys on a LEADING 0x1f; this marker leads with the label and uses 0x1f as a field separator, so it never starts with a bare 0x1f and cannot conflate with a secondmate's own afk-daemon escalation marker. This satisfies the captain's explicit constraint to avoid conflating with the afk use of 0x1f.

Hard constraint honored: the hardened backbone was deliberately NOT touched - bin/fm-watch.sh, fm-watch-arm.sh, fm-wake-lib.sh, fm-supervise-daemon.sh, fm-tmux-lib.sh, and the afk skill are unchanged. The afk daemon's message_is_injection/should_exit_afk are contract-encoding functions not invoked at runtime, so no backbone change was needed. (If a review finding suggests editing any of those files, that is intentionally out of scope and must be escalated, not applied.)

Scope of changes (deliberately confined):

  • bin/fm-send.sh: when the resolved target is a bare fm- whose meta records kind=secondmate, prepend FM_FROMFIRST_MARK to the literal text. Crewmate/scout targets, explicit session:window escape-hatch targets (which do no meta lookup), and the --key path are intentionally left unmarked and behave exactly as before. The slash-command settle decision deliberately keys on the original unmarked text.
  • bin/fm-brief.sh: the generated secondmate charter gains a "Requests from the main firstmate" section documenting the recognize-and-respond-via-status contract (a status line for a terse result; a doc under the home plus a status pointer - the scout-report pattern - for a detailed one), and that an UNMARKED message stays conversational, authoritative captain intervention.
  • AGENTS.md (its CLAUDE.md is a symlink): intake and supervise sections document that fm-send marks secondmate requests and the answer returns on the status/doc path (read it as an ordinary status signal; do not peek the secondmate's chat).
  • bin/fm-marker-lib.sh: new small sourced helper, single source of truth for the marker plus a fm_message_from_firstmate detector that mirrors the afk daemon's message_is_injection testable-contract pattern. Non-executable, matching the recent sourced-lib convention (fm-tangle-lib.sh, fm-tasks-axi-lib.sh).

Tests: new hermetic tests/fm-send-secondmate-marker.test.sh (stubbed tmux) asserts a kind=secondmate target gets the marker prepended, a crewmate (kind=ship) target does not, an explicit session:window target is never marked, the --key path carries no marker, and the marker is exactly "[fm-from-firstmate]" + 0x1f with the detector keying on that untypable sequence. tests/fm-secondmate-lifecycle-e2e.test.sh phase_send was updated to expect the now-marked secondmate send (window-routing intent preserved, marker + payload asserted). A CONTRIBUTING.md test-index line was added. The full suite (15 test files) and shellcheck bin/.sh tests/.sh are green.

What Changed

  • Added a distinct main-firstmate-to-secondmate request marker and taught fm-send to apply it only for bare fm-<id> targets whose metadata records kind=secondmate.
  • Updated secondmate charter and project docs so marked requests are answered through status or doc pointers, while unmarked captain messages remain conversational.
  • Added focused coverage for marker formatting, send routing, explicit session:window, --key, and secondmate lifecycle expectations. Captain, the pipeline reported passing intent, rebase, review, test, document, lint, and push stages.

Risk Assessment

✅ Low: Captain, the change is narrowly scoped to secondmate message marking, documents the new contract, and preserves existing crewmate, explicit-window, and --key behavior.

Testing

Captain, author context reported prior full-suite and shellcheck success; in this run I reran the focused marker test, secondmate lifecycle E2E, and all tests/*.test.sh behavior tests, then generated byte-level fake-tmux evidence for the user-facing send behavior. All executed checks passed, the worktree remained clean, and shellcheck was not rerun because static analysis was disallowed.

Evidence: Secondmate marker fake-tmux transcript
# secondmate from-firstmate marker evidence
worktree: /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KW175787X7AVJDHRT2PKB8NV
intent: a main firstmate send to a bare fm-<id> secondmate is marked, while captain/escape paths stay unmarked.

## Marker contract
FM_FROMFIRST_LABEL: [fm-from-firstmate]
FM_FROMFIRST_MARK bytes (hex): 5b 66 6d 2d 66 72 6f 6d 2d 66 69 72 73 74 6d 61
74 65 5d 1f 

FM_FROMFIRST_MARK bytes (od -c): [ f m - f r o m - f i r s t m a
t e ] 037 

afk distinction: PASS - marker ends with 0x1f but does not start with bare 0x1f
detector marked message: PASS
detector plain message: PASS
detector label-only message: PASS

## Bare fm-domain with kind=secondmate meta
tmux target: sess:fm-domain
payload bytes (hex): 5b 66 6d 2d 66 72 6f 6d 2d 66 69 72 73 74 6d 61
74 65 5d 1f 72 6f 75 74 65 20 74 68 69 73 20 77
6f 72 6b 

payload bytes (od -c): [ f m - f r o m - f i r s t m a
t e ] 037 r o u t e t h i s w
o r k 

classified as from-firstmate: PASS

## Bare fm-build with kind=ship meta
tmux target: sess:fm-build
payload bytes (hex): 66 69 78 20 74 68 65 20 74 65 73 74 

payload bytes (od -c): f i x t h e t e s t 

classified as from-firstmate: PASS (unmarked)

## Explicit session:window target
tmux target: sess:fm-domain
payload bytes (hex): 63 61 70 74 61 69 6e 20 74 79 70 65 64 20 64 69
72 65 63 74 6c 79 20 2f 20 65 78 70 6c 69 63 69
74 20 65 73 63 61 70 65 20 68 61 74 63 68 

payload bytes (od -c): c a p t a i n t y p e d d i
r e c t l y / e x p l i c i
t e s c a p e h a t c h 

classified as from-firstmate: PASS (unmarked)

## --key path
literal payload byte count: 0
result: PASS - keypress path typed no marker-bearing literal text

## Generated secondmate charter excerpt
# Requests from the main firstmate
You are a firstmate in your own home, so an incoming message reaches you in your own chat.
You must distinguish who it is from, because the answer goes to a different place.
A request relayed to you by the main firstmate (your supervisor) is tagged with a leading `[fm-from-firstmate]` marker followed by an invisible system separator; this marker is untypable, so a human never produces it.
When a message carries that marker, do the work, then respond via the STATUS/ESCALATION path below, never only in this chat: the main firstmate does not read your chat, so a chat-only reply is lost.
For a terse result, a status line is the whole answer.
For a detailed answer (an investigation, a plan, an audit), write it to a doc under your home's `data/` and append a status line that points to that doc - the scout-report pattern - so the main firstmate is woken and can read it.
A message with NO marker is the captain typing directly into your pane: treat it as authoritative captain intervention and stay conversational exactly as you would for any captain message; do not force it onto the status path.

# Escalation to main firstmate

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

✅ **Review** - passed

✅ No issues found.

✅ **Test** - passed

✅ No issues found.

  • tests/fm-send-secondmate-marker.test.sh
  • tests/fm-secondmate-lifecycle-e2e.test.sh
  • for test_script in tests/*.test.sh; do "$test_script"; done
  • Manual fake-tmux bin/fm-send.sh check covering secondmate, crewmate, explicit session:window, and --key paths, with transcript written to /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KW175787X7AVJDHRT2PKB8NV/secondmate-marker-evidence.txt
  • sed -n '1,220p' /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KW175787X7AVJDHRT2PKB8NV/secondmate-marker-evidence.txt
  • git status --short
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

fm-send now prepends a distinct from-firstmate marker (new bin/fm-marker-lib.sh:
the label [fm-from-firstmate] followed by ASCII unit separator 0x1f) when the
resolved target is a bare fm-<id> whose meta records kind=secondmate. The marker
reuses the afk untypable-separator concept but is deliberately distinct from the
daemon's bare leading 0x1f, so it never conflates with a secondmate's own afk
escalation marker.

A secondmate is itself a firstmate, so a relayed request lands in its own chat,
which the main firstmate never reads. The secondmate charter (fm-brief.sh) and
AGENTS.md now document the recognize-and-respond-via-status contract: a marked
request is answered via the status file (or a doc plus a status pointer), never
chat-only; an unmarked message stays conversational captain intervention.

Crewmate/scout targets, explicit session:window targets, and the --key path are
unchanged. Backbone (watcher, daemon, tmux-lib, afk) untouched.

Hermetic tests cover marked vs unmarked sends and the exact marker bytes.
@kunchenguid kunchenguid force-pushed the fm/sm-request-marker-h4 branch from 085e6b5 to 3ab149f Compare June 26, 2026 05:59
@kunchenguid kunchenguid merged commit b1de8a8 into main Jun 26, 2026
4 checks passed
@kunchenguid kunchenguid deleted the fm/sm-request-marker-h4 branch June 26, 2026 16:23
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