fix(bin): refuse a Herdr Claude submit that would send only a message tail - #5336
Conversation
A long typed payload can sit in the composer as a suffix, or as a paste placeholder plus a remainder, and the following Enter was still reported as delivered. Prove the selected composer holds the payload before Enter, and report failure when it does not.
…es from a timing race in an existing test that this PR doesn't touch. **What failed:** `tests/fm-procevent.test.sh` failed at "the superseded paced runner invoked its stale command" (line ~3313). The PR only changes the Herdr files and their tests, and the same shard passed on main at the base commit. **Why it can fail:** the fixture starts a second runner with a 3-second launch floor (the minimum wait since the source's last launch). That runner sleeps for the rest of the floor and only then checks whether its registration was replaced (`fm_procevent_launch_floor_wait` in `bin/fm-procevent-lib.sh`). The test then waits for the claim and re-registers the source. If that takes longer than about 3 seconds after the first launch, the old runner wakes up, finds its registration still current, and runs the stale command. That produces the second log line the test reports. The CI shard was slow (this one test took 160 s). **Fix:** in `tests/fm-procevent.test.sh` I raised the superseded runner's floor from 3 to 15 seconds and added a comment explaining why. The floor now outlasts the fixture setup even on a loaded runner. Nothing else changed: the first launch and the later fresh-registration start still use a 3-second floor, and no product code changed. **Verification:** - The full test file can't give a reliable result on this machine (load average about 64 on 8 cores). It failed earlier, at the reconcile assertion around line 1680, before it reached this section. - I ran the changed section by itself (file setup plus the pacing-race block) five times with the fix: all passed, in about 9-13 s each. - The original code also passed five out of five, so the race didn't reproduce locally. The diagnosis rests on the code path and the CI log. - I haven't seen the full file or the CI shard pass with the fix yet
586cfa2 to
450fc33
Compare
|
Speaking as Kun's firstmate: HEAD Contract-class: restore. Tip vs main: VISION.md (per-rule)
Decision: waiting-author — fix CI (serial 9 / bearings-board-render) and re-attest if HEAD moves. Do not auto-merge while CI red. No Firstmate captain flag (not otherwise-ready). No workflow approval. No security FYI. |
|
The serial 9 failure is in On this pull request the job failed here: The log says the board did not build because the source was not listening after reconcile ( The same test failed the same way on #5358: I ran I cannot re-run the upstream job. Please re-run the failed shard. I can file an issue for the flaky test if that would help. |
|
Speaking as Kun's firstmate: this is merged. Thank you @tiago-peixoto — really appreciate you taking the time on this. HEAD |
… tail (kunchenguid#5336) * fix(bin): refuse a Herdr submit that would send only a message tail A long typed payload can sit in the composer as a suffix, or as a paste placeholder plus a remainder, and the following Enter was still reported as delivered. Prove the selected composer holds the payload before Enter, and report failure when it does not. * no-mistakes(review): Scope Herdr payload proof to Claude, clear composer on refusal * no-mistakes(test): Clear refused Herdr composer drafts one wrapped row per press * no-mistakes(test): Accept Claude's multi-line paste placeholder in Herdr submit proof * no-mistakes(review): Accept Claude read-back that drops U+2063 in Herdr proof * no-mistakes(document): Document Herdr proof ignoring U+2063 operational mark * no-mistakes(ci): I made a one-line test change. The failing check comes from a timing race in an existing test that this PR doesn't touch. **What failed:** `tests/fm-procevent.test.sh` failed at "the superseded paced runner invoked its stale command" (line ~3313). The PR only changes the Herdr files and their tests, and the same shard passed on main at the base commit. **Why it can fail:** the fixture starts a second runner with a 3-second launch floor (the minimum wait since the source's last launch). That runner sleeps for the rest of the floor and only then checks whether its registration was replaced (`fm_procevent_launch_floor_wait` in `bin/fm-procevent-lib.sh`). The test then waits for the claim and re-registers the source. If that takes longer than about 3 seconds after the first launch, the old runner wakes up, finds its registration still current, and runs the stale command. That produces the second log line the test reports. The CI shard was slow (this one test took 160 s). **Fix:** in `tests/fm-procevent.test.sh` I raised the superseded runner's floor from 3 to 15 seconds and added a comment explaining why. The floor now outlasts the fixture setup even on a loaded runner. Nothing else changed: the first launch and the later fresh-registration start still use a 3-second floor, and no product code changed. **Verification:** - The full test file can't give a reliable result on this machine (load average about 64 on 8 cores). It failed earlier, at the reconcile assertion around line 1680, before it reached this section. - I ran the changed section by itself (file setup plus the pacing-race block) five times with the fix: all passed, in about 9-13 s each. - The original code also passed five out of five, so the race didn't reproduce locally. The diagnosis rests on the code path and the CI log. - I haven't seen the full file or the CI shard pass with the fix yet * no-mistakes(test): Accept Claude folder-trust prompt via down+enter in live e2e * no-mistakes(document): Note unreadable Claude composer refusal in Herdr docs
… tail (kunchenguid#5336) * fix(bin): refuse a Herdr submit that would send only a message tail A long typed payload can sit in the composer as a suffix, or as a paste placeholder plus a remainder, and the following Enter was still reported as delivered. Prove the selected composer holds the payload before Enter, and report failure when it does not. * no-mistakes(review): Scope Herdr payload proof to Claude, clear composer on refusal * no-mistakes(test): Clear refused Herdr composer drafts one wrapped row per press * no-mistakes(test): Accept Claude's multi-line paste placeholder in Herdr submit proof * no-mistakes(review): Accept Claude read-back that drops U+2063 in Herdr proof * no-mistakes(document): Document Herdr proof ignoring U+2063 operational mark * no-mistakes(ci): I made a one-line test change. The failing check comes from a timing race in an existing test that this PR doesn't touch. **What failed:** `tests/fm-procevent.test.sh` failed at "the superseded paced runner invoked its stale command" (line ~3313). The PR only changes the Herdr files and their tests, and the same shard passed on main at the base commit. **Why it can fail:** the fixture starts a second runner with a 3-second launch floor (the minimum wait since the source's last launch). That runner sleeps for the rest of the floor and only then checks whether its registration was replaced (`fm_procevent_launch_floor_wait` in `bin/fm-procevent-lib.sh`). The test then waits for the claim and re-registers the source. If that takes longer than about 3 seconds after the first launch, the old runner wakes up, finds its registration still current, and runs the stale command. That produces the second log line the test reports. The CI shard was slow (this one test took 160 s). **Fix:** in `tests/fm-procevent.test.sh` I raised the superseded runner's floor from 3 to 15 seconds and added a comment explaining why. The floor now outlasts the fixture setup even on a loaded runner. Nothing else changed: the first launch and the later fresh-registration start still use a 3-second floor, and no product code changed. **Verification:** - The full test file can't give a reliable result on this machine (load average about 64 on 8 cores). It failed earlier, at the reconcile assertion around line 1680, before it reached this section. - I ran the changed section by itself (file setup plus the pacing-race block) five times with the fix: all passed, in about 9-13 s each. - The original code also passed five out of five, so the race didn't reproduce locally. The diagnosis rests on the code path and the CI log. - I haven't seen the full file or the CI shard pass with the fix yet * no-mistakes(test): Accept Claude folder-trust prompt via down+enter in live e2e * no-mistakes(document): Note unreadable Claude composer refusal in Herdr docs
… tail (kunchenguid#5336) * fix(bin): refuse a Herdr submit that would send only a message tail A long typed payload can sit in the composer as a suffix, or as a paste placeholder plus a remainder, and the following Enter was still reported as delivered. Prove the selected composer holds the payload before Enter, and report failure when it does not. * no-mistakes(review): Scope Herdr payload proof to Claude, clear composer on refusal * no-mistakes(test): Clear refused Herdr composer drafts one wrapped row per press * no-mistakes(test): Accept Claude's multi-line paste placeholder in Herdr submit proof * no-mistakes(review): Accept Claude read-back that drops U+2063 in Herdr proof * no-mistakes(document): Document Herdr proof ignoring U+2063 operational mark * no-mistakes(ci): I made a one-line test change. The failing check comes from a timing race in an existing test that this PR doesn't touch. **What failed:** `tests/fm-procevent.test.sh` failed at "the superseded paced runner invoked its stale command" (line ~3313). The PR only changes the Herdr files and their tests, and the same shard passed on main at the base commit. **Why it can fail:** the fixture starts a second runner with a 3-second launch floor (the minimum wait since the source's last launch). That runner sleeps for the rest of the floor and only then checks whether its registration was replaced (`fm_procevent_launch_floor_wait` in `bin/fm-procevent-lib.sh`). The test then waits for the claim and re-registers the source. If that takes longer than about 3 seconds after the first launch, the old runner wakes up, finds its registration still current, and runs the stale command. That produces the second log line the test reports. The CI shard was slow (this one test took 160 s). **Fix:** in `tests/fm-procevent.test.sh` I raised the superseded runner's floor from 3 to 15 seconds and added a comment explaining why. The floor now outlasts the fixture setup even on a loaded runner. Nothing else changed: the first launch and the later fresh-registration start still use a 3-second floor, and no product code changed. **Verification:** - The full test file can't give a reliable result on this machine (load average about 64 on 8 cores). It failed earlier, at the reconcile assertion around line 1680, before it reached this section. - I ran the changed section by itself (file setup plus the pacing-race block) five times with the fix: all passed, in about 9-13 s each. - The original code also passed five out of five, so the race didn't reproduce locally. The diagnosis rests on the code path and the CI log. - I haven't seen the full file or the CI shard pass with the fix yet * no-mistakes(test): Accept Claude folder-trust prompt via down+enter in live e2e * no-mistakes(document): Note unreadable Claude composer refusal in Herdr docs
* fix(bin): refuse a Herdr submit whose composer holds only a message tail Port upstream kunchenguid/firstmate 1d3ac67 (kunchenguid#5336) and widen its payload-proof gate to the fork's OMP away-mode supervisor. A Herdr submit used to type the literal and press Enter without proving the composer held the whole payload, so a long message could submit only its tail and still be reported delivered. Away-mode inject_msg had the same check-then-type race: an affirmative empty-composer read, then a human could type into the OMP supervisor before the escalation sent. The adapter now types only into a verified-empty composer and, before Enter, reads the selected composer back and requires it to show the typed payload. Comparison ignores whitespace and U+2063 (Claude's Herdr read-back drops the mark), and accepts pure Claude paste placeholders with no literal remainder. A suffix, a stale transcript head above a suffix, a placeholder with a literal remainder, or an unreadable composer withholds Enter; the draft is cleared with bounded Ctrl+U presses, reported send-failed when the clear is verified and unknown when it is not. The gate is identity-driven: native `agent get` identity `claude` for non-OMP sends, and the submit snapshot's proven `omp` identity on an idle or done baseline for OMP sends. Busy and blocked OMP baselines keep their exact session-event and ask-answer proofs; other harnesses and unidentified panes keep the type-then-Enter path. For the away-mode daemon, inject_msg's existing empty check stands and the send-time proof runs inside the backend call it already makes, so a refused escalation stays buffered with no daemon redesign. Composer-content extraction is fork-local: the shared Unicode-space normalization lands in bin/fm-composer-lib.sh, and the herdr adapter extracts the selected composer for bare `❯`/`›` prompts (unframed, or framed by `─` rules - verified as Claude's real composer on Herdr 0.9.0) and for the native OMP box. * no-mistakes(document): Document Herdr payload-proof submit behavior * no-mistakes(ci): Updated the OMP Herdr test fixtures to model the new payload-proof contract: executable pane reads now expose verified empty/full composer states, and the Bun width stub returns measured row widths. The CI failures were caused by fixtures returning unstructured/empty reads before the turn-start assertions * no-mistakes(ci): Fixed the CI-causing fixture/lint issues. The remote Herdr fixture now tracks composer text separately from the launch command, so pre-submit reads are genuinely empty and post-submit reads contain only typed payload; this restores the widened OMP payload-proof path and the remote lifecycle expectation. Added a narrowly scoped ShellCheck annotation for the generated Bun stub. Verified `shellcheck -x` on both changed fixtures, `git diff --check`, and the full `tests/fm-backend-herdr.test.sh` suite (including the previously failing literal-send case) pass * no-mistakes(ci): Fixed the probe-aware Herdr CI fixtures and behavior: identity now occupies call 1, literal-send stderr is replayed, the regression test is added, OMP composer fixtures model verified empty/full states, and the generated Bun stub is ShellCheck-clean. `shellcheck`, syntax checks, and diff validation pass; the requested send-turn test still hits its pre-existing bounded wake-lock timeout (exit 142) * no-mistakes(test): Bounded wake-lock persistence handling added * no-mistakes(test): Guard queue inspection against malformed lock hangs * no-mistakes(review): Guarded all lock acquisitions against invalid lock shapes * no-mistakes(review): Made malformed locks permanently fail-closed for all callers * no-mistakes(review): Guarded every lock acquisition before protected operations * no-mistakes(review): Bound bare composer parsing at known footer boundaries * no-mistakes(review): Hardened footer parsing and malformed-lock propagation * no-mistakes(review): Preserved multiline payloads and hardened footer boundaries * no-mistakes(test): Default omitted idle case and add nounset regression coverage * no-mistakes(document): Corrected Herdr payload-proof configuration documentation * no-mistakes(ci): Fixed both CI failures. The shared terminal-width helper now uses the Node width path for canonical Node runtimes even when the OMP entrypoint is separate, restoring valid remote OMP payload proof. The Herdr busy/blocked regression fixture now exports its typed-composer path on both sibling calls. Verified with composer, Herdr backend, and send-turn-start tests; syntax and diff checks pass * no-mistakes(ci): Fixed the shared OMP terminal-width boundary in bin/fm-composer-lib.sh. Runtime detection now behaviorally identifies Node-compatible canonical runtimes instead of relying only on basename, preserving Bun handling and preventing valid remote OMP composers from being classified unknown. Verified with fm-composer-lib tests, bash syntax, shellcheck, and git diff checks * no-mistakes(ci): Fixed the CI-1 root cause in bin/fm-composer-lib.sh: standalone compiled OMP entrypoints are now routed to the Node width path before any `-e` probe, so they cannot receive unsupported Bun evaluation flags. Verified fm-composer-lib and fm-tmux-submit-busy tests, bash syntax, shellcheck, and diff checks. CI-2’s remote-secondmate failure was a transient delivery-verdict race (unknown instead of expected missing-turn-start) with no reproducible code defect identified * no-mistakes(ci): Fixed tests/remote-herdr-fixture.sh to calculate OMP box and composer widths with the shared terminal-width helper, avoiding locale-sensitive Bash character counts that produced malformed Unicode box widths and `unknown` composer verdicts. Verified with bash -n, shellcheck, git diff --check, and direct parser validation showing a valid OMP candidate * no-mistakes(ci): Fixed CI-1 by preserving canonical `omp_bun`/`omp_bin` through remote control output into parent route metadata, and requiring complete OMP runtime metadata. Removed the incomplete remote-only proof bypass. Added e2e assertions for the metadata. `fm-backend-herdr.test.sh`, syntax, shellcheck, and the isolated active-turn stall test pass. CI-2’s isolated stall case passes on this branch; no lock-related code change was warranted * no-mistakes(ci): Fixed tests/remote-herdr-fixture.sh: OMP frame borders now dynamically match composer display width while preserving the parser-required `──╮` structure, and Ctrl+U clears the composer without marking it working. Verified bash syntax, shellcheck, diff checks, and `tests/fm-remote-secondmate-lifecycle-e2e.test.sh` (ALL TESTS PASSED). The fm-watch failure was non-deterministic: the standalone suite showed timing failures on repeated runs, while the reported turn-end case passed; no fm-watch code change was warranted
… tail (kunchenguid#5336) * fix(bin): refuse a Herdr submit that would send only a message tail A long typed payload can sit in the composer as a suffix, or as a paste placeholder plus a remainder, and the following Enter was still reported as delivered. Prove the selected composer holds the payload before Enter, and report failure when it does not. * no-mistakes(review): Scope Herdr payload proof to Claude, clear composer on refusal * no-mistakes(test): Clear refused Herdr composer drafts one wrapped row per press * no-mistakes(test): Accept Claude's multi-line paste placeholder in Herdr submit proof * no-mistakes(review): Accept Claude read-back that drops U+2063 in Herdr proof * no-mistakes(document): Document Herdr proof ignoring U+2063 operational mark * no-mistakes(ci): I made a one-line test change. The failing check comes from a timing race in an existing test that this PR doesn't touch. **What failed:** `tests/fm-procevent.test.sh` failed at "the superseded paced runner invoked its stale command" (line ~3313). The PR only changes the Herdr files and their tests, and the same shard passed on main at the base commit. **Why it can fail:** the fixture starts a second runner with a 3-second launch floor (the minimum wait since the source's last launch). That runner sleeps for the rest of the floor and only then checks whether its registration was replaced (`fm_procevent_launch_floor_wait` in `bin/fm-procevent-lib.sh`). The test then waits for the claim and re-registers the source. If that takes longer than about 3 seconds after the first launch, the old runner wakes up, finds its registration still current, and runs the stale command. That produces the second log line the test reports. The CI shard was slow (this one test took 160 s). **Fix:** in `tests/fm-procevent.test.sh` I raised the superseded runner's floor from 3 to 15 seconds and added a comment explaining why. The floor now outlasts the fixture setup even on a loaded runner. Nothing else changed: the first launch and the later fresh-registration start still use a 3-second floor, and no product code changed. **Verification:** - The full test file can't give a reliable result on this machine (load average about 64 on 8 cores). It failed earlier, at the reconcile assertion around line 1680, before it reached this section. - I ran the changed section by itself (file setup plus the pacing-race block) five times with the fix: all passed, in about 9-13 s each. - The original code also passed five out of five, so the race didn't reproduce locally. The diagnosis rests on the code path and the CI log. - I haven't seen the full file or the CI shard pass with the fix yet * no-mistakes(test): Accept Claude folder-trust prompt via down+enter in live e2e * no-mistakes(document): Note unreadable Claude composer refusal in Herdr docs
… tail (kunchenguid#5336) * fix(bin): refuse a Herdr submit that would send only a message tail A long typed payload can sit in the composer as a suffix, or as a paste placeholder plus a remainder, and the following Enter was still reported as delivered. Prove the selected composer holds the payload before Enter, and report failure when it does not. * no-mistakes(review): Scope Herdr payload proof to Claude, clear composer on refusal * no-mistakes(test): Clear refused Herdr composer drafts one wrapped row per press * no-mistakes(test): Accept Claude's multi-line paste placeholder in Herdr submit proof * no-mistakes(review): Accept Claude read-back that drops U+2063 in Herdr proof * no-mistakes(document): Document Herdr proof ignoring U+2063 operational mark * no-mistakes(ci): I made a one-line test change. The failing check comes from a timing race in an existing test that this PR doesn't touch. **What failed:** `tests/fm-procevent.test.sh` failed at "the superseded paced runner invoked its stale command" (line ~3313). The PR only changes the Herdr files and their tests, and the same shard passed on main at the base commit. **Why it can fail:** the fixture starts a second runner with a 3-second launch floor (the minimum wait since the source's last launch). That runner sleeps for the rest of the floor and only then checks whether its registration was replaced (`fm_procevent_launch_floor_wait` in `bin/fm-procevent-lib.sh`). The test then waits for the claim and re-registers the source. If that takes longer than about 3 seconds after the first launch, the old runner wakes up, finds its registration still current, and runs the stale command. That produces the second log line the test reports. The CI shard was slow (this one test took 160 s). **Fix:** in `tests/fm-procevent.test.sh` I raised the superseded runner's floor from 3 to 15 seconds and added a comment explaining why. The floor now outlasts the fixture setup even on a loaded runner. Nothing else changed: the first launch and the later fresh-registration start still use a 3-second floor, and no product code changed. **Verification:** - The full test file can't give a reliable result on this machine (load average about 64 on 8 cores). It failed earlier, at the reconcile assertion around line 1680, before it reached this section. - I ran the changed section by itself (file setup plus the pacing-race block) five times with the fix: all passed, in about 9-13 s each. - The original code also passed five out of five, so the race didn't reproduce locally. The diagnosis rests on the code path and the CI log. - I haven't seen the full file or the CI shard pass with the fix yet * no-mistakes(test): Accept Claude folder-trust prompt via down+enter in live e2e * no-mistakes(document): Note unreadable Claude composer refusal in Herdr docs
… tail (kunchenguid#5336) * fix(bin): refuse a Herdr submit that would send only a message tail A long typed payload can sit in the composer as a suffix, or as a paste placeholder plus a remainder, and the following Enter was still reported as delivered. Prove the selected composer holds the payload before Enter, and report failure when it does not. * no-mistakes(review): Scope Herdr payload proof to Claude, clear composer on refusal * no-mistakes(test): Clear refused Herdr composer drafts one wrapped row per press * no-mistakes(test): Accept Claude's multi-line paste placeholder in Herdr submit proof * no-mistakes(review): Accept Claude read-back that drops U+2063 in Herdr proof * no-mistakes(document): Document Herdr proof ignoring U+2063 operational mark * no-mistakes(ci): I made a one-line test change. The failing check comes from a timing race in an existing test that this PR doesn't touch. **What failed:** `tests/fm-procevent.test.sh` failed at "the superseded paced runner invoked its stale command" (line ~3313). The PR only changes the Herdr files and their tests, and the same shard passed on main at the base commit. **Why it can fail:** the fixture starts a second runner with a 3-second launch floor (the minimum wait since the source's last launch). That runner sleeps for the rest of the floor and only then checks whether its registration was replaced (`fm_procevent_launch_floor_wait` in `bin/fm-procevent-lib.sh`). The test then waits for the claim and re-registers the source. If that takes longer than about 3 seconds after the first launch, the old runner wakes up, finds its registration still current, and runs the stale command. That produces the second log line the test reports. The CI shard was slow (this one test took 160 s). **Fix:** in `tests/fm-procevent.test.sh` I raised the superseded runner's floor from 3 to 15 seconds and added a comment explaining why. The floor now outlasts the fixture setup even on a loaded runner. Nothing else changed: the first launch and the later fresh-registration start still use a 3-second floor, and no product code changed. **Verification:** - The full test file can't give a reliable result on this machine (load average about 64 on 8 cores). It failed earlier, at the reconcile assertion around line 1680, before it reached this section. - I ran the changed section by itself (file setup plus the pacing-race block) five times with the fix: all passed, in about 9-13 s each. - The original code also passed five out of five, so the race didn't reproduce locally. The diagnosis rests on the code path and the CI log. - I haven't seen the full file or the CI shard pass with the fix yet * no-mistakes(test): Accept Claude folder-trust prompt via down+enter in live e2e * no-mistakes(document): Note unreadable Claude composer refusal in Herdr docs
… tail (kunchenguid#5336) * fix(bin): refuse a Herdr submit that would send only a message tail A long typed payload can sit in the composer as a suffix, or as a paste placeholder plus a remainder, and the following Enter was still reported as delivered. Prove the selected composer holds the payload before Enter, and report failure when it does not. * no-mistakes(review): Scope Herdr payload proof to Claude, clear composer on refusal * no-mistakes(test): Clear refused Herdr composer drafts one wrapped row per press * no-mistakes(test): Accept Claude's multi-line paste placeholder in Herdr submit proof * no-mistakes(review): Accept Claude read-back that drops U+2063 in Herdr proof * no-mistakes(document): Document Herdr proof ignoring U+2063 operational mark * no-mistakes(ci): I made a one-line test change. The failing check comes from a timing race in an existing test that this PR doesn't touch. **What failed:** `tests/fm-procevent.test.sh` failed at "the superseded paced runner invoked its stale command" (line ~3313). The PR only changes the Herdr files and their tests, and the same shard passed on main at the base commit. **Why it can fail:** the fixture starts a second runner with a 3-second launch floor (the minimum wait since the source's last launch). That runner sleeps for the rest of the floor and only then checks whether its registration was replaced (`fm_procevent_launch_floor_wait` in `bin/fm-procevent-lib.sh`). The test then waits for the claim and re-registers the source. If that takes longer than about 3 seconds after the first launch, the old runner wakes up, finds its registration still current, and runs the stale command. That produces the second log line the test reports. The CI shard was slow (this one test took 160 s). **Fix:** in `tests/fm-procevent.test.sh` I raised the superseded runner's floor from 3 to 15 seconds and added a comment explaining why. The floor now outlasts the fixture setup even on a loaded runner. Nothing else changed: the first launch and the later fresh-registration start still use a 3-second floor, and no product code changed. **Verification:** - The full test file can't give a reliable result on this machine (load average about 64 on 8 cores). It failed earlier, at the reconcile assertion around line 1680, before it reached this section. - I ran the changed section by itself (file setup plus the pacing-race block) five times with the fix: all passed, in about 9-13 s each. - The original code also passed five out of five, so the race didn't reproduce locally. The diagnosis rests on the code path and the CI log. - I haven't seen the full file or the CI shard pass with the fix yet * no-mistakes(test): Accept Claude folder-trust prompt via down+enter in live e2e * no-mistakes(document): Note unreadable Claude composer refusal in Herdr docs
Intent
Fixes #3473
Herdr and Claude typed delivery can report success while submitting only the tail of a long message.
That affects away-mode digests and ordinary fm-send steers alike, so the fix has to cover every Claude-on-Herdr delivery, not only the digest path.
What Changed
fm_backend_herdr_send_text_submitinbin/backends/herdr.sh) now checks the composer before pressing Enter when the pane's native identity is Claude. This covers away-mode digests and ordinaryfm-sendsteers alike. The adapter types only into an empty composer. It sends Enter only when the composer shows the full payload (ignoring whitespace and the U+2063 operational mark), or shows only Claude[Pasted text #N]placeholders with nothing else. If the composer shows a shorter suffix, a placeholder followed by leftover literal text, or can't be read, Enter is not sent. The adapter then presses Ctrl+U until the composer reads empty and reportssend-failed, or reportsunknownif it can't confirm the composer is empty. Other harnesses keep the existing type-then-Enter path.docs/herdr-backend.mdanddocs/architecture.mddescribe the Claude pre-Enter check, the Ctrl+U clear, and thesend-failed/unknownverdicts.tests/fm-backend-herdr.test.shadds unit coverage for the check and the clear. The opt-in live e2e (tests/fm-herdr-submit-confirm-live-e2e.test.sh) now accepts Claude's folder-trust prompt and adds a check that a U+2063 away-supervisor payload is submitted and answered. Separately,tests/fm-procevent.test.shraises one fixture's launch floor from 3 to 15 seconds to fix a timing race seen in CI.Risk Assessment
✅ Low: The pre-Enter proof applies only to panes that native identity reports as Claude, follows the recorded user decisions (clear to a verified-empty composer before returning send-failed, return unknown otherwise, ignore U+2063, accept placeholder-only read-backs, leave other harnesses unchanged), covers both the digest path and the fm-send path through the shared send_text_submit, and is tested through the executable interface; the procevent change only lengthens a test fixture's launch floor to remove a timing race.
Testing
I ran the branch's existing live Herdr submit-confirm e2e, which passed for both the ASCII steer and the U+2063 away-supervisor payload. I also wrote a 7-scenario live script and ran it against real Claude Code in a throwaway fm-lab Herdr session, first on this branch and then on the base commit for comparison. On this branch all 7 scenarios passed. In the three long-message scenarios, the message starts with a code and Claude must reply with that code followed by a second code from the end, so a reply containing both proves Claude received the start of the message. On the base commit, S4 and S5 show the old unsafe behavior. S1-S3 also failed on base, but not from truncation: there, Claude received the whole message and declined to act on text it read as pasted content. The natural tail-only truncation from the issue did not reproduce on this host, so S5 creates it by wrapping send_literal so it types only the last 400 characters. Every lab session was torn down, and the worktree is clean. There is no UI change, so there are no screenshots; the saved pane screens are the visual record.
Evidence: Existing live e2e transcript (branch)
Source: Existing live e2e transcript (branch)
Evidence: Live scenario script
Source: Live scenario script
Evidence: Live scenario results (branch)
Source: Live scenario results (branch)
S1 PASS long single-line (2634 chars) verdict=empty reply=HEADA489TAILB10108 S2 PASS long multi-line (1847 chars) verdict=empty reply=HEADM27105TAILM6419 S3 PASS long U+2063 digest (2107 chars) verdict=empty reply=HEADO30445TAILO10526 S4 PASS pre-filled composer refused verdict=send-failed, draft kept, payload not typed S5 PASS tail-only composer refused verdict=send-failed composer=empty, no turn submitted S6 PASS clean retry after refusal verdict=empty reply=HEADT2650TAILT1836 S7 PASS non-Claude shell pane executed long command (verdict=unknown)Evidence: Baseline results on base commit 39f4c2a
Source: Baseline results on base commit 39f4c2af
Evidence: Base: payload typed onto operator draft and submitted
Source: Base: payload typed onto operator draft and submitted
❯ operator draft 4064 keep meReply with exactly NEVERSENT13605. ● NEVERSENT13605Evidence: Base: injected tail-only message submitted
Source: Base: injected tail-only message submitted
action. Reply with exactly TAILT1781-TRUNCATED and nothing else. ● TAILT1781-TRUNCATEDPipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
✅ **Review** - passed
✅ No issues found.
🔧 **Test** - 2 issues found → auto-fixed ✅
tests/fm-herdr-submit-confirm-live-e2e.test.sh:97- The branch's new folder-trust handler pressed Enter on Claude's trust prompt, which preselects "No, exit". Enter therefore quit Claude, and the live test failed with 'never registered an idle agent' when run from any untrusted checkout. Reproduced live from a fresh /tmp copy of HEAD. Fixed in the worktree (not committed) by sendingdown enter, with a comment explaining why; after the fix the test passes both from the fresh copy and from the worktree.tests/fm-backend-herdr.test.sh- Locally the file stops at test 18 ('a registered agent with a live agent-named descendant must stay live/alive') withcoreutils: unknown program 'pi'. This host's uutils coreutils can't be run under another name, and the base commit fails the same way, so it is a host limitation unrelated to this change. The branch's new unit cases in this file therefore rely on CI.FM_HERDR_SUBMIT_CONFIRM_LIVE=1 bash tests/fm-herdr-submit-confirm-live-e2e.test.sh(worktree, before and after the trust-prompt fix)Same live e2e from an untrusted /tmp copy of HEAD, before the fix (fails: Claude exits at the trust prompt) and after it (passes)bash live-long-steer-scenarios.sh <worktree> target(evidence dir): long steer, draft already in the composer, injected tail-only composer plus retry, shell panebash live-long-steer-scenarios.sh <base-39f4c2af-export> base: same scenarios on the base commit for comparisonbash tests/fm-backend-herdr.test.shon target and base (stops early at test 18 on this host, same on both)🔧 Fix applied.
✅ Re-checked - no issues remain.
FM_HERDR_SUBMIT_CONFIRM_LIVE=1 bash tests/fm-herdr-submit-confirm-live-e2e.test.sh(real Claude Code 2.1.280 on herdr 0.9.1 in an isolated fm-lab session: ASCII steer plus a U+2063 away-supervisor payload)ROOT=$PWD EVID=<evidence> bash <evidence>/long-delivery-scenarios.sh: 7 live scenarios (S1-S7) through bin/fm-herdr-lab.sh against real Claude, followed by teardownSame scenario script run against a /tmp git-archive copy of base 39f4c2af as a before/after baselineRead the saved lab pane screens for S1-S6 (the reply rendered, the operator's draft was kept, the tail was never submitted)✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.