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
4 changes: 2 additions & 2 deletions .dev/features/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ holding its **process and audit artifacts** from the build loop (the `pharn-dev-

These record _how_ an increment of PHARN was specified, planned, grilled, built, and reviewed. This is
the **developer / contributor** side of the dev/product boundary — the apparatus a PHARN contributor
uses, **not** what a PHARN user receives. The built capabilities themselves live in their modules at the
repo root (`pharn-contracts/`, `pharn-review/`, …); the PRODUCT pipeline's artifacts live in the
uses, **not** what a PHARN user receives. The built capabilities themselves live in their modules under
`pharn/` (`pharn/pharn-contracts/`, `pharn/pharn-review/`, …); the PRODUCT pipeline's artifacts live in the
**root-level `features/`** (see `../../features/README.md`).

Artifacts are written only when they genuinely exist. An increment that had no SPEC or PLAN — e.g. a
Expand Down
58 changes: 58 additions & 0 deletions .dev/features/docs-drift-resync/PLAN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# PLAN — docs-drift-resync: re-derive stale doc claims across the shipped and meta surfaces

- spec_content_hash: 8f5ec002e3b18cbfd2f094b08a3671f7ed42a05a3fbaf01a11bbbd28da30fb52 # fix #4
- applied_lessons: [L25]
- increment: Correct every doc sentence a full-tree audit CONFIRMED stale against the live repo — no reframing, no new claims; each edit restores a sentence to what the tree already proves.
- layer(s): none — doc corrections across contracts, floor README, two lenses, and repo-meta # pharn/ARCHITECTURE.md §4
- constitution_refs: [P0, P6]

## Applied lessons

- **L25** — every target sentence here is exactly L25's defect at doc scale: prose written when a
state was true (checker unbuilt, path pre-relocation, gate list shorter) and trusted after the
state moved. Each fix RE-DERIVES the sentence from the live tree rather than patching the visible
symptom: the "3c runner not yet built" sites are rewritten to name the runners that now invoke
`check-structural.mjs` (not merely to delete "not yet"), and the honest bounds they carried
(nothing fires at write time) are preserved rather than dropped.

## What this is

An audited resync, run at the maintainer's direct request ("analyze, update README/CLAUDE.md, check
if docs need updating"). A six-agent audit verified every falsifiable doc claim against the live
tree; this increment applies only the CONFIRMED-stale corrections that are framing-independent.
It deliberately does NOT touch the four trusted docs (hook-denied, human-only — their issues are
reported to the human instead) and does NOT change the repo's adoptability framing.

## Scope of correction (why each file)

- Shipped surface (bumps SKILLS_VERSION, patch): `eval-format.md` still calls `check-structural.mjs`
"the next increment"; `finding-shape.md` still calls the 3c runner "not yet built" and cites
pre-relocation `floor/…` paths plus a pre-move `features/trust-fence` example;
`pharn/floor/README.md` says "nothing in the build loop invokes it automatically yet" (stale — the
verify/eval stages do) and understates the hook matcher; the two lenses repeat the "3c not yet
wired" bound.
- Repo-meta (no bump): CLAUDE.md (7-gate list vs the live 8; "21 tagged" vs 32; the four-constants
enumeration omits `GEN`; a present-tense `pharn` CLI that does not exist); README.md line 60's
"authorization checked" inside a "can guarantee" sentence (the repo's own security griller records
that floor candidate as REJECTED); SECURITY.md pointing at the deliberately inert `package.json`
version; CONTRIBUTING.md's `pharn/floor/` bump sentence missing the `*.test.mjs` carve-out; both
GitHub templates citing pre-relocation `floor/validate.mjs` and pre-rename `/plan`-style commands;
`.dev/features/README.md` placing modules at the repo root.

## Files

- `.dev/features/docs-drift-resync/PLAN.md` — this record
- `README.md` — line-60 guarantee triple; version badge (bump)
- `CLAUDE.md` — gate list, tagged count, four-constants enumeration, planned-CLI tense
- `SECURITY.md` — version-of-record sentence
- `CONTRIBUTING.md` — `pharn/floor/` bump sentence test-file carve-out
- `.github/PULL_REQUEST_TEMPLATE.md` — gate list, floor path, trusted-doc paths
- `.github/ISSUE_TEMPLATE/bug_report.md` — command names, floor path, module path
- `.dev/features/README.md` — module locations under `pharn/`
- `pharn/pharn-contracts/eval-format.md` — checker landed; `pharn/floor/` spellings
- `pharn/pharn-contracts/finding-shape.md` — 3c landed; path spellings; trust-fence example path
- `pharn/floor/README.md` — invokers named; hook matcher
- `pharn/pharn-review/input-validation/input-validation.md` — 3c bound re-derived
- `pharn/pharn-review/hallucinated-api/hallucinated-api.md` — 3c bound re-derived
- `SKILLS_VERSION` — 2.7.13 → 2.7.14 (patch: corrections to already-shipped bytes)
- `CHANGELOG.md` — the matching entry
4 changes: 2 additions & 2 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ A clear description of the bug.

## Which part of PHARN

- **Artifact**: <!-- e.g. /plan, /build, /review, the floor (floor/validate.mjs), the write-guard hook, a capability under pharn-review/, or a rule_id -->
- **Artifact**: <!-- e.g. /pharn-plan, /pharn-build, /pharn-review (or a /pharn-dev-* command), the floor (pharn/floor/validate.mjs), the write-guard hook, a capability under pharn/pharn-review/, or a rule_id -->
- **PHARN version** (`SKILLS_VERSION`):

## Steps to reproduce
Expand All @@ -37,4 +37,4 @@ What actually happened. Paste the relevant Claude Code output (redact any secret

## Additional context

Anything else that helps — the relevant files, and the floor output (`node floor/validate.mjs .`).
Anything else that helps — the relevant files, and the floor output (`node pharn/floor/validate.mjs .`).
6 changes: 3 additions & 3 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@ Closes #<!-- issue number -->

## Checklist

- [ ] I read the four trusted docs (`CONSTITUTION.md` → `ARCHITECTURE.md` → `THREAT-MODEL.md` → `LIMITS.md`) and did **not** edit them — they are human-only and hook-protected.
- [ ] I read the four trusted docs (`pharn/CONSTITUTION.md` → `pharn/ARCHITECTURE.md` → `THREAT-MODEL.md` → `LIMITS.md`) and did **not** edit them — they are human-only and hook-protected.
- [ ] No constitutional principle (P0–P7) is violated.
- [ ] If this adds a Capability: it has a `role:` and ships evals (`evals/cases/` + `evals/expected/`), and every `enforces` rule_id is produced by ≥1 eval case (P1).
- [ ] Findings cite a file-qualified `rule_id`; no guarantee is claimed without a floor reduction, otherwise it is labeled `advisory` (P0).
- [ ] `CHANGELOG.md` updated; `SKILLS_VERSION` bumped if the released surface changed.

## Quality gates

- [ ] `npm run check` passes locally (`format:check` + `lint` + `lint:md` + `test`).
- [ ] `node floor/validate.mjs .` is GREEN.
- [ ] `npm run check` passes locally (every gate `package.json`'s `scripts.check` chains — the authoritative list).
- [ ] `node pharn/floor/validate.mjs .` is GREEN.

## Notes for the reviewer

Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),

### Fixed

- **Shipped prose that still described landed floor machinery as unbuilt, re-derived against the live tree** (`SKILLS_VERSION` 2.7.13 → **2.7.14**, patch). A full-tree claim audit (every falsifiable doc sentence checked against the live repo) found the shipped surface carrying "not yet built" claims that had been true once and false since: [`pharn/pharn-contracts/eval-format.md`](./pharn/pharn-contracts/eval-format.md) still called the `structural[]` checker "the **next increment**" (it landed as `pharn/floor/check-structural.mjs`); [`pharn/pharn-contracts/finding-shape.md`](./pharn/pharn-contracts/finding-shape.md) still said "no runner yet invokes it … increment **3c, not yet built**" (3c landed as `/pharn-dev-eval` via `.dev/floor/check-variance.mjs`, and `/pharn-verify` / `/pharn-dev-verify` invoke the checker per committed eval pair); [`pharn/floor/README.md`](./pharn/floor/README.md) said "nothing in the build loop invokes it automatically yet" and understated the hook matcher (the live wiring is `Write|Edit|MultiEdit|NotebookEdit`); the `input-validation` and `hallucinated-api` lenses repeated the "3c not yet wired" bound. Each site was **re-derived, not deleted** (L25): the bound that survives — nothing fires at write time; the enforcement moment is the verify/eval stage — is kept, and the invokers that now exist are named. The same pass corrected pre-relocation `floor/…` path spellings and a pre-move `features/trust-fence` example inside the shipped contracts. Repo-meta corrections rode along without bumping: `CLAUDE.md`'s seven-gate aggregate list vs the live eight (`check:contributing` was missing — the exact drift class `check-contributing-gates` guards, one file over), its stale "21 tagged" count (now count-free per P6), its four-constants enumeration (the pinned set is `{CANON_PATH, OUT_PATH, GEN, REGEN}`; the absent-canon semantics are pinned separately as divergent functions), and its present-tense `pharn` CLI (specified, not built); `README.md`'s "authorization checked" inside a "can guarantee" sentence (the security griller records that floor candidate as **REJECTED**, so the sentence now names the writes-scope guarantee instead); `SECURITY.md` pointing at the deliberately inert `package.json` version; `CONTRIBUTING.md`'s missing test-file carve-out on the `pharn/floor/` bump rule; both GitHub templates' pre-relocation `floor/validate.mjs` paths, the PR template's four-of-eight gate list, and the bug template's pre-rename `/plan`-style command names; `.dev/features/README.md`'s modules-at-repo-root claim. Audit + scope record: `.dev/features/docs-drift-resync/PLAN.md`.
- **Six confirmed product-floor defects, each reproduced live before it was scoped and each now pinned by a test that fails without its fix** (`SKILLS_VERSION` 2.7.12 → 2.7.13). None was hypothetical; all six came from an external review and were re-derived against the live tree, which mattered — one of the six reports carried a reproduce command that did not reproduce the defect it described.
- **A prototype-walking gate comparison let a real gate-set mismatch pass silently** ([`pharn/floor/check-regress.mjs`](./pharn/floor/check-regress.mjs)). The mismatch check used `k in obj`, which walks the prototype chain, so a gate id colliding with an `Object.prototype` member (`toString`, `valueOf`) read as PRESENT in a map that did not have it. The extra failing gate was treated as shared and the verdict came back `no-regressions` at exit 0, where a gate-set mismatch is contractually `inconclusive`/exit 2 — a silent pass inside the checker whose entire promise is that there is never one. Now `Object.hasOwn`, which is the own-property test `lessons-learned` **L15** already prescribes; this is that lesson recurring.
- **A UTF-8 BOM defeated every frontmatter anchor in the floor** — and the fix is a new shared core, [`pharn/floor/frontmatter-core.mjs`](./pharn/floor/frontmatter-core.mjs), because the defect was a **set** problem. `FM_RE` had been copy-pasted byte-identically into **six** checkers (`check-spec`, `check-loop-record`, `check-plan-lessons`, `check-plan-spec-agree`, `check-ship-briefing`, `render-ship-briefing`) with nothing ranging over them, so a byte-valid file written by a BOM-emitting editor RED'd with "no YAML frontmatter block" in all six, and fixing whichever one surfaced would have left five broken with no test able to tell. That is **L31** exactly. The anchor now has one definition, the BOM strip happens at **read** (beside the existing CRLF fold, so the two input-normalisation defences live together — **L25**), and a consumer-set pin asserts no checker re-declares its own anchor, imports the core, and actually calls `stripBom`. **Narrowed:** exactly one leading `U+FEFF` is stripped, only at offset 0 — a doubled BOM stays malformed, and a genuinely frontmatter-less file still REDs. The fix removes a FALSE red; it creates no path to a false GREEN.
Expand Down
16 changes: 10 additions & 6 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,9 @@ Packaging later = "ship root minus `.dev/`". `.dev/` (committed apparatus) is un
## SKILLS_VERSION discipline (versioning the shipped surface)

`SKILLS_VERSION` (repo root) versions the **product surface** — the bytes a PHARN user receives (the
`pharn` CLI installs them; `pharn status` / `pharn update` compare a user's install against this
file). It does **not** version the build apparatus.
planned `pharn` CLI — specified, **not built**; no installer exists yet — would install them, and its
`pharn status` / `pharn update` would compare a user's install against this file). It does **not**
version the build apparatus.

- **Any change that alters product-surface bytes MUST bump `SKILLS_VERSION` and add a `CHANGELOG.md`
entry — prose-only edits included.** A clarified `/pharn-*` command step, a reworded contract, or a
Expand Down Expand Up @@ -278,7 +279,8 @@ echo '{"tool_name":"Write","tool_input":{"file_path":"pharn/pharn-core/rules/x.m
- **Dev tooling is real; the methodology stays stdlib-only.** The floor, the hook, and the commands
have **zero runtime dependencies** (Node stdlib; Node 24). The repo carries **dev-only**
devDependencies (ESLint, Prettier, markdownlint) wired as npm scripts: `npm run check`
(`format:check` + `lint` + `lint:md` + `docs:check` + `check:markers` + `check:badge` + `test`) is the
(`format:check` + `lint` + `lint:md` + `docs:check` + `check:markers` + `check:badge` +
`check:contributing` + `test`) is the
aggregate gate, and `npm test` runs
`node --test` over the hook, product-floor, and dev-floor suites (`.claude/hooks/*.test.cjs` +
`pharn/floor/*.test.mjs` + `.dev/floor/*.test.mjs`) — **green** at this writing; read the count live
Expand Down Expand Up @@ -501,7 +503,8 @@ framework-specific`), via the first-match-wins procedure in `pharn/ARCHITECTURE.
never against the index. The index's `type` / `concepts` columns are model-drafted values a human
ratified at the promote gate, so **"typed `floor`" never means "about the floor"** — selecting on them
is advisory context selection. **Every dev canon entry is now tagged** — the legacy L1–L17 were
retro-tagged, so the index renders `21 tagged · 0 malformed · 0 untagged` and **both** absence markers
retro-tagged, so the index renders every entry tagged (`0 malformed · 0 untagged`; read the live
counts from `docs/lessons-index.md`, never from this doc — P6) and **both** absence markers
are now unexpected: a `-` means no tag line, i.e. an entry that reached canon without the promote
gate's `type`/`concepts`; a `?` means a tag line is present but **failed its gate**. Read that entry in
canon and flag it either way. (The PRODUCT twin keeps the benign reading of `-` on purpose — a user's
Expand All @@ -523,8 +526,9 @@ framework-specific`), via the first-match-wins procedure in `pharn/ARCHITECTURE.
advisory: skipping it just yields a `STALE` the next plan degrades on.
- **The two cores are deliberate SEPARATE COPIES** (`pharn/floor/lessons-index-core.mjs` vs
`.dev/floor/lessons-index-core.mjs`), the `check-provenance.mjs` precedent. Four constants diverge on
purpose — `CANON_PATH`, `OUT_PATH`, `REGEN`, and the **absent/empty-canon semantics** (the product
copy treats no-canon as a benign no-op where the dev copy throws). ✧ tests in
purpose — `CANON_PATH`, `OUT_PATH`, `GEN`, `REGEN` — as does the **absent/empty-canon semantics**
(a divergent function, pinned separately: the product copy treats no-canon as a benign no-op where
the dev copy throws). ✧ tests in
`.dev/floor/lessons-index-core.test.mjs` pin **both** halves: every shared constant must AGREE and
those four must DIFFER. The pin lives on the dev side because a user's install ships `pharn/floor/`
**without** `.dev/`, so the dependency may only point `.dev/` → `pharn/`; the honest consequence is
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ When you add a PHARN capability, follow the conventions in [`CLAUDE.md`](./CLAUD
The repo separates the **product** (what a user receives) from the **build apparatus** (what a contributor uses), in the filesystem and in command names:

- **`.dev/`** holds the apparatus — `.dev/floor/` (dev-only checkers + tests), `.dev/features/` (build-loop audit trails), `.dev/memory-bank/`. It is committed but is **not** what a user receives.
- **The product lives under `pharn/`** — `pharn/pharn-contracts/`, `pharn/pharn-core/`, `pharn/pharn-pipeline/`, `pharn/pharn-review/`, and the **product floor** `pharn/floor/` (the checkers the `/pharn-*` commands run on a user's code) — plus a root `features/` for product-pipeline artifacts. `pharn/floor/validate.mjs` **is** that product floor, and it excludes `.dev/**` wholesale: it scans the product surface only. So a change under `pharn/floor/` **ships** and is not apparatus — which means it must bump [`SKILLS_VERSION`](./SKILLS_VERSION) and add a `CHANGELOG.md` entry; see [`CLAUDE.md`](./CLAUDE.md) ("SKILLS_VERSION discipline") for what counts as product surface and how big the bump is.
- **The product lives under `pharn/`** — `pharn/pharn-contracts/`, `pharn/pharn-core/`, `pharn/pharn-pipeline/`, `pharn/pharn-review/`, and the **product floor** `pharn/floor/` (the checkers the `/pharn-*` commands run on a user's code) — plus a root `features/` for product-pipeline artifacts. `pharn/floor/validate.mjs` **is** that product floor, and it excludes `.dev/**` wholesale: it scans the product surface only. So a change under `pharn/floor/` **ships** and is not apparatus — which means it must bump [`SKILLS_VERSION`](./SKILLS_VERSION) and add a `CHANGELOG.md` entry (the one exception: the checkers' `*.test.mjs` files never ship, so a test-only change does not bump); see [`CLAUDE.md`](./CLAUDE.md) ("SKILLS_VERSION discipline") for what counts as product surface and how big the bump is.
- **Two floors exist on purpose:** `.dev/floor/` is contributor tooling that never ships; `pharn/floor/` ships. [`CLAUDE.md`](./CLAUDE.md) ("Repo layout — the dev/product boundary") is the authoritative split — defer to it rather than to this summary.
- **Commands split by name prefix** (they cannot move out of `.claude/`): build-apparatus commands are **`pharn-dev-*`** (`pharn-dev-plan`, `-build`, …); product commands are **`pharn-*`** without `-dev-`. The prefix is naming/UX only — **not** an access gate.

Expand Down
Loading