The A3a/A3b persona system injects a per-agent persona contract that the launcher composes from process.env.MOSAIC_AGENT_CLASS (compose-contract → readPersonaContractBlock, launch.js:325). generateAgentEnv already writes MOSAIC_AGENT_CLASS to agents/<name>.env, but start-agent-session.sh only re-exported MOSAIC_AGENT_NAME into the pane snippet — never the class.
The tmux pane inherits the tmux SERVER environment, not this script's env nor the systemd unit's EnvironmentFile. So the class written to the .env was invisible in-pane; the launcher saw an undefined class and silently injected no persona contract. Every fleet agent came up class-less while fleet ps reported healthy. The comms block kept working only because it keys on MOSAIC_AGENT_NAME, which is re-exported.
Fix
Re-export MOSAIC_AGENT_CLASS into PANE_SHELL_SNIPPET exactly as MOSAIC_AGENT_NAME, %q-quoted. An empty/unset class %q-quotes to '' and is a harmless no-op (matches readPersonaContractBlock's empty-class contract).
Verification
Reproduced live on the jarvis fleet (0.0.47): adding the class to .env + restart alone left the pane withoutMOSAIC_AGENT_CLASS and without a persona block — proving the .env reaches only the systemd service, not the pane.
After this script fix + restart: the relaunched agent carries MOSAIC_AGENT_CLASS=<class> in its pane env and the matching # Persona Contract (<class>) block in its composed prompt (claude: in --append-system-prompt; pi: via compose-contract reading the same env). Negative control (env -u MOSAIC_AGENT_CLASS compose-contract pi) emits no persona — confirming it is genuinely class-gated.
Independently re-verified by a second agent (review-of-record).
Adds a regression guard to test-start-agent-session.sh asserting the pane snippet exports both MOSAIC_AGENT_NAME and the per-agent MOSAIC_AGENT_CLASS.
## Problem
The A3a/A3b persona system injects a per-agent persona contract that the launcher composes from `process.env.MOSAIC_AGENT_CLASS` (`compose-contract` → `readPersonaContractBlock`, `launch.js:325`). `generateAgentEnv` already writes `MOSAIC_AGENT_CLASS` to `agents/<name>.env`, **but `start-agent-session.sh` only re-exported `MOSAIC_AGENT_NAME` into the pane snippet — never the class.**
The tmux **pane inherits the tmux SERVER environment**, not this script's env nor the systemd unit's `EnvironmentFile`. So the class written to the `.env` was invisible in-pane; the launcher saw an undefined class and **silently injected no persona contract**. Every fleet agent came up class-less while `fleet ps` reported healthy. The comms block kept working only because it keys on `MOSAIC_AGENT_NAME`, which *is* re-exported.
## Fix
Re-export `MOSAIC_AGENT_CLASS` into `PANE_SHELL_SNIPPET` exactly as `MOSAIC_AGENT_NAME`, `%q`-quoted. An empty/unset class `%q`-quotes to `''` and is a harmless no-op (matches `readPersonaContractBlock`'s empty-class contract).
## Verification
- Reproduced live on the jarvis fleet (0.0.47): adding the class to `.env` + restart alone left the pane **without** `MOSAIC_AGENT_CLASS` and **without** a persona block — proving the `.env` reaches only the systemd service, not the pane.
- After this script fix + restart: the relaunched agent carries `MOSAIC_AGENT_CLASS=<class>` in its pane env and the matching `# Persona Contract (<class>)` block in its composed prompt (claude: in `--append-system-prompt`; pi: via `compose-contract` reading the same env). Negative control (`env -u MOSAIC_AGENT_CLASS compose-contract pi`) emits no persona — confirming it is genuinely class-gated.
- Independently re-verified by a second agent (review-of-record).
Adds a regression guard to `test-start-agent-session.sh` asserting the pane snippet exports both `MOSAIC_AGENT_NAME` and the per-agent `MOSAIC_AGENT_CLASS`.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
A3a/A3b ship a per-agent persona contract that the launcher composes from
process.env.MOSAIC_AGENT_CLASS (compose-contract -> readPersonaContractBlock,
launch.js). generateAgentEnv already writes MOSAIC_AGENT_CLASS to
agents/<name>.env, but start-agent-session.sh only re-exported
MOSAIC_AGENT_NAME into the pane snippet — never the class.
The pane inherits the tmux SERVER environment, not this script's env nor the
systemd unit's EnvironmentFile, so the class set in the .env was invisible
in-pane. The launcher then saw an undefined class and silently injected NO
persona contract: every fleet agent came up class-less while `fleet ps`
reported healthy. The comms block kept working only because it keys on
MOSAIC_AGENT_NAME (which IS re-exported).
Fix: re-export MOSAIC_AGENT_CLASS into PANE_SHELL_SNIPPET exactly as
MOSAIC_AGENT_NAME, %q-quoted (empty/unset class -> '' is a harmless no-op,
matching readPersonaContractBlock's empty-class contract). Verified live: a
relaunched agent now carries MOSAIC_AGENT_CLASS in its pane env and the
matching `# Persona Contract (<class>)` block in its composed prompt.
Adds a regression guard to test-start-agent-session.sh asserting the pane
snippet exports both MOSAIC_AGENT_NAME and the per-agent MOSAIC_AGENT_CLASS.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Reviewed PR head 92cbe42 against origin/main from a fresh extract of both blobs.
Diff: 2 files, +26/-2 — matches description, no collateral changes.
start-agent-session.sh:MOSAIC_AGENT_CLASS is now re-exported into PANE_SHELL_SNIPPET in BOTH branches (with/without runtime-bin prefix), %q-quoted from ${MOSAIC_AGENT_CLASS:-}. Empty/unset class %q-quotes to '', a harmless no-op (readPersonaContractBlock returns '' for an empty class). Mirrors the existing MOSAIC_AGENT_NAME handling exactly. Comment correctly explains the pane inherits the tmux SERVER env (not the script env nor the systemd EnvironmentFile), which is why the explicit re-export is required.
Regression guard: verified it has TEETH via the suggested negative control:
as-is: ok - start-agent-session, EXIT 0
strip the CLASS export from the snippet: test FAILS with FAIL: pane command does not export MOSAIC_AGENT_CLASS into the pane (persona would silently drop), EXIT 1
restore: green again
bash -n clean on both files.
Verdict: APPROVE. Correctly and minimally closes the A3a pane-propagation gap; generateAgentEnv already emits the CLASS line, so post-merge mosaic fleet install converges cleanly. Recommend squash-merge on CI-green.
## Independent review-of-record — mos-claude-1 (two-perspective) — APPROVE
Reviewed PR head `92cbe42` against `origin/main` from a fresh extract of both blobs.
**Diff:** 2 files, +26/-2 — matches description, no collateral changes.
**start-agent-session.sh:** `MOSAIC_AGENT_CLASS` is now re-exported into `PANE_SHELL_SNIPPET` in BOTH branches (with/without runtime-bin prefix), `%q`-quoted from `${MOSAIC_AGENT_CLASS:-}`. Empty/unset class `%q`-quotes to `''`, a harmless no-op (`readPersonaContractBlock` returns `''` for an empty class). Mirrors the existing `MOSAIC_AGENT_NAME` handling exactly. Comment correctly explains the pane inherits the tmux SERVER env (not the script env nor the systemd EnvironmentFile), which is why the explicit re-export is required.
**Regression guard:** verified it has TEETH via the suggested negative control:
- as-is: `ok - start-agent-session`, EXIT 0
- strip the CLASS export from the snippet: test FAILS with `FAIL: pane command does not export MOSAIC_AGENT_CLASS into the pane (persona would silently drop)`, EXIT 1
- restore: green again
`bash -n` clean on both files.
**Verdict: APPROVE.** Correctly and minimally closes the A3a pane-propagation gap; `generateAgentEnv` already emits the CLASS line, so post-merge `mosaic fleet install` converges cleanly. Recommend squash-merge on CI-green.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Problem
The A3a/A3b persona system injects a per-agent persona contract that the launcher composes from
process.env.MOSAIC_AGENT_CLASS(compose-contract→readPersonaContractBlock,launch.js:325).generateAgentEnvalready writesMOSAIC_AGENT_CLASStoagents/<name>.env, butstart-agent-session.shonly re-exportedMOSAIC_AGENT_NAMEinto the pane snippet — never the class.The tmux pane inherits the tmux SERVER environment, not this script's env nor the systemd unit's
EnvironmentFile. So the class written to the.envwas invisible in-pane; the launcher saw an undefined class and silently injected no persona contract. Every fleet agent came up class-less whilefleet psreported healthy. The comms block kept working only because it keys onMOSAIC_AGENT_NAME, which is re-exported.Fix
Re-export
MOSAIC_AGENT_CLASSintoPANE_SHELL_SNIPPETexactly asMOSAIC_AGENT_NAME,%q-quoted. An empty/unset class%q-quotes to''and is a harmless no-op (matchesreadPersonaContractBlock's empty-class contract).Verification
.env+ restart alone left the pane withoutMOSAIC_AGENT_CLASSand without a persona block — proving the.envreaches only the systemd service, not the pane.MOSAIC_AGENT_CLASS=<class>in its pane env and the matching# Persona Contract (<class>)block in its composed prompt (claude: in--append-system-prompt; pi: viacompose-contractreading the same env). Negative control (env -u MOSAIC_AGENT_CLASS compose-contract pi) emits no persona — confirming it is genuinely class-gated.Adds a regression guard to
test-start-agent-session.shasserting the pane snippet exports bothMOSAIC_AGENT_NAMEand the per-agentMOSAIC_AGENT_CLASS.🤖 Generated with Claude Code
Independent review-of-record — mos-claude-1 (two-perspective) — APPROVE
Reviewed PR head
92cbe42againstorigin/mainfrom a fresh extract of both blobs.Diff: 2 files, +26/-2 — matches description, no collateral changes.
start-agent-session.sh:
MOSAIC_AGENT_CLASSis now re-exported intoPANE_SHELL_SNIPPETin BOTH branches (with/without runtime-bin prefix),%q-quoted from${MOSAIC_AGENT_CLASS:-}. Empty/unset class%q-quotes to'', a harmless no-op (readPersonaContractBlockreturns''for an empty class). Mirrors the existingMOSAIC_AGENT_NAMEhandling exactly. Comment correctly explains the pane inherits the tmux SERVER env (not the script env nor the systemd EnvironmentFile), which is why the explicit re-export is required.Regression guard: verified it has TEETH via the suggested negative control:
ok - start-agent-session, EXIT 0FAIL: pane command does not export MOSAIC_AGENT_CLASS into the pane (persona would silently drop), EXIT 1bash -nclean on both files.Verdict: APPROVE. Correctly and minimally closes the A3a pane-propagation gap;
generateAgentEnvalready emits the CLASS line, so post-mergemosaic fleet installconverges cleanly. Recommend squash-merge on CI-green.