feat(adapters): sanctioned mission directives injection (#17)

- 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
This commit is contained in:
2026-09-02 21:19:22 -05:00
parent bb5cecb348
commit 4ebb123ba3
2 changed files with 28 additions and 1 deletions
+14 -1
View File
@@ -210,12 +210,25 @@ function runTask(taskFile) {
const startedAt = new Date();
const stderrFile = path.join(runDir, "stderr.txt");
// Sanctioned mission injection: point the container at the run snapshot's
// CONTAINER path (dataRoot maps to /var/lib/mosaic in the image).
const spawnEnv = { ...process.env };
spawnEnv.MOSAIC_ADAPTER = resolved.execution.adapter;
if (task.missionSnapshot) {
const relative = path.relative(resolved.dataRoot, runDir);
if (relative.startsWith("..") || path.isAbsolute(relative)) {
fail(4, `run directory is outside the configured dataRoot: ${runDir}`);
}
spawnEnv.MOSAIC_MISSION_FILE = `/var/lib/mosaic/${relative.split(path.sep).join("/")}/mission.json`;
}
const proc = spawnSync(
"docker",
["compose", "run", "--rm", "-T", "mosaic-agent", task.prompt],
{
cwd: PROJECT_ROOT,
env: process.env, // MOSAIC_DATA_ROOT / MOSAIC_PROVIDER / MOSAIC_MODEL resolved by run-task.sh
env: spawnEnv,
input: "", // stdin detached: print mode must never wait on a terminal (see issue #5)
encoding: "utf8",
maxBuffer: 16 * 1024 * 1024,
+14
View File
@@ -40,6 +40,20 @@ cfg() { printf '%s' "$2" > "$SANDBOX/$1"; }
DATA_ROOT="$SANDBOX/data"
# --- adapter selection (M4) ---
cfg default-adapter.json '{"configVersion":1,"environment":"development","dataRoot":"'$DATA_ROOT'","execution":{"backend":"docker","provider":"zai","model":"m"}}'
MOSAIC_CONFIG="$SANDBOX/default-adapter.json" $CONFIG_OP validate | grep -q '"adapter": "pi"'
check "absent adapter defaults to pi" $?
cfg mock-adapter.json '{"configVersion":1,"environment":"development","dataRoot":"'$DATA_ROOT'","execution":{"backend":"docker","provider":"zai","model":"m","adapter":"mock"}}'
expect_exit "adapter mock validates" 0 -- env MOSAIC_CONFIG="$SANDBOX/mock-adapter.json" $CONFIG_OP validate
cfg bad-adapter.json '{"configVersion":1,"environment":"development","dataRoot":"'$DATA_ROOT'","execution":{"backend":"docker","provider":"zai","model":"m","adapter":"claude"}}'
expect_exit "unsupported adapter exits 2" 2 -- env MOSAIC_CONFIG="$SANDBOX/bad-adapter.json" $CONFIG_OP validate
MOSAIC_CONFIG="$SANDBOX/mock-adapter.json" $CONFIG_OP env | grep -q "MOSAIC_ADAPTER='mock'"
check "env exports adapter" $?
# --- bootstrap ---
rm -f "$SANDBOX/config.json"
expect_exit "bootstrap creates default when absent" 0 -- \