fix: scan JSON and front-matter prose values for banned punctuation - #1334
Conversation
vivek7405
left a comment
There was a problem hiding this comment.
Read the whole diff. Nothing to fix.
The key-scoped design is the right call. Scoping to description / title / displayName rather than to a file path is what keeps this off semver ranges, script commands, urls and globs, and it means the Bash payload is covered too, which a file_path gate could never do since a heredoc writing a manifest carries no path. Reusing each rule's existing character class byte for byte is the load-bearing part: the letter bound is the only reason "1.2.3 - 2.3.4" cannot match, and a loosened class would quietly admit every version range in the tree.
The SIGPIPE find is the more valuable half of this. Rules 1 through 4 were silently no-ops on any payload past the pipe buffer, so the gate was strongest on exactly the small edits that need it least. Worth remembering that grep -q behind a pipe under pipefail is a trap, not a style choice, and the comment at the top of the block says so.
The one thing I would keep an eye on is the column-0 anchor on the front-matter pattern. It is what separates document front matter from an indented workflow input, and it is a single character with no second line of defence, so if a surface ever emits front matter with leading whitespace the rule goes quiet rather than loud. That is the correct direction to fail for a hard-blocking gate, and the indented-YAML test pins the behaviour, so it stays as is.
The prose-punctuation hook gated its pause-hyphen and pause-semicolon rules on four line shapes (a comment, a markdown heading, a blockquote, an HTML prose tag), so a JSON string value matched none of them and invariant 11 shipped straight through. The repo root manifest description and the ui registry description both carried a pause-hyphen because of it, and the repo-wide punctuation cleanup that introduced one of them was not caught. Rules 2 and 3 now also scan a description / title / displayName value, in JSON and in column-0 YAML front matter. The scope is the KEY, not the file, which is what keeps the rule off semver ranges, script commands, urls, paths and globs, since every one of those lives under a different key. Rules 1 through 4 also silently stopped enforcing on a payload past the pipe buffer: grep -q exits on its first match, that closes the pipe under printf, and under pipefail the SIGPIPE became the pipeline status, so the if was false and the rule skipped. Measured 0 of 8 blocks at 200 KB before, 8 of 8 after. Every match now reads from a here-string.
c874ce4 to
1f309c0
Compare
Closes #1269
The prose-punctuation hook gated its pause-hyphen and pause-semicolon rules on four line shapes (a comment line, a markdown heading, a blockquote, an HTML prose tag), so a JSON string value matched none of them and invariant 11 shipped straight through. Rules 2 and 3 now also scan a
description,title, ordisplayNamevalue, in JSON and in column-0 YAML front matter. The scope is the KEY, not the file and not the value, which is what keeps the rule off semver ranges, script commands, urls, paths and globs, since every one of those lives under a different key.What changed
"1.2.3 - 2.3.4"unmatchable.printf ... | grep -q, andgrep -qexits on its first match, which closes the pipe underprintf. Underset -o pipefailthat SIGPIPE became the pipeline status, so theifwas false and the rule silently skipped on any payload past the pipe buffer. Measured on this branch: 0 of 8 blocks at 200 KB before, 8 of 8 after. Every match now reads from a here-string. Rule 5 was already safe (it reads all of its input through a command substitution) and is untouched.descriptionandpackages/ui/packages/registry/package.json. The repo-wide punctuation cleanup that introduced one of them swapped an em-dash for a space-hyphen inside a JSON value, and nothing caught it, because the hook did not read JSON.@param {type} name - descriptionseparator lines the SIGPIPE fix now surfaces are rewritten. Those were the only three in the tree, so the repo convention was already not to use the separator.packages/cli/templates/,examples/blog/), and the two scaffold copies were derived from the repo copy by a script validated to reproduce the committed scaffold byte for byte from the committed repo copy.item 10citations now readitem 11.Test plan
test/hooks/block-prose-punctuation.test.mjs, 32 tests, all passing. New coverage:description:andtitle:, and all four ORIGINAL contexts (the file had zero rule 2 / rule 3 coverage before).enginesrange, ascriptscommand, acommandpath, aname, a config-block leaf, amainpath, raw SQL underdefault, a compound word inside a scanned value, an ordinary English semicolon with no surrounding spaces, and an INDENTED YAMLdescription:(which is what proves the column-0 anchor is real).*.jsonand*.md. It reds against the unfixed tree atpackage.json:5andpackages/ui/packages/registry/package.json:6.grep -q.Counterfactuals, each isolating one failure mode:
printf ... |pipes back. The 200 KB test and both drift guards red, every small-payload test stays green. This is the one that proves the SIGPIPE fix is load-bearing rather than cosmetic.The full Node suite is green apart from 5 assertions in
packages/server/test/elision/differential-elision.test.jsandtest/bun/listener*, which are pre-existing in a linked worktree rather than a regression here: they pass in a checkout with a real install, and they still fail with this branch's diff reverted file by file toorigin/maincontent. CI builds from the branch, so it is unaffected.Doc surfaces
AGENTS.mdinvariant 11, whose "semicolons and colons stay fine inside code / TS / JSON / CSS" clause read as a blanket JSON exemption and is now wrong for the three prose keys. It now says JSON SYNTAX is code while a prose-key VALUE is prose..agents/skills/webjs// MCP / editor plugins: this is repo tooling, not framework behaviour, so there is no user-facing surface to sync. The scaffold and dogfood rule copies state invariant 11 generically with no JSON exemption, so they are already correct.packages/edits are a JSDoc separator and a manifest description.