Files
stack/docs/plans/2026-10-04_slice-1.md
T
2026-10-04 21:50:59 -05:00

21 KiB

Slice 1: the first working system (2026-10-04)

Status: written by Sage, lead, for Jason's acceptance. Each ## section below is the brief for one queue row, in the shape set by docs/plans/BRIEF-TEMPLATE.md. This first section covers what all the rows share.

What slice 1 is

The foundation direction page (docs/plans/2026-10-04_foundation-direction.md, lead decisions 43 and 44) describes it. Jason opens mosaic in a terminal and talks to the PM role. The PM files a task in Vikunja, linked to a PRD requirement, and assigns it to the coder role. The stack launches that agent with the role's contract, variables and credentials. The agent works. A gated question reaches Jason's inbox, and he answers it in one word. The task is reviewed and closed. The CLI and the WebUI both show the trail, and every step is an event in the bus.

Acceptance is the PRD's: Jason gives one-sentence requests in the CLI, and Mosaic Stack builds pieces of itself from them, five pieces in a row. For each piece, only gated decisions reach Jason, and every step leaves a trail in the CLI and the WebUI (row S7).

Sources

  • PRD: docs/prd/mosaic-stack.md, draft 0.4. Every row cites its requirement ids. If an id changes before Jason approves the PRD, Sage updates this brief and re-pins the rows.
  • Data model: agents/darkwing/work/slice1-data-model-2026-10-04.md, plus addendum A and addendum B. Where they disagree, B wins over A, and A wins over the note.
  • Vikunja facts: agents/researcher/work/2026-10-04_vikunja-pocketid.md and agents/researcher/work/2026-10-04_vikunja-probes.md.
  • Harness facts: agents/filbert/work/meta-harness-survey-2026-10-04.md.
  • Schema: agents/darkwing/work/slice1-proto/schema-v2.sql, which becomes schema v3 with addendum B section 5 (lead decision 52, B3) before row S2 starts.
  • Rulings: lead decisions 43 to 53.

What is not a wall in slice 1

Agents run as Jason's OS user (PRD round 3, 3A). Any agent process can read or write anything Jason's user can, including the bus file, the broker's socket and the token files. The broker, the verbs and the hooks are rules a well-behaved agent follows. They don't stop a hostile one. Two things do hold in slice 1:

  • agents never receive a service token, so a role can act in Gitea or Vikunja only through the broker, which checks its authority map;
  • the harness's tool limit, on the harnesses where row S0 proves it.

Containers come after slice 1. No document in this slice may call a hook or a broker check "enforcement" without that qualifier.

Model-provider login (Claude, the Pi providers) stays as it is today, because slice 1 has no container to isolate it in. REQ-CRED-1 and REQ-CRED-2 cover the service tokens, Gitea and Vikunja.

Field table

From addendum B section 4. The broker's task verbs enforce it (REQ-TASK-1).

Field Writer Action Vikunja call
title, description pm at create; after assignment only through a resolved task.scope.change task.create, task.scope.change POST /projects/{p}/tasks; PATCH /tasks/{t}
due date, priority pm task.schedule, task.priority.change PATCH /tasks/{t}
percent done the assigned role task.update.assigned PATCH /tasks/{t}
state (bucket) other than done the assigned role task.update.assigned PUT /projects/{p}/views/{k}/buckets/{b}/tasks
done pm, after a review verdict task.close the same move, into the done bucket
assignee pm task.assign, task.reassign POST /tasks/{t}/assignees; DELETE /tasks/{t}/assignees/{u}
labels pm task.create, task.schedule POST /tasks/{t}/labels; DELETE /tasks/{t}/labels/{l}
relations pm task.create, task.schedule POST /tasks/{t}/relations; DELETE /tasks/{t}/relations/{kind}/{other}
comments any role on the task, append only task.update.assigned, message.send POST /tasks/{t}/comments

A PATCH body carries only title, description, due_date, priority and percent_done, and only the keys the action owns. Vikunja answers 200 to a bucket_id in a PATCH and doesn't move the task, so the broker refuses that key before sending. Reopening is a person's act.

Rows and order

Row Piece Owner Reviewer After
S0 Harness probe matrix filbert darkwing none
SR Install runbook for slice 1 identities sage darkwing none
S1 Roles v2, business and project files, variable layers darkwing filbert none
S2 The bus and the broker core rocko darkwing schema v3
S3 Tasks: the Vikunja adapter, broker task verbs and sync darkwing filbert S1, S2, SR
S4 The mosaic CLI: inbox, decide, tasks, agents, trail rocko filbert S2, S3
S5 WebUI: inbox, tasks, agents and trails; CHAT-03 Gate E dewey darkwing, filbert S4
S6 Meta-harness and launching; the PM moves off T3 filbert darkwing S0, S1, S3
S7 Acceptance: five pieces in a row sage none S5, S6

Rows S0, SR and S1 start at once. S2 starts once Darkwing lands schema v3 in slice1-proto/. Every row's gate is a reviewer's approval on the row's issue plus the named suites green on an index export, and Sage commits. Row S7's gate is Jason's.

Every row emits the events its steps produce (REQ-EVT-1), using the closed kind list in the schema. A row that needs a new kind changes the schema through row S2's owner and the CTO's review.

Slice 1 S0: harness probe matrix (Pi and Claude Code hooks, pinned versions)

Problem

REQ-HARN-2 says a probe run against the live harnesses must pass before any build relies on a block. Filbert's survey (section 9) checked Pi's tool_call block and Claude's hook docs against source and docs, but ran nothing. It also found that Claude command hooks at a mistyped path leave the gate silently off, and that a Codex hook that crashes doesn't block. Row S6 can't choose which layer stops what until these are measured.

Owner and reviewer

  • Owner: filbert, who wrote the survey.
  • Reviewer: darkwing.

Files owned

  • agents/filbert/work/slice1-probes/ (scripts, outputs, the matrix).
  • Nothing outside it. The probes run in a scratch directory under $HOME, not /tmp, with no service tokens and no network beyond the model providers the harnesses already use.

What ships

A matrix for Pi 0.85.1 and Claude Code 2.1.289, each at the pinned version, with one row per case:

  1. a gate that blocks;
  2. a gate that crashes (throws, or exits nonzero other than the block code);
  3. a gate at a missing path;
  4. a gate that times out;
  5. a bash route to the same action as a blocked tool;
  6. for Claude Code, an Agent SDK callback hook that throws (the survey's "not established").

Each cell records the command, the exact outcome (tool ran or didn't, the message the model saw) and whether the harness fails closed. The matrix ends with one line per case saying which layer slice 1 can rely on: the tool limit, the hook, or neither. Codex is out of scope until after v1.

Out of scope

  • Building the generator or any adapter (row S6).
  • The Vikunja points still unverified in addendum B section 7. Row S3 probes those before relying on them.

Gate

Darkwing approves the matrix on the row's issue. Sage reads the "rely on" lines and copies them into row S6's brief section before S6 starts.

Slice 1 SR: install runbook for slice 1 identities

Problem

REQ-CRED-1 (round 3, 1A and 2A) has Jason create each role's tokens by hand. No runbook exists, and the identities are new: four Gitea bot users, four Vikunja bot-<role> users and one read-only bot-<business>-sync. Vikunja tokens must expire, and Vikunja accepts a past expiry date. Gitea tokens don't expire.

Owner and reviewer

  • Owner: sage.
  • Reviewer: darkwing, for the Vikunja scope map.

Files owned

  • docs/guides/slice-1-identities.md.
  • A business file template, templates/business/mosaic-stack.example.json, with credential references only, coordinated with row S1's schema.

What ships

A runbook Jason follows in about 20 minutes:

  • creating pm-bot, cto-bot, coder-bot and reviewer-bot in Gitea, each with a token at the narrowest scope that does that role's work, and each token written to a 0600 file outside the repository;
  • creating the Vikunja owner account (a deployed bundled instance or an existing one, REQ-TASK-3), the four role bots and the sync bot, the project shares (write for roles, permission 0 for sync), and one token per bot with the scope map from addendum B section 2 and an explicit expiry date;
  • the business file entries that reference each token file;
  • how to check each token with stat only, and a mosaic check that calls the broker's startup scope probe once row S3 exists;
  • rotation: what to do before a Vikunja token expires, and the Gitea rotation step with a record.

No token value appears in the runbook, a command line or a URL.

Out of scope

  • Any script that creates users or tokens. Minting is out of v1.
  • Running the runbook. Jason does that, and row S3's live tests wait for it.

Gate

Darkwing approves the scope tables. Jason runs it, and the broker's startup probe passes for every identity.

Slice 1 S1: roles v2, business and project files, variable layers

Problem

roles/ holds conductor-policy.json and researcher.json at version 1, with tool and network ceilings only. No business roles exist, no authority map, no credential needs, and no variable layers beyond ~/.config/mosaic-dev/config.json. REQ-ROLE-1 to 4 and REQ-VAR-1 and 2 need all of these.

Owner and reviewer

  • Owner: darkwing (CTO), who designed them (note sections 1 and 2).
  • Reviewer: filbert.

Files owned

  • roles/pm.json, roles/cto.json, roles/coder.json, roles/reviewer.json (new, version 2).
  • The role validator and resolve-role in scripts/mosaic-task.mjs, to accept version 2 while version 1 files keep working.
  • packages/business/ (new): the business file and project file loaders, the variable key registry (type, allowed layers, merge rule) and the resolver.
  • contracts/ only if the note's closed action vocabulary must be image-baked. Darkwing says which in the build packet, and a contract change needs Sage's review.
  • docs/TOOLS.md entries for any new command.

What ships

  • The role schema v2: contract, authority map over the closed vocabulary (unlisted means gated), credential needs by service and scope, and the version 1 ceilings kept.
  • The business file at ~/.config/mosaic-dev/businesses/<id>.json: role instances (PM held by Sage, CTO by Darkwing, coder, reviewer), arbiters, credential references, business variables, and the launch block (lead decision 49). The stack reads it and never writes it. A missing or invalid file fails closed.
  • The project file at <project>/.mosaic/project.json.
  • The resolver: most specific layer wins for plain values, limits intersect, an unknown key or a key at a layer it isn't allowed in is refused.
  • Tests in packages/business/tests/, plus test-config.sh and test-task.sh green.

Out of scope

  • Role claims and the one-holder rule (row S2, they live in the bus).
  • Launching anything (row S6).

Gate

Filbert approves on the row's issue. Suites green: packages/business tests, test-config.sh, test-task.sh, test-conductor.sh.

Slice 1 S2: the bus and the broker core (decisions, messages, role claims, events)

Problem

Decisions don't exist as objects. Escalations are chat lines, the waiting-on-jason state and "Input needed:" lines. Messages are addressed to T3 thread ids, not roles. REQ-DEC-1 to 3, REQ-MSG-1, REQ-ROLE-2 and REQ-EVT-1 need a store and a single writer.

Owner and reviewer

  • Owner: rocko. It needs no credentials: the broker's token handling is built and tested here with fixture tokens, and real tokens arrive in row S3.
  • Reviewer: darkwing, who wrote the schema and the prototype.

Files owned

  • packages/bus/ (new): the store over node:sqlite, the broker process, its local socket protocol, the verbs, tests.
  • <dataRoot>/bus/ at runtime, created by the broker on first start, 0700.

What ships

  • bus.sqlite from schema v3, with the open-time digest check, WAL, and every table append-only.
  • The broker: the only writer. It stamps role and run on every write from the launch record. It holds tokens in memory only, read from the business file's references, and never logs or writes them (REQ-VAR-2).
  • Verbs: raise, route and resolve a decision; send a message to a role and deliver it to the current holder; claim and release a role instance; emit an event. A verb whose action is gated refuses unless it cites a resolved decision.
  • Routing by class (REQ-DEC-2): routine and within-role are logged; cross-role goes to the business's arbiter; gated goes to the human.
  • Human resolution only from a CLI process outside any agent run (REQ-DEC-3). launch.revoked and launch.restored are written only on that path (lead decision 50).
  • Tests covering every refusal the prototype shows, plus the broker protocol, on Node 24 and Node 26.

Out of scope

  • Vikunja (row S3). The task_snapshots table ships in the schema, and row S3 writes it.
  • Delivery to Discord and the digest (row S4 owns the outbound side).

Gate

Darkwing approves on the row's issue. Suites green: packages/bus tests on Node 24 and Node 26, and every scripts/test-*.sh.

Slice 1 S3: tasks (the Vikunja adapter, broker task verbs, sync)

Problem

Tasks live in the queue, which is built for agents and has no dates, assignees Jason can see, or UI. REQ-TASK-1 to 4 put tasks in Vikunja through the broker. The probes showed what Vikunja actually does: wrong scope names in addendum A, a cursor that misses column moves and deletions, If-Match not enforced, and 401 for both an expired token and one out of scope.

Owner and reviewer

  • Owner: darkwing, who ran the probes behind addendum B.
  • Reviewer: filbert.

Files owned

  • packages/tasks/ (new): the Vikunja v2 client, the broker's task verbs, the poller, the reconcile and the startup checks.
  • packages/bus/ only to register the task verbs, coordinated with row S2's owner.
  • packages/tasks/deploy/vikunja/: the bundled option, the unmodified upstream image pinned by digest, bound to 127.0.0.1 (REQ-TASK-3). No Vikunja code enters the repository.

What ships

  • Before relying on them, probes of addendum B section 7's open points on a scratch instance, recorded in the build packet. The first is which labels GET /labels shows a bot. Until that's answered, the business file lists label ids.
  • Task verbs that follow the field table, refuse any other PATCH key, compare before writing, and write only with the acting role's token.
  • Sync with the read-only sync bot: every 30 seconds, the open-task kanban listing plus the updated cursor; an hourly full reconcile; task_snapshots rows for every read and write; task.changed.external for a person's edit.
  • Startup checks: each token's scopes probed against a task id that doesn't exist, expires_at checked locally, the kanban view's done_bucket_id checked. Any failure fails closed.
  • A task can't be created without a requirement id (REQ-TASK-1). The queue keeps the agents' lock, review rounds and audit log, and no field syncs both ways (REQ-TASK-4).
  • Tests against a recorded fake of the v2 routes, plus one live run against the instance from row SR.

Out of scope

  • Webhooks. Slice 1 has none.
  • Pocket ID and SSO (after v1).

Gate

Filbert approves on the row's issue. Suites green: packages/tasks tests, packages/bus tests, every scripts/test-*.sh, and the live run's log in the packet with no token value in it.

Slice 1 S4: the mosaic CLI (inbox, decide, tasks, agents, trail)

Problem

Jason has no front door (REQ-CLI-1). scripts/mosaic dispatches to seat and queue only. Blocking gated decisions have no route to him, and nothing sends a digest (REQ-DEC-4).

Owner and reviewer

  • Owner: rocko.
  • Reviewer: filbert.

Files owned

  • scripts/mosaic (the dispatcher).
  • packages/cli/ (new): mosaic inbox, mosaic decide <id> <option>, mosaic tasks, mosaic agents, mosaic trail <task|decision>.
  • The outbound side: a blocking gated decision goes to Jason's Discord DM through the existing connector (#1509, round 3, 4A), and a daily digest at 08:00 Central (5A). Changes inside packages/discord/ need that package's existing review rules.

What ships

  • Every command reads through the broker, never the SQLite file directly.
  • mosaic decide works only outside an agent run, and refuses inside one (REQ-DEC-3).
  • mosaic trail shows each event for a task or decision in order: request, decisions, launches, task changes, review, close.
  • Tests in packages/cli/tests/, and the connector suite green.

Out of scope

  • mosaic talk (row S6, since it needs the launched PM).
  • The WebUI (row S5).

Gate

Filbert approves on the row's issue. Suites green: packages/cli tests, test-discord.sh, every scripts/test-*.sh.

Slice 1 S5: WebUI (inbox, tasks, agents and trails; CHAT-03 Gate E)

Problem

packages/webui shows the control board and, through CHAT-03 I1 (243e153c), can drive a sealed Pi against fixtures. It doesn't show decisions, tasks or trails (REQ-WEB-1). Row 5's Gate E demonstration happens in this step (round 3, 7B).

Owner and reviewer

  • Owner: dewey, who owns the WebUI and CHAT-03.
  • Reviewers: darkwing and filbert.

Files owned

  • packages/webui/, packages/conversation/ and packages/control-board/, within their existing READMEs' rules.
  • agents/dewey/work/ for design and evidence.

What ships

  • Views for the inbox (read-only in slice 1, resolution stays in the CLI under REQ-DEC-3), tasks, agents and trails, reading the same broker data as the CLI.
  • Conversations through CHAT-03, with the I3 follow-ups in DEFERRED that the live PM session needs: the seal covering the engine command, and an explicit engine environment.
  • Gate E: the interactive demonstration with all seats, then Jason's workday ruling. Live cutover still needs its own approval.

Out of scope

  • Resolving decisions from the browser. That needs authentication, which comes with Pocket ID after v1.

Gate

Both reviewers approve on the row's issue. Suites green: webui, conversation, control-board, and every scripts/test-*.sh. Row 5's Gate E is Jason's.

Slice 1 S6: meta-harness and launching (the PM moves off T3)

Problem

Seats are launched by hand in T3, with prompts and tools set per launcher (scripts/agent-host-dev.sh, agents/rocko/launch.sh). Nothing generates a session from a role (REQ-HARN-1). The PM can't launch anyone (REQ-LAUNCH-1), and mosaic can't talk to a PM the stack owns (REQ-CLI-2).

Owner and reviewer

  • Owner: filbert, who wrote the survey and row S0's matrix.
  • Reviewer: darkwing.

Files owned

  • packages/harness/ (new): the launch bundle generator (prompt, policy, skills, typed tools for the vocabulary actions, manifest).
  • adapters/pi/ and a new adapters/claude/, under adapters/README.md's contract.
  • packages/seat/ for mosaic launch and the new mosaic talk and mosaic stop.
  • scripts/agent-host-dev.sh, to stop passing --approve (DEFERRED).

What ships

  • Bundles for Pi first, then Claude Code, from the role file and the resolved variables. Each layer the bundle relies on is one row S0 proved.
  • The PM launches role sessions through the broker, within the business file's launch block: role instances only, at most 4 Opus and 4 Sonnet sessions, every launch logged, one word from Jason stops new launches, and mosaic stop ends a running session.
  • A session that finds only founder credentials stops (REQ-CRED-2).
  • The named step where the PM moves off T3: the stack launches the PM session without a window, mosaic talk reaches it, and Sage's T3 thread hands over. After this step the product doesn't depend on T3.

Out of scope

  • Codex (after v1).
  • Containers (after slice 1).

Gate

Darkwing approves on the row's issue. Suites green: packages/harness, packages/seat, every scripts/test-*.sh. A recorded run where the PM launches a coder session and mosaic agents shows it.

Slice 1 S7: acceptance, five pieces in a row

Problem

The PRD's acceptance can only be shown end to end.

Owner and reviewer

  • Owner: sage, as the PM role.
  • Reviewer: none. Jason is the gate.

Files owned

  • agents/sage/work/slice1-acceptance/ for the run log.
  • Queue rows and issues the five pieces create.

What ships

Five one-sentence requests from Jason in the CLI, each carried to a closed, reviewed task. For each: the trail in mosaic trail and the WebUI, the decisions that reached Jason (gated only), and the event counts from the bus. Those counts are the first measures (REQ-EVT-1).

Out of scope

  • Measure targets. Slice 1 produces the first numbers, and targets come after.

Gate

Jason, from the five trails: only gated decisions reached him, and every step left a trail.