Applies the document contract from
docs/plans/2026-08-20_stack-docs-flatten-and-alignment.md section 3, partially:
`kind` and `status` only. `parent` is deliberately held until the flatten in
section 4 lands, so that 127 documents do not have to be re-pointed by hand
when docs/fleet/NORTH_STAR.yaml moves to docs/NORTH_STAR.yaml.
Scope, measured on origin/next at 63069149:
127 live docs = all *.md under docs/ minus docs/archive/ minus docs/_old_structure/
104 stamped here
19 held operator judgement (plan section 9), worklist in the same PR
3 held the SUPERSEDED TASKS.md stamps, which cite the moving path
1 untouched docs/fleet/FLEET-DOCTRINE.md, already stamped in W1
Kinds applied: 54 guide, 34 record, 9 spec, 6 tracking, 1 projection.
Every row carries a confidence and a one-line rationale in the worklist.
Two collisions with the existing state, both flagged rather than resolved:
1. docs/README.md:150-160 already documents a front-matter convention
(title/type/audience/status/source_of_truth) with its own allowed values.
It is applied to 4 of 127 files. Its `status` vocabulary is
current|draft|deprecated|historical; the new contract's is active|superseded-by.
The key collides. This commit lets the new contract win and rewrites
`status: current` to `status: active` on those 4 files, keeping their other
legacy keys untouched. No code reads any of them: `git grep source_of_truth`
outside docs/ returns nothing. docs/README.md still prescribes the old
convention and is an operator row, so it is not edited here.
2. Two of the plan's 20 operator rows are YAML files, not markdown
(docs/fleet/examples/roster-v2.yaml, docs/openapi-tess.yaml), and the
contract's front-matter form has no defined meaning for a .yaml document.
That gap also applies to docs/fleet/NORTH_STAR.yaml, the source of truth
itself. Raised in the worklist.
A third row from the plan, docs/fleet/north-star.md, no longer exists: W1
renamed it to docs/fleet/FLEET-DOCTRINE.md.
Verification: 104/104 parse with the expected kind and status in front matter;
the check was shown to reject a wrong kind before it was trusted. The diff
removes 4 lines total, all of them `status: current`.
6.4 KiB
kind, status
| kind | status |
|---|---|
| guide | active |
Local Fleet Canary
The local fleet canary runs a small tmux-backed Mosaic agent fleet on an
isolated tmux socket. The default socket is mosaic-fleet; the commands do
not use or stop the default tmux server.
Files
Product-owned defaults:
packages/mosaic/framework/fleet/roster.schema.jsonpackages/mosaic/framework/fleet/examples/minimal.yamlpackages/mosaic/framework/fleet/examples/local-canary.yamlpackages/mosaic/framework/systemd/user/mosaic-tmux-holder.servicepackages/mosaic/framework/systemd/user/[email protected]packages/mosaic/framework/tools/fleet/start-agent-session.shpackages/mosaic/framework/tools/tmux/agent-send.shpackages/mosaic/framework/tools/tmux/send-message.sh
These files are published through packages/mosaic/package.json, whose files
allowlist includes framework along with dist.
Site-owned local roster:
~/.config/mosaic/fleet/roster.yaml
Do not put a host-specific full roster into product defaults. Start from an
example and edit the local roster after mosaic fleet init --write.
Install
Minimal canary:
mosaic fleet init --profile minimal --write
# If a site-owned roster already exists, inspect it first; overwrite only explicitly:
# mosaic fleet init --profile minimal --write --force
mosaic fleet install-systemd
systemctl --user daemon-reload
mosaic fleet start
mosaic fleet verify
Small dogfood roster:
mosaic fleet init --profile local-canary --write
# Use --force only after preserving any site-owned roster changes.
mosaic fleet install-systemd
systemctl --user daemon-reload
mosaic fleet start
mosaic fleet status
Agent Operations
mosaic agent roster
mosaic agent status
mosaic agent status canary-pi
mosaic agent send canary-pi --message "status check"
mosaic agent reset canary-pi --new
mosaic agent tail canary-pi -n 80
These commands read the roster and target the configured tmux socket. The
generated systemd agent services use start-agent-session.sh; message delivery
uses the tmux send tools with -L mosaic-fleet.
mosaic agent send is operator-origin traffic unless a caller explicitly says
otherwise. The CLI always passes a deterministic source label to
agent-send.sh with -S, defaulting to <hostname>:operator, so it does not
query the target tmux socket and accidentally identify as an active agent pane.
Use --source-label <label> or --source <label> only when deliberately
impersonating a known handoff lane. The lower-level inter-agent wrapper
agent-send.sh -S <label> remains the explicit source override for scripts.
Verification
Use these checks before expanding the roster:
tmux -L mosaic-fleet ls
tmux ls
mosaic fleet verify
systemctl --user status mosaic-tmux-holder.service
Expected results:
tmux -L mosaic-fleet lsshows_holderand roster agent sessions.tmux lsshows only the default tmux server sessions and is not changed by fleet start/stop operations.mosaic fleet verifychecks exact session targets on the isolated socket.systemctl --user status ...may showactive (exited)for oneshot units; that means the unit ran, not that an agent pane is live. Treat tmuxhas-session,list-panes, process tree, and logs as the liveness evidence.
Recovery — rebuild generated env projections
Each agent's ~/.config/mosaic/fleet/agents/<name>.env.generated is a
deterministic projection of roster.yaml (the SSOT) that the launcher
(start-agent-session.sh) sources at start. If an upgrade or a manual mistake
wipes or diverges those projections, rebuild them from the roster with
mosaic fleet regen — do NOT restart the affected unit first.
mosaic fleet regen # dry-run (default): show create/rebuild plan per agent
mosaic fleet regen --json # same plan, machine-readable
mosaic fleet regen --write # rebuild fleet/agents/<name>.env.generated on disk
regen is projection-only and never restarts an agent — it has no path to
systemd lifecycle. It is dry-run by default, deterministic/idempotent, uses the
same roster→env mapping as mosaic fleet reconcile, and emits paths and counts
only (never the projected KEY=value body). After --write, verify each unit
resolves the intended values before restarting one unit at a time. The unit sets
no EnvironmentFile= — start-agent-session.sh sources .env.generated itself —
so verify the generated file directly and the launcher path, not a nonexistent
EnvironmentFile property:
test -f ~/.config/mosaic/fleet/agents/<name>.env.generated
systemctl --user cat mosaic-agent@<name> | grep ExecStart
systemctl --user restart mosaic-agent@<name>
Full recovery runbook and the three-layer #791 protection model (manifest ownership → pre-update snapshot/restore → regen): see Upgrade Safety & Recovery.
Release Preflight
Run this checklist before cutting or dogfooding a fleet release:
- Real AI dogfood: send at least one task through
mosaic agent send, then confirm the agent accepted/responded using pane, process, or log evidence. - Restart/stop/idempotency: run
mosaic fleet start,restart,stop, and a repeatedstartagainst the named socket; verify the default tmux server is unchanged. - Liveness verification: run
mosaic fleet verifyand confirm roster sessions withtmux -L mosaic-fleet lsor exacthas-sessionchecks. - Package dry-run: run
npm pack --dry-run --jsonfrompackages/mosaicand confirmframework/fleet,framework/systemd/user,framework/tools/fleet, andframework/tools/tmuxassets are included. - Mosaic update test: install or upgrade from the packed artifact in a temporary
Mosaic home and confirm
mosaic updateor the release upgrade path does not remove local roster/config files.
Rollback
Stop the local canary:
mosaic fleet stop
systemctl --user disable [email protected]
systemctl --user disable mosaic-tmux-holder.service
systemctl --user daemon-reload
For a full local cleanup of generated canary files:
rm -f ~/.config/systemd/user/[email protected]
rm -f ~/.config/systemd/user/mosaic-tmux-holder.service
rm -rf ~/.config/mosaic/fleet
rm -rf ~/.config/mosaic/tools/fleet
This rollback leaves the default tmux server untouched. If a canary session is still present after service stop, remove only the isolated socket server:
tmux -L mosaic-fleet kill-server