Normalization is the whole design
Hash raw bytes and the marker fires on every trailing space and every reformat — and a marker that cries wolf gets deleted. In order:
- Apply
reindent first (WORK-589). Moving a function into a class shifts every line two columns without changing a character of content. - Normalize line endings, strip per-line trailing whitespace, normalize the trailing newline.
- Stop. Comments stay in — D2.
Excluding comments would make markers quieter and the masker from WORK-597 could do it for free. It is still wrong: a doc comment is very often the exact text the surrounding prose paraphrases. If @param timeout's description changes meaning and the marker stays green, the feature has failed at the only job it has.
Where findings go (D6)
Into SPEC-132's diagnostics surface as PipelineWarnings, and no further. Nothing is rendered into the page.
This is deliberately different from SPEC-131 D6, and the difference is principled: SPEC-131 uses an error fence because the content could not be produced. Here the content was produced perfectly — the code is real, current, and correctly located. What is uncertain is the prose beside it, which the resolver cannot see. Replacing a correct code block with an error because a paragraph might be stale is a straightforward regression for every reader.
Acceptance Criteria
reviewed is accepted on snippet and file-ref, and its absence leaves behaviour unchangedexpand does not accept reviewed — it resolves no slice, so there is nothing of the right shape to hash- The hashed form applies
reindent first, then normalizes line endings, per-line trailing whitespace, and the trailing newline - Comments are included in the hashed form, covered by a test where only a doc comment changes and the marker fires
- A nesting-only change (a function moved into a class, content otherwise identical) does not fire the marker
- The stored hash is truncated to 8–12 hex characters
- Both hash levels are computed and exposed, with the loose form additionally collapsing whitespace runs and dropping blank lines
- A change that alters the strict hash but not the loose hash is classified formatting-only, covered by a reformatting test
- A stale marker produces a
PipelineWarning and renders nothing into the page - A refused anchor never evaluates
reviewed - A
snippet command group is registered in packages/cli/src/commands/ — no such command exists today, and this item is the first to need it; WORK-592's review extends it rather than adding a second refrakt snippet review --check reports every stale marker with file, line, anchor, and a summary of what changed--check has its own exit code, independent of the pipeline diagnostic channel- SPEC-134's Approach is corrected — it still says the feature "would ship as a no-op with a CLI attached" until WORK-573, which WORK-575 made untrue in v0.36.0 — and its first open question is marked answered by WORK-580's pre-merge job
Approach
Build the checker before the writer. The feature is useful read-only the moment a marker can be placed, and building --check first keeps the normalization decisions honest — every normalization choice is immediately visible as a marker that does or does not fire.
Budget the normalization, not the hash. Hashing is a line. Getting normalization wrong produces a feature that is worse than not having it, because a noisy marker trains people to ignore a real one.
Blocked by
- WORK-589 —
reindent is normalization step 1
Notes
SPEC-134's stated prerequisite is stale and this item is not blocked on it. The spec's Approach says the feature would "ship as a no-op with a CLI attached" until WORK-573 lands, on the strength of WORK-554's finding that the adapter dev server prints no diagnostics at all.
WORK-575 has since shipped in v0.36.0 and fixed exactly that: one reporter at loadContent, printing in dev. So a review-marker diagnostic now is seen by someone editing documentation, which was the disqualifying row.
What WORK-573 still owns is whether an error-severity diagnostic fails a build — and D7 says a fired marker is a review prompt, not a failure, so this feature actively does not want that. No dependency in either direction.
D1 is worth respecting in the naming: reviewed, not pin. This freezes nothing — the snippet still tracks HEAD and re-resolves every build. pin= would steer an author toward "this is frozen, nothing to do here", the opposite of the intended response.
References
- SPEC-134 — D1 (the name), D2 (comments in), D3 (proven formatting-only), D4 (inline and truncated, not a sidecar), D6 (diagnostic, never a fence), D9 (the anchor refuses first)
- WORK-589 —
reindent - WORK-575 — the reporter seam that makes the diagnostic visible in dev, resolving the spec's stated blocker
- SPEC-132 — the diagnostics routing model
packages/content/src/site.ts — the diagnostics surfacepackages/editor/src/community-tags-builder.ts — the 8-character truncated-hash precedent
Resolution
Completed: 2026-09-24
Branch: claude/v0-37-0-review-vqpl41 PR: refrakt-md/refrakt#649 (batched with WORK-592 and WORK-593)
What was done
packages/runes/src/lib/review-marker.ts (new) — normalizeStrict, normalizeLoose, hashSlice, compareMarker, formatMarker, parseMarker, canCarryMarker.snippet-pipeline.ts — comparison after resolution, emitting a PipelineWarning and rendering nothing into the page.reviewed attribute on snippet and file-ref; deliberately not expand.refrakt snippet review --check (the command group lives in WORK-592's module, registered here as the first consumer).- SPEC-134's Approach corrected; both its open questions marked answered.
packages/runes/test/review-marker.test.ts — 18 tests.
Notes
- Both hash levels are stored inline, colon-separated. This is a decision beyond what the spec states. D3 requires formatting-only to be proven, and with a single stored value it cannot be — so a one-value marker degrades to
stale rather than guessing. That direction is deliberate: it asks for a review that may be trivial rather than skipping one that mattered. - The pipeline warning carries no diff, and cannot. At transform time the only record of the reviewed version is its hash. Recovering the old content needs git, which is WORK-592's job — this is why D5's "show content, never hashes" is the CLI's responsibility rather than the pipeline's.
- D9's layering is load-bearing in the code, not just the spec. A refused anchor returns before the marker is evaluated, so one failure never produces two findings. Covered by a test that renames a symbol and asserts silence.
- Comments stay in the hash (D2) even though SPEC-131's masker could strip them for free. The test that pins this changes only a doc comment and asserts the marker fires.
Verification
4903 tests pass. refrakt snippet review --check clean, exit 0.