diff --git a/agents/sage/work/vikunja-setup/2026-10-11_probe-62.json b/agents/sage/work/vikunja-setup/2026-10-11_probe-62.json new file mode 100644 index 000000000..eeb465978 --- /dev/null +++ b/agents/sage/work/vikunja-setup/2026-10-11_probe-62.json @@ -0,0 +1,76 @@ +{ + "at": "2026-10-11T19:14:20.231Z", + "key": "sync (key 9)", + "reads": [ + { + "path": "/projects?per_page=50&page=1", + "status": 200, + "count": 30, + "total": 30, + "code": null + }, + { + "path": "/tasks?filter=project%20in%2019%2C%2018%2C%2020%2C%2021&sort_by=id&order_by=asc&per_page=50&page=1", + "status": 200, + "count": 42, + "total": 42, + "code": null, + "projects": [ + 18, + 19, + 20, + 21 + ] + }, + { + "path": "/projects/19/tasks?filter=done%20%3D%20false&sort_by=id&order_by=asc&per_page=50&page=1", + "status": 200, + "count": 18, + "total": 18, + "code": null + }, + { + "path": "/projects/18/tasks?filter=done%20%3D%20false&sort_by=id&order_by=asc&per_page=50&page=1", + "status": 200, + "count": 4, + "total": 4, + "code": null + }, + { + "path": "/projects/20/tasks?filter=done%20%3D%20false&sort_by=id&order_by=asc&per_page=50&page=1", + "status": 200, + "count": 3, + "total": 3, + "code": null + }, + { + "path": "/projects/21/tasks?filter=done%20%3D%20false&sort_by=id&order_by=asc&per_page=50&page=1", + "status": 200, + "count": 1, + "total": 1, + "code": null + } + ], + "tree": [ + { + "id": 19, + "parent": 33, + "archived": false + }, + { + "id": 18, + "parent": 19, + "archived": false + }, + { + "id": 20, + "parent": 19, + "archived": false + }, + { + "id": 21, + "parent": 19, + "archived": false + } + ] +} diff --git a/agents/sage/work/vikunja-setup/probe-62.mjs b/agents/sage/work/vikunja-setup/probe-62.mjs new file mode 100644 index 000000000..7e3b0bd67 --- /dev/null +++ b/agents/sage/work/vikunja-setup/probe-62.mjs @@ -0,0 +1,33 @@ +// Sage, 2026-10-11: read-only, row 62 probes.md "Gate read". With the sync key: the project list +// (status, page count, total, and parent and archived for #19 and its children 18, 20, 21), one +// cross-project task read, and the four per-project open reads for comparison. GETs only; records +// no titles; refuses to overwrite OUT; prints no secret. +// Usage: node probe-62.mjs https://tasks.woltje.com SYNC_TOKEN_FILE OUT_JSON +import { readFileSync, writeFileSync, existsSync } from "node:fs"; +const [ORIGIN, KEY, 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 BASE = ORIGIN + "/api/v2", TREE = [19, 18, 20, 21]; +const key = readFileSync(KEY, "utf8").trim(); +const get = async (path) => { + const r = await fetch(BASE + path, { headers: { Authorization: `Bearer ${key}` }, redirect: "error" }); + const t = await r.text(); let json = null; try { json = JSON.parse(t); } catch {} + const items = json?.items ?? (Array.isArray(json) ? json : null); + return { path, status: r.status, count: items ? items.length : null, total: json?.total ?? null, code: r.status === 200 ? null : json?.code ?? null, items }; +}; +const strip = ({ items, ...rest }) => rest; +const rec = { at: new Date().toISOString(), key: "sync (key 9)", reads: [] }; +const list = await get("/projects?per_page=50&page=1"); +rec.reads.push(strip(list)); +rec.tree = TREE.map((id) => { + const p = (list.items ?? []).find((x) => x.id === id); + return p ? { id, parent: p.parent_project_id, archived: p.is_archived } : { id, listed: false }; +}); +const filter = encodeURIComponent(`project in ${TREE.join(", ")}`); +const cross = await get(`/tasks?filter=${filter}&sort_by=id&order_by=asc&per_page=50&page=1`); +rec.reads.push({ ...strip(cross), projects: cross.items ? [...new Set(cross.items.map((t) => t.project_id))].sort((a, b) => a - b) : null }); +for (const id of TREE) rec.reads.push(strip(await get(`/projects/${id}/tasks?filter=${encodeURIComponent("done = false")}&sort_by=id&order_by=asc&per_page=50&page=1`))); +const out = JSON.stringify(rec, null, 1) + "\n"; +if (out.includes(key)) throw new Error("secret in output; not written"); +writeFileSync(OUT, out, { flag: "wx" }); +process.stdout.write(out);