docs: PRD version 1.5 under Q167 and the task 314 acceptance run (row 135, #1629)
Version 1.5 records Jason's Q167 ruling and added text as its source block. It replaces REQ-DEC-2 routing: a gated decision citing the project's current spec commit goes to the domain arbiter, and only an uncited decision or a hard wait goes to the human. It adds the hard waits (REQ-WAIT-1 to 3, with data.delete, network.expose and account.create), projects and the spec file (REQ-PROJ-1 to 4), agent landing (REQ-LAND-1), approvals that outlive the run (REQ-DEC-5), settled decisions under a new spec (REQ-DEC-6), the four workflow roles and proactive contracts (REQ-ROLE-5, 6), staff requests (REQ-COS-2), assistant summaries (REQ-ASSIST-3) and name addressing (REQ-MSG-2). docs/acceptance/314.md is the run Jason does in the Stack once the rows are live. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
7a2987cf51
commit
92c10d56eb
5 files changed
+412
-9
No files matched your search
@@ -0,0 +1,152 @@
|
|||||||
|
# Acceptance run for task 314 (Q167)
|
||||||
|
|
||||||
|
Jason runs this inside the Stack once rows 135 to 143 and 147 to 149 are
|
||||||
|
live. It checks PRD version 1.5: one need goes from his spec to a landed
|
||||||
|
commit, and only hard waits reach his inbox.
|
||||||
|
|
||||||
|
`<business>` is the business the bus host runs. `<project>` is the
|
||||||
|
project the need belongs to, `stack` for this run. Every command runs on
|
||||||
|
the host, as Jason.
|
||||||
|
|
||||||
|
The need is a `mosaic version` command that prints the release and the
|
||||||
|
bus schema.
|
||||||
|
|
||||||
|
## Before the run
|
||||||
|
|
||||||
|
- `mosaic bus status` says the host is live.
|
||||||
|
- `mosaic inbox` shows no open decision. If one is open, resolve or
|
||||||
|
supersede it first, so step 6 starts from an empty inbox.
|
||||||
|
- The organization has security-reviewer and merge-gate instances, added
|
||||||
|
through the chief of staff's organization change with Mos's go, and
|
||||||
|
both are in `launch.instances`.
|
||||||
|
- `arbiters.technical` is `cto`, which step 5 assumes.
|
||||||
|
|
||||||
|
## Steps
|
||||||
|
|
||||||
|
1. Create the project repository, if it doesn't exist yet:
|
||||||
|
|
||||||
|
```
|
||||||
|
mosaic project init <business> <project>
|
||||||
|
mosaic project check <business> <project>
|
||||||
|
```
|
||||||
|
|
||||||
|
Pass: `check` reports the layout valid. The first commit's author is
|
||||||
|
Jason. (REQ-PROJ-1)
|
||||||
|
|
||||||
|
2. Open the spec in the Console editor and write it:
|
||||||
|
|
||||||
|
> North star: anyone can tell what is running in one command.
|
||||||
|
> Need: a `mosaic version` command that prints the release and the bus
|
||||||
|
> schema.
|
||||||
|
|
||||||
|
Before saving, open the same file in a second Console tab. Save in the
|
||||||
|
first tab. Pass: the save makes one local commit with Jason as author,
|
||||||
|
and the editor shows the new commit. Then edit and save in the second
|
||||||
|
tab, which still holds the old commit: the save is refused and writes
|
||||||
|
nothing. (REQ-PROJ-2, REQ-PROJ-3)
|
||||||
|
|
||||||
|
3. Tell the assistant about the need:
|
||||||
|
|
||||||
|
```
|
||||||
|
mosaic talk assistant "New need: the mosaic version command in spec/north-star.md. Start it."
|
||||||
|
```
|
||||||
|
|
||||||
|
Pass: `talk` prints only the assistant's reply to this message.
|
||||||
|
|
||||||
|
4. Watch the work start:
|
||||||
|
|
||||||
|
```
|
||||||
|
mosaic tasks
|
||||||
|
mosaic agents
|
||||||
|
mosaic trail <task>
|
||||||
|
```
|
||||||
|
|
||||||
|
Pass, all on the trail:
|
||||||
|
- pm makes the tasks and sends the chief of staff a staff request
|
||||||
|
naming the roles and counts (REQ-COS-2);
|
||||||
|
- the chief of staff launches a coder, a reviewer and a
|
||||||
|
security-reviewer through role.launch (REQ-ROLE-5);
|
||||||
|
- the coder pushes a work ref, and the reviewer and the
|
||||||
|
security-reviewer each approve it.
|
||||||
|
|
||||||
|
5. Watch it land:
|
||||||
|
|
||||||
|
```
|
||||||
|
mosaic trail <decision>
|
||||||
|
git -C <working repository> log --oneline -3 main
|
||||||
|
```
|
||||||
|
|
||||||
|
Pass:
|
||||||
|
- pm hands the approved candidate to merge-gate, and merge-gate
|
||||||
|
raises the `git.merge.protected` decision citing
|
||||||
|
`spec/north-star.md` at the commit from step 2;
|
||||||
|
- the CTO resolves it, not Jason (REQ-DEC-2);
|
||||||
|
- merge-gate lands it, the trail records the allowed action with the
|
||||||
|
landed commit, and the commit is on the working repository's `main`
|
||||||
|
(REQ-LAND-1).
|
||||||
|
|
||||||
|
6. Check the inbox, then ask for one hard wait on purpose:
|
||||||
|
|
||||||
|
```
|
||||||
|
mosaic inbox
|
||||||
|
mosaic talk assistant "Open a new account for the project on an outside service."
|
||||||
|
mosaic inbox
|
||||||
|
```
|
||||||
|
|
||||||
|
Pass: the first `inbox` is empty, so nothing from steps 3 to 5 reached
|
||||||
|
Jason. After the request, the inbox holds one decision for
|
||||||
|
`account.create`. Resolve it with the reject option. (REQ-WAIT-1)
|
||||||
|
|
||||||
|
7. Restart the host halfway through the next merge. Use a second small
|
||||||
|
change, for example "print the help when an option is unknown", and
|
||||||
|
restart once the CTO has resolved its merge decision and before
|
||||||
|
merge-gate lands it. Mos or Jason runs the restart:
|
||||||
|
|
||||||
|
```
|
||||||
|
mosaic bus stop
|
||||||
|
mosaic bus start <business> --pm
|
||||||
|
```
|
||||||
|
|
||||||
|
Pass: the merge lands from a new run of the same merge-gate instance
|
||||||
|
with the approval from before the restart, and no new decision is
|
||||||
|
raised.
|
||||||
|
(REQ-DEC-5)
|
||||||
|
|
||||||
|
8. Change the spec in the Console editor: also print the commit. Save.
|
||||||
|
|
||||||
|
Pass:
|
||||||
|
- the save makes a new commit by Jason;
|
||||||
|
- `mosaic trail` shows no decision settled under the step 2 commit
|
||||||
|
reopened (REQ-DEC-6);
|
||||||
|
- `mosaic trail` shows new raises citing the new commit, and no
|
||||||
|
raise citing the step 2 commit accepted after the save;
|
||||||
|
- pm and the CTO each answer the spec commit with an accept
|
||||||
|
(REQ-PROJ-4).
|
||||||
|
|
||||||
|
9. Change the spec in a way that breaks a settled decision, for example:
|
||||||
|
"the version command prints nothing and exits." Save.
|
||||||
|
|
||||||
|
Pass: pm or the CTO answers with a BLOCK naming the conflict, the
|
||||||
|
commit stays, and the assistant tells Jason about the block in its own
|
||||||
|
words. The assistant's message doesn't quote the chief of staff's text.
|
||||||
|
(REQ-PROJ-4, REQ-ASSIST-3) Then revert the spec in the editor with a
|
||||||
|
new commit.
|
||||||
|
|
||||||
|
10. Give the coder instance a name with the CLI command row 141 adds
|
||||||
|
(`docs/TOOLS.md` names it once that row lands), then send it a
|
||||||
|
message by that name:
|
||||||
|
|
||||||
|
```
|
||||||
|
mosaic agents
|
||||||
|
mosaic talk <name from the agents list> "Reply with your role name."
|
||||||
|
```
|
||||||
|
|
||||||
|
Pass: the reply comes from the instance with that name, and the trail
|
||||||
|
records both the name and the instance. (REQ-MSG-2)
|
||||||
|
|
||||||
|
## Result
|
||||||
|
|
||||||
|
The run passes when every step passes. Record the run with the date, the
|
||||||
|
release, the bus schema and the result of each step in Vikunja task 314.
|
||||||
|
A failed step names the requirement it checks, so the fix goes back to
|
||||||
|
that row.
|
||||||
+240
-4
@@ -1,6 +1,6 @@
|
|||||||
# PRD: Mosaic Stack
|
# PRD: Mosaic Stack
|
||||||
|
|
||||||
- Status: approved, version 1.4. Jason approved draft 0.4 unchanged as
|
- Status: approved, version 1.5. 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
|
||||||
@@ -10,8 +10,45 @@
|
|||||||
decision 143). Mos reviewed the 1.3 text before it landed. Version 1.4
|
decision 143). Mos reviewed the 1.3 text before it landed. Version 1.4
|
||||||
adds REQ-WEB-3 and REQ-INST-1, approved by Jason in decisions
|
adds REQ-WEB-3 and REQ-INST-1, approved by Jason in decisions
|
||||||
021af0c8 (prd.approve, yes; request dfebf8ce) and 1ea053cc (prd.approve,
|
021af0c8 (prd.approve, yes; request dfebf8ce) and 1ea053cc (prd.approve,
|
||||||
yes; request b28979c9). It is never edited in place. Changes are a new
|
yes; request b28979c9). Version 1.5 records Jason's Q167 ruling and
|
||||||
|
his added Q167 text, relayed by Mos on 2026-10-15. Under Q167 Jason's
|
||||||
|
confirmation of the text is the approval, so there is no in-Stack
|
||||||
|
prd.approve for it. It is never edited in place. Changes are a new
|
||||||
version.
|
version.
|
||||||
|
- Version 1.5 source, Jason's words relayed by Mos on 2026-10-15:
|
||||||
|
- Q167: "Me having to approve every decision within the Stack is not
|
||||||
|
working for me. I gave a directive, I want it to be done." and "Use
|
||||||
|
the T3 seats to make these updates and changes, so I can have a
|
||||||
|
functional system."
|
||||||
|
- Q167, added text: "All north star specs should be visible and
|
||||||
|
modifiable by the user within each given Project. I need to be able
|
||||||
|
to read and make the changes ad-hoc without an agent when desired.
|
||||||
|
This will allow review and revise to occur outside the chat. The
|
||||||
|
agent will be able to see the changes of course. Changes made should
|
||||||
|
use git (at least locally) to maintain the change history.
|
||||||
|
Organization of data in a Project is critical. We need a data
|
||||||
|
structure with json files and templates for everything. Markdown
|
||||||
|
files with a WYSIWYG editor for the north star/spec files.
|
||||||
|
Documentation within the project. Consistent storage of docs, reqs,
|
||||||
|
decisions, etc. should be kept in the project for reference as
|
||||||
|
needed. We will need to have these agents for a coding workflow: -
|
||||||
|
coder - code review - security review - merge gate. The appropriate
|
||||||
|
agent profile will be assigned to the agents. The roles and tools
|
||||||
|
allow agentic control of the needed workflow steps. Autonomy within
|
||||||
|
the rails is encouraged. Agents are proactive. The rest of your
|
||||||
|
reading is accurate."
|
||||||
|
- On the revised row list (task 314, comment 225): "Your decisions look
|
||||||
|
good. Proceed."
|
||||||
|
- Jason's waits list, as Mos gave it: spending money, deleting data,
|
||||||
|
anything irreversible, new internet exposure, new external accounts,
|
||||||
|
messages to people outside the fleet, and force-push or history
|
||||||
|
rewrite. Mos's reading, Q5: "Q167 covers shrinking GATED_ONLY and the
|
||||||
|
role grants through rows 135 and 139." Sage's plan for the rows adds
|
||||||
|
that any change to the list itself, and any grant of a listed action
|
||||||
|
to a role, also goes to Jason.
|
||||||
|
- Mos's reading, Q4: agents land into the business working
|
||||||
|
repository's protected branch through the host. This repository's
|
||||||
|
canonical `refactor` branch stays the lead's landing.
|
||||||
- Version 1.3 source, Jason's words relayed by Mos:
|
- Version 1.3 source, Jason's words relayed by Mos:
|
||||||
- Q153: "2. agreed. Make it modular though. We should be able to
|
- Q153: "2. agreed. Make it modular though. We should be able to
|
||||||
extend without code change on the core app." Mos's reading, which
|
extend without code change on the core app." Mos's reading, which
|
||||||
@@ -158,6 +195,30 @@ parent requirement is refused.
|
|||||||
assistant's name is set in the organization file. Jason's may be named
|
assistant's name is set in the organization file. Jason's may be named
|
||||||
Mos.
|
Mos.
|
||||||
|
|
||||||
|
Added in version 1.5 (Q167):
|
||||||
|
|
||||||
|
- **REQ-ROLE-5.** The coding workflow has four roles, each with its own
|
||||||
|
role file:
|
||||||
|
- coder, which writes the change;
|
||||||
|
- reviewer, which gives the code review verdict;
|
||||||
|
- security-reviewer, which reviews a candidate for security only and
|
||||||
|
approves or blocks with the reason. It has read-only tools and the
|
||||||
|
verdict, and no shell, no write tools and no push;
|
||||||
|
- merge-gate, which takes a candidate the reviewers approved, raises
|
||||||
|
the merge decision for it in the technical domain, so the CTO
|
||||||
|
decides, and lands it once the decision is resolved (REQ-LAND-1).
|
||||||
|
Otherwise it reports the refusal. Its one grant is
|
||||||
|
`git.merge.protected` as a cross-role action, which is what landing
|
||||||
|
checks. Raising a decision needs no grant. It has no write tools.
|
||||||
|
Because only the instance that raised an approval can consume it
|
||||||
|
(REQ-DEC-5), the role that lands is the role that raises.
|
||||||
|
|
||||||
|
Instances of these roles are added through the chief of staff's
|
||||||
|
organization change verb (REQ-ROLE-3).
|
||||||
|
- **REQ-ROLE-6.** Agents are proactive. Every role contract says: within
|
||||||
|
your grants, act on what you see without waiting to be asked, and
|
||||||
|
raise a decision or send a message for anything outside them.
|
||||||
|
|
||||||
Added in version 1.2:
|
Added in version 1.2:
|
||||||
|
|
||||||
- **REQ-COS-1.** The chief of staff runs the organization. It hires and
|
- **REQ-COS-1.** The chief of staff runs the organization. It hires and
|
||||||
@@ -166,6 +227,10 @@ Added in version 1.2:
|
|||||||
from the assistant and assigns it through the roles that do it. It
|
from the assistant and assigns it through the roles that do it. It
|
||||||
never talks to the user. Gated decisions still reach the human
|
never talks to the user. Gated decisions still reach the human
|
||||||
through the inbox (REQ-DEC-2, REQ-DEC-4).
|
through the inbox (REQ-DEC-2, REQ-DEC-4).
|
||||||
|
- **REQ-COS-2.** The PM asks the chief of staff for staff. A staff request
|
||||||
|
names the roles and how many of each. The chief of staff launches them
|
||||||
|
through role.launch, within the agent limits (REQ-LIMIT-1). (Version
|
||||||
|
1.5.)
|
||||||
- **REQ-ASSIST-1.** The assistant is the user's main agent. Each user has
|
- **REQ-ASSIST-1.** The assistant is the user's main agent. Each user has
|
||||||
one assistant role per organization. It talks with the user, and it
|
one assistant role per organization. It talks with the user, and it
|
||||||
passes work for the organization to the chief of staff. It doesn't
|
passes work for the organization to the chief of staff. It doesn't
|
||||||
@@ -176,6 +241,9 @@ Added in version 1.2:
|
|||||||
chief of staff;
|
chief of staff;
|
||||||
- only the user and the chief of staff may send to the assistant;
|
- only the user and the chief of staff may send to the assistant;
|
||||||
- every refused send is logged with its reason.
|
- every refused send is logged with its reason.
|
||||||
|
- **REQ-ASSIST-3.** The assistant tells the user what other roles sent it
|
||||||
|
in its own words. It summarizes and doesn't relay their text. (Version
|
||||||
|
1.5.)
|
||||||
|
|
||||||
### Variables
|
### Variables
|
||||||
|
|
||||||
@@ -217,10 +285,21 @@ Added in version 1.2:
|
|||||||
- its timestamps.
|
- its timestamps.
|
||||||
|
|
||||||
A correction is a new row.
|
A correction is a new row.
|
||||||
- **REQ-DEC-2.** Routing follows the decision's class:
|
- **REQ-DEC-2.** Routing follows the decision's class and whether it
|
||||||
|
cites the project's spec (REQ-PROJ-2). Version 1.5 replaces the 1.0
|
||||||
|
text, which sent every gated decision to the human:
|
||||||
- routine and within-role decisions are logged;
|
- routine and within-role decisions are logged;
|
||||||
- cross-role decisions go to the business's arbiter for that domain;
|
- cross-role decisions go to the business's arbiter for that domain;
|
||||||
- gated decisions go to the human.
|
- a gated decision whose raise cites the project's current spec commit
|
||||||
|
goes to the business's arbiter for its domain, technical or delivery,
|
||||||
|
and not to the human;
|
||||||
|
- a gated decision with no spec citation goes to the human;
|
||||||
|
- a hard wait (REQ-WAIT-1) goes to the human whether or not it cites
|
||||||
|
the spec.
|
||||||
|
|
||||||
|
A raise that cites a spec commit other than the current one is
|
||||||
|
refused, and so is a raise citing a spec whose last commit isn't the
|
||||||
|
human's.
|
||||||
- **REQ-DEC-3.** A human resolution, by an authorized human, comes only from a `mosaic` CLI session
|
- **REQ-DEC-3.** A human resolution, by an authorized human, comes only from a `mosaic` CLI session
|
||||||
started outside any agent run. Agents reach the decision store only
|
started outside any agent run. Agents reach the decision store only
|
||||||
through a broker. The broker stamps role and run from the launch record.
|
through a broker. The broker stamps role and run from the launch record.
|
||||||
@@ -229,6 +308,49 @@ Added in version 1.2:
|
|||||||
connector (#1509). Everything else goes into one daily digest at 08:00
|
connector (#1509). Everything else goes into one daily digest at 08:00
|
||||||
Central. (Round 2, answers 2A and 3A; round 3, 4A and 5A.)
|
Central. (Round 2, answers 2A and 3A; round 3, 4A and 5A.)
|
||||||
|
|
||||||
|
Added in version 1.5 (Q167):
|
||||||
|
|
||||||
|
- **REQ-DEC-5.** An approval outlives the run that raised it. It binds to
|
||||||
|
the decision and to the role instance and thread that raised it. A
|
||||||
|
later run of the same instance, on the same thread, may consume it
|
||||||
|
once. The first consuming action is
|
||||||
|
recorded, a second consume is refused, and another instance can't
|
||||||
|
consume it. Superseding a decision follows the same rule.
|
||||||
|
- **REQ-DEC-6.** A new spec commit never reopens a settled decision. Each
|
||||||
|
decision records the spec path and commit its raise cited. A decision
|
||||||
|
settled under one commit stays settled when a later commit lands, and
|
||||||
|
new raises cite the new commit.
|
||||||
|
|
||||||
|
### Hard waits
|
||||||
|
|
||||||
|
Added in version 1.5 (Q167).
|
||||||
|
|
||||||
|
- **REQ-WAIT-1.** These are the hard waits. Each always goes to the
|
||||||
|
human, and each maps to actions in the closed vocabulary:
|
||||||
|
|
||||||
|
| Jason's wait | Actions |
|
||||||
|
| --- | --- |
|
||||||
|
| Spending money | `spend` |
|
||||||
|
| Deleting data | `data.delete`, new in 1.5 |
|
||||||
|
| Anything irreversible | `data.delete`, `git.push.protected` and REQ-WAIT-3 |
|
||||||
|
| New internet exposure | `network.expose`, new in 1.5: a listener, port, tunnel, DNS record or proxy route reachable from outside the host. `deploy` stays a hard wait too. |
|
||||||
|
| New external accounts | `account.create`, new in 1.5, and `credential.mint` |
|
||||||
|
| Messages to people outside the fleet | `message.external` |
|
||||||
|
| Force-push or history rewrite | `git.push.protected`, which covers a push to a protected branch and any force push or history rewrite on any branch |
|
||||||
|
|
||||||
|
The always-gated lists in the broker and the business vocabulary hold
|
||||||
|
exactly these actions. `git.merge.protected`, `policy.change`,
|
||||||
|
`prd.approve` and `role.revoke` leave those lists and route by
|
||||||
|
REQ-DEC-2. REQ-ROLE-2 still requires a resolved decision for a revoke.
|
||||||
|
- **REQ-WAIT-2.** No role file may grant a hard-wait action, at any
|
||||||
|
class, and the validator refuses a file that does. A change to the
|
||||||
|
hard-waits list, or a grant of a hard-wait action to a role, is itself
|
||||||
|
a hard wait. The list changes only by a reviewed commit to the
|
||||||
|
vocabulary, under a new PRD version that Jason approves.
|
||||||
|
- **REQ-WAIT-3.** An action added to the vocabulary is a hard wait until a
|
||||||
|
PRD version says it isn't. The reviewed commit that adds it names which
|
||||||
|
it is.
|
||||||
|
|
||||||
### Messages
|
### Messages
|
||||||
|
|
||||||
- **REQ-MSG-1.** Messages are addressed to a role. They are append-only
|
- **REQ-MSG-1.** Messages are addressed to a role. They are append-only
|
||||||
@@ -237,6 +359,13 @@ Added in version 1.2:
|
|||||||
the assistant sends work to the chief of staff, the reply comes back to
|
the assistant sends work to the chief of staff, the reply comes back to
|
||||||
the same thread. T3 and Discord carry messages; the address is still
|
the same thread. T3 and Discord carry messages; the address is still
|
||||||
the role.
|
the role.
|
||||||
|
- **REQ-MSG-2.** Messages can also be addressed by agent name. A role
|
||||||
|
instance may have a name, unique in its business, that equals no
|
||||||
|
instance key and no role name. A send takes a name or a role. A name
|
||||||
|
resolves to its instance, the message records both, and a role still
|
||||||
|
delivers as REQ-MSG-1 says. The human sets names through the CLI. The
|
||||||
|
chief of staff sets them once its role file grants that within-role.
|
||||||
|
(Version 1.5.)
|
||||||
|
|
||||||
### Tasks
|
### Tasks
|
||||||
|
|
||||||
@@ -257,6 +386,70 @@ Added in version 1.2:
|
|||||||
- **REQ-TASK-4.** The queue keeps the agents' lock, review rounds and
|
- **REQ-TASK-4.** The queue keeps the agents' lock, review rounds and
|
||||||
audit log. No field syncs in both directions.
|
audit log. No field syncs in both directions.
|
||||||
|
|
||||||
|
### Projects
|
||||||
|
|
||||||
|
Added in version 1.5 (Q167).
|
||||||
|
|
||||||
|
- **REQ-PROJ-1.** Each project keeps its records in its own local git
|
||||||
|
repository, separate from its code checkout, with no remote. The
|
||||||
|
layout is fixed and versioned: `project.json`, `spec/north-star.md`,
|
||||||
|
`docs/`, `requirements/` and `decisions/` with one JSON file per
|
||||||
|
record, and `templates/` with a template for each kind. Each JSON kind
|
||||||
|
has a schema, and a record that fails it is refused. `mosaic project
|
||||||
|
init` creates the repository with its first commit as the business's
|
||||||
|
human.
|
||||||
|
- **REQ-PROJ-2.** The north-star spec is `spec/north-star.md` in the
|
||||||
|
project repository. Only the human commits to it. Agents read it and
|
||||||
|
never write it. The spec counts only when its last commit's author is
|
||||||
|
the business's human. Agents write docs, requirements and decisions as
|
||||||
|
agent-authored commits, through the layout's validators.
|
||||||
|
- **REQ-PROJ-3.** The Console has an editor for the spec and the
|
||||||
|
Markdown files in `docs/`. The user opens a file at its current commit,
|
||||||
|
edits its Markdown source beside a rendered preview, and saves it as
|
||||||
|
one local commit with the human as author. A save made from a commit
|
||||||
|
older than the file's last commit is refused and writes nothing. The
|
||||||
|
preview runs no script from the file and is built from code in this
|
||||||
|
repository. A WYSIWYG editor may replace the source view only with a
|
||||||
|
library that is vendored and pinned with a permissive license and a
|
||||||
|
recorded hash for each file, loads nothing at runtime, and needs no
|
||||||
|
change to the Console's content security policy. No library passed
|
||||||
|
that for 1.5.
|
||||||
|
- **REQ-PROJ-4.** Agents review the human's spec commits. Each human
|
||||||
|
commit to the spec saved through the Console editor (REQ-PROJ-3) sends
|
||||||
|
one notice to the PM and the CTO. A commit the human makes with git
|
||||||
|
outside the Console sends no notice yet. They check
|
||||||
|
the change against settled decisions and work in flight, and each
|
||||||
|
answers with an accept or a BLOCK that names the conflict. A BLOCK
|
||||||
|
doesn't revert the commit. It goes to the assistant through the chief
|
||||||
|
of staff (REQ-ASSIST-2), and the assistant tells the user (REQ-ASSIST-3).
|
||||||
|
|
||||||
|
### Landing
|
||||||
|
|
||||||
|
Added in version 1.5 (Q167, Mos's reading Q4).
|
||||||
|
|
||||||
|
- **REQ-LAND-1.** Agents land approved merges into the business working
|
||||||
|
repository. The run that holds an approved `git.merge.protected`
|
||||||
|
decision asks the host to land the work ref the decision names into
|
||||||
|
the working repository's `main`. The host runs the landing checks and
|
||||||
|
the project's gate:
|
||||||
|
- the decision is resolved with the approval option;
|
||||||
|
- the ref is at the reviewed commit, and a verdict approves it;
|
||||||
|
- the decision isn't superseded;
|
||||||
|
- the base hasn't moved.
|
||||||
|
|
||||||
|
A refusal is logged on the task's trail with its code, and a project
|
||||||
|
with no gate is refused. A landing is recorded as the allowed action
|
||||||
|
with the landed commit, and it consumes the decision (REQ-DEC-5). This
|
||||||
|
repository's canonical branch is never a target.
|
||||||
|
|
||||||
|
Only a role whose role file grants `git.merge.protected` as a
|
||||||
|
cross-role action can land. A merge decision raised by a role without
|
||||||
|
that grant is gated, and its approval never lands, whether the arbiter
|
||||||
|
or the human gave it. So an arbiter's judgment can't move landing to a
|
||||||
|
role REQ-ROLE-5 didn't give it. The land operation refuses a gated
|
||||||
|
decision; the check ships with whichever of rows 137 and 139 lands
|
||||||
|
second.
|
||||||
|
|
||||||
### Interface
|
### Interface
|
||||||
|
|
||||||
- **REQ-CLI-1.** `mosaic` is the front door. From it Jason can:
|
- **REQ-CLI-1.** `mosaic` is the front door. From it Jason can:
|
||||||
@@ -486,6 +679,10 @@ itself from them, five pieces in a row. For each piece:
|
|||||||
|
|
||||||
(Round 1, answer A; the assistant and threads from version 1.2.)
|
(Round 1, answer A; the assistant and threads from version 1.2.)
|
||||||
|
|
||||||
|
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
|
||||||
|
only hard waits in his inbox.
|
||||||
|
|
||||||
## 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
|
||||||
@@ -623,6 +820,45 @@ itself from them, five pieces in a row. For each piece:
|
|||||||
the requirement only.
|
the requirement only.
|
||||||
- REQ-WEB-2 is held for the Console decide buttons amendment
|
- REQ-WEB-2 is held for the Console decide buttons amendment
|
||||||
(vikunja:19/266), so its id is not reused here.
|
(vikunja:19/266), so its id is not reused here.
|
||||||
|
- Version 1.5:
|
||||||
|
- The 1.0 text of REQ-DEC-2 was: "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." On 2026-10-15 the Stack routed 16
|
||||||
|
decisions, and all 16 went to Jason. 9 of them were `policy.change`
|
||||||
|
or `prd.approve`, which no role file could grant.
|
||||||
|
- Before 1.5 the always-gated lists, `GATED` in
|
||||||
|
`packages/bus/src/broker.mjs` and `GATED_ONLY` in
|
||||||
|
`packages/business/src/vocabulary.mjs`, held nine actions:
|
||||||
|
`credential.mint`, `git.merge.protected`, `git.push.protected`,
|
||||||
|
`deploy`, `spend`, `message.external`, `policy.change`,
|
||||||
|
`prd.approve` and `role.revoke`. Row 139 (#1633) changes them to
|
||||||
|
REQ-WAIT-1's list and adds `data.delete`, `network.expose` and
|
||||||
|
`account.create`.
|
||||||
|
- The arbiters are the organization file's `arbiters.technical` and
|
||||||
|
`arbiters.delivery` (`packages/bus/src/broker.mjs`).
|
||||||
|
- Where each requirement is built, all under Vikunja P19 task 314:
|
||||||
|
REQ-DEC-5 is row 136 (#1630); REQ-PROJ-1 is row 147 (#1641);
|
||||||
|
REQ-PROJ-2 and REQ-DEC-6 are row 138 (#1632); REQ-COS-2 is row 140
|
||||||
|
(#1634). Those are landed. REQ-LAND-1 is row 137 (#1631);
|
||||||
|
REQ-DEC-2 and REQ-WAIT-1 to REQ-WAIT-3 are row 139 (#1633);
|
||||||
|
REQ-MSG-2 is row 141 (#1635); REQ-PROJ-4 and REQ-ASSIST-3 are row 143
|
||||||
|
(#1637); REQ-PROJ-3 is row 148 (#1642); REQ-ROLE-5 and REQ-ROLE-6
|
||||||
|
are row 149. This amendment adds the requirements only.
|
||||||
|
- The spec counts as confirmed when its last commit's author name is
|
||||||
|
the business's `human` (`packages/project/src/read.mjs`). The broker
|
||||||
|
refuses a raise that cites another commit with `stale-spec`, and an
|
||||||
|
unconfirmed spec with `spec-unconfirmed`. Until `mosaic project
|
||||||
|
init` runs for a project, spec reads and record writes for it are
|
||||||
|
refused.
|
||||||
|
- `git.push.protected` covering force pushes on any branch is new in
|
||||||
|
1.5. Today the host refuses a non-fast-forward push to the working
|
||||||
|
repository (`packages/seat/src/working.mjs`), so no role can force
|
||||||
|
push. REQ-WAIT-1 makes any future path to one a hard wait.
|
||||||
|
- REQ-PROJ-4 covers Console saves only (row 143). Noticing spec commits
|
||||||
|
made with git outside the Console is a later row.
|
||||||
|
- REQ-LAND-1's refusal of a gated merge decision is pinned in rows 137
|
||||||
|
and 139. Row 139 carries it, or row 137 if 139 lands first.
|
||||||
|
|
||||||
## Risks and open questions
|
## Risks and open questions
|
||||||
|
|
||||||
|
|||||||
@@ -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, 43);
|
assert.equal(list.requirements.length, 58);
|
||||||
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/);
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
{
|
{
|
||||||
"prd": "docs/prd/mosaic-stack.md",
|
"prd": "docs/prd/mosaic-stack.md",
|
||||||
"version": "1.4",
|
"version": "1.5",
|
||||||
"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" },
|
||||||
{ "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-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-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" },
|
||||||
|
{ "id": "REQ-ROLE-6", "title": "Agents are proactive: each role contract says to act within its grants without waiting to be asked" },
|
||||||
{ "id": "REQ-COS-1", "title": "The chief of staff runs the organization: hires, terminates, launches; never talks to the user" },
|
{ "id": "REQ-COS-1", "title": "The chief of staff runs the organization: hires, terminates, launches; never talks to the user" },
|
||||||
|
{ "id": "REQ-COS-2", "title": "The PM asks the chief of staff for staff; a staff request names the roles and how many of each" },
|
||||||
{ "id": "REQ-ASSIST-1", "title": "The assistant is the user's main agent and passes organization work to the chief of staff" },
|
{ "id": "REQ-ASSIST-1", "title": "The assistant is the user's main agent and passes organization work to the chief of staff" },
|
||||||
{ "id": "REQ-ASSIST-2", "title": "The broker limits the assistant to the user and the chief of staff in both directions; refusals logged" },
|
{ "id": "REQ-ASSIST-2", "title": "The broker limits the assistant to the user and the chief of staff in both directions; refusals logged" },
|
||||||
|
{ "id": "REQ-ASSIST-3", "title": "The assistant tells the user what other roles sent it in its own words and does not relay their text" },
|
||||||
{ "id": "REQ-VAR-1", "title": "Variables in four layers, declared in code; limits intersect; unknown keys refused" },
|
{ "id": "REQ-VAR-1", "title": "Variables in four layers, declared in code; limits intersect; unknown keys refused" },
|
||||||
{ "id": "REQ-VAR-2", "title": "One system config, never auto-written; secrets only as references the broker reads" },
|
{ "id": "REQ-VAR-2", "title": "One system config, never auto-written; secrets only as references the broker reads" },
|
||||||
{ "id": "REQ-CRED-1", "title": "Each role instance has its own Gitea and Vikunja token, held by the broker" },
|
{ "id": "REQ-CRED-1", "title": "Each role instance has its own Gitea and Vikunja token, held by the broker" },
|
||||||
@@ -17,11 +21,22 @@
|
|||||||
{ "id": "REQ-DEC-2", "title": "Decisions route by class: logged, to the domain arbiter, or to the human" },
|
{ "id": "REQ-DEC-2", "title": "Decisions route by class: logged, to the domain arbiter, or to the human" },
|
||||||
{ "id": "REQ-DEC-3", "title": "Authorized-human resolutions come only from a mosaic CLI session outside any agent run" },
|
{ "id": "REQ-DEC-3", "title": "Authorized-human resolutions come only from a mosaic CLI session outside any agent run" },
|
||||||
{ "id": "REQ-DEC-4", "title": "A blocking gated decision reaches an authorized human at once; the rest go in a daily digest" },
|
{ "id": "REQ-DEC-4", "title": "A blocking gated decision reaches an authorized human at once; the rest go in a daily digest" },
|
||||||
|
{ "id": "REQ-DEC-5", "title": "An approval outlives its run, binds to the decision, instance and thread, and is consumed once" },
|
||||||
|
{ "id": "REQ-DEC-6", "title": "A spec commit never reopens a settled decision" },
|
||||||
|
{ "id": "REQ-WAIT-1", "title": "Hard waits always go to the human: spend, delete, irreversible, exposure, accounts, external messages, protected push" },
|
||||||
|
{ "id": "REQ-WAIT-2", "title": "No role file grants a hard wait; changing the list or granting a listed action is itself a hard wait" },
|
||||||
|
{ "id": "REQ-WAIT-3", "title": "An action added to the vocabulary is a hard wait until a PRD version says it is not" },
|
||||||
{ "id": "REQ-MSG-1", "title": "Messages are addressed to a role, append-only, delivered to the current holder; assistant messages name a thread" },
|
{ "id": "REQ-MSG-1", "title": "Messages are addressed to a role, append-only, delivered to the current holder; assistant messages name a thread" },
|
||||||
|
{ "id": "REQ-MSG-2", "title": "Messages can be addressed by an instance name unique in its business; the message records the name and the instance" },
|
||||||
{ "id": "REQ-TASK-1", "title": "Tasks live in Vikunja, each citing a requirement id; the verbs enforce field writers" },
|
{ "id": "REQ-TASK-1", "title": "Tasks live in Vikunja, each citing a requirement id; the verbs enforce field writers" },
|
||||||
{ "id": "REQ-TASK-2", "title": "Polling is the source of truth: a 30-second board poll and an hourly reconcile" },
|
{ "id": "REQ-TASK-2", "title": "Polling is the source of truth: a 30-second board poll and an hourly reconcile" },
|
||||||
{ "id": "REQ-TASK-3", "title": "The installer points at an existing Vikunja or deploys the unmodified upstream image" },
|
{ "id": "REQ-TASK-3", "title": "The installer points at an existing Vikunja or deploys the unmodified upstream image" },
|
||||||
{ "id": "REQ-TASK-4", "title": "The queue keeps the agents' lock, review rounds and audit log; no two-way sync" },
|
{ "id": "REQ-TASK-4", "title": "The queue keeps the agents' lock, review rounds and audit log; no two-way sync" },
|
||||||
|
{ "id": "REQ-PROJ-1", "title": "Each project keeps its records in its own local git repository, with no remote and a fixed, validated layout" },
|
||||||
|
{ "id": "REQ-PROJ-2", "title": "Only the human commits the north-star spec; it counts only when its last commit is by the business human" },
|
||||||
|
{ "id": "REQ-PROJ-3", "title": "The Console edits the spec and docs Markdown as source with a preview; a stale save is refused" },
|
||||||
|
{ "id": "REQ-PROJ-4", "title": "Each human spec commit saved in the Console sends one notice to the PM and the CTO for review" },
|
||||||
|
{ "id": "REQ-LAND-1", "title": "A resolved merge decision lands into the working repository main through the host, only for a granted role" },
|
||||||
{ "id": "REQ-CLI-1", "title": "mosaic is the front door: talk to the assistant in a thread, the inbox, tasks, agents and trails" },
|
{ "id": "REQ-CLI-1", "title": "mosaic is the front door: talk to the assistant in a thread, the inbox, tasks, agents and trails" },
|
||||||
{ "id": "REQ-CLI-2", "title": "The assistant session mosaic talks to is launched and owned by the stack, not T3" },
|
{ "id": "REQ-CLI-2", "title": "The assistant session mosaic talks to is launched and owned by the stack, not T3" },
|
||||||
{ "id": "REQ-WEB-1", "title": "The WebUI shows the CLI's data, a chat area of projects and threads, and Settings for agent limits" },
|
{ "id": "REQ-WEB-1", "title": "The WebUI shows the CLI's data, a chat area of projects and threads, and Settings for agent limits" },
|
||||||
|
|||||||
@@ -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, 43);
|
assert.equal(ids.length, 58);
|
||||||
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.4']);
|
assert.deepEqual([list.prd, list.version], ['docs/prd/mosaic-stack.md', '1.5']);
|
||||||
});
|
});
|
||||||
|
|
||||||
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[43].id: REQ-ROLE-1 twice'));
|
assert.throws(() => loadRequirements(join(dir, 'twice.json')), listError('requirements[58].id: REQ-ROLE-1 twice'));
|
||||||
} finally {
|
} finally {
|
||||||
rmSync(dir, { recursive: true, force: true });
|
rmSync(dir, { recursive: true, force: true });
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user