From 47a8190d3c20fa7cd60f83bb6fd84142039bf5d0 Mon Sep 17 00:00:00 2001 From: Jason Woltje Date: Tue, 13 Oct 2026 09:27:42 -0500 Subject: [PATCH] docs: register Sage row 91 landing; file its follow-up as Vikunja #256 Co-Authored-By: Claude Opus 5.5 --- .../2026-10-13_r91-followup.json | 17 +++++ .../work/vikunja-setup/file-r91-followup.mjs | 64 +++++++++++++++++++ docs/SESSIONS.md | 1 + 3 files changed, 82 insertions(+) create mode 100644 agents/sage/work/vikunja-setup/2026-10-13_r91-followup.json create mode 100644 agents/sage/work/vikunja-setup/file-r91-followup.mjs diff --git a/agents/sage/work/vikunja-setup/2026-10-13_r91-followup.json b/agents/sage/work/vikunja-setup/2026-10-13_r91-followup.json new file mode 100644 index 000000000..a866e5409 --- /dev/null +++ b/agents/sage/work/vikunja-setup/2026-10-13_r91-followup.json @@ -0,0 +1,17 @@ +{ + "at": "2026-10-13T14:27:34.266Z", + "project": 19, + "created": [ + { + "title": "Seat checkout follow-up from row 91: a project root with a submodule never updates its clone", + "id": 256, + "index": 99, + "inverse": "mark done: POST /api/v1/tasks/256 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": [] +} diff --git a/agents/sage/work/vikunja-setup/file-r91-followup.mjs b/agents/sage/work/vikunja-setup/file-r91-followup.mjs new file mode 100644 index 000000000..6ab34bd29 --- /dev/null +++ b/agents/sage/work/vikunja-setup/file-r91-followup.mjs @@ -0,0 +1,64 @@ +// Sage, 2026-10-13: decision 110. Files row 91's non-blocking follow-up (#1585) as one task in project 19 +// with Sage's own key (bot user 13). 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-r91-followup.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, ">"); +const p = (...xs) => xs.map((x) => `

${esc(x)}

`).join("") + `

${esc(SIG)}

`; +const TASKS = [ + { title: "Seat checkout follow-up from row 91: a project root with a submodule never updates its clone", + description: p("Row 91 (Gitea #1585) landed as d240645d. Dewey left one non-blocking note with the round 3 approval (comment 28150).", + "packages/seat/src/checkout.mjs keeps a clone whose index holds a gitlink (mode 160000) with reason submodule. A project whose main branch tracks a submodule gets a fresh clone at the first launch, and that clone has the gitlink in its index. So every later launch keeps it as submodule and never fetches or fast-forwards it. The behavior is deliberate (the host never recurses into a submodule), but packages/seat/README.md does not say a submodule project root stops updating after the first launch.", + "Fix: add a sentence to the seat README next to the kept-outcome table, saying what an operator sees and how to refresh (remove the clone so the next launch reclones). Doc-only, packages/seat. No bus restart needed for a README change.") }, +]; +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); diff --git a/docs/SESSIONS.md b/docs/SESSIONS.md index 9066adcb4..5b960f8b2 100644 --- a/docs/SESSIONS.md +++ b/docs/SESSIONS.md @@ -842,3 +842,4 @@ are never rewritten or removed; corrections are new entries. 2026-10-13T14:11:18Z | sage (1ef1e4f8) | decision 133 redeploy: bus unit reinstall (EnvironmentFile line), start 14:04:18Z on 8ac8055c with rows 89, 90, 92, 94; row 99 dropped (rev 907, #1593 comment 28146 closed, P19 #250 comment 134 done); Darkwing and Dewey resumed; decision 134 Filbert to Opus thread 048f3c63, f712d8af retired | done; Mos enabled the unit 14:09:15Z; revs 903-907 still pending queue-commit 2026-10-13T14:11:50Z | filbert (048f3c63) | onboarding on Claude Opus 5.5 under decision 134; read AGENTS.md, filbert README, SOUL, CONTEXT, decisions 122, 130, 134; queue next filbert: nothing; Gitea GET user returns filbert | idle, no review in flight; rows 91 and 93 stay with Dewey; no suites, mutants or queue-commit run; no push 2026-10-13T14:17:28Z | dewey | row 91 round 3 review (#1585, decision 130), rerun in full after the 13:40Z reboot | approve on manifest ca570607…, comment 28150; gate green incl. test-release 14/14; 41 mutants, 34 killed, 7 survivors all equivalent or second-line; queue rev 908 (f29e4eb20), packet d4dd41db2; worktrees and r91_default removed +2026-10-13T14:27:42Z | sage (1ef1e4f8) | row 91 landing (#1585) on Mos's go: gate rerun on fc6d5822 all green (14 node suites, 9 scripts/test-*.sh); verify-commit ok round 3, 8 paths | landed d240645d, pushed; queue revs 909 to 913 (00e760d4): row 91 done, row 93 to darkwing with reviewers dewey and sage, gate and decision 135 note (decision 131); #1585 closed, comment 28154; Mos notified, bus restart held for Jason's PM re-test (decision 133); Dewey's submodule note filed as Vikunja #256; slot held for rows 98 and 97