diff --git a/docs/prd/mosaic-stack.md b/docs/prd/mosaic-stack.md index da1a14775..5f5247dfd 100644 --- a/docs/prd/mosaic-stack.md +++ b/docs/prd/mosaic-stack.md @@ -1,6 +1,6 @@ # PRD: Mosaic Stack -- Status: approved, version 1.9. Jason approved draft 0.4 unchanged as +- Status: approved, version 1.10. 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 @@ -26,7 +26,13 @@ prd.approve for it. Version 1.9 adds REQ-WEB-2 and amends REQ-DEC-3: the Console decide buttons Jason approved in decision 00ed58e2 (vikunja:19/266), signed in as his Q175 ruling set, and OIDC-only as he - chose in Q203, relayed by Mos on 2026-10-21 (mos-1202). + chose in Q203, relayed by Mos on 2026-10-21 (mos-1202). Version 1.10 + adds REQ-INST-2, post-v1: the second sentence of Jason's Q203 answer, + with Mos's reading, relayed by Mos on 2026-10-21 (mos-1202, mos-1204). +- Version 1.10 source, the second sentence of Jason's Q203 answer quoted + under 1.9 below, Mos's reading of it in mos-1202, and his ruling in + mos-1204 on 2026-10-21 to add it as drafted and mark it post-v1 the + way REQ-FED-1 is (vikunja:19/458). - Version 1.9 source, Jason's answer to Q203 in T3, relayed by Mos on 2026-10-21 (DECISIONS Q203, mos-1202): "Let's use O6. If a user does not have an OIDC provider to configure, we will need to use a bundled @@ -969,6 +975,18 @@ Added in version 1.4 (decision 1ea053cc). pinned, health-checked and removable, and is switched on in the organization file or system config. The first is SearXNG: JSON output enabled, limiter off, bound to loopback only. +- **REQ-INST-2.** Jason, Q203: "If a user does not have an OIDC provider + to configure, we will need to use a bundled Pocket ID instances to + handle this traffic." This is post-v1. (Version 1.10, Q203.) Mos's + reading (mos-1202), following lead decision 44 items 5 and 6: + - The installer offers two choices: point the Console at one or more + existing OIDC providers, or deploy a bundled Pocket ID. + - The stack installs that Pocket ID as an install component under + REQ-INST-1 and writes it as the Console's one providers entry. No + Pocket ID source goes in the repository, and the Console gets no + sign-in path apart from its own OIDC sign-in (REQ-WEB-2). + - Pocket ID is passkey-only for people. Agents keep per-role service + tokens. ## Acceptance criteria and success measures @@ -1194,6 +1212,12 @@ only hard waits in his inbox. - The live Console runs without `--sso` and without `--send`, so it resolves nothing until the Q175 phase 2 window turns both on and a re-pin carries row 252. This amendment adds the requirement only. +- Version 1.10: + - REQ-WEB-2 (1.9) uses the Console's own OIDC sign-in from Q175. The + out-of-scope list above still names 'Pocket ID and SSO'. Jason hasn't + ruled on that line (vikunja:19/459). + - The design row for REQ-INST-2 is row 255 (#1760). This amendment adds + the requirement only. ## Risks and open questions diff --git a/packages/bus/tests/requirements.test.mjs b/packages/bus/tests/requirements.test.mjs index a5de92f40..8e093e7aa 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, 69); + assert.equal(list.requirements.length, 70); 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 234ee4cde..335ee6439 100644 --- a/packages/tasks/requirements.json +++ b/packages/tasks/requirements.json @@ -1,6 +1,6 @@ { "prd": "docs/prd/mosaic-stack.md", - "version": "1.9", + "version": "1.10", "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" }, @@ -70,6 +70,7 @@ { "id": "REQ-THREAD-2", "title": "The user creates projects and named threads; each thread is its own assistant session with kept history" }, { "id": "REQ-TPL-1", "title": "Templates for agent roles and for organization types" }, { "id": "REQ-FED-1", "title": "Federation between installs (post-v1)" }, - { "id": "REQ-INST-1", "title": "The installer offers optional components: pinned, health-checked, removable, switched on in config; first is SearXNG" } + { "id": "REQ-INST-1", "title": "The installer offers optional components: pinned, health-checked, removable, switched on in config; first is SearXNG" }, + { "id": "REQ-INST-2", "title": "With no OIDC provider to configure, the installer deploys a bundled Pocket ID, Q203 (post-v1)" } ] } diff --git a/packages/tasks/tests/requirements.test.mjs b/packages/tasks/tests/requirements.test.mjs index f7175d528..8d57802ac 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, 69); + assert.equal(ids.length, 70); 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.9']); + assert.deepEqual([list.prd, list.version], ['docs/prd/mosaic-stack.md', '1.10']); }); 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[69].id: REQ-ROLE-1 twice')); + assert.throws(() => loadRequirements(join(dir, 'twice.json')), listError('requirements[70].id: REQ-ROLE-1 twice')); } finally { rmSync(dir, { recursive: true, force: true }); }