From b955bd4931c10215bce7703d2c444c6a5acd2748 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Galarowicz?= Date: Wed, 12 Aug 2026 16:03:30 +0200 Subject: [PATCH 1/2] fix(docs): document Mode-B ## Files exclusion-cue caveat in /pharn-plan A bare narrative line under ## Files can truncate writes-scope; name the trap and the three safe patterns, and bump SKILLS_VERSION to 2.5.3. Co-authored-by: Cursor --- .claude/commands/pharn-plan.md | 15 +++- .dev/features/f13-modeb-cue/GRILL.md | 82 +++++++++++++++++++ .dev/features/f13-modeb-cue/PLAN.md | 51 ++++++++++++ .dev/features/f13-modeb-cue/REGRESSION.md | 53 ++++++++++++ .dev/features/f13-modeb-cue/REVIEW.md | 75 +++++++++++++++++ .dev/features/f13-modeb-cue/SHIP.md | 52 ++++++++++++ .dev/features/f13-modeb-cue/VERIFY.md | 26 ++++++ .../f13-modeb-cue/regression-report.json | 28 +++++++ .../features/f13-modeb-cue/verify-report.json | 14 ++++ CHANGELOG.md | 2 + README.md | 2 +- SKILLS_VERSION | 2 +- 12 files changed, 396 insertions(+), 6 deletions(-) create mode 100644 .dev/features/f13-modeb-cue/GRILL.md create mode 100644 .dev/features/f13-modeb-cue/PLAN.md create mode 100644 .dev/features/f13-modeb-cue/REGRESSION.md create mode 100644 .dev/features/f13-modeb-cue/REVIEW.md create mode 100644 .dev/features/f13-modeb-cue/SHIP.md create mode 100644 .dev/features/f13-modeb-cue/VERIFY.md create mode 100644 .dev/features/f13-modeb-cue/regression-report.json create mode 100644 .dev/features/f13-modeb-cue/verify-report.json diff --git a/.claude/commands/pharn-plan.md b/.claude/commands/pharn-plan.md index fc72fc2..c0d0be9 100644 --- a/.claude/commands/pharn-plan.md +++ b/.claude/commands/pharn-plan.md @@ -232,10 +232,17 @@ applied_lessons: none | [L1, L2] # MANDATORY — floor-checked (Step 4b); `none` > **leading token is a back-tick path** — ``- `path/to/file` — ``; (3) to **exclude** a > path, put it under the `### Explicitly not touched` **subsection** (the setter stops at that > heading) — **never** inline as ``- `path` — not touched`` (an inline-marked item still enters -> scope). Keep an unfilled placeholder in **angle-brackets** (`` `` ``) so an un-filled -> `## Files` **fails closed** at the setter — a bare word like `` `path` `` would wrongly parse as a -> real scope path. The `## Steps` above is **advisory prose**; only `## Files` back-tick paths become -> the build's scope, and `/pharn-build` writes nothing outside them (fix #7). +> scope). **Caveat:** a bare, non-blockquote prose line under `## Files` that reads like an exclusion +> (wording such as _not touch/writ/modif/edit/chang_, _explicitly excluded_, _out of scope_, _off +> limits_) is treated by the setter as a head-less exclusion intro and **truncates the authorized list +> right there** — every path after it silently falls out of scope. Keep narrative in a **blockquote** +> (`> …`) or as a **path-item description** (``- `path` — note``), and use the `### Explicitly not +touched` heading (rule 3) for a real exclusion. Keep an unfilled placeholder in **angle-brackets** +> (`` `` ``) so an un-filled `## Files` **fails closed** at the setter — a bare word like +> `` `path` `` would wrongly parse as a real scope path. The `## Steps` above is **advisory prose**, +> but a non-path line under `## Files` is not harmless (see the caveat above) — only the `## Files` +> back-tick paths **before any such truncation** become the build's scope, and `/pharn-build` writes +> nothing outside them (fix #7). ## Step 4b — Check the lessons declaration (FLOOR) diff --git a/.dev/features/f13-modeb-cue/GRILL.md b/.dev/features/f13-modeb-cue/GRILL.md new file mode 100644 index 0000000..5dbe354 --- /dev/null +++ b/.dev/features/f13-modeb-cue/GRILL.md @@ -0,0 +1,82 @@ +# GRILL — f13-modeb-cue + +Plan under interrogation: `.dev/features/f13-modeb-cue/PLAN.md` (option A, approved by the human at +GATE 1). Spec-hash check: **MATCH** — `sha256(pharn/ARCHITECTURE.md)` recomputed live as +`8f5ec002e3b18cbfd2f094b08a3671f7ed42a05a3fbaf01a11bbbd28da30fb52`, identical to the plan's +`spec_content_hash`. No drift; `/pharn-dev-build`'s floor-gate (fix #4) will pass this check too. + +Griller discovery (`node pharn/floor/count-grillers.mjs .`): 13 `role: griller` capabilities +registered (a11y, architecture, comprehension, coupling, documentation, error-handling, i18n, +migrations, observability, performance, privacy, security, testability). This increment adds no +application code (no SSR/SPA/backend surface), so the `applies: ["ssr","spa"]` (a11y, i18n) and +`applies: ["backend","ssr"]` (migrations) grillers have no target to interrogate — their axis does not +exist for a `.claude/commands/*.md` prose edit, and running them would manufacture findings against +nothing. The `applies: ["universal"]` grillers were interrogated against the plan below, keyed by the +principle each `enforces`. + +## Findings + +```yaml +- type: FINDING + rule_id: "P5" + severity: minor + file: ".dev/features/f13-modeb-cue/PLAN.md (build-prompt provenance, not a PLAN.md line)" + problem: "The build prompt's 'Write procedure' step for option (A) says to scope with `set-writes-scope.cjs --from-frontmatter --allow-claude-dir` for `.claude/commands/pharn-plan.md`, but `--allow-claude-dir` is only required for CONTROL_SURFACE entries (`.claude/settings.json`, `.claude/settings.local.json`, and the three hook scripts) — `.claude/commands/pharn-plan.md` is not one of them, and the setter's header states command files are deliberately exempt from the opt-in flag." + evidence: "'set a writes-scope with set-writes-scope.cjs --from-frontmatter --allow-claude-dir for .claude/commands/pharn-plan.md, CHANGELOG.md, SKILLS_VERSION'" +``` + +Non-blocking: harmless if followed literally (the flag is simply a no-op when no CONTROL_SURFACE path +is in scope), and moot under `/pharn-dev-ship`/`/pharn-dev-build` regardless — `/pharn-dev-build`'s own +Step 0 scopes from the approved `PLAN.md`'s `## Files` via `--from-plan`, which already authorizes +exactly `.claude/commands/pharn-plan.md`, `CHANGELOG.md`, and `SKILLS_VERSION` with no flag needed. +Flagged only so the human isn't surprised if they later run the manual procedure by hand outside +`/pharn-dev-ship`. + +No other findings. Specifically checked and clean: + +- **P0 (guarantee-audit completeness):** every claim in the plan's Guarantee audit section either + names a floor primitive (`check-version-badge.mjs`'s narrowed scope, `set-writes-scope.test.cjs` + unchanged-and-green) or is explicitly labeled `advisory` (the caveat's prose guidance). Nothing reads + as a guarantee without a reduction. +- **P1 (eval coverage):** correctly scoped as not applicable — `/pharn-plan.md` is a command doc, not a + `role:`-bearing Capability (`pharn/ARCHITECTURE.md §3.1`), so P1's per-Capability eval requirement does + not attach to it. No new `rule_id` is introduced by this increment for a testability griller to check + binding on. +- **P2 (trust propagation):** correctly marked not applicable — no untrusted artifact is ingested by + the increment's own work (the build prompt's task description contains no instruction-looking content + beyond ordinary prose). +- **P3 (one axis of change / no sibling imports):** the three touched files (`pharn-plan.md`, + `CHANGELOG.md`, `SKILLS_VERSION`) are one coherent change-reason (documenting a caveat + its mandatory + version/changelog bookkeeping under this repo's own SKILLS_VERSION discipline) — not two unrelated + reasons bundled. No sibling-module reference is introduced; a command doc is not part of the + `pharn-contracts` layer tree. +- **P5 (determinism):** both prior open questions (option A vs B; the 2.5.1-vs-2.5.2 version-base + discrepancy) were resolved by asking the human at GATE 1, not guessed. The plan's own remaining + branches (which file gets which edit) are fixed, not classified. +- **P7 (honest scope / no speculation):** the plan explicitly excludes touching + `set-writes-scope.cjs` (option B) and `pharn-dev-plan.md` (no parity edit requested by the build + prompt) under `### Explicitly not touched`, with reasons — the smallest coherent increment, not two + bundled. The lesson L18 citation is genuinely on-point (same code path, adjacent failure mode), not a + padded citation. +- **Architecture/coupling axis:** no new module, no layer crossing; a `.claude/commands/*.md` edit is + outside `pharn/ARCHITECTURE.md §4`'s capability layer tree entirely, so there is nothing to couple. +- **Security/privacy axis:** no secret, credential, or PII-shaped content is added or handled; the + caveat text is pure process documentation. +- **Documentation/comprehension axis:** the plan's stated placement (after rule 3, before the closing + `## Steps` sentence, in the existing "Three rules keep it parseable" blockquote) keeps the new caveat + co-located with the rules it qualifies, and the plan separately calls out correcting the adjacent + "only back-tick paths become the build's scope" sentence so the new caveat doesn't read as + contradicting it — a comprehension-griller-shaped concern the plan already anticipated and handled. + +## Summary + +The plan is narrowly scoped, cites a genuinely relevant lesson (L18) with a substantive rather than +decorative explanation of the connection, and its guarantee audit is honest about what is advisory +(the prose caveat) versus what reduces to floor (the unchanged hook, checked by the unmodified test +suite). The single finding above is a minor discrepancy in the build prompt's own suggested +write-procedure command, not in the plan; it does not affect what `/pharn-dev-build` will actually do +under `/pharn-dev-ship`, where scope is set programmatically from the plan's `## Files`, not by hand. + +**ADVISORY VERDICT: 1 concern raised (0 blocking-severity, 1 minor) — for the human to weigh before +`/pharn-dev-build`. This is advisory input, not a gate; `/pharn-dev-build`'s own floor-gates (spec-hash +re-check, fix #4; writes-scope, fix #7) are what actually enforce anything here.** diff --git a/.dev/features/f13-modeb-cue/PLAN.md b/.dev/features/f13-modeb-cue/PLAN.md new file mode 100644 index 0000000..eec277b --- /dev/null +++ b/.dev/features/f13-modeb-cue/PLAN.md @@ -0,0 +1,51 @@ +# PLAN — F13: document the Mode-B `## Files` exclusion-cue caveat + +- spec_content_hash: 8f5ec002e3b18cbfd2f094b08a3671f7ed42a05a3fbaf01a11bbbd28da30fb52 +- applied_lessons: [L18] +- increment: Document, in `/pharn-plan.md`'s `## Files` contract blockquote, that a bare non-blockquote prose line under `## Files` containing exclusion-style wording (`not touch/writ/modif/edit/chang`, `explicitly excluded`, `out of scope`, `off limits`) truncates the authorized list at that line — and tell the author to use a blockquote, a path-item description, or the `### Explicitly not touched` heading instead. No code change to `set-writes-scope.cjs`. +- layer(s): `.claude/` product command surface (not a `pharn-contracts` layer — a command doc, per `pharn/ARCHITECTURE.md §4`'s command/hook split) +- constitution_refs: [P0, P5, P6, P7] + +## Applied lessons + +- L18 — L18 established that Boundary 1 (a real markdown heading) is the structural, wording-independent mechanism for a PLAN's exclusion block, and that the free-text Boundary-2 cue's vocabulary is deliberately narrow (so a differently-worded real exclusion can miss it and fail OPEN). That is the exact asymmetry this increment weighs: narrowing the cue to stop today's false-positive (an innocent sentence, fail-CLOSED) risks recreating L18's fail-OPEN failure mode for a real exclusion phrased outside the narrowed pattern. L18's own remedy was "use the heading form" — which is precisely what this increment tells the author to do, in the one place (`/pharn-plan.md`'s `## Files` blockquote) an author reads before writing a plan. This confirms option (A) — document and steer authors to the heading — over option (B) — narrow the regex — because (B) is the code-side echo of the mistake L18 already showed is dangerous. + +## Files + +- `.claude/commands/pharn-plan.md` — add a short caveat to the existing "`## Files` is the PARSEABLE writes-scope" blockquote (after rule 3, before the closing sentence about `## Steps`): a bare, non-blockquote prose line under `## Files` that reads like an exclusion truncates the authorized list at that line; put narrative in a blockquote or a path-item description, and use `### Explicitly not touched` for real exclusions. Also rephrases the blockquote's closing "only `## Files` back-tick paths become the build's scope" sentence so it does not read as "non-path lines are harmless." +- `CHANGELOG.md` — add an `[Unreleased]` entry under `### Fixed` (a doc clarification, framed honestly as such — no behavioral change) recording the caveat and the `SKILLS_VERSION` bump. +- `SKILLS_VERSION` — bump `2.5.2` → `2.5.3` (patch). +- `README.md` — update the shields version badge (line 13) from `pharn-2.5.2-blue` to `pharn-2.5.3-blue`, + so it agrees with the bumped `SKILLS_VERSION` (`.dev/floor/check-version-badge.mjs`'s live invariant). + Added after `/pharn-dev-regress` caught this omission as a real, deterministic regression + (`.dev/floor/check-version-badge.test.mjs`'s live self-check against this repo) — not a speculative + addition (P7): the failure was observed, not hypothesized. + +### Explicitly not touched + +- `.claude/hooks/set-writes-scope.cjs` — reused as-is; Boundary 1 and Boundary 2 (including the exclusion-cue regex) are unchanged. This plan implements option (A) from the build prompt (document), not option (B) (narrow the regex). +- `.claude/hooks/set-writes-scope.test.cjs` — no behavior changed, so no new/updated assertions are required; existing tests continue to pass unmodified. +- `.claude/commands/pharn-dev-plan.md` — this command's own `## Files` template is simpler (no equivalent "Three rules" blockquote) and is not named by the build prompt's scope; out of scope for this increment (P7 — no speculative parity edit not triggered by a real failure). + +## Contracts satisfied + +- No `pharn-contracts` schema is touched — this is a product-command documentation fix, not a Capability, contract, or floor checker change. `pharn/ARCHITECTURE.md §4`'s layer tree does not apply to `.claude/commands/*.md`; the relevant governing doc is CLAUDE.md's "SKILLS_VERSION discipline" (product-surface bump rules) and "Writes-scope" (fix #7) sections, cited not restated. + +## Evals to write (P1) + +- None. `/pharn-plan.md` is a command doc, not a `role:`-bearing Capability (`pharn/ARCHITECTURE.md §3.1`), so P1's eval requirement does not apply. No `rule_id` is introduced. + +## Guarantee audit (P0) + +- "The `## Files` blockquote now documents the exclusion-cue truncation behavior" → **advisory** (prose guidance for a human author; nothing on the floor enforces that an author reads or follows it). +- "The truncation _behavior itself_ is unchanged" → reduces to the existing floor: `set-writes-scope.cjs` Boundary 1 (structural heading match) and Boundary 2 (the unchanged regex cue) are untouched code, verified by re-running the existing `.claude/hooks/set-writes-scope.test.cjs` suite unmodified and green, plus a live re-run of the repro command from the build prompt showing identical output before and after this change. +- "`SKILLS_VERSION` correctly reflects that product-surface bytes changed" → **floor, narrowed**: `.dev/floor/check-version-badge.mjs` only checks the README badge agrees with `SKILLS_VERSION`; it does not check that a bump was _warranted_ — that judgment is CLAUDE.md's bump-discipline rules, applied here by hand (a `.claude/commands/*.md` non-`pharn-dev-` file is bump-triggering product surface per CLAUDE.md). + +## Trust audit (P2) + +Not applicable — no untrusted artifact is ingested by this increment. The build prompt (this plan's own input) is treated as `trust: untrusted` per `/pharn-dev-plan`'s standing instruction, but it contains no instruction-looking content beyond ordinary task description; nothing in it is executed as a directive outside the human-gated plan/build/review loop. + +## Open questions (HALT) + +- Confirm the resolution: option (A) document-only (recommended by the build prompt and reinforced by L18) vs option (B) narrow the regex. This plan is drafted for (A); if the human selects (B) instead, the plan must be revised (different `## Files`, different guarantee audit, different tests) before approval. +- The build prompt states "currently `SKILLS_VERSION 2.5.1`", but live discovery (Step 1) found `SKILLS_VERSION` is already `2.5.2` (an unrelated prior fix, per `CHANGELOG.md`'s `[Unreleased]` section, already landed on `main`). This plan bumps from the live `2.5.2` to `2.5.3`, not from `2.5.1` — confirm this is expected and not a sign the prompt was written against a stale checkout. diff --git a/.dev/features/f13-modeb-cue/REGRESSION.md b/.dev/features/f13-modeb-cue/REGRESSION.md new file mode 100644 index 0000000..c322a8b --- /dev/null +++ b/.dev/features/f13-modeb-cue/REGRESSION.md @@ -0,0 +1,53 @@ +# REGRESSION — f13-modeb-cue + +**Base:** `HEAD` (auto-detected — the working tree was dirty at run time, so the baseline is the +pre-build commit `84c6f15`, checked out into a detached `git worktree`). + +**Inside (the changed scope):** + +- `.claude/commands/pharn-plan.md` +- `.dev/features/f13-modeb-cue/GRILL.md` (this feature's own artifact — exempt from the escape check + via `--feature f13-modeb-cue`) +- `.dev/features/f13-modeb-cue/PLAN.md` (same exemption) +- `CHANGELOG.md` +- `SKILLS_VERSION` +- `README.md` (added mid-run — see "First pass" below) + +**Declared (`PLAN.md`'s `## Files`):** `.claude/commands/pharn-plan.md`, `CHANGELOG.md`, +`SKILLS_VERSION`, `README.md` — `escaped: []`. No write left the plan's declared scope. + +**Style gates skipped:** `inside` touches no shared style config (`eslint.config.mjs`, +`.prettierrc.json`, `.prettierignore`, `.markdownlint-cli2.jsonc`), so `lint` / `format:check` / +`lint:md` cannot flip over the outside files and were not run (deterministic skip rule). + +## First pass — a real regression, since corrected + +The plan's first draft bumped `SKILLS_VERSION` to `2.5.3` without declaring `README.md`. That run's +`tests` gate flipped GREEN (base) → RED (head): + +```text +test at .dev/floor/check-version-badge.test.mjs:311:1 +✖ the checker is GREEN against this repo + AssertionError [ERR_ASSERTION]: VERSION-BADGE: RED — 1 finding(s) + - [DRIFT] README.md + the badge reads "2.5.2" but SKILLS_VERSION is "2.5.3" +``` + +`check-version-badge.test.mjs` runs live against this repo's actual `README.md` / `SKILLS_VERSION` (not +a fixture); the regression was real and directly caused by this feature's incomplete `## Files`. Per +the human's direction, `PLAN.md` was amended to add `README.md` (updating the shields badge to +`2.5.3`), the build was re-run for the added file, and `/pharn-dev-regress` was re-run in full below. + +## Outside-gate results (base → head, final run) + +| gate | base | head | flipped? | +| --------------------------------------- | ---- | ---- | -------- | +| `tests` | 0 | 0 | no | +| `validate` | 0 | 0 | no | +| `structural:expected-injection-comment` | 0 | 0 | no | + +**REGRESSIONS: none — no deterministically-detectable breakage outside the feature.** + +**Honest residual (P7):** this report certifies only what its gate set covers — `node --test` over the +listed outside test files, `validate.mjs`, and one `structural:*` eval pair. It does not certify that +nothing else broke; it certifies that no _covered_ gate flipped. diff --git a/.dev/features/f13-modeb-cue/REVIEW.md b/.dev/features/f13-modeb-cue/REVIEW.md new file mode 100644 index 0000000..f0e5998 --- /dev/null +++ b/.dev/features/f13-modeb-cue/REVIEW.md @@ -0,0 +1,75 @@ +# REVIEW — f13-modeb-cue + +**Floor first (P0):** `node pharn/floor/validate.mjs .` → `FLOOR: GREEN — 36 capabilities checked in .` +The increment reached review with a green floor. Final diff: `.claude/commands/pharn-plan.md`, +`CHANGELOG.md`, `SKILLS_VERSION`, `README.md` — exactly the amended `PLAN.md`'s `## Files`, `escaped: +[]` (confirmed by `/pharn-dev-regress`). + +## L-floor → P0 + +No new guarantee claim. The increment's own guarantee audit (in `PLAN.md`) labels the caveat prose +`advisory` and the unchanged-parser claim as reducing to the existing floor (the untouched +`set-writes-scope.cjs` + its unmodified, still-green test suite). Reviewed the actual diff: the caveat +text added to `pharn-plan.md` makes no guarantee claim of its own — it is pure author-facing guidance. +`check-version-badge.mjs`'s live check (`.dev/floor/check-version-badge.test.mjs`) now correctly reports +GREEN for `README.md` vs `SKILLS_VERSION` `2.5.3`. **No findings.** + +## L-eval → P1 + +Not applicable — no new Capability, no new `rule_id`, confirmed by `validate.mjs` staying at the same +36-capability count before and after this increment. The plan's "None" declaration and the floor agree. +**No findings.** + +## L-trust → P2 + +No finding-emitting Capability was introduced or modified. The build prompt (the increment's own +provenance) was treated as `trust: untrusted` throughout the pipeline; nothing instruction-looking in it, +in the PLAN, in the GRILL findings, or in the diff changed agent behavior outside the human-gated +plan → grill → build → regress → verify → review sequence. **No findings.** + +## L-axis → P3 + +Four files changed, one axis: documenting and shipping the writes-scope exclusion-cue caveat (the doc +edit itself, plus its mandatory version/changelog bookkeeping, plus the badge correction that +bookkeeping's own live invariant required). No sibling module reference introduced; `.claude/commands/*.md` +and root files are outside the `pharn-contracts` layer tree entirely. **No findings.** + +## Findings + +None — floor-gate or advisory. + +## Proposed lesson candidate (for a separate, human-gated `/pharn-dev-memory-promote` run) + +**Candidate.** _A `SKILLS_VERSION` bump must be planned together with the README badge it feeds, or a +later stage catches it as a false "regression."_ + +**Why it's real, not hypothetical (P7):** this increment's own first `/pharn-dev-regress` pass caught +exactly this. The plan bumped `SKILLS_VERSION` `2.5.2` → `2.5.3` without declaring `README.md` in +`## Files`; `.dev/floor/check-version-badge.test.mjs` runs live against the actual repo (not a fixture), +so the `tests` gate flipped GREEN → RED at `/pharn-dev-regress`, correctly surfaced as a deterministic +regression, and required a mid-run plan amendment (adding `README.md`, updating the badge, re-running +build + regress) to resolve. The checker itself already prints the fix +(`FIX: SKILLS_VERSION is the single source — update the README badge to match it`) — nothing here is +new information the tooling doesn't already say, but this increment shows a plan can still omit it, and +that the omission is caught **downstream at regress**, not at plan or build time, which costs a full +extra planning/build/regress cycle. + +**Suggested remedy, for the human to weigh at promotion:** `/pharn-dev-plan` and `/pharn-dev-build`'s +lessons/guidance could name this pairing explicitly (mirroring how `check-version-badge.mjs`'s own +addition was itself driven by `.dev/memory-bank/lessons-learned.md` **L20** — a defect whose only +remedy is "remember" earns a floor check) — though a full floor check here would mean `/pharn-dev-plan` +itself asserting `## Files` completeness against `SKILLS_VERSION` diffs, which is a larger, separate +design question left to the human/promotion step, not decided here. + +**Provenance.** + +- feature: `f13-modeb-cue` +- commit: not yet committed (this REVIEW predates the commit; `/pharn-dev-memory-promote`, if run, should + capture the eventual commit SHA or `unknown`) +- source: `.dev/features/f13-modeb-cue/REGRESSION.md` ("First pass — a real regression, since corrected") + - `.dev/features/f13-modeb-cue/PLAN.md`'s amended `## Files` entry for `README.md` + +## Verdict + +**GREEN — no floor-gate or advisory findings.** One lesson candidate is proposed above for a human to +weigh at a separate `/pharn-dev-memory-promote` run; nothing here blocks the increment. diff --git a/.dev/features/f13-modeb-cue/SHIP.md b/.dev/features/f13-modeb-cue/SHIP.md new file mode 100644 index 0000000..3bf149c --- /dev/null +++ b/.dev/features/f13-modeb-cue/SHIP.md @@ -0,0 +1,52 @@ +# SHIP — f13-modeb-cue + +Gated `/pharn-dev-ship` run over the F13 build prompt (a bare prose line under a PLAN's `## Files` can +falsely truncate the writes-scope — option A, document-only). + +## Stages run, in order + +1. **`/pharn-dev-plan`** — wrote `.dev/features/f13-modeb-cue/PLAN.md`. **GATE 1**: human approved as + written (option A; SKILLS_VERSION base corrected 2.5.2→2.5.3 after live discovery found the build + prompt's stated `2.5.1` was stale). +2. **`/pharn-dev-grill`** — wrote `.dev/features/f13-modeb-cue/GRILL.md`. Advisory verdict: 1 minor, + non-blocking finding (a note about the build prompt's own suggested write-procedure command, not + about the plan). Presented, then proceeded per `/pharn-dev-ship`'s "grill gates nothing" rule. + Additionally honored an increment-specific **HALT 2 diff halt** (from the build prompt itself, + layered on top of the standard chain) — the exact diff was presented and approved before any write. +3. **`/pharn-dev-build`** — wrote `.claude/commands/pharn-plan.md`, `CHANGELOG.md`, `SKILLS_VERSION`. + Spec-hash re-check: match, no drift. **Verdict read: `validate.mjs` exit 0 (GREEN)** → proceed. +4. **`/pharn-dev-regress`** — first pass: **verdict `"regressions"`** (`tests` gate flipped GREEN→RED — + `check-version-badge.test.mjs`'s live self-check, caused by the plan's `SKILLS_VERSION` bump omitting + `README.md`). Per `/pharn-dev-ship`'s protocol this is a non-GREEN verdict → **STOPped and presented**. + Human directed a correction: amend `PLAN.md` to add `README.md`, re-run the build for that one file, + re-run regress. Second pass: **verdict `"no-regressions"`** → proceed. +5. **`/pharn-dev-verify`** — wrote `.dev/features/f13-modeb-cue/verify-report.json` + + `VERIFY.md`. Gates run: `test`, `validate`, `lint`, `format:check`, `lint:md`, + `structural:expected-injection-comment` — all exit 0. Zero verifiers registered. **Verdict read: + `"PASS"`** → proceed. +6. **`/pharn-dev-review`** — wrote `.dev/features/f13-modeb-cue/REVIEW.md`. Floor GREEN; all four + lenses (L-floor/P0, L-eval/P1, L-trust/P2, L-axis/P3) — no findings. One lesson candidate proposed + (SKILLS_VERSION bumps should be planned together with the README badge) for a separate, human-gated + `/pharn-dev-memory-promote` run — not promoted here. + +## Standing floor verdicts (verbatim, as read) + +- `/pharn-dev-build` → `pharn/floor/validate.mjs` exit `0` — `FLOOR: GREEN — 36 capabilities checked in .` +- `/pharn-dev-regress` (final) → `.dev/features/f13-modeb-cue/regression-report.json` `.verdict` = + `"no-regressions"` (`regressions: []`) +- `/pharn-dev-verify` → `.dev/features/f13-modeb-cue/verify-report.json` `.verdict` = `"PASS"` + (`failing_gates: []`) + +## Pointers (not restated — P4) + +- `.dev/features/f13-modeb-cue/GRILL.md` — advisory grill-log +- `.dev/features/f13-modeb-cue/REGRESSION.md` — full regression detail, including the first-pass + regression and its correction +- `.dev/features/f13-modeb-cue/VERIFY.md` — full gate table +- `.dev/features/f13-modeb-cue/REVIEW.md` — full lens findings + proposed lesson candidate + +## The standing decision is the human's + +This chain ran and the named floor verdicts are as shown above — this is **NOT** a judgment that the +increment is good or wise; that is the human's call at this post-review gate (**GATE 2**). No merge, +push, commit, or `PHARN ✓ reviewed` seal has been applied by this run. diff --git a/.dev/features/f13-modeb-cue/VERIFY.md b/.dev/features/f13-modeb-cue/VERIFY.md new file mode 100644 index 0000000..814c8d2 --- /dev/null +++ b/.dev/features/f13-modeb-cue/VERIFY.md @@ -0,0 +1,26 @@ +# VERIFY — f13-modeb-cue + +## Gate results + +| gate | exit | +| --------------------------------------- | ---- | +| `test` | 0 | +| `validate` | 0 | +| `lint` | 0 | +| `format:check` | 0 | +| `lint:md` | 0 | +| `structural:expected-injection-comment` | 0 | + +**VERIFIED: floor gates PASS.** + +## Verifiers (advisory) + +No verifiers registered — floor gates only (`node pharn/floor/count-verifiers.mjs .` → +`{"registered":0,"verifiers":[]}`). + +## Honest residual (P0/P7) + +Verified = the named gates passed; this is **not** a guarantee of correctness beyond what those gates +check — verifier concerns are advisory help, not assurance. The gate set here tracks the full `npm run +check` aggregate (`test`, `validate`, `lint`, `format:check`, `lint:md`) plus one feature-specific +`structural:*` eval-pair gate; a defect none of those cover is invisible to this verdict. diff --git a/.dev/features/f13-modeb-cue/regression-report.json b/.dev/features/f13-modeb-cue/regression-report.json new file mode 100644 index 0000000..29dbb3b --- /dev/null +++ b/.dev/features/f13-modeb-cue/regression-report.json @@ -0,0 +1,28 @@ +{ + "base": "HEAD", + "inside": [ + ".claude/commands/pharn-plan.md", + ".dev/features/f13-modeb-cue/GRILL.md", + ".dev/features/f13-modeb-cue/PLAN.md", + "CHANGELOG.md", + "SKILLS_VERSION", + "README.md" + ], + "outside_gates": { + "structural:expected-injection-comment": { + "base": 0, + "head": 0 + }, + "tests": { + "base": 0, + "head": 0 + }, + "validate": { + "base": 0, + "head": 0 + } + }, + "regressions": [], + "pre_existing": [], + "verdict": "no-regressions" +} diff --git a/.dev/features/f13-modeb-cue/verify-report.json b/.dev/features/f13-modeb-cue/verify-report.json new file mode 100644 index 0000000..900f9a0 --- /dev/null +++ b/.dev/features/f13-modeb-cue/verify-report.json @@ -0,0 +1,14 @@ +{ + "feature": "f13-modeb-cue", + "gates": { + "format:check": 0, + "lint": 0, + "lint:md": 0, + "structural:expected-injection-comment": 0, + "test": 0, + "validate": 0 + }, + "verdict": "PASS", + "failing_gates": [], + "verifiers": { "registered": 0, "findings": [] } +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 86d5a78..0ed34ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ### Fixed +- **A bare, non-blockquote prose line under a PLAN's `## Files` could silently truncate the authorized writes-scope, and `/pharn-plan.md` didn't say so.** `set-writes-scope.cjs`'s Mode-B parser (`pathsFromPlanFiles`) already exempts an authorized path-item's own description and an explanatory blockquote from its fail-closed exclusion-cue fallback (Boundary 2), but a bare narrative sentence between two path items — e.g. "these steps do not change the public API" — still matched the cue and ended the list there, dropping every path after it from `.pharn/writes-scope.json`. This is fail-closed (the build is blocked, not silently under-protected) and the underlying matcher is unchanged: narrowing it would trade today's false-positive for a fail-**open** false-negative on a real, unusually-worded exclusion — the exact failure mode L18 already documented. `/pharn-plan.md`'s `## Files` contract blockquote — the one place an author writes this section — now names the caveat and the three ways to avoid it (blockquote, path-item description, or the `### Explicitly not touched` heading), and clarifies that its closing sentence ("only back-tick paths become the build's scope") does not mean non-path lines are harmless. **`SKILLS_VERSION` bumped to `2.5.3` (patch)** — `pharn-plan.md` is product surface (a non-`pharn-dev-` command), and this is a prose clarification of already-shipped bytes with no behavior change. + - **`set-writes-scope.cjs` no longer mangles a Next.js route-group directory in a `writes:` entry.** Its `clean()` helper strips a trailing " (annotation)" (e.g. " (gated)") from a declared path, but the regex used `\s*` (zero-or-more space) before the paren, so it also matched a path segment that itself legitimately ends in `)` — `app/(marketing)` collapsed to `app/`, and a nested `app/(a)/(b)` would have collapsed the same way. A route-group `writes:` entry therefore silently under-scoped: the build's intended writes under `app/(marketing)/…` fell outside the emitted scope, and `enforce-writes-scope.cjs` denied them — fail-closed on a common, real layout, not a hypothetical one. The regex now requires `\s+` (one-or-more space), which still strips the documented space-separated annotation form but leaves a route-group segment (no leading space before its own paren) intact. **Why `\s+` and not removing the strip entirely.** An annotation is always written with a leading space per the function's own doc comment; a route-group segment never has one. Requiring the space is therefore a precise, minimal fix on that one distinguishing axis. Discovery confirmed live that no `writes:` frontmatter in this repo's `.claude/commands/*.md` currently exercises the annotation-strip on a real trailing `)` — the one paren-containing entry (`pharn-build.md`'s placeholder) ends in `>`, not `)`, so it was never reachable either way — so removing the strip was a defensible alternative, but `\s+` is the smaller, zero-cost change and preserves the documented behavior for a future spaced annotation. diff --git a/README.md b/README.md index 55bedd4..aa284a4 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ runs on Claude Code today, and the discipline itself ships as readable markdown lenses, rules — that you read, diff, and version yourself. PHARN does not make anyone understand the code; it keeps a deterministic floor under it and the record available the moment someone needs it. -[![pharn](https://img.shields.io/badge/pharn-2.5.2-blue)](./CHANGELOG.md) +[![pharn](https://img.shields.io/badge/pharn-2.5.3-blue)](./CHANGELOG.md) [![License: Apache 2.0](https://img.shields.io/badge/license-Apache%202.0-green)](./LICENSE) [![CI](https://github.com/pharn-dev/pharn-oss/actions/workflows/ci.yml/badge.svg)](https://github.com/pharn-dev/pharn-oss/actions/workflows/ci.yml) [![CodeQL](https://github.com/pharn-dev/pharn-oss/actions/workflows/codeql.yml/badge.svg)](https://github.com/pharn-dev/pharn-oss/actions/workflows/codeql.yml) diff --git a/SKILLS_VERSION b/SKILLS_VERSION index f225a78..aedc15b 100644 --- a/SKILLS_VERSION +++ b/SKILLS_VERSION @@ -1 +1 @@ -2.5.2 +2.5.3 From 0382d6568ca956ff09d9fe421e2886203313fe42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Galarowicz?= Date: Wed, 12 Aug 2026 16:36:22 +0200 Subject: [PATCH 2/2] update --- .claude/commands/pharn-plan.md | 7 ++++--- CHANGELOG.md | 2 ++ README.md | 2 +- SKILLS_VERSION | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.claude/commands/pharn-plan.md b/.claude/commands/pharn-plan.md index c0d0be9..0d598b2 100644 --- a/.claude/commands/pharn-plan.md +++ b/.claude/commands/pharn-plan.md @@ -237,9 +237,10 @@ applied_lessons: none | [L1, L2] # MANDATORY — floor-checked (Step 4b); `none` > limits_) is treated by the setter as a head-less exclusion intro and **truncates the authorized list > right there** — every path after it silently falls out of scope. Keep narrative in a **blockquote** > (`> …`) or as a **path-item description** (``- `path` — note``), and use the `### Explicitly not -touched` heading (rule 3) for a real exclusion. Keep an unfilled placeholder in **angle-brackets** -> (`` `` ``) so an un-filled `## Files` **fails closed** at the setter — a bare word like -> `` `path` `` would wrongly parse as a real scope path. The `## Steps` above is **advisory prose**, +touched` heading (rule 3) for a real exclusion. Keep unfilled placeholders as **list items** whose +> leading token is an angle-bracket path — ``- `` `` (matching `pathsFromPlanFiles`) — so an +> un-filled `## Files` **fails closed** at the setter (`isConcrete` rejects `<`/`>`); a bare +> ``- `path` `` item is **unsafe** because it parses as a real scope path. The `## Steps` above is **advisory prose**, > but a non-path line under `## Files` is not harmless (see the caveat above) — only the `## Files` > back-tick paths **before any such truncation** become the build's scope, and `/pharn-build` writes > nothing outside them (fix #7). diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ed34ef..f9dd4a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ### Fixed +- **`/pharn-plan.md`'s `## Files` placeholder guidance now shows the list-item shape `pathsFromPlanFiles` actually parses.** The contract blockquote had said to keep an unfilled placeholder in angle-brackets (`` `` ``) without showing it as a list item, and warned only that a bare `` `path` `` "word" would parse as scope — but the parser matches ``- `…` `` list items (`pathsFromPlanFiles`), so the unsafe form is specifically ``- `path` `` (no angle brackets), which `isConcrete` accepts while ``- `` `` does not. The guidance now shows ``- `` `` explicitly and names the bare list-item form as unsafe. **`SKILLS_VERSION` bumped to `2.5.4` (patch)** — `pharn-plan.md` is product surface. + - **A bare, non-blockquote prose line under a PLAN's `## Files` could silently truncate the authorized writes-scope, and `/pharn-plan.md` didn't say so.** `set-writes-scope.cjs`'s Mode-B parser (`pathsFromPlanFiles`) already exempts an authorized path-item's own description and an explanatory blockquote from its fail-closed exclusion-cue fallback (Boundary 2), but a bare narrative sentence between two path items — e.g. "these steps do not change the public API" — still matched the cue and ended the list there, dropping every path after it from `.pharn/writes-scope.json`. This is fail-closed (the build is blocked, not silently under-protected) and the underlying matcher is unchanged: narrowing it would trade today's false-positive for a fail-**open** false-negative on a real, unusually-worded exclusion — the exact failure mode L18 already documented. `/pharn-plan.md`'s `## Files` contract blockquote — the one place an author writes this section — now names the caveat and the three ways to avoid it (blockquote, path-item description, or the `### Explicitly not touched` heading), and clarifies that its closing sentence ("only back-tick paths become the build's scope") does not mean non-path lines are harmless. **`SKILLS_VERSION` bumped to `2.5.3` (patch)** — `pharn-plan.md` is product surface (a non-`pharn-dev-` command), and this is a prose clarification of already-shipped bytes with no behavior change. - **`set-writes-scope.cjs` no longer mangles a Next.js route-group directory in a `writes:` entry.** Its `clean()` helper strips a trailing " (annotation)" (e.g. " (gated)") from a declared path, but the regex used `\s*` (zero-or-more space) before the paren, so it also matched a path segment that itself legitimately ends in `)` — `app/(marketing)` collapsed to `app/`, and a nested `app/(a)/(b)` would have collapsed the same way. A route-group `writes:` entry therefore silently under-scoped: the build's intended writes under `app/(marketing)/…` fell outside the emitted scope, and `enforce-writes-scope.cjs` denied them — fail-closed on a common, real layout, not a hypothetical one. The regex now requires `\s+` (one-or-more space), which still strips the documented space-separated annotation form but leaves a route-group segment (no leading space before its own paren) intact. diff --git a/README.md b/README.md index aa284a4..64244e7 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ runs on Claude Code today, and the discipline itself ships as readable markdown lenses, rules — that you read, diff, and version yourself. PHARN does not make anyone understand the code; it keeps a deterministic floor under it and the record available the moment someone needs it. -[![pharn](https://img.shields.io/badge/pharn-2.5.3-blue)](./CHANGELOG.md) +[![pharn](https://img.shields.io/badge/pharn-2.5.4-blue)](./CHANGELOG.md) [![License: Apache 2.0](https://img.shields.io/badge/license-Apache%202.0-green)](./LICENSE) [![CI](https://github.com/pharn-dev/pharn-oss/actions/workflows/ci.yml/badge.svg)](https://github.com/pharn-dev/pharn-oss/actions/workflows/ci.yml) [![CodeQL](https://github.com/pharn-dev/pharn-oss/actions/workflows/codeql.yml/badge.svg)](https://github.com/pharn-dev/pharn-oss/actions/workflows/codeql.yml) diff --git a/SKILLS_VERSION b/SKILLS_VERSION index aedc15b..fe16b34 100644 --- a/SKILLS_VERSION +++ b/SKILLS_VERSION @@ -1 +1 @@ -2.5.3 +2.5.4