# Slice 1: the first working system (2026-10-04) Status: accepted by Jason as written, 2026-10-05 (lead decision 62). 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-` users and one read-only `bot--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/.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 `/.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. - `/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. Darkwing is second reviewer for the `packages/discord/` change and the broker host (lead decision 70). ### Files owned - `scripts/mosaic` (the dispatcher). - `packages/cli/` (new): `mosaic inbox`, `mosaic decide