diff --git a/docs/plans/2026-09-26_lead-decisions.md b/docs/plans/2026-09-26_lead-decisions.md index e83363dca..92bbd5261 100644 --- a/docs/plans/2026-09-26_lead-decisions.md +++ b/docs/plans/2026-09-26_lead-decisions.md @@ -3171,3 +3171,41 @@ which stay with him. Each item names who decided it and what happened. interaction for the user." Sage's PRD 1.2 draft is `agents/sage/work/prd-v1-2/2026-10-14_prd-1.2-amendment-draft.md`. No rows, role files or business-file edits until Jason approves it. + +142. **PRD 1.2 approved: the chief of staff, the assistant, threads and + agent limits (2026-10-14, Jason, relayed by Mos in sage-0185 to + sage-0187).** Jason approved the draft in Q149 on one condition: + "approved as long as the agent limits are tunable via "Settings" in + the WebUI and via the CLI tooling." He set the limits in Q150, quoted + in full under Launching in the PRD: "No hard cap on agent limits. All + tunable." Mos read Q150 back as Q151, with the default that launches + over a limit are refused and logged and a run that hits its token + limit is stopped, and Jason answered "Q151: yes". Mos checked the + final draft against Q147 to Q151 and gave the go for build step 1 + (sage-0187). + + Mos ruled on the draft's choices 6 to 9, within the approved spec, + and is reporting them to Jason, who can change any of them: + - 6. Count every token kind the harness reports, apply limits to the + sum, and show each kind in Settings. + - 7. "Absolute" means a running total that resets only by hand. + - 8. Stops land at the end of a turn. The 900-second turn clock + bounds the overrun. Stops inside a turn (build step 9b) stay + optional and aren't filed until the token rows show a need. + - 9. Only the stack's own use counts, and Settings says so. + + Mos added one note: REQ-LIMIT-2 measures the daily quota in tokens + as a draft position, since Q150 names no unit and OAuth plans meter + by usage windows. The PRD says so. + + Mos's proxy facts (sage-0188, CLIProxyAPI v8.0.29 public source): + per-client-key tokens exist only in the usage queue, which is off by + default, keeps 60 seconds, drains on read, carries the client key in + plaintext and sits behind the management API. The proxy has no + per-key quota. So stack-side counting (step 7) stays, and the proxy + route for step 9c is an open design point riding on #237. Management + API access is Jason's call. + + Mos's first paragraph in sage-0187 also listed 9b as its own row. + Ruling 8 is the later and more specific instruction, so Sage left 9b + unfiled. Decision 123 applies to build steps 5 and 11. diff --git a/docs/plans/2026-10-14_prd-1.2-build.md b/docs/plans/2026-10-14_prd-1.2-build.md new file mode 100644 index 000000000..1141bc2b7 --- /dev/null +++ b/docs/plans/2026-10-14_prd-1.2-build.md @@ -0,0 +1,354 @@ +# PRD 1.2 build rows (2026-10-14) + +Filed by Sage after Mos's go for build step 1 (sage-0187, lead decision +142). The requirements are PRD version 1.2, `docs/prd/mosaic-stack.md`, +landed in 7eb5ad0cb. Each section below is one queue row's brief. The +steps keep the PRD 1.2 draft's numbering (`agents/sage/work/prd-v1-2/`). + +## Rules for every row in this file + +- Cite the PRD 1.2 requirement ids the row meets in the packet, and in a + test's name or comment where a requirement drives it. +- Targeted builder gate (decision 136). Sage reruns the full gate at the + landing target. Nothing goes live except through a re-pin on Mos's go + (decision 109). A landing that changes `packages/webui/src/public/` + waits for Mos's go too (decision 123). +- No row edits Jason's organization file or role workspaces. If a row + needs his file changed, the packet gives the exact change and Sage + sends it to Mos. +- A bus schema change ships a migration, tested on a copy of a + schema-current database, and lands with a re-pin that has downtime. +- One mutant per check, listed in the packet. Each fails a test. +- If a row needs a decision its brief doesn't make, stop and ask Sage. + Don't widen the row. +- Limits have no ceiling in code (Q150). Don't add one back as a + constant, a validator bound or a default that can't be changed. + +## Role files for the chief of staff and the assistant + +Build step 2, small. Gitea #1613. + +- Owner: researcher (wrote the chief-of-staff role file in row 84). +- Reviewer: filbert (decision 130: role files, no sandbox change). +- Requirements: REQ-COS-1, REQ-ASSIST-1, REQ-ROLE-4, REQ-LAUNCH-1. + +**Files owned.** `roles/assistant.json` and `roles/assistant.md` (new), +`roles/chief-of-staff.json` and `.md`, `roles/pm.json` and `.md`, and the +tests that pin role files (`packages/business/tests/chief-of-staff.test.mjs` +and any other the owner finds). + +**What ships.** + +1. `roles/assistant.json` and `.md`. The contract is REQ-ASSIST-1: the + user's main agent, talks with the user in a thread, passes work for + the organization to the chief of staff, and never directs other + agents, creates or assigns tasks, or launches sessions. Authority is + `message.send` within role and nothing else. No credentials. Tools as + read-only as the chief of staff's. +2. `roles/chief-of-staff.json` gains `role.launch` (decision 139, Q144). + Its contract is rewritten to REQ-COS-1: hires and terminates, owns the + organization's structure, launches, takes work from the assistant and + never talks to the user. Remove every "main point of interaction" duty + left from decision 140. +3. `roles/pm.json` drops `role.launch`, and `roles/pm.md` no longer + launches. The PM takes work from the chief of staff. +4. The packet lists the change Jason's organization file needs: the + assistant and chief-of-staff instances, and `launch.by` naming the + chief of staff. Until Jason makes it, the broker still gates the chief + of staff's launches (`broker.mjs:305`), and that's expected. + +**Not in this row.** The broker's send limits (step 3), ending runs and +the organization-file verb (step 6), threads (step 4). + +**Acceptance.** Every role file loads through the business validator. +Tests pin that the assistant's authority is `message.send` only, that +the chief of staff holds `role.launch`, and that the PM doesn't. All +existing suites pass. + +## The broker limits who the assistant sends to and hears from + +Build step 3, small to medium. Gitea #1614. + +- Owner: researcher, after step 2. +- Reviewer: filbert (broker message check, no sandbox change). +- Requirements: REQ-ASSIST-2. + +**Files owned.** `packages/business/src/role.mjs` (one new optional role +field), `packages/bus/src/broker.mjs` (`message.send` only), +`roles/assistant.json`, and tests in both packages. + +**What ships.** + +1. An optional role field naming the roles a holder may send to and the + roles that may send to it. The owner picks its name and shape and + records why. `human` names the user. +2. In `message.send`, the broker refuses a send when the sender's role + has the field and the target isn't in it, or when the target's role + has the field and the sender isn't in it. Each refusal is logged with + its reason, the way the broker logs other refusals. +3. `roles/assistant.json` sets both lists to the user and the chief of + staff. +4. A role without the field behaves exactly as today. +5. The packet says what happens to a tester's send to the assistant. The + default is refused, per REQ-ASSIST-2. + +"In the thread" in REQ-ASSIST-2 needs threads, so it lands in step 4. + +**Acceptance.** Tests for both directions, for an allowed send each way, +and for a role without the field. Mutants: drop each direction's check +and drop the refusal log. + +## Projects and threads in the bus + +Build step 4, large. Gitea #1615. + +- Owner: darkwing, after step 3. It also follows darkwing's rows 116 and + 118. +- Reviewer: dewey (decision 130: a per-thread session store reaches the + run's HOME and the sandbox). +- Requirements: REQ-ORG-2, REQ-THREAD-1, REQ-THREAD-2, REQ-USER-2, + REQ-ROLE-2 (one assistant holder per thread), REQ-MSG-1, REQ-LAUNCH-1 + (thread sessions start on demand and stop when idle). + +**Design first.** Before code, post a design packet on the issue for +Sage's approval. It covers: + +- the schema: projects and threads, and the thread on messages, claims + and deliveries; +- the migration and its downtime; +- how REQ-ORG-2 projects relate to the organization file's `projects` + key, which today names git repositories to clone + (`packages/seat/src/checkout.mjs`); +- the per-thread session store. Claude Code's session directory and HOME + are per run today (`launcher.mjs:204-206`, `sandbox.mjs:157`). Choose a + store that outlives the run, or a rebuild of context from the thread's + messages, and say why; +- on-demand start and idle stop, with the idle time set in the + organization file; +- how REQ-USER-2 holds with the one user there is today. + +**Files owned.** Set by the approved design, within +`packages/bus/src`, `packages/seat/src`, `packages/cli/src/launcher.mjs`, +`packages/business/src` (the idle setting) and their tests. + +**Not in this row.** `mosaic thread` (step 10), the WebUI chat area +(step 11), limits (steps 5, 7 to 9a). + +**Acceptance.** + +- The migration runs on a copy of a schema-current database, and the + bus loads the result. +- Tests: one holder per thread; a message names its thread; the chief of + staff's reply comes back to the thread that sent the work; a thread's + history survives an idle stop and a restart (stub harness); one + thread's messages never reach another; an idle thread holds no session + and counts against no limit. +- Mutants for each of those. Dewey approves on the issue naming the + candidate manifest. + +## Agent limits as settings, with the organization-file write path + +Build step 5, medium to large. Gitea #1616. Decision 123 applies to the +Settings form. + +- Owner: rocko. +- Reviewer: filbert (business, launcher, CLI and Console; no sandbox + change). +- Requirements: REQ-ROLE-3 (the write path), REQ-LIMIT-1, REQ-WEB-1 + (Settings), REQ-LAUNCH-1 (refusals and launch logging). + +**Design first.** A short design note on the issue before code: the +`launch` schema with its model table, and the migration of Jason's file +from `{ opus: 4, sonnet: 4 }`. + +**What ships.** + +1. One host module for organization-file writes. It validates the whole + file with the existing validator, writes atomically, keeps the + previous version, and logs who changed what with old and new values. + An invalid change is refused, and the old file stays. +2. A `launch` schema with a model table, a provider and a model per + entry, and limits in total, per provider, and per provider and model. + It replaces family matching (`packages/seat/src/session.mjs:52-57`) + and `LAUNCH_CEILING` (`packages/business/src/vocabulary.mjs:86`). Any + limit may be unset (no limit). A value is a whole number of 0 or more, + and 0 stops new launches of that kind. 4 Opus and 4 Sonnet become + defaults. +3. A migration command for an organization file in the old shape. It + prints the new file, and writes it only when a human runs it. The row + doesn't run it on Jason's file. +4. The launcher re-reads limits at each launch (today once at start, + `launcher.mjs:123`). It refuses a launch over any limit and logs the + limit it hit. Each launch is logged with harness, provider and model. + Lowering a limit doesn't end running sessions. +5. `mosaic limits` to view and set limits and each role instance's + model, behind the human proof. +6. A Settings form in the Console that shows every limit and each + instance's model and changes them through the Console's send checks. + Today `/api/settings` is GET-only (`serve.mjs:233-236`). + +**Not in this row.** The chief of staff as a caller (step 6), token +counts and limits (steps 7 to 9a). + +**Acceptance.** Tests for the write path (atomic, previous version kept, +change log, invalid refused), each limit kind (unset, 0, at limit, over +limit), the re-read at launch, the migration, `mosaic limits` and the +form's send checks. Mutants for each. Filbert approves. The landing waits +for Mos's go (decision 123). + +## The chief of staff ends runs and changes the organization file + +Build step 6, small to medium. Gitea #1617. + +- Owner: researcher, after steps 2 and 5. +- Reviewer: filbert (broker authority, no sandbox change). +- Requirements: REQ-ROLE-2, REQ-ROLE-3, REQ-LIMIT-1, REQ-COS-1. + +**What ships.** + +1. `role.revoke` by the chief of staff needs no resolved decision. It + takes a reason and logs it. For every other role, revoke stays gated + and cites a decision (`broker.mjs:442`, `:738`). +2. A broker verb that lets the chief of staff change the organization + file, limits included, through step 5's write path. The change log + names the chief of staff as the actor. The owner names the verb. +3. `roles/chief-of-staff.json` grants both. + +**Acceptance.** Tests: the chief of staff ends a run without a decision, +logged with its reason; another role still needs one; a chief-of-staff +change is validated, written and logged like a human's; an invalid +change is refused. Mutants for each. + +## Token accounting per run + +Build step 7, medium. Gitea #1618. + +- Owner: rocko, after step 5. +- Reviewer: dewey (decision 130: the host reads files inside the run's + HOME, which the session can write). +- Requirements: REQ-LIMIT-2 and REQ-LIMIT-3 (their base), REQ-WEB-1 (the + readout). + +**What ships.** + +1. After each turn, the host reads usage from the Claude Code transcript + in the run's HOME (`home/.claude/projects/