Skip to content

build: derive libc verifier scope from optionalDependencies#1172

Merged
carlos-alm merged 7 commits into
mainfrom
fix/1168-libc-verifier-derive
May 20, 2026
Merged

build: derive libc verifier scope from optionalDependencies#1172
carlos-alm merged 7 commits into
mainfrom
fix/1168-libc-verifier-derive

Conversation

@carlos-alm
Copy link
Copy Markdown
Contributor

Summary

  • scripts/verify-lockfile-libc.mjs previously hand-maintained EXPECTED_LIBC, duplicating the list of linux native packages already declared in package.json's optionalDependencies. Adding a new platform required updating both — and follow-up: extend lockfile libc verifier when linux-arm64-musl is added to optionalDependencies #1168 exists precisely because they drifted (@optave/codegraph-linux-arm64-musl is mapped in ci-install-native.mjs and the CI parity job, but was absent from EXPECTED_LIBC).
  • This change derives the package list from optionalDependencies and infers the expected libc value from the napi-rs -gnu/-musl suffix. New linux platforms added to optionalDependencies are now covered automatically with no script edit.
  • An unknown suffix (e.g. linux-armhf-gnueabihf) fails loudly with a pointer to the inference rule, so silent gaps cannot reappear.

Test plan

  • Happy path: node scripts/verify-lockfile-libc.mjs on the current lockfile → package-lock.json libc discriminators OK (3 linux package(s) checked).
  • Derivation: temporarily added @optave/codegraph-linux-arm64-musl to optionalDependencies and re-ran — verifier reports missing from package-lock.json and exits 1 (proves the script picks up new entries without code changes).
  • Unknown-suffix guard: temporarily added @optave/codegraph-linux-armhf-gnueabihf — verifier reports the suffix is unknown, points at LIBC_BY_SUFFIX, and exits 1.
  • CI lint job remains green on this branch.

Closes #1168

EXPECTED_LIBC was a hand-maintained map duplicating knowledge already in
package.json's optionalDependencies. Adding a new linux-* platform required
updating both, and #1168 exists because they drifted (arm64-musl is mapped
in the CI parity job but absent here).

Derive the package list from optionalDependencies and infer expected libc
from the napi-rs -gnu/-musl suffix. New linux platforms added to
optionalDependencies are now covered automatically. An unknown suffix
fails loudly so silent gaps cannot reappear.
@claude
Copy link
Copy Markdown

claude Bot commented May 19, 2026

Claude finished @carlos-alm's task —— View job


Reviewing PR: build: derive libc verifier scope from optionalDependencies

Todo List

  • Read and analyze the modified script file
  • Check related context files (package.json, etc.)
  • Apply 5-phase review framework
  • Provide comprehensive review feedback

Root Cause Analysis

Problem Being Solved: Issue #1168 - The verify-lockfile-libc.mjs script maintained a hardcoded EXPECTED_LIBC map that duplicated package names already declared in package.json's optionalDependencies. When new linux platforms were added, both locations needed updates, and they drifted out of sync (@optave/codegraph-linux-arm64-musl was missing from the manual list).

Why This Approach: The solution derives the package list dynamically from optionalDependencies and infers the expected libc value from the napi-rs naming convention (-gnuglibc, -muslmusl). This eliminates the dual-maintenance burden and automatically covers new platforms.

Risk Assessment: Low risk. This is a build-time validation script with clear error handling and explicit testing in the PR description.

Backlog Compliance

  • Zero-dep: ✓ — No new runtime dependencies, only uses Node.js built-in modules
  • Foundation-aligned: ✓ — Improves build tooling without violating core principles
  • Problem-fit: 3 — Useful build system improvement but doesn't directly address the core "lost AI" problem
  • Breaking: No — Pure build script improvement with no API/CLI changes
  • Tier: 1

Code Quality Analysis

Strengths ⭐⭐⭐⭐

  • Clean elimination of duplication: Removes the hand-maintained EXPECTED_LIBC map in favor of dynamic derivation from authoritative source (package.json)
  • Robust error handling: Unknown suffixes fail loudly with clear guidance on how to extend the mapping
  • Clear intent: Well-documented logic and comments explain the napi-rs naming convention
  • Defensive programming: Handles edge cases (no linux packages, missing package-lock entries)
  • Proper path resolution: Uses import.meta.url and proper path resolution to work from any CWD

Technical Implementation

The regex LINUX_PKG_PATTERN = /^@optave\/codegraph-linux-[^-]+-(gnu|musl)$/ correctly matches the expected package naming pattern and captures the libc suffix. The mapping logic is sound:

const expectedLibc = LIBC_BY_SUFFIX[match[1]]; // gnu → glibc, musl → musl

Potential Concerns (Minor)

  1. Line 74-75: The error message uses import.meta.url.replace('file://', '') which could be simplified with fileURLToPath(import.meta.url) for consistency, but this is stylistic.

  2. Future-proofing: The current implementation assumes the napi-rs naming convention will remain stable. If new libc variants emerge, the script will fail loudly (which is good), but the error message could be slightly clearer about this being an expected failure mode.

Test Integrity ✅

The PR author manually validated:

  • Happy path execution
  • Detection of missing packages when temporarily adding new entries
  • Unknown suffix detection and clear error messaging

Final Recommendation

Minor suggestions (non-blocking):

  1. Consider using fileURLToPath(import.meta.url) instead of string replacement on line 74 for consistency
  2. The script works correctly and the approach is sound

Job Run

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 19, 2026

Greptile Summary

This PR eliminates the manual EXPECTED_LIBC map in scripts/verify-lockfile-libc.mjs by reading package.json's optionalDependencies at runtime and inferring expected libc values from each package name's -gnu/-musl suffix. The regex is derived from LIBC_BY_SUFFIX keys so the two can never drift.

Confidence Score: 5/5

Safe to merge — a focused script refactor that removes a maintenance footgun without changing the lockfile validation logic.

The change replaces a static map with a derivation from optionalDependencies, keeping all the same lockfile checks intact. The regex is constructed from the same keys as the lookup map, so they are structurally coupled. Edge cases (empty package list, unknown suffix) are handled explicitly and exit non-zero. No data is written, no external systems are touched, and the test plan confirms the happy path and both failure modes.

No files require special attention.

Important Files Changed

Filename Overview
scripts/verify-lockfile-libc.mjs Replaces hardcoded EXPECTED_LIBC map with derivation from package.json optionalDependencies; adds unknown-suffix guard and count in success message. Logic is sound and well-structured.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Start] --> B[Read package.json]
    B --> C[Read package-lock.json]
    C --> D[Filter optionalDependencies\nfor @optave/codegraph-linux-*]
    D --> E{linuxPackages\nempty?}
    E -- Yes --> F[Log: skipping\nexit 0]
    E -- No --> G[For each package:\nmatch LINUX_PKG_PATTERN]
    G --> H{Regex\nmatch?}
    H -- No --> I[Collect into\nunknownSuffixes]
    H -- Yes --> J[Look up expectedLibc\nfrom LIBC_BY_SUFFIX]
    J --> K{Entry in\nlockfile?}
    K -- No --> L[Collect into failures:\nmissing from lockfile]
    K -- Yes --> M{libc field\ncorrect?}
    M -- No --> N[Collect into failures:\nwrong libc value]
    M -- Yes --> O[OK]
    I --> P{unknownSuffixes\nnon-empty?}
    L --> P
    N --> P
    O --> P
    P -- Yes --> Q[Print unknown suffix error\nwith fix instructions\nexit 1]
    P -- No --> R{failures\nnon-empty?}
    R -- Yes --> S[Print libc failures\nexit 1]
    R -- No --> T[Log: OK N packages checked\nexit 0]
Loading

Reviews (8): Last reviewed commit: "Merge branch 'main' into fix/1168-libc-v..." | Re-trigger Greptile

Comment on lines +68 to +78
if (unknownSuffixes.length > 0) {
console.error(
'package-lock.json libc discriminator check cannot infer expected libc for:\n',
);
for (const name of unknownSuffixes) console.error(` - ${name}`);
console.error(
`\nExtend LIBC_BY_SUFFIX in ${import.meta.url.replace('file://', '')}\n` +
'to cover the new suffix (current rule: -gnu → glibc, -musl → musl).',
);
process.exit(1);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Incomplete fix guidance in unknown-suffix error

The error message tells the developer to "Extend LIBC_BY_SUFFIX", but that alone is not sufficient to fix the problem. A future suffix like gnueabihf would also require updating LINUX_PKG_PATTERN (specifically the (gnu|musl) capture group), because the regex is the gatekeeper that determines what reaches the map lookup — extending the map alone leaves the package permanently matched as an unknown suffix and exiting 1. A developer following only the printed instructions would be confused when adding a new map entry doesn't resolve the failure.

Fix in Claude Code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 4af75ec — derived LINUX_PKG_PATTERN from Object.keys(LIBC_BY_SUFFIX) so the regex and the map can never drift apart. Adding a new suffix to the map is now genuinely the only required edit (the regex picks it up automatically). The error message has been updated to reflect this and to list the current rules from the map dynamically.

carlos-alm and others added 5 commits May 19, 2026 18:29
Greptile flagged that the unknown-suffix error message told developers to
extend LIBC_BY_SUFFIX, but the regex (gnu|musl) capture group is the actual
gatekeeper — adding only a map entry would leave the package permanently
unmatched. Derive LINUX_PKG_PATTERN from Object.keys(LIBC_BY_SUFFIX) so the
regex and map can never drift apart, and update the error to reflect that
adding a map entry is now genuinely the only required change.

Also switch the script path interpolation to use fileURLToPath(import.meta.url)
instead of string-replacing the file:// scheme, per Claude's review note.
@carlos-alm
Copy link
Copy Markdown
Contributor Author

Thanks for the review! Addressed both points in 4af75ec:

  • Switched the script-path interpolation from import.meta.url.replace('file://', '') to fileURLToPath(import.meta.url) (cached in a __filename const alongside the existing __dirname derivation).
  • Also addressed Greptile's related concern by deriving LINUX_PKG_PATTERN from Object.keys(LIBC_BY_SUFFIX), so the regex and the map can no longer drift apart when a new libc suffix is added. The unknown-suffix error message now lists the active rule dynamically from the map.

@carlos-alm
Copy link
Copy Markdown
Contributor Author

@greptileai

@carlos-alm carlos-alm merged commit 3bb8343 into main May 20, 2026
21 checks passed
@carlos-alm carlos-alm deleted the fix/1168-libc-verifier-derive branch May 20, 2026 16:59
@github-actions github-actions Bot locked and limited conversation to collaborators May 20, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

follow-up: extend lockfile libc verifier when linux-arm64-musl is added to optionalDependencies

1 participant