Skip to content

Commit 7b0268e

Browse files
committed
fix(pm): advisory_read says a GET that never reached the platform did not, not "HTTP 0"
On status 0 (`rest`'s word for a call that threw before any answer) the read row now says the request did not reach the platform, with the row status `not reached`. It no longer prints "HTTP 0" or appends the read-grant remedy, because the platform judged nothing. The exception's message is still printed nowhere, since it can carry the path and so the id. A self-test case pins this, and the run joins the marker and id sweep (19 runs). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J8VVAng9QxNxoEzyWkqbW6
1 parent d2ee86c commit 7b0268e

2 files changed

Lines changed: 27 additions & 4 deletions

File tree

‎scripts/pm/fleet-write/execute.mjs‎

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -243,8 +243,11 @@
243243
*
244244
* ## The read — `advisory_read`, sealed before anything leaves (`advisoryReadAction`)
245245
*
246-
* ONE `GET /repos/{repo}/security-advisories/{ghsa_id}`, nothing written. The
247-
* executor judges `ADVISORY_READ_FENCES` on that answer — a 200 advisory, the
246+
* ONE `GET /repos/{repo}/security-advisories/{ghsa_id}`, nothing written. A
247+
* GET that never reached the platform (the call threw: `rest`'s status 0) is a
248+
* FAILED action with `ADVISORY_READ_UNREACHED` and the row status
249+
* `not reached` — no HTTP status, no grant remedy, ⛔ never the exception's
250+
* message. On an answer, the executor judges `ADVISORY_READ_FENCES` on it — a 200 advisory, the
248251
* one the action names, and a sender the row's `senderGate` admits (the
249252
* default, `collaborator`: the sender on the advisory's own
250253
* `collaborating_users`; `ops.mjs` records the ruling the maintainer owes) —
@@ -404,6 +407,8 @@ import {
404407
ADVISORY_READ_ACCESS_REMEDY,
405408
ADVISORY_READ_FIELDS,
406409
ADVISORY_READ_SEAL_FAILED,
410+
ADVISORY_READ_UNREACHED,
411+
ADVISORY_READ_UNREACHED_STATUS,
407412
advisoryReadAad,
408413
advisoryReadCall,
409414
advisoryReadFenceFailures,
@@ -924,6 +929,9 @@ export function confirmAdvisory(req, answer, pre) {
924929
export async function advisoryReadAction(req, { payload, index, sender, api, t, seal = sealAdvisoryRead, senderGate = OPS.advisory_read.senderGate }) {
925930
const { ghsaId, publicKey } = req.advisoryRead;
926931
const r = await rest(api, req.path, { method: 'GET' }, t);
932+
// Status 0 is `rest`'s word for a call that threw before any answer: the platform judged nothing, so the row names no
933+
// HTTP status and no grant remedy — and ⛔ never the exception's message, which can carry the path and so the id.
934+
if (r.status === 0) return { ok: false, status: ADVISORY_READ_UNREACHED_STATUS, why: ADVISORY_READ_UNREACHED, notes: [] };
927935
const failures = advisoryReadFenceFailures({ ghsaId, answer: r, sender, gate: senderGate });
928936
if (failures.length) {
929937
const refused = !(r.status >= 200 && r.status < 300);
@@ -1452,7 +1460,7 @@ const SELF_TEST_BATTERIES = Object.freeze({
14521460
"the run_rerun_failed: the five fences judged HERE on the run and its pull, read immediately before ONE bodiless POST — the pull's current head by its own GET, never the run's embedded copy, and a run naming no pull refused by name; each refused with the POST not sent and accepted on the measured run; a run, pull, baseline or fence that fails is zero writes; the 201 confirmed only by reading attempt 2 back with its failed jobs queued or in progress, naming their job ids; attempt 2 never seen, a failed job completed, or a job list unread is FAILED; no annotation": 21,
14531461
"the hierarchy link: sub_issue_add and sub_issue_remove read the parent's and the child's own answers, then the child's parent (add) or the parent's whole list (remove), immediately before ONE write whose body is the child's REST id from its own answer — never its number, never replace_parent; a pull, a transferred or unread card, a child under another parent, one the parent does not list, or a read that is neither answer is zero writes; a child already under this parent is a no-op landed on the list; the answer confirmed only when the parent's list reads the child back (or no longer does) inside the window; a refused write, a list that never settles or never reads whole is FAILED; no annotation, and the rows ride a stroke beside other ops": 20,
14541462
"the advisory rows: advisory_report and advisory_create are ONE POST of their body each, landed on a 2xx carrying an id — a draft's reading state draft — that the row and a notice annotation name; advisory_collaborators_add and advisory_state read the advisory first and send ONE PATCH whose body is the executor's — every existing login kept with the missing ones added, an all-present list a no-op, or the one state a legal transition reaches — or nothing at all on a fence: an unread, other or wrong-state advisory or an illegal move; each lands only when its answer shows the result; ⛔ the marker in a report's or a draft's summary, description and packages and in every advisory answer's summary, description and vulnerabilities reaches no log line, row, step summary or annotation": 27,
1455-
"advisory_read: ONE GET after the sender gate and nothing written; the read, advisory and sender fences judged on that answer — collaborator by default, target-write by switch, an unknown gate closed — then the answer sealed to the seat's key and emitted as notice parts the reader parses back, opening byte for byte for that key and read alone; a failed or unsealed seal and an over-cap answer FAILED with nothing emitted; ⛔ the marker in the summary, description and vulnerabilities, and the advisory's id, reach no log line, row, step summary or annotation, and the sealed answer alone carries no marker": 16,
1463+
"advisory_read: ONE GET after the sender gate and nothing written; the read, advisory and sender fences judged on that answer — collaborator by default, target-write by switch, an unknown gate closed — then the answer sealed to the seat's key and emitted as notice parts the reader parses back, opening byte for byte for that key and read alone; a failed or unsealed seal and an over-cap answer FAILED with nothing emitted; ⛔ the marker in the summary, description and vulnerabilities, and the advisory's id, reach no log line, row, step summary or annotation, and the sealed answer alone carries no marker": 17,
14561464
});
14571465
// Raised 17 → 18 with the governed-review follow-up battery, 18 → 19 with the run_rerun_failed battery, 19 → 20 with the
14581466
// hierarchy link battery, 20 → 21 with the advisory rows' battery, 21 → 22 with advisory_read's: one battery, one row.
@@ -2551,6 +2559,10 @@ export async function selfTest() {
25512559
const missing = await readRun([read()], { ...allowed, [A_GET]: { status: 404, json: { message: MARKER } } });
25522560
const elsewhere = await readRun([read()], { ...allowed, [A_GET]: advisory({ ghsa_id: OTHER }) });
25532561
t('⛔ fence read and advisory: a 403 quoting the one message the relay knows plus the read remedy, a 404 whose message is prose (not quoted), and an answer naming another advisory — each FAILED with nothing sealed, no part, and neither id in its row', [[forbidden, missing, elsewhere].map((r) => [r.exit, r.annotations.length, r.rows[0]?.status, [GHSA, OTHER].some((id) => JSON.stringify(r.rows).includes(id))]), forbidden.rows[0]?.result.includes('(the platform: Resource not accessible by integration); the relay token carries repository-advisories READ for this stroke alone'), missing.rows[0]?.result.includes('the platform'), elsewhere.rows[0]?.result.includes('fence `advisory`: the answer names another advisory')], [[[EXIT_ACTION_FAILED, 0, 403, false], [EXIT_ACTION_FAILED, 0, 404, false], [EXIT_ACTION_FAILED, 0, 200, false]], true, false, true]);
2562+
// ⛔ A GET that never reached the platform: the call threw, so `rest` answers status 0 — its message carrying the id and the marker.
2563+
const unreached = await readRun([read()], { ...allowed, [A_GET]: { throws: `fetch failed for ${ADV} holding ${MARKER}` } });
2564+
const unreachedText = [...unreached.logs, ...(unreached.lines ?? []), unreached.summary ?? '', JSON.stringify(unreached.rows)].join('\n');
2565+
t("⛔ a GET that never reached the platform (the call threw) is FAILED saying so: its row names no HTTP status and no grant remedy — no grant was judged — nothing sealed, no part, and the exception's message printed nowhere", [unreached.exit, unreached.seen.map((s) => s.call), unreached.annotations.length, notices(unreached).length, unreached.rows.map((x) => [x.status, x.result]), /HTTP 0\b/u.test(unreachedText), unreachedText.includes(ADVISORY_READ_ACCESS_REMEDY), unreachedText.includes('fetch failed')], [EXIT_ACTION_FAILED, [PERM, A_GET], 0, 0, [[ADVISORY_READ_UNREACHED_STATUS, `FAILED: ${ADVISORY_READ_UNREACHED}`]], false, false, false], unreachedText);
25542566
// ⛔ A failed seal, and one too large.
25552567
const thrown = await readRun([read()], { ...allowed, [A_GET]: advisory() }, { advisoryRead: { seal: () => { throw new Error(`seal blew up holding ${MARKER}`); } } });
25562568
const unsealed = await readRun([read()], { ...allowed, [A_GET]: advisory() }, { advisoryRead: { seal: ({ plaintext }) => plaintext } });
@@ -2568,7 +2580,7 @@ export async function selfTest() {
25682580
// ⛔ The sweep: every run above, every channel this file emits on — no marker, and no advisory id.
25692581
const emitted = swept.map(({ r, paceLines }) => [...(r.logs ?? []), ...(r.lines ?? []), r.summary ?? '', JSON.stringify(r.rows ?? []), ...(r.annotations ?? []).map((n) => relayAdvisoryReadAnnotationMessage(n)), ...paceLines].join('\n'));
25702582
const leaks = emitted.map((text, i) => (text.includes('ADVISORY MARKER') ? i : null)).filter((i) => i !== null);
2571-
t('⛔ THE MARKER TEST: across every run of this battery — landed, long, refused at the sender, read and advisory fences, the switched and unknown gates, a seal that throws, one that does not seal, one too large, refused payloads — the marker served in the summary, the description and the vulnerabilities (and every other field) reaches NO log line, row, step summary, annotation or pace-log line', [swept.length, leaks], [18, []], leaks.map((i) => emitted[i].split('\n').find((l) => l.includes('ADVISORY MARKER'))).join(' | '));
2583+
t('⛔ THE MARKER TEST: across every run of this battery — landed, long, refused at the sender, read and advisory fences, the switched and unknown gates, a GET that never reached the platform, a seal that throws, one that does not seal, one too large, refused payloads — the marker served in the summary, the description and the vulnerabilities (and every other field) reaches NO log line, row, step summary, annotation or pace-log line', [swept.length, leaks], [19, []], leaks.map((i) => emitted[i].split('\n').find((l) => l.includes('ADVISORY MARKER'))).join(' | '));
25722584
const named = emitted.map((text, i) => ([GHSA, OTHER].some((id) => text.includes(id)) ? i : null)).filter((i) => i !== null);
25732585
t("⛔ …and NO advisory id: neither the one asked for nor the one answered appears on any line, row, summary or annotation of any run — the call's placeholder stands where every other row prints its path", named, [], named.map((i) => emitted[i].split('\n').find((l) => l.includes(GHSA) || l.includes(OTHER))).join(' | '));
25742586
t('…positive control: every answer served carried the marker and the id, the request carried the id to the platform — where it belongs — and the generic row printer, given the same answer, WOULD print both', [swept.every(({ answers }) => JSON.stringify(answers).includes('ADVISORY MARKER') && JSON.stringify(answers).includes(GHSA)), landed.seen.some((s) => s.call === A_GET), resultOf({}, served.json).includes('ADVISORY MARKER') && resultOf({}, served.json).includes(GHSA)], [true, true, true]);

‎scripts/pm/fleet-write/ops.mjs‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1375,6 +1375,17 @@ export const ADVISORY_READ_ACCESS_REMEDY =
13751375
/** What a read whose seal failed prints: nothing of the answer, and no part emitted. */
13761376
export const ADVISORY_READ_SEAL_FAILED = 'the seal failed, so nothing of the answer was emitted — ⛔ the text never leaves another way; re-send the read with a fresh key';
13771377

1378+
/**
1379+
* What a read whose GET never reached the platform prints — `rest` answers status 0 when the call threw before any
1380+
* answer — and the status its row carries in place of a number. The platform judged nothing, so there is no HTTP
1381+
* status to print and no grant to remedy (`ADVISORY_READ_ACCESS_REMEDY` is a refusal's, and none came); ⛔ nor the
1382+
* exception's message, which can carry the request's path and so the id.
1383+
*/
1384+
export const ADVISORY_READ_UNREACHED =
1385+
"REFUSED before any fence: the advisory's GET did not reach the platform (the runner's call threw before any answer), so no grant, " +
1386+
'advisory or sender was judged and nothing was sealed or emitted — re-send the read with a fresh key';
1387+
export const ADVISORY_READ_UNREACHED_STATUS = 'not reached';
1388+
13781389
/** What a read too large for the channel prints: the counts, and nothing of the answer. Pure. */
13791390
export function advisoryReadOverCapText({ plaintextBytes, sealedChars }) {
13801391
return (

0 commit comments

Comments
 (0)