Uh oh!
There was an error while loading. Please reload this page.
hypgrep enters the dependency set behind an overrides pin that holds the exact LLP 0222 hyparquet floor - #877
Conversation
… LLP 0222 floor (#870) `hyp query grep` (LLP 0264) both builds and reads hypgrep sidecar indexes, so hypgrep is a plain root dependency. It cannot simply be installed: hypgrep 0.5.1 pins hyparquet 1.27.1 and hyparquet-writer 0.16.4, and npm resolves both privately under `node_modules/hypgrep`. That second, older hyparquet is exactly the failure LLP 0222 #hyparquet-floor exists to prevent: icebird's converter pushes bare relational bounds, correct only because 1.28.2's `matchFilter` rejects null cells in the relational operators, so a 1.27.1 copy leaks NULL rows through a bound and fails as wrong rows rather than as an error. The adoption therefore carries a root `overrides` entry beside icebird's, pinning hypgrep's hyparquet to 1.28.2 and its writer to 0.16.6. Both then dedupe to the copies already at the root, so the dependency joins the read path without adding a resolved copy of anything. `hyparquet-writer` stays an optionalDependency: the override pins a version inside hypgrep's subtree, it does not promote the root entry. Verified that `npm install --omit=optional` still boots and reads. `test/core/hyparquet-floor-pin.test.js` is the gate, in the lint idiom of `repo-scratch-hygiene.test.js`, and both halves are load-bearing. The manifest half reads the floor off the root pin and requires every root dependency declaring its own hyparquet or hyparquet-writer to either agree with it or be named in `overrides`, so the next below-floor dependency reddens here instead of in a row count. The resolved half is `npm ls` in assertion form: an override with the wrong shape still parses as JSON and still installs a private copy, and only the tree shows that. Dropping the override reddens both. `npm pack --dry-run` file set is byte-identical at 932 files. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…depth 1 (#877 review) Review of #877 found the test's two halves were not the two halves its header claimed. Both of the checks that could catch a dropped override read `node_modules`, so on a checkout with nothing installed all four tests passed green having verified nothing about hypgrep at all. - The override itself is now asserted straight off the root package.json, so deleting `overrides.hypgrep` reddens with no install present. - The two tree-reading tests skip visibly instead of returning green when `node_modules` is absent, and the header says which half is which. - The nested-copy scan recurses through each root dependency's subtree rather than looking one level down, so a private copy a level below the dependency that pulled it in is still seen. - A nested copy already at the pinned version is no longer reported as a floor violation, which it is not. - The declaration lint reads optionalDependencies and peerDependencies too, since npm resolves copies for those the same way. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
philcunliffe
commented
Aug 19, 2026
Neutral review round: PR #877 @ |
#877 review) The gate LLP 0264 #dependency's override needs had five ways to be wrong or silent, all found by review of 7588c37: - The floor was asserted as `FLOOR === '1.28.2'` right after being read off the root pin, so bumping hyparquet reddened the suite with "1.28.2 is a floor and an exact pin". LLP 0222 #hyparquet-floor states the requirement as `>= 1.28.2`, so the floor is now its own constant and the root pin is checked exact-shaped and at or above it. - Declarations were compared to the pin by string equality, so a version *above* the floor was reported under "answers relational bounds wrong", which it does not. Comparison is now numeric against the floor, and the message names the floor it is below. - With `hyparquet-writer` gone from optionalDependencies the writer pin read as undefined and `overrides[name]?.[dep] === pin` matched `undefined === undefined`, turning that half of the lint off. A missing root pin is now reported, once. - `packagesIn` filtered on `isDirectory()`, which is false for a symlink, so the nested-copy walk found nothing under pnpm or `npm link` and passed while a private 1.27.1 sat on disk. Symlinks are walked, with a realpath set for cycles. - A dependency an override governs but that is not installed was skipped, so the resolved half proved nothing about the one entry it exists for (the state of any tree installed before this PR). That is now reported as a stale tree. Also records what the check cannot reach: npm honours `overrides` only for the root project, so a `npx hypaware` install still resolves hypgrep's own 1.27.1. LLP 0264 #dependency already names widening the range upstream as the durable fix; the header now says the override is not a substitute. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
philcunliffe
commented
Aug 19, 2026
Neutral review round: 7588c37 - findings (5 fixed, 1 open, 1 informational)Reviewed The findings are all in the gate, not in the pin. Fixed and pushed (415587c)1. 2. 3. 4. 5. Open - needs a human decision, not fixed here6. Mitigating: hypgrep's only pushdown is Not fixed because both remedies are design calls above a review round. LLP 0264 #dependency already names the durable one ("widening the range upstream in hypgrep"); the other is shipping an Related: a check on this would have to pack and install the package, which does not belong in the traditional suite. If you want it gated, it is an acceptance-tier candidate ( Informational7. Checked and cleared
|
philcunliffe
commented
Aug 19, 2026
Neutral triage: PR #877 @ |
Uh oh!
There was an error while loading. Please reload this page.
Root cause
hyp query grep(LLP 0264 #dependency) both builds and reads hypgrep sidecar indexes, sohypgrepis a plain root dependency. Installed as-is it is not inert: hypgrep 0.5.1 pinshyparquet1.27.1 andhyparquet-writer0.16.4, and npm resolves both privately undernode_modules/hypgrep:That second, older hyparquet is the exact failure LLP 0222 #hyparquet-floor exists to prevent. icebird's converter pushes bare relational bounds, correct only because hyparquet >= 1.28.2's
matchFilterrejects null cells in$lt/$lte/$gt/$gte. On 1.27.1 those coerce a null cell to 0 and the bound leaks NULL rows. The floor is exact, not a minimum, and the failure is wrong rows rather than an error.The fix
hypgrep0.5.1 intodependencies, and a rootoverridesentry beside icebird's pinning hypgrep'shyparquetto 1.28.2 and itshyparquet-writerto 0.16.6. Both then dedupe to the copies already at the root, so the dependency joins the read path without adding a resolved copy of anything:hyparquet-writerstays an optionalDependency: the override pins a version inside hypgrep's subtree, it does not promote the root entry. Verifiednpm install --omit=optionalstill boots and reads (hyp --version,hyp query sql "select 1 as x", andimport('hypgrep')all fine; thepreparestep fails there on master too, because typescript 7 ships its platform binary as its own optionalDependency, unrelated to this change).The test that proves it
test/core/hyparquet-floor-pin.test.js, a lint on a repository property in the idiom ofrepo-scratch-hygiene.test.js. Both halves are load-bearing and both redden if the override is dropped:hyparquetpin rather than writing it twice, then requires every rootdependenciesentry that declares its ownhyparquetorhyparquet-writerto either agree with the floor or be named inoverrides. The next below-floor dependency reddens here instead of in a row count. Also pins thathypgrepis independenciesand thathyparquet-writeris optional-only.npm lsin assertion form, over the installed tree, skipped whennode_modulesis absent. This is the part a manifest read cannot give you: an override nested under the wrong key, or naming a version no longer at the root, still parses as valid JSON and still installs a private copy.Observed failing on
master(hypgrep belongs in dependencies), and observed failing in the counterfactual (hypgrep added without the override) with both halves red:Verification
npm test: 4487 pass, 0 fail, 1 skipped.npm run typecheck: clean.npm pack --dry-run: file set byte-identical tomaster, 932 files (diffed the two--jsonfile lists).Note on "exactly one resolved copy"
The acceptance criterion asks for one
npm ls hyparquetcopy tree-wide. That is already false onmaster, before this change, and for reasons outside the read path:hyparquet-writer@0.16.6pins its ownhyparquet@1.28.1, and the optionalhypvector@0.2.2bringshyparquet@1.26.2plushyparquet-writer@0.16.1withhyparquet@1.26.1. All are exact pins, so collapsing them needs overrides on those packages too, which forces untested version combinations on the write and vector paths and is not what this issue asked for. What this change does hold, and what the test asserts, is the read path LLP 0222's floor governs: hypgrep adds no resolved copy of anything, and rootdependencies(hyparquet, icebird, hypgrep) all resolve the single 1.28.2. The pre-existing write-side and vector-side copies are worth their own issue.Fixes#870