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
140 changes: 140 additions & 0 deletions .dev/features/diff-unreadable-partition/GRILL.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
# GRILL — diff-unreadable-partition (ADVISORY)

Plan under interrogation: `.dev/features/diff-unreadable-partition/PLAN.md`.
**Spec-hash check: MATCH** — recomputed `sha256(ARCHITECTURE.md)` =
`bca940a5ad247c120e6d8a3acba119d0d8df51dca275964d0e54c48d729d3c4e`, equal to the plan's
`spec_content_hash`. (Computation is floor-grade; here it only warns — the block on drift is
`/pharn-dev-build`'s gate, fix #4.)

**Registered grillers: 0** — `node .dev/floor/count-grillers.mjs .` → `{"registered":0,"grillers":[]}`.
This repo ships no `role: griller` capability of its own (the stage command's own frontmatter lives
under the excluded `.claude/commands/` and correctly does not register). Membership is FLOOR; the
empty set means the built-in Step 2 axes below are the whole interrogation. Recorded honestly rather
than implying a griller fleet ran.

## Findings

### Axis: guarantee-audit completeness (P0)

```yaml
- type: FINDING
rule_id: "P0"
severity: important
file: ".dev/features/diff-unreadable-partition/PLAN.md:201"
problem: "The plan calls the Inv-7 source-scan the increment's 'only new floor primitive' and then, in the same sentence, says it is not a checker — two incompatible labels for one thing, which is exactly the ambiguity P0 exists to remove."
evidence: "This is the increment's only *new* floor primitive, and it is a test assertion in the existing suite, not a new checker."
```

> **Interrogation.** `ARCHITECTURE.md §2` enumerates the floor as regex/enum allowlists, path
> containment, schema exact-match, network guards. A vitest assertion that regex-scans `diff.ts` for
> two import specifiers **is** a regex membership test, and it runs inside `npm test`, which
> `/pharn-dev-verify` treats as a floor gate — so the reduction is real. What is **not** real is the
> word "primitive": no file lands in `.dev/floor/`, and nothing outside the vitest run enforces it.
> The honest label is *floor-reducible assertion carried by the existing suite*. Left as-is, a later
> reader could cite this plan as precedent for "a test I wrote is a floor primitive."

### Axis: eval coverage / structural-vs-semantic (P1, `eval-format.md`)

```yaml
- type: FINDING
rule_id: "P1"
severity: important
file: ".dev/features/diff-unreadable-partition/PLAN.md:115"
problem: "The plan fixes the new subsection's position as third, after MISSING, but no listed eval asserts the ordering — Inv 8 pins only the absent case, so a build that renders UNREADABLE first would pass every planned test."
evidence: "Rendered inside the **existing DRIFT note**, as the **third** subsection after MISSING (mirroring `SKIP_ORDER`'s placement), and **only when non-empty**"
```

> **Interrogation.** This is the classic shape of a plan claim with no eval behind it: a specific,
> checkable ordering asserted in prose and then never tested. P5 also bears on it — deterministic
> output order is a stated repo value (`SKIP_ORDER` exists precisely so skip groups never depend on map
> iteration order), so its status-side twin deserves the same pin. Cheap fix: one assertion on a
> DRIFT body containing all three subsections, comparing `indexOf` positions.

```yaml
- type: FINDING
rule_id: "P1"
severity: minor
file: ".dev/features/diff-unreadable-partition/PLAN.md:145"
problem: "The reason-string assertion policy asserts /symlink/ for symlinks but only non-emptiness for the directory and ENOTDIR cases, so a build that returned the symlink reason for a directory would satisfy every planned assertion."
evidence: "The directory/ENOTDIR cases assert only that `reason` is a non-empty string (the `kind`-equivalent), leaving that copy free."
```

> **Interrogation.** The plan's justification for the asymmetry is sound (evidence 2 is *about* the
> reason; evidences 1 and 3 are about the partition). But the asymmetry buys a real hole: nothing
> distinguishes the three non-symlink reasons from each other. Weighing it: the partition is the
> contract and the partition **is** pinned, so this is genuinely minor — recorded so the human can
> decide whether display-copy coupling is worth closing it.

### Axis: honest scope / no speculation (P7)

```yaml
- type: FINDING
rule_id: "P7"
severity: minor
file: ".dev/features/diff-unreadable-partition/PLAN.md:57"
problem: "The build prompt scoped status.ts to 'inside-file: printDriftSection + the strict condition', but the plan's type-only InstallDiff import adds a top-level line outside both, widening the whitelist without naming that it did."
evidence: "`src/commands/status.ts` — `printDriftSection` gains the third subsection + the clean-bill condition gains `unreadable`; the `--strict` condition at `:94-96` gains `|| result.unreadable.length`."
```

> **Interrogation.** The widening is trivial and defensible — an erased `import type` touches no
> runtime behavior and leaves the VERSION/MODELS notes, fetch/cleanup flow, and re-add hint
> byte-equivalent as required. The finding is not that the change is wrong; it is that the plan made a
> scope decision **silently**. P7 asks for limits to be labeled as limits, and a whitelist quietly
> widened by one line is still a whitelist quietly widened.

```yaml
- type: FINDING
rule_id: "P4"
severity: minor
file: ".dev/features/diff-unreadable-partition/PLAN.md:65"
problem: "The plan names CHANGELOG.md as a file to edit but never says which section receives the entry, and the working tree's staged version bump to 0.4.0 makes 'Unreleased' versus a 0.4.0 heading a live ambiguity rather than a hypothetical one."
evidence: "- `CHANGELOG.md` — the four user-facing changes. layer: docs"
```

> **Interrogation.** Resolved against live state this run rather than left as a guess (P6):
> `CHANGELOG.md:8` is `## [Unreleased]` with a `### Fixed` subsection already open, and no `0.4.0`
> heading exists despite `package.json` reading `0.4.0` in the staged index. So `[Unreleased] → Fixed`
> is the correct target. Recorded because the plan should have said so.

### Axis: trust propagation (P2)

**No findings.** The plan's trust audit is verifiable and was verified: the four `reason` strings are
literals in `src/lib/apply-update.ts:54,58,61,66`, never derived from fetched bytes, so rendering them
adds no untrusted text to the terminal. The `rel` paths already flowed through
`collectExpectedInstallPaths`. Taint does not widen.

### Axis: one axis of change / no sibling imports (P3)

**No findings.** `diff.ts` → `apply-update.ts` is lib→lib, not command→command or step→step, so the
P3 prohibition is not engaged. Both files keep one reason to change (`diff.ts`: how drift is computed;
`apply-update.ts`: untouched). The plan's refusal to relocate `readDiskState` on a two-consumer count
is correctly conservative.

### Axis: determinism (P5)

**No findings.** The four-way branch is membership over a closed union; the `<`/`>` sort choice over
`localeCompare` is the right call and the plan states the reason. Terminal fallback does not end in a
guess.

## Summary

The plan is unusually well-grounded — it re-verified its own build prompt and corrected three of its
premises rather than inheriting them, and its guarantee audit does the hard part (noticing that
deleting `diff.ts`'s `safeJoin` call leaves the containment claim needing a new home, then pointing at
the two callees that provide it). The concerns above are about **labeling and eval coverage, not
design**: one overstated floor label (P0), one prose claim with no eval behind it (the subsection
ordering — the only finding likely to survive into the built artifact as a real gap), one
display-copy hole the plan already reasoned about, and two unstated scope/target decisions.

The single concern most worth acting on before `/pharn-dev-build` is the **ordering eval**: it is one
assertion, it closes a claim the plan makes explicitly, and it matches an ordering discipline this repo
already enforces on the write side.

## Verdict

**ADVISORY VERDICT: 5 concerns raised (0 blocking-severity, 2 important, 3 minor) — for the human to
weigh before `/pharn-dev-build`.**

This grill-log is **advisory end-to-end**. It gates nothing: `/pharn-dev-build` proceeds regardless, and
the deterministic backstops remain where they always were — the spec-hash gate, the unresolved-open-
questions gate, and `.dev/floor/validate.mjs`. Nothing here should be read as "the plan passed."
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
fix: pharn status reports unreadable paths instead of crashing or misclassifying them by PrzemekGalarowicz · Pull Request #82 · pharn-dev/pharn-cli · GitHub
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
140 changes: 140 additions & 0 deletions .dev/features/diff-unreadable-partition/GRILL.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
# GRILL — diff-unreadable-partition (ADVISORY)

Plan under interrogation: `.dev/features/diff-unreadable-partition/PLAN.md`.
**Spec-hash check: MATCH** — recomputed `sha256(ARCHITECTURE.md)` =
`bca940a5ad247c120e6d8a3acba119d0d8df51dca275964d0e54c48d729d3c4e`, equal to the plan's
`spec_content_hash`. (Computation is floor-grade; here it only warns — the block on drift is
`/pharn-dev-build`'s gate, fix #4.)

**Registered grillers: 0** — `node .dev/floor/count-grillers.mjs .` → `{"registered":0,"grillers":[]}`.
This repo ships no `role: griller` capability of its own (the stage command's own frontmatter lives
under the excluded `.claude/commands/` and correctly does not register). Membership is FLOOR; the
empty set means the built-in Step 2 axes below are the whole interrogation. Recorded honestly rather
than implying a griller fleet ran.

## Findings

### Axis: guarantee-audit completeness (P0)

```yaml
- type: FINDING
rule_id: "P0"
severity: important
file: ".dev/features/diff-unreadable-partition/PLAN.md:201"
problem: "The plan calls the Inv-7 source-scan the increment's 'only new floor primitive' and then, in the same sentence, says it is not a checker — two incompatible labels for one thing, which is exactly the ambiguity P0 exists to remove."
evidence: "This is the increment's only *new* floor primitive, and it is a test assertion in the existing suite, not a new checker."
```

> **Interrogation.** `ARCHITECTURE.md §2` enumerates the floor as regex/enum allowlists, path
> containment, schema exact-match, network guards. A vitest assertion that regex-scans `diff.ts` for
> two import specifiers **is** a regex membership test, and it runs inside `npm test`, which
> `/pharn-dev-verify` treats as a floor gate — so the reduction is real. What is **not** real is the
> word "primitive": no file lands in `.dev/floor/`, and nothing outside the vitest run enforces it.
> The honest label is *floor-reducible assertion carried by the existing suite*. Left as-is, a later
> reader could cite this plan as precedent for "a test I wrote is a floor primitive."

### Axis: eval coverage / structural-vs-semantic (P1, `eval-format.md`)

```yaml
- type: FINDING
rule_id: "P1"
severity: important
file: ".dev/features/diff-unreadable-partition/PLAN.md:115"
problem: "The plan fixes the new subsection's position as third, after MISSING, but no listed eval asserts the ordering — Inv 8 pins only the absent case, so a build that renders UNREADABLE first would pass every planned test."
evidence: "Rendered inside the **existing DRIFT note**, as the **third** subsection after MISSING (mirroring `SKIP_ORDER`'s placement), and **only when non-empty**"
```

> **Interrogation.** This is the classic shape of a plan claim with no eval behind it: a specific,
> checkable ordering asserted in prose and then never tested. P5 also bears on it — deterministic
> output order is a stated repo value (`SKIP_ORDER` exists precisely so skip groups never depend on map
> iteration order), so its status-side twin deserves the same pin. Cheap fix: one assertion on a
> DRIFT body containing all three subsections, comparing `indexOf` positions.

```yaml
- type: FINDING
rule_id: "P1"
severity: minor
file: ".dev/features/diff-unreadable-partition/PLAN.md:145"
problem: "The reason-string assertion policy asserts /symlink/ for symlinks but only non-emptiness for the directory and ENOTDIR cases, so a build that returned the symlink reason for a directory would satisfy every planned assertion."
evidence: "The directory/ENOTDIR cases assert only that `reason` is a non-empty string (the `kind`-equivalent), leaving that copy free."
```

> **Interrogation.** The plan's justification for the asymmetry is sound (evidence 2 is *about* the
> reason; evidences 1 and 3 are about the partition). But the asymmetry buys a real hole: nothing
> distinguishes the three non-symlink reasons from each other. Weighing it: the partition is the
> contract and the partition **is** pinned, so this is genuinely minor — recorded so the human can
> decide whether display-copy coupling is worth closing it.

### Axis: honest scope / no speculation (P7)

```yaml
- type: FINDING
rule_id: "P7"
severity: minor
file: ".dev/features/diff-unreadable-partition/PLAN.md:57"
problem: "The build prompt scoped status.ts to 'inside-file: printDriftSection + the strict condition', but the plan's type-only InstallDiff import adds a top-level line outside both, widening the whitelist without naming that it did."
evidence: "`src/commands/status.ts` — `printDriftSection` gains the third subsection + the clean-bill condition gains `unreadable`; the `--strict` condition at `:94-96` gains `|| result.unreadable.length`."
```

> **Interrogation.** The widening is trivial and defensible — an erased `import type` touches no
> runtime behavior and leaves the VERSION/MODELS notes, fetch/cleanup flow, and re-add hint
> byte-equivalent as required. The finding is not that the change is wrong; it is that the plan made a
> scope decision **silently**. P7 asks for limits to be labeled as limits, and a whitelist quietly
> widened by one line is still a whitelist quietly widened.

```yaml
- type: FINDING
rule_id: "P4"
severity: minor
file: ".dev/features/diff-unreadable-partition/PLAN.md:65"
problem: "The plan names CHANGELOG.md as a file to edit but never says which section receives the entry, and the working tree's staged version bump to 0.4.0 makes 'Unreleased' versus a 0.4.0 heading a live ambiguity rather than a hypothetical one."
evidence: "- `CHANGELOG.md` — the four user-facing changes. layer: docs"
```

> **Interrogation.** Resolved against live state this run rather than left as a guess (P6):
> `CHANGELOG.md:8` is `## [Unreleased]` with a `### Fixed` subsection already open, and no `0.4.0`
> heading exists despite `package.json` reading `0.4.0` in the staged index. So `[Unreleased] → Fixed`
> is the correct target. Recorded because the plan should have said so.

### Axis: trust propagation (P2)

**No findings.** The plan's trust audit is verifiable and was verified: the four `reason` strings are
literals in `src/lib/apply-update.ts:54,58,61,66`, never derived from fetched bytes, so rendering them
adds no untrusted text to the terminal. The `rel` paths already flowed through
`collectExpectedInstallPaths`. Taint does not widen.

### Axis: one axis of change / no sibling imports (P3)

**No findings.** `diff.ts` → `apply-update.ts` is lib→lib, not command→command or step→step, so the
P3 prohibition is not engaged. Both files keep one reason to change (`diff.ts`: how drift is computed;
`apply-update.ts`: untouched). The plan's refusal to relocate `readDiskState` on a two-consumer count
is correctly conservative.

### Axis: determinism (P5)

**No findings.** The four-way branch is membership over a closed union; the `<`/`>` sort choice over
`localeCompare` is the right call and the plan states the reason. Terminal fallback does not end in a
guess.

## Summary

The plan is unusually well-grounded — it re-verified its own build prompt and corrected three of its
premises rather than inheriting them, and its guarantee audit does the hard part (noticing that
deleting `diff.ts`'s `safeJoin` call leaves the containment claim needing a new home, then pointing at
the two callees that provide it). The concerns above are about **labeling and eval coverage, not
design**: one overstated floor label (P0), one prose claim with no eval behind it (the subsection
ordering — the only finding likely to survive into the built artifact as a real gap), one
display-copy hole the plan already reasoned about, and two unstated scope/target decisions.

The single concern most worth acting on before `/pharn-dev-build` is the **ordering eval**: it is one
assertion, it closes a claim the plan makes explicitly, and it matches an ordering discipline this repo
already enforces on the write side.

## Verdict

**ADVISORY VERDICT: 5 concerns raised (0 blocking-severity, 2 important, 3 minor) — for the human to
weigh before `/pharn-dev-build`.**

This grill-log is **advisory end-to-end**. It gates nothing: `/pharn-dev-build` proceeds regardless, and
the deterministic backstops remain where they always were — the spec-hash gate, the unresolved-open-
questions gate, and `.dev/floor/validate.mjs`. Nothing here should be read as "the plan passed."
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix: pharn status reports unreadable paths instead of crashing or misclassifying them by PrzemekGalarowicz · Pull Request #82 · pharn-dev/pharn-cli · GitHub
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
140 changes: 140 additions & 0 deletions .dev/features/diff-unreadable-partition/GRILL.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
# GRILL — diff-unreadable-partition (ADVISORY)

Plan under interrogation: `.dev/features/diff-unreadable-partition/PLAN.md`.
**Spec-hash check: MATCH** — recomputed `sha256(ARCHITECTURE.md)` =
`bca940a5ad247c120e6d8a3acba119d0d8df51dca275964d0e54c48d729d3c4e`, equal to the plan's
`spec_content_hash`. (Computation is floor-grade; here it only warns — the block on drift is
`/pharn-dev-build`'s gate, fix #4.)

**Registered grillers: 0** — `node .dev/floor/count-grillers.mjs .` → `{"registered":0,"grillers":[]}`.
This repo ships no `role: griller` capability of its own (the stage command's own frontmatter lives
under the excluded `.claude/commands/` and correctly does not register). Membership is FLOOR; the
empty set means the built-in Step 2 axes below are the whole interrogation. Recorded honestly rather
than implying a griller fleet ran.

## Findings

### Axis: guarantee-audit completeness (P0)

```yaml
- type: FINDING
rule_id: "P0"
severity: important
file: ".dev/features/diff-unreadable-partition/PLAN.md:201"
problem: "The plan calls the Inv-7 source-scan the increment's 'only new floor primitive' and then, in the same sentence, says it is not a checker — two incompatible labels for one thing, which is exactly the ambiguity P0 exists to remove."
evidence: "This is the increment's only *new* floor primitive, and it is a test assertion in the existing suite, not a new checker."
```

> **Interrogation.** `ARCHITECTURE.md §2` enumerates the floor as regex/enum allowlists, path
> containment, schema exact-match, network guards. A vitest assertion that regex-scans `diff.ts` for
> two import specifiers **is** a regex membership test, and it runs inside `npm test`, which
> `/pharn-dev-verify` treats as a floor gate — so the reduction is real. What is **not** real is the
> word "primitive": no file lands in `.dev/floor/`, and nothing outside the vitest run enforces it.
> The honest label is *floor-reducible assertion carried by the existing suite*. Left as-is, a later
> reader could cite this plan as precedent for "a test I wrote is a floor primitive."

### Axis: eval coverage / structural-vs-semantic (P1, `eval-format.md`)

```yaml
- type: FINDING
rule_id: "P1"
severity: important
file: ".dev/features/diff-unreadable-partition/PLAN.md:115"
problem: "The plan fixes the new subsection's position as third, after MISSING, but no listed eval asserts the ordering — Inv 8 pins only the absent case, so a build that renders UNREADABLE first would pass every planned test."
evidence: "Rendered inside the **existing DRIFT note**, as the **third** subsection after MISSING (mirroring `SKIP_ORDER`'s placement), and **only when non-empty**"
```

> **Interrogation.** This is the classic shape of a plan claim with no eval behind it: a specific,
> checkable ordering asserted in prose and then never tested. P5 also bears on it — deterministic
> output order is a stated repo value (`SKIP_ORDER` exists precisely so skip groups never depend on map
> iteration order), so its status-side twin deserves the same pin. Cheap fix: one assertion on a
> DRIFT body containing all three subsections, comparing `indexOf` positions.

```yaml
- type: FINDING
rule_id: "P1"
severity: minor
file: ".dev/features/diff-unreadable-partition/PLAN.md:145"
problem: "The reason-string assertion policy asserts /symlink/ for symlinks but only non-emptiness for the directory and ENOTDIR cases, so a build that returned the symlink reason for a directory would satisfy every planned assertion."
evidence: "The directory/ENOTDIR cases assert only that `reason` is a non-empty string (the `kind`-equivalent), leaving that copy free."
```

> **Interrogation.** The plan's justification for the asymmetry is sound (evidence 2 is *about* the
> reason; evidences 1 and 3 are about the partition). But the asymmetry buys a real hole: nothing
> distinguishes the three non-symlink reasons from each other. Weighing it: the partition is the
> contract and the partition **is** pinned, so this is genuinely minor — recorded so the human can
> decide whether display-copy coupling is worth closing it.

### Axis: honest scope / no speculation (P7)

```yaml
- type: FINDING
rule_id: "P7"
severity: minor
file: ".dev/features/diff-unreadable-partition/PLAN.md:57"
problem: "The build prompt scoped status.ts to 'inside-file: printDriftSection + the strict condition', but the plan's type-only InstallDiff import adds a top-level line outside both, widening the whitelist without naming that it did."
evidence: "`src/commands/status.ts` — `printDriftSection` gains the third subsection + the clean-bill condition gains `unreadable`; the `--strict` condition at `:94-96` gains `|| result.unreadable.length`."
```

> **Interrogation.** The widening is trivial and defensible — an erased `import type` touches no
> runtime behavior and leaves the VERSION/MODELS notes, fetch/cleanup flow, and re-add hint
> byte-equivalent as required. The finding is not that the change is wrong; it is that the plan made a
> scope decision **silently**. P7 asks for limits to be labeled as limits, and a whitelist quietly
> widened by one line is still a whitelist quietly widened.

```yaml
- type: FINDING
rule_id: "P4"
severity: minor
file: ".dev/features/diff-unreadable-partition/PLAN.md:65"
problem: "The plan names CHANGELOG.md as a file to edit but never says which section receives the entry, and the working tree's staged version bump to 0.4.0 makes 'Unreleased' versus a 0.4.0 heading a live ambiguity rather than a hypothetical one."
evidence: "- `CHANGELOG.md` — the four user-facing changes. layer: docs"
```

> **Interrogation.** Resolved against live state this run rather than left as a guess (P6):
> `CHANGELOG.md:8` is `## [Unreleased]` with a `### Fixed` subsection already open, and no `0.4.0`
> heading exists despite `package.json` reading `0.4.0` in the staged index. So `[Unreleased] → Fixed`
> is the correct target. Recorded because the plan should have said so.

### Axis: trust propagation (P2)

**No findings.** The plan's trust audit is verifiable and was verified: the four `reason` strings are
literals in `src/lib/apply-update.ts:54,58,61,66`, never derived from fetched bytes, so rendering them
adds no untrusted text to the terminal. The `rel` paths already flowed through
`collectExpectedInstallPaths`. Taint does not widen.

### Axis: one axis of change / no sibling imports (P3)

**No findings.** `diff.ts` → `apply-update.ts` is lib→lib, not command→command or step→step, so the
P3 prohibition is not engaged. Both files keep one reason to change (`diff.ts`: how drift is computed;
`apply-update.ts`: untouched). The plan's refusal to relocate `readDiskState` on a two-consumer count
is correctly conservative.

### Axis: determinism (P5)

**No findings.** The four-way branch is membership over a closed union; the `<`/`>` sort choice over
`localeCompare` is the right call and the plan states the reason. Terminal fallback does not end in a
guess.

## Summary

The plan is unusually well-grounded — it re-verified its own build prompt and corrected three of its
premises rather than inheriting them, and its guarantee audit does the hard part (noticing that
deleting `diff.ts`'s `safeJoin` call leaves the containment claim needing a new home, then pointing at
the two callees that provide it). The concerns above are about **labeling and eval coverage, not
design**: one overstated floor label (P0), one prose claim with no eval behind it (the subsection
ordering — the only finding likely to survive into the built artifact as a real gap), one
display-copy hole the plan already reasoned about, and two unstated scope/target decisions.

The single concern most worth acting on before `/pharn-dev-build` is the **ordering eval**: it is one
assertion, it closes a claim the plan makes explicitly, and it matches an ordering discipline this repo
already enforces on the write side.

## Verdict

**ADVISORY VERDICT: 5 concerns raised (0 blocking-severity, 2 important, 3 minor) — for the human to
weigh before `/pharn-dev-build`.**

This grill-log is **advisory end-to-end**. It gates nothing: `/pharn-dev-build` proceeds regardless, and
the deterministic backstops remain where they always were — the spec-hash gate, the unresolved-open-
questions gate, and `.dev/floor/validate.mjs`. Nothing here should be read as "the plan passed."
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix: pharn status reports unreadable paths instead of crashing or misclassifying them by PrzemekGalarowicz · Pull Request #82 · pharn-dev/pharn-cli · GitHub
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
140 changes: 140 additions & 0 deletions .dev/features/diff-unreadable-partition/GRILL.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
# GRILL — diff-unreadable-partition (ADVISORY)

Plan under interrogation: `.dev/features/diff-unreadable-partition/PLAN.md`.
**Spec-hash check: MATCH** — recomputed `sha256(ARCHITECTURE.md)` =
`bca940a5ad247c120e6d8a3acba119d0d8df51dca275964d0e54c48d729d3c4e`, equal to the plan's
`spec_content_hash`. (Computation is floor-grade; here it only warns — the block on drift is
`/pharn-dev-build`'s gate, fix #4.)

**Registered grillers: 0** — `node .dev/floor/count-grillers.mjs .` → `{"registered":0,"grillers":[]}`.
This repo ships no `role: griller` capability of its own (the stage command's own frontmatter lives
under the excluded `.claude/commands/` and correctly does not register). Membership is FLOOR; the
empty set means the built-in Step 2 axes below are the whole interrogation. Recorded honestly rather
than implying a griller fleet ran.

## Findings

### Axis: guarantee-audit completeness (P0)

```yaml
- type: FINDING
rule_id: "P0"
severity: important
file: ".dev/features/diff-unreadable-partition/PLAN.md:201"
problem: "The plan calls the Inv-7 source-scan the increment's 'only new floor primitive' and then, in the same sentence, says it is not a checker — two incompatible labels for one thing, which is exactly the ambiguity P0 exists to remove."
evidence: "This is the increment's only *new* floor primitive, and it is a test assertion in the existing suite, not a new checker."
```

> **Interrogation.** `ARCHITECTURE.md §2` enumerates the floor as regex/enum allowlists, path
> containment, schema exact-match, network guards. A vitest assertion that regex-scans `diff.ts` for
> two import specifiers **is** a regex membership test, and it runs inside `npm test`, which
> `/pharn-dev-verify` treats as a floor gate — so the reduction is real. What is **not** real is the
> word "primitive": no file lands in `.dev/floor/`, and nothing outside the vitest run enforces it.
> The honest label is *floor-reducible assertion carried by the existing suite*. Left as-is, a later
> reader could cite this plan as precedent for "a test I wrote is a floor primitive."

### Axis: eval coverage / structural-vs-semantic (P1, `eval-format.md`)

```yaml
- type: FINDING
rule_id: "P1"
severity: important
file: ".dev/features/diff-unreadable-partition/PLAN.md:115"
problem: "The plan fixes the new subsection's position as third, after MISSING, but no listed eval asserts the ordering — Inv 8 pins only the absent case, so a build that renders UNREADABLE first would pass every planned test."
evidence: "Rendered inside the **existing DRIFT note**, as the **third** subsection after MISSING (mirroring `SKIP_ORDER`'s placement), and **only when non-empty**"
```

> **Interrogation.** This is the classic shape of a plan claim with no eval behind it: a specific,
> checkable ordering asserted in prose and then never tested. P5 also bears on it — deterministic
> output order is a stated repo value (`SKIP_ORDER` exists precisely so skip groups never depend on map
> iteration order), so its status-side twin deserves the same pin. Cheap fix: one assertion on a
> DRIFT body containing all three subsections, comparing `indexOf` positions.

```yaml
- type: FINDING
rule_id: "P1"
severity: minor
file: ".dev/features/diff-unreadable-partition/PLAN.md:145"
problem: "The reason-string assertion policy asserts /symlink/ for symlinks but only non-emptiness for the directory and ENOTDIR cases, so a build that returned the symlink reason for a directory would satisfy every planned assertion."
evidence: "The directory/ENOTDIR cases assert only that `reason` is a non-empty string (the `kind`-equivalent), leaving that copy free."
```

> **Interrogation.** The plan's justification for the asymmetry is sound (evidence 2 is *about* the
> reason; evidences 1 and 3 are about the partition). But the asymmetry buys a real hole: nothing
> distinguishes the three non-symlink reasons from each other. Weighing it: the partition is the
> contract and the partition **is** pinned, so this is genuinely minor — recorded so the human can
> decide whether display-copy coupling is worth closing it.

### Axis: honest scope / no speculation (P7)

```yaml
- type: FINDING
rule_id: "P7"
severity: minor
file: ".dev/features/diff-unreadable-partition/PLAN.md:57"
problem: "The build prompt scoped status.ts to 'inside-file: printDriftSection + the strict condition', but the plan's type-only InstallDiff import adds a top-level line outside both, widening the whitelist without naming that it did."
evidence: "`src/commands/status.ts` — `printDriftSection` gains the third subsection + the clean-bill condition gains `unreadable`; the `--strict` condition at `:94-96` gains `|| result.unreadable.length`."
```

> **Interrogation.** The widening is trivial and defensible — an erased `import type` touches no
> runtime behavior and leaves the VERSION/MODELS notes, fetch/cleanup flow, and re-add hint
> byte-equivalent as required. The finding is not that the change is wrong; it is that the plan made a
> scope decision **silently**. P7 asks for limits to be labeled as limits, and a whitelist quietly
> widened by one line is still a whitelist quietly widened.

```yaml
- type: FINDING
rule_id: "P4"
severity: minor
file: ".dev/features/diff-unreadable-partition/PLAN.md:65"
problem: "The plan names CHANGELOG.md as a file to edit but never says which section receives the entry, and the working tree's staged version bump to 0.4.0 makes 'Unreleased' versus a 0.4.0 heading a live ambiguity rather than a hypothetical one."
evidence: "- `CHANGELOG.md` — the four user-facing changes. layer: docs"
```

> **Interrogation.** Resolved against live state this run rather than left as a guess (P6):
> `CHANGELOG.md:8` is `## [Unreleased]` with a `### Fixed` subsection already open, and no `0.4.0`
> heading exists despite `package.json` reading `0.4.0` in the staged index. So `[Unreleased] → Fixed`
> is the correct target. Recorded because the plan should have said so.

### Axis: trust propagation (P2)

**No findings.** The plan's trust audit is verifiable and was verified: the four `reason` strings are
literals in `src/lib/apply-update.ts:54,58,61,66`, never derived from fetched bytes, so rendering them
adds no untrusted text to the terminal. The `rel` paths already flowed through
`collectExpectedInstallPaths`. Taint does not widen.

### Axis: one axis of change / no sibling imports (P3)

**No findings.** `diff.ts` → `apply-update.ts` is lib→lib, not command→command or step→step, so the
P3 prohibition is not engaged. Both files keep one reason to change (`diff.ts`: how drift is computed;
`apply-update.ts`: untouched). The plan's refusal to relocate `readDiskState` on a two-consumer count
is correctly conservative.

### Axis: determinism (P5)

**No findings.** The four-way branch is membership over a closed union; the `<`/`>` sort choice over
`localeCompare` is the right call and the plan states the reason. Terminal fallback does not end in a
guess.

## Summary

The plan is unusually well-grounded — it re-verified its own build prompt and corrected three of its
premises rather than inheriting them, and its guarantee audit does the hard part (noticing that
deleting `diff.ts`'s `safeJoin` call leaves the containment claim needing a new home, then pointing at
the two callees that provide it). The concerns above are about **labeling and eval coverage, not
design**: one overstated floor label (P0), one prose claim with no eval behind it (the subsection
ordering — the only finding likely to survive into the built artifact as a real gap), one
display-copy hole the plan already reasoned about, and two unstated scope/target decisions.

The single concern most worth acting on before `/pharn-dev-build` is the **ordering eval**: it is one
assertion, it closes a claim the plan makes explicitly, and it matches an ordering discipline this repo
already enforces on the write side.

## Verdict

**ADVISORY VERDICT: 5 concerns raised (0 blocking-severity, 2 important, 3 minor) — for the human to
weigh before `/pharn-dev-build`.**

This grill-log is **advisory end-to-end**. It gates nothing: `/pharn-dev-build` proceeds regardless, and
the deterministic backstops remain where they always were — the spec-hash gate, the unresolved-open-
questions gate, and `.dev/floor/validate.mjs`. Nothing here should be read as "the plan passed."
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' fix: pharn status reports unreadable paths instead of crashing or misclassifying them by PrzemekGalarowicz · Pull Request #82 · pharn-dev/pharn-cli · GitHub
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
140 changes: 140 additions & 0 deletions .dev/features/diff-unreadable-partition/GRILL.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
# GRILL — diff-unreadable-partition (ADVISORY)

Plan under interrogation: `.dev/features/diff-unreadable-partition/PLAN.md`.
**Spec-hash check: MATCH** — recomputed `sha256(ARCHITECTURE.md)` =
`bca940a5ad247c120e6d8a3acba119d0d8df51dca275964d0e54c48d729d3c4e`, equal to the plan's
`spec_content_hash`. (Computation is floor-grade; here it only warns — the block on drift is
`/pharn-dev-build`'s gate, fix #4.)

**Registered grillers: 0** — `node .dev/floor/count-grillers.mjs .` → `{"registered":0,"grillers":[]}`.
This repo ships no `role: griller` capability of its own (the stage command's own frontmatter lives
under the excluded `.claude/commands/` and correctly does not register). Membership is FLOOR; the
empty set means the built-in Step 2 axes below are the whole interrogation. Recorded honestly rather
than implying a griller fleet ran.

## Findings

### Axis: guarantee-audit completeness (P0)

```yaml
- type: FINDING
rule_id: "P0"
severity: important
file: ".dev/features/diff-unreadable-partition/PLAN.md:201"
problem: "The plan calls the Inv-7 source-scan the increment's 'only new floor primitive' and then, in the same sentence, says it is not a checker — two incompatible labels for one thing, which is exactly the ambiguity P0 exists to remove."
evidence: "This is the increment's only *new* floor primitive, and it is a test assertion in the existing suite, not a new checker."
```

> **Interrogation.** `ARCHITECTURE.md §2` enumerates the floor as regex/enum allowlists, path
> containment, schema exact-match, network guards. A vitest assertion that regex-scans `diff.ts` for
> two import specifiers **is** a regex membership test, and it runs inside `npm test`, which
> `/pharn-dev-verify` treats as a floor gate — so the reduction is real. What is **not** real is the
> word "primitive": no file lands in `.dev/floor/`, and nothing outside the vitest run enforces it.
> The honest label is *floor-reducible assertion carried by the existing suite*. Left as-is, a later
> reader could cite this plan as precedent for "a test I wrote is a floor primitive."

### Axis: eval coverage / structural-vs-semantic (P1, `eval-format.md`)

```yaml
- type: FINDING
rule_id: "P1"
severity: important
file: ".dev/features/diff-unreadable-partition/PLAN.md:115"
problem: "The plan fixes the new subsection's position as third, after MISSING, but no listed eval asserts the ordering — Inv 8 pins only the absent case, so a build that renders UNREADABLE first would pass every planned test."
evidence: "Rendered inside the **existing DRIFT note**, as the **third** subsection after MISSING (mirroring `SKIP_ORDER`'s placement), and **only when non-empty**"
```

> **Interrogation.** This is the classic shape of a plan claim with no eval behind it: a specific,
> checkable ordering asserted in prose and then never tested. P5 also bears on it — deterministic
> output order is a stated repo value (`SKIP_ORDER` exists precisely so skip groups never depend on map
> iteration order), so its status-side twin deserves the same pin. Cheap fix: one assertion on a
> DRIFT body containing all three subsections, comparing `indexOf` positions.

```yaml
- type: FINDING
rule_id: "P1"
severity: minor
file: ".dev/features/diff-unreadable-partition/PLAN.md:145"
problem: "The reason-string assertion policy asserts /symlink/ for symlinks but only non-emptiness for the directory and ENOTDIR cases, so a build that returned the symlink reason for a directory would satisfy every planned assertion."
evidence: "The directory/ENOTDIR cases assert only that `reason` is a non-empty string (the `kind`-equivalent), leaving that copy free."
```

> **Interrogation.** The plan's justification for the asymmetry is sound (evidence 2 is *about* the
> reason; evidences 1 and 3 are about the partition). But the asymmetry buys a real hole: nothing
> distinguishes the three non-symlink reasons from each other. Weighing it: the partition is the
> contract and the partition **is** pinned, so this is genuinely minor — recorded so the human can
> decide whether display-copy coupling is worth closing it.

### Axis: honest scope / no speculation (P7)

```yaml
- type: FINDING
rule_id: "P7"
severity: minor
file: ".dev/features/diff-unreadable-partition/PLAN.md:57"
problem: "The build prompt scoped status.ts to 'inside-file: printDriftSection + the strict condition', but the plan's type-only InstallDiff import adds a top-level line outside both, widening the whitelist without naming that it did."
evidence: "`src/commands/status.ts` — `printDriftSection` gains the third subsection + the clean-bill condition gains `unreadable`; the `--strict` condition at `:94-96` gains `|| result.unreadable.length`."
```

> **Interrogation.** The widening is trivial and defensible — an erased `import type` touches no
> runtime behavior and leaves the VERSION/MODELS notes, fetch/cleanup flow, and re-add hint
> byte-equivalent as required. The finding is not that the change is wrong; it is that the plan made a
> scope decision **silently**. P7 asks for limits to be labeled as limits, and a whitelist quietly
> widened by one line is still a whitelist quietly widened.

```yaml
- type: FINDING
rule_id: "P4"
severity: minor
file: ".dev/features/diff-unreadable-partition/PLAN.md:65"
problem: "The plan names CHANGELOG.md as a file to edit but never says which section receives the entry, and the working tree's staged version bump to 0.4.0 makes 'Unreleased' versus a 0.4.0 heading a live ambiguity rather than a hypothetical one."
evidence: "- `CHANGELOG.md` — the four user-facing changes. layer: docs"
```

> **Interrogation.** Resolved against live state this run rather than left as a guess (P6):
> `CHANGELOG.md:8` is `## [Unreleased]` with a `### Fixed` subsection already open, and no `0.4.0`
> heading exists despite `package.json` reading `0.4.0` in the staged index. So `[Unreleased] → Fixed`
> is the correct target. Recorded because the plan should have said so.

### Axis: trust propagation (P2)

**No findings.** The plan's trust audit is verifiable and was verified: the four `reason` strings are
literals in `src/lib/apply-update.ts:54,58,61,66`, never derived from fetched bytes, so rendering them
adds no untrusted text to the terminal. The `rel` paths already flowed through
`collectExpectedInstallPaths`. Taint does not widen.

### Axis: one axis of change / no sibling imports (P3)

**No findings.** `diff.ts` → `apply-update.ts` is lib→lib, not command→command or step→step, so the
P3 prohibition is not engaged. Both files keep one reason to change (`diff.ts`: how drift is computed;
`apply-update.ts`: untouched). The plan's refusal to relocate `readDiskState` on a two-consumer count
is correctly conservative.

### Axis: determinism (P5)

**No findings.** The four-way branch is membership over a closed union; the `<`/`>` sort choice over
`localeCompare` is the right call and the plan states the reason. Terminal fallback does not end in a
guess.

## Summary

The plan is unusually well-grounded — it re-verified its own build prompt and corrected three of its
premises rather than inheriting them, and its guarantee audit does the hard part (noticing that
deleting `diff.ts`'s `safeJoin` call leaves the containment claim needing a new home, then pointing at
the two callees that provide it). The concerns above are about **labeling and eval coverage, not
design**: one overstated floor label (P0), one prose claim with no eval behind it (the subsection
ordering — the only finding likely to survive into the built artifact as a real gap), one
display-copy hole the plan already reasoned about, and two unstated scope/target decisions.

The single concern most worth acting on before `/pharn-dev-build` is the **ordering eval**: it is one
assertion, it closes a claim the plan makes explicitly, and it matches an ordering discipline this repo
already enforces on the write side.

## Verdict

**ADVISORY VERDICT: 5 concerns raised (0 blocking-severity, 2 important, 3 minor) — for the human to
weigh before `/pharn-dev-build`.**

This grill-log is **advisory end-to-end**. It gates nothing: `/pharn-dev-build` proceeds regardless, and
the deterministic backstops remain where they always were — the spec-hash gate, the unresolved-open-
questions gate, and `.dev/floor/validate.mjs`. Nothing here should be read as "the plan passed."
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix: pharn status reports unreadable paths instead of crashing or misclassifying them by PrzemekGalarowicz · Pull Request #82 · pharn-dev/pharn-cli · GitHub
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
140 changes: 140 additions & 0 deletions .dev/features/diff-unreadable-partition/GRILL.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
# GRILL — diff-unreadable-partition (ADVISORY)

Plan under interrogation: `.dev/features/diff-unreadable-partition/PLAN.md`.
**Spec-hash check: MATCH** — recomputed `sha256(ARCHITECTURE.md)` =
`bca940a5ad247c120e6d8a3acba119d0d8df51dca275964d0e54c48d729d3c4e`, equal to the plan's
`spec_content_hash`. (Computation is floor-grade; here it only warns — the block on drift is
`/pharn-dev-build`'s gate, fix #4.)

**Registered grillers: 0** — `node .dev/floor/count-grillers.mjs .` → `{"registered":0,"grillers":[]}`.
This repo ships no `role: griller` capability of its own (the stage command's own frontmatter lives
under the excluded `.claude/commands/` and correctly does not register). Membership is FLOOR; the
empty set means the built-in Step 2 axes below are the whole interrogation. Recorded honestly rather
than implying a griller fleet ran.

## Findings

### Axis: guarantee-audit completeness (P0)

```yaml
- type: FINDING
rule_id: "P0"
severity: important
file: ".dev/features/diff-unreadable-partition/PLAN.md:201"
problem: "The plan calls the Inv-7 source-scan the increment's 'only new floor primitive' and then, in the same sentence, says it is not a checker — two incompatible labels for one thing, which is exactly the ambiguity P0 exists to remove."
evidence: "This is the increment's only *new* floor primitive, and it is a test assertion in the existing suite, not a new checker."
```

> **Interrogation.** `ARCHITECTURE.md §2` enumerates the floor as regex/enum allowlists, path
> containment, schema exact-match, network guards. A vitest assertion that regex-scans `diff.ts` for
> two import specifiers **is** a regex membership test, and it runs inside `npm test`, which
> `/pharn-dev-verify` treats as a floor gate — so the reduction is real. What is **not** real is the
> word "primitive": no file lands in `.dev/floor/`, and nothing outside the vitest run enforces it.
> The honest label is *floor-reducible assertion carried by the existing suite*. Left as-is, a later
> reader could cite this plan as precedent for "a test I wrote is a floor primitive."

### Axis: eval coverage / structural-vs-semantic (P1, `eval-format.md`)

```yaml
- type: FINDING
rule_id: "P1"
severity: important
file: ".dev/features/diff-unreadable-partition/PLAN.md:115"
problem: "The plan fixes the new subsection's position as third, after MISSING, but no listed eval asserts the ordering — Inv 8 pins only the absent case, so a build that renders UNREADABLE first would pass every planned test."
evidence: "Rendered inside the **existing DRIFT note**, as the **third** subsection after MISSING (mirroring `SKIP_ORDER`'s placement), and **only when non-empty**"
```

> **Interrogation.** This is the classic shape of a plan claim with no eval behind it: a specific,
> checkable ordering asserted in prose and then never tested. P5 also bears on it — deterministic
> output order is a stated repo value (`SKIP_ORDER` exists precisely so skip groups never depend on map
> iteration order), so its status-side twin deserves the same pin. Cheap fix: one assertion on a
> DRIFT body containing all three subsections, comparing `indexOf` positions.

```yaml
- type: FINDING
rule_id: "P1"
severity: minor
file: ".dev/features/diff-unreadable-partition/PLAN.md:145"
problem: "The reason-string assertion policy asserts /symlink/ for symlinks but only non-emptiness for the directory and ENOTDIR cases, so a build that returned the symlink reason for a directory would satisfy every planned assertion."
evidence: "The directory/ENOTDIR cases assert only that `reason` is a non-empty string (the `kind`-equivalent), leaving that copy free."
```

> **Interrogation.** The plan's justification for the asymmetry is sound (evidence 2 is *about* the
> reason; evidences 1 and 3 are about the partition). But the asymmetry buys a real hole: nothing
> distinguishes the three non-symlink reasons from each other. Weighing it: the partition is the
> contract and the partition **is** pinned, so this is genuinely minor — recorded so the human can
> decide whether display-copy coupling is worth closing it.

### Axis: honest scope / no speculation (P7)

```yaml
- type: FINDING
rule_id: "P7"
severity: minor
file: ".dev/features/diff-unreadable-partition/PLAN.md:57"
problem: "The build prompt scoped status.ts to 'inside-file: printDriftSection + the strict condition', but the plan's type-only InstallDiff import adds a top-level line outside both, widening the whitelist without naming that it did."
evidence: "`src/commands/status.ts` — `printDriftSection` gains the third subsection + the clean-bill condition gains `unreadable`; the `--strict` condition at `:94-96` gains `|| result.unreadable.length`."
```

> **Interrogation.** The widening is trivial and defensible — an erased `import type` touches no
> runtime behavior and leaves the VERSION/MODELS notes, fetch/cleanup flow, and re-add hint
> byte-equivalent as required. The finding is not that the change is wrong; it is that the plan made a
> scope decision **silently**. P7 asks for limits to be labeled as limits, and a whitelist quietly
> widened by one line is still a whitelist quietly widened.

```yaml
- type: FINDING
rule_id: "P4"
severity: minor
file: ".dev/features/diff-unreadable-partition/PLAN.md:65"
problem: "The plan names CHANGELOG.md as a file to edit but never says which section receives the entry, and the working tree's staged version bump to 0.4.0 makes 'Unreleased' versus a 0.4.0 heading a live ambiguity rather than a hypothetical one."
evidence: "- `CHANGELOG.md` — the four user-facing changes. layer: docs"
```

> **Interrogation.** Resolved against live state this run rather than left as a guess (P6):
> `CHANGELOG.md:8` is `## [Unreleased]` with a `### Fixed` subsection already open, and no `0.4.0`
> heading exists despite `package.json` reading `0.4.0` in the staged index. So `[Unreleased] → Fixed`
> is the correct target. Recorded because the plan should have said so.

### Axis: trust propagation (P2)

**No findings.** The plan's trust audit is verifiable and was verified: the four `reason` strings are
literals in `src/lib/apply-update.ts:54,58,61,66`, never derived from fetched bytes, so rendering them
adds no untrusted text to the terminal. The `rel` paths already flowed through
`collectExpectedInstallPaths`. Taint does not widen.

### Axis: one axis of change / no sibling imports (P3)

**No findings.** `diff.ts` → `apply-update.ts` is lib→lib, not command→command or step→step, so the
P3 prohibition is not engaged. Both files keep one reason to change (`diff.ts`: how drift is computed;
`apply-update.ts`: untouched). The plan's refusal to relocate `readDiskState` on a two-consumer count
is correctly conservative.

### Axis: determinism (P5)

**No findings.** The four-way branch is membership over a closed union; the `<`/`>` sort choice over
`localeCompare` is the right call and the plan states the reason. Terminal fallback does not end in a
guess.

## Summary

The plan is unusually well-grounded — it re-verified its own build prompt and corrected three of its
premises rather than inheriting them, and its guarantee audit does the hard part (noticing that
deleting `diff.ts`'s `safeJoin` call leaves the containment claim needing a new home, then pointing at
the two callees that provide it). The concerns above are about **labeling and eval coverage, not
design**: one overstated floor label (P0), one prose claim with no eval behind it (the subsection
ordering — the only finding likely to survive into the built artifact as a real gap), one
display-copy hole the plan already reasoned about, and two unstated scope/target decisions.

The single concern most worth acting on before `/pharn-dev-build` is the **ordering eval**: it is one
assertion, it closes a claim the plan makes explicitly, and it matches an ordering discipline this repo
already enforces on the write side.

## Verdict

**ADVISORY VERDICT: 5 concerns raised (0 blocking-severity, 2 important, 3 minor) — for the human to
weigh before `/pharn-dev-build`.**

This grill-log is **advisory end-to-end**. It gates nothing: `/pharn-dev-build` proceeds regardless, and
the deterministic backstops remain where they always were — the spec-hash gate, the unresolved-open-
questions gate, and `.dev/floor/validate.mjs`. Nothing here should be read as "the plan passed."
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); fix: pharn status reports unreadable paths instead of crashing or misclassifying them by PrzemekGalarowicz · Pull Request #82 · pharn-dev/pharn-cli · GitHub
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
140 changes: 140 additions & 0 deletions .dev/features/diff-unreadable-partition/GRILL.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
# GRILL — diff-unreadable-partition (ADVISORY)

Plan under interrogation: `.dev/features/diff-unreadable-partition/PLAN.md`.
**Spec-hash check: MATCH** — recomputed `sha256(ARCHITECTURE.md)` =
`bca940a5ad247c120e6d8a3acba119d0d8df51dca275964d0e54c48d729d3c4e`, equal to the plan's
`spec_content_hash`. (Computation is floor-grade; here it only warns — the block on drift is
`/pharn-dev-build`'s gate, fix #4.)

**Registered grillers: 0** — `node .dev/floor/count-grillers.mjs .` → `{"registered":0,"grillers":[]}`.
This repo ships no `role: griller` capability of its own (the stage command's own frontmatter lives
under the excluded `.claude/commands/` and correctly does not register). Membership is FLOOR; the
empty set means the built-in Step 2 axes below are the whole interrogation. Recorded honestly rather
than implying a griller fleet ran.

## Findings

### Axis: guarantee-audit completeness (P0)

```yaml
- type: FINDING
rule_id: "P0"
severity: important
file: ".dev/features/diff-unreadable-partition/PLAN.md:201"
problem: "The plan calls the Inv-7 source-scan the increment's 'only new floor primitive' and then, in the same sentence, says it is not a checker — two incompatible labels for one thing, which is exactly the ambiguity P0 exists to remove."
evidence: "This is the increment's only *new* floor primitive, and it is a test assertion in the existing suite, not a new checker."
```

> **Interrogation.** `ARCHITECTURE.md §2` enumerates the floor as regex/enum allowlists, path
> containment, schema exact-match, network guards. A vitest assertion that regex-scans `diff.ts` for
> two import specifiers **is** a regex membership test, and it runs inside `npm test`, which
> `/pharn-dev-verify` treats as a floor gate — so the reduction is real. What is **not** real is the
> word "primitive": no file lands in `.dev/floor/`, and nothing outside the vitest run enforces it.
> The honest label is *floor-reducible assertion carried by the existing suite*. Left as-is, a later
> reader could cite this plan as precedent for "a test I wrote is a floor primitive."

### Axis: eval coverage / structural-vs-semantic (P1, `eval-format.md`)

```yaml
- type: FINDING
rule_id: "P1"
severity: important
file: ".dev/features/diff-unreadable-partition/PLAN.md:115"
problem: "The plan fixes the new subsection's position as third, after MISSING, but no listed eval asserts the ordering — Inv 8 pins only the absent case, so a build that renders UNREADABLE first would pass every planned test."
evidence: "Rendered inside the **existing DRIFT note**, as the **third** subsection after MISSING (mirroring `SKIP_ORDER`'s placement), and **only when non-empty**"
```

> **Interrogation.** This is the classic shape of a plan claim with no eval behind it: a specific,
> checkable ordering asserted in prose and then never tested. P5 also bears on it — deterministic
> output order is a stated repo value (`SKIP_ORDER` exists precisely so skip groups never depend on map
> iteration order), so its status-side twin deserves the same pin. Cheap fix: one assertion on a
> DRIFT body containing all three subsections, comparing `indexOf` positions.

```yaml
- type: FINDING
rule_id: "P1"
severity: minor
file: ".dev/features/diff-unreadable-partition/PLAN.md:145"
problem: "The reason-string assertion policy asserts /symlink/ for symlinks but only non-emptiness for the directory and ENOTDIR cases, so a build that returned the symlink reason for a directory would satisfy every planned assertion."
evidence: "The directory/ENOTDIR cases assert only that `reason` is a non-empty string (the `kind`-equivalent), leaving that copy free."
```

> **Interrogation.** The plan's justification for the asymmetry is sound (evidence 2 is *about* the
> reason; evidences 1 and 3 are about the partition). But the asymmetry buys a real hole: nothing
> distinguishes the three non-symlink reasons from each other. Weighing it: the partition is the
> contract and the partition **is** pinned, so this is genuinely minor — recorded so the human can
> decide whether display-copy coupling is worth closing it.

### Axis: honest scope / no speculation (P7)

```yaml
- type: FINDING
rule_id: "P7"
severity: minor
file: ".dev/features/diff-unreadable-partition/PLAN.md:57"
problem: "The build prompt scoped status.ts to 'inside-file: printDriftSection + the strict condition', but the plan's type-only InstallDiff import adds a top-level line outside both, widening the whitelist without naming that it did."
evidence: "`src/commands/status.ts` — `printDriftSection` gains the third subsection + the clean-bill condition gains `unreadable`; the `--strict` condition at `:94-96` gains `|| result.unreadable.length`."
```

> **Interrogation.** The widening is trivial and defensible — an erased `import type` touches no
> runtime behavior and leaves the VERSION/MODELS notes, fetch/cleanup flow, and re-add hint
> byte-equivalent as required. The finding is not that the change is wrong; it is that the plan made a
> scope decision **silently**. P7 asks for limits to be labeled as limits, and a whitelist quietly
> widened by one line is still a whitelist quietly widened.

```yaml
- type: FINDING
rule_id: "P4"
severity: minor
file: ".dev/features/diff-unreadable-partition/PLAN.md:65"
problem: "The plan names CHANGELOG.md as a file to edit but never says which section receives the entry, and the working tree's staged version bump to 0.4.0 makes 'Unreleased' versus a 0.4.0 heading a live ambiguity rather than a hypothetical one."
evidence: "- `CHANGELOG.md` — the four user-facing changes. layer: docs"
```

> **Interrogation.** Resolved against live state this run rather than left as a guess (P6):
> `CHANGELOG.md:8` is `## [Unreleased]` with a `### Fixed` subsection already open, and no `0.4.0`
> heading exists despite `package.json` reading `0.4.0` in the staged index. So `[Unreleased] → Fixed`
> is the correct target. Recorded because the plan should have said so.

### Axis: trust propagation (P2)

**No findings.** The plan's trust audit is verifiable and was verified: the four `reason` strings are
literals in `src/lib/apply-update.ts:54,58,61,66`, never derived from fetched bytes, so rendering them
adds no untrusted text to the terminal. The `rel` paths already flowed through
`collectExpectedInstallPaths`. Taint does not widen.

### Axis: one axis of change / no sibling imports (P3)

**No findings.** `diff.ts` → `apply-update.ts` is lib→lib, not command→command or step→step, so the
P3 prohibition is not engaged. Both files keep one reason to change (`diff.ts`: how drift is computed;
`apply-update.ts`: untouched). The plan's refusal to relocate `readDiskState` on a two-consumer count
is correctly conservative.

### Axis: determinism (P5)

**No findings.** The four-way branch is membership over a closed union; the `<`/`>` sort choice over
`localeCompare` is the right call and the plan states the reason. Terminal fallback does not end in a
guess.

## Summary

The plan is unusually well-grounded — it re-verified its own build prompt and corrected three of its
premises rather than inheriting them, and its guarantee audit does the hard part (noticing that
deleting `diff.ts`'s `safeJoin` call leaves the containment claim needing a new home, then pointing at
the two callees that provide it). The concerns above are about **labeling and eval coverage, not
design**: one overstated floor label (P0), one prose claim with no eval behind it (the subsection
ordering — the only finding likely to survive into the built artifact as a real gap), one
display-copy hole the plan already reasoned about, and two unstated scope/target decisions.

The single concern most worth acting on before `/pharn-dev-build` is the **ordering eval**: it is one
assertion, it closes a claim the plan makes explicitly, and it matches an ordering discipline this repo
already enforces on the write side.

## Verdict

**ADVISORY VERDICT: 5 concerns raised (0 blocking-severity, 2 important, 3 minor) — for the human to
weigh before `/pharn-dev-build`.**

This grill-log is **advisory end-to-end**. It gates nothing: `/pharn-dev-build` proceeds regardless, and
the deterministic backstops remain where they always were — the spec-hash gate, the unresolved-open-
questions gate, and `.dev/floor/validate.mjs`. Nothing here should be read as "the plan passed."
Loading