PRD 1.11: v1 is the 10-27 goal (Q211, mos-1227, Vikunja 459)

Follows Jason's Q211 ruling and Mos's reading. Adds to v1 and removes nothing.
- Status and a Version 1.11 source bullet quoting Q211.
- Users: two v1 users on their own instances (Q176). Where it runs: two PVE VMs, dragon-lin dev only (Q180, Q181).
- Out of scope: minting credentials (Q188, Q190), SSO (Q175) and access beyond localhost (Q176, Q179, Q180) move into v1. The bundled Pocket ID stays out (REQ-INST-2, Q203). Codex and SetSpark stay out. Case 2's own organization is v1.
- REQ-CRED-1: Gitea per seat through a grant (Q190).
- REQ-FED-1: person-to-person v1, agent-to-agent post-v1 (Q179, Q180).
- New REQ-INST-3: one-command install and upgrade, Console unit enabled in both v1 profiles (Q187, Q189).
- Acceptance: the 10-27 goal (Q176). The 1.10 note at :1217 now points to Q211. A Version 1.11 technical note keeps the old list text.
requirements.json goes to 1.11 with 71 ids. Both requirements tests are updated to match.
This commit is contained in:
jason.woltje committed 2026-10-21 17:01:08 -05:00
1 parent cca206cdce
commit ea0b2a16d6
4 files changed
+100 -19

No files matched your search

+92 -12
View File
@@ -1,6 +1,6 @@
# PRD: Mosaic Stack # PRD: Mosaic Stack
- Status: approved, version 1.10. Jason approved draft 0.4 unchanged as - Status: approved, version 1.11. Jason approved draft 0.4 unchanged as
1.0 on 2026-10-05 (lead decision 61). They approved version 1.1, the 1.0 on 2026-10-05 (lead decision 61). They approved version 1.1, the
Organization requirements, in decision 03130d08-ddce-480d-a601-e59f64744fc3 Organization requirements, in decision 03130d08-ddce-480d-a601-e59f64744fc3
on 2026-10-14. They approved version 1.2, the chief of staff, the on 2026-10-14. They approved version 1.2, the chief of staff, the
@@ -29,6 +29,24 @@
chose in Q203, relayed by Mos on 2026-10-21 (mos-1202). Version 1.10 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, 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). with Mos's reading, relayed by Mos on 2026-10-21 (mos-1202, mos-1204).
Version 1.11 records Jason's ruling Q211 of 2026-10-21: v1 is the 10-27
goal. It updates the lines that the 10-27 rulings settle and adds
REQ-INST-3. Q211 sets the path: one seat drafts, a second seat reviews,
and Sage lands it. So there is no in-Stack prd.approve for it.
- Version 1.11 source, Jason's answer to Q23 in T3, relayed by Mos on
2026-10-21 (DECISIONS Q211, mos-1227): "It appears that I have expanded
the requirements for v1. My recent goal for the 10-27 release is the
goal. The PRD may need to be updated to reflect this change. The PRD can
be considered partly stale." Mos's reading, which this version follows:
- The Console's own OIDC sign-in (REQ-WEB-2) is v1.
- v1 is the 10-27 goal: Q176 (the Given list and Cases 1 and 2) plus the
later rulings that put items on the 10-27 path, such as Q179, Q184,
Q187, Q188, Q189 and Q190. Where the PRD and those rulings differ, the
rulings win.
- The answer adds to v1 and removes nothing. Cutting a v1 item goes to
Jason.
- Rulings that keep an item off the 10-27 path still hold: Q186, Q198's
timing, Q203's dragon-lin line and Q208. REQ-INST-2 stays post-v1.
- Version 1.10 source, the second sentence of Jason's Q203 answer quoted - 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 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 mos-1204 on 2026-10-21 to add it as drafted and mark it post-v1 the
@@ -190,13 +208,16 @@ and outstanding decisions don't collect anywhere.
## Users ## Users
For now the only user is Jason. The stack must still be installable by v1 has two users, each on their own instance: Jason on Case 1, the
outside users later, which means: software factory, and Melanie on Case 2, the creative writing hub (Q176,
Q211). The stack must still be installable by outside users later, which
means:
- an installer; - an installer;
- no paths or names specific to Jason; - no paths or names specific to Jason;
- configuration through the variable layers. - configuration through the variable layers.
(Round 1, answer D.) (Round 1, answer D. The 1.10 text began "For now the only user is
Jason.")
## Problem statement ## Problem statement
@@ -228,14 +249,28 @@ decides it later.
Self-hosted, on Jason's machines and homelab (round 1, "A first"). Cloud Self-hosted, on Jason's machines and homelab (round 1, "A first"). Cloud
workers aren't ruled out later. workers aren't ruled out later.
v1 runs as two instances on VMs on the PVE node: Case 1 on VM 1008 and
Case 2 on VM 1012. Each instance runs under its own OS user without sudo.
Jason's workstation, dragon-lin, is for dev work only. (Q180, Q181, Q211.)
### Out of scope for v1 ### Out of scope for v1
Round 2, "all": Round 2, "all", as version 1.11 updates it (Q211):
- businesses other than Mosaic Stack (SetSpark joins after v1); - businesses other than Mosaic Stack on Case 1 (SetSpark joins after
- minting credentials (v1 hands out tokens Jason creates); v1). Case 2's hub runs its own organization on its own instance, and
that is v1 (Q176);
- Codex (v1 covers Pi and Claude Code, and Codex comes next); - Codex (v1 covers Pi and Claude Code, and Codex comes next);
- Pocket ID and SSO; - the bundled Pocket ID (REQ-INST-2, Q203).
- access from beyond localhost.
Version 1.11 moves three round 2 items into v1:
- Minting credentials. On the user's Approve, protected stack code makes
the key, the token or a seat's own Gitea user (REQ-GRANT-2; Q188, Q190).
- SSO. The Console runs its own OIDC sign-in (REQ-WEB-2; Q175, Q211).
- Access from beyond localhost. Each case runs on its own VM, and people
get access across the two instances (Q176, Q179, Q180). The Console's
own sign-in (Q175) comes first for any instance that a second user or
another instance can reach (Q176). A new listener reachable from outside
the host is still a `network.expose` hard wait (REQ-WAIT-1).
## Requirements ## Requirements
@@ -401,6 +436,10 @@ Added in version 1.7:
Since version 1.8, a token or key for a grant is made by the grant code Since version 1.8, a token or key for a grant is made by the grant code
on the user's Approve (REQ-GRANT-2), not by hand. Tokens that exist stay on the user's Approve (REQ-GRANT-2), not by hand. Tokens that exist stay
as they are. as they are.
Since version 1.11, Gitea follows Q190: each agent seat gets its own
Gitea user through a grant (REQ-GRANT-1, REQ-GRANT-2), not a bot user
per role made by hand. Vikunja is unchanged.
- **REQ-CRED-2.** A session that finds only founder credentials stops. - **REQ-CRED-2.** A session that finds only founder credentials stops.
Added in version 1.7: Added in version 1.7:
@@ -965,7 +1004,12 @@ Added in version 1.1 (decision 03130d08).
in another's. Added in version 1.2. in another's. Added in version 1.2.
- **REQ-TPL-1.** Templates exist for agent roles and for organization - **REQ-TPL-1.** Templates exist for agent roles and for organization
types. types.
- **REQ-FED-1.** Installs can federate with each other. This is post-v1. - **REQ-FED-1.** Installs can federate with each other. Person-to-person
federation is v1: a person gets access to another instance by signing in
to its Console (REQ-WEB-2), the broker enforces the grants that give
that access, and the access can be revoked. Agent-to-agent federation is
post-v1. (Version 1.11; Q179, Q180, Q211. The 1.10 text ended "This is
post-v1.")
### Installer ### Installer
@@ -988,6 +1032,20 @@ Added in version 1.4 (decision 1ea053cc).
- Pocket ID is passkey-only for people. Agents keep per-role service - Pocket ID is passkey-only for people. Agents keep per-role service
tokens. tokens.
Added in version 1.11 (Q187, Q189, Q211):
- **REQ-INST-3.** Install and upgrade are one command each.
- Install sets up a fresh host as a named instance with its config. The
per-case setup is installer config, a profile per case, not one-off
scripts.
- Upgrade backs up, moves to the new release, and rolls back if its
checks fail.
- Both are proven on a throwaway VM on the PVE node, install, upgrade to
the next release, then rollback, before either v1 VM gets the stack.
- Both v1 profiles enable the Console unit, so the Console starts at
install and at every boot (Q189). dragon-lin is unchanged.
- The container-based install comes after v1.
## Acceptance criteria and success measures ## Acceptance criteria and success measures
v1 is done when Jason gives one-sentence requests to the assistant, in a v1 is done when Jason gives one-sentence requests to the assistant, in a
@@ -1002,6 +1060,11 @@ Version 1.5 adds Jason's Q167 acceptance run, `docs/acceptance/314.md`.
It passes when one need goes from Jason's spec to a landed commit with It passes when one need goes from Jason's spec to a landed commit with
only hard waits in his inbox. only hard waits in his inbox.
Version 1.11 adds the 10-27 goal (Q176, Q211). v1 is also done only when
both instances are installed and handle their cases: Case 1, the
software factory, and Case 2, the creative writing hub. 2026-10-27 is a
latest-by date and a hard target (Q176).
## Technical considerations ## Technical considerations
- Vikunja v2.7.0 is AGPL-3.0. Its API tokens can't create users or mint - Vikunja v2.7.0 is AGPL-3.0. Its API tokens can't create users or mint
@@ -1214,10 +1277,27 @@ only hard waits in his inbox.
re-pin carries row 252. This amendment adds the requirement only. re-pin carries row 252. This amendment adds the requirement only.
- Version 1.10: - Version 1.10:
- REQ-WEB-2 (1.9) uses the Console's own OIDC sign-in from Q175. The - 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 out-of-scope list above then still named 'Pocket ID and SSO'. Jason
ruled on that line (vikunja:19/459). ruled on that line in Q211, and version 1.11 updates it
(vikunja:19/459).
- The design row for REQ-INST-2 is row 255 (#1760). This amendment adds - The design row for REQ-INST-2 is row 255 (#1760). This amendment adds
the requirement only. the requirement only.
- Version 1.11:
- The 1.10 out-of-scope list, under Round 2, "all", was: 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.
- Q211 names Q176's Given list and Cases 1 and 2 as v1. They are
tracked on Vikunja #414, #415 and #416. This version doesn't add
requirement ids for the items in them that the PRD lacks.
- The Case 2 MVP for 10-27 is the Markdown library (#416). The WYSIWYG
editor (Q195, Q196) is an add-on that doesn't gate it.
- Today the stack has an upgrade tool with activate, rollback and a
practice run, and nothing that sets up a fresh host (Q187). The
installer row is Vikunja #432, and the throwaway VM proof is #433.
- REQ-INST-3 replaces the hand start of the Console for the two v1 VMs
only (Q189, mos-1097 item 1).
## Risks and open questions ## Risks and open questions
+1 -1
View File
@@ -45,7 +45,7 @@ test('requirements returns the committed list to a holding role session, and no
const pm = agent('pm'); const pm = agent('pm');
const list = call(b, pm, 'requirements'); const list = call(b, pm, 'requirements');
assert.deepEqual(list, loadRequirements()); assert.deepEqual(list, loadRequirements());
assert.equal(list.requirements.length, 70); assert.equal(list.requirements.length, 71);
assert.deepEqual(call(b, agent('coder'), 'requirements'), list); assert.deepEqual(call(b, agent('coder'), 'requirements'), list);
assert.throws(() => call(b, human, 'requirements'), /agent-required/); assert.throws(() => call(b, human, 'requirements'), /agent-required/);
assert.throws(() => call(b, reader, 'requirements'), /read-only/); assert.throws(() => call(b, reader, 'requirements'), /read-only/);
+4 -3
View File
@@ -1,6 +1,6 @@
{ {
"prd": "docs/prd/mosaic-stack.md", "prd": "docs/prd/mosaic-stack.md",
"version": "1.10", "version": "1.11",
"requirements": [ "requirements": [
{ "id": "REQ-ROLE-1", "title": "Each role is a reviewed roles/<name>.json: contract, authority map, credentials" }, { "id": "REQ-ROLE-1", "title": "Each role is a reviewed roles/<name>.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 thread for the assistant; the chief of staff may end a run, others need a decision" },
@@ -69,8 +69,9 @@
{ "id": "REQ-THREAD-1", "title": "Threads live under projects" }, { "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; 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-TPL-1", "title": "Templates for agent roles and for organization types" },
{ "id": "REQ-FED-1", "title": "Federation between installs (post-v1)" }, { "id": "REQ-FED-1", "title": "Federation between installs: person-to-person in v1, agent-to-agent 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)" } { "id": "REQ-INST-2", "title": "With no OIDC provider to configure, the installer deploys a bundled Pocket ID, Q203 (post-v1)" },
{ "id": "REQ-INST-3", "title": "One-command install of a named instance from a profile, and one-command upgrade with backup and rollback" }
] ]
} }
+3 -3
View File
@@ -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]); 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. // 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, [...prd.matchAll(/\*\*REQ-/g)].length);
assert.equal(ids.length, 70); assert.equal(ids.length, 71);
assert.deepEqual(list.requirements.map((r) => r.id), ids); assert.deepEqual(list.requirements.map((r) => r.id), ids);
assert.match(prd, new RegExp(`^- Status: approved, version ${list.version.replace('.', '\\.')}\\.`, 'm')); assert.match(prd, new RegExp(`^- Status: approved, version ${list.version.replace('.', '\\.')}\\.`, 'm'));
assert.deepEqual([list.prd, list.version], ['docs/prd/mosaic-stack.md', '1.10']); assert.deepEqual([list.prd, list.version], ['docs/prd/mosaic-stack.md', '1.11']);
}); });
test('the committed list loads frozen', () => { test('the committed list loads frozen', () => {
@@ -74,7 +74,7 @@ test('the schema refuses any other shape', () => {
const twice = raw(); const twice = raw();
twice.requirements.push(structuredClone(twice.requirements[0])); twice.requirements.push(structuredClone(twice.requirements[0]));
writeFileSync(join(dir, 'twice.json'), JSON.stringify(twice)); writeFileSync(join(dir, 'twice.json'), JSON.stringify(twice));
assert.throws(() => loadRequirements(join(dir, 'twice.json')), listError('requirements[70].id: REQ-ROLE-1 twice')); assert.throws(() => loadRequirements(join(dir, 'twice.json')), listError('requirements[71].id: REQ-ROLE-1 twice'));
} finally { } finally {
rmSync(dir, { recursive: true, force: true }); rmSync(dir, { recursive: true, force: true });
} }