# FCM-M2-001 — Generated Environment Boundary - **Issue/card:** #758 / FCM-M2-001 - **Branch/base:** `feat/758-generated-env-boundary` from `origin/main` `e9c4aa3e8b3780719cd5a43c0ef3f37fc70de666` - **Budget assumption:** 30K-card budget; implement only the deterministic generated/local environment boundary and its launch-chain/docs/tests. ## Objective Replace the generic fleet agent `.env` authority/merge path with a deterministic roster-derived `.env.generated` projection and strict, data-only `.env.local`. The roster remains the desired-state authority. Reject bad input before the launcher creates a tmux session; never print sensitive or privileged-command values. ## Scope and non-goals - In scope: deterministic render/write, strict generated/local parse rules, legacy `.env` disposition/quarantine, systemd/launcher boundary, permission/path checks, focused fail-closed tests, operator/reference documentation, USC interface evidence. - Excluded: roster CRUD/mutation, v2 roster schema changes, lifecycle/reconcile/apply behavior, migration/canary rollout, connectors, remote surfaces, live-fleet actions, and M2-002. ## Plan 1. Add red tests for generated-key shadowing, malformed/duplicate/unknown/command/sensitive input, no-value diagnostics, deterministic/idempotent projection, secure file modes, and legacy disposition. 2. Implement a pure strict environment contract plus atomic projection/quarantine helper. 3. Replace the generic `.env` writer/merge path and systemd reference with `.env.generated` + `.env.local` ownership. 4. Make the shell launcher parse the files without `source`/`eval`, reject unsafe input before tmux creation, and construct only the roster-derived runtime command. 5. Add operator/reference documentation with the requested USC M1 interface evidence and M2–M4 gate statement. 6. Run focused/package/root gates and audit the USC interface packet. Per continuation scope, stop before review, commit, push, PR, or live mutation. ## Initial evidence - No existing owner: target worktree path absent; no target local/remote branch; `pr-list.sh -s open` returned no open PRs. - M1 compiler/API/docs and executable disposition evidence are present at the assigned base. - Existing launch chain writes `fleet/agents/.env`, preserves arbitrary legacy lines via `mergeAgentEnv`, sources `MOSAIC_AGENT_COMMAND`, and executes it through `bash -c`; all are M2 remediation targets. - `~/.config/mosaic/guides/SECURITY.md` is absent. Read the available security-review role contract and the vault/secrets guide instead. ## Verification log ### Continuation (2026-07-14) - Preserved the inherited 14-file delta; no reset, stash, rebase, roster mutation, lifecycle action, live-fleet action, commit, push, or PR action was performed. - Focused gates passed: - `pnpm --dir packages/mosaic test -- src/fleet/generated-env-boundary.spec.ts` — 1 file, 10 tests passed. - `bash packages/mosaic/framework/tools/fleet/test-start-agent-session.sh` — passed. - `bash packages/mosaic/framework/systemd/user/test-fleet-units.sh` — passed. - `pnpm --dir packages/mosaic test -- src/commands/fleet.spec.ts` — 1 file, 192 tests passed. - Package gates passed before final documentation/format follow-up: - `pnpm --dir packages/mosaic typecheck` — passed. - `pnpm --dir packages/mosaic lint` — passed. - `pnpm --dir packages/mosaic test` — 52 files, 752 tests passed. - `pnpm format:check` initially failed only for the new boundary reference and generated-boundary TypeScript files; targeted Prettier normalization was applied. A final `pnpm format:check` passed. - USC packet audit: the M1 structural compiler is `parseRosterV2` with roster `version: 2`; the semantic resolver is `validateRosterV2Semantics`; disposition artifacts retain `version: 1` fixture evidence. `docs/TASKS.md` records M1-001 done, M1-002 in-progress, and M1-003 not-started; no product release version is claimed. The packet now distinguishes these statuses from checkout artifact presence and states the M2 → M3 → M4 downstream gates. ## Review remediation (2026-07-14) - **Blocker 1 red-first:** Added a launcher reproducer with a `0777` `fleet/agents` parent and a private generated file. Before implementation, `bash packages/mosaic/framework/tools/fleet/test-start-agent-session.sh` failed: `FAIL: generated file under a world-writable parent was accepted`. The failure occurred after the launch path reached fake tmux, proving the parent was not validated. - **Blocker 2 red-first:** Added a `symlink()` projection-directory reproducer that preloads generated/local/quarantine/legacy target files and asserts no target mutation. Before implementation, `pnpm --dir packages/mosaic test -- src/fleet/generated-env-boundary.spec.ts` failed the new test because the existing writer followed the `agentEnvDir` symlink and parsed its target legacy input (`expected /unsafe-directory/i`, received `code=malformed-line`). The initial test-only missing `mkdir` import was corrected before recording this behavior failure. - **Blocker 3 red-first:** Added fresh/stale/absent native-heartbeat regression coverage. Before implementation, an isolated fake-tmux launcher reproducer with a fresh `.hb.native` marker failed `FAIL: fresh native heartbeat was overwritten`; the existing sidecar immediately replaced native `status=busy`/`model` content. - **Remediation result:** The launcher now rejects a group/world-accessible or symlinked `fleet/agents` parent before an environment read or tmux call. The projection writer uses `lstat` before chmod/write processing and rejects a symlinked directory without creating generated/local/quarantine files or deleting legacy input. The heartbeat sidecar defers to a fresh non-symlink native marker and falls back when stale/absent. Focused green evidence before independent review: `bash packages/mosaic/framework/tools/fleet/test-start-agent-session.sh` and `pnpm --dir packages/mosaic test -- src/fleet/generated-env-boundary.spec.ts` (11 tests) passed. - **Independent-review follow-up red-first:** Codex code review returned one blocker and security review one medium CWE-732 finding: the writer repaired an already `0777` directory with `chmod` before trusting its contents. Added a reproducer with a safe local file beneath an existing `0777` directory. Before the follow-up fix, `pnpm --dir packages/mosaic test -- src/fleet/generated-env-boundary.spec.ts` failed because the promise resolved and wrote `coder0.env.generated` instead of rejecting. - **Independent-review remediation:** Existing directories now pass non-following private-directory validation before any read or chmod; only a directory created in this call is normalized to `0700`. The fleet-add test fixture now creates its simulated trusted `fleet/agents` boundary at `0700`; this corrects fixture setup to match the new required contract rather than weakening the rejection assertion. Focused reruns passed: generated-boundary 12 tests, launcher boundary suite, and fleet suite 192 tests. - **Final verification before re-review:** Launcher + systemd suites passed; package suite passed (52 files, 754 tests); package lint/typecheck, root typecheck (42 tasks), format check, and diff check passed. The rerun code review still reports a tmux command-arity blocker, and the security rerun reports systemd `EnvironmentFile` pre-validation injection findings for both agent units. These were discovered after the specified three-remediation scope; no additional source changes were made. Independent review therefore remains `REQUEST CHANGES` despite the requested three fixes passing their behavioral suites. ## Systemd pre-validation remediation (2026-07-14) - **Red-first:** Updated the fleet unit contract to reject any `EnvironmentFile=` projection preload, require a cleared bootstrap environment, and require a validated exact-stop path. Before implementation, `bash packages/mosaic/framework/systemd/user/test-fleet-units.sh` failed: `FAIL: agent units must not preload projections before strict parsing`. - **Red-first parser/stop coverage:** Added interaction-wrapper and exact-stop cases to the launcher boundary suite. Before implementation, `bash packages/mosaic/framework/tools/fleet/test-start-agent-session.sh` failed: `FAIL: interaction did not use shared strict parser first`, because the interaction wrapper consumed inherited environment before projection validation. - **Focused green:** Both unit templates now use `env -i` with fixed `HOME`, agent instance, and PATH; neither has `Environment=`/`EnvironmentFile=`. The interaction wrapper delegates to `start-agent-session.sh --interaction`, so strict generated/local parsing precedes pinned Pi profile checks. `--stop` reuses the strict generated parser before exact `=` socket/session termination. Passed: systemd unit suite, launcher boundary suite (including malformed interaction, pinned profile, and ambient-socket stop cases), and 210 focused TypeScript tests. - **Final verification:** `pnpm --dir packages/mosaic test` passed (52 files, 754 tests); package lint/typecheck, root typecheck (42 tasks), format/diff, and shell syntax checks passed. Security review passed with no findings. Code review repeated the previously refuted tmux argv concern and a pre-existing Claude trust-lock suggestion; per the assigned narrow follow-up, no tmux or unrelated trust-path change was made. ## Risks and next review - This card is uncommitted and unreleased. The canonical tracker still records its dependencies as M1-002 in progress and M1-003 not started; this continuation does not reinterpret those task states. - Final post-documentation checks passed: `pnpm --dir packages/mosaic typecheck`, `pnpm --dir packages/mosaic lint`, `pnpm --dir packages/mosaic test` (52 files, 752 tests), `pnpm typecheck` (42 Turbo tasks), and `pnpm format:check`. - Obtain independent code and security review of the complete delta next. Do not run commit, push, PR, or live-fleet commands in this continuation. ## Fresh-install directory remediation (2026-07-14) - **Objective:** Remediate only the fresh-install path where `installFleet` created `fleet/agents` with host-umask permissions before the boundary writer correctly rejected it. - **Plan:** Add a real `fleet install --no-enable` integration reproducer; prove red; let the existing boundary writer own directory creation; run focused and full gates. No commit, push, PR, review disposition, or live-fleet action. - **Red evidence:** Before the one-line remediation, `pnpm --dir packages/mosaic test -- src/commands/fleet.spec.ts` failed the new test with `AgentEnvBoundaryError: code=unsafe-permissions` at `ensurePrivateProjectionDirectory`, after `installFleet` pre-created the directory. - **Change:** Removed only the recursive `mkdir(activePaths.agentEnvDir)` in `installFleet`. `writeAgentEnvironmentProjection` remains the sole creator and retains its existing `lstat`, private-directory, symlink, and existing-unsafe-directory fail-closed checks. - **Focused green:** `pnpm --dir packages/mosaic test -- src/commands/fleet.spec.ts` — 193 tests passed. The new integration executes a fresh `fleet install --no-enable`, asserts a real non-symlink `0700` directory and a `0600` generated projection. Existing unsafe-directory coverage remains in `generated-env-boundary.spec.ts` and asserts no chmod repair/no generated file write. - **Full gates green:** generated-boundary 12 tests; launcher and systemd suites; package typecheck/lint and 52 files / 755 tests; root typecheck (42 tasks), lint, format, diff check, and root test (42 tasks) all passed. - **Independent review:** The complete inherited uncommitted delta still has Codex `REQUEST CHANGES` findings outside this narrow fix (tmux command arity and Claude trust-lock regression), plus a security-review medium finding on unvalidated writable ancestor directories. No out-of-scope source changes were made. - **Risk:** The writer's existing create-then-validate sequence is relied on for the creation boundary; a concurrent substitution causes fail-closed validation rather than repair. The review findings above remain residual risks for the complete card delta.