sage: row 187 landing, P19 407-410 filed and P19 385 comment 357, #1685 closed (comment 29425), SESSIONS line

This commit is contained in:
2026-10-20 05:24:32 -05:00
parent 652d6f3a85
commit a843ae284e
3 changed files with 169 additions and 0 deletions
@@ -0,0 +1,114 @@
// Sage, 2026-10-20: row 187 (#1685, Part B step A0) landing. Files Filbert's four row 187 follow-ups as P19
// tasks, one each, owner Sage, no due date (the mos-0955 form), then comments on P19 385 with the landing and the
// task numbers (mos-0882: every landing gets a Vikunja comment). Sage's own key (bot user 13); the sync key reads
// back. No labels, no assignees, no due date, no bucket moves. Refuses if a title exists. Rollback is marking a
// new task done; deletion is Jason's or ops-01's. Prints no secret.
// Usage: node file-r187-landed.mjs https://tasks.woltje.com SAGE_TOKEN_FILE SYNC_TOKEN_FILE OUT_JSON LANDING_SHA
import { readFileSync, writeFileSync, existsSync } from "node:fs";
const [ORIGIN, KEY, SYNC, OUT, LANDING] = 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");
if (!/^[0-9a-f]{9}$/.test(LANDING ?? "")) throw new Error("LANDING_SHA: 9 hex characters");
const PROJECT = 19;
const SIG = "Created by Sage (T3 1ef1e4f8) with Sage's own key (bot user 13).";
const PSIG = "Posted by Sage (T3 1ef1e4f8) with Sage's own key (bot user 13).";
const esc = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
const ps = (sig, xs) => xs.map((x) => `<p>${esc(x)}</p>`).join("") + `<p>${esc(sig)}</p>`;
const SRC = `Filbert's row 187 r1 approve (Gitea #1685), landing ${LANDING}.`;
const TAIL = "Owner Sage. No queue row until scheduled (the mos-0955 form). Not live-affecting.";
const TASKS = [
{
title: "stack-release.sh: the proof_for_target comment says a new proof pair goes last (row 187)",
description: ps(SIG, [
`${SRC} The comment above proof_for_target in scripts/stack-release.sh says a new pair "replaces the pointer's and goes last". The code puts it after the preview flags and before --send and --tester-port, the order webui-service.sh render writes and flags_drift compares (row 187 BUILD.md deviation 2). Found by Sage, non-blocking per Filbert.`,
"The fix is one comment line.",
TAIL,
]),
},
{
title: "docs/TOOLS.md: the row 178 paragraph says stack-release.sh carries the proof pair unchanged (row 187)",
description: ps(SIG, [
`${SRC} docs/TOOLS.md:904, in the row 178 paragraph, still says stack-release.sh "preserves both flags unchanged on re-pin and rollback". Since row 187, activate sets or drops the pair, and a rollback to a copy without proof support drops it. The row 187 paragraph under "Pinned stack update" is right. Doc-only; no test reads TOOLS.md.`,
"Can share a row with the comment fix.",
TAIL,
]),
},
{
title: "stack-release test gap: no test kills a carried proof pair skipping proof_checks (row 187 M8)",
description: ps(SIG, [
`${SRC} Filbert's mutant M8 makes a carried pair (the pointer's pair, no pair flags given) skip proof_checks. It survived T53 12/12. BUILD.md says the checks run on a carried pair, so a file moved or chmod-ed since the pair went in refuses before the stop, but no T53 case changes the file between the activate that set the pair and the run that carries it.`,
"The fix is a T53 case that sets a pair, chmods the file to 644 or moves it, then plans and runs an activate or rollback that carries it and expects proof-file-invalid with no stop.",
TAIL,
]),
},
{
title: "stack-release test gap: no test kills dropping the repository case from the proof file check (row 187 M9)",
description: ps(SIG, [
`${SRC} Filbert's mutant M9 drops the repository case from the realpath check in proof_file_problem. It survived T53 12/12. T53 refuses a proof file inside the copies but has no case for one inside the repository, which webui-service.sh render refuses after the stop.`,
"The fix is a T53 case with a mode-600 proof file under the test's repository root that expects proof-file-invalid with no stop.",
TAIL,
]),
},
];
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, landing: LANDING, created: [], comment385: null, 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();
for (const T of TASKS) {
const clash = before.filter((t) => t.title === T.title);
if (clash.length) throw new Error(`exists: #${clash[0].id}; refusing`);
}
const t385 = await api("GET", `/api/v2/tasks/385`, undefined, sync);
if (t385.status !== 200 || t385.json?.project_id !== PROJECT || !/X-authentik-username from 127\.0\.0\.1/.test(t385.json?.title ?? "")) throw new Error(`task 385: ${t385.status}, not the P19 385 task`);
writeFileSync(OUT, "", { flag: "wx" });
for (const T of TASKS) {
const r = await api("PUT", `/api/v1/projects/${PROJECT}/tasks`, { title: T.title, description: T.description }, key);
const c = { title: T.title, status: r.status, id: r.json?.id ?? null, index: r.json?.index ?? null };
rec.created.push(c);
if (r.status >= 300 || !Number.isSafeInteger(r.json?.id)) { rec.problems.push(`create: ${r.status} ${r.json?.message ?? ""}`); save(); continue; }
c.inverse = `mark done: POST /api/v1/tasks/${r.json.id} done true (sage key); delete is Jason's or ops-01's`;
const back = await api("GET", `/api/v2/tasks/${r.json.id}`, undefined, sync);
Object.assign(c, { readBack: back.status, done: back.json?.done, due: back.json?.due_date ?? null });
if (back.status !== 200 || back.json?.title !== T.title || back.json?.project_id !== PROJECT || back.json?.done) rec.problems.push(`create ${r.json.id} read-back differs`);
save();
}
const ids = rec.created.map((c) => c.id).filter(Number.isSafeInteger);
if (ids.length === TASKS.length) {
const body = ps(PSIG, [
`Part B step A0 has landed: row 187 (Gitea #1685) is ${LANDING} on refactor. Filbert approved round 1, and Sage's gate passed (the cli suite and every scripts/test-*.sh). activate now takes --proof-header NAME --proof-file PATH, which writes the pair into the pointer and the Console unit, and --no-proof, which drops it. An activate may name the pointer's copy only to set a pair or to drop one with --no-proof. That makes B3 and the corrected B-R1 (activate <live copy> mosaic-stack --pm --no-proof) possible. The script checks the proof file with stat and realpath, never opens it, and refuses a bad header or file before anything stops.`,
"The tool is on refactor only. The live pointer is still 490e2771d3d0. Nothing turned the proof on. That stays Part B, which waits for Jason (J1-J6), and then a re-pin window that Mos checks at A2.",
`Filbert's four non-blocking follow-ups are P19 tasks, owner Sage, no due date: #${ids[0]} (stale proof_for_target comment), #${ids[1]} (stale TOOLS.md row 178 paragraph), #${ids[2]} (M8, no test for a carried pair skipping the checks) and #${ids[3]} (M9, no test for a proof file inside the repository).`,
]);
const c = await api("PUT", `/api/v1/tasks/385/comments`, { comment: body }, key);
rec.comment385 = { status: c.status, id: c.json?.id ?? null };
if (c.status >= 300 || !Number.isSafeInteger(c.json?.id)) rec.problems.push(`comment 385: ${c.status} ${c.json?.message ?? ""}`);
else {
const back = await api("GET", `/api/v2/tasks/385/comments?per_page=50`, undefined, sync);
const found = list(back.json).find((x) => x.id === c.json.id);
Object.assign(rec.comment385, { readBack: back.status, found: Boolean(found) });
if (back.status !== 200 || !found) rec.problems.push(`comment 385 read-back: ${back.status}`);
}
}
save();
process.stdout.write(JSON.stringify(rec, null, 1) + "\n");
if (rec.problems.length) process.exit(2);
@@ -0,0 +1,54 @@
{
"at": "2026-10-20T10:22:54.778Z",
"project": 19,
"landing": "9a71e4e80",
"created": [
{
"title": "stack-release.sh: the proof_for_target comment says a new proof pair goes last (row 187)",
"status": 201,
"id": 407,
"index": 240,
"inverse": "mark done: POST /api/v1/tasks/407 done true (sage key); delete is Jason's or ops-01's",
"readBack": 200,
"done": false,
"due": "0001-01-01T00:00:00Z"
},
{
"title": "docs/TOOLS.md: the row 178 paragraph says stack-release.sh carries the proof pair unchanged (row 187)",
"status": 201,
"id": 408,
"index": 241,
"inverse": "mark done: POST /api/v1/tasks/408 done true (sage key); delete is Jason's or ops-01's",
"readBack": 200,
"done": false,
"due": "0001-01-01T00:00:00Z"
},
{
"title": "stack-release test gap: no test kills a carried proof pair skipping proof_checks (row 187 M8)",
"status": 201,
"id": 409,
"index": 242,
"inverse": "mark done: POST /api/v1/tasks/409 done true (sage key); delete is Jason's or ops-01's",
"readBack": 200,
"done": false,
"due": "0001-01-01T00:00:00Z"
},
{
"title": "stack-release test gap: no test kills dropping the repository case from the proof file check (row 187 M9)",
"status": 201,
"id": 410,
"index": 243,
"inverse": "mark done: POST /api/v1/tasks/410 done true (sage key); delete is Jason's or ops-01's",
"readBack": 200,
"done": false,
"due": "0001-01-01T00:00:00Z"
}
],
"comment385": {
"status": 201,
"id": 357,
"readBack": 200,
"found": true
},
"problems": []
}
+1
View File
@@ -1341,3 +1341,4 @@ are never rewritten or removed; corrections are new entries.
2026-10-20T09:13:21Z | Darkwing (Opus 5.5, 7525c8e4) | row 186 (#1684, P19 404): the activate plan's backup line says what the run will do, Filbert's N1/N2 on row 185 | landed: 269134d8d (Sage, pushed with 16a936c79, gate node 2681/0 scripts 422/0); candidate 449e91219333 matches the landing; stack-release 132/132, base control red, brief mutants m1-m3 killed; Filbert approve 29419; follow-ups on P19 404 comment 354 (M6 no-store order test, unclean-store form)
2026-10-20T09:13:15Z | Sage (Opus 5.5, 1ef1e4f8) | row 186 landing gate and landing (#1684, P19 404) | landed 269134d8d (author Darkwing), patch-id c97b9f6e25cd = r1 candidate 449e9121; gate b367f5730+patch 09:00:20Z-09:08:59Z node 2681/0, scripts 422/0, network removed; queue 2198-2200 in 16a936c79, pushed with Filbert's 3c5da3a91; #1684 closed (29421); P19 404 comment 354 with two follow-ups (M6 gap, store-unclean reading); row 187 handed to Darkwing (sage-1263)
2026-10-20T10:13:43Z | Filbert (Opus 5.5, 048f3c63) | row 187 r1 review (#1685, manifest 52a5ecc1d143) | approve, comment 29424, rev 2204; stack-release 144/144 on my slot run; M8 and M9 survived (N1, N2 test gaps); N3 :689 comment and N4 TOOLS.md:904 follow-ups per sage-1272
2026-10-20T10:24:26Z | Sage (Opus 5.5, 1ef1e4f8) | row 187 landing gate and landing (#1685, Part B A0) | landed 9a71e4e80 (author Darkwing), patch-id 6b57c47bc661 = r1 candidate 52a5ecc1d143; gate passed 10:21:55Z (cli 446/446, every package and scripts/test-*.sh); queue 652d6f3a8 revs 2201-2206; #1685 comment 29425, closed; P19 407-410 filed, P19 385 comment 357