- mission schema: optional capabilities.tools (same validation as task)
- merge semantics in runTask: neither -> none; mission only -> mission;
task only -> task; both -> intersection (task narrows, never widens);
empty intersection -> tool-free run with an explicit stderr note
- result.json records EFFECTIVE tools; task/mission snapshots remain the
immutable declaration of intent
- adapters unchanged; host-side only (no image change, 0.0.6 still active)
- task suite +5 cases (41 total): all four merge cases asserted from run
evidence + invalid mission capabilities rejected
Policy decision recorded: missions govern; tasks cannot escalate.
Closes#30
result.json recorded lineage correctly; the human-facing show command
omitted the field. Found by owner test: show | grep retriedFrom was
empty on a run whose result.json contained it.
Closes#29
- retryRun rewrites a snapshot's relative mission path to the run's own
recorded mission.json (absolute) before execution — retries stay
faithful to what originally ran
- runTask accepts options.retriedFrom; retry records lineage in
result.json (additive optional field, no schema break)
- task suite +4 cases: retry succeeds, lineage recorded, mission section
present after retry (36 total), missing-run retry exits 4
Closes#28
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
- task schema: optional workspace (absent | :run ephemeral | named
persistent under dataRoot/workspaces) and capabilities.tools (pi
documented tool allowlist); strict validation, traversal-proof names
- runner: creates host workspace, passes MOSAIC_WORKSPACE (container
path) + MOSAIC_TOOLS; result.json records both
- pi adapter: cds into workspace; --tools when allowlist present else
--no-tools
- mock adapter: logs delivered MOSAIC_* vars to stderr as deterministic
plumbing evidence (dash prints 'export K=v', so use env not export)
Closes#20
- load-contracts.sh: MOSAIC_MISSION_FILE (readable) appends a MISSION
(runtime) section — objective + directives — after the immutable
contracts; unreadable path is a hard error, absent env changes nothing
- mosaic-task.mjs: exports MOSAIC_MISSION_FILE as the run snapshot's
container path (/var/lib/mosaic/runs/<id>/mission.json), with an
outside-dataRoot guard; also exports the configured adapter
Verified: contract-only prompt has no mission section; mission-bearing
run shows objective + directives in the generated prompt, snapshot
recorded, real provider returns exactly MOSAIC_HELLO_OK.
Closes#17
- scripts/mosaic-task.mjs: validate | run | list
- Strict v1 schemas: unknown keys rejected; ids/prompt/expectExact/
timeoutSeconds bounds enforced; optional mission file resolved against
the task file and validated too
- run: executes through the config-driven container path with stdin
detached (issue #5 class), SIGKILL timeout (default 120s), trimmed
response capture
- Immutable run records under <dataRoot>/runs/r-<utcstamp>-<rand>/:
task.json + mission.json snapshots (write-once), stderr.txt, result.json
- expectExact gate: mismatch -> status failed, exit 1; result.json is
always written
- scripts/run-task.sh: load_config + bootstrap_runtime_dir before exec
- M2 scope: mission directives are snapshotted for provenance, not yet
injected into the runtime prompt (later policy layer)
Closes#6, closes#7