sessions, vikunja-setup: TARGET.md gap table filed as Vikunja #206
Mos sage-0088, fact-finding only. Filed with Sage's own key (user 13) and read back with the sync key. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"at": "2026-10-12T18:00:27.416Z",
|
||||
"project": 19,
|
||||
"title": "Gap table: TARGET.md against Mosaic Stack today (Mos sage-0088)",
|
||||
"problems": [],
|
||||
"create": 201,
|
||||
"id": 206,
|
||||
"index": 57,
|
||||
"createdBy": "bot-mosaic-stack-sage",
|
||||
"inverse": "mark done: POST /api/v1/tasks/206 done true; delete is Jason's or ops-01's",
|
||||
"readBack": 200,
|
||||
"projectBack": 19,
|
||||
"descriptionBytes": 8634
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
// Sage, 2026-10-12: Mos sage-0088. Files the TARGET.md gap table (fact-finding, no build) as one task
|
||||
// in project 19 with Sage's own key (bot user 13, #198). Refuses if the title already exists. The sync
|
||||
// key reads it 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-gap-table.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 TITLE = "Gap table: TARGET.md against Mosaic Stack today (Mos sage-0088)";
|
||||
const esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||
const P = (s) => `<p>${esc(s)}</p>`;
|
||||
const H = (s) => `<h3>${esc(s)}</h3>`;
|
||||
const UL = (...xs) => `<ul>${xs.map((x) => `<li><p>${esc(x)}</p></li>`).join("")}</ul>`;
|
||||
const item = (name, status, evidence, covers) =>
|
||||
H(`${name}: ${status}`) + UL(...evidence.map((e) => `Evidence: ${e}`), `Covered by: ${covers}`);
|
||||
const DESCRIPTION = [
|
||||
P("Fact-finding only, for Jason's fast-track and dogfood decision. Read against /mnt/storage/src/jason-master-restart/TARGET.md and the tree at origin/refactor c2c1a543 on 2026-10-12. Status is exists, partial or missing. Plan items are decision 96's tracks: S7, M1-M3, D1-D4, I1-I6."),
|
||||
item("Deployment at mosaic.woltje.com", "exists", [
|
||||
"Mos started the Console at 2026-10-12T17:21:41Z under decisions 102 and 103; Sage checked status active, send on, healthz 200 (BUILD-LOG, 51711d60).",
|
||||
"It runs from the canonical checkout, so each landing in packages/webui, bus or cli is a live change (decision 109).",
|
||||
], "#146 pinned copy and #147 uid reinstall (both due 10-13); D1 release and rollback; D2 staging."),
|
||||
item("WebUI for agent interaction", "partial", [
|
||||
"Has inbox, tasks, agents and trail views (packages/webui/README.md:246, S5 #1522), and a Message a role box to bus roles under --send (README.md:113, row 76 #1570, row 83 1a8ba587).",
|
||||
"Has the control board Reply into seat panes (README.md:215) and a read-only history view for repository Pi seats only (README.md:225).",
|
||||
"Missing: answering a decision in the browser. The Console shows a mosaic decide command to copy, and decisions are answered in a terminal (README.md:257, REQ-DEC-3).",
|
||||
"Missing: the reply to a web send (row 81 #1575, held by decision 94), a thread view for a role conversation (M2), and signed-in human authority (I4). T3 seats such as Mos, Sage, Rocko and Filbert can't be reached from it.",
|
||||
], "rows 81 and 77; M1-M3; I4. S7 (row 42) is the acceptance run that exercises it."),
|
||||
item("Vikunja as task home", "exists, partial in scope", [
|
||||
"tasks.woltje.com project 19 is the stack's tracker: .mosaic/project.json tracker.project 19, adapter packages/tasks/src/vikunja.mjs, bus sync packages/tasks/src/sync.mjs, Console #/tasks. Decision 110 makes P19 the home for work items. Keys for seats that file their own work are done (#198, bot users 11-14).",
|
||||
"Partial: the queue (docs/plans/queue.json) and its Gitea issues stay the review and landing record, and moving them into Vikunja is Jason's call (decision 110). The sync records snapshots and events but dispatches nothing from them.",
|
||||
"Held by decision 94: task verbs across a project tree (row 63 #1556), the field-writer table (row 61 #1550), the comment cursor across restarts (row 49 #1535), tree-read tests (row 71 #1565).",
|
||||
], "rows 49, 61, 63, 71; S7 (row 42)."),
|
||||
item("TUI on the workstation talking to mosaic.woltje.com", "partial", [
|
||||
"scripts/mosaic with no argument is the full-screen terminal app (packages/cli/README.md:3-8, row 55 #1544), with inbox, decide, tasks, agents, trail and talk.",
|
||||
"It reaches the bus through the broker's Unix socket with human-cli.mjs as a child (packages/cli/README.md:40). On dragon-lin, the host that serves mosaic.woltje.com, it uses the same bus as the Console.",
|
||||
"Missing: a network client. From any other machine it can't reach mosaic.woltje.com, and human writes are proven by local process ancestry (decision 62), not by a sign-in.",
|
||||
], "none for a remote client. I4 (signed-in human authority) is its prerequisite. Rows 69 and 70 are follow-ups to the local app."),
|
||||
item("Settings: git repo locations", "partial", [
|
||||
"The business file <configDir>/businesses/<id>.json holds projects.<id>.root (packages/business/README.md:22, packages/business/examples/mosaic-stack.example.json). Jason writes it by hand.",
|
||||
"The Console shows projects read-only on #/business (packages/webui/README.md:354). Nothing edits them from the Console or the CLI.",
|
||||
], "none."),
|
||||
item("Settings: task management locations", "partial", [
|
||||
"tracker.kind (vikunja only), tracker.baseUrl and tracker.reconcileMinutes at the business layer; tracker.project and tracker.descendants at the project layer (packages/business/src/vars.mjs:90-103).",
|
||||
"File-only. The Console reads them, nothing edits them.",
|
||||
], "none."),
|
||||
item("Settings: repo settings", "partial", [
|
||||
"<root>/.mosaic/project.json holds project vars, changed only by reviewed commits in that repo (packages/business/README.md:23). Keys include issues.repo (vars.mjs:107) and per-role limits.",
|
||||
"No settings view for a repo beyond #/business.",
|
||||
], "none."),
|
||||
item("Settings: system-level branch strategy", "missing", [
|
||||
"git.workingBranch and git.protectedBranches exist only at the project layer (vars.mjs:104-105). There is no system or business-level branch key.",
|
||||
"The system-level rule today is prose (AGENTS.md invariant 8) plus two always-gated actions, git.push.protected and git.merge.protected (packages/business/src/vocabulary.mjs:21, README.md:52).",
|
||||
], "none."),
|
||||
item("Settings: repo-level branch strategy", "partial, declared but not enforced", [
|
||||
"git.workingBranch and git.protectedBranches are declared (vars.mjs:104-105) and set in .mosaic/project.json (refactor; main and next).",
|
||||
"git grep finds no code outside vars.mjs and its tests that reads either key, so nothing enforces them.",
|
||||
], "none."),
|
||||
item("Settings: project settings", "partial", [
|
||||
"The project file plus the business file's projects, resolved in layers system, business, project, agent (packages/business/README.md:111-126).",
|
||||
"The Console is read-only by design: #/business shows the resolved values and #/settings shows only appearance, the notifier binding by name and an about block (packages/webui/README.md:363).",
|
||||
], "none. Row 63 (multi-project verbs) is adjacent."),
|
||||
item("CLIProxyAPI with its WebUI", "missing", [
|
||||
"The only mention: docs/plans/2026-10-11_managed-session-config.md:210-211, where the provider and profile registry in packages/mosaic stays parked and 'comes with CLIProxyAPI'.",
|
||||
"Related and not running: row 65 model routing table (held by decision 94), row 27 registry review and row 29 model switching in the Console (both parked by Jason). Decision 108 chose the subscription token as a read-only file secret over a key-holding gateway for I1.",
|
||||
], "none."),
|
||||
H("What could move earlier without waiting for I1"),
|
||||
UL(
|
||||
"S7 (row 42) is already first and doesn't depend on I1.",
|
||||
"M1, the chief-of-staff role file and its tests, has no write tool (decision 104) and can land on 10-13 (#154). Only M3, retiring T3 Mos, waits on I1 and I4.",
|
||||
"M2, a thread view in the Console, has no containment dependency. Row 81 (the reply to a web send) is its first piece.",
|
||||
"D1 release and rollback for the Console has no I1 dependency and is where #146's pinned copy belongs. scripts/release.sh already does health-gated activate and rollback for worker images (docs/TOOLS.md:105-106).",
|
||||
"D2 staging has no I1 dependency. The mosaic-preview.woltje.com route and unit flags exist from row 74 (packages/webui/README.md:29).",
|
||||
"The decision 94 holds (rows 49, 61, 63-66, 69-71, 80, 81) don't need I1. They can restart on 10-13.",
|
||||
"Not on the plan, and no I1 dependency: a CLI write path for settings, branch keys at the system and business layers, and enforcement of git.workingBranch and git.protectedBranches. Editing settings from the browser is a new human write there, so it meets the same limit as decisions (REQ-DEC-3, decision 62) and waits for I4.",
|
||||
"Should wait: a remote TUI (needs I4's network human proof), CLIProxyAPI as the provider path (it belongs in I1's egress design, agents/sage/work/next-target/I1-review.md:22; a read-only spike could start now), D3 managed coders and reviewers with write tools, I2-I6 and M3.",
|
||||
),
|
||||
P("Caveat: decision 96 names I2-I6 only as 'the images, the human proof inside containers and the install mode', with I4 the signed-in Console. I didn't find a per-item split written down, so this table cites I4 alone and the rest as I2-I6."),
|
||||
P("Created by Sage (T3 1ef1e4f8) with Sage's own key (bot user 13)."),
|
||||
].join("");
|
||||
|
||||
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, title: TITLE, 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) => t.title === TITLE);
|
||||
if (clash.length) throw new Error(`exists: #${clash[0].id}; refusing`);
|
||||
writeFileSync(OUT, "", { flag: "wx" });
|
||||
const r = await api("PUT", `/api/v1/projects/${PROJECT}/tasks`, { title: TITLE, description: DESCRIPTION }, key);
|
||||
rec.create = r.status;
|
||||
if (r.status >= 300 || !Number.isSafeInteger(r.json?.id)) {
|
||||
rec.problems.push(`create: ${r.status} ${r.json?.code ?? ""} ${r.json?.message ?? ""}`);
|
||||
} else {
|
||||
Object.assign(rec, { id: r.json.id, index: r.json.index, createdBy: r.json.created_by?.username, 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, { readBack: back.status, projectBack: back.json?.project_id, descriptionBytes: (back.json?.description ?? "").length });
|
||||
if (back.status !== 200 || back.json?.title !== TITLE || back.json?.project_id !== PROJECT || back.json?.description !== DESCRIPTION) rec.problems.push("read-back differs");
|
||||
}
|
||||
save();
|
||||
process.stdout.write(JSON.stringify(rec, null, 1) + "\n");
|
||||
if (rec.problems.length) process.exit(2);
|
||||
@@ -756,3 +756,4 @@ are never rewritten or removed; corrections are new entries.
|
||||
2026-10-12T17:42:50Z | sage | Vikunja #151 Mos key | Mos sage-0083: svc-mosaic-stack minted bot-mosaic-stack-mos (user 11) and key 11 to mosaic-stack/mos-vikunja.token (0600, expires 2027-01-07), Mos's catalogue cut to Q61a (no task/project delete or duplicate, no share/team/token groups); svc left teams (decision 85) so team 1 membership is Jason's step; /tasks/151 403 until then; cto, coder, reviewer slots read 200 on v2 at check
|
||||
2026-10-12T17:46:46Z | sage | Vikunja #198 launchpad-pm key | Mos sage-0085: svc-mosaic-stack minted bot-launchpad-pm (user 12) and key 12 to mosaic-stack/launchpad-pm-vikunja.token (0600, 43 bytes, expires 2027-01-07) with mint-bot.mjs, the mint-mos.mjs pattern generalized; scopes identical to key 11 (Q61a cut); /tasks/198 403 and 0 projects until ops-01 adds user 12 to team 1 (Mos routes it); seat list to Mos
|
||||
2026-10-12T17:47:42Z | sage | Vikunja #198 sage and ops-01 keys | Mos sage-0086 (rule: a seat that files its own work on tasks.woltje.com gets a key): mint-bot.mjs minted bot-mosaic-stack-sage (user 13, key 13) and bot-mosaic-stack-ops-01 (user 14, key 14) to mosaic-stack/sage-vikunja.token and ops-01-vikunja.token (0600, 43 bytes, expires 2027-01-07), scopes identical to key 11; 0 projects and /tasks/198 403 until ops-01 adds users 12-14 to team 1 (ops01-0098 for 12, Mos go for 13 and 14); Darkwing, Dewey, Filbert, Researcher and Rocko get none, Sage files for them
|
||||
2026-10-12T18:00:47Z | sage | gap table vs TARGET.md, Vikunja #206 | Mos sage-0088, fact-finding only: eleven TARGET items rated exists/partial/missing with file:line evidence and covering rows or plan items; filed as P19 #206 with Sage's own key (user 13), read back with sync; deployment and Vikunja exist, WebUI, TUI and most settings partial, system-level branch strategy and CLIProxyAPI missing
|
||||
|
||||
Reference in New Issue
Block a user