fix(07-03): fold the follow-up fixes described in 07-03-SUMMARY

- asset-header probe reads HTTP status BEFORE interpreting headers — a
  non-2xx is deployment drift (the notRun exit-2 class), never a cache
  violation; the violations.length count bug fixed
- XHTML namespace URI joins the SVG spec-identifier adjudication
- probeUnavailable carries the reason only (the CLI prints NOT RUN)
- CP2/AU1 suite vectors updated to the corrected contracts

Residue note: these edits were on the tree during 07-03/07-04 execution
but escaped those plans' commits (07-04-SUMMARY residue caution).
This commit is contained in:
araharah-executor
2026-10-25 05:15:38 +08:00
parent 4ecd4325f8
commit 7a1206d5c8
3 changed files with 39 additions and 20 deletions
+25 -10
View File
@@ -37,9 +37,10 @@ const WRANGLER_JSONC = path.join(REPO_ROOT, "bot", "wrangler.jsonc");
* family is REPORTED, never assumed absent (07-RESEARCH A5). */
export const KEY_FAMILIES = ["session:", "chat:", "loc:", "guidance:"];
/** A missing environment is the exit-2 class: pass:false + notRun:true. */
/** A missing environment is the exit-2 class: pass:false + notRun:true. The
* evidence carries the REASON only — the CLI prints the NOT RUN status. */
export function probeUnavailable(reason) {
return { id: "probe", pass: false, notRun: true, evidence: `NOT RUN — ${reason}` };
return { id: "probe", pass: false, notRun: true, evidence: reason };
}
function notRun(id, reason) {
@@ -178,6 +179,12 @@ function categoryOf(err) {
* `cf-cache-status` headers VERBATIM. CF-Cache-Status is probabilistic per
* Cloudflare's own footnote — the evidence prints it, it does not interpret
* it as a guarantee (07-RESEARCH Pitfall 10).
*
* Honesty laws: every leg's HTTP status is read BEFORE any header is
* interpreted — a non-2xx is deployment/environment drift (the notRun
* class), never a cache-header violation. The immutable `/assets/*` policy
* itself is `web/public/_headers`' claim (07-04) — this probe quotes, it
* does not assert the policy.
*/
export function runAssetHeaderProbe(opts = {}) {
const id = "asset-headers";
@@ -193,7 +200,7 @@ export function runAssetHeaderProbe(opts = {}) {
}
const lines = [];
let violations = 0;
const observed = [];
const targets = [["HTML entry /", `${baseUrl}/`]];
if (assetPath !== null) targets.push([`hashed asset ${assetPath}`, `${baseUrl}${assetPath}`]);
try {
@@ -203,23 +210,31 @@ export function runAssetHeaderProbe(opts = {}) {
timeout: 30000,
stdio: ["ignore", "pipe", "pipe"],
});
const status = Number(out.match(/^HTTP\/[\d.]+\s+(\d+)/m)?.[1] ?? 0);
const cacheControl = out.match(/^cache-control:.*$/im)?.[0]?.trim() ?? null;
const cfStatus = out.match(/^cf-cache-status:.*$/im)?.[0]?.trim() ?? null;
if (cacheControl === null) {
violations += 1;
lines.push(`${label}: no cache-control header observed`);
} else {
lines.push(`${label}: ${cacheControl}${cfStatus !== null ? ` | ${cfStatus}` : " | cf-cache-status: not reported by this response"}`);
observed.push(
`${label}: HTTP ${status || "unknown"}${cacheControl !== null ? ` | ${cacheControl}` : " | no cache-control"}${cfStatus !== null ? ` | ${cfStatus}` : ""}`,
);
if (!Number.isInteger(status) || status < 200 || status >= 400) {
return notRun(
id,
`deployment drift — ${label} answered HTTP ${status || "unknown"} ` +
`(the deployed origin does not match web/dist); observed: ${observed.join(" || ")}`,
);
}
if (cacheControl === null) lines.push(`${label}: NO cache-control header observed`);
else lines.push(`${label}: ${cacheControl} | ${cfStatus ?? "cf-cache-status not reported"}`);
}
} catch (err) {
return notRun(id, `origin unreachable (${categoryOf(err)})`);
}
const violations = lines.filter((l) => l.includes("NO cache-control")).length;
const pass = violations === 0;
const evidence = pass
? `${targets.length} response header(s), VERBATIM (cf-cache-status is probabilistic — reported, not interpreted as a guarantee): ${lines.join(" || ")}`
: `${violations.length} violation(s): ${lines.join(" || ")}`;
? `${targets.length} response header(s), VERBATIM (cf-cache-status is probabilistic — quoted, never interpreted as a guarantee): ${lines.join(" || ")}`
: `${violations} violation(s): ${lines.join(" || ")}`;
return { id, pass, evidence, violations };
}