docs(guides): slice 1 identities round 2; lead decision 58

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-04 22:10:32 -05:00
co-authored by Claude Opus 5.5
parent 4d51c1304d
commit 104cf4f3f0
4 changed files with 93 additions and 42 deletions
+1
View File
@@ -487,3 +487,4 @@ are never rewritten or removed; corrections are new entries.
2026-10-04 | rocko | Slice 1 S2 #1519 row 37 | Started after schema v3 pin verification and Sage authorization; fixture credentials only; Darkwing review and Sage commit pending.
2026-10-05T02:58:34Z | Sage (T3 Claude Code, thread 1ef1e4f8) | slice 1 SR runbook, S5 rulings | docs/guides/slice-1-identities.md drafted for Darkwing's review (row 35); lead decision 56 accepts Dewey's Q1-Q5; Rocko started row 37
2026-10-05T03:03:02Z | Sage (T3 Claude Code, thread 1ef1e4f8) | schema v3a | Darkwing's v3a (29daa482) rerun matched on Node 26; lead decision 57; row 37 note
2026-10-05T03:10:32Z | Sage (T3 Claude Code, thread 1ef1e4f8) | row 35 SR round 2 | guide revised per Darkwing's round 1 (comment 26709): mint error path, jq strings, read -rs Gitea tokens, business-prefixed bot names; PRD 0.4 REQ-CRED-1 naming; lead decision 58
+71 -38
View File
@@ -32,47 +32,69 @@ GITEA=https://git.example # your Gitea base URL
EXP=$(date -u -d '+90 days' +%Y-%m-%dT00:00:00Z) # Vikunja token expiry
```
Write `EXP` down. It goes in the business file as each Vikunja token's
`expires`.
Write down the date part of `EXP`. It goes in the business file as
each Vikunja token's `expires`, in `YYYY-MM-DD` form. The broker treats
00:00Z on that date as the expiry, the same instant `EXP` names.
The commands need curl 7.76 or later, for `--fail-with-body`.
## 1. Gitea: four bot users and their tokens
Gitea tokens don't expire, and the HTTP route for creating one needs a
password, so this part uses the web UI.
1. As a site admin, create the users `pm-bot`, `cto-bot`, `coder-bot`
and `reviewer-bot` (Site Administration, User Accounts, Create). Give
1. As a site admin, create the users `mosaic-stack-pm-bot`,
`mosaic-stack-cto-bot`, `mosaic-stack-coder-bot` and
`mosaic-stack-reviewer-bot` (Site Administration, User Accounts,
Create). Gitea usernames are global to the instance, so the business
id in the name keeps one business's bots, and revoking them, apart
from another's. Give
each a long random password, which you won't need again after step 3.
None of them is an admin.
2. In `mosaicstack/stack`, add each bot as a collaborator:
| Bot | Repository access |
|---|---|
| pm-bot | Write (labels, assignees and closing need it) |
| cto-bot | Write |
| coder-bot | Write |
| reviewer-bot | Read |
| mosaic-stack-pm-bot | Write (labels, assignees and closing need it) |
| mosaic-stack-cto-bot | Write |
| mosaic-stack-coder-bot | Write |
| mosaic-stack-reviewer-bot | Read |
No bot is on a protected branch's push or merge allowlist. Pushing to
a protected branch is a gated action, and it stays Jason's.
No bot is on a protected branch's push, merge or approvals allowlist.
Pushing to a protected branch is a gated action, and merges stay
Jason's. Reviewer-bot's approvals in Gitea are advisory and don't
count toward required approvals. The review verdict that gates a row
lives in the queue and the bus.
3. Log in as each bot, go to Settings, Applications, and generate one
token named `mosaic-stack-<role>-<date>` with these scopes:
| Bot | Scopes |
|---|---|
| pm-bot | `write:issue`, `read:repository`, `read:user` |
| cto-bot | `write:issue`, `write:repository`, `read:user` |
| coder-bot | `write:issue`, `write:repository`, `read:user` |
| reviewer-bot | `write:issue`, `write:repository`, `read:user` |
| mosaic-stack-pm-bot | `write:issue`, `read:repository`, `read:user` |
| mosaic-stack-cto-bot | `write:issue`, `write:repository`, `read:user` |
| mosaic-stack-coder-bot | `write:issue`, `write:repository`, `read:user` |
| mosaic-stack-reviewer-bot | `write:issue`, `write:repository`, `read:user` |
Leave every other scope unset, and set none to "public only".
Reviewer-bot's `write:repository` is there because Gitea files pull
request reviews under the repository scope. Its Read collaborator
access is what stops it pushing. Row SR's review checks that before
anyone relies on it.
4. Copy each token into its file with an editor, never `echo`:
`$S/pm-gitea.token`, `$S/cto-gitea.token`, `$S/coder-gitea.token`,
`$S/reviewer-gitea.token`. One line, the token only.
Reviewer-bot's `write:repository` is there because Gitea checks pull
request reviews against the repository scope. Its Read collaborator
access stops pushes to branches and tags. It does not stop an AGit
push to `refs/for/<branch>`, which opens a pull request. That's
acceptable only because the broker holds the token and offers the
reviewer no push action.
4. Write each token to its file from a silent prompt, in the same
`umask 077` shell. Paste the token at the prompt and press Enter:
```sh
for r in pm cto coder reviewer; do
printf '%s token: ' "$r"; read -rs t; echo
printf %s "$t" > "$S/$r-gitea.token"; unset t
done
```
`read` and `printf` are shell builtins, so the value never reaches
`ps` or the history. Don't use an editor, which can leave a swap or
backup copy behind.
5. Log out of each bot account. Record today's date as each Gitea
token's `rotateBy` base. Section 5 has the rotation schedule.
@@ -110,9 +132,13 @@ docker exec -it mosaic-vikunja /app/vikunja/vikunja user create -u jason -e you@
VK=http://127.0.0.1:3456
```
`user create` asks for the password when you leave out `-p`. If your
build doesn't prompt, stop and set it some other way. Don't put the
password on the command line.
`user create` asks for the password when you leave out `-p`. Keep `-it`,
because without a terminal the command exits instead of prompting. Never
put the password on the command line.
Both mounts are required. The container runs as your uid, and
`/app/vikunja` isn't writable to it, so Vikunja needs the files mount
for its startup write check and the db mount for its database.
### Owner login for this guide
@@ -135,8 +161,8 @@ print("owner header written")
PY
```
The probes logged in at `/api/v2/login`. The `token` field name is
assumed from v1, so the script stops if it's missing. Section 4 deletes
The `token` field is confirmed in the v2.7.0 source (`auth_login.go`).
The script still stops if it's missing. Section 4 deletes
the header file.
## 3. Vikunja: the project, the board and the bots
@@ -150,9 +176,9 @@ In the web UI, as the owner:
`blocked`. Each title appears once.
3. In the view's settings, check that the done bucket is `done` and the
default bucket is `todo`. Leave bucket configuration on manual.
4. Create the labels the business file template lists, and note each id.
The business file names labels by id until row S3 settles which
labels a bot can see.
4. Slice 1 needs no labels, since the requirement id lives in the task,
not in a label. If you create any, note each id. The business file
names labels by id until row S3 settles which labels a bot can see.
```sh
P=<project id>
@@ -213,8 +239,10 @@ mint() { # mint ROLE BOT_ID SCOPES_FILE
local r=$1 id=$2 sc=$3 resp="$S/.mint-$1.json"
jq -n --arg t "$BIZ-$r-$(date +%F)" --argjson o "$id" --arg e "$EXP" --slurpfile p "$sc" \
'{title:$t, owner_id:$o, expires_at:$e, permissions:$p[0]}' |
api -X POST "$VK/api/v2/tokens" --data-binary @- -o "$resp" &&
jq -j .token "$resp" > "$S/$r-vikunja.token" &&
curl -s --fail-with-body -H @"$S/vikunja-owner.hdr" -H 'Content-Type: application/json' \
-X POST "$VK/api/v2/tokens" --data-binary @- -o "$resp" ||
{ jq -c '{code, message}' "$resp"; rm -f "$resp"; return 1; }
jq -je '.token | strings' "$resp" > "$S/$r-vikunja.token" &&
jq -c '{id, owner_id, expires_at, starts_tk: (.token | startswith("tk_"))}' "$resp"
rm -f "$resp"
}
@@ -226,9 +254,11 @@ mint sync <sync id> "$S/scopes-sync.json"
```
Check that each line shows `starts_tk: true` and the `expires_at` you
set. Vikunja accepts a past expiry without complaint, so read it. A 400
with code 14002 means a scope name is wrong. Fix the scope file and mint
again.
set. Vikunja accepts a past expiry without complaint, so read it. On an
error, `mint` prints the response's `code` and `message`, which carry no
token. Code 14002 means a scope name is wrong. Fix the scope file and
mint again. If the response has no `token` field, `mint` writes nothing
usable and stops; delete the empty file before you retry.
## 4. Check and clean up
@@ -261,14 +291,17 @@ role. The stack never writes this file.
## 5. Rotation
- **Vikunja**, before `expires`: log in as the owner (section 2), mint a
new token for the same bot with `mint`, update `expires` in the
business file, and restart the broker. Then revoke the old token:
- **Vikunja**, before `expires`: in a new shell, rerun section 0, the
owner login in section 2, and the `api` and `mint` definitions in
section 3. Mint a new token for the same bot, update `expires` in the
business file, restart the broker, and finish with section 4's
`rm -f`. Then revoke the old token:
`api -X DELETE "$VK/api/v2/tokens/<old id>"`. The broker refuses to
start with a token past its `expires`, and it treats any 401 as a
refusal, never a retry.
- **Gitea**, every 90 days or at once if a token may have leaked: log in
as the bot, generate the replacement, write it to the file, restart
as the bot, generate the replacement, write it to the file with the
`read -rs` loop from section 1, restart
the broker, then delete the old token in the same settings page.
Update `rotateBy`. Each rotation gets one line in
`docs/SESSIONS.md`: date, who, which identities, and no values.
+16
View File
@@ -952,3 +952,19 @@ which stay with him. Each item names who decided it and what happened.
- The broker, not a trigger, links `action.*` and `review.*` events
to a task, because the kind alone doesn't say when one is about a
task.
58. **Row SR round 1 rulings (2026-10-04).** Source: Darkwing's review,
#1517 comment 26709, record `agents/darkwing/work/slice1-sr-review-r1-2026-10-04.md`.
Every change requested is taken into the guide.
- Bot names carry the business id on both services, because usernames
are global to each instance: Gitea `<business>-<role>-bot`, Vikunja
`bot-<business>-<role>`. PRD draft 0.4's REQ-CRED-1 wording changes
to match. That is a naming correction inside a draft, not a new
requirement.
- Reviewer-bot's Gitea approvals are advisory. It joins no approvals
allowlist, merges stay Jason's, and the verdict that gates a row
lives in the queue and the bus.
- The guide says plainly that reviewer-bot's token can open a pull
request through an AGit push. That's acceptable only because the
broker holds the token and gives the reviewer no push action.
- Slice 1 needs no Vikunja labels. The business file example comes
from row S1, and the template file waits for it.
+5 -4
View File
@@ -122,10 +122,11 @@ parent requirement is refused.
(Gitea, Vikunja). The broker holds them, and they never enter an agent's
environment or files. In v1 Jason creates them by hand from a runbook
Sage writes (round 3, 1A):
- in Gitea, a new bot user per role: pm-bot, cto-bot, coder-bot and
reviewer-bot (round 3, 2A);
- in Vikunja, a `bot-<role>` user per role, plus one read-only
`bot-<business>-sync` that does all polling (lead decision 52).
- in Gitea, a new bot user per role, named `<business>-<role>-bot`,
for example `mosaic-stack-pm-bot` (round 3, 2A; lead decision 58);
- in Vikunja, a `bot-<business>-<role>` user per role, plus one
read-only `bot-<business>-sync` that does all polling (lead
decisions 52 and 58).
- **REQ-CRED-2.** A session that finds only founder credentials stops.
### Decisions