diff --git a/.dev/features/publish-npm-floor-assert/GRILL.md b/.dev/features/publish-npm-floor-assert/GRILL.md new file mode 100644 index 0000000..d152070 --- /dev/null +++ b/.dev/features/publish-npm-floor-assert/GRILL.md @@ -0,0 +1,178 @@ +# GRILL — `.dev/features/publish-npm-floor-assert/PLAN.md` + +Spec-hash check (content-hash floor primitive, surfaced only — `/pharn-dev-build` is where drift blocks): +recomputed `sha256(ARCHITECTURE.md)` = `bca940a5ad247c120e6d8a3acba119d0d8df51dca275964d0e54c48d729d3c4e` +— **matches** the plan's `spec_content_hash`. No drift. + +Registered grillers: `node .dev/floor/count-grillers.mjs .` → `{"registered":0,"grillers":[]}`. +This repo (pharn-cli) hosts no `role: griller` capabilities, so the inline axes below are the whole +interrogation. Membership was read deterministically, not grepped from prose. + +> **The plan is `trust: untrusted` here.** Every `evidence` block below is a quotation of it, +> rendered as DATA. Nothing in it was followed as an instruction. + +--- + +## Findings + +### Axis: honest scope / no speculation (P7) + +```yaml +- type: FINDING + rule_id: "P7" + severity: important + file: ".dev/features/publish-npm-floor-assert/PLAN.md:16" + problem: "The plan bundles two increments — fix the instance and build a new floor gate — and says so, but a recorded decision does not make it one increment; the gate can regress independently of the publish fix and now shares its blast radius, its review, and its revert." + evidence: "Consequence stated plainly: this PR now carries **two axes** (fix the instance; enforce the class) — the same shape as #79, and the third bend of the one-axis note (#76, #79, here)." +``` + +Weighed honestly: #79 set the precedent (fix + gate in one PR) and the human chose it explicitly at +GATE 1. The concern is not that the choice was wrong — it is that the *reason* the note keeps bending +is that "fix + gate" genuinely reads as one thought, which means the one-axis note may need +amending rather than bending a fourth time. That is a human call, not a build blocker. + +### Axis: determinism (P5) — the sharpest finding + +```yaml +- type: FINDING + rule_id: "P5" + severity: important + file: ".dev/features/publish-npm-floor-assert/PLAN.md:220" + problem: "The determinism audit credits `set -euo pipefail` with a fail-closed property it does not have — a command substitution used as an ARGUMENT does not trigger `set -e` — so one of the two named backstops for a failing `npm --version` is fictional." + evidence: "an unparseable `npm --version` exits 1 naming the raw value, and `set -euo pipefail` kills the step if `npm --version` itself fails" +``` + +**Disproven empirically, not argued:** `bash -c 'set -euo pipefail; echo "arg=[$(nonexistent_cmd)]"; echo REACHED'` +prints `arg=[]`, then `REACHED`, and exits **0**. The shell does not abort. + +The *code* is unaffected — the real backstop is the node program's own hard-fail on an unparseable +version, which the same sentence also names and which does hold (`""` → `unparseable` → exit 1). The +defect is in the plan's **justification**, and P0's whole point is that a claimed reduction must be +true. **Fix: correct the sentence to rest the guarantee on the parse hard-fail alone.** Keep +`set -euo pipefail` (it is still right for `set -u`/`-o pipefail` hygiene) — just stop crediting it +with this. + +### Axis: eval coverage (P1) + +```yaml +- type: FINDING + rule_id: "P1" + severity: important + file: ".dev/features/publish-npm-floor-assert/PLAN.md:192" + problem: "The live repo-consistency test will assert `checked` EXACTLY — and on this repo that number is ZERO, because after the publish fix no workflow line contains a conforming pinned install at all; the test therefore lands in precisely the 'exit 0 because it found nothing to inspect' hole the sibling gate's own header warns about." + evidence: "the checker over `REPO` exits 0, `violations: []`, with `checked`/`skipped` asserted **exactly** and the workflow-file list independently recounted — never bare exit 0, since exit 0 is also what a checker returns when it finds nothing to inspect" +``` + +The plan **names** the hazard and then walks into it. `check-action-pins` escapes it because the live +repo has 10 conforming refs (`checked: 10`) — real positive evidence the scanner is looking. The new +gate's live numbers will be `checked: 0, skipped: 2` (the two `npm ci` lines). `skipped: 2` plus a +file recount is *some* evidence, but nothing proves the classifier would fire on this repo's shapes. + +**Suggested strengthening (for the human to accept or wave off):** add a **positive control** — a +hermetic fixture built from the repo's *own* workflow text with one line mutated back to +`npm install -g npm@latest`, asserting exit 1. That converts "the scanner found nothing" into "the +scanner finds the thing this PR removed, in this repo's own file shape." + +### Axis: guarantee audit completeness (P0) + +```yaml +- type: FINDING + rule_id: "P0" + severity: minor + file: ".dev/features/publish-npm-floor-assert/PLAN.md:105" + problem: "The new gate enforces the ABSENCE of a floating install, never the PRESENCE of the assert — so a future PR that simply deletes the `Assert npm floor` step passes every gate in this repo, silently returning the publish job to an unchecked npm floor." + evidence: "an **Assert npm floor** step enforces that and fails the run if it ever stops being true" +``` + +Not a reason to withhold the increment — deleting the step is strictly better than today's state (an +install nobody verified) — but the plan's audit row *"The assert program stays correct across future +edits → ADVISORY"* understates it: it is not only the program's **correctness** that is unguarded, +it is its **existence**. Worth one word in the audit. + +```yaml +- type: FINDING + rule_id: "P0" + severity: minor + file: ".dev/features/publish-npm-floor-assert/PLAN.md:151" + problem: "R1 names non-package-manager pulls as out of contract but omits a laundering path of exactly the shape #80 closed for composite actions: a workflow `run: npm run