decisions, build-log, sessions: 110, Q109 follow-ups filed in Vikunja P19 (#153-#161)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -4388,3 +4388,9 @@ After (1a8ba587): Dewey's round 1, 4 files +202 -22. render and install take `--
|
||||
Before: mosaic.woltje.com served the old build, and the Console unit (installed 04:14:24Z from 777ada59) was installed for mosaic-preview.woltje.com, disabled and inactive.
|
||||
|
||||
After: Jason gave the go at 17:21:09Z. Mos reports that ops-01 switched Argo `mosaic-preview` to `overlays/cutover` (Synced and Healthy at ebfd7065 from 17:21:32Z). Mos reinstalled the unit for mosaic.woltje.com under decision 103 and started it at 17:21:41Z, PID 3951396, from 6040d424, which has no webui diff from 36e34e5b. Their checks: anonymous LAN and external requests and forged headers get 302 to Authentik. Directly through the tunnel, the Console returns 401 with no identity and 403 for a non-local Host (ops-01, 17:22Z). Jason reached the dashboard from the WAN. Sage's read-only check at 17:27Z: `scripts/webui-service.sh status` enabled, active, NRestarts 0, "send: on", /healthz 200; ExecStart is decision 103's command. Undo: the Argo path back to `overlays/prod` and a reinstall with the preview host. Found while checking: the unit runs from the canonical checkout, and each request spawns human-cli from it, so landings there are live changes. Decision 109 sets the landing rule until a follow-up row pins the Console's copy.
|
||||
|
||||
### 2026-10-12 — Sage, follow-ups move to Vikunja (Q109, decision 110)
|
||||
|
||||
Before: the follow-ups from rows 76, 83, 67 and the I1 review existed only as lists in BUILD-LOG entries, the standup and decision 109. Mos had filed #146 (decision 109's pinned Console copy) and #147 (decision 101's uid reinstall) in Vikunja project 19, both due 2026-10-13.
|
||||
|
||||
After: Jason ruled "Always use Vikunja for task tracking" (Q109, relayed by Mos in sage-0081). At 17:37:55Z Sage filed nine tasks in project 19 on tasks.woltje.com with the pm token slot, each read back with the sync key: #153 to #155 (the I1, M1 and row 77 briefs, due 2026-10-13) and #156 to #161 (the open follow-ups). Evidence: `agents/sage/work/vikunja-setup/2026-10-12_followups.json` and `file-followups.mjs`. The follow-up lists in the row 83 and cutover entries above are now tracked as #159, #160 and #146. Later entries cite Vikunja ids instead of listing follow-ups.
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
{
|
||||
"at": "2026-10-12T17:37:55.750Z",
|
||||
"project": 19,
|
||||
"created": [
|
||||
{
|
||||
"title": "I1 brief: containerize managed sessions by role (decisions 105-108)",
|
||||
"id": 153,
|
||||
"index": 39,
|
||||
"inverse": "mark done: POST /api/v1/tasks/153 done true (pm); delete is Jason's or ops-01's",
|
||||
"readBack": 200,
|
||||
"project_back": 19,
|
||||
"due_back": "2026-10-13T22:00:00Z",
|
||||
"done": false
|
||||
},
|
||||
{
|
||||
"title": "M1 brief and queue row: chief-of-staff role (decision 104)",
|
||||
"id": 154,
|
||||
"index": 40,
|
||||
"inverse": "mark done: POST /api/v1/tasks/154 done true (pm); delete is Jason's or ops-01's",
|
||||
"readBack": 200,
|
||||
"project_back": 19,
|
||||
"due_back": "2026-10-13T22:00:00Z",
|
||||
"done": false
|
||||
},
|
||||
{
|
||||
"title": "Row 77 brief: Console service hardening (#1571)",
|
||||
"id": 155,
|
||||
"index": 41,
|
||||
"inverse": "mark done: POST /api/v1/tasks/155 done true (pm); delete is Jason's or ops-01's",
|
||||
"readBack": 200,
|
||||
"project_back": 19,
|
||||
"due_back": "2026-10-13T22:00:00Z",
|
||||
"done": false
|
||||
},
|
||||
{
|
||||
"title": "Narrow the pi worker's dataRoot mount (compose.yaml:40)",
|
||||
"id": 156,
|
||||
"index": 42,
|
||||
"inverse": "mark done: POST /api/v1/tasks/156 done true (pm); delete is Jason's or ops-01's",
|
||||
"readBack": 200,
|
||||
"project_back": 19,
|
||||
"due_back": "0001-01-01T00:00:00Z",
|
||||
"done": false
|
||||
},
|
||||
{
|
||||
"title": "Stale docs: LAYERS.md L4 and CONDUCTOR.md lines 17 and 40",
|
||||
"id": 157,
|
||||
"index": 43,
|
||||
"inverse": "mark done: POST /api/v1/tasks/157 done true (pm); delete is Jason's or ops-01's",
|
||||
"readBack": 200,
|
||||
"project_back": 19,
|
||||
"due_back": "0001-01-01T00:00:00Z",
|
||||
"done": false
|
||||
},
|
||||
{
|
||||
"title": "Board Reply needs a delivery path that isn't tmux",
|
||||
"id": 158,
|
||||
"index": 44,
|
||||
"inverse": "mark done: POST /api/v1/tasks/158 done true (pm); delete is Jason's or ops-01's",
|
||||
"readBack": 200,
|
||||
"project_back": 19,
|
||||
"due_back": "0001-01-01T00:00:00Z",
|
||||
"done": false
|
||||
},
|
||||
{
|
||||
"title": "Console cosmetic follow-ups from rows 76 and 83",
|
||||
"id": 159,
|
||||
"index": 45,
|
||||
"inverse": "mark done: POST /api/v1/tasks/159 done true (pm); delete is Jason's or ops-01's",
|
||||
"readBack": 200,
|
||||
"project_back": 19,
|
||||
"due_back": "0001-01-01T00:00:00Z",
|
||||
"done": false
|
||||
},
|
||||
{
|
||||
"title": "Console flag tests: allowed user starting with \"-\", and the [--send] usage text",
|
||||
"id": 160,
|
||||
"index": 46,
|
||||
"inverse": "mark done: POST /api/v1/tasks/160 done true (pm); delete is Jason's or ops-01's",
|
||||
"readBack": 200,
|
||||
"project_back": 19,
|
||||
"due_back": "0001-01-01T00:00:00Z",
|
||||
"done": false
|
||||
},
|
||||
{
|
||||
"title": "Row 67 test 4: assert its new decisions are listed (Dewey N1)",
|
||||
"id": 161,
|
||||
"index": 47,
|
||||
"inverse": "mark done: POST /api/v1/tasks/161 done true (pm); delete is Jason's or ops-01's",
|
||||
"readBack": 200,
|
||||
"project_back": 19,
|
||||
"due_back": "0001-01-01T00:00:00Z",
|
||||
"done": false
|
||||
}
|
||||
],
|
||||
"problems": []
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// Sage, 2026-10-12: Q109 ("Always use Vikunja for task tracking"). Files Sage's open Mosaic follow-ups
|
||||
// as tasks in project 19 with the pm token slot, the way Mos filed #146 to #152. Refuses if any title
|
||||
// already exists in project 19. The sync key reads each task back. No labels, no assignees, no bucket
|
||||
// moves. Rollback is marking the task done; deletion is Jason's or ops-01's. Prints no secret.
|
||||
// Usage: node file-followups.mjs https://tasks.woltje.com PM_TOKEN_FILE SYNC_TOKEN_FILE OUT_JSON
|
||||
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
||||
const [ORIGIN, KEY, SYNC, OUT] = process.argv.slice(2);
|
||||
if (!/^https:\/\/tasks\.woltje\.com$/.test(ORIGIN)) throw new Error("tasks.woltje.com only");
|
||||
if (!OUT || existsSync(OUT)) throw new Error("OUT_JSON missing or exists; refusing");
|
||||
const PROJECT = 19, DUE = "2026-10-13T22:00:00Z";
|
||||
const SIG = "Created by Sage (T3 1ef1e4f8) with the pm token slot until Sage has its own key.";
|
||||
const p = (...xs) => xs.map((x) => `<p>${x}</p>`).join("") + `<p>${SIG}</p>`;
|
||||
const TASKS = [
|
||||
{ title: "I1 brief: containerize managed sessions by role (decisions 105-108)", due: DUE,
|
||||
description: p("Sage writes the I1 brief on BRIEF-TEMPLATE.md from agents/sage/work/next-target/I1-review.md: two broker sockets with the human one only in the Console, a new via value in authenticateHuman, narrow per-run mounts, cap_drop ALL and limits, an internal network with one egress proxy, the rootless endpoint pinned with a refusal unless docker info lists name=rootless, and the subscription token as a read-only file secret. The survey's unverified items go in as spikes with tests. Owner Sage.") },
|
||||
{ title: "M1 brief and queue row: chief-of-staff role (decision 104)", due: DUE,
|
||||
description: p("Move agents/sage/work/next-target/M1-draft.md to docs/plans on BRIEF-TEMPLATE.md and open its queue row: owner Rocko, reviewers Darkwing and Filbert, gate Sage. The row ships roles/chief-of-staff.json and .md, fixture tests in packages/business/tests and the documented instance entry; it never writes Jason's business file. The instance name is #150. Owner Sage.") },
|
||||
{ title: "Row 77 brief: Console service hardening (#1571)", due: DUE,
|
||||
description: p("Write the brief for queue row 77 (Gitea #1571, owner Rocko), held until after the cutover, which is done (#152). Coordinate with #146 and #147, since all three change how the mosaic-webui unit runs. Owner Sage.") },
|
||||
{ title: "Narrow the pi worker's dataRoot mount (compose.yaml:40)",
|
||||
description: p("compose.yaml:40 mounts the whole dataRoot read-write into the pi worker as uid 1000, which includes bus/broker.sock. Narrow it to the worker's own run and workspace paths. This exposure exists today and doesn't wait on I1. Found in Sage's I1 review. Needs a queue row and brief.") },
|
||||
{ title: "Stale docs: LAYERS.md L4 and CONDUCTOR.md lines 17 and 40",
|
||||
description: p("LAYERS.md L4 and docs/plans/CONDUCTOR.md:17,40 say managed sessions run in containers. They run on the host today. Correct the docs, and point at I1 for the containerized design. Found in Sage's I1 review.") },
|
||||
{ title: "Board Reply needs a delivery path that isn't tmux",
|
||||
description: p("The control board's /api/reply pastes into a tmux pane, which can't cross a container boundary. It needs another delivery path before the board or the sessions containerize under I1. Found in Sage's I1 review.") },
|
||||
{ title: "Console cosmetic follow-ups from rows 76 and 83",
|
||||
description: p("Small fixes left after the cutover:",
|
||||
"1. packages/webui/systemd/mosaic-webui.service.in lines 6-9 still say Jason starts the unit and cite decisions 62 and 94; decision 102 says Jason or Mos.",
|
||||
"2. packages/webui/README.md line 106 is 102 characters; wrap it.",
|
||||
"3. Row 76: Darkwing's spliced header-comment sentence at packages/webui/tests/send-browser.test.mjs line 4, and Filbert's two round 2 notes (Gitea #1570 comment 27685).",
|
||||
"4. When a session has expired, the proxy redirects a Reply to sign-in, the fetch fails without a status, and the page says \"Delivery may be unknown\" (packages/webui/src/public/app.js:380). Nothing reached the Console, so it should say not sent and to sign in again.",
|
||||
"Touches packages/webui, so decision 109's notice to Mos applies before landing.") },
|
||||
{ title: "Console flag tests: allowed user starting with \"-\", and the [--send] usage text",
|
||||
description: p("From the row 83 landing (1a8ba587): an --allow-user value beginning with \"-\" needs a decision and a test in both scripts/webui-service.sh and packages/webui/src/serve.mjs, kept consistent. Darkwing's review (#1576 comment 27721) left a surviving mutant on the [--send] usage text; add a test that kills it. Touches packages/webui, so decision 109's notice to Mos applies before landing.") },
|
||||
{ title: "Row 67 test 4: assert its new decisions are listed (Dewey N1)",
|
||||
description: p("Dewey's N1 on row 67 (#1560 comment 27452), which Darkwing repeated (27627): test 4 creates decisions but doesn't assert that the listing shows them. Add the assertion.") },
|
||||
];
|
||||
const key = readFileSync(KEY, "utf8").trim(), sync = readFileSync(SYNC, "utf8").trim();
|
||||
async function api(method, path, body, auth) {
|
||||
const r = await fetch(ORIGIN + path, { method, headers: { Authorization: `Bearer ${auth}`, "Content-Type": "application/json" }, body: body === undefined ? undefined : JSON.stringify(body), redirect: "error" });
|
||||
const t = await r.text(); let json = null; try { json = JSON.parse(t); } catch {}
|
||||
return { status: r.status, json };
|
||||
}
|
||||
const list = (j) => j?.items ?? (Array.isArray(j) ? j : []);
|
||||
async function existing() {
|
||||
const out = [];
|
||||
for (let page = 1; page < 20; page++) {
|
||||
const r = await api("GET", `/api/v2/projects/${PROJECT}/tasks?per_page=50&page=${page}`, undefined, sync);
|
||||
if (r.status !== 200) throw new Error(`list tasks: ${r.status}`);
|
||||
out.push(...list(r.json));
|
||||
if (list(r.json).length < 50) return out;
|
||||
}
|
||||
throw new Error("more than 19 pages");
|
||||
}
|
||||
const rec = { at: new Date().toISOString(), project: PROJECT, created: [], problems: [] };
|
||||
const save = () => {
|
||||
const out = JSON.stringify(rec, null, 1) + "\n";
|
||||
if (out.includes(key) || out.includes(sync)) throw new Error("secret in the record; not written");
|
||||
writeFileSync(OUT, out);
|
||||
};
|
||||
const before = await existing();
|
||||
const clash = before.filter((t) => TASKS.some((x) => x.title === t.title));
|
||||
if (clash.length) throw new Error(`exists: ${clash.map((t) => `#${t.id} ${t.title}`).join("; ")}; refusing`);
|
||||
writeFileSync(OUT, "", { flag: "wx" });
|
||||
|
||||
for (const t of TASKS) {
|
||||
rec.created.push({ title: t.title });
|
||||
save();
|
||||
const body = { title: t.title, description: t.description, ...(t.due ? { due_date: t.due } : {}) };
|
||||
const r = await api("PUT", `/api/v1/projects/${PROJECT}/tasks`, body, key);
|
||||
if (r.status >= 300 || !Number.isSafeInteger(r.json?.id)) {
|
||||
rec.problems.push(`create ${t.title}: ${r.status} ${r.json?.code ?? ""} ${r.json?.message ?? ""}`);
|
||||
break;
|
||||
}
|
||||
Object.assign(rec.created.at(-1), { id: r.json.id, index: r.json.index, inverse: `mark done: POST /api/v1/tasks/${r.json.id} done true (pm); delete is Jason's or ops-01's` });
|
||||
const back = await api("GET", `/api/v2/tasks/${r.json.id}`, undefined, sync);
|
||||
Object.assign(rec.created.at(-1), { readBack: back.status, project_back: back.json?.project_id, due_back: back.json?.due_date, done: back.json?.done });
|
||||
if (back.status !== 200 || back.json?.title !== t.title || back.json?.project_id !== PROJECT || back.json?.done) rec.problems.push(`${t.title} read-back differs`);
|
||||
}
|
||||
save();
|
||||
process.stdout.write(JSON.stringify(rec, null, 1) + "\n");
|
||||
if (rec.problems.length) process.exit(2);
|
||||
@@ -752,3 +752,4 @@ are never rewritten or removed; corrections are new entries.
|
||||
2026-10-12T14:59:20Z | sage | decision 107, I1-3 deferred | Jason deferred Q104 via Mos sage-0077: no WebAuthn in the I1 brief; reopens only as a new question once containers run; a compromised Console holding human authority is recorded as open
|
||||
2026-10-12T15:00:59Z | sage | decision 108, I1-4 answered | Jason chose Q105 A via Mos sage-0078: subscription token as a read-only file secret per session container, no API key or gateway; copy-out risk accepted, bounded by the egress allowlist; all four I1 items answered, brief is Sage's on 2026-10-13
|
||||
2026-10-12T17:27:49Z | sage | cutover live, decision 109 | verified the mosaic.woltje.com Console read-only after Mos's 17:21Z start (status active, send on, healthz 200); decision 109: landings touching webui, bus or cli in the canonical checkout are live changes until the Console runs from a pinned copy; BUILD-LOG entry
|
||||
2026-10-12T17:38:19Z | sage | decision 110, follow-ups to Vikunja | Jason's Q109 via Mos sage-0081: Vikunja project 19 holds Mosaic work items; filed #153-#161 with the pm slot, read back with sync; #146 and #147 cited for the 10-13 rows; queue and Gitea stay the review and landing record; BUILD-LOG entry
|
||||
|
||||
@@ -2437,3 +2437,28 @@ which stay with him. Each item names who decided it and what happened.
|
||||
being deploys. Pointing the unit at it is a reinstall, which is
|
||||
Mos's step under decisions 102 and 103. It goes in the same window
|
||||
as decision 101's uid reinstall.
|
||||
110. **Vikunja holds Mosaic work items (2026-10-12, Q109).** Jason, relayed
|
||||
by Mos in sage-0081: "Always use Vikunja for task tracking." Mosaic
|
||||
work items live as tasks in project 19 (Mosaic Stack) on
|
||||
tasks.woltje.com. Sage files each new one there when raising it and
|
||||
stops keeping follow-up lists in BUILD-LOG, standups or decisions.
|
||||
Those records cite the Vikunja id.
|
||||
- Filed at 17:37:55Z with the pm token slot, as Mos filed #146 to
|
||||
#152, since Sage has no key of its own: #153 I1 brief, #154 M1
|
||||
brief and row, #155 row 77 brief (all three due 2026-10-13), #156
|
||||
compose.yaml:40 mount, #157 stale LAYERS and CONDUCTOR docs, #158
|
||||
board Reply without tmux, #159 Console cosmetic notes from rows 76
|
||||
and 83, #160 Console flag tests, #161 row 67 test 4 (Dewey N1).
|
||||
Each was read back with the sync key. No labels, assignees or
|
||||
bucket moves. Evidence:
|
||||
`agents/sage/work/vikunja-setup/2026-10-12_followups.json`.
|
||||
- Decision 109's pinned-copy row is #146 and decision 101's uid
|
||||
reinstall is #147. Both are due 2026-10-13, and the 10-13 rows
|
||||
cite them.
|
||||
- The queue and its Gitea issues stay the review and landing
|
||||
record, because `queue review` and `verify-commit` gate landings
|
||||
on them. When a row opens for a Vikunja task, the task names the
|
||||
row and the row's brief names the task. Moving the queue itself
|
||||
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.
|
||||
|
||||
Reference in New Issue
Block a user