Skip to content

chore: prepare v5.3.3 ledger closeout#21

Open
flyingrobots wants to merge 4 commits intomainfrom
feat/v5.3.3-ledger-closeout
Open

chore: prepare v5.3.3 ledger closeout#21
flyingrobots wants to merge 4 commits intomainfrom
feat/v5.3.3-ledger-closeout

Conversation

@flyingrobots
Copy link
Member

@flyingrobots flyingrobots commented Mar 16, 2026

Summary

  • retarget the repo to the in-flight v5.3.3 M17 Ledger closeout line
  • add the remaining M17 ops pieces: CODEOWNERS, release runbook, expanded test conventions, and pnpm release:verify
  • add deterministic property-based envelope coverage, harden Bun Git blob writes, and move the Docker test stages to Ubuntu-based images

Details

  • bump package.json and jsr.json to 5.3.3
  • update CHANGELOG.md, STATUS.md, and ROADMAP.md so v5.3.2 is the latest shipped release and v5.3.3 is the in-flight line
  • keep the README lead section on v5.3.2 until release finalization, per the release plan
  • add .github/CODEOWNERS with repo-wide ownership for @git-stunts
  • add docs/RELEASE.md and wire it from CONTRIBUTING.md
  • add scripts/release/verify.js plus unit coverage for summary generation and failure propagation
  • add seeded fast-check envelope property tests plus shared property helpers
  • add createGitPlumbing() so Bun uses the stable Node-backed plumbing runner path
  • update GitPersistenceAdapter.writeBlob() to hash temp files under Bun instead of streaming large buffers to git hash-object --stdin
  • switch the Node, Bun, and Deno Docker test stages to ubuntu:24.04, copying runtime binaries from the official upstream images rather than inheriting Debian-based runtime images directly

Validation

  • pnpm release:verify
  • docker compose run --build --rm test-node npx vitest run test/integration

pnpm release:verify passed end to end with:

  • Lint
  • Unit Tests (Node)
  • Unit Tests (Bun)
  • Unit Tests (Deno)
  • Integration Tests (Node)
  • Integration Tests (Bun)
  • Integration Tests (Deno)
  • npm pack --dry-run
  • npx jsr publish --dry-run --allow-dirty

Local Docker Cleanup

  • pruned unused Docker images, networks, volumes, and build cache
  • reduced local Docker footprint from roughly 45.76GB images + 36.46GB build cache to roughly 3.37GB images + 809MB build cache after rebuild

Summary by CodeRabbit

  • New Features

    • Release verification automation across Node/Bun/Deno with a release:verify helper; added CODEOWNERS.
  • Bug Fixes

    • Improved release verification and git persistence behavior to avoid cross-runtime test failures.
  • Documentation

    • Added canonical patch-release workflow; updated CHANGELOG, CONTRIBUTING, README, ROADMAP, and STATUS for v5.3.x.
  • Chores

    • Bumped package version to v5.3.3 and added fast-check dev dependency.
  • Tests

    • Added property-based tests for envelope encryption and expanded multi-runtime integration/unit test coverage.

@coderabbitai
Copy link

coderabbitai bot commented Mar 16, 2026

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 362f861b-dc1a-4414-8c2c-c4cb06342301

📥 Commits

Reviewing files that changed from the base of the PR and between 749a4b4 and 133f674.

📒 Files selected for processing (2)
  • scripts/release/verify.js
  • test/unit/scripts/release-verify.test.js
 _________________________________________________________
< Nuke the bugs from orbit. It's the only way to be sure. >
 ---------------------------------------------------------
  \
   \   (\__/)
       (•ㅅ•)
       /   づ
📝 Walkthrough

Walkthrough

Refactors Git plumbing instantiation to a factory, adds Bun-aware blob write path, introduces a multi-step release verification script and tests, adds property-based tests and helpers, bumps version to 5.3.3, and updates docs, CI/Docker, and contribution/release workflows.

Changes

Cohort / File(s) Summary
Release & Version Management
package.json, jsr.json, CHANGELOG.md, ROADMAP.md, STATUS.md, README.md, docs/RELEASE.md
Bump to 5.3.3; add release:verify script and fast-check devDep; add RELEASE.md with canonical patch-release flow and verification steps; update changelog, roadmap, status, and README for v5.3.2/v5.3.3 notes.
Repository Policy & Contribution
.github/CODEOWNERS, CONTRIBUTING.md, test/CONVENTIONS.md
Add CODEOWNERS entry; add per-runtime integration test commands and release:verify gate; document test/runtime and subprocess conventions.
Release Verification Automation
scripts/release/verify.js, test/unit/scripts/release-verify.test.js
New release verification orchestration (RELEASE_STEPS, runner abstraction, summary rendering, error class); comprehensive unit tests for parsing, summary, success/failure flows.
Git Plumbing Factory & CLI
src/infrastructure/createGitPlumbing.js, bin/git-cas.js, test/unit/infrastructure/createGitPlumbing.test.js
New createGitPlumbing factory (runtime detection, runner creation); CLI and tests switched to use factory instead of direct GitPlumbing instantiation.
Git Persistence Adapter (Bun handling)
src/infrastructure/adapters/GitPersistenceAdapter.js, test/unit/infrastructure/adapters/GitPersistenceAdapter.writeBlob.test.js
writeBlob adds Bun-specific temp-file hashing path (#writeBlobFromTempFile) to avoid EPIPE; FS temp helpers and cleanup added; unit tests cover both stdin and temp-file paths with mocks.
Property-Based Testing & Envelope Tests
test/helpers/property.js, test/unit/domain/services/CasService.envelope.property.test.js
Add fast-check-based arbitraries and config; new property tests validating envelope round-trip and tamper detection across generated payloads/recipients.
Integration & Unit Test Adjustments
test/integration/..., test/unit/...
Multiple tests updated to use createGitPlumbing({ cwd }) instead of GitPlumbing.createDefault(...); new unit tests for createGitPlumbing and GitPersistenceAdapter writeBlob behavior added.
Dockerfile Refactor
Dockerfile
Centralized ubuntu-base stage; Node/Bun/Deno stages inherit from it and copy runtime binaries; unified user/env setup and standardized test CMDs.
CLI Script Wiring
bin/git-cas.js
Switched internal plumbing construction to use createGitPlumbing factory import.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant CLI as release:verify<br/>(scripts/release/verify.js)
    participant Steps as RELEASE_STEPS<br/>(metadata & commands)
    participant Runner as Shell Runner
    participant Tools as Lint/Test/Pack
    participant Summary as Markdown Summary

    User->>CLI: pnpm release:verify
    CLI->>CLI: read version from package.json
    CLI->>Steps: iterate steps (lint, unit, docker, integration, pack, publish)

    loop for each step
        CLI->>Runner: execute step.command (via defaultRunner)
        Runner->>Tools: spawn/exec command
        Tools-->>Runner: stdout/stderr + exit code
        Runner->>CLI: buildStepResult (include test count if vitest)
        CLI->>Steps: collect step result
    end

    CLI->>Summary: renderMarkdownSummary(version, results, totalTests)
    Summary-->>User: print markdown summary

    alt all pass
        CLI->>User: exit 0
    else any fail
        CLI->>User: throw ReleaseVerifyError + exit 1
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐇 I nibble code with careful paws,

factories hum and temp-files pause,
Release steps hop, tests run through night,
Envelopes guard secrets tight,
Version 5.3.3 — a joyous little cause! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 53.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'chore: prepare v5.3.3 ledger closeout' directly and clearly describes the main objective of the PR: preparing the repository for version 5.3.3 as part of the M17 Ledger closeout milestone.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/v5.3.3-ledger-closeout
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@flyingrobots
Copy link
Member Author

@coderabbitai review please 🥕

@coderabbitai
Copy link

coderabbitai bot commented Mar 16, 2026

@flyingrobots: On it! Starting the review now 🐇🥕

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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: 1

Caution

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

⚠️ Outside diff range comments (1)
Dockerfile (1)

1-42: ⚠️ Potential issue | 🟠 Major

Run test images as a non-root user.

All stages currently execute as root. Add a non-root user and switch before CMD to reduce container breakout blast radius.

🔐 Suggested hardening patch
 FROM ubuntu:24.04 AS ubuntu-base
 ENV DEBIAN_FRONTEND=noninteractive
 RUN apt-get update \
  && apt-get install -y --no-install-recommends ca-certificates git \
  && rm -rf /var/lib/apt/lists/*
+RUN groupadd --system app && useradd --system --gid app --create-home --shell /usr/sbin/nologin app

 # --- Node ---
 FROM ubuntu-base AS node
 COPY --from=node-runtime /usr/local/ /usr/local/
 RUN npm install -g pnpm@10
 WORKDIR /app
 COPY package.json pnpm-lock.yaml ./
 RUN pnpm install --frozen-lockfile
 COPY . .
+RUN chown -R app:app /app
 ENV GIT_STUNTS_DOCKER=1
+USER app
 CMD ["pnpm", "vitest", "run", "test/unit"]

 # --- Bun ---
 FROM ubuntu-base AS bun
 COPY --from=bun-runtime /usr/local/bin/bun /usr/local/bin/bun
 COPY --from=bun-runtime /usr/local/bin/bunx /usr/local/bin/bunx
 WORKDIR /app
 COPY package.json ./
 RUN bun install
 COPY . .
+RUN chown -R app:app /app
 ENV GIT_STUNTS_DOCKER=1
+USER app
 CMD ["bunx", "vitest", "run", "test/unit"]

 # --- Deno ---
 FROM ubuntu-base AS deno
 COPY --from=deno-runtime /usr/bin/deno /usr/local/bin/deno
 WORKDIR /app
 COPY package.json ./
 RUN deno install --allow-scripts || true
 COPY . .
 RUN deno install --allow-scripts
+RUN chown -R app:app /app
 ENV GIT_STUNTS_DOCKER=1
+USER app
 CMD ["deno", "run", "-A", "npm:vitest", "run", "test/unit"]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Dockerfile` around lines 1 - 42, Add a non-root user and switch to it in each
stage (node, bun, deno) before CMD: create a user (e.g., gitstunts) and group,
set HOME, chown /app and any copied runtime binaries (e.g., /usr/local,
/usr/local/bin/bun, /usr/local/bin/bunx, /usr/local/bin/deno) as needed, then
add a USER gitstunts line in the node, bun and deno stages so the final CMD
(["pnpm"..."vitest"], ["bunx"..."vitest"], ["deno"..."vitest"]) runs
unprivileged; ensure any install steps that require root stay before the USER
switch and preserve file permissions for that non-root user.
🧹 Nitpick comments (1)
test/unit/scripts/release-verify.test.js (1)

73-85: Consider simplifying the redundant error capture.

The test calls runReleaseVerify twice with the same failing runner: once at line 76 (assertion only) and again at line 81 (to capture the error). Since vi.fn() accumulates call counts, the assertion at line 84 (toHaveBeenCalledTimes(6)) reflects both invocations (3 steps each).

This works but is subtle. Consider refactoring to a single call:

♻️ Suggested simplification
   it('stops on the first failure and exposes a partial summary', async () => {
     const runner = makeFailingRunner('unit-bun');
 
-    await expect(runReleaseVerify({ runner, logger: QUIET_LOGGER })).rejects.toMatchObject({
-      name: 'ReleaseVerifyError',
-      step: expect.objectContaining({ id: 'unit-bun', passed: false }),
-    });
-
-    const failure = await runReleaseVerify({ runner, logger: QUIET_LOGGER }).catch((error) => error);
+    const failure = await runReleaseVerify({ runner, logger: QUIET_LOGGER }).catch((error) => error);
     expect(failure).toBeInstanceOf(ReleaseVerifyError);
+    expect(failure.name).toBe('ReleaseVerifyError');
+    expect(failure.step).toMatchObject({ id: 'unit-bun', passed: false });
     expect(failure.summary).toContain('| Unit Tests (Bun) | FAIL | 5 |');
-    expect(runner).toHaveBeenCalledTimes(6);
+    expect(runner).toHaveBeenCalledTimes(3);
   });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/unit/scripts/release-verify.test.js` around lines 73 - 85, The test
currently calls runReleaseVerify twice which doubles runner invocations; change
it to a single invocation that both asserts the rejection and captures the error
for further assertions: call runReleaseVerify({ runner, logger: QUIET_LOGGER })
once (using either await expect(...).rejects.toMatchObject(...) and then await
the same call inside a try/catch to capture the error, or perform a single
try/catch to capture the thrown ReleaseVerifyError), then assert on
failure.summary and runner call count; update references to runReleaseVerify,
makeFailingRunner, ReleaseVerifyError, QUIET_LOGGER and runner accordingly so
the runner invocation count reflects only the single test run.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/release/verify.js`:
- Around line 188-200: Wrap the call to runner(step, { cwd }) in a try/catch so
any thrown exception becomes a step failure instead of escaping: catch the
error, set an outcome object describing the failure (include the thrown
error/message), then call buildStepResult(step, outcome) and push that result
onto results; after that reuse the existing logic (totalObservedTests,
renderMarkdownSummary) and throw a ReleaseVerifyError with the structured
summary if result.passed is false. Reference runner, buildStepResult,
totalObservedTests, renderMarkdownSummary, ReleaseVerifyError, step, results,
and outcome when making the change.

---

Outside diff comments:
In `@Dockerfile`:
- Around line 1-42: Add a non-root user and switch to it in each stage (node,
bun, deno) before CMD: create a user (e.g., gitstunts) and group, set HOME,
chown /app and any copied runtime binaries (e.g., /usr/local,
/usr/local/bin/bun, /usr/local/bin/bunx, /usr/local/bin/deno) as needed, then
add a USER gitstunts line in the node, bun and deno stages so the final CMD
(["pnpm"..."vitest"], ["bunx"..."vitest"], ["deno"..."vitest"]) runs
unprivileged; ensure any install steps that require root stay before the USER
switch and preserve file permissions for that non-root user.

---

Nitpick comments:
In `@test/unit/scripts/release-verify.test.js`:
- Around line 73-85: The test currently calls runReleaseVerify twice which
doubles runner invocations; change it to a single invocation that both asserts
the rejection and captures the error for further assertions: call
runReleaseVerify({ runner, logger: QUIET_LOGGER }) once (using either await
expect(...).rejects.toMatchObject(...) and then await the same call inside a
try/catch to capture the error, or perform a single try/catch to capture the
thrown ReleaseVerifyError), then assert on failure.summary and runner call
count; update references to runReleaseVerify, makeFailingRunner,
ReleaseVerifyError, QUIET_LOGGER and runner accordingly so the runner invocation
count reflects only the single test run.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1b78e1fa-b201-46de-a152-c8006ff10cdf

📥 Commits

Reviewing files that changed from the base of the PR and between a5de919 and 6f85527.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (25)
  • .github/CODEOWNERS
  • CHANGELOG.md
  • CONTRIBUTING.md
  • Dockerfile
  • README.md
  • ROADMAP.md
  • STATUS.md
  • bin/git-cas.js
  • docs/RELEASE.md
  • jsr.json
  • package.json
  • scripts/release/verify.js
  • src/infrastructure/adapters/GitPersistenceAdapter.js
  • src/infrastructure/createGitPlumbing.js
  • test/CONVENTIONS.md
  • test/helpers/property.js
  • test/integration/round-trip.test.js
  • test/integration/vault-cli.test.js
  • test/integration/vault.test.js
  • test/unit/domain/services/CasService.envelope.property.test.js
  • test/unit/domain/services/rotateVaultPassphrase.test.js
  • test/unit/facade/ContentAddressableStore.rotation.test.js
  • test/unit/infrastructure/adapters/GitPersistenceAdapter.writeBlob.test.js
  • test/unit/infrastructure/createGitPlumbing.test.js
  • test/unit/scripts/release-verify.test.js

@flyingrobots
Copy link
Member Author

@coderabbitai review please 🥕

@coderabbitai
Copy link

coderabbitai bot commented Mar 17, 2026

@flyingrobots: Kicking off the review now — double-carrot dedication noted 🥕🥕🐇

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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: 1

🧹 Nitpick comments (1)
test/unit/scripts/release-verify.test.js (1)

99-106: Avoid hardcoded total test count to reduce maintenance churn.

expect(report.totalTests).toBe(30) couples this test to the current number of testCount steps. Derive expected total from RELEASE_STEPS to keep intent stable as steps evolve.

♻️ Suggested refactor
   it('runs the release steps in order and aggregates test counts', async () => {
     const runner = makeSuccessRunner();

     const report = await runReleaseVerify({ runner, logger: QUIET_LOGGER });
+    const expectedTotal = RELEASE_STEPS.filter((step) => step.testCount).length * 5;

     expect(runner).toHaveBeenCalledTimes(RELEASE_STEPS.length);
-    expect(report.totalTests).toBe(30);
+    expect(report.totalTests).toBe(expectedTotal);
     expect(report.results.every((result) => result.passed)).toBe(true);
   });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/unit/scripts/release-verify.test.js` around lines 99 - 106, Replace the
hardcoded expected totalTests value with a computed sum derived from the
RELEASE_STEPS constant: compute the expected total by reducing RELEASE_STEPS
over each step's testCount and assert report.totalTests equals that computed
value so the test adapts when RELEASE_STEPS changes; update the assertion in the
test that calls runReleaseVerify (in release-verify.test.js) that currently
checks expect(report.totalTests).toBe(30) to use the computed sum via
RELEASE_STEPS.reduce((acc, step) => acc + step.testCount, 0).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/release/verify.js`:
- Around line 149-156: In buildStepResult normalize outcome.signal to a single
value (e.g., const signal = outcome.signal ?? null) and use that normalized
signal when computing passed and when setting the signal property, so that
undefined signals are treated as null and a step with code === 0 and no signal
is correctly marked passed; update references in the returned object to use this
normalized signal instead of outcome.signal directly.

---

Nitpick comments:
In `@test/unit/scripts/release-verify.test.js`:
- Around line 99-106: Replace the hardcoded expected totalTests value with a
computed sum derived from the RELEASE_STEPS constant: compute the expected total
by reducing RELEASE_STEPS over each step's testCount and assert
report.totalTests equals that computed value so the test adapts when
RELEASE_STEPS changes; update the assertion in the test that calls
runReleaseVerify (in release-verify.test.js) that currently checks
expect(report.totalTests).toBe(30) to use the computed sum via
RELEASE_STEPS.reduce((acc, step) => acc + step.testCount, 0).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 561a4cc1-79dc-43d3-8769-1dcfaec029aa

📥 Commits

Reviewing files that changed from the base of the PR and between 6f85527 and 749a4b4.

📒 Files selected for processing (5)
  • CHANGELOG.md
  • Dockerfile
  • scripts/release/verify.js
  • src/infrastructure/createGitPlumbing.js
  • test/unit/scripts/release-verify.test.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/infrastructure/createGitPlumbing.js

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