Skip to content

feat: add scoped staging workspaces - #76

Merged
flyingrobots merged 5 commits into
mainfrom
feat/scoped-staging-workspaces
Jul 18, 2026
Merged

feat: add scoped staging workspaces#76
flyingrobots merged 5 commits into
mainfrom
feat/scoped-staging-workspaces

Conversation

@flyingrobots

@flyingrobots flyingrobots commented Jul 18, 2026

Copy link
Copy Markdown
Member

Summary

  • add cas.workspaces.open(), inspect(), and sweep() as the public API for scoped multi-object staging
  • retain workspace descriptors and target roots through exact-generation RootSet anchors
  • support staged asset, page, bundle, cache, and publication workflows without application-managed Git refs
  • expose cursor-based bounded cleanup and workspace retention, age, expiry posture, logical bytes, and direct-root object bytes through repository doctor diagnostics
  • preserve staged evidence when a low-level write succeeds but workspace retention fails

Why

Multi-object operations currently need either application-managed refs or temporary cache publications. That leaks storage ownership and imposes a major performance cost on downstream consumers such as git-warp. A staging workspace gives callers an explicit lease whose immutable targets survive aggressive pruning until exact-generation release.

Safety Invariants

  • workspace targets are immutable application handles retained by a RootSet generation
  • expiry is inspection and sweep posture; it does not silently delete data
  • inventory and cleanup remain bounded while opaque cursors traverse every page
  • inspection-time generation races remain visible as conflicts
  • promotion releases the source only after an exact anchored destination retention witness is returned
  • rejected, malformed, or failed promotion leaves the source workspace active
  • release is idempotent, direct-ref-only, and generation-checked
  • diagnostics report logical and direct-root Git object bytes without claiming physical residency

Linked Issue

Design / Proof

Validation

npm run release:verify -- --skip-jsr
pnpm exec vitest run test/unit/docs --reporter=dot
git diff --check
  • release verifier: 13/13 steps passed; 6,511 Vitest tests observed
  • unit tests: Node 1,984; Bun 1,983; Deno 1,974
  • integration tests: 190 each on Node, Bun, and Deno
  • three example processes, public type compatibility, build stamp, lint, and npm pack dry-run passed
  • focused documentation suite: 60/60 passed
  • Graft found additive structural changes and no removed exports
  • first CodeRabbit and independent Code Lawyer findings are addressed in the hardening commits

JSR publication verification was intentionally skipped because this is the feature PR, not the release-preparation PR.

Release Impact

  • Changelog updated, if release behavior changed.
  • Public docs updated, if user-facing behavior changed.
  • Issue labels/milestone reflect final disposition. The release milestone/version remains an explicit post-merge decision because existing v6.4.0 milestone scope conflicts with this feature.

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds scoped staging workspaces with RootSet-backed retention, checkpointing, renewal, promotion, release, namespace-bounded inspection, expiry sweeping, repository diagnostics, public types, documentation, and unit/integration coverage.

Changes

Scoped staging workspaces

Layer / File(s) Summary
Workspace contracts and data model
src/domain/value-objects/WorkspaceRef.js, src/domain/services/WorkspaceDescriptorCodec.js, src/domain/errors/Codes.js, index.d.ts
Defines canonical workspace references and descriptors, workspace error codes, diagnostics shapes, staging result types, and the public workspace capability.
Workspace staging and promotion runtime
src/domain/services/StagingWorkspace.js
Implements serialized staging, checkpointing, renewal, retention witnesses, cache/publication promotion, generation-safe cleanup, and release semantics.
Workspace registry, inspection, and sweep
src/domain/services/StagingWorkspaceRegistry.js
Creates namespaced workspaces and provides bounded inspection, logical/root-object byte accounting, expiry classification, and generation-checked sweeping.
Facade and repository diagnostics
index.js, src/domain/services/RepositoryDoctor.js
Exposes cas.workspaces, lazily wires the registry, and adds workspace inventory, health, totals, failure evidence, and truncation reporting to repository diagnostics.
Workspace behavior validation
test/unit/domain/services/*Workspace*.test.js, test/unit/domain/value-objects/WorkspaceRef.test.js, test/unit/facade/ContentAddressableStore.application-storage.test.js, test/integration/staging-workspace.test.js
Covers lifecycle, promotion, descriptor/ref validation, diagnostics, facade exposure, Git reachability, pruning, expiry, and sweep behavior.
Workspace documentation and release records
docs/API.md, docs/design/0049-scoped-staging-workspaces/*, docs/releases/v6.4.0.md, README.md, CHANGELOG.md
Documents the API, design contract, verification evidence, release behavior, capability listing, and unreleased changes.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

  • git-stunts/git-cas#66: Extends the same RepositoryDoctor diagnostics implementation used for workspace inventory and health reporting.

Suggested reviewers: git-stunts

Poem

I hop through roots where staging blooms,
With leases bright in Git’s dark rooms.
I checkpoint, renew, then proudly release,
While witnesses guard each byte’s peace.
Sweep the expired trails away—
A bunny ships a safer day!

🚥 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
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.
Title check ✅ Passed The title is concise and accurately summarizes the main change: adding scoped staging workspaces.
Description check ✅ Passed The description matches the template with linked issue, design/proof, validation, and release impact sections filled in.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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

@flyingrobots

flyingrobots commented Jul 18, 2026

Copy link
Copy Markdown
Member Author

Self-Code Review — Final Posture

Severity Unresolved
Blocker 0
Critical 0
High 0
Medium 0
Low 0

Reviewed the complete origin/main..d0132b27 diff after fetching. The worktree is clean and synchronized. GitHub reports CLEAN, MERGEABLE, and APPROVED; every review thread is resolved.

Review corrections now include:

  1. exact anchored destination evidence before source release;
  2. complete doctor fallback evidence and unhealthy issue-bearing records;
  3. descriptor error normalization;
  4. bounded cursor continuation beyond the first 1,000 workspace refs;
  5. explicit sweep accounting for inspection-time generation races;
  6. hard bounds for hostile duplicate/infinite checkpoint iterables;
  7. recoverable staged evidence when a post-write retention update fails;
  8. direct publication-order, outside-handle, failure, malformed-witness, and symbolic-ref containment tests.

Verification evidence:

  • npm run release:verify -- --skip-jsr: 13/13 gates passed
  • 6,511 Vitest tests observed: Node unit 1,984; Bun unit 1,983; Deno unit 1,974; 190 integrations on each runtime
  • focused documentation suite: 60/60 passed
  • GitHub lint, unit, Node Docker, Bun Docker, Deno Docker, and CodeRabbit checks passed
  • Graft: 25 additive exports, zero removed or changed exports, no breaking changes, minor semver impact
  • independent Code Lawyer findings were addressed by 29bcf03e; CodeRabbit approved the final head

Residual risks are documented rather than hidden: the unavoidable object-write-to-ref-update grace window, cumulative RootSet construction cost at very large target counts, and the still-pending downstream git-warp ten-second performance gate.

@codex please confirm this final evidence posture and flag any remaining retention-safety concern.

@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.

Actionable comments posted: 6

🤖 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 `@docs/design/0049-scoped-staging-workspaces/witness/verification.md`:
- Around line 59-76: Reconcile the verification summary around the release
verifier results: make the “13 executed steps” count match the table by adding
the two missing result rows or correcting the count, and clarify whether the
reported 6,454 observed tests excludes the three example checks. Update the
surrounding verification wording consistently without changing unrelated
results.

In `@index.d.ts`:
- Around line 1376-1382: Make the workspaces property required in the
RepositoryDoctor dependency type, while keeping its inspectRecord method and
return type unchanged. Align the declaration with RepositoryDoctor’s
unconditional use of this.workspaces.inspectRecord and ensure callers must
provide the adapter.

In `@src/domain/services/RepositoryDoctor.js`:
- Around line 409-433: Update workspaceUsage so healthy is false whenever
report.issue is non-null, while preserving the existing invalid-posture check.
Ensure records are healthy only when posture is not invalid and no inspection
issue is present.

In `@src/domain/services/StagingWorkspace.js`:
- Around line 264-277: Bound iteration in `#resolveTargets` by enforcing the
configured maximum checkpoint count while consuming handles, before calling
`#resolveTarget` for each entry. Stop or throw once the limit is reached,
including for repeated duplicates and non-terminating iterables, while
preserving deduplication, sorting, and existing invalid-iterable handling.

In `@src/domain/services/StagingWorkspaceRegistry.js`:
- Around line 88-110: The inspect/sweep flow only scans the first limit refs,
preventing later expired workspaces from being reached. Update
StagingWorkspaceRegistry.inspect and its sweep caller to support continuation
pagination (return and accept a cursor) or an explicit bounded internal scan,
while preserving the limit-sized result; add coverage where an active first page
is followed by an expired workspace.

In `@test/unit/domain/services/StagingWorkspacePromotion.test.js`:
- Around line 192-217: Update the test “publishes before releasing and rejects
handles outside the workspace” to cover both stated behaviors: make the mocked
publications.commit callback assert the workspace generation still exists when
invoked, and add a separate promoteToPublication attempt using an unstaged
handle that expects the appropriate rejection. Preserve the existing successful
publication assertions and released-workspace check.
🪄 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: ASSERTIVE

Plan: Pro

Run ID: 8ceeb9e2-3b8f-48dd-bd23-2c92f8a37b4e

📥 Commits

Reviewing files that changed from the base of the PR and between beb8a29 and 8153614.

📒 Files selected for processing (22)
  • CHANGELOG.md
  • README.md
  • docs/API.md
  • docs/design/0049-scoped-staging-workspaces/scoped-staging-workspaces.md
  • docs/design/0049-scoped-staging-workspaces/witness/verification.md
  • docs/design/README.md
  • docs/releases/v6.4.0.md
  • index.d.ts
  • index.js
  • src/domain/errors/Codes.js
  • src/domain/services/RepositoryDoctor.js
  • src/domain/services/StagingWorkspace.js
  • src/domain/services/StagingWorkspaceRegistry.js
  • src/domain/services/WorkspaceDescriptorCodec.js
  • src/domain/value-objects/WorkspaceRef.js
  • test/integration/staging-workspace.test.js
  • test/unit/domain/services/RepositoryDoctor.test.js
  • test/unit/domain/services/StagingWorkspace.test.js
  • test/unit/domain/services/StagingWorkspacePromotion.test.js
  • test/unit/domain/services/WorkspaceDescriptorCodec.test.js
  • test/unit/domain/value-objects/WorkspaceRef.test.js
  • test/unit/facade/ContentAddressableStore.application-storage.test.js
📜 Review details
🧰 Additional context used
📓 Path-based instructions (3)
CHANGELOG.md

📄 CodeRabbit inference engine (AGENTS.md)

Use CHANGELOG.md to record the historical truth of merged behavior

Files:

  • CHANGELOG.md
docs/design/**

📄 CodeRabbit inference engine (AGENTS.md)

Use docs/design/ directory for durable design contracts and proof plans

Files:

  • docs/design/README.md
  • docs/design/0049-scoped-staging-workspaces/witness/verification.md
  • docs/design/0049-scoped-staging-workspaces/scoped-staging-workspaces.md
README.md

📄 CodeRabbit inference engine (AGENTS.md)

Use README.md as the public front door, core value prop, and quick start documentation

Files:

  • README.md
🧠 Learnings (3)
📚 Learning: 2026-02-28T19:21:13.982Z
Learnt from: flyingrobots
Repo: git-stunts/git-cas PR: 15
File: test/unit/domain/services/CasService.envelope.test.js:326-330
Timestamp: 2026-02-28T19:21:13.982Z
Learning: In fuzz tests for cryptographic operations, use a seeded PRNG (e.g., xorshift32) for control-flow variables such as plaintext size selection and recipient index selection to ensure reproducibility. Continue to use randomBytes() for cryptographic keys and nonces to preserve realistic randomness and avoid security anti-patterns. This guideline applies to test files that perform fuzz testing of crypto logic; implement a consistent seed setup (e.g., fixed seed in test initialization) and document the rationale to enable deterministic replays across runs.

Applied to files:

  • test/unit/domain/services/WorkspaceDescriptorCodec.test.js
  • test/unit/domain/value-objects/WorkspaceRef.test.js
  • test/integration/staging-workspace.test.js
  • test/unit/facade/ContentAddressableStore.application-storage.test.js
  • test/unit/domain/services/StagingWorkspacePromotion.test.js
  • test/unit/domain/services/StagingWorkspace.test.js
  • test/unit/domain/services/RepositoryDoctor.test.js
📚 Learning: 2026-03-30T19:53:48.000Z
Learnt from: flyingrobots
Repo: git-stunts/git-cas PR: 29
File: docs/design/TR-010-planning-index-consistency-review.md:121-131
Timestamp: 2026-03-30T19:53:48.000Z
Learning: In this repo, CHANGELOG.md entries should be user-facing and descriptive (bullet points) rather than cycle-ID headings (e.g., use a phrase like “Planning-index consistency review” instead of a “TR-010 — …” heading). When searching the changelog, don’t rely on grepping for cycle IDs (e.g., “TR-010”) because it may cause false negatives; search for the descriptive keywords/phrases from the bullet entries instead.

Applied to files:

  • CHANGELOG.md
📚 Learning: 2026-07-13T17:00:46.222Z
Learnt from: flyingrobots
Repo: git-stunts/git-cas PR: 65
File: src/domain/helpers/isCanonicalCollectionKey.js:21-35
Timestamp: 2026-07-13T17:00:46.222Z
Learning: In git-stunts/git-cas, the codebase is intended to run on multiple JavaScript runtimes (Node, Bun, and Deno), not just Node. During code review, don’t suggest replacing existing portable helper logic with newer Node-only built-ins (e.g., Node 20+ APIs like `String.prototype.isWellFormed()`) unless the change preserves Bun/Deno compatibility (via portable/polyfill implementations or safe runtime gating). If a file includes manual implementations (such as `src/domain/helpers/isCanonicalCollectionKey.js`), treat them as intentional to avoid requiring a higher host built-in baseline.

Applied to files:

  • src/domain/value-objects/WorkspaceRef.js
  • src/domain/errors/Codes.js
  • src/domain/services/WorkspaceDescriptorCodec.js
  • src/domain/services/StagingWorkspaceRegistry.js
  • src/domain/services/RepositoryDoctor.js
  • src/domain/services/StagingWorkspace.js
🪛 ast-grep (0.44.1)
test/integration/staging-workspace.test.js

[warning] 8-8: Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { spawnSync } from 'node:child_process';
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process)

🪛 LanguageTool
docs/design/0049-scoped-staging-workspaces/scoped-staging-workspaces.md

[style] ~487-~487: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...registry and scoped staging wrappers. - Add cache/publication promotion and failure...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~488-~488: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...on promotion and failure-order tests. - Add bounded inspection, expiry sweep, and r...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)

🪛 OpenGrep (1.25.0)
src/domain/value-objects/WorkspaceRef.js

[ERROR] 112-112: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)

🔇 Additional comments (23)
CHANGELOG.md (1)

10-26: LGTM!

README.md (1)

105-105: LGTM!

Also applies to: 143-153

docs/API.md (1)

19-30: LGTM!

Also applies to: 1243-1366, 1914-1922, 1960-1965, 3327-3335

docs/design/0049-scoped-staging-workspaces/scoped-staging-workspaces.md (1)

1-614: LGTM!

docs/design/0049-scoped-staging-workspaces/witness/verification.md (1)

1-58: LGTM!

Also applies to: 78-94

docs/design/README.md (1)

14-15: LGTM!

docs/releases/v6.4.0.md (1)

1-114: LGTM!

src/domain/errors/Codes.js (1)

103-111: LGTM!

src/domain/value-objects/WorkspaceRef.js (2)

1-42: LGTM!

Also applies to: 50-89, 108-151


43-49: 🔒 Security & Privacy

Literal + cannot appear in collection namespaces

Collection namespaces are limited to lowercase ASCII components ([a-z0-9.-]), so the /+ mapping here stays injective for valid inputs.

			> Likely an incorrect or invalid review comment.
src/domain/services/WorkspaceDescriptorCodec.js (1)

1-144: LGTM!

index.d.ts (1)

1244-1269: LGTM!

Also applies to: 1350-1361, 1500-1627, 1681-1681

src/domain/services/StagingWorkspace.js (1)

1-263: LGTM!

Also applies to: 278-499

src/domain/services/RepositoryDoctor.js (1)

7-7: LGTM!

Also applies to: 21-27, 135-149, 184-190, 479-488, 528-537, 580-586, 623-634, 698-709

test/unit/domain/services/RepositoryDoctor.test.js (1)

71-90: LGTM!

Also applies to: 91-128, 291-319, 525-563, 565-595

test/unit/domain/services/WorkspaceDescriptorCodec.test.js (1)

1-65: LGTM!

src/domain/services/StagingWorkspaceRegistry.js (1)

1-87: LGTM!

Also applies to: 160-489

index.js (1)

19-20: LGTM!

Also applies to: 74-74, 197-201, 250-251, 326-326, 350-360, 426-430

test/integration/staging-workspace.test.js (1)

1-228: LGTM!

test/unit/domain/services/StagingWorkspace.test.js (1)

1-334: LGTM!

test/unit/domain/services/StagingWorkspacePromotion.test.js (1)

1-191: LGTM!

test/unit/domain/value-objects/WorkspaceRef.test.js (1)

1-56: LGTM!

test/unit/facade/ContentAddressableStore.application-storage.test.js (1)

15-15: LGTM!

Also applies to: 43-51, 69-69, 92-106

Comment thread docs/design/0049-scoped-staging-workspaces/witness/verification.md Outdated
Comment thread index.d.ts
Comment thread src/domain/services/RepositoryDoctor.js
Comment thread src/domain/services/StagingWorkspace.js
Comment thread src/domain/services/StagingWorkspaceRegistry.js Outdated
Comment thread test/unit/domain/services/StagingWorkspacePromotion.test.js
@flyingrobots
flyingrobots merged commit 1ac2fc8 into main Jul 18, 2026
6 checks passed
@flyingrobots
flyingrobots deleted the feat/scoped-staging-workspaces branch July 18, 2026 01:26
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