Files
stack/docs/prd/mosaic-stack.md
T
jason.woltjeandClaude Opus 5.5 c57998772d docs(prd): PRD draft 0.2 with requirement ids; lead decision 48
PRDY round 2 answers, and Researcher's Vikunja and Pocket ID report as
a record.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 14:48:15 -05:00

9.5 KiB

PRD: Mosaic Stack

  • Status: draft, version 0.2. Jason approves it, and once approved it is never edited in place. Changes after approval are a new version.
  • Owner: Jason. Sage writes it from the PRDY interview.
  • Template: PRDY "software" (v1/packages/prdy/src/templates.ts), filled by hand until PRDY is ported.
  • Interview record: Sage's thread 1ef1e4f8. Round 1 is lead decision 45. Round 2 is lead decision 48. Design inputs are lead decisions 43, 44, 46 and 47.

Introduction

Objective

The working north star, ratified as lead decision 44: "Jason declares businesses, projects and roles. Agents in those roles carry the work end to end under declared policy, and only gated decisions reach him."

Context

See docs/plans/2026-10-04_foundation-direction.md. Agents already do real work here. But a person has to launch them, relay between them, hand them credentials and correct them. Jason has no CLI and no complete WebUI, and outstanding decisions don't collect anywhere.

Users

For now the only user is Jason. The stack must still be installable by outside users later, which means:

  • an installer;
  • no paths or names specific to Jason;
  • configuration through the variable layers.

(Round 1, answer D.)

Problem statement

In priority order (round 1):

  1. Admin falls on Jason. He launches agents, relays between them, finds credentials and corrects them.
  2. Jason can't see what the agents are doing or what's waiting on him.
  3. Agents drift from what the business needs. Nothing ties a piece of work to a stated goal.

Scope and non-goals

Hard limits

The stack doesn't do any of these. Each one is either a gated decision or off the table (round 1, "all five"):

  • A. Spend money without Jason.
  • B. Speak externally as Jason without his approval.
  • C. Give agents Jason's personal credentials. Each role gets its own.
  • D. Become a multi-tenant SaaS in v1.
  • E. Replace Claude Code, Codex or Pi. It wraps them.

Where it runs

Self-hosted, on Jason's machines and homelab (round 1, "A first"). Cloud workers aren't ruled out later.

Out of scope for v1

Round 2, "all":

  • businesses other than Mosaic Stack (SetSpark joins after v1);
  • minting credentials (v1 hands out tokens Jason creates);
  • Codex (v1 covers Pi and Claude Code, and Codex comes next);
  • Pocket ID and SSO;
  • access from beyond localhost.

Requirements

Every task the stack creates cites one of these ids. A task with no parent requirement is refused.

Roles

  • REQ-ROLE-1. Each role is a reviewed file, roles/<name>.json at version 2. The file holds:

    • a contract;
    • an authority map over a closed vocabulary of actions;
    • the credentials it needs, by service and scope.

    Any action the file doesn't list is gated.

  • REQ-ROLE-2. Each role instance has one holder at a time. A claim takes a lock and logs it, and releasing the role or ending the run frees it. Revoking a stuck holder is gated, and the revoke cites a resolved decision.

  • REQ-ROLE-3. A business file declares the role instances, the arbiters and the credential references. The user writes it, the stack never writes it, and a missing or invalid file fails closed.

  • REQ-ROLE-4. The first business is Mosaic Stack, with four role instances:

    • PM, held by Sage;
    • CTO, held by Darkwing;
    • coder;
    • reviewer.

    The schema can also declare CEO, CFO and the other C-suite roles.

Variables

  • REQ-VAR-1. Variables come in four layers: system, business, project and agent. Every key is declared in code with a type, the layers allowed to set it, and a merge rule.
    • The most specific layer wins for plain values.
    • Limits intersect, so a lower layer can only narrow them.
    • An unknown key is refused, and so is a key set at a layer it isn't allowed in.
  • REQ-VAR-2. ~/.config/mosaic-dev/config.json stays the only system config, and nothing writes it automatically. A secret appears only as a reference, either a file path or an environment variable name. The stack checks a referenced file with stat and never reads its contents.

Credentials

  • REQ-CRED-1. Every role instance has its own token for each service (Gitea, Vikunja). In v1 Jason creates these tokens by hand. The broker holds them, and they never enter an agent's environment or files.
  • REQ-CRED-2. A session that finds only founder credentials stops.

Decisions

  • REQ-DEC-1. Every decision is a record in an append-only SQLite table. The record holds:

    • the role and run that raised it;
    • its class and action;
    • the options and a recommendation;
    • who resolved it;
    • its timestamps.

    A correction is a new row.

  • REQ-DEC-2. Routing follows the decision's class:

    • routine and within-role decisions are logged;
    • cross-role decisions go to the business's arbiter for that domain;
    • gated decisions go to the human.
  • REQ-DEC-3. A human resolution comes only from a mosaic CLI session started outside any agent run. Agents reach the decision store only through a broker. The broker stamps role and run from the launch record.

  • REQ-DEC-4. A gated decision that blocks work reaches Jason right away: in the CLI inbox, plus a Discord DM. Everything else goes into one daily digest. (Round 2, answers 2A and 3A.)

Messages

  • REQ-MSG-1. Messages are addressed to a role, not a thread. They are append-only and go to whoever currently holds the role. T3 and Discord carry messages; the address is still the role.

Tasks

  • REQ-TASK-1. Tasks live in Vikunja, reached through /api/v2 with a bot token for each role. Each task cites a requirement id. Only the assigned role writes a task's mutable fields. A person's edits come in as events.
  • REQ-TASK-2. Polling for changes since the last check is the source of truth, and webhooks only trigger a check sooner.
  • REQ-TASK-3. The installer offers two choices: point at an existing Vikunja, or deploy the bundled one. The bundled one is the unmodified upstream image. No Vikunja code enters the repository.
  • REQ-TASK-4. The queue keeps the agents' lock, review rounds and audit log. No field syncs in both directions.

Interface

  • REQ-CLI-1. mosaic is the front door. From it Jason can:
    • talk to the PM;
    • see the decision inbox and resolve decisions;
    • see tasks and agents;
    • follow the trail of any piece of work.
  • REQ-CLI-2. The PM session that mosaic talks to is one the stack launches and owns, running Pi or Claude Code without a window. The product doesn't depend on T3. (Round 2, answer 5A.)
  • REQ-WEB-1. The WebUI shows the same data as the CLI: conversations, the inbox, tasks, agents and trails.

Launching

  • REQ-LAUNCH-1. The PM launches role sessions within limits written in the business file:

    • role instances only;
    • at most 4 Opus and 4 Sonnet sessions at once;
    • every launch logged;
    • revocable with one word.

    (Round 2, answer 6A.)

Meta-harness

  • REQ-HARN-1. The meta-harness generates each session's launch bundle from the role and the resolved variables:

    • the prompt;
    • the policy;
    • skills;
    • typed tools for the vocabulary actions;
    • a manifest.

    Pi comes first, then Claude Code. Codex comes after v1.

  • REQ-HARN-2. Four layers actually enforce the rules:

    • credential scope;
    • verbs that refuse without a resolved decision;
    • the harness's tool limit;
    • a container.

    Hooks only refuse early and log, and no document may call them enforcement. A probe run against the live harnesses must pass before any build relies on a block.

Events

  • REQ-EVT-1. Every step in slice 1 emits an event to the append-only store. Measures are computed from events, never from tagging chat messages by hand.

Acceptance criteria and success measures

v1 is done when 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;
  • every step leaves a trail in the CLI and the WebUI.

(Round 1, answer A.)

Technical considerations

  • Vikunja v2.7.0 is AGPL-3.0. Its API tokens can't create users or mint more tokens. A Mosaic-owned account mints one bot user and one scoped, expiring token per role. The owner password for that account is the one high-value Vikunja secret. Instance admin features need a paid licence, and the plan doesn't use them. Source: agents/researcher/work/2026-10-04_vikunja-pocketid.md.
  • Gitea tokens never expire, and creating one needs a password or the host shell. Rotation will be a scheduled step with a record.
  • Pocket ID v2.17.0 is BSD-2. It offers passkey-only login for people and a client-credentials flow, but it can't issue tokens that Vikunja or Gitea accept. Agents use per-service role tokens. Pocket ID is wired in after v1.
  • Decisions, messages, role claims and events share one SQLite file, <dataRoot>/bus/bus.sqlite. A guard on every table refuses UPDATE, DELETE and REPLACE. Run records stay write-once files.
  • None of the three harnesses' hooks fails closed by default except Pi's tool_call. Source: agents/filbert/work/meta-harness-survey-2026-10-04.md.

Risks and open questions

Round 3 sets these. Known so far:

  • Agents running as the same OS user as Jason can write anything that user can write. Until seats run as another user or in a container, the broker is a rule they follow, not a wall.
  • The Vikunja owner password and Gitea token creation are gated. Jason holds them.

Milestones

Slice 1, in the order on the foundation direction page. The slice 1 brief maps each step to requirement ids.