decisions, vikunja-setup, S7: 114 (#162 round 1, Q113-Q119), 115 (order around I1, S7 restart); P19 #219-#222; S7 run log
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
# Slice 1 S7 run log (row 42, #1524)
|
||||
|
||||
Append-only. Brief: `docs/plans/2026-10-04_slice-1.md`, "Slice 1 S7:
|
||||
acceptance, five pieces in a row". Gate: Jason, from the five trails.
|
||||
|
||||
## 2026-10-12T18:30Z start (lead decisions 94, 114, 115)
|
||||
|
||||
Row 42 moved briefed to in-progress at queue rev 673 (op
|
||||
sage-42-start-q118), on Mos's sage-0092 and Jason's Q118 A.
|
||||
|
||||
What counts as a piece: one of the first five Mosaic Stack requests Jason
|
||||
sends to the PM through the Console or `mosaic talk pm` after the Q119
|
||||
confirm at 2026-10-12T18:27Z. A request sent to T3 Mos doesn't count. Mos
|
||||
gives Jason the command and logs the slip on Vikunja #205 (decision 114).
|
||||
|
||||
What each piece needs to pass (the brief): carried to a closed, reviewed
|
||||
task; its trail in `mosaic trail` and the Console; only gated decisions
|
||||
reached Jason; the bus event counts, which are the first measures
|
||||
(REQ-EVT-1).
|
||||
|
||||
How I track it. Agent seats can't read the trail or the bus (REQ-DEC-3;
|
||||
`scripts/mosaic trail` from this seat refused before 2026-10-12T18:30Z, "this runs only
|
||||
from a human shell"). So I track each piece through what I can read: the
|
||||
P19 task the PM creates (sync key), and any queue row and Gitea issue the
|
||||
piece makes. For each piece this log records the trail subject (the task
|
||||
ref `vikunja:19/<id>`, or the message id if Jason gives it), so Jason can
|
||||
read the trail and count the events in the Console `#/trail` or with
|
||||
`mosaic trail <subject>` at his gate. Nobody builds an agent read path
|
||||
around that (decision 115).
|
||||
|
||||
Known gaps on day one:
|
||||
- The Console doesn't show the reply to a web send yet. That's row 81
|
||||
(Dewey, in progress); `mosaic talk pm` shows replies until it lands.
|
||||
- Mos files the friction they see on #205 with the dogfood label.
|
||||
|
||||
## Pieces
|
||||
|
||||
| # | Sent (UTC) | Channel | Request | P19 task | Trail subject | Decisions to Jason | Closed and reviewed | Event counts (Jason) |
|
||||
|---|---|---|---|---|---|---|---|---|
|
||||
| 1 | | | | | | | | |
|
||||
| 2 | | | | | | | | |
|
||||
| 3 | | | | | | | | |
|
||||
| 4 | | | | | | | | |
|
||||
| 5 | | | | | | | | |
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"at": "2026-10-12T18:30:17.774Z",
|
||||
"project": 19,
|
||||
"created": [
|
||||
{
|
||||
"title": "Q115: system branch floor enforced by the gate (decision 114)",
|
||||
"id": 219,
|
||||
"index": 70,
|
||||
"inverse": "mark done: POST /api/v1/tasks/219 done true; delete is Jason's or ops-01's",
|
||||
"readBack": 200,
|
||||
"createdBy": "bot-mosaic-stack-sage"
|
||||
},
|
||||
{
|
||||
"title": "Q114: mosaic settings set for TARGET settings, gated and trailed (decision 114)",
|
||||
"id": 220,
|
||||
"index": 71,
|
||||
"inverse": "mark done: POST /api/v1/tasks/220 done true; delete is Jason's or ops-01's",
|
||||
"readBack": 200,
|
||||
"createdBy": "bot-mosaic-stack-sage"
|
||||
},
|
||||
{
|
||||
"title": "Q116 ops: deploy CLIProxyAPI read-only behind Authentik on mosaic.woltje.com (ops-01 via Mos)",
|
||||
"id": 221,
|
||||
"index": 72,
|
||||
"inverse": "mark done: POST /api/v1/tasks/221 done true; delete is Jason's or ops-01's",
|
||||
"readBack": 200,
|
||||
"createdBy": "bot-mosaic-stack-sage"
|
||||
},
|
||||
{
|
||||
"title": "Q116 stack: Console shows CLIProxyAPI usage per account, read-only (decision 114)",
|
||||
"id": 222,
|
||||
"index": 73,
|
||||
"inverse": "mark done: POST /api/v1/tasks/222 done true; delete is Jason's or ops-01's",
|
||||
"readBack": 200,
|
||||
"createdBy": "bot-mosaic-stack-sage"
|
||||
}
|
||||
],
|
||||
"problems": []
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
// Sage, 2026-10-12: Mos sage-0092 and lead decisions 114-115. Files one task in project 19 for each piece of
|
||||
// work from #162 round 1 (Q114, Q115, Q116's ops and stack parts), in next order around I1. Sage's own key
|
||||
// (bot user 13). Refuses if any title already exists. The sync key reads each task back. No labels, no
|
||||
// assignees, no bucket moves. Rollback is marking a task done; deletion is Jason's or ops-01's. Prints no secret.
|
||||
// Usage: node file-162-round1.mjs https://tasks.woltje.com SAGE_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;
|
||||
const SIG = "Created by Sage (T3 1ef1e4f8) with Sage's own key (bot user 13).";
|
||||
const esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||
const p = (...xs) => xs.map((x) => `<p>${esc(x)}</p>`).join("") + `<p>${esc(SIG)}</p>`;
|
||||
const ORDER = "Order (lead decision 115): Sage writes the I1 brief (#153) first, then Q115's brief, then Q114's. Q115 is built before the I1 images; Q114 follows Q115 and can run beside D1-D2 and I1. Q116's ops part starts now with ops-01; its stack part follows the ops part and row 77.";
|
||||
const TASKS = [
|
||||
{
|
||||
title: "Q115: system branch floor enforced by the gate (decision 114)",
|
||||
description: p(
|
||||
"Jason's Q115 A, confirmed by Q119. A system branch floor that the gate enforces in code. Agents never push to a protected branch. A protected branch changes only by a PR with green CI and review, which is how R28 merges continue. A repo can tighten the floor, never loosen it. A repo that names no protected branch has its default branch protected.",
|
||||
"Today git.push.protected and git.merge.protected are always gated and reach Jason as decisions (packages/business/src/vocabulary.mjs:42-43). git.workingBranch and git.protectedBranches exist only at the project layer and nothing outside vars.mjs reads them. The brief settles how the floor turns those into refusals and checks, and adds the system-layer key.",
|
||||
"Next step: Sage writes the brief and queue row, after the I1 brief (#153). Second in this set.",
|
||||
ORDER,
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Q114: mosaic settings set for TARGET settings, gated and trailed (decision 114)",
|
||||
description: p(
|
||||
"Jason's Q114 A, confirmed by Q119. A CLI write path, mosaic settings set, for repo locations, task locations, repo settings and project settings (the gap table in #206). Every write goes through the gate and into the trail. Browser editing waits for I4.",
|
||||
"If the design lets an agent loosen a setting Jason set, it goes back to Jason through Mos before it ships. Branch settings follow Q115's floor, so this comes after Q115.",
|
||||
"Next step: Sage writes the brief and queue row after Q115's. Third in this set.",
|
||||
ORDER,
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Q116 ops: deploy CLIProxyAPI read-only behind Authentik on mosaic.woltje.com (ops-01 via Mos)",
|
||||
description: p(
|
||||
"Jason's Q116 A, confirmed by Q119. ops-01's part, routed through Mos: run CLIProxyAPI with its WebUI at a path on the existing mosaic.woltje.com host, behind Authentik, admitting jason.woltje only (as the Console, Q108). Jason's A approves this exposure. A new DNS name or subdomain goes back to Jason first.",
|
||||
"The management key goes in a slot that ops-01 wires by name. No agent reads it and nothing prints it. Q105's file-secret token is unchanged. No session traffic goes through CLIProxyAPI until Jason rules on I1's egress routing design.",
|
||||
"Can start now. First in this set.",
|
||||
ORDER,
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Q116 stack: Console shows CLIProxyAPI usage per account, read-only (decision 114)",
|
||||
description: p(
|
||||
"Jason's Q116 A, confirmed by Q119. The stack part: the Console shows usage per account from CLIProxyAPI, read-only. No session traffic through it until Jason rules on I1's egress routing design. No agent reads the management key; the stack names it by slot only.",
|
||||
"Follows the ops task (deployment behind Authentik) and queue row 77, since it is a Console landing (decision 109). Sage writes the brief and queue row then. Fourth in this set.",
|
||||
ORDER,
|
||||
),
|
||||
},
|
||||
];
|
||||
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 clash = (await existing()).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 r = await api("PUT", `/api/v1/projects/${PROJECT}/tasks`, { title: t.title, description: t.description }, 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; 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, createdBy: back.json?.created_by?.username });
|
||||
if (back.status !== 200 || back.json?.title !== t.title || back.json?.project_id !== PROJECT || back.json?.description !== t.description) rec.problems.push(`${t.title} read-back differs`);
|
||||
}
|
||||
save();
|
||||
process.stdout.write(JSON.stringify(rec.created.map(({ id, title, readBack, createdBy }) => ({ id, title, readBack, createdBy })), null, 1) + "\n");
|
||||
if (rec.problems.length) { process.stdout.write(JSON.stringify(rec.problems) + "\n"); process.exit(2); }
|
||||
Reference in New Issue
Block a user