Owner direction: the stack must never impact default harness usage.
Correction to M19 as shipped (nothing had been created in ~/.pi — the
move breaks nothing).
- Mosaic-managed accounts: <dataRoot>/auth/<account>.json, perms 0600
enforced (loose perms flagged in listings, refused by --auth — mirrors
gitea-api.sh credential hygiene).
- ~/.pi is read-only to the stack, permanently; the only interaction
remains the existing read-only container mount of the default
credential. Recorded as a ROADMAP standing decision.
- auth.sh is now config-driven (data root from config.json, fail closed,
consistent with every other tool); status reports both sources labeled.
- agent.sh --auth resolution moved after load_config (needs the data
root); missing/symlinked/non-0600 accounts refuse.
- test-auth.sh: 15 no-Docker cases (accounts-create-nothing, loose-perms
refusal, invalid-config refusal added). Test-authoring correction
recorded in BUILD-LOG (fixture-state mismatch caught before running).
Suites 24/15/90/14/17 + verify green.
Owner decision after live verification of M18: an explicit agents-dir
override that cannot resolve the named seat now refuses the launch
(exit 4, names the seat and dir) instead of launching seatless and
unbounded. Unsetting the override keeps the M13 plain governed TUI.
MOSAIC_ROLES_DIR needs no symmetric change - the M18 gate already
refuses unresolvable role contracts.
Task suite 88 -> 90 (refusal + refusal-names-the-seat). TOOLS.md Agent
section documents the refusal.
Suites 24/90/14/17 + verify green.
Role contracts (roles/<role>.json): roleVersion, name bound to filename,
tools ceiling (subset of pi built-ins), network declared (none|api-only|
open; enforced when network policy lands). Strict schema, fail closed -
a non-role document refuses resolution.
mosaic-task.mjs resolve-role: config-free contract validation, emits
MOSAIC_ROLE_TOOLS / MOSAIC_ROLE_NETWORK.
agent.sh: a declared role binds to its contract. Missing/invalid contract
refuses the launch (exit 2, names the role - the under-equipped-seat
failure mode, mirroring M17 skills). Effective tools = ceiling ∩ requested
(CLI --tools or agent.json caps); no request -> ceiling stands; narrowing
and tool-free outcomes loud on stderr. Adapters unchanged; headless M9
chain (mission ∩ task) untouched.
Ships roles/researcher.json (existing seat declares the role; without the
contract the fail-closed gate would refuse its launch).
Task suite 74 -> 88: contract resolution, wrong-kind/name/network/
duplicate/unsupported/missing refusals, ceiling narrowing E2E (mock
adapter), tool-free E2E, missing-contract refusal. Test-authoring
correction recorded in BUILD-LOG (a check that registered on one path
only, caught by count arithmetic).
Suites 24/88/14/17 + verify green.
Owner feedback: grep match-highlighting made the word 'policy' red while
status words were plain - counter-indicative. Suites + verify now emit
ANSI colors (green success, red failure) when stdout is a terminal;
piped/machine-parsed output stays plain, honoring NO_COLOR. Word 'ok'
promoted to 'OK' for scannability.
Verified byte-level via forced-pty run; piped output unchanged; suites
41/24/14 + verify green.
Collaboration record (conductor loop, docs/plans/CONDUCTOR.md):
- round 1 (worker session worker-1, 2m28s): retry implemented per spec
- conductor live test exposed spec gap: direct invocation lacked
launcher env exports
- round 2 (same worker session, 59s): spawnEnv made self-sufficient,
but used PI_* where compose interpolates MOSAIC_*
- conductor hotfix: 3-line rename to MOSAIC_PROVIDER/MOSAIC_MODEL/
MOSAIC_DATA_ROOT
Final: node scripts/mosaic-task.mjs retry <runId> re-executes a run's
task snapshot as a new run; live retry replied REMEMBERED; all suites
green (24/32/14 + verify).
Known limitation: retrying a run whose task used a RELATIVE mission path
resolves it against the temp dir; lineage tracking deferred.
Closes#25, closes#26, closes#27