Skip to content

feat(session-start): arm direct Telegram receiver#7

Merged
Freudator86 merged 8 commits into
mainfrom
fm/tg-direct-session-start
Jul 21, 2026
Merged

feat(session-start): arm direct Telegram receiver#7
Freudator86 merged 8 commits into
mainfrom
fm/tg-direct-session-start

Conversation

@Freudator86

Copy link
Copy Markdown
Owner

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

  • Added a session-start Telegram receiver arm path that emits bin/fm-tg-recv-arm.sh guidance when config/telegram.env is present.
  • Introduced bin/fm-tg-recv-arm.sh to supervise the local receiver as a tracked background task with lock recovery, output relay preservation, and resolved home environment export.
  • Extended arm command policy, docs, and focused tests for the Telegram receiver arm flow and session-start guidance.

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"}

## 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 requires fm_pid_identity to succeed before it ever relays captured output. If config/fm-tg-recv.sh exits 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 deletes child_out, dropping the CAPTAIN-TELEGRAM line. 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 run bin/fm-tg-recv-arm.sh only 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 same shell &, pipeline, or bundled-command mistake this system already blocks for fm-watch-arm.sh, so add bin/fm-tg-recv-arm.sh to 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 records receiver-path, but the shared lock cleanup only removes known files like watcher-path, so every successful receiver run leaves a .tg-recv.lock.owner.* directory behind in state. 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.lock but not yet written receiver metadata will fail immediately, because healthy_receiver cannot 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, cleanup removes .tg-recv.lock immediately 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. If config/fm-tg-recv.sh traps 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 resolves FM_HOME internally but starts the private receiver without exporting that resolved home. The session-start guidance tells agents to run plain bin/fm-tg-recv-arm.sh, and in that common path FM_HOME may be only a shell variable set by fm-wake-lib.sh, not an environment variable inherited by config/fm-tg-recv.sh; local receiver implementations that rely on the standard FM_HOME contract will then run with an empty/default home. Export the resolved FM_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 sees healthy_receiver fail and calls clear_dead_recorded_receiver_lock without first relaying output-path, deleting the only pointer to the captured CAPTAIN-TELEGRAM line. Relay the recorded output before clearing any dead matching receiver lock, not only from attach_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.lock but before recording full receiver metadata leaves a partial live-lock that future arms cannot steal or clear. attach_if_receiver_becomes_healthy times out, the second create still fails because the symlink exists, and clear_dead_recorded_receiver_lock refuses to remove it because fm-home/receiver-path are 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.sh
  • bash tests/fm-tg-recv-arm.test.sh
  • bash tests/fm-session-start.test.sh
  • bash tests/fm-arm-pretool-check.test.sh
  • Evidence transcript: created a temporary FM_HOME with config/telegram.env and executable config/fm-tg-recv.sh, ran bin/fm-session-start.sh, ran bin/fm-tg-recv-arm.sh, and ran bin/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) ✅
  • ⚠️ linter found issues (exit code 127)

🔧 Fix: Captain, verify lint passes
1 warning still open:

  • ⚠️ linter found issues (exit code 127)

🔧 Fix: Captain, verify lint passes
1 warning still open:

  • ⚠️ linter found issues (exit code 127)

🔧 Fix: Captain, verify lint passes
1 warning still open:

  • ⚠️ linter found issues (exit code 127)

🔧 Fix: Captain, lint passes
1 warning still open:

  • ⚠️ linter found issues (exit code 127)

🔧 Fix: Captain, lint passes
✅ Re-checked - no issues remain.

✅ **Push** - passed

✅ No issues found.

@Freudator86
Freudator86 merged commit a8ce717 into main Jul 21, 2026
5 checks passed
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