Skip to content
2 changes: 1 addition & 1 deletion .claude/commands/pharn-dev-build.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ For each file the plan names in `## Files`:
- Determinism (P5): branches are membership tests; the terminal fallback is "ask", never a guess.

> Building a PHARN **markdown capability** (frontmatter contract, `evals/cases` + `evals/expected`,
> `seal:`) applies only when the increment adds one. pharn-cli is TypeScript — its "capabilities"
> `seal:`) applies only when the increment adds one. pharn is TypeScript — its "capabilities"
> are modules and its "evals" are vitest tests; the structural `validate.mjs` floor below stays
> vacuously-green until a markdown capability is actually added.

Expand Down
2 changes: 1 addition & 1 deletion .claude/hooks/enforce-writes-scope.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ function resolveWriteTarget(p) {
// the Write tool cannot self-escalate by editing the gate's input.
const ALWAYS = [".pharn/**"];

// Fail-closed allow-list used when no scope file is set. pharn-cli's editable code (src/, tests/) +
// Fail-closed allow-list used when no scope file is set. pharn's editable code (src/, tests/) +
// the dev-loop's own build artifacts (.dev/features/) + the legacy root artifact zone (features/,
// retained, harmless) + process scratch (.pharn/, via ALWAYS). The sensitive zones (.dev/floor/,
// .dev/memory-bank/, .claude/, root spec docs, docs/, config) are intentionally absent — reaching
Expand Down
4 changes: 2 additions & 2 deletions .claude/hooks/enforce-writes-scope.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,15 @@ function setter(cwd, ...args) {

// --- Hook, no scope file: fail-closed default-safe-set ---

test("no scope: src/ (pharn-cli source) is ALLOWED", () => {
test("no scope: src/ (pharn source) is ALLOWED", () => {
assert.equal(hook(tmp(), "src/lib/installer.ts").status, 0);
});

test("no scope: tests/ is ALLOWED", () => {
assert.equal(hook(tmp(), "tests/installer.test.ts").status, 0);
});

test("no scope: a pharn-oss module path (pharn-review/) is DENIED (pharn-cli has no module dirs — not in the safe-set)", () => {
test("no scope: a pharn-oss module path (pharn-review/) is DENIED (pharn has no module dirs — not in the safe-set)", () => {
assert.equal(hook(tmp(), "pharn-review/foo.md").status, 2);
});

Expand Down
4 changes: 2 additions & 2 deletions .dev/features/archetype-enum-align/PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

- spec_content_hash: bca940a5ad247c120e6d8a3acba119d0d8df51dca275964d0e54c48d729d3c4e # fix #4 (ARCHITECTURE.md)
- increment: Map DB signals (`.sql` files, `migrations/` dirs, and `prisma` / `@prisma/client` / `drizzle-orm` deps) to the existing `backend` archetype — the one residual after discovery found the enum alignment already complete. **Approved by the human at the halt (this run); knowingly reverses the tested decision #2 of increment #21.** (The enum↔pharn-oss alignment itself was already implemented across #17/#20/#21 — confirmed, no change needed.)
- layer(s): pharn-cli `src/lib` capability-resolver (ARCHITECTURE.md §5, "Archetype + map-consistency") — no `pharn-contracts` / `pharn-core` module files touched
- layer(s): pharn `src/lib` capability-resolver (ARCHITECTURE.md §5, "Archetype + map-consistency") — no `pharn-contracts` / `pharn-core` module files touched
- constitution_refs: [P6, P7, P5, P2, P3]

## Discovery finding — READ FIRST (the increment's premise is stale)

Everything the request describes as a *change* is **already in the tree** (verified by reads this run, not memory — P6). The task's premise — _"pharn-cli's archetype.ts currently uses {frontend, db, …} — mismatched"_ — is **false against live state**:
Everything the request describes as a *change* is **already in the tree** (verified by reads this run, not memory — P6). The task's premise — _"pharn's archetype.ts currently uses {frontend, db, …} — mismatched"_ — is **false against live state**:

| Requested change | Live state | Evidence |
| --- | --- | --- |
Expand Down
2 changes: 1 addition & 1 deletion .dev/features/archetype-file-tree-scan/PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ Signal → archetype mapping (the enum has no `db`/`frontend`, so names map onto
## Contracts satisfied

- **N/A — no `pharn-contracts` reference.** `pharn-contracts` is a PHARN-*product* layer (ARCHITECTURE.md
§4); this is a pharn-cli-internal `src/lib/` boundary and satisfies no product contract (as with #20).
§4); this is a pharn-internal `src/lib/` boundary and satisfies no product contract (as with #20).
- **Spec cited, not restated (P4):** ARCHITECTURE.md §5 (`archetype ∈ {ssr, backend, spa, lib}`, deterministic
detection) and §4 (the frameworkless `lib` base). This increment **extends the detection INPUT surface**
(adds file-tree signals) while preserving the enum and determinism; it does not restate the rule.
Expand Down
2 changes: 1 addition & 1 deletion .dev/features/archetype-io-boundary/PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ deliberately **out of scope** here (P7 — smallest coherent increment).
## Contracts satisfied

- **N/A — no `pharn-contracts` reference.** `pharn-contracts` is a PHARN-*product* layer (ARCHITECTURE.md
§4); this increment is a pharn-cli-internal `src/lib/` boundary and satisfies no product contract.
§4); this increment is a pharn-internal `src/lib/` boundary and satisfies no product contract.
- **Spec cited, not restated (P4):** ARCHITECTURE.md §5 (`archetype ∈ {ssr, backend, spa, lib}`, "detected
deterministically (membership over `package.json`)") and §4 (the frameworkless `lib` base). The new
boundary adds the I/O around that rule; it does not re-implement or restate the rule.
Expand Down
2 changes: 1 addition & 1 deletion .dev/features/archetype-missing-signal/PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ refactor, out of scope now (P7).

## Contracts satisfied

- **N/A — no `pharn-contracts` reference** (a pharn-cli-internal `src/lib/` boundary, as in the prior
- **N/A — no `pharn-contracts` reference** (a pharn-internal `src/lib/` boundary, as in the prior
increment).
- **Spec cited, not restated (P4):** ARCHITECTURE.md §5 (`archetype ∈ {ssr, backend, spa, lib}`,
"membership over `package.json`") and §4 (frameworkless `lib` base). The change only enriches the
Expand Down
2 changes: 1 addition & 1 deletion .dev/features/archetype-path-context/PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

- spec_content_hash: bca940a5ad247c120e6d8a3acba119d0d8df51dca275964d0e54c48d729d3c4e # fix #4 (ARCHITECTURE.md)
- increment: Give `classifyEntry` (detect-archetype.ts) the **path context** its four file-tree rules already assume in their comments — scope `api/`, `route.*`, `.sql`, and `.tsx/.jsx` matches to their documented locations so a file merely _named_ x no longer implies the project _has_ surface x; and (secondary) loosen `parseApplies` (capability-index.ts) to accept unquoted YAML `applies` tokens. No new archetype/enum member; determinism preserved.
- layer(s): pharn-cli `src/lib` — the archetype-detection **I/O boundary** (`detect-archetype.ts`) and the capability **fetch boundary** (`capability-index.ts`), both under ARCHITECTURE.md §5 "Archetype + map-consistency" / "Trust-fence". No `pharn-contracts` / `pharn-core` / `ARCHITECTURE.md` files touched (§5 is hook-protected + human-only).
- layer(s): pharn `src/lib` — the archetype-detection **I/O boundary** (`detect-archetype.ts`) and the capability **fetch boundary** (`capability-index.ts`), both under ARCHITECTURE.md §5 "Archetype + map-consistency" / "Trust-fence". No `pharn-contracts` / `pharn-core` / `ARCHITECTURE.md` files touched (§5 is hook-protected + human-only).
- constitution_refs: [P5, P2, P6, P7, P1, P3]

## Discovery findings — READ FIRST (grounded in reads this run, not memory — P6)
Expand Down
57 changes: 57 additions & 0 deletions .dev/features/canonical-npm-name/GRILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# GRILL — canonical-npm-name (advisory)

Plan under interrogation: `.dev/features/canonical-npm-name/PLAN.md`.
Spec-hash check: `sha256(ARCHITECTURE.md)` = `bca940a5ad247c120e6d8a3acba119d0d8df51dca275964d0e54c48d729d3c4e` — **matches** the plan's `spec_content_hash`. No spec drift (the binding block on drift is `/pharn-dev-build`'s floor-gate; here it only confirms).

> The PLAN is `trust: untrusted` to this griller. `problem`/`evidence` free-text below quotes the plan as DATA; the enum-gated fields (`type`/`rule_id`/`severity`/`file`) are this griller's own membership/path assertions. Grillers gate nothing — this whole log is advisory (fix #3).

## Findings

### Axis: testability (P1)

```yaml
- type: FINDING
rule_id: "P1"
severity: minor # advisory assignment — a griller never gates (fix #3)
file: ".dev/features/canonical-npm-name/PLAN.md:65"
problem: "The rename of the user-facing help/error copy in src/index.ts (USAGE) and src/steps/prereqs.ts is not pinned by any test, so a future edit could silently reintroduce the deprecated scoped name without a red gate."
evidence: "None required... the existing tests/index.test.ts --help assertion checks toContain('Usage:'), not the package-name string, so it stays green whether or not the USAGE string changes."
```

Note: this is a copy-only string change (no control-flow), so the miss is low-risk and P1 is arguably satisfied vacuously — but a one-line assertion (USAGE contains `pharn`, not `@pharn-dev/pharn`) would make the rename a *tested* fact for ~free. Surfaced for the human; not a gate.

### Axis: guarantee-audit / P0

```yaml
- type: FINDING
rule_id: "P0"
severity: minor # advisory assignment
file: ".dev/features/canonical-npm-name/PLAN.md:69"
problem: "The 'same file list as the scoped publish' guarantee is established by construction plus the local npm pack output, not by an actual list/byte diff against the already-published @pharn-dev/pharn@0.2.0 tarball (which is not fetched this run)."
evidence: "Published tarball is pharn-0.2.0.tgz, same file list as the scoped publish; content diffs = package.json.name + the bundled help/error strings in dist/index.js ... floor: npm pack --dry-run output."
```

Note: the floor that *does* hold deterministically is `npm pack --dry-run` → tarball name `pharn-0.2.0.tgz` + its file list (`package.json` + `dist/`). "Identical to the *scoped* publish" is a reasoning claim (only the name field + two bundled strings changed), honest to label as such rather than as a verified diff. Low-risk; the file SET is unchanged by construction (`files: ["dist"]`).

### Axis: documentation (P7) — no finding

The documentation griller's own rule: a **mechanical rename** may legitimately need no new docs, and this increment *is* the documentation update — README, SECURITY, `docs/**`, and CHANGELOG all move to the canonical name in-scope. The public surface (package name + install command) is documented, not shipped as self-explanatory. Declaration recognized → **no absence finding**.

### Axes with no findings

- **P2 (trust):** N/A — the increment ingests no untrusted artifact (no manifest/degit content); no taint introduced. Correctly stated in the plan.
- **P3 (one axis / no sibling imports):** every touched file changes for the single axis "canonical package name"; no command→command or step→step import is added.
- **P5 (determinism):** no new branch; the three open questions were resolved by asking the human at GATE 1 (terminal fallback = ask, honored).
- **P7 (scope):** real trigger (publish under the free canonical name); not speculative; one axis, not two bundled.

## Tooling observation (not a plan finding)

`node .dev/floor/count-grillers.mjs .` reported **81** registered grillers — but **all 81 are under `test-*/` install fixtures** (`test-backend/pharn/...`, `test-full/pharn/...`, etc.), i.e. copies PHARN installed into this CLI's own test apps, not this repo's dev-loop grillers. The counter does not exclude the local test-app dirs, so it over-reports. This does not affect this increment (it is a dev-loop tooling quirk, not a defect in the plan), but the same over-count likely appears in `count-verifiers.mjs` at `/pharn-dev-verify` — noted so it is expected there. Recorded as prose (no `PLAN.md:line` to cite → not a finding-shape object).

## Summary

The plan is a tightly-scoped, single-axis mechanical rename that also updates all of its own user-facing documentation. The guarantee audit reduces its real claims to the floor (`npm pack`, `npm run check`) and labels the rest advisory; the trust surface is untouched; the three live-state contradictions (phantom `--wizard`, `provenance` mis-description, CHANGELOG layout) were surfaced and human-resolved before build. The only concerns are two **minor, advisory** ones: (1) the src copy rename is not pinned by a test, and (2) "identical to the scoped publish" is a reasoning claim, not a fetched-tarball diff. Neither blocks build.

## Verdict

ADVISORY VERDICT: 2 concerns raised (0 blocking-severity, 2 minor/advisory) — for the human to weigh before `/pharn-dev-build`. This grill did NOT "pass" the plan and guarantees nothing; it surfaces the above. The only deterministic backstops remain `/pharn-dev-build`'s floor-gates (spec-hash, unresolved HALT questions) and `.dev/floor/validate.mjs`.
Loading