Skip to content

@relayburn/sdk: npm umbrella + napi-rs CI matrix + conformance scaffold (#247 part b)#308

Merged
willwashburn merged 7 commits intomainfrom
rust-port/sdk-node-umbrella
May 6, 2026
Merged

@relayburn/sdk: npm umbrella + napi-rs CI matrix + conformance scaffold (#247 part b)#308
willwashburn merged 7 commits intomainfrom
rust-port/sdk-node-umbrella

Conversation

@willwashburn
Copy link
Copy Markdown
Member

Wave 1 D2 of the Rust port (epic #240). Stands up the npm packaging side of the napi-rs deliverable so #247-a (parallel agent — crates/relayburn-sdk-node source) has a target to publish into.

Refs #247.

Summary

  • packages/sdk-node/ umbrella + per-platform packages. @relayburn/sdk@2.0.0-pre.0 resolves the right native binary via optionalDependencies on @relayburn/sdk-{darwin-arm64,darwin-x64,linux-arm64-gnu,linux-x64-gnu} (napi-rs's standard layout — same shape as @napi-rs/canvas, @swc/core, etc.). Win32 deferred to a follow-up.
  • TS facade (src/index.js ESM + src/index.cjs) re-exports the six verbs (ingest, summary, sessionCost, overhead, overheadTrim, hotspots) plus compare and the Ledger constructor — no behavior, just pass-through to ./binding.js. src/index.d.ts mirrors packages/sdk/index.d.ts byte-for-byte modulo bigint for u64 token counts.
  • napi-rs build matrix in .github/workflows/napi-build.yml: builds prebuilt .node for darwin-arm64, darwin-x64, linux-x64-gnu, linux-arm64-gnu (latter via cross). Runs on PRs touching crates/relayburn-sdk{,-node}/** or packages/sdk-node/**, on main pushes, and on workflow_dispatch. Publish step is a stub — full wiring lands in [Rust port] Lockstep 2.0 release (TS 1.x final + cutover + npm deprecate) #249 (Wave 3 cutover).
  • Conformance scaffold (test/conformance.test.js): deep-equals TS @relayburn/sdk@1.x vs the napi-rs umbrella across all six verbs against the existing fixture ledger. Skips when RELAYBURN_SDK_NAPI_BUILT is unset (the default while [Rust port] relayburn-sdk-node + @relayburn/sdk 2.0 (napi-rs) #247-a is in flight). Once [Rust port] relayburn-sdk-node + @relayburn/sdk 2.0 (napi-rs) #247-a's bindings produce a real *.node artifact, flipping the env var in napi-build.yml enables the gate.
  • esbuild bundle smoke test (test/esbuild-smoke.test.js): writes a tiny entry that imports every named export from @relayburn/sdk, runs it through esbuild with native-binding externals, and asserts no errors / warnings. Catches resolution / bundling regressions for downstream embedders.
  • pnpm-workspace.yaml: excludes packages/sdk-node so its @relayburn/sdk@2.0.0-pre.0 doesn't shadow packages/sdk@1.10.0 during workspace:* resolution in cli / mcp. CI installs sdk-node deps standalone via pnpm install --ignore-workspace.

Coordination with #247-a

This PR does not touch crates/relayburn-sdk-node/src/lib.rs or Cargo.toml — that's #247-a's scope (parallel agent on rust-port/sdk-node-skeleton). I assumed the bindings will export the seven verb names listed above, matching packages/sdk/index.d.ts. If #247-a settles on different export names (e.g. snake_case from Rust → camelCase via napi-rs), packages/sdk-node/src/index.{js,cjs,d.ts} needs a one-line edit per verb.

TODOs left for the #247-a-after-merge follow-up:

  • Replace src/binding.{js,d.ts} stubs once napi build runs cleanly against a real binding crate (the napi-rs CLI overwrites both files with generated output).
  • Flip RELAYBURN_SDK_NAPI_BUILT=1 in .github/workflows/napi-build.yml to enable the conformance gate.
  • Wave 2 D9 (separate PR): generated .d.ts snapshot test + bigint boundary docs.
  • Wave 2 D10 (separate PR): publish 2.0.0-pre to npm under next tag.

Test plan

  • CI green on napi-build.yml's build matrix (4 platforms; the build step may produce empty addons until [Rust port] relayburn-sdk-node + @relayburn/sdk 2.0 (napi-rs) #247-a lands its #[napi] exports — that's expected).
  • pnpm install --frozen-lockfile && pnpm run build && pnpm run test at the workspace root passes (verifies the workspace exclusion didn't break the 1.x packages).
  • cd packages/sdk-node && pnpm install --ignore-workspace && node --test test/ passes locally — esbuild smoke test runs, conformance tests skip cleanly.
  • cargo build --workspace && cargo test --workspace still green.
  • After [Rust port] relayburn-sdk-node + @relayburn/sdk 2.0 (napi-rs) #247-a lands: flip RELAYBURN_SDK_NAPI_BUILT=1, confirm conformance suite turns green on all four matrix legs.

🤖 Generated with Claude Code

…ce gate

Wave 1 D2 of the Rust port (#247 part b). Stands up the npm packaging
side of the napi-rs deliverable while #247-a (parallel agent) fills in
crates/relayburn-sdk-node.

- packages/sdk-node/: umbrella @relayburn/sdk@2.0.0-pre.0 with thin TS
  facade re-exporting the napi-rs verbs (ingest, summary, sessionCost,
  overhead, overheadTrim, hotspots, compare). Resolves the right native
  binary via optionalDependencies on @relayburn/sdk-{darwin-arm64,
  darwin-x64,linux-arm64-gnu,linux-x64-gnu}. win32-x64-msvc deferred
  to a #247 follow-up.
- pnpm-workspace.yaml: exclude packages/sdk-node so the 2.x umbrella
  doesn't collide with the 1.x @relayburn/sdk during workspace:*
  resolution. CI installs sdk-node deps with --ignore-workspace.
- .github/workflows/napi-build.yml: napi-rs build matrix (darwin-arm64,
  darwin-x64, linux-arm64-gnu, linux-x64-gnu) on PRs / main / dispatch.
  Caches Cargo + pnpm. Publish step is stubbed; full wiring lands in
  #249 (lockstep release workflow).
- packages/sdk-node/test/conformance.test.js: deep-equal scaffold
  comparing TS @relayburn/sdk@1.x vs the napi-rs umbrella across the
  six verbs. Skips while #247-a is in flight; flip
  RELAYBURN_SDK_NAPI_BUILT=1 in CI once bindings land.
- packages/sdk-node/test/esbuild-smoke.test.js: bundles the umbrella
  facade through esbuild with native-binding externals — catches
  resolution / bundling regressions for downstream embedders.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 6, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a Node.js N-API v2 SDK under packages/sdk-node/ (TS types, ESM/CJS facades, binding loader), per-platform prebuilt package manifests, esbuild and conformance tests, a pnpm workspace exclusion, and a GitHub Actions "napi build" workflow that builds and uploads .node artifacts across macOS and Linux targets.

Changes

Node SDK v2.x with N-API Bindings & CI

Layer / File(s) Summary
Data Shape / Declarations
packages/sdk-node/src/index.d.ts, packages/sdk-node/src/binding.d.ts
Adds full TypeScript public surface for SDK v2 (Ledger.open, ingest, summary, sessionCost, overhead, overheadTrim, hotspots, compare) with option/result interfaces and bigint-capable token counts.
Platform Manifests
packages/sdk-node/npm/*/package.json, packages/sdk-node/npm/*/README.md
Adds per-platform prebuilt npm package manifests and READMEs for darwin-arm64, darwin-x64, linux-arm64-gnu, linux-x64-gnu describing binary metadata and resolution via optionalDependencies.
Binding Loader / Runtime Wiring
packages/sdk-node/src/binding.cjs
Implements native-binding loader with platform/arch and musl detection, prefers local .node, falls back to optionalDependencies, records underlying load errors and surfaces detailed guidance when missing.
Facade Implementations
packages/sdk-node/src/index.js, packages/sdk-node/src/index.cjs, packages/sdk-node/src/binding.d.ts
Adds ESM and CommonJS umbrella facades that import the binding and re-expose Ledger and verb functions as thin wrappers delegating to the native binding; adds corresponding binding declarations.
Package Config & Workspace
packages/sdk-node/package.json, pnpm-workspace.yaml
Adds umbrella package manifest with exports, napi config, scripts, devDeps, platform optionalDependencies; excludes packages/sdk-node from pnpm workspace resolution.
Tests / Smoke / Conformance
packages/sdk-node/test/esbuild-smoke.test.js, packages/sdk-node/test/conformance.test.js
Adds esbuild smoke test that bundles the umbrella facade and a conformance test scaffold comparing 1.x TS SDK to napi v2 for verbs (skips when binding missing).
CI Build & Artifact Distribution
.github/workflows/napi-build.yml
New GitHub Actions workflow "napi build" with matrix across macOS/Linux targets, sets up pnpm/Node/Rust (including cross), builds per-target napi bindings, caches cargo/git, uploads .node artifacts, runs smoke test (conformance skipped), and includes a stubbed publish job for dispatch.
Changelog & Docs
CHANGELOG.md, packages/sdk-node/CHANGELOG.md, packages/sdk-node/README.md
Adds root and SDK changelog entries and README documenting 2.x SDK, platform mapping, migration notes (bigint guidance), status, and package layout.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor "GitHub Actions" as GH
  participant "Runner (macOS/Linux)" as Runner
  participant "pnpm / Node" as NodeTool
  participant "Rust / cross" as RustTool
  participant "Artifacts (upload)" as Artifacts
  participant "Tests (esbuild/conformance)" as Tests
  participant "Publish Job (stub)" as Publish

  GH->>Runner: trigger workflow (push/pr/dispatch)
  Runner->>NodeTool: setup pnpm & Node
  Runner->>RustTool: setup Rust toolchain (cross for linux-arm64)
  Runner->>Runner: run matrix builds (per-target napi build)
  Runner->>Artifacts: produce and upload .node artifacts
  Runner->>Tests: run esbuild smoke (conformance skipped if bindings missing)
  alt workflow_dispatch publish=true
    Publish->>Artifacts: download artifacts
    Publish->>Publish: execute stubbed publish steps
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

  • AgentWorkforce/burn#299: Adds the Rust Ledger surface (Ledger::open and verb APIs) that these N-API bindings and facades expose.

Poem

🐰 I stitched some bindings, snug and spry,

Ledger.open and verbs now try.
CI hops builds across each board,
Artifacts bundled, tests explored.
A little rabbit hums—SDK, hooray!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.11% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding npm packaging and CI scaffolding for napi-rs bindings, with specific references to the umbrella package, matrix build, and conformance tests.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, covering all major components: npm umbrella, TS facade, CI workflow, conformance tests, esbuild smoke test, and workspace configuration.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch rust-port/sdk-node-umbrella

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 54d0f7fdf1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +66 to +68
if (existsSync(FIXTURE_LEDGER_DIR)) {
cpSync(FIXTURE_LEDGER_DIR, home, { recursive: true });
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Fail conformance when the fixture ledger is missing

This test gate silently falls back to an empty temporary ledgerHome whenever tests/fixtures/ledger is absent, so once RELAYBURN_SDK_NAPI_BUILT=1 is enabled the deep-equality checks can pass on near-empty outputs instead of real fixture data. In this repo there is no tests/fixtures/ledger tree, so the current behavior weakens the core parity check and can let Rust/TS divergences ship undetected.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Addressed in 7801bd9. Removed the empty-tempdir fallback in makeHome() and added an ensureFixtureLedger() precondition at the top of callBoth() that throws (with a seed hint) when tests/fixtures/ledger/ is missing. With RELAYBURN_SDK_NAPI_BUILT=1 and no fixture, all 7 conformance tests now fail loudly instead of tautologically passing on empty homes; with the env var unset the suite still skips. Documented the fixture location + seeding expectation in the file header so the gate can't be silently re-neutered.

Comment on lines +93 to +97
const VERBS = [
{ name: 'summary', opts: {} },
{ name: 'sessionCost', opts: { session: 'fixture-session-1' } },
{ name: 'overhead', opts: { project: REPO_ROOT } },
{ name: 'overheadTrim', opts: { project: REPO_ROOT, includeDiff: false } },
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Add compare() to the conformance verb matrix

The facade exports compare, but the conformance matrix only enumerates summary/sessionCost/overhead/overheadTrim/hotspots (with ingest separate), so behavioral drift in compare() will not be caught by the parity gate. Since 2.x is presented as a drop-in replacement, leaving this exported verb unverified creates a test hole in a user-facing API.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Addressed in 7801bd9. Added a compare row to the VERBS matrix with { models: ['claude-sonnet-4-5', 'claude-opus-4-7'], minFidelity: 'partial' } — both implementations see the same fixture so even rows with no matches surface as identical empty/no-data shapes, while drift in cell layout, fidelity accounting, or totals still fails the deep-equal. Updated the file-header verb count from 6 to 7 to match.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
packages/sdk-node/CHANGELOG.md (1)

5-9: 💤 Low value

Remove the issue/PR link from the changelog entry.

The coding guidelines specify to drop issue/PR links from package changelog entries. Remove (#247 part b) from the end of this entry.

Suggested fix
 - Initial scaffolding: umbrella package layout (`@relayburn/sdk`) +
   per-platform packages (`@relayburn/sdk-{darwin-arm64,darwin-x64,linux-arm64-gnu,linux-x64-gnu}`)
   resolved via `optionalDependencies`, TS facade re-exporting the napi-rs
-  binding, conformance scaffold against the TS 1.x SDK, esbuild bundle
-  smoke test. (`#247` part b)
+  binding, conformance scaffold against the TS 1.x SDK, esbuild bundle
+  smoke test.

As per coding guidelines: "drop issue/PR links, internal notes, backstory".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/sdk-node/CHANGELOG.md` around lines 5 - 9, Edit the CHANGELOG.md
entry that begins "Initial scaffolding: umbrella package layout..." and remove
the trailing issue/PR reference string "(`#247` part b)" so the line ends after
"smoke test."; ensure no other text is altered and commit the cleaned changelog
entry.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/napi-build.yml:
- Around line 52-54: The workflow currently uses the retired runner label "os:
macos-13" for the x86_64-apple-darwin matrix (see the block containing "target:
x86_64-apple-darwin" and "short: darwin-x64"); update that runner label to a
supported Intel macOS image such as "macos-15-intel" (or "macos-14-intel") so
the CI job continues to run, leaving the other keys ("target" and "short")
unchanged.

In `@packages/sdk-node/src/index.cjs`:
- Line 8: Rename the CommonJS export file binding.js to binding.cjs and update
all requires to the new filename: change require('./binding.js') to
require('./binding.cjs') in both index.cjs and index.js; also update the
package.json "files" array (and any build/generation scripts) to reference
binding.cjs instead of binding.js so consumers see the correct CommonJS
artifact. Ensure the renamed file preserves module.exports and that
index.cjs/index.js still use require(...) (not import) to load binding.cjs.

In `@packages/sdk-node/test/conformance.test.js`:
- Around line 118-132: The test comment promises a follow-up summary()
comparison but the code only compares the ingest return value; either update the
comment to remove the summary claim or implement the summary check: after
callBoth('ingest', {}) use callBoth('summary', {}) (same helper used in this
file) to get summary results and assert.deepStrictEqual(summary.napiResult,
summary.tsResult) to confirm both implementations wrote the same rows; update
the test block for test 'conformance: ingest() matches TS 1.x' and reference
callBoth, out.napiResult/out.tsResult, and the summary results when making the
change.

---

Nitpick comments:
In `@packages/sdk-node/CHANGELOG.md`:
- Around line 5-9: Edit the CHANGELOG.md entry that begins "Initial scaffolding:
umbrella package layout..." and remove the trailing issue/PR reference string
"(`#247` part b)" so the line ends after "smoke test."; ensure no other text is
altered and commit the cleaned changelog entry.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ac7f7d98-57ad-417e-b118-32e869b5fa51

📥 Commits

Reviewing files that changed from the base of the PR and between 09cc4e0 and 54d0f7f.

⛔ Files ignored due to path filters (1)
  • packages/sdk-node/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (21)
  • .github/workflows/napi-build.yml
  • CHANGELOG.md
  • packages/sdk-node/CHANGELOG.md
  • packages/sdk-node/README.md
  • packages/sdk-node/npm/darwin-arm64/README.md
  • packages/sdk-node/npm/darwin-arm64/package.json
  • packages/sdk-node/npm/darwin-x64/README.md
  • packages/sdk-node/npm/darwin-x64/package.json
  • packages/sdk-node/npm/linux-arm64-gnu/README.md
  • packages/sdk-node/npm/linux-arm64-gnu/package.json
  • packages/sdk-node/npm/linux-x64-gnu/README.md
  • packages/sdk-node/npm/linux-x64-gnu/package.json
  • packages/sdk-node/package.json
  • packages/sdk-node/src/binding.d.ts
  • packages/sdk-node/src/binding.js
  • packages/sdk-node/src/index.cjs
  • packages/sdk-node/src/index.d.ts
  • packages/sdk-node/src/index.js
  • packages/sdk-node/test/conformance.test.js
  • packages/sdk-node/test/esbuild-smoke.test.js
  • pnpm-workspace.yaml

Comment thread .github/workflows/napi-build.yml
Comment thread packages/sdk-node/src/index.cjs Outdated
Comment thread packages/sdk-node/test/conformance.test.js
Address review feedback on PR #308:

- Fail conformance fast when `RELAYBURN_SDK_NAPI_BUILT=1` is set but the
  fixture ledger at `tests/fixtures/ledger/` is missing. Previously the
  test silently fell back to an empty temp home, which would let the
  deep-equality checks pass on near-empty outputs once the gate was
  enabled.
- Add `compare()` to the conformance verb matrix so behavioral drift in
  the per-(model, activity) shape is caught alongside the other read
  verbs.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/sdk-node/test/conformance.test.js`:
- Around line 75-83: ensureFixtureLedger currently only verifies
FIXTURE_LEDGER_DIR exists, allowing empty or mis-copied fixtures to pass; update
ensureFixtureLedger to validate the fixture contents (e.g., require a small
manifest or specific expected filenames and that those files are non-empty or
exceed a minimal size). In practice, check FIXTURE_LEDGER_DIR for a required set
of files (use the existing FIXTURE_LEDGER_DIR constant and the
ensureFixtureLedger() function), verify each required file exists and is not
zero bytes (or validate a simple checksum/manifest if present), and throw a
descriptive Error if any file is missing or malformed so the conformance suite
fails closed on bad fixtures.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d0733ced-edc4-4e3d-98c6-a2a532993575

📥 Commits

Reviewing files that changed from the base of the PR and between 54d0f7f and 7801bd9.

📒 Files selected for processing (1)
  • packages/sdk-node/test/conformance.test.js

Comment thread packages/sdk-node/test/conformance.test.js
… (review fixes round 2)

- Rename src/binding.js to src/binding.cjs so Node can require it under
  the package's "type": "module" setting (ESM would reject module.exports
  on a bare .js). Update src/index.{js,cjs} requires, the package.json
  files array + build:napi script, and the napi-build workflow's
  --js flag to match. Add a binding.cjs header note explaining the
  extension choice so a future napi build regen doesn't accidentally
  flip back to .js.
- Bump the x86_64-apple-darwin matrix runner from macos-13 (retired
  Dec 2025) to macos-15-intel.
- Fix the ingest() conformance test comment: the "follow-up summary()
  comparison" was aspirational, not implemented; reword as a
  forward-looking note instead of describing absent behavior.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
packages/sdk-node/test/conformance.test.js (1)

75-84: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Harden fixture validation to prevent silent false-positives.

The current check only verifies the directory exists. An empty or malformed fixture would let both implementations return identical empty results, causing the deep-equality checks to pass vacuously.

Proposed hardening
+import { statSync } from 'node:fs';
...
 function ensureFixtureLedger() {
+  const ledgerJsonl = join(FIXTURE_LEDGER_DIR, 'ledger.jsonl');
   if (!existsSync(FIXTURE_LEDGER_DIR)) {
     throw new Error(
       `conformance fixture ledger missing at ${FIXTURE_LEDGER_DIR}. ` +
         `Seed it (e.g. cp -R ~/.relayburn tests/fixtures/ledger) before ` +
         `running with RELAYBURN_SDK_NAPI_BUILT=1, or unset the env var to ` +
         `skip the conformance suite.`,
     );
   }
+  if (!existsSync(ledgerJsonl) || statSync(ledgerJsonl).size === 0) {
+    throw new Error(
+      `conformance fixture is incomplete: expected a non-empty ${ledgerJsonl}`,
+    );
+  }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/sdk-node/test/conformance.test.js` around lines 75 - 84, The
ensureFixtureLedger function currently only checks for existence of
FIXTURE_LEDGER_DIR; update it to validate the fixture contents to avoid
empty/malformed fixtures by checking for required files and non-empty data:
inside ensureFixtureLedger validate that FIXTURE_LEDGER_DIR contains expected
files (e.g., any ledger file names your tests rely on) and that those files are
non-empty (size > 0) and/or parseable (e.g., attempt a lightweight JSON/format
parse if applicable); if any required file is missing, empty, or invalid, throw
an Error with a clear message referencing FIXTURE_LEDGER_DIR so the conformance
suite fails fast rather than producing vacuous equality results.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@packages/sdk-node/test/conformance.test.js`:
- Around line 75-84: The ensureFixtureLedger function currently only checks for
existence of FIXTURE_LEDGER_DIR; update it to validate the fixture contents to
avoid empty/malformed fixtures by checking for required files and non-empty
data: inside ensureFixtureLedger validate that FIXTURE_LEDGER_DIR contains
expected files (e.g., any ledger file names your tests rely on) and that those
files are non-empty (size > 0) and/or parseable (e.g., attempt a lightweight
JSON/format parse if applicable); if any required file is missing, empty, or
invalid, throw an Error with a clear message referencing FIXTURE_LEDGER_DIR so
the conformance suite fails fast rather than producing vacuous equality results.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a5fee9d4-3054-4018-8caa-31aa89d048f1

📥 Commits

Reviewing files that changed from the base of the PR and between 7801bd9 and b1d2995.

📒 Files selected for processing (6)
  • .github/workflows/napi-build.yml
  • packages/sdk-node/package.json
  • packages/sdk-node/src/binding.cjs
  • packages/sdk-node/src/index.cjs
  • packages/sdk-node/src/index.js
  • packages/sdk-node/test/conformance.test.js
✅ Files skipped from review due to trivial changes (1)
  • packages/sdk-node/package.json

willwashburn and others added 4 commits May 6, 2026 08:41
…nd 3)

Extend ensureFixtureLedger() to fail closed on malformed fixtures, not
just missing directories. The previous check (existsSync of the dir)
let an empty or wrongly-copied fixture pass: both impls would then
deep-equal identical empty outputs, tautologically green.

New preconditions, all with seed-hint messages:
  1. ledger.jsonl exists (canonical filename per packages/ledger/src/paths.ts).
  2. ledger.jsonl is non-empty.
  3. First line parses as JSON and matches LedgerLine shape (v:1 + known kind).
  4. At least one kind:'turn' line is present so summary/hotspots/compare
     have non-trivial input on both sides.

Manually exercised each failure path with RELAYBURN_SDK_NAPI_BUILT=1
against (a) empty dir, (b) empty file, (c) non-JSON content, and
(d) stamp-only fixture — each produced a distinct, actionable error.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@willwashburn willwashburn merged commit 54f0682 into main May 6, 2026
8 checks passed
@willwashburn willwashburn deleted the rust-port/sdk-node-umbrella branch May 6, 2026 14:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant