vikunja: row 62 gate-read probe, cross-project task read works for the sync key (sage)
GETs only, sync key 9. Project list 200, total 30 (31 with archived #15). #18, #20 and #21 have parent 19, none archived. GET /tasks with filter project in 19, 18, 20, 21: 200, 42 tasks across all four. The per-project open reads give 18, 4, 3 and 1. A follow-up row moves a tick to the single read after row 62 is done. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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);
|
||||
Reference in New Issue
Block a user