diff --git a/docs/prd/mosaic-stack.md b/docs/prd/mosaic-stack.md index 66648f894..ccacabd4f 100644 --- a/docs/prd/mosaic-stack.md +++ b/docs/prd/mosaic-stack.md @@ -1,6 +1,6 @@ # PRD: Mosaic Stack -- Status: approved, version 1.12. Jason approved draft 0.4 unchanged as +- Status: approved, version 1.13. Jason approved draft 0.4 unchanged as 1.0 on 2026-10-05 (lead decision 61). They approved version 1.1, the Organization requirements, in decision 03130d08-ddce-480d-a601-e59f64744fc3 on 2026-10-14. They approved version 1.2, the chief of staff, the @@ -36,7 +36,19 @@ prd.approve for it. Version 1.12 adds requirement ids for the Q176 items the PRD lacked, drafted from the approved designs, and updates two stale lines. It takes the same path as 1.11 (mos-1229, - vikunja:19/462). + vikunja:19/462). Version 1.13 adds the session requirements from the + approved T1 item 7 design and replaces REQ-ROLE-2 and REQ-THREAD-2. It + takes the same path as 1.11 (mos-1231, vikunja:19/463). +- Version 1.13 source, Mos's ruling Q1 in mos-1231 on 2026-10-21: "T1 + item 7 is v1." It quotes Jason's Q176 lines, TARGET.md:57 "Agents can + have multiple independent sessions within a given project (thread)", + :104 "User must be able to see the number of compactions that have + occurred for a session" and :105 "User must be able to easily clear the + context for the agent to trigger a new session". Mos's reading of Q211: + "where REQ-ROLE-2 and REQ-THREAD-2 differ from line 57, line 57 wins." + And: "The ids and the REQ-ROLE-2 and REQ-THREAD-2 changes come in 1.13 + from the approved design." Mos's further readings, mos-1235 and + mos-1236, are in the Version 1.13 technical note. - Version 1.12 source, Mos's answers to the 1.11 questions in mos-1229 on 2026-10-21: "M2: agreed. 1.11 adds no ids for the Given list or Case 2 items. You draft 1.12 from the approved #414 and #416 designs as soon as @@ -297,9 +309,13 @@ parent requirement is refused. Any action the file doesn't list is gated. - **REQ-ROLE-2.** Each role instance has one holder at a time. The - assistant is the exception: it has one holder per thread at a time. A - claim takes a lock and logs it, and releasing the role or ending the run - frees it. The chief of staff may end another role's run, and that act is + assistant is the exception: within a thread it can have several + independent sessions, each with one holder at a time. No other role + holds a thread session. A claim takes a lock and logs it. An assistant + claim names the organization, thread, instance and session. Releasing + the role or ending the run frees only that holder. Creating or clearing + a session grants no role authority, and a pending approval never moves + to another session. (Version 1.13.) The chief of staff may end another role's run, and that act is logged with its reason. Anyone else who revokes a stuck holder needs a resolved decision, and the revoke cites it. The assistant is the exception here too (version 1.6, Q169): the chief of staff is the only @@ -1007,11 +1023,17 @@ Added in version 1.1 (decision 03130d08). area. Added in version 1.2. - **REQ-THREAD-1.** Threads live under projects. - **REQ-THREAD-2.** The user creates projects and names threads inside - them, from the CLI or the WebUI. Each thread is a separate assistant - session. A thread's messages are kept. When the user comes back to a - thread, after an idle stop or a restart, the assistant picks up where - it left off with that history. One thread's conversation never appears - in another's. Added in version 1.2. + them, from the CLI or the WebUI. A thread holds one or more independent + sessions of its assistant, on pi or Claude Code. Each session has its + own messages, native context, workspace and compaction count, and only + one run writes its workspace at a time. A thread's messages are kept. + When the user comes back to a session, after an idle stop or a restart, + the assistant picks up where that session left off, never in another + session. A client that names only the thread uses that thread's saved + default session. One session's conversation never appears in another + session, thread or organization, under REQ-USER-2, REQ-USER-3 and + REQ-USER-4. Added in version 1.2. (Version 1.13: sessions, from the + design `docs/plans/2026-10-21_t1-sessions.md`, 2c843d190 on `huey/t1-sessions-design`, approved by Scrooge in scrooge-0007 and scrooge-0008, D1 to D3 and D9.) - **REQ-TPL-1.** Templates exist for agent roles and for organization types. - **REQ-FED-1.** Installs can federate with each other. Person-to-person @@ -1085,6 +1107,45 @@ is drafted from and the Q176 item it covers. switch, and protected mode can clear itself but never a human kill (`docs/plans/2026-10-21_q198-system-manager.md` D13 and section 5). +Added in version 1.13 (Q176, Q211, mos-1231). Drafted from the +design `docs/plans/2026-10-21_t1-sessions.md`, 2c843d190 on `huey/t1-sessions-design`, approved by Scrooge in scrooge-0007 and scrooge-0008. They cover MVP plan T1 item 7. + +- **REQ-SESS-1.** The user sees how many compactions each session has + completed over its life, on pi and Claude Code. Starts, failed + compactions, branch summaries and repeated reads don't count. An idle + stop or a restart leaves the count as it is. A new session and a clear + successor start at zero, and a retired session keeps its count. When + the count can't be proven, it shows unknown with the reason, never + zero. Anyone who can read the thread sees the count. The organization's + owner can see a count and status, with opaque ids only, for another + user's thread it can't read, but no names, messages or summaries. + (Design D4 and D6, tests T3 and T5. Q176 TARGET.md:104.) +- **REQ-SESS-2.** Clear context ends one session and starts a fresh + successor for the same assistant. Only the thread's owning user, while + their write grant covers the thread, creates or clears a session. A + viewer, a share, the organization's owner on another user's thread and + an agent can't. Clear waits until the session stops at a turn boundary + with no child running, and shows that it is clearing until then. It + never force-stops a busy session. The old session's messages, results + and run evidence stay as they are. Its workspace stays at its path with + any unpushed work, and nothing writes there again. The successor gets + new native stores and a fresh checkout of the approved revision. It + gets nothing of the old context: no history, summary, prompt or pending + input. Undelivered old input is cancelled with a receipt, never + replayed. A repeated clear returns the same operation, and one clear + makes at most one successor. (Design D4, D5 and D7, tests T4 and T5. + Q176 TARGET.md:105.) +- **REQ-SESS-3.** Every start of a session goes through the same checks + as any launch: an open, a first send, a restart, an idle wake and a + clear successor. The checks are the human's current authority, the + configured role and harness, launch revocation, the kill switch + (REQ-KILL-1), the agent limits and admission. The claim is checked + again just before the spawn. A session never picks its own route or + priority. An admission service that is switched on but unavailable + refuses. While the kill switch is on, a clear or a new start refuses + with `processing-halted` and changes nothing. A clear never sets or + clears the kill switch. (Design D8, test T6.) + ### Creative library Added in version 1.12 (Q176 Case 2, Q211). Drafted from the Case 2 @@ -1509,6 +1570,48 @@ latest-by date and a hard target (Q176). its follow-up row 239. REQ-KILL-1 is row 191 (#1689). The REQ-LIB ids are the C2 rows in `docs/plans/2026-10-20_case2-rows.md` (#416). This amendment adds the requirements only. +- Version 1.13: + - The 1.12 text of REQ-ROLE-2 began: "Each role instance has one holder + at a time. The assistant is the exception: it has one holder per + thread at a time. A claim takes a lock and logs it, and releasing the + role or ending the run frees it." The rest, from "The chief of staff + may end another role's run", is unchanged. Who may end or revoke the + assistant is unchanged. + - The 1.12 text of REQ-THREAD-2 was: "The user creates projects and + names threads inside them, from the CLI or the WebUI. Each thread is a + separate assistant session. A thread's messages are kept. When the + user comes back to a thread, after an idle stop or a restart, the + assistant picks up where it left off with that history. One thread's + conversation never appears in another's. Added in version 1.2." + - The titles of REQ-ROLE-2 and REQ-THREAD-2 in + `packages/tasks/requirements.json` change with their text. + - Mos's readings, which the design records in section 8 (mos-1235). + They are reported to Jason after this version lands and don't go to + him as questions: + - Q1: the count is for the session's whole life. A clear successor is + a new session under TARGET.md:105 and starts at zero. + - Q2: clear waits for a proven safe stop and shows that it is + pending. It adds no force-stop, and the kill switch stays separate. + - Q3: the organization's owner doesn't clear another user's session + without that user's write authority, because "A user's messages + stay private from the owner" (REQ-USER-4). + - Mos's ruling mos-1236: sessions are for the assistant (the + `THREAD_DEFINITION` instance) only. Letting other roles hold thread + sessions would change launch policy and is outside Vikunja 463. + - Build dependency (design N2, vikunja:19/466): Claude Code's format + for a completed compaction has to be pinned offline, and a reader for + the managed session stores added. Today's reader covers pi only. + REQ-SESS-1 can't pass its acceptance test until both exist, and the + count isn't shipped for either harness until then. + - Until Q198's admission step is built, REQ-SESS-3 uses the existing + launcher and limit checks, plus row 191's kill checks once they land + (design D8). + - Moving existing threads keeps their history: each becomes one + default session, bound to its existing native store, with nothing + rewritten. The old shared instance workspace is drained and kept as + history (design D9). + - The build row is Sage's to file under Vikunja 463. This amendment + adds the requirements only. ## Risks and open questions diff --git a/packages/bus/tests/requirements.test.mjs b/packages/bus/tests/requirements.test.mjs index 2f162a186..843487d10 100644 --- a/packages/bus/tests/requirements.test.mjs +++ b/packages/bus/tests/requirements.test.mjs @@ -45,7 +45,7 @@ test('requirements returns the committed list to a holding role session, and no const pm = agent('pm'); const list = call(b, pm, 'requirements'); assert.deepEqual(list, loadRequirements()); - assert.equal(list.requirements.length, 82); + assert.equal(list.requirements.length, 85); assert.deepEqual(call(b, agent('coder'), 'requirements'), list); assert.throws(() => call(b, human, 'requirements'), /agent-required/); assert.throws(() => call(b, reader, 'requirements'), /read-only/); diff --git a/packages/tasks/requirements.json b/packages/tasks/requirements.json index d83ff6e94..7f4337049 100644 --- a/packages/tasks/requirements.json +++ b/packages/tasks/requirements.json @@ -1,9 +1,9 @@ { "prd": "docs/prd/mosaic-stack.md", - "version": "1.12", + "version": "1.13", "requirements": [ { "id": "REQ-ROLE-1", "title": "Each role is a reviewed roles/.json: contract, authority map, credentials" }, - { "id": "REQ-ROLE-2", "title": "One holder per role instance, one per thread for the assistant; the chief of staff may end a run, others need a decision" }, + { "id": "REQ-ROLE-2", "title": "One holder per role instance, one per assistant session; the chief of staff may end a run, others need a decision" }, { "id": "REQ-ROLE-3", "title": "The user and the chief of staff write the organization file; the chief of staff's writes go through a logged broker verb" }, { "id": "REQ-ROLE-4", "title": "The first organization is Mosaic Stack: chief of staff, assistant, PM, CTO, coder and reviewer" }, { "id": "REQ-ROLE-5", "title": "Q167 adds security-reviewer and merge-gate; merge-gate raises the merge in the technical domain and lands it" }, @@ -67,13 +67,16 @@ { "id": "REQ-USER-1", "title": "Users and RBAC at install and organization level; an agent reaches only its allowed projects and threads" }, { "id": "REQ-USER-2", "title": "Only a thread's owning user, their assistant and the chief of staff's replies reach the thread" }, { "id": "REQ-THREAD-1", "title": "Threads live under projects" }, - { "id": "REQ-THREAD-2", "title": "The user creates projects and named threads; each thread is its own assistant session with kept history" }, + { "id": "REQ-THREAD-2", "title": "The user creates projects and named threads; a thread holds independent assistant sessions, each with kept history" }, { "id": "REQ-TPL-1", "title": "Templates for agent roles and for organization types" }, { "id": "REQ-FED-1", "title": "Federation between installs: person-to-person in v1, agent-to-agent post-v1" }, { "id": "REQ-USER-3", "title": "Several humans per organization: the owner, and users with collaborator or viewer grants per project and instance" }, { "id": "REQ-USER-4", "title": "A thread is private to its user unless shared read-only; a share never widens a grant and ends on revoke" }, { "id": "REQ-LIFE-1", "title": "The user creates, launches, terminates, regenerates and deletes agents from the CLI and the Console" }, { "id": "REQ-KILL-1", "title": "One human command or Console button stops all work, runs, Discord and launches on an instance until a human clears it" }, + { "id": "REQ-SESS-1", "title": "Each session shows its lifetime count of completed compactions on pi and Claude Code; unknown is shown, never zero" }, + { "id": "REQ-SESS-2", "title": "Clear context retires a session after a safe stop and starts a fresh successor with none of the old context" }, + { "id": "REQ-SESS-3", "title": "Every session start passes the launch checks: authority, route, revocation, kill switch, limits and admission" }, { "id": "REQ-LIB-1", "title": "A library maps the user's writing repository onto universes, books, types and playgrounds; only the human changes it" }, { "id": "REQ-LIB-2", "title": "Scope comes from the path; lookups default to canon and a playground file is never cited as canon" }, { "id": "REQ-LIB-3", "title": "Universes stay separate; a crossover is a hybrid universe only a human creates, reading its sources' canon" }, diff --git a/packages/tasks/tests/requirements.test.mjs b/packages/tasks/tests/requirements.test.mjs index a4fe63bab..fe50e6e59 100644 --- a/packages/tasks/tests/requirements.test.mjs +++ b/packages/tasks/tests/requirements.test.mjs @@ -18,10 +18,10 @@ test("the list holds the PRD's requirement ids, in its order, at its version", ( const ids = [...prd.matchAll(/^- \*\*(REQ-[A-Z]+-[0-9]+)\.\*\*/gm)].map((m) => m[1]); // Any other mention of a bold id would be a bullet this pattern missed. assert.equal(ids.length, [...prd.matchAll(/\*\*REQ-/g)].length); - assert.equal(ids.length, 82); + assert.equal(ids.length, 85); assert.deepEqual(list.requirements.map((r) => r.id), ids); assert.match(prd, new RegExp(`^- Status: approved, version ${list.version.replace('.', '\\.')}\\.`, 'm')); - assert.deepEqual([list.prd, list.version], ['docs/prd/mosaic-stack.md', '1.12']); + assert.deepEqual([list.prd, list.version], ['docs/prd/mosaic-stack.md', '1.13']); }); test('the committed list loads frozen', () => { @@ -74,7 +74,7 @@ test('the schema refuses any other shape', () => { const twice = raw(); twice.requirements.push(structuredClone(twice.requirements[0])); writeFileSync(join(dir, 'twice.json'), JSON.stringify(twice)); - assert.throws(() => loadRequirements(join(dir, 'twice.json')), listError('requirements[82].id: REQ-ROLE-1 twice')); + assert.throws(() => loadRequirements(join(dir, 'twice.json')), listError('requirements[85].id: REQ-ROLE-1 twice')); } finally { rmSync(dir, { recursive: true, force: true }); }