decisions, briefs: 111 (Q111/Q112 no date gating), 112 and row 77's pinned-copy brief
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -2462,3 +2462,55 @@ which stay with him. Each item names who decided it and what happened.
|
||||
into Vikunja would be a separate change and goes to Jason first.
|
||||
- If the pm slot fails for Sage, Mos files the task (sage-0081).
|
||||
Agent key scopes are #151, owned by ops-01.
|
||||
111. **Q111 and Q112: work advances without dates (2026-10-12).** Jason,
|
||||
relayed by Mos in sage-0089: "Q111: A, but time is irrelevant. We
|
||||
move through tasks with a "next..." philosophy. Anything that can be
|
||||
done without me should be done without me. Automatic advancement
|
||||
without time restriction." Mos read it back and Jason confirmed in
|
||||
Q112: "precisely" (sage-0090).
|
||||
- Dates don't gate work. The next task starts as soon as its
|
||||
prerequisites are done. A due date is a latest-by marker, not a
|
||||
start date. Holds that named a date, such as decision 94's "after
|
||||
the cutover", end when their condition is met.
|
||||
- Anything that doesn't need Jason goes ahead without him and is
|
||||
reported after. The hard-rule waits (push and merge to next or
|
||||
main, deployment, spend, secrets, policy and role changes, starting
|
||||
the Console) and strategy calls still wait for him.
|
||||
- The Q97 order stands: S7, then M1-M2, then D1-D2 with I1, then
|
||||
I2-I6. I1 containment still comes before Mos runs in the stack
|
||||
(#164).
|
||||
- Sage passes this to Darkwing, Dewey, Filbert, Researcher and Rocko
|
||||
as lead. Each seat goes straight to its next row when one is done.
|
||||
112. **One Console row for the pinned copy, the uid rule and row 77
|
||||
(2026-10-12).** Mos asked for one row and one reinstall (sage-0089)
|
||||
covering Vikunja #146, #147 and #155. Row 77 (#1571, Rocko) takes a
|
||||
new brief, `docs/plans/2026-10-12_console-pinned-copy.md`, with
|
||||
Darkwing added beside Filbert as reviewer because it now changes
|
||||
`packages/webui/src`.
|
||||
- The copy is a `git archive` of a commit on `refactor`, written
|
||||
read-only under `~/.local/share/mosaic-console/<id>/`. A worktree
|
||||
can't be the copy, because the queue store refuses linked
|
||||
worktrees (`packages/queue/src/store.mjs:70`).
|
||||
- The queue view keeps reading through the canonical checkout's
|
||||
`queue-read.mjs` with a new `serve --checkout PATH`, because the
|
||||
store refuses code outside the canonical root (`store.mjs:85`).
|
||||
Tradeoff: that one read child still runs live checkout code, so a
|
||||
landing in `packages/queue` or `queue-read.mjs` still reaches the
|
||||
Console without a reinstall. It writes nothing. Narrowing it
|
||||
means a read path in the store that takes the root as an
|
||||
argument, a `packages/queue` change; I'll file it if the review
|
||||
or use shows a need.
|
||||
- #147 reads decision 101 as a dedicated uid for the unit. Decision
|
||||
101 means the `X-authentik-uid` header in place of
|
||||
`X-authentik-username`, not a separate Unix user. A Unix user
|
||||
would break decision 62's process-ancestry human proof and needs
|
||||
sudo, which Q18 rules out. The row adds the check decision 101
|
||||
describes: with that header, the allowed user must match
|
||||
`^[0-9a-f]{64}$`.
|
||||
- Reading the uid: ops-01 reads Authentik user pk 5's `uid` with
|
||||
its existing admin access, writing nothing, and gives it to Mos.
|
||||
Mos uses it in the reinstall only if it matches the pattern;
|
||||
otherwise the unit stays on the username and Mos reports it.
|
||||
- Mos does the one reinstall after the row lands: `copy`, stop,
|
||||
`install --copy ID` with the uid, start, then status, `/healthz`
|
||||
and the anonymous 302.
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
# Console runs from a pinned copy (2026-10-12)
|
||||
|
||||
Status: written by Sage, lead. It follows `docs/plans/BRIEF-TEMPLATE.md`
|
||||
and replaces row 77's brief in
|
||||
`docs/plans/2026-10-11_console-service-followups.md`. Mos asked for one
|
||||
row that ends in one reinstall (sage-0089): Vikunja #146 (a pinned copy
|
||||
with a deliberate update step), #147 (the uid from lead decision 101) and
|
||||
#155 (row 77's brief). Lead decision 112 records the choices below.
|
||||
|
||||
## Console pinned copy, update step and service hardening
|
||||
|
||||
### Problem
|
||||
|
||||
The Console unit runs `node packages/webui/src/cli.mjs serve` straight
|
||||
from the canonical checkout: `scripts/webui-service.sh:35` sets REPO from
|
||||
the script's own location, and the template puts it in `WorkingDirectory`
|
||||
and `ExecStart` (`packages/webui/systemd/mosaic-webui.service.in:19,24`).
|
||||
Every landing that touches the code the Console loads is a live change
|
||||
the next time Node reads a file or the unit restarts (lead decision 109).
|
||||
The Console needs to run from a copy fixed at one landed commit, changed
|
||||
only by a step someone takes on purpose.
|
||||
|
||||
Two things in the code shape the fix:
|
||||
|
||||
- The queue view runs `packages/webui/src/queue-read.mjs` as a child with
|
||||
its cwd at REPO (`packages/webui/src/reads.mjs:62-66`). The queue store
|
||||
refuses code outside the canonical root (`packages/queue/src/store.mjs:85`)
|
||||
and refuses linked worktrees (`store.mjs:70`). So a copy can't read the
|
||||
queue by itself, and a worktree can't serve as the copy.
|
||||
- The rest of the Console loads relative to its own files: the bus
|
||||
child (`packages/webui/src/bus.mjs:31`), the config check
|
||||
(`packages/cli/src/config.mjs:15,22`), the roles directory
|
||||
(`config.mjs:36`) and `RELEASE` (`reads.mjs:198`). Those can all come
|
||||
from the copy.
|
||||
|
||||
Row 75's review left five gaps (Filbert, comment 27430 on #1569), N1 to N5:
|
||||
|
||||
- N1: `install` doesn't refuse while the unit is active, so the unit's
|
||||
next `Restart=on-failure` runs the new ExecStart without anyone
|
||||
starting it.
|
||||
- N2: an install from a worktree points the unit at the worktree, and no
|
||||
doc says to install from the canonical checkout.
|
||||
- N3: `uninstall --no-reload` skips the active check while the docs say
|
||||
uninstall always refuses while active.
|
||||
- N4: if `systemctl --user list-units` fails, the script exits 1 with no
|
||||
message.
|
||||
- N5: the `--allow-user` value has no check that it starts with a letter
|
||||
or digit.
|
||||
|
||||
Decision 101 switches the unit from `X-authentik-username` to
|
||||
`X-authentik-uid` once the uid is known, and only for a value matching
|
||||
`^[0-9a-f]{64}$`. Today nothing in the script holds that rule. #147's
|
||||
title says "a dedicated uid"; decision 101 means the Authentik header, not
|
||||
a separate Unix user (decision 112).
|
||||
|
||||
### Owner and reviewer
|
||||
|
||||
Owner: Rocko. Reviewers: Darkwing and Filbert. Rocko has no Gitea token;
|
||||
Sage runs the review request as jarvis.
|
||||
|
||||
### Files owned
|
||||
|
||||
- `scripts/webui-service.sh`
|
||||
- `packages/webui/systemd/mosaic-webui.service.in`
|
||||
- `packages/webui/src/cli.mjs`, the new `--checkout` flag and passing it
|
||||
to `consoleReads`
|
||||
- `packages/webui/src/reads.mjs`, only so the queue reader takes its own
|
||||
root
|
||||
- `packages/webui/tests/service.test.mjs` and new tests in
|
||||
`packages/webui/tests/`
|
||||
- `packages/webui/README.md`, the service section
|
||||
- `docs/TOOLS.md`, the script's entry only
|
||||
- `agents/rocko/work/queue-77/`
|
||||
|
||||
### What ships
|
||||
|
||||
The copy:
|
||||
|
||||
- A new verb, `scripts/webui-service.sh copy COMMIT [--copies DIR]`.
|
||||
It runs only from the canonical checkout (the repository root has a
|
||||
`.git` directory, not a worktree's `.git` file). It refuses a COMMIT
|
||||
that isn't reachable from `refs/heads/refactor`. It resolves COMMIT
|
||||
to the full 40-character id.
|
||||
- It writes `git archive` of the paths the Console loads into
|
||||
`DIR/<full id>/`, by default
|
||||
`${XDG_DATA_HOME:-$HOME/.local/share}/mosaic-console/<full id>/`. The
|
||||
path list lives in the script with a comment. It writes into a temp
|
||||
directory beside the target and renames it, then removes write
|
||||
permission from every file in the copy. A copy that already exists is
|
||||
left as it is, and the verb prints `exists:` and its path.
|
||||
- A test makes a copy from a fixture repository, starts `cli.mjs serve`
|
||||
from the copy with a stand-in socket, and gets `/healthz` 200. So a
|
||||
path missing from the list fails the suite.
|
||||
|
||||
The unit:
|
||||
|
||||
- `install` and `render` take `--copy ID`, which names an existing copy
|
||||
by its full id. `install` refuses without it. The template's `@REPO@`
|
||||
becomes the copy's path, so `WorkingDirectory` and `ExecStart` point at
|
||||
the copy.
|
||||
- `serve` takes `--checkout PATH`, the canonical checkout, and the unit
|
||||
passes the checkout the script runs from. The queue read runs the
|
||||
checkout's `queue-read.mjs` with its cwd there, as today. `RELEASE`,
|
||||
the roles directory, the config check and the bus child come from the
|
||||
copy. Without `--checkout`, serve behaves as it does now. A test shows
|
||||
each of the two roots in use.
|
||||
- `status` prints the copy id the unit runs, read from its
|
||||
`WorkingDirectory`, and whether `refactor` in the checkout points at
|
||||
another commit. It never changes anything.
|
||||
|
||||
The update step, documented in TOOLS.md and the README: `copy`, then
|
||||
stop, `install --copy ID` with the same flags, then start. Rollback is
|
||||
the same steps with the earlier id; copies are kept until someone removes
|
||||
them. The stop and start stay Jason's or Mos's (decisions 94, 102 and
|
||||
103).
|
||||
|
||||
N1 to N5:
|
||||
|
||||
- N1: `install` refuses while the unit is active, activating or
|
||||
deactivating, and its message says Jason or Mos stops it first. A test
|
||||
uses a stand-in `systemctl`.
|
||||
- N2: `install` and `copy` refuse when the script isn't in the canonical
|
||||
checkout. `--allow-worktree` overrides the refusal for tests only, and
|
||||
the help text says so.
|
||||
- N3: `uninstall` checks for an active unit even with `--no-reload`, or
|
||||
the docs say exactly when it doesn't. Rocko picks one and a test shows
|
||||
it.
|
||||
- N4: a failed state read prints one line to stderr naming the failed
|
||||
`systemctl` call.
|
||||
- N5: `--allow-user` and the other values must start with a letter or
|
||||
digit. Tests cover the two leading-character checks that row 75's
|
||||
mutants left untested.
|
||||
|
||||
The uid rule from decision 101:
|
||||
|
||||
- When `--auth-header` is `X-authentik-uid` in any letter case,
|
||||
`--allow-user` must match `^[0-9a-f]{64}$`, and `render` and `install`
|
||||
refuse anything else with a message naming the rule. Tests cover a
|
||||
good value, 63 and 65 characters, upper-case hex and a username.
|
||||
|
||||
The template's header comment (lines 6-9) still says only Jason starts the
|
||||
unit; it gets decision 102's wording, Jason or Mos (Vikunja #159 item 1).
|
||||
|
||||
### Out of scope
|
||||
|
||||
Starting, stopping, enabling or restarting the unit. Reading the uid
|
||||
itself: ops-01 reads it without writing anything (decision 112), and the
|
||||
reinstall that uses it is Mos's. Pruning old copies. A health-gated
|
||||
activate and rollback like `scripts/release.sh`, which is D1 for the
|
||||
Console as a whole. Any change to `packages/queue`. The bus service
|
||||
script.
|
||||
|
||||
### Gate
|
||||
|
||||
Darkwing and Filbert approve on #1571, each naming the candidate
|
||||
manifest. The webui node suite and every `scripts/test-*.sh` pass on
|
||||
Sage's gate rerun. Before the landing Sage notifies Mos (decision 109);
|
||||
after it, Sage sends Mos the commit, `scripts/webui-service.sh status`
|
||||
and `/healthz`. The live unit changes only at Mos's one reinstall:
|
||||
`copy` at the landed commit, stop, `install --copy ID` with the uid
|
||||
header and value, start, then status, `/healthz` and the anonymous 302.
|
||||
Reference in New Issue
Block a user