From 2b75ef4183fec36f408a54e6950819389137c6c6 Mon Sep 17 00:00:00 2001 From: Jason Woltje Date: Tue, 13 Oct 2026 08:08:54 -0500 Subject: [PATCH] ledger: weekly run 2026-10-04 to 2026-10-10, issue call refused; P19 #252 (#1508) Co-Authored-By: Claude Opus 5.5 --- agents/sage/work/ledger/2026-10-13_weekly.txt | 63 ++++++++++++++++++ .../2026-10-13_ledger-before.json | 17 +++++ .../work/vikunja-setup/file-ledger-before.mjs | 64 +++++++++++++++++++ 3 files changed, 144 insertions(+) create mode 100644 agents/sage/work/ledger/2026-10-13_weekly.txt create mode 100644 agents/sage/work/vikunja-setup/2026-10-13_ledger-before.json create mode 100644 agents/sage/work/vikunja-setup/file-ledger-before.mjs diff --git a/agents/sage/work/ledger/2026-10-13_weekly.txt b/agents/sage/work/ledger/2026-10-13_weekly.txt new file mode 100644 index 000000000..819212332 --- /dev/null +++ b/agents/sage/work/ledger/2026-10-13_weekly.txt @@ -0,0 +1,63 @@ +Weekly ledger run by Sage, 2026-10-13, week 2026-10-04 to 2026-10-10 (row 7, #1508). Due Monday 2026-10-12; it ran a day late. +Seats declared with --unsupported-runtime: darkwing, dewey, filbert, researcher, rocko, sage. + +== Run 1, with issues, 2026-10-13T13:07:52Z, exit 2 +Issues unavailable: full 50-row page may be incomplete; one-call limit forbids pagination. Narrow --since or use --no-issues + +== Run 2, --no-issues, 2026-10-13T13:08:19Z, exit 0 +Queue checks: queue.json revision 892, as of 2026-10-13T13:08:19.302Z +violation age row 13: row 13 "Ledger "queue" section: issue/row/seat drift printed with the weekly number" is required and not done, 30 days since 2026-09-13 +undecided issues-not-run: the issue checks did not run (--no-issues) +owner darkwing (rows 13, 91): exempt, declared with --unsupported-runtime +owner researcher (rows 71, 80, 84, 88, 97, 98, 99): exempt, declared with --unsupported-runtime +owner sage (rows 7, 42): exempt, declared with --unsupported-runtime +liveness: 0 pid-present (unverified), 3 exempt, 0 pid-unknown, 0 missing, 0 invalid, 0 pid-gone +declared unsupported runtime: darkwing, dewey, filbert, researcher, rocko, sage +queue issue checks: not run +protected change rev 32 move by sage at 2026-10-04T05:04:12.580Z: row 9 +protected change rev 33 move by sage at 2026-10-04T05:04:12.696Z: row 10 +protected change rev 35 note by sage at 2026-10-04T05:04:12.925Z: row 11 +protected change rev 55 move by jason at 2026-10-04T18:59:33.100Z: row 32 +protected change rev 122 move by sage at 2026-10-05T21:43:41.213Z: row 11 +protected changes in range: 5 (not checks; confirm the actors) +queue: 1 violation; result fail + +Ledger 2026-10-04 through 2026-10-10 UTC +Issue | Title | Opened | Hours open | Commits | Follow-ups | Seats +#1507 | unknown | unknown | unknown | 4 | 3 | darkwing, dewey, filbert, sage +#1508 | unknown | unknown | unknown | 19 | 18 | darkwing, dewey, filbert, sage +#1510 | unknown | unknown | unknown | 2 | 1 | +#1514 | unknown | unknown | unknown | 2 | 1 | darkwing +#1516 | unknown | unknown | unknown | 4 | 3 | darkwing, filbert, sage +#1517 | unknown | unknown | unknown | 5 | 4 | darkwing, sage +#1518 | unknown | unknown | unknown | 4 | 3 | darkwing, filbert, rocko, sage +#1519 | unknown | unknown | unknown | 2 | 1 | darkwing, rocko, sage +#1520 | unknown | unknown | unknown | 1 | 0 | darkwing, filbert, rocko, sage +#1521 | unknown | unknown | unknown | 1 | 0 | darkwing, filbert, rocko, sage +#1522 | unknown | unknown | unknown | 2 | 1 | darkwing, dewey, filbert, sage +#1525 | unknown | unknown | unknown | 4 | 3 | darkwing, rocko, sage +#1526 | unknown | unknown | unknown | 3 | 2 | darkwing, dewey, rocko, sage +#1527 | unknown | unknown | unknown | 8 | 7 | darkwing, filbert, rocko, sage +#1528 | unknown | unknown | unknown | 2 | 1 | darkwing, dewey, sage +#1533 | unknown | unknown | unknown | 2 | 1 | darkwing, dewey, filbert, sage +#1534 | unknown | unknown | unknown | 1 | 0 | darkwing, dewey, filbert, sage +#1535 | unknown | unknown | unknown | 1 | 0 | +#1536 | unknown | unknown | unknown | 2 | 1 | darkwing, dewey, filbert, sage +#1537 | unknown | unknown | unknown | 3 | 2 | darkwing, dewey, filbert, sage +#1538 | unknown | unknown | unknown | 3 | 2 | darkwing, dewey, filbert, sage +#1542 | unknown | unknown | unknown | 13 | 12 | darkwing, dewey, filbert, sage +#1543 | unknown | unknown | unknown | 1 | 0 | darkwing, dewey, filbert, sage +#1545 | unknown | unknown | unknown | 9 | 8 | darkwing, filbert, rocko, sage +#1552 | unknown | unknown | unknown | 1 | 0 | +#1554 | unknown | unknown | unknown | 1 | 0 | darkwing, dewey + +Seat | Board | Agent | Human +darkwing | 0 | 77 | 20 +dewey | 0 | 46 | 25 +filbert | 0 | 46 | 22 +researcher | 0 | 2 | 0 +rocko | 0 | 51 | 4 +sage | 0 | 149 | 49 +t3:unmapped | 0 | 0 | 1 + +Totals: issues closed unknown | median hours open unknown | commits 96 | follow-ups per issue 2.8 | human messages per closed issue unknown diff --git a/agents/sage/work/vikunja-setup/2026-10-13_ledger-before.json b/agents/sage/work/vikunja-setup/2026-10-13_ledger-before.json new file mode 100644 index 000000000..01c45126c --- /dev/null +++ b/agents/sage/work/vikunja-setup/2026-10-13_ledger-before.json @@ -0,0 +1,17 @@ +{ + "at": "2026-10-13T13:08:44.555Z", + "project": 19, + "created": [ + { + "title": "Ledger issue query needs an upper bound: weekly run refuses on a full page", + "id": 252, + "index": 95, + "inverse": "mark done: POST /api/v1/tasks/252 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-ledger-before.mjs b/agents/sage/work/vikunja-setup/file-ledger-before.mjs new file mode 100644 index 000000000..12dc2876d --- /dev/null +++ b/agents/sage/work/vikunja-setup/file-ledger-before.mjs @@ -0,0 +1,64 @@ +// Sage, 2026-10-13: decision 110. Files the ledger issue-query bound (row 7, #1508) 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-ledger-before.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: "Ledger issue query needs an upper bound: weekly run refuses on a full page", + description: p("Row 7's run for 2026-10-04 to 2026-10-10 (2026-10-13) exited 2: the issue call returned a full 50-row page. packages/ledger/src/ledger.mjs:52 asks Gitea for issues updated since the start date with no upper bound, so every issue touched after the week also counts toward the 50. With the volume since 2026-10-04, no past week can run with issues.", + "Fix: pass Gitea's before parameter at the end of the range, so the one call covers the week only, and test it with the fake gitea-api.sh. Note that an issue closed in the week and updated again later drops out of an updated-before window, so check whether close-only rows need a second bounded call and say so in the README.", + "Until it lands, the weekly number (human messages per closed issue) can't be computed, and row 13's gate (a Monday run with zero violations) can't be met, because --no-issues skips the queue issue checks. Run record: agents/sage/work/ledger/2026-10-13_weekly.txt. Needs a queue row and brief; owner Sage to assign.") }, +]; +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);