Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions .claude/commands/pharn-plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,10 +232,18 @@ applied_lessons: none | [L1, L2] # MANDATORY — floor-checked (Step 4b); `none`
> **leading token is a back-tick path** — ``- `path/to/file` — <what changes>``; (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** (`` `<path>` ``) 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 unfilled placeholders as **list items** whose
> leading token is an angle-bracket path — ``- `<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).

## Step 4b — Check the lessons declaration (FLOOR)

Expand Down
82 changes: 82 additions & 0 deletions .dev/features/f13-modeb-cue/GRILL.md
Original file line number Diff line number Diff line change
@@ -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 <scratch> --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 <scratch> --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.
Comment on lines +48 to +60

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Reconcile the final scope with the grill gate.

The final plan contains README.md, but the grill analyzes only three files. The shipment record confirms that the file was added after the grill and that no grill rerun occurred.

  • .dev/features/f13-modeb-cue/GRILL.md#L48-L60: review README.md or record an approved post-grill scope amendment.
  • .dev/features/f13-modeb-cue/GRILL.md#L71-L82: update the summary and verdict to cover the final scope.
  • .dev/features/f13-modeb-cue/SHIP.md#L18-L30: rerun the grill or record the required human approval before closing the chain.
📍 Affects 2 files
  • .dev/features/f13-modeb-cue/GRILL.md#L48-L60 (this comment)
  • .dev/features/f13-modeb-cue/GRILL.md#L71-L82
  • .dev/features/f13-modeb-cue/SHIP.md#L18-L30
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.dev/features/f13-modeb-cue/GRILL.md around lines 48 - 60, Reconcile the
final scope with the grill gate: in .dev/features/f13-modeb-cue/GRILL.md lines
48-60, review README.md or record an approved post-grill scope amendment; in
lines 71-82, update the summary and verdict to cover the final scope; and in
.dev/features/f13-modeb-cue/SHIP.md lines 18-30, rerun the grill or record the
required human approval before closing the chain.

- **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.**
51 changes: 51 additions & 0 deletions .dev/features/f13-modeb-cue/PLAN.md
Original file line number Diff line number Diff line change
@@ -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.
53 changes: 53 additions & 0 deletions .dev/features/f13-modeb-cue/REGRESSION.md
Original file line number Diff line number Diff line change
@@ -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.
Loading