Skip to content

feat(ci): add codec perf/CI gates#38

Merged
bbopen merged 3 commits intomainfrom
feat/codec-ci-gates
Jan 17, 2026
Merged

feat(ci): add codec perf/CI gates#38
bbopen merged 3 commits intomainfrom
feat/codec-ci-gates

Conversation

@bbopen
Copy link
Owner

@bbopen bbopen commented Jan 17, 2026

Summary

  • add codec decode performance budgets gated by TYWRAP_PERF_BUDGETS
  • document codec perf env vars in configuration
  • run codec-suite in CI on codec PRs and on main pushes

Testing

  • NODE_OPTIONS=--expose-gc TYWRAP_PERF_BUDGETS=1 npm test -- test/codec-performance.test.ts

Closes #27

@coderabbitai
Copy link

coderabbitai bot commented Jan 17, 2026

📝 Walkthrough

Walkthrough

Updated CI triggers for the codec-suite job, added documentation entries for codec performance environment variables, and introduced/adjusted Node.js performance tests that enforce configurable time and memory budgets for codec decode paths.

Changes

Cohort / File(s) Summary
CI Workflow Configuration
.github/workflows/ci.yml
Modified codec-suite job triggers: now runs on push and on pull_request events when labeled area:codec, in addition to schedule and workflow_dispatch. Conditional uses folded YAML form.
Performance Configuration Documentation
docs/configuration.md
Added environment variable declarations under Performance tuning: TYWRAP_PERF_BUDGETS, TYWRAP_PERF_TIME_BUDGET_MS, TYWRAP_PERF_MEMORY_BUDGET_MB, TYWRAP_CODEC_PERF_ITERATIONS, TYWRAP_CODEC_PERF_TIME_BUDGET_MS, TYWRAP_CODEC_PERF_MEMORY_BUDGET_MB.
Codec Performance Test
test/codec-performance.test.ts
New Node-only performance test benchmarking decodeValue() across three representative envelopes; measures heap before/after, runs configurable iterations, and asserts against time and memory budgets controlled by env vars.
Performance Test Run Condition
test/performance-budgets.test.ts
Adjusted run condition to remove CI gate: tests now run when Node.js and TYWRAP_PERF_BUDGETS === '1' (no additional CI check). Added node:perf_hooks import.

Sequence Diagram(s)

(omitted)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

priority:p2

Poem

🐇 I hop through loops and count each beat,
Timers ticking, memory neat,
CI winks as I race the clock,
Decode, measure—tick-tock-tock!
A rabbit cheers: the tests are sweet.

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 'feat(ci): add codec perf/CI gates' directly summarizes the main change: adding performance gates and CI coverage for codec testing.
Description check ✅ Passed The description clearly relates to the changeset, covering performance budgets, documentation, and CI configuration changes with a specific testing example.
Linked Issues check ✅ Passed The PR addresses all three coding requirements from issue #27: performance budgets are documented in configuration and enforced via environment variables in tests, CI coverage is added via workflow trigger on codec PRs, and performance tests check regression constraints.
Out of Scope Changes check ✅ Passed All changes are directly scoped to the linked issue objectives: CI workflow updates, performance budget documentation, and codec performance test implementation with no extraneous modifications.

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

✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ea4215c and 2fb604a.

📒 Files selected for processing (2)
  • test/codec-performance.test.ts
  • test/performance-budgets.test.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: codec-suite
🔇 Additional comments (5)
test/performance-budgets.test.ts (2)

1-1: LGTM on explicit perf_hooks import.

Using an explicit import for performance from node:perf_hooks ensures consistent behavior across Node.js versions.


13-14: LGTM on the gating condition.

Requiring the explicit TYWRAP_PERF_BUDGETS === '1' flag (without an implicit CI fallback) gives precise control over when performance tests execute.

test/codec-performance.test.ts (3)

1-7: LGTM on imports and gating logic.

The explicit node:perf_hooks import and the TYWRAP_PERF_BUDGETS === '1' gating (without implicit CI fallback) address previous review feedback correctly.


12-16: Good extraction of runGc helper.

Clean abstraction for conditional GC invocation.


25-66: Test envelope definitions look good.

The three envelopes (sparseEnvelope, torchEnvelope, sklearnEnvelope) provide representative coverage of different codec types with appropriate metadata structures.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


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

@bbopen bbopen added the enhancement New feature or request label Jan 17, 2026
@bbopen bbopen self-assigned this Jan 17, 2026
@bbopen bbopen added area:ci Area: CI and automation area:codec Area: codecs and serialization priority:p3 Priority P3 (low) labels Jan 17, 2026
@coderabbitai coderabbitai bot added area:tooling Area: tooling and CLI documentation Improvements or additions to documentation labels Jan 17, 2026
Copy link

@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

🤖 Fix all issues with AI agents
In `@docs/configuration.md`:
- Around line 304-309: The three new env vars (TYWRAP_PERF_BUDGETS,
TYWRAP_PERF_TIME_BUDGET_MS, TYWRAP_PERF_MEMORY_BUDGET_MB) lack inline
descriptions; update the block to add brief comments next to each export
explaining their purpose and units — mention that TYWRAP_PERF_BUDGETS toggles
enforcement of performance budget checks in the test suite (see
test/performance-budgets.test.ts), TYWRAP_PERF_TIME_BUDGET_MS is the per-test
time budget in milliseconds, and TYWRAP_PERF_MEMORY_BUDGET_MB is the per-test
memory budget in MB (follow the style of the TYWRAP_CODEC_MAX_BYTES comment for
wording and placement) so users know what triggers checks and how to adjust
values.

In `@test/codec-performance.test.ts`:
- Around line 10-12: Validate the parsed environment variables iterations,
timeBudgetMs, and memoryBudgetMb after calling Number() in
test/codec-performance.test.ts and handle NaN: check Number.isFinite(...) (or
!Number.isNaN(...)) for each variable and either fall back to the intended
default values or throw a clear error if invalid; update the initialization
logic for iterations, timeBudgetMs, and memoryBudgetMb to perform this
validation so the test loop has deterministic numeric bounds and fails loudly on
bad env input.
- Around line 57-77: The memory assertion is unreliable when global.gc isn't
exposed; update the test to detect whether global.gc exists and, if not, skip
(or only warn about) the memory assertion: before calling process.memoryUsage()
for the final assertion, check global.gc and only run the endMem/deltaMem and
expect(deltaMem).toBeLessThan(memoryBudgetMb * 1024 * 1024) when global.gc is
present, otherwise log a concise warning and omit the memory expect; keep the
timing assertions and the
decodeValue(sparseEnvelope/torchEnvelope/sklearnEnvelope) loop unchanged so
behavior is identical in CI where NODE_OPTIONS=--expose-gc.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5c7b4f5 and 9ca0a10.

📒 Files selected for processing (3)
  • .github/workflows/ci.yml
  • docs/configuration.md
  • test/codec-performance.test.ts
🔇 Additional comments (3)
.github/workflows/ci.yml (1)

162-167: LGTM!

The expanded trigger conditions correctly implement the PR objective: the codec-suite now runs on pushes to main and on PRs labeled area:codec, in addition to scheduled/manual runs. The multi-line YAML syntax is correct.

test/codec-performance.test.ts (2)

14-55: LGTM!

The test envelopes are well-structured and represent key codec types (sparse matrices, torch tensors, sklearn estimators). Using as const for type narrowing is a good practice. The coverage aligns with the PR objective of adding performance gates for codec decode paths.


5-6: LGTM!

The conditional execution logic correctly gates the performance tests to run only in Node.js when either in CI or explicitly opted-in via TYWRAP_PERF_BUDGETS=1. This prevents accidental test failures in environments where performance characteristics differ.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@bbopen
Copy link
Owner Author

bbopen commented Jan 17, 2026

@coderabbitai review\n\nPlease run a full review.

@coderabbitai
Copy link

coderabbitai bot commented Jan 17, 2026

@bbopen, I'll conduct a full review of the changes in this PR.

✅ Actions performed

Full review triggered.

Copy link

@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: 2

🤖 Fix all issues with AI agents
In `@test/codec-performance.test.ts`:
- Around line 69-77: The test currently uses the global performance variable
(used in the start/duration timing around the decodeValue loop), which can be
brittle; add an explicit import of performance from the perf_hooks module and
use that imported symbol (performance) in the timing code (around the start =
performance.now() and duration = performance.now() - start) so the test reliably
uses Node's perf_hooks API; ensure the import appears at the top of the test
file alongside other imports.
- Around line 5-6: Update the test gating logic so performance-budget tests only
run when the explicit TYWRAP_PERF_BUDGETS flag is set: change the shouldRun
expression that currently uses isNodejs() && (process.env.CI ||
process.env.TYWRAP_PERF_BUDGETS === '1') to require
process.env.TYWRAP_PERF_BUDGETS === '1' (retaining isNodejs() check if needed),
so describeBudget (and the shouldRun definition) will not rely on
process.env.CI; apply the same change to the other instance in
performance-budgets.test.ts to keep behavior consistent.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9ca0a10 and ea4215c.

📒 Files selected for processing (1)
  • test/codec-performance.test.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: codec-suite

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@bbopen
Copy link
Owner Author

bbopen commented Jan 17, 2026

@coderabbitai review incremental changes for perf budget gating + perf_hooks import.

@coderabbitai
Copy link

coderabbitai bot commented Jan 17, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Read the codec performance test file to analyze perf_hooks import and budget gating
cat test/codec-performance.test.ts

Length of output: 2559


🏁 Script executed:

#!/bin/bash
# Read the CI workflow file to check how perf budgets are integrated
cat .github/workflows/ci.yml

Length of output: 8854


🏁 Script executed:

#!/bin/bash
# Check if there are any other files that import or use perf_hooks
rg -n "perf_hooks" --type ts

Length of output: 728


@bbopen, I've reviewed the incremental changes for performance budget gating and perf_hooks import. Here's my analysis:

✅ Performance Budget Gating

The gating implementation is well-designed:

The budget gating uses a clean pattern with shouldRun = isNodejs() && process.env.TYWRAP_PERF_BUDGETS === '1' and describeBudget = shouldRun ? describe : describe.skip, which ensures tests:

  • Only run in Node.js environments (not browsers/Bun)
  • Are explicitly opt-in via the TYWRAP_PERF_BUDGETS='1' flag
  • Gracefully skip when conditions aren't met

The three configurable budget parameters with sensible defaults are well-chosen:

  • TYWRAP_CODEC_PERF_ITERATIONS (default: 200)
  • TYWRAP_CODEC_PERF_TIME_BUDGET_MS (default: 500)
  • TYWRAP_CODEC_PERF_MEMORY_BUDGET_MB (default: 32)

✅ perf_hooks Import

The import uses the modern node:perf_hooks prefix, which is the recommended approach for Node.js built-in modules (available since Node.js 14.18.0+). This provides better clarity and avoids potential conflicts with user-land packages.

Minor observation: I noticed the codebase has mixed import styles for perf_hooks:

  • New file: 'node:perf_hooks'
  • Some existing files: 'perf_hooks' (without prefix)

While not critical, you might consider standardizing to the node: prefix across the codebase for consistency.

✅ CI Integration

The CI workflow properly enables performance budgets in the test and os jobs:

env:
  NODE_OPTIONS: --expose-gc
  TYWRAP_PERF_BUDGETS: '1'

This ensures:

  • global.gc() is available for accurate memory measurements
  • Performance budgets are enforced in CI

The codec-suite job correctly does NOT set these variables since it runs different codec tests.

Overall: The implementation is production-ready! 🚀

@coderabbitai coderabbitai bot added the priority:p2 Priority P2 (medium) label Jan 17, 2026
Copy link

@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: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
test/performance-budgets.test.ts (2)

78-79: Add validation for parsed environment variables.

If environment variables are set to non-numeric values, Number() returns NaN, causing assertions to behave unexpectedly (e.g., duration < NaN is always false).

🔧 Proposed fix with validation
-    const timeBudgetMs = Number(process.env.TYWRAP_PERF_TIME_BUDGET_MS ?? '2000');
-    const memoryBudgetMb = Number(process.env.TYWRAP_PERF_MEMORY_BUDGET_MB ?? '64');
+    const timeBudgetMs = Number(process.env.TYWRAP_PERF_TIME_BUDGET_MS ?? '2000');
+    const memoryBudgetMb = Number(process.env.TYWRAP_PERF_MEMORY_BUDGET_MB ?? '64');
+
+    expect(Number.isFinite(timeBudgetMs) && timeBudgetMs > 0).toBe(true);
+    expect(Number.isFinite(memoryBudgetMb) && memoryBudgetMb > 0).toBe(true);

Consider extracting a shared helper (like readEnvNumber in codec-performance.test.ts) with built-in validation for consistency across both test files.


93-101: Memory budget assertion may be unreliable without --expose-gc.

Without --expose-gc, the global.gc call is skipped, leaving unreleased garbage in the heap. This makes deltaMem unpredictable and the assertion flaky for local runs.

♻️ Option: Skip memory assertion when GC isn't exposed
     const duration = performance.now() - startTime;
-    if (global.gc) {
-      global.gc();
-    }
+    const hasGc = typeof global.gc === 'function';
+    if (hasGc) {
+      global.gc();
+    }
     const endMem = process.memoryUsage().heapUsed;
     const deltaMem = endMem - startMem;

     expect(result.typescript.length).toBeGreaterThan(0);
     expect(duration).toBeLessThan(timeBudgetMs);
-    expect(deltaMem).toBeLessThan(memoryBudgetMb * 1024 * 1024);
+    if (hasGc) {
+      expect(deltaMem).toBeLessThan(memoryBudgetMb * 1024 * 1024);
+    }
♻️ Duplicate comments (2)
test/codec-performance.test.ts (2)

9-10: Add validation to readEnvNumber for non-numeric values.

If environment variables are set to non-numeric strings, Number() returns NaN. This causes the loop to never execute (i < NaN is always false) and assertions to pass vacuously or fail unexpectedly.

🔧 Proposed fix with validation
-const readEnvNumber = (name: string, fallback: string): number =>
-  Number(process.env[name] ?? fallback);
+const readEnvNumber = (name: string, fallback: string): number => {
+  const value = Number(process.env[name] ?? fallback);
+  if (!Number.isFinite(value) || value <= 0) {
+    throw new Error(`Invalid env ${name}: expected positive number, got '${process.env[name]}'`);
+  }
+  return value;
+};

78-84: Memory budget assertion may be unreliable without --expose-gc.

Without --expose-gc, the runGc() call is a no-op, leaving unreleased garbage in the heap. This makes deltaMem unpredictable. CI sets NODE_OPTIONS=--expose-gc, but local runs may not.

♻️ Option: Skip memory assertion when GC isn't exposed
     const duration = performance.now() - start;
     runGc();
     const endMem = process.memoryUsage().heapUsed;
     const deltaMem = endMem - startMem;

     expect(duration).toBeLessThan(timeBudgetMs);
-    expect(deltaMem).toBeLessThan(memoryBudgetBytes);
+    if (global.gc) {
+      expect(deltaMem).toBeLessThan(memoryBudgetBytes);
+    }
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ea4215c and 2fb604a.

📒 Files selected for processing (2)
  • test/codec-performance.test.ts
  • test/performance-budgets.test.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: codec-suite
🔇 Additional comments (5)
test/performance-budgets.test.ts (2)

1-1: LGTM on explicit perf_hooks import.

Using an explicit import for performance from node:perf_hooks ensures consistent behavior across Node.js versions.


13-14: LGTM on the gating condition.

Requiring the explicit TYWRAP_PERF_BUDGETS === '1' flag (without an implicit CI fallback) gives precise control over when performance tests execute.

test/codec-performance.test.ts (3)

1-7: LGTM on imports and gating logic.

The explicit node:perf_hooks import and the TYWRAP_PERF_BUDGETS === '1' gating (without implicit CI fallback) address previous review feedback correctly.


12-16: Good extraction of runGc helper.

Clean abstraction for conditional GC invocation.


25-66: Test envelope definitions look good.

The three envelopes (sparseEnvelope, torchEnvelope, sklearnEnvelope) provide representative coverage of different codec types with appropriate metadata structures.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@bbopen bbopen merged commit 16fac47 into main Jan 17, 2026
20 checks passed
@bbopen bbopen deleted the feat/codec-ci-gates branch January 17, 2026 16:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:ci Area: CI and automation area:codec Area: codecs and serialization area:tooling Area: tooling and CLI documentation Improvements or additions to documentation enhancement New feature or request priority:p2 Priority P2 (medium) priority:p3 Priority P3 (low)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Codec roadmap: performance gates + CI coverage

1 participant