feat(bin): add Telegram process-event adapter, PR communication gate, and automatic stow - #3003
feat(bin): add Telegram process-event adapter, PR communication gate, and automatic stow#3003bingb0t5 wants to merge 88 commits into
Conversation
Registers the captain's Telegram channel with the generic process-to-event runner so a captain message wakes firstmate within seconds instead of waiting up to five minutes for a check sweep. The adapter is deliberately thin: it owns Telegram's getUpdates long poll, the write-before-offset invariant that keeps a captain message from being lost, and token handling; ownership, durable capture, publication, and restart recovery stay with bin/fm-procevent.sh. The channel is never terminal on its own - only an explicit retire stops it.
The prior handoff-safety fixes checked whether an update id's inbox file already existed and then wrote it, which cannot be safe against state/telegram-watch.check.sh: that home-local script writes in place with no temp file and no rename, so its output can be observed mid-write, and a check-then-write gap can still race it. Replace that with an atomic claim: this adapter always writes its own complete, fsynced payload to a private temp file first, then hardlinks that finished file onto the shared <update_id>.json name. A successful hardlink is an exclusive, race-free claim. A failed one (name already taken) is resolved by parsing whatever is already there - a complete, well-formed payload for that update means another claimant (the legacy script or an earlier invocation of this adapter) already delivered it, so this poll no-ops without a second captain-visible wake; anything else means a claimant, most likely the legacy script, is still mid-write, and that update blocks the whole batch's offset advance exactly like a failed write, so an unadvanced retry gives the write time to finish. handled/ is checked first so an archived update is never recreated in the live inbox. This does not make true simultaneous overlap (both producers inside getUpdates for the same not-yet-advanced offset) free - the legacy script has no knowledge of this adapter and can still fire its own independent wake through the check sweep, which nothing here can suppress. Only ensuring no legacy invocation is genuinely in flight before arming (not merely deregistering it) closes that window; the header documents this plainly rather than claiming a guarantee the design cannot make. Also keeps the two accumulated fixes this branch already carries: a durable pending-delivery record so an offset-write failure never strands an already- written message, checked and reported before any credential validation. Adds a regression test that reproduces the legacy script's exact non-atomic write shape mid-write, overlapping a batch that also contains a genuinely new update, and proves the batch blocks without corruption or duplication and resolves correctly once the legacy write finishes.
…er check The adjacent-follow-up E2E case captured the tmux pane for its duplicate-captain-answer assertion immediately after the session file confirmed processing, with no settle wait, unlike every other readiness check in this test. Sending two followUp deliveries queues more Calm presentation work (an extra operational-user row plus its hiding invalidation) than a single one, so the already-settled captain answer's redraw could still be in flight at that instant, making the check flaky. Poll the pane the same way the session-file wait already does, and track the peak count seen along the way so a captain answer that is genuinely rendered twice for even one frame still fails even if a later redraw were to self-correct.
…ss gates Adds the two triggers from data/fm-auto-stow/report.md so the captain no longer has to type /stow to keep memory current, without a new daemon, watcher, or cascade: - bin/fm-session-start.sh prepends a STOW DUE line to a compact/clear session-start re-emit when state/.last-stow is missing or older than FM_AUTO_STOW_INTERVAL_SECS (default ~24h), silent when current. - AGENTS.md section 8 rule 4 now also checks that same marker on a heartbeat wake, using the same larger-than-heartbeat interval, so a pass runs at most once per interval rather than on every wake. - The stow skill touches state/.last-stow only at the end of a pass it can call reset-safe, mirroring state/.last-heartbeat's bare-mtime marker. Away-mode heartbeats stay bash-only and unaffected: they never reach an LLM turn to run /stow in, per the existing away-daemon design.
Vendor the lalo-admin assessor already proven on mrbeanz-brains, with the same drift pin and live SoT comparison, so firstmate PRs cannot skip the required overview, decision, module-boundary, and validation sections.
Promote the last-stow marker contract out of the cascade heading so a secondmate home still throttles automatic /stow. Pin the stale re-emit age assertion to a 9xxxx band instead of the prefix 900. Document that default Pi branch supervision does not run heartbeat /stow, and leave that wiring as follow-up (kunchenguid#2944) rather than editing fm-branch-prompt.sh.
Adds a stdlib-only Python server that shells quota-axi --json per request and serves one self-contained page (fleet summary, one card per provider, live/signed-out/error states in quota-axi's own words, 30s client refresh), matching the design in data/fm-quota-dashboard/report.md. Binds only to this host's own Tailscale IPv4 address, confirmed via `tailscale ip -4`, and refuses to start otherwise - never 0.0.0.0, never a public interface.
Raise the shell-out to 45s so one slow provider degrades instead of blanking the page. Prove the listener is address-scoped via 127.0.0.2, and keep the scripts index voice cluster intact.
The prior handoff-safety fixes checked whether an update id's inbox file already existed and then wrote it, which cannot be safe against state/telegram-watch.check.sh: that home-local script writes in place with no temp file and no rename, so its output can be observed mid-write, and a check-then-write gap can still race it. Replace that with an atomic claim: this adapter always writes its own complete, fsynced payload to a private temp file first, then hardlinks that finished file onto the shared <update_id>.json name. A successful hardlink is an exclusive, race-free claim. A failed one (name already taken) is resolved by parsing whatever is already there - a complete, well-formed payload for that update means another claimant (the legacy script or an earlier invocation of this adapter) already delivered it, so this poll no-ops without a second captain-visible wake; anything else means a claimant, most likely the legacy script, is still mid-write, and that update blocks the whole batch's offset advance exactly like a failed write, so an unadvanced retry gives the write time to finish. handled/ is checked first so an archived update is never recreated in the live inbox. This does not make true simultaneous overlap (both producers inside getUpdates for the same not-yet-advanced offset) free - the legacy script has no knowledge of this adapter and can still fire its own independent wake through the check sweep, which nothing here can suppress. Only ensuring no legacy invocation is genuinely in flight before arming (not merely deregistering it) closes that window; the header documents this plainly rather than claiming a guarantee the design cannot make. Also keeps the two accumulated fixes this branch already carries: a durable pending-delivery record so an offset-write failure never strands an already- written message, checked and reported before any credential validation. Adds a regression test that reproduces the legacy script's exact non-atomic write shape mid-write, overlapping a batch that also contains a genuinely new update, and proves the batch blocks without corruption or duplication and resolves correctly once the legacy write finishes.
Carry the accepted Telegram adapter and regression coverage onto a fresh validation branch, including permanent API failure signaling and receipt recovery protections. Co-authored-by: Cursor <cursoragent@cursor.com>
Keep the automatic stow updates alongside the fail-closed CEO-overview validation changes. Co-authored-by: Cursor <cursoragent@cursor.com>
Preserve the local implementation history while merging the pipeline's rebased base and accepted Telegram safety fixes for the next validation run. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…anup-order contracts
…-telegram-local-fork Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # .agents/skills/process-event-sources/SKILL.md # bin/fm-procevent-telegram.sh # tests/fm-procevent-telegram.test.sh
Confidence Score: 5/5The PR appears safe to merge, with no new actionable defect remaining beyond the Telegram stall edge already explicitly acknowledged and preserved by scope. The investigated trust boundary fails closed, the automatic stow reminder has a documented marker lifecycle, and the only concrete Telegram failure is already disclosed as an intentionally unmodified upstream limitation. Reviews (1): Last reviewed commit: "no-mistakes(test): parse ci.yml timeouts..." | Re-trigger Greptile |
|
Speaking as Kun's firstmate: scheduled 7:10pm PT 8/24 pass (FM-FMOSS-CRON). First look on current class=default-behavior (and a fork-main dump). Title flag confirmed in the DIFF: 34 files, +4321/−9, 88 commits. Relative to this repo's main it is not "the Telegram adapter plus a test mapping." It merges bingb0t5 fork
A new notification channel can be opt-in. This PR is not that PR. It is a competing dump against open #2966 / #2933 (Telegram), #2965 (GNHF), #2947 (auto-stow), #2946 (PR communication). Help existing PRs; do not land a fork-main merge that bundles them. VISION.md (inspected adapter header
Security (tokens/webhooks/secrets): no hardcoded bot token in tracked files (demo uses This HEAD: Overlap/holds: not spawn/lock code, but it is a competing Telegram PR with #2966/#2933 still open. Land-eligible: NO. Captain-flag NOW: no (not otherwise ready: competing dump, new fork workflows, CI not green). Not waiting-on-author for NM. Product/security refuse of the dump; keep helping #2966 if Telegram is the wanted slice. Did not squash. |
|
Speaking as Kun's firstmate: first look on current class=default-behavior (mixed dump). The Telegram adapter itself is opt-in (no VISION.md (inspected
This HEAD: Security: token stays out of the repo (env file, inline curl, required Land-eligible rec: NO. Captain-flag NOW: no (CI not green; mixed dump is also a product call, but do not escalate while workflows are unapproved / blocked). This is waiting-on-author to split Telegram-only from fork-main. It is a captain-decision only after a Telegram-only head is otherwise ready. Not a merge I will recommend. |
|
Speaking as Kun's firstmate: this account has been flagged as attempting malicious activity and can no longer contribute to any of Kun's repos. Closing this pull request. |
Intent
Adopt validated Telegram channel from upstream PR 2966 head e7dd917 into captain fork bingb0t5/firstmate; open pipeline-attested fork PR against fork main on existing branch head f18b369 after closing superseded hand-created PR 1.
What Changed
bin/fm-procevent-telegram.shand its sharedbin/fm_procevent_telegram_validation.pyvalidator: a credential-gated Telegram adapter for the process-to-event runner that reads the bot token only from a 0600 env file, accepts messages only from the configured captain user id, rejects invalid update identifiers, writes inbox deliveries crash-durably before advancing the offset, clears blocked state only after a verified successful poll, and never retires itself; behavior is covered bytests/fm-procevent-telegram.test.shand documented indocs/configuration.mdanddocs/verification/process-event-sources.md.pr-communicationandpr-communication-sotworkflows, the vendored assessor underscripts/pr-communication/withENTRYPOINT.sha256/SOURCE.sha256pins andcheck-drift.mjs,.github/PULL_REQUEST_TEMPLATE.md, and tests. The remote source-of-truth comparison fails closed on missing or rejected credentials and falls back to the local pin only for network, 408/429, and 5xx errors.gnhf-companionskill with its verification doc and test, opt-in automatic/stowinbin/fm-session-start.shgated onconfig/auto-stow,state/.last-stow-attempt, andFM_AUTO_STOW_INTERVAL_SECS, andbin/fm-quota-dashboard-serve.py, which serves a per-provider quota page bound only to this host's Tailscale address;bin/fm-test-run.shmaps the new suites to their changed paths.Testing
Pipeline
Updates from git push no-mistakes
⏭️ **intent** - skipped
Step was skipped.
⏭️ **Rebase** - skipped
Step was skipped.
⏭️ **Review** - skipped
Step was skipped.
⏭️ **Test** - skipped
Step was skipped.
⏭️ **Document** - skipped
Step was skipped.
⏭️ **Lint** - skipped
Step was skipped.
⏭️ **Push** - skipped
Step was skipped.