fix(fleet): enforce exact comms authority (#787)
All checks were successful
ci/woodpecker/push/publish Pipeline was successful
ci/woodpecker/push/ci Pipeline was successful

This commit was merged in pull request #787.
This commit is contained in:
2026-07-16 00:32:23 +00:00
parent c1aecfabe9
commit adad486b6f
22 changed files with 3534 additions and 859 deletions

View File

@@ -0,0 +1,80 @@
# Issue 766 — exact cross-harness fleet comms targeting
- **Issue:** #766
- **Branch:** `fix/766-exact-fleet-comms`
- **Worktree:** `/home/jarvis/src/stack-issue-766`
- **Delivery boundary:** source/tests/docs only; no live tmux, session, or fleet actions; leave uncommitted for independent review.
## Objective
Replace inference-prone fleet onboarding guidance with one roster-resolved contract that gives Claude Code, Codex, OpenCode, and Pi the same authoritative local identity and exact executable command for every known peer.
## Plan
1. Add issue-specific normative requirements to `docs/PRD.md` before source changes; do not modify orchestrator-owned `docs/TASKS.md`.
2. Extract the existing v1 roster parsing/normalization into one lightweight shared resolver used by both fleet commands and runtime comms composition.
3. Write failing contract tests for explicit SSH-only cross-host targeting, global/default socket authority, authoritative identity, unknown-peer failure, no operational metavariables, and four-harness parity.
4. Make source `TOOLS.md` non-operational and marker-versioned; prove fresh installation preserves that exact contract and composition detects a stale installed copy without rewriting it.
5. Render a deterministic comms generation and document comparison/relaunch handling; never rewrite an active session.
6. Run focused Vitest and shell exact-target tests, then package/repository typecheck, lint, format, test, and build gates as relevant.
7. Reconstruct the exact uncommitted tree, including untracked files, for independent review and remediate findings without committing.
## Contract decisions
- `tmux.socket_name` is the one supported socket authority for every local fleet session. A per-agent `socket`, when present for compatibility, must equal that global value; independent per-agent sockets fail closed because the runtime does not provision them. A named socket renders `-L`, while the empty literal default renders no `-L`.
- A peer is same-host only when its resolved host equals the current roster member's resolved host. Every host-omitted member resolves against the stable local fleet-host baseline, never against the viewer's explicit host. Same-host rows never render `-H`.
- A cross-host row requires that peer's explicit roster `ssh`; absence is a contract error. Never substitute `host` as an SSH target.
- The current member's explicit roster `host` wins; otherwise the local machine's short hostname is the baseline for host-omitted local members.
- Unknown members/peers return a deterministic error listing exact known names and an exact self-scoped discovery command. No fuzzy session lookup.
- Exact command fields are structurally constrained to safe targeting grammars and shell-rendered as individual arguments. Unsafe host/SSH/socket values fail roster normalization rather than entering executable guidance.
- Existing installed `TOOLS.md` remains user-owned during ordinary keep-mode updates. Currency requires the expected source and installed marker/version plus bounded SHA-256 byte identity. Explicit `mosaic update --repair-tools` is the supported current-version recovery path: it makes a digest-qualified no-clobber backup, restores the contract and regular executable helper, and does not rewrite active context.
- The v1 resolver preserves and validates `tmux`, `discord`, and `matrix` connector blocks in YAML and JSON; conflicting snake/camel aliases fail closed unless their values are identical.
- JSON roster fallback occurs only when `roster.yaml` is absent. Keep-mode reseed preserves both formats, and relaunch discovery uses the same canonical resolver.
- The helper is inspected without following symlinks and must be a regular executable file. Missing, directory, symlink, and non-executable installations fail closed with deterministic repair guidance.
- Active contexts carry a deterministic comms generation. Operators compare it to `mosaic agent comms-block <exact-agent>` output; mismatch means stale and requires an explicit exact-agent relaunch.
## Risks
- Import cycles if runtime composition imports the command-heavy `fleet.ts`; mitigate with a lightweight shared roster module and re-export compatibility.
- Existing schema prose allowed independent per-agent sockets even though runtime provisioning used one global socket; constrain compatibility declarations to the global value and preserve empty-global default behavior.
- Remote inventory may be incomplete. Fail composition closed for an unreachable cross-host row rather than generating a guessed command.
- `TOOLS.md` is user-seeded and intentionally preserved. Detect/report drift instead of overwriting custom content.
## Planned evidence
- `comms-onboarding.spec.ts`: resolver/renderer/failure/generation contracts.
- `compose-contract.spec.ts`: identical authoritative comms section for all four harnesses and stale installed-contract reporting without mutation.
- `file-adapter.test.ts`: source-to-fresh-install byte equality and preservation of customized installed `TOOLS.md`.
- Existing `agent-send.test.sh`, socket isolation, and tmux runtime transport tests.
- Repository quality gates and independent uncommitted-tree review.
## Evidence log
- Preflight collision scan: no issue-766 local/remote branch, worktree, or open PR collision before branch creation.
- Isolated branch created from fetched `origin/main` at `4990905`; original checkout not edited.
- One strict v1 resolver now serves fleet commands and communications composition; roster writes preserve `host`, `ssh`, and `socket`.
- Exact renderer covers authoritative self identity, global/default socket authority, rejected independent sockets, stable hostless-peer resolution, same-host omission of `-H`, explicit-SSH-only cross-host rows, shell-safe argv rendering, deterministic generations, and fail-closed unknown/missing targets.
- Real framework `defaults/TOOLS.md` is tested byte-equal through a fresh `FileConfigAdapter` install, the installed helper is executable, and the final Pi contract contains the same source contract plus exact generated command; separate parity coverage proves byte-equivalent comms sections for Claude Code, Codex, OpenCode, and Pi.
- Second review remediation adds strict connector/alias coverage, full-semantic generation coverage, ENOENT-only fallback, no-follow helper validation, unconditional current-version repair, digest-qualified no-clobber backups, and marker/version-gated currency.
- The helper, roster, installed TOOLS, and framework source files are read with canonical containment, every existing ancestor and target rejected if symlinked, `O_NOFOLLOW` descriptor reads, inode stability checks, and effective-identity execute access. Read-only TOOLS status treats source/installed symlinks as unavailable without following or rewriting them.
- Explicit repair validates both bundled inputs before destination creation, stages backup/TOOLS/helper plus exact-mode rollback files before any persistent file commit, revalidates destination identity at each commit boundary, installs the digest backup without clobber, and removes or exactly rolls back every committed output on injected failure. `changed: false` is returned only after full cleanup; cleanup/rollback failure is reported as `changed: true`.
- Connector schema and runtime normalization require kind-matching settings and reject inactive connector blocks. Keep-mode installers preserve only exact `roster.yaml`, `roster.json`, `agents/`, and `run/` paths while refreshing framework `roster.schema.json`; shell evidence covers byte preservation and schema refresh.
- Solo contracts render normalized role/class plus explicit no-peer/no-remote authority boundaries; composed-contract evidence keeps role Mandate/Boundaries before Fleet Comms.
- Operational documentation and CLI metavariable now use `mosaic agent comms-block <exact-member>`; historical issue-633 scratchpad text remains historical.
- The latest independent review rejected synthetic tree `556ae4ea04f2715a4e9d381f3cafaf4c8b991b2e` on three mandatory findings: installed `TOOLS.md` could be read through target/ancestor symlinks before unsafe status was reported; ambient class/tool-policy state could split identity authority from the canonical roster member; and the connector schema admitted empty or whitespace-only Discord/Matrix strings rejected by runtime parsing.
- Red-first reproduction proved all three findings with 20 failures and 79 passes. Remediation routes installed `TOOLS.md` through the bounded secure regular-file reader before composition, resolves one exact canonical fleet identity for persona/tool policy/normalized class/Fleet Comms, rejects canonicalized ambient class mismatches, canonicalizes compatibility classes during roster parsing, and aligns parser/schema non-whitespace requirements.
- Four-runtime coverage proves unsafe target and ancestor symlink content is omitted without mutation, while Claude Code, Codex, OpenCode, and Pi all project the same canonical member authority. Connector parser/schema coverage includes empty and whitespace-only Discord `channel_id` and Matrix `homeserver_url`, `user_id`, and `room_id` values.
- Remediated focused gates passed: 99/99 across the two finding-focused suites plus connector schema regression PASS; the six changed-suite matrix passed 341/341; secure-file/transaction coverage remains green, including 28/28 transactional repair tests; installer migration passed 21/21.
- Mosaic package suite passed 906/906. Shell/runtime regressions passed: `agent-send.test.sh` `PASS=11 FAIL=0`; named-socket isolation; matrix/tmux transport 12/12 (Matrix 5/5, tmux 7/7).
- Final repository gates passed: format check; typecheck 42/42 tasks; lint 23/23; tests 42/42 tasks (Mosaic 906/906, gateway 628 passed/12 skipped); build 23/23.
- A subsequent immutable review of tree `aa6414123643a504145fce6ac1d66f0b535feb5e` found one roster-authority blocker: a canonical member with omitted `tool_policy` inherited ambient `MOSAIC_AGENT_TOOL_POLICY`. Red-first four-runtime coverage failed 4/39 specifically on the leaked operator-interaction policy. Composition now branches on canonical membership: fleet launches use only `canonicalMember.toolPolicy` (including canonical absence), while genuinely non-fleet launches retain ambient fallback.
- Final remediated gates passed: four-runtime regression 39/39; six changed-suite matrix 345/345; connector schema regression PASS; Mosaic package 910/910; installer migration 21/21; `agent-send.test.sh` 11/11; named-socket isolation PASS; Matrix/tmux transport 12/12; repository format PASS; typecheck 42/42 tasks; lint 23/23 tasks; tests 42/42 tasks; build 23/23 tasks.
- No live tmux/session/fleet mutation, commit, push, PR mutation, issue mutation, context mutation, or reviewer launch performed.
- Exact synthetic-tree reconstruction and frozen evidence are included in the coordinator handoff.
- Sole-remediation preflight reverified the clean committed checkout at head `0dc47cac92c93a3ffd39ba9dd6685ac4165f6361`, tree `538de6ccce1f8c44ba288a7493286e63a3413e75`, branch `fix/766-exact-fleet-comms`; issue and PR state were read only through Mosaic wrappers.
- Deterministic red-first ancestor substitution swapped validated `root/tools` for an external symlink immediately after `lstat`; current head returned `external marker` (`1 failed, 4 passed`) before implementation.
- Secure reads now hold `/` and every root/descendant directory descriptor, traverse appended components through Linux `/proc/self/fd` with `O_DIRECTORY|O_NOFOLLOW`, and read plus effective-identity execute-check the same final descriptor. Non-Linux or unavailable proc-fd capability fails closed; stable errors redact managed paths while retaining Node `code` compatibility for missing/non-executable repair behavior.
- Added deterministic root-selection, descendant-ancestor, and final-target substitution coverage. All return trusted descriptor-bound bytes after rename/symlink replacement; the race suite passed 50/50 repeated runs.
- Isolated CLI verification drove `mosaic agent --mosaic-home <fixture> comms-block self` while repeatedly swapping `fleet/` with an external symlink: `trusted=2 fail_closed=10 external_marker=0`; a persistent symlink ancestor exited 1 with a redacted unsafe-ancestor error. No live fleet state was used or mutated.
- Remediation gates: focused secure-file/comms/launch/tmux/Matrix `110/110`; full `@mosaicstack/mosaic` `914/914`; package and repository typecheck pass (`42/42` repository tasks); package and repository lint pass (`23/23` repository tasks); repository format check and `git diff --check` pass.
- Independent review found one production hardening blocker (nonblocking final open), one redacted-error blocker, and a deterministic ancestor-test gap. Remediation added `O_NONBLOCK`, normalized execute errors while preserving errno, proved the ancestor hook fires, and added final-target substitution coverage; post-remediation review evidence is clean on the production invariant.