Skip to content

fix(retention): enforce Node 22 operator runtime - #1078

Open
qnbs wants to merge 3 commits into
mainfrom
codex/retention-node22-guard-20261017
Open

qnbs wants to merge 3 commits into
mainfrom
codex/retention-node22-guard-20261017

Conversation

@qnbs

@qnbs qnbs commented Oct 17, 2026 •

Copy link
Copy Markdown
Owner

User description

Summary

  • Require the retention operator process to run Node 22.19.0 or later in the 22.x line before argument handling, source checks, child processes, provider calls, runner imports, or lock acquisition.
  • Keep the application Node 22/24 CI matrix and package engine range unchanged.
  • Add subprocess regressions that prove unsupported and malformed runtimes stop with zero external CLI, provider, runner-import, or lock calls; run the suite through the existing Vitest wrapper on both supported CI matrix versions.

Evidence

  • Retention suites: 179/179 pass under Node 22.23.3 and 24.18.0.
  • Targeted Vitest wrapper passes under Node 22.23.3 and 24.18.0.
  • ci:prepush passes on the signed branch commit; change classification was conservative AMBIGUOUS, so the full local admission checks ran.
  • Owner maintenance authorization is recorded in ci(vercel): prune stale Preview deployments after PR closure #627 comment 6238132825.

Merge conditions

Merge only after current required checks, the repository's three review channels, and all security checks pass on the exact PR head. This change does not remediate the existing ZIP OSV findings.

Summary by Sourcery

Require the Vercel retention operator to run on a supported Node 22 runtime before performing any retention work.

Bug Fixes:

  • Enforce Node 22.19.0 or later in the 22.x line before retention operations can handle arguments, access sources or providers, import runners, or acquire locks.

Enhancements:

  • Document the retention operator runtime contract, refusal behavior, and verification guidance while keeping the application engine range and Node 22/24 CI matrix unchanged.

Documentation:

  • Document the required Node 22.19.0+ retention runtime and the new runtime-mismatch stop reason.

Tests:

  • Add runtime validation and subprocess regressions confirming unsupported or malformed runtimes produce no external CLI, provider, runner-import, or lock calls across dry-run and reconcile modes.

Chores:

  • Record the retention runtime requirement in the changelog.

Summary by cubic

Enforces the Vercel retention operator's Node 22.19.0+ runtime requirement. The operator now validates process.versions.node at entry and stops with RETENTION_STOP: runtime-mismatch on unsupported or malformed runtimes before argument handling, source checks, provider calls, runner imports, or lock acquisition, with no override.

Behavior

  • The application's Node 22/24 CI matrix and package engine range are unchanged.
  • Behavior tests stub the runtime so supported-runtime fixtures run under both CI matrix versions, and reconcile tests assert the exact deleted deployment IDs.
  • Adds subprocess regressions proving unsupported runtimes make zero external CLI, provider, runner-import, or lock calls.
  • Documents the runtime contract, the new stop reason, and runtime verification guidance, and records it in the changelog.

Written for commit 19dfd02. Summary will update on new commits.

View guided diff Turn on auto-fix


CodeAnt-AI Description

Require Node 22.19 or later for retention operations

What Changed

  • Retention dry runs and reconciliations now stop with a runtime mismatch on unsupported or malformed Node versions, including Node 24.
  • The runtime check happens before source checks, provider calls, external tools, runner imports, or lock acquisition.
  • Document the runtime requirement and refusal behavior; the application’s Node 22/24 CI matrix and package engine range remain unchanged.

Impact

✅ No provider calls or locks on unsupported Node runtimes
✅ Clear Node 22.19+ requirement for retention operators
✅ Node 24 remains supported for the application

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

Summary by CodeRabbit

  • Bug Fixes
    • The retention runner now stops with a clear runtime-mismatch reason on unsupported or malformed Node versions, before source checks, imports, external calls, or lock acquisition.
    • The runner requires Node 22.19.0 or later within the 22.x line; the application’s existing Node support remains unchanged.
  • Documentation
    • Updated runner guidance to explain the runtime requirement, affected entry points, and early-stop behavior.

@sourcery-ai

sourcery-ai Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

Reviewer's Guide

The PR enforces the retention operator’s independent Node 22.19.0+ runtime contract at the earliest CLI entry point, documents the behavior without changing the application Node engine or CI matrix, and adds cross-matrix in-process and subprocess regressions proving both rejection safety and unchanged supported-runtime behavior.

Sequence diagram for retention runtime enforcement

sequenceDiagram
    participant CLI
    participant main
    participant Runtime as assertRuntime
    participant Mode as parseMode
    participant Source as assertTrustedSource
    participant Runner as apply.mjs

    CLI->>main: main(argv, deps, io)
    main->>Runtime: assertRuntime()
    alt runtime is unsupported or malformed
        Runtime-->>main: stop(runtime-mismatch)
        main-->>CLI: runtime-mismatch
    else Node 22.19.0+ runtime
        Runtime-->>main: return
        main->>Mode: parseMode(argv)
        opt reconcile mode
            main->>Source: assertTrustedSource(deps)
        end
        main->>Runner: import('./apply.mjs')
    end
Loading

File-Level Changes

Change Details Files
Add an early runtime guard requiring the retention operator to run on Node 22.19.0 or newer within the 22.x line.
  • Validate the actual runtime version with strict handling of malformed, prerelease, unsupported-major, and unsafe numeric versions.
  • Run the guard before argument parsing, source validation, dynamic runner import, external commands, provider access, or lock operations.
  • Document the runtime contract and runtime-mismatch stop reason while leaving application engines and CI matrix unchanged.
scripts/vercel-retention/cli.mjs
docs/VERCEL-RETENTION-RUNNER.md
Adapt unit-test fixtures and coverage so application behavior remains testable across the Node 22/24 matrix while runtime enforcement is tested in real subprocesses.
  • Provide a test-only supported-runtime fixture for in-process behavior tests.
  • Add subprocess coverage proving unsupported and malformed runtimes produce no CLI, provider, runner-import, or lock side effects.
  • Verify supported runtimes preserve source checks, provider behavior, mutations, and lock semantics.
  • Include runtime regressions in the existing standalone Node test suite invoked through the Vitest wrapper.
tests/unit/tooling/support/vercelRetentionFixture.mjs
tests/unit/tooling/vercelRetention.test.mjs
tests/unit/tooling/vercelRetention.test.ts
tests/unit/tooling/vercelRetentionRuntime.test.mjs
tests/unit/tooling/vercelRetentionSafety.test.mjs

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@codeant-ai

codeant-ai Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

🤖 CodeAnt AI — Review Status

Status Commit Started (UTC) Finished (UTC)
✅ Reviewed your PR 6fbe175 Oct 17, 2026 · 18:52 18:56

@codeant-ai

codeant-ai Bot commented Oct 17, 2026

Copy link
Copy Markdown

Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@vercel

vercel Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
worldscript-studio Ready Ready Preview Oct 17, 2026 7:07pm UTC

@codeant-ai codeant-ai Bot added the size:L This PR changes 100-499 lines, ignoring generated files label Oct 17, 2026
@codeant-ai

codeant-ai Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

🏁 CodeAnt Quality Gate Results

Commit: 19dfd022
Scan Time: 2026-10-17 19:07:45 UTC

✅ Overall Status: PASSED

Quality Gate Details

Quality Gate Status Details
Secrets ✅ PASSED 0 secrets found
Duplicate Code ✅ PASSED 0.0% duplicated
SAST ✅ PASSED No security issues
Bugs ✅ PASSED Rating S: No bugs
IAC ✅ PASSED No IAC issues

View Full Results

codescene-access[bot]

This comment was marked as outdated.

@coderabbitai

coderabbitai Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Warning

Review limit reached

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

Next included review available in 45 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 2 included reviews currently available. Your 57 included PR review attempts over the past 7 days set your current allowance at 2 reviews per hour.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Repository: qnbs/WorldScript-Studio/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Essentials
  • Run ID: 345ae1dc-bff1-447a-95a8-ce1a11fe1ffe
📥 Commits

Reviewing files that changed from the base of the PR and between 3027553 and 19dfd02.

📒 Files selected for processing: 1
  • tests/unit/tooling/vercelRetentionRuntime.test.mjs
📝 Walkthrough

The Vercel retention CLI now checks for Node 22.19.0 or later in the 22.x line before parsing arguments or starting further work. Documentation, changelog text, and tests describe or verify the runtime restriction.

Changes: Runtime validation

Layer / File(s) Summary
Runtime policy and entry gate
scripts/vercel-retention/cli.mjs, docs/VERCEL-RETENTION-RUNNER.md, CHANGELOG.md, tests/unit/tooling/vercelRetentionRuntime.test.mjs
The CLI rejects malformed, unsafe, or unsupported Node versions with runtime-mismatch before parsing the mode. Documentation and the changelog describe the required runtime, check order, and stop reason. Tests cover accepted and rejected versions and verify runtime declarations.
Runtime boundary validation
tests/unit/tooling/support/vercelRetentionFixture.mjs, tests/unit/tooling/vercelRetentionRuntime.test.mjs, tests/unit/tooling/vercelRetention.test.mjs, tests/unit/tooling/vercelRetention.test.ts, tests/unit/tooling/vercelRetentionSafety.test.mjs
Runtime tests check that unsupported versions stop before guarded operations and that supported runtimes reach the expected paths. Existing CLI tests use a temporary supported-runtime fixture, and the test runner includes the runtime suite.

Priority: ⬇️ Low

Merge Risk: 🔵 Low · up to 30275

The CLI currently stops unsupported Node versions before parsing, but tests do not protect that ordering; a regression could show an argument error instead of the required runtime-mismatch. This is a bounded test follow-up, so the change is otherwise mergeable.

- [ ] Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 2 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="tests/unit/tooling/vercelRetentionRuntime.test.mjs" line_range="109-110" />
<code_context>
+}
+
+test('the actual matrix runtime refuses before tools, locks and runner imports', () => {
+  const supported = /^22\./.test(process.versions.node);
+  const argv = supported ? ['--execute'] : ['reconcile'];
+  const stderr = guardedChild(argv);
+  assert.ok(
</code_context>
<issue_to_address>
**Supported Node 22 checks fail**

When the test suite runs under Node 22.0.0 through 22.18.x, the test `the actual matrix runtime refuses before tools, locks and runner imports` treats every Node 22 version as supported and expects `invalid-arguments`; on Node 22.18 or earlier, the correct runtime guard returns `runtime-mismatch`, so this test fails.

Determine support using the full 22.19.0 minimum, or derive the expected reason by calling `assertRuntime` with the current version.
</issue_to_address>

### Comment 2
<location path="tests/unit/tooling/vercelRetentionRuntime.test.mjs" line_range="102-103" />
<code_context>
+      lines.find((line) => line.startsWith('RETENTION_SUMMARY ')).slice(18),
+    );
+    assert.equal(summary.stop, null);
+    assert.equal(summary.writeAttempts, mode === 'dry-run' ? 0 : world.writes.length);
+    assert.equal(world.released, mode === 'dry-run' ? 0 : 1);
+    assert.ok(world.calls.some((call) => call.startsWith('vercel api')));
+  });
</code_context>
<issue_to_address>
**No-op reconcile passes runtime test**

When the reconcile implementation skips all retirement writes but still completes its provider reads and post-proof without stopping, the test `reconcile accepts supported runtime with mocked provider and unchanged source checks` only compares `summary.writeAttempts` with `world.writes.length`; if reconcile stops performing writes, both are zero, and the remaining assertions still pass, so the test reports success for a no-op reconcile.

For reconcile mode, assert a positive expected write count and verify the expected deployment IDs were written.
</issue_to_address>

Sourcery assessment

Approval pending. 2 findings to address first.

Blocking findings: tests/unit/tooling/vercelRetentionRuntime.test.mjs:110, tests/unit/tooling/vercelRetentionRuntime.test.mjs:103


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread tests/unit/tooling/vercelRetentionRuntime.test.mjs Outdated
Comment thread tests/unit/tooling/vercelRetentionRuntime.test.mjs
@codeant-ai

codeant-ai Bot commented Oct 17, 2026

Copy link
Copy Markdown

CodeAnt PR Risk: Low Risk

  • The PR appears safe to merge; the runtime check runs before mode handling, runner imports, external tools, or lock acquisition.
  • Tests cover supported and malformed Node versions and verify unsupported runtimes trigger no tool, lock, or runner side effects.

Assessed commit: 6fbe175f7be4

codescene-access[bot]

This comment was marked as outdated.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments: 1
tests/unit/tooling/vercelRetentionRuntime.test.mjs: 1

76-90: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add an invalid-argument case for rejected runtimes.

The rejected-runtime cases pass valid modes. If argument parsing moves before the runtime check, these cases can still report runtime-mismatch and pass. The existing invalid-argument case runs only on a supported runtime, so it does not detect this ordering regression.

Suggested fix
+test('rejects unsupported runtimes before argument parsing', () => {
+  for (const version of [
+    '24.18.0',
+    '20.19.0',
+    '21.9.0',
+    '23.0.0',
+    '22.18.9',
+    'private-invalid-value',
+  ]) {
+    const stderr = guardedChild(['--execute'], version);
+    assert.match(stderr, /RETENTION_STOP: runtime-mismatch/);
+    assert.ok(!stderr.includes('invalid-arguments'));
+  }
+});

ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository: qnbs/WorldScript-Studio/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Essentials
  • Run ID: 048d883d-a23e-4215-b383-75ebb013460f
📥 Commits

Reviewing files that changed from the base of the PR and between 6fbe175 and 3027553.

📒 Files selected for processing: 1
  • CHANGELOG.md

Included review availability: This review used your included allowance. 0 included reviews remain after this review. Your included PR review attempts over the past 7 days set your current allowance at 2 reviews per hour.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Sourcery assessment

Approved.

@codescene-access codescene-access Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Gates Passed
3 Quality Gates Passed

See analysis details in CodeScene

Quality Gate Profile: The Bare Minimum
Install CodeScene MCP: safeguard and uplift AI-generated code. Catch issues early with our IDE extension and CLI tool.

@qnbs

qnbs commented Oct 17, 2026

Copy link
Copy Markdown
Owner Author

Final-head status — 2026-10-17

19dfd0223ecae4da8b35c8932e1ee92ad33bd614 remains the final head; no additional correction push is planned.

Reviewer evidence on this head: Sourcery and CodeScene approved; CodeAnt quality gates passed; reviewer-governance check passed; both inline Sourcery threads are resolved and the paginated review/comment channels were checked. CodeRabbit's latest exact-head attempt is rate-limited (NO_SIGNAL), not approval. Its earlier argument-order finding is covered by the current-runtime test, which derives expectedStop from assertRuntime() and then invokes the child with invalid arguments.

The required Security Audit is still red: exact-head run 39061065120, OSV exit 1, including ZIP advisories RUSTSEC-2026-0367/0368/0369 and informational unmaintained notices RUSTSEC-2026-0370/0371/0372. CI Success fails because the required security job failed. Status remains BLOCKED_BY_GLOBAL_OSV; no merge until the actual required audit passes. This PR does not resolve the flagged dependency graph.

This branch was successfully deployed

1 active deployment
Preview — 19dfd022 Deployed Oct 17, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant