feat(bin): relay trust hardening, usage ledger, and fleet ops tooling - #3533
derickdsouza wants to merge 11 commits into
Conversation
…guid#149) (#1) Extend fmx_poll_shim_valid to honor config/harbor-telegram-hitl.poll-shim.registration.json so harbor telegram-hitl can replace x-watch.check.sh without watcher rejection. Enforce relay ack-before-long-work: fm-x-reply records x-context/<id>.answered.json on the initial answer; fm-spawn refuses while any open x-inbox row lacks that marker. Co-authored-by: Cursor <cursoragent@cursor.com>
Port of derickdsouza/firstmate PR #2 onto org main: the explicit bin/fm-x-watch-register.sh binding command with colocated tests, the stock/registered validator split, watcher exec-target dispatch with the re-register hint, and bootstrap preservation of a still-matching binding. Mainline harbor manifest validation is kept as fmx_poll_shim_manifest_valid and chained into fmx_poll_shim_valid, so harbor-installed and CLI-registered alternates authenticate through one dispatch-time check.
Port of derickdsouza/firstmate PR #3 in full: deterministic length-bound fm-untrusted-text-lib.sh neutralizing injection role-markers, HTML comments, and operational-prefix impersonation, wired into the relay mention stash path with claim-and-dismiss for empty-after-sanitize mentions, batched chain sanitizing bounded by string size rather than entry count, and colocated coverage across locale, multibyte, fixed-point, and budget cases.
…ontract Port of the parked fm/adopt-constitution-layering branch (7ba0d37): ship and promoted-scout briefs carry the layering discipline - shared worker-facing rule files are law and are never same-name overridden by a project; project specialization lives in distinctly-namespaced local files. Scaffold tests pin the contract on both brief paths.
Bring in 9 upstream commits (relay media, spawn/startup/wake fixes, Bearings concurrency, pi supervision, CI shards) while preserving fleet commits: HITL poll-shim trust, ack-before-spawn, sanitizer, constitution layering. Conflicts resolved in docs/configuration.md and tests/fm-x-mode.test.sh (fleet untrusted-text sanitization kept). Co-authored-by: Cursor <cursoragent@cursor.com>
…-09-02 merge(upstream): sync kunchenguid/firstmate main (9 commits)
Process-alive is not health. A remote helper with unacked steering-inbox mail past the ladder, or a repeating Node missing-module capture after a harness upgrade under a live process, is stuck. launch now replaces stuck helpers instead of reusing them. Session-start liveness respawns those stuck remotes. The parent watcher ticks the remote inbox ladder on the host. Spawn/bootstrap reconcile the reply runner after arming so a registered source is not left without a child.
…down (kunchenguid#4) (kunchenguid#5) Co-authored-by: Derick D'Souza <derick@nivasritech.com>
* feat(bin): fleet when-watch poller with cron dedupe and single-flight lock * feat(bin): agent login probe with login-shell PATH resolution Cron hardening: default USER/LOGNAME when unset and prepend the standard CLI install roots (~/.local/bin, nvm per-version bin, ~/.grok/bin, Homebrew) to PATH so a minimal cron environment no longer misreports installed CLIs as absent. Also fix the grok and cursor checks, whose has_stdout ... | grep pipeline could never match because has_stdout captures the output and returns only a status; they now grep the real command output. Every expansion is set -u safe under env -i.
kunchenguid#7) Grok and cursor cannot neutralize AGENTS.md/CLAUDE.md, so they cannot gate fleet repos. Keep the chain in ~/.no-mistakes/config.yaml and out of the tracked repo override.
Confidence Score: 4/5The PR appears safe to merge, with a non-blocking relay-sanitizer hardening gap around zero-width joiners. The new sanitizer leaves U+200D-containing role markers intact because it deliberately preserves the character while recognizing only contiguous ASCII role names; no blocking failure remains after accounting for the PR’s explicitly acknowledged tradeoffs. Files Needing Attention: bin/fm-untrusted-text-lib.sh
|
|
|
||
| fm_untrusted_strip_format_chars_var() { | ||
| local text=$1 c | ||
| # Strip Cf / bidi / BOM / soft-hyphen hides. Do not strip U+200D ZWJ. |
There was a problem hiding this comment.
ZWJ bypasses role neutralization
A line-leading role marker containing U+200D, such as system:, survives format stripping and fails the contiguous ASCII role-name match, leaving visually disguised role instructions in the agent-facing inbox and weakening the new prompt-injection boundary.
How this was verified: Tracing a U+200D-containing role prefix through format stripping and contiguous role matching shows it reaches the inbox without replacement.
|
Opened against the wrong parent. This home's no-mistakes gate is initialized to push derickdsouza/firstmate and PR kunchenguid/firstmate, so the run bundled ten already-merged nivasritech commits onto a stale fork main. The actual #7 change is the one-commit docs PR against nivasritech/firstmate: https://github.com/nivasritech/firstmate/pull/8 |
Intent
Work the only open nivasritech/firstmate GitHub issue (#7): set the global no-mistakes reviewer chain to [pi, claude] so validation can launch in repos with AGENTS.md/CLAUDE.md.
Captain decisions already made on that issue:
This change records that policy in docs/configuration.md, CONTRIBUTING.md (a pointer to Gate defaults), and a comment on .no-mistakes.yaml explaining why there is no repo-level agent override. The operator-local ~/.no-mistakes/config.yaml on this home was updated to agent: [pi, claude] separately and is not part of the tracked diff.
What Changed
bin/fm-untrusted-text-lib.shsanitizes untrusted mention/thread text before the inbox stash (sanitized-to-empty mentions are claimed and dismissed at the relay instead of re-offered),fm-spawn.shrefuses to launch work while a stashed mention lacks itsansweredmarker (recorded byfm-x-reply.shon the initial answer), and the newbin/fm-x-watch-register.shCLI plus unifiedfm-x-lib.shvalidators let the watcher and bootstrap dispatch a hash-bound alternate poll shim (harbor telegram-hitl manifest or explicit state-local registration) alongside the stock generated shim.fm-spawn.shbinds each task's harness session log into meta (usage_source=/usage_log=for claude, codex, and pi) andfm-teardown.shappends a best-effortfm-usage.v1token line todata/usage.jsonl; newfm-secondmate-health-lib.shclassifiers let the bootstrap liveness sweep and the watcher's remote inbox tick replace alive-but-stuck remote helpers (unacked steering mail past the ladder, or a repeating Node missing-module capture) instead of reusing them.fm-fleet-watch-poller.shkeeps the when-watches armed andfm-agent-login-probe.shprobes pi/grok/cursor-agent/claude login health on mac and VPS;fm-dod-lib.shgains a worker-facing rule-file layering line rendered into both ship briefs (fm-brief.sh) and promoted ship instructions (fm-promote.sh);docs/configuration.md"Gate defaults" records the global no-mistakes reviewer chain[pi, claude]with a CONTRIBUTING pointer and a.no-mistakes.yamlcomment explaining why the tracked file sets noagent:; new test suites cover the sanitizer, usage ledger, poll-shim registration, health classifiers, and fleet poller.Risk Assessment
Testing
Parsed the tracked .no-mistakes.yaml with PyYAML to prove the change is comments-only (semantic model identical to base, no agent: override, all existing pins intact), verified every intent-mandated policy element in the docs/configuration.md Gate defaults section plus the CONTRIBUTING.md pointer and resolvable cross-references, confirmed via source inspection that bootstrap never writes the global config, ran the adjacent fm-gate-refuse suite (7/7 pass), and confirmed the operator-local global chain is agent: [pi, claude] with this very pi session in an AGENTS.md/CLAUDE.md repo as end-to-end proof; all checks passed with the worktree left clean.
Evidence: Gate-defaults reviewer-chain evidence (rendered doc sections + verification transcript)
Source: Gate-defaults reviewer-chain evidence (rendered doc sections + verification transcript)
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
.agents/skills/fmx-respond/SKILL.md- branch carries 10 commit(s) that exist on your local main branch but were never pushed to origin/main; rebasing would bundle this unrelated work (45 file(s)) into the PR:Push main to origin, or rebase your branch onto origin/main, before gating.
bin/fm-agent-login-probe.sh:87- check_mac_claude reports OK when ~/.claude/.credentials.json is absent but ~/.claude/plugins exists (line 87), or wheneverclaude --versionsucceeds (line 91) -- neither proves login, and --version works offline for a fully logged-out CLI. Concrete path: Claude Code session expires/credentials removed, plugins dir persists -> probe prints 'OK claude@mac' and exits 0, so the login-watch/fleet probe never flags claude for re-login unless the CLI is entirely uninstalled. The script's stated contract ('exit 1 when at least one target needs re-login') is silently unenforceable for one of its four targets. Remedy requires choosing a real login signal (credentials-only check, or a non-interactive auth-status command), which is a deliberate probe-design decision, so it needs user input rather than an auto-fix.bin/fm-x-reply.sh:342- A dry-run reply (FMX_DRY) writes the x-context/<id>.answered.json marker that disarms fmx_inbox_spawn_guard without any relay POST. This is documented deliberately (docs/configuration.md: 'records that marker on the initial answer (including dry-run)'), presumably so preview homes are not wedged. Residual tradeoff worth acknowledging: fmx_load_config lets an explicit FMX_DRY_RUN environment variable win over .env, so any process -- including the agent the fix: accept landed squash-merged PR heads #149 ack-before-long-work guard constrains -- can runFMX_DRY_RUN=1 fm-x-reply.sh <id> ...to mark the ack and pass the spawn refusal while the Telegram question remains unanswered at the relay. No change requested; noting the documented weakening and its reachable bypass.bin/fm-usage-lib.sh:71- The codex binding pins usage_log to the spawn-date directory (date +%Y/%m/%d), so a codex task whose session continues past local midnight has its later session files written into the next date directory and they are never matched at teardown -- a silent token undercount in data/usage.jsonl (fm_usage_collect_rows finds nothing new; no error, no usage_source=missing since the old-date files still match). The header documents the v1 reused-worktree overcount limitation but not this midnight-rollover undercount; a one-line contract note (or a follow-up date-range glob) would make the best-effort schema honest.✅ **Test** - passed
✅ No issues found.
python3 PyYAML semantic verification:.no-mistakes.yamlparses, has no top-levelagentkey, noagent_configkey,disable_project_settings: true,commands.lint/test.evidence.store_in_repopins intact, and parsed model equals base commit 8988af2's model (comments-only tracked diff)bash tests/fm-gate-refuse.test.sh(adjacent gate-agent refusal suite around the tracked no-mistakes config boundary) - 7/7 passManual doc-acceptance check:sed -n '201,218p' docs/configuration.mdandsed -n '68,76p' CONTRIBUTING.mdconfirm all intent-mandated policy elements and the Gate-defaults pointer; heading 'Gate defaults (.no-mistakes.yaml)' exists at docs/configuration.md:201Manual factual-claim check:grep -rn 'no-mistakes/config\|config\.yaml' bin/confirms no firstmate script (including bin/fm-bootstrap.sh) writes ~/.no-mistakes/config.yamlRead-only end-to-end check: parsed ~/.no-mistakes/config.yaml -> agent: [pi, claude], matching the documented policy (this pi-driven validation session in an AGENTS.md/CLAUDE.md repo is the live proof)✅ **Document** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.