feat(session-start): arm direct Telegram receiver#7
Merged
Conversation
added 8 commits
July 21, 2026 02:01
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intent
Fold firstmate's direct Telegram receiver into the normal session-start routine instead of requiring a manual per-session start when config/telegram.env exists. Keep the change narrowly scoped: do not change the local Telegram receive/send logic, only add tracked session-start arming behavior that matches the existing watcher discipline by using its own tracked background task, never shell '&' or a bundled command. Update AGENTS.md to document the automated behavior and add focused tests for the new receiver arm wrapper and the session-start guidance.
What Changed
bin/fm-tg-recv-arm.shguidance whenconfig/telegram.envis present.bin/fm-tg-recv-arm.shto supervise the local receiver as a tracked background task with lock recovery, output relay preservation, and resolved home environment export.Risk Assessment
✅ Low: Captain, the change is narrowly scoped to session-start Telegram receiver arming, lock/output handling, and matching focused tests, with no material merge-blocking risks found in the reviewed diff.
Testing
The baseline full shell suite was already green; I reran the focused Telegram receiver, session-start, and pretool policy tests, then captured a product-level CLI transcript proving configured session start surfaces the Telegram arm command, the wrapper starts and relays receiver output, and unsafe shell-background arming is rejected.
Evidence: Telegram session-start transcript
## session-start with config/telegram.env and executable receiver TELEGRAM RECEIVER -------------------------------------------------------------------------------- TELEGRAM_RECEIVER: active - run bin/fm-tg-recv-arm.sh as its own tracked background task, never shell &; it starts or attaches to this home's receiver === SUPERVISION OPERATING INSTRUCTIONS - primary harness: codex ## tracked Telegram receiver arm wrapper telegram receiver: started pid=2117294 CAPTAIN-TELEGRAM: demo message from direct receiver ## pretool policy rejects shell-backgrounded Telegram arm {"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny"},"systemMessage":"[watcher-background] a protected watcher command cannot run in an asynchronous shell list or through nohup/disown"} {"decision":"deny","reason":"[watcher-background] a protected watcher command cannot run in an asynchronous shell list or through nohup/disown"}Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
🔧 **Review** - 2 issues found → auto-fixed (6) ✅
bin/fm-tg-recv-arm.sh:126- The wrapper starts the receiver in the background and immediately requiresfm_pid_identityto succeed before it ever relays captured output. Ifconfig/fm-tg-recv.shexits quickly, which is plausible when a Telegram message is already pending, the PID may be gone by this point; the wrapper then prints a failure and deleteschild_out, dropping theCAPTAIN-TELEGRAMline. Handle an already-exited child by waiting and replaying its output before treating identity lookup as fatal.bin/fm-session-start.sh:315- Session start now tells agents to runbin/fm-tg-recv-arm.shonly as a tracked background task, but the command-position seatbelt still protects only the watcher scripts. That leaves the new receiver arm vulnerable to the sameshell &, pipeline, or bundled-command mistake this system already blocks forfm-watch-arm.sh, so addbin/fm-tg-recv-arm.shto the protected arm policy and cover the deny/allow shapes.🔧 Fix: Captain, fix Telegram arm regressions
2 warnings still open:
bin/fm-tg-recv-arm.sh:146- The receiver lock recordsreceiver-path, but the shared lock cleanup only removes known files likewatcher-path, so every successful receiver run leaves a.tg-recv.lock.owner.*directory behind instate. Either reuse a cleanup-known field name or teach the lock helper to remove this metadata file.bin/fm-tg-recv-arm.sh:96- A second arm that starts while the first wrapper has created.tg-recv.lockbut not yet written receiver metadata will fail immediately, becausehealthy_receivercannot confirm the lock until lines 142-146 complete. Treat this as an in-progress lock and retry briefly so duplicate tracked arms attach instead of reporting a false failure.🔧 Fix: Captain, fix Telegram receiver lock races
1 warning still open:
bin/fm-tg-recv-arm.sh:127- Captain,cleanupremoves.tg-recv.lockimmediately after sending TERM to the child, without waiting for the receiver to actually exit or verifying the lock still points to this wrapper's owner dir. Ifconfig/fm-tg-recv.shtraps TERM or has a slow child process, a later arm can acquire the now-empty lock and start a second receiver while the old one is still running. Wait boundedly for the child to exit and only release the lock when this wrapper still owns it, or leave the lock for stale/health detection.🔧 Fix: Captain, fix Telegram receiver cleanup race
1 warning still open:
bin/fm-tg-recv-arm.sh:178- If cleanup cannot terminate a receiver that traps TERM, it intentionally leaves the live lock in place but still deletes the only output capture. A later arm will attach to that live receiver and just wait for it to disappear, so the eventual CAPTAIN-TELEGRAM line is written to an unlinked file and never reaches the harness. Keep the capture relayable while leaving the lock held, or avoid leaving a live receiver whose stdout is no longer supervised.🔧 Fix: Captain, preserve Telegram receiver output relay
1 warning still open:
bin/fm-tg-recv-arm.sh:211- Captain, the wrapper resolvesFM_HOMEinternally but starts the private receiver without exporting that resolved home. The session-start guidance tells agents to run plainbin/fm-tg-recv-arm.sh, and in that common pathFM_HOMEmay be only a shell variable set byfm-wake-lib.sh, not an environment variable inherited byconfig/fm-tg-recv.sh; local receiver implementations that rely on the standardFM_HOMEcontract will then run with an empty/default home. Export the resolvedFM_HOME(and likely the resolved state/config overrides if intentional) when launching the receiver.🔧 Fix: Captain, export Telegram receiver home env
2 warnings still open:
bin/fm-tg-recv-arm.sh:130- Captain, a later arm can still drop an orphaned Telegram message after the original wrapper dies. If the wrapper leaves a recorded receiver lock and output capture behind, and the receiver exits before any new attach wrapper is running, this top-level path seeshealthy_receiverfail and callsclear_dead_recorded_receiver_lockwithout first relayingoutput-path, deleting the only pointer to the capturedCAPTAIN-TELEGRAMline. Relay the recorded output before clearing any dead matching receiver lock, not only fromattach_and_wait.bin/fm-tg-recv-arm.sh:133- Captain, the receiver acquires the raw create-only lock, so a wrapper killed after publishing.tg-recv.lockbut before recording full receiver metadata leaves a partial live-lock that future arms cannot steal or clear.attach_if_receiver_becomes_healthytimes out, the second create still fails because the symlink exists, andclear_dead_recorded_receiver_lockrefuses to remove it becausefm-home/receiver-pathare missing. Use the stale-aware lock acquisition path or explicitly clear stale partial owner locks after the freshness window.🔧 Fix: Captain, harden Telegram receiver lock recovery
✅ Re-checked - no issues remain.
✅ **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 already completed before this validation: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"bin/fm-session-start.shbash tests/fm-tg-recv-arm.test.shbash tests/fm-session-start.test.shbash tests/fm-arm-pretool-check.test.shEvidence transcript: created a temporary FM_HOME withconfig/telegram.envand executableconfig/fm-tg-recv.sh, ranbin/fm-session-start.sh, ranbin/fm-tg-recv-arm.sh, and ranbin/fm-arm-pretool-check.sh --command 'bin/fm-tg-recv-arm.sh &'into/tmp/no-mistakes-evidence/01KY16JYXAM6JWYGECYEC8ZTS8/telegram-session-start-transcript.txt✅ **Document** - passed
✅ No issues found.
🔧 **Lint** - 1 issue found → auto-fixed (5) ✅
🔧 Fix: Captain, verify lint passes
1 warning still open:
🔧 Fix: Captain, verify lint passes
1 warning still open:
🔧 Fix: Captain, verify lint passes
1 warning still open:
🔧 Fix: Captain, lint passes
1 warning still open:
🔧 Fix: Captain, lint passes
✅ Re-checked - no issues remain.
✅ **Push** - passed
✅ No issues found.