fix(#1264): bootstrap fleet identity without a TTY
ci/woodpecker/pr/ci Pipeline failed

This commit is contained in:
goals
2026-08-16 17:37:32 -05:00
parent 476db12b92
commit 43fa047787
21 changed files with 1255 additions and 11 deletions
+1
View File
@@ -26,6 +26,7 @@ This book is the canonical home for architecture, package and application guides
- [Lease-broker operations and verification](testing/lease-broker-operations.md) — safe static/test commands plus explicitly held live operations.
- [Channel adapters](integrations/channel-adapters.md) — current shared contracts and Discord reference boundary; future adapter parity is draft.
- [Fleet first-start identity](architecture/fleet-first-start-identity.md) — no-TTY launch boundary, roster authority, and no-clobber filesystem design.
Every promoted page must be added to this index and to [`SITEMAP.md`](../SITEMAP.md) in the same migration slice.
@@ -11,6 +11,7 @@ This chapter is the canonical home for Mosaic Stack's system model, component bo
- [`mutator-class-gate.md`](mutator-class-gate.md) — default-deny tool authorization, runtime adapters, launch choke point, and parser assurance boundary.
- [`compaction-revocation.md`](compaction-revocation.md) — Claude/Pi observer lifecycle, runtime generations, revocation, and the bounded residual stale window.
- [`channel-protocol.md`](channel-protocol.md) — current shared channel DTOs and Discord compatibility baseline, with unimplemented adapter work explicitly marked draft.
- [`fleet-first-start-identity.md`](fleet-first-start-identity.md) — roster-owned identity bootstrap for concurrent no-TTY fleet launches.
- [`decisions/mos-runtime-portability-m1.md`](decisions/mos-runtime-portability-m1.md) — current logical identity, connector lease, grant, audit, and fencing decision; connector activation remains held.
These pages are current security-contract references and are consumed by the lease-broker acceptance suites. Their live deployment gaps remain explicitly labeled in the pages; this migration does not change runtime behavior.
@@ -0,0 +1,71 @@
# Fleet First-Start Identity Boundary
> **Status:** Implemented by issue #1264. Requirements: `FCM-REQ-12`, `AC-FCM-10`.
## Problem
`launchRuntime()` called `checkSoul()` before runtime execution. A missing top-level `SOUL.md`
caused `checkSoul()` to spawn a child `mosaic wizard` with inherited stdio. Under a systemd-created
fleet pane with no TTY, that child blocked or failed before the runtime boundary even though generic
`defaults/SOUL.md` and `defaults/USER.md` already shipped in the same `MOSAIC_HOME`.
## Chosen boundary
The fix remains at `checkSoul()` and does not add flags to `yolo`, fleet commands, systemd units, or
`start-agent-session.sh`:
1. A nonblank `MOSAIC_AGENT_NAME` selects the fleet path.
2. `resolveFleetIdentity()` must resolve that exact member through the existing roster/helper
boundary before any identity seed.
3. Safe bounded snapshots are read from only the missing contracts under `defaults/`.
4. Each snapshot is written to a random owner-private temporary file in `MOSAIC_HOME`.
5. `linkSync()` publishes the complete file without overwriting an existing path. `EEXIST` means a
concurrent seat or operator won; the existing path is preserved and revalidated.
6. Temporary files are removed, and both installed contracts are re-opened through the no-symlink
secure-file reader before launch continues.
7. `composeContract()` independently re-resolves the roster and injects exact member identity and
communications data.
A standalone launch with no `MOSAIC_AGENT_NAME` retains the interactive wizard.
## Identity and authority
The copied defaults deliberately say “Mosaic agent”; they are a generic behavioral base. They are
not the source of a fleet seat's identity. The canonical roster controls:
- exact agent/session name;
- canonical role/class and persona;
- peer rows and point of contact;
- tmux socket and helper target; and
- communications generation.
An unknown ambient name fails before any file is seeded. This avoids replacing the interactive wall
with a fleet of indistinguishable or ambiently invented identities.
## Concurrency and filesystem properties
- Sources and final destinations are bounded regular files beneath `MOSAIC_HOME`; symlinks are not
followed.
- New files have mode `0600`.
- Hard-link publication is same-filesystem, atomic, and no-clobber.
- A temporary path is removed only when this process successfully created it.
- All required source snapshots are validated before the first destination is published, preventing
a missing second default from leaving a partial seed.
- Existing operator files are never chmodded or rewritten.
## Verification
`src/commands/launch-first-start.spec.ts` uses the production-kind boundary: the real built CLI in a
no-TTY subprocess, not a direct wizard test. A fake lease launcher records whether execution reached
the runtime boundary and captures the composed prompt. Positive and negative cases prove the check
can both proceed and refuse.
Real Pi authentication and provider task execution remain environment tests, not claims of this
fixture.
## Non-goals
- Runtime installation or pane-PATH resolution (#1256/#1258).
- The held `~/.mosaic` launch-composition layer in PR #1213.
- Personalizing the operator's standalone identity without a wizard.
- Changing fleet systemd or shell launcher code.