@@ -221,4 +221,58 @@ Release model and safe updates verified by drills. `main` merged with M3 and tag
|
||||
|
||||
Adapter seam verified; harness boundary is now additive by construction. `main` merged with M4 and tagged `adapter-seam-v1`.
|
||||
|
||||
---
|
||||
|
||||
## Phase 9: Workspaces + capability envelope (M5)
|
||||
|
||||
### Entry 9.1 — before
|
||||
|
||||
- Timestamp: 2026-09-03
|
||||
- Intended action: Optional task workspace (absent / ":run" ephemeral / named persistent) and capabilities.tools allowlist (pi built-ins); runner plumbing via MOSAIC_WORKSPACE/MOSAIC_TOOLS; pi adapter maps to cwd + --tools; mock adapter logs delivered MOSAIC_* vars for deterministic assertions (Gitea #20, #21).
|
||||
- Reason: Agents that only answer text cannot do work; the workspace+tools pair is the smallest real capability step, bounded by the container.
|
||||
- Expected result: plumbing asserted via run-record stderr; live pi writes a host-visible file.
|
||||
|
||||
### Entry 9.2 — after
|
||||
|
||||
- Timestamp: 2026-09-03
|
||||
- Commands run: build; mock plumbing run; validate negatives; full task suite; live workspace demo.
|
||||
- Observed result: task suite 32/32; MOSAIC_WORKSPACE/MOSAIC_TOOLS asserted in run record; dataRoot/workspaces/<name> created host-side; live pi used bash to write proof.txt into the demo workspace (host-visible).
|
||||
- Failure or correction: (1) batch edit dropped SUPPORTED_TOOLS const (runtime ReferenceError, exit 1 instead of 2) — restored; (2) mock env dump used `export` which this dash prints as `export K='v'` — switched to `env`; (3) selftest fed a mismatching mock response to the plain-task case — test bug, fixed.
|
||||
|
||||
## Phase 10: Named sessions (M6)
|
||||
|
||||
### Entry 10.1 — before
|
||||
|
||||
- Timestamp: 2026-09-03
|
||||
- Intended action: Optional task.session name -> persistent session dir dataRoot/sessions/<name> via pi --session-dir; resume most recent with -c when present; isolation per name; teach/recall demo fixtures (Gitea #22, #23).
|
||||
- Reason: L1 persistence is the prerequisite for any multi-step agent work.
|
||||
- Expected result: session dir populated after first run; second run recalls taught context.
|
||||
|
||||
### Entry 10.2 — after
|
||||
|
||||
- Timestamp: 2026-09-03
|
||||
- Commands run: build; mock plumbing run; live teach/recall E2E; task suite.
|
||||
- Observed result: task suite 32/32; teach run replied REMEMBERED and session JSONL persisted host-side; recall run resumed (-c) and answered exactly 'mosaico'; single continued session file (no duplicate sessions).
|
||||
- Failure or correction: none. Design note: ephemeral (--no-session) remains the default when no session is declared.
|
||||
|
||||
## Phase 11: Operator ergonomics (M7)
|
||||
|
||||
### Entry 11.1 — before
|
||||
|
||||
- Timestamp: 2026-09-03
|
||||
- Intended action: mosaic-task.mjs show <runId> (full record + snapshots + artifacts, traversal-safe), list with workspace/session columns, RELEASE -> 0.0.6, package + health-gated activate, docs (Gitea #24).
|
||||
- Reason: Run records are only as valuable as they are inspectable; release activation closes the loop on container-content changes.
|
||||
- Expected result: show works for real/missing/traversal ids; suites green; 0.0.6 active.
|
||||
|
||||
### Entry 11.2 — after
|
||||
|
||||
- Timestamp: 2026-09-03
|
||||
- Commands run: build; show on real/missing/traversal ids; full sweep; release package + activate.
|
||||
- Observed result: config 24/24, task 32/32, release 14/14, verify PASS; 0.0.6 packaged and activated via exact-marker health gate.
|
||||
- Failure or correction: showRun initially rejected valid run ids (lowercase-only regex vs uppercase timestamp) and crashed on missing ids (uncaught readdir) — both fixed and covered.
|
||||
|
||||
## Autonomous run result
|
||||
|
||||
M5 tagged `workspace-capabilities-v1`, M6 tagged `sessions-v1`, M7 tagged `operator-ergonomics-v1`; release 0.0.6 active. Tracker: docs/plans/2026-09-03_autonomous-run.md.
|
||||
|
||||
|
||||
|
||||
@@ -119,6 +119,31 @@ adapters/<name>/adapter.sh env in: MOSAIC_SYSTEM_PROMPT_FILE, MOSAIC_REQUEST,
|
||||
|
||||
See `adapters/README.md` for the full contract.
|
||||
|
||||
## Workspaces, capabilities, sessions (M5/M6)
|
||||
|
||||
Optional task fields extend what an agent can do — all defaulting to the previous behavior:
|
||||
|
||||
```json
|
||||
{
|
||||
"workspace": "demo", // ":run" ephemeral, or persistent dataRoot/workspaces/<name>
|
||||
"capabilities": { "tools": ["bash", "read"] }, // pi tool allowlist; absent = no tools
|
||||
"session": "demo" // persistent session at dataRoot/sessions/<name>
|
||||
}
|
||||
```
|
||||
|
||||
- The adapter runs inside the workspace; files it writes are host-visible (`dataRoot/workspaces/<name>`).
|
||||
- Sessions persist via pi's documented `--session-dir`; a follow-up run in the same session resumes the conversation (`-c`) and can recall prior context. Distinct names never share state. Ephemeral (`--no-session`) remains the default when no session is declared.
|
||||
- Selection authority: config for adapter/provider/model; the task file for workspace/capabilities/session.
|
||||
|
||||
Inspect anything:
|
||||
|
||||
```bash
|
||||
node scripts/mosaic-task.mjs list # runs with task/workspace/session columns
|
||||
node scripts/mosaic-task.mjs show <runId> # full record + snapshots + artifacts
|
||||
```
|
||||
|
||||
Demo fixtures: `tasks/workspace-demo.json`, `tasks/session-demo-1.json` + `tasks/session-demo-2.json`.
|
||||
|
||||
See `docs/plans/2026-09-02_atomic-mosaic-foundation.md` for the full plan.
|
||||
|
||||
Inside the container:
|
||||
|
||||
@@ -13,8 +13,8 @@ Advance the Mosaic Stack rebuild several verified layers in one batch, focused o
|
||||
|---|---|---|
|
||||
| M5 | Task workspaces + capability envelope (tools allowlist) | DONE |
|
||||
| M6 | Named sessions — persistence and resume (L1) | DONE |
|
||||
| M7 | Operator ergonomics: run inspection commands | DONE (partial by design) |
|
||||
| — | Release 0.0.6 packaged + health-gated activated | DONE |
|
||||
| M7 | Operator ergonomics: run inspection commands | DONE |
|
||||
| — | Releases 0.0.5+0.0.6 packaged; 0.0.6 health-gated activated | DONE |
|
||||
|
||||
Explicitly deferred (do not mistake for forgotten):
|
||||
- Claude/Codex/OpenCode adapters (owner: focus on Pi for now)
|
||||
@@ -60,14 +60,17 @@ Explicitly deferred (do not mistake for forgotten):
|
||||
6. Inspect any run: `node scripts/mosaic-task.mjs show <runId>`
|
||||
7. Gitea: milestones M5/M6/M7 closed; issues referenced by merge commits
|
||||
|
||||
## Results (to be filled at end of run)
|
||||
## Results
|
||||
|
||||
- M5: pending
|
||||
- M6: pending
|
||||
- M7: pending
|
||||
- Release activation: pending
|
||||
- Final suite counts: pending
|
||||
- Corrections encountered: pending
|
||||
- M5 merged on `main` (merge commit `ddb1554`), tagged `workspace-capabilities-v1`
|
||||
- M6 merged on `main` (merge commit `4e2a413`), tagged `sessions-v1`
|
||||
- M7 merged on `main`, tagged `operator-ergonomics-v1`
|
||||
- Release 0.0.6 packaged, health-gated activated, full sweep green
|
||||
- Suites at end of run: config 24/24, task 32/32, release 14/14, verify PASS
|
||||
- Build log: Phases 9 (M5), 10 (M6), 11 (M7) appended with corrections
|
||||
- Corrections encountered: dropped constant from a failed atomic edit batch (SUPPORTED_TOOLS); dash `export` output format vs `env`; three selftest authoring defects; showRun id-regex case sensitivity + missing-run crash. All fixed and covered by tests.
|
||||
- Commits pushed incrementally; nothing left uncommitted
|
||||
- Live proof: workspace file host-visible; session teach/recall ('mosaico') verified
|
||||
|
||||
## Next steps after this run (not started)
|
||||
|
||||
|
||||
+58
-2
@@ -365,17 +365,69 @@ function listRuns() {
|
||||
for (const runId of entries) {
|
||||
let status = "unknown";
|
||||
let taskId = "-";
|
||||
let workspace = "-";
|
||||
let session = "-";
|
||||
try {
|
||||
const result = JSON.parse(fs.readFileSync(path.join(root, runId, "result.json"), "utf8"));
|
||||
status = result.status;
|
||||
taskId = result.taskId;
|
||||
workspace = result.workspace ?? "-";
|
||||
session = result.session ?? "-";
|
||||
} catch {
|
||||
// Incomplete run record; report as unknown.
|
||||
}
|
||||
process.stdout.write(`${runId} ${status.padEnd(9)} ${taskId}\n`);
|
||||
process.stdout.write(`${runId} ${status.padEnd(9)} task=${taskId.padEnd(18)} ws=${String(workspace).padEnd(10)} session=${session}\n`);
|
||||
}
|
||||
}
|
||||
|
||||
function showRun(runId) {
|
||||
if (!/^r-[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(runId)) {
|
||||
fail(4, `invalid run id: ${JSON.stringify(runId)} (expected r-<id>)`);
|
||||
}
|
||||
const resolved = loadConfig();
|
||||
const dir = path.join(runsRoot(resolved), runId);
|
||||
if (!fs.existsSync(dir)) {
|
||||
fail(4, `run not found: ${runId} (under ${runsRoot(resolved)})`);
|
||||
}
|
||||
|
||||
const read = (name) => {
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(path.join(dir, name), "utf8"));
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
const result = read("result.json");
|
||||
const task = read("task.json");
|
||||
const mission = read("mission.json");
|
||||
|
||||
process.stdout.write(`run: ${runId}\n`);
|
||||
if (result) {
|
||||
process.stdout.write(
|
||||
[
|
||||
`status: ${result.status}${result.reason ? ` (${result.reason})` : ""}`,
|
||||
`task: ${result.taskId}`,
|
||||
result.missionId ? `mission: ${result.missionId}` : null,
|
||||
result.workspace ? `workspace: ${result.workspace}` : null,
|
||||
result.session ? `session: ${result.session}` : null,
|
||||
result.tools ? `tools: ${result.tools.join(", ")}` : null,
|
||||
`adapter: ${"(see config)"} provider=${result.provider} model=${result.model}`,
|
||||
`request: ${JSON.stringify(result.request)}`,
|
||||
`response: ${JSON.stringify(result.response)}`,
|
||||
result.expectedExact !== null && result.expectedExact !== undefined ? `expected: ${JSON.stringify(result.expectedExact)}` : null,
|
||||
`timing: ${result.startedAt} -> ${result.finishedAt} (${result.durationMs} ms)`,
|
||||
`exit: ${result.exitCode}${result.signal ? ` signal=${result.signal}` : ""}`,
|
||||
].filter((line) => line !== null).join("\n") + "\n",
|
||||
);
|
||||
} else {
|
||||
process.stdout.write("result.json: (missing or unreadable)\n");
|
||||
}
|
||||
if (task) process.stdout.write(`task snapshot: ${"task.json"} present\n`);
|
||||
if (mission) process.stdout.write(`mission snapshot: ${mission.id} - ${mission.objective}\n`);
|
||||
process.stdout.write(`artifacts: ${fs.readdirSync(dir).map((f) => `${f}`).join(", ")}\n`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const operation = process.argv[2];
|
||||
const target = process.argv[3];
|
||||
|
||||
@@ -392,9 +444,13 @@ switch (operation) {
|
||||
if (!target) fail(4, "usage: mosaic-task.mjs run <taskFile>");
|
||||
runTask(path.resolve(target));
|
||||
break;
|
||||
case "show":
|
||||
if (!target) fail(4, "usage: mosaic-task.mjs show <runId>");
|
||||
showRun(target);
|
||||
break;
|
||||
case "list":
|
||||
listRuns();
|
||||
process.exit(0);
|
||||
default:
|
||||
fail(4, `unknown operation: ${JSON.stringify(operation ?? "")} (expected validate | run | list)`);
|
||||
fail(4, `unknown operation: ${JSON.stringify(operation ?? "")} (expected validate | run | show | list)`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user