Skip to content

refactor(providers): readonly-domain-types (stack 4/12, re-split #307) - #377

Closed
YosefHayim wants to merge 1 commit into
refactor/types/readonly-stack-03-googlefrom
refactor/types/readonly-stack-04-providers
Closed

refactor(providers): readonly-domain-types (stack 4/12, re-split #307)#377
YosefHayim wants to merge 1 commit into
refactor/types/readonly-stack-03-googlefrom
refactor/types/readonly-stack-04-providers

Conversation

@YosefHayim

@YosefHayim YosefHayim commented Aug 7, 2026

Copy link
Copy Markdown
Owner

Stack 4/12 of re-split HOLD #307

Domain: providers
Base: refactor/types/readonly-stack-03-google
Full green tip: refactor/foundation/readonly-types-full

Land stack in order. Intermediate PRs may not typecheck alone.


Summary by cubic

Make providers compatible with readonly types by removing mutation in local storage and using a mutable test context when needed. Improves type safety and immutability without changing behavior.

  • Refactors
    • src/providers/storage/local.ts: writeIndex now accepts readonly BuildArtifact[]; replaced in-place unshift with [newItem, ...existing].
    • src/providers/credentials/local.test.ts: use MutableDeep<ResolvedBuildContext> from @core/types/mutable.js for test-only mutations.

Written for commit 5c6fdbc. Summary will update on new commits.

Review in cubic

@codeant-ai

codeant-ai Bot commented Aug 7, 2026

Copy link
Copy Markdown

🤖 CodeAnt AI — Review Status

Status Commit Started (UTC) Finished (UTC)
✅ Reviewed your PR 5c6fdbc Aug 07, 2026 · 11:06 11:09

@codeant-ai

codeant-ai Bot commented Aug 7, 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

@changeset-bot

changeset-bot Bot commented Aug 7, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 5c6fdbc

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@codeant-ai codeant-ai Bot added the size:XS This PR changes 0-9 lines, ignoring generated files label Aug 7, 2026
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Refactor providers for readonly domain types (credentials tests + local storage)

✨ Enhancement 🧪 Tests 🕐 10-20 Minutes

Grey Divider

AI Description

• Adjust provider consumers to compile against newly readonly domain type definitions.
• Update local credentials tests to build a mutable context for optional field injection.
• Make local storage index writes immutable and accept readonly artifact index inputs.
Diagram

graph TD
  A["Credentials test"] --> B["MutableDeep"] --> C["ResolvedBuildContext"]
  D["Local storage"] --> E["ArtifactRetention"] --> F["Artifact index"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Cast locally instead of MutableDeep
  • ➕ Avoids importing an extra utility type into the test file
  • ➕ Keeps the test context typed as ResolvedBuildContext
  • ➖ Relies on unsafe assertions and can hide genuine readonly violations
  • ➖ Harder to grep/audit for readonly workarounds later
2. Clone then mutate (e.g., [...index].unshift())
  • ➕ Minimal change to existing control flow
  • ➖ Still uses mutation patterns that the readonly refactor is trying to eliminate
  • ➖ Easy to accidentally reintroduce in-place mutation elsewhere
3. Move readonly compatibility into ArtifactRetention service types
  • ➕ Centralizes the readonly-to-mutable boundary in one place
  • ➕ Reduces churn across multiple storage provider implementations
  • ➖ Likely part of another stack PR; doing it here may conflict with the planned landing order
  • ➖ Still requires some consumer updates where arrays were mutated in-place

Recommendation: Current approach is the best fit for a readonly-types migration stack: keep production code immutable (replace unshift with a new array) and confine deep-mutable workarounds to test setup via MutableDeep. Prefer avoiding type assertions so readonly violations remain visible during the rollout.

Files changed (2) +4 / -4

Refactor (1) +2 / -3
local.tsMake artifact index writes immutable and accept readonly index input +2/-3

Make artifact index writes immutable and accept readonly index input

• Updates the local writeIndex helper to take a readonly BuildArtifact array. Replaces in-place unshift() mutation with an immutable array prepend when adding a newly stored artifact before persisting the index.

src/providers/storage/local.ts

Tests (1) +2 / -1
local.test.tsUse MutableDeep build context for readonly ResolvedBuildContext +2/-1

Use MutableDeep build context for readonly ResolvedBuildContext

• Imports MutableDeep and uses it for the locally-constructed ResolvedBuildContext test fixture. This allows conditionally setting optional fields (account) during test setup while the domain type becomes readonly.

src/providers/credentials/local.test.ts

const artifactIndex = yield* readIndex();
artifactIndex.unshift({ ...artifact, path: destination });
yield* writeIndex(artifactIndex);
yield* writeIndex([{ ...artifact, path: destination }, ...artifactIndex]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: If writeIndex fails after the copy succeeds, put returns a failure while leaving the copied binary at destination without an index entry. Retries or repeated failures can accumulate unreachable artifact files; remove the copied file when index persistence fails or make the operation recoverable. [resource leak]

Severity Level: Major ⚠️
- ❌ Failed stores leave unindexed artifact binaries.
- ⚠️ Repeated failures consume artifact-directory disk space.
- ⚠️ `list()` cannot discover orphaned files.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/providers/storage/local.ts
**Line:** 40:40
**Comment:**
	*Resource Leak: If `writeIndex` fails after the copy succeeds, `put` returns a failure while leaving the copied binary at `destination` without an index entry. Retries or repeated failures can accumulate unreachable artifact files; remove the copied file when index persistence fails or make the operation recoverable.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 96969233-20dc-475a-86a6-5c8bbcbdb82a

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Readonly index type mismatch 🐞 Bug ≡ Correctness
Description
makeLocalStorageProvider now defines writeIndex to accept readonly BuildArtifact[] but passes
that value to ArtifactRetention.writeIndex, whose service contract currently requires
BuildArtifact[] (mutable). This causes a TypeScript incompatibility (readonly array not assignable
to mutable array) and can break typechecking/builds for the local storage provider.
Code

src/providers/storage/local.ts[R26-27]

+    const writeIndex = (artifactIndex: readonly BuildArtifact[]) =>
      artifactRetention.writeIndex(artifactIndex, artifactIndexPath);
Evidence
The provider helper was changed to readonly BuildArtifact[], but the downstream service contract
and implementation still require BuildArtifact[], making the call type-incompatible.

src/providers/storage/local.ts[25-41]
src/core/services/artifactRetention.ts[11-21]
src/core/build/artifactRetention.ts[45-56]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`src/providers/storage/local.ts` changed `writeIndex` to accept `readonly BuildArtifact[]`, but it forwards that parameter to `artifactRetention.writeIndex`, which is typed to require a mutable `BuildArtifact[]`. TypeScript will reject passing a readonly array to a function that may mutate it.

## Issue Context
The underlying implementation (`writeArtifactIndex`) simply serializes the array and does not mutate it, so the easiest fix is to make the `ArtifactRetention` write API accept `readonly BuildArtifact[]` (or alternatively, keep `writeIndex` mutable in the provider).

## Fix Focus Areas
- src/providers/storage/local.ts[25-41]
- src/core/services/artifactRetention.ts[11-21]
- src/core/build/artifactRetention.ts[45-56]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context used
✅ Compliance rules (platform): 48 rules

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

Comment on lines +26 to 27
const writeIndex = (artifactIndex: readonly BuildArtifact[]) =>
artifactRetention.writeIndex(artifactIndex, artifactIndexPath);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. Readonly index type mismatch 🐞 Bug ≡ Correctness

makeLocalStorageProvider now defines writeIndex to accept readonly BuildArtifact[] but passes
that value to ArtifactRetention.writeIndex, whose service contract currently requires
BuildArtifact[] (mutable). This causes a TypeScript incompatibility (readonly array not assignable
to mutable array) and can break typechecking/builds for the local storage provider.
Agent Prompt
## Issue description
`src/providers/storage/local.ts` changed `writeIndex` to accept `readonly BuildArtifact[]`, but it forwards that parameter to `artifactRetention.writeIndex`, which is typed to require a mutable `BuildArtifact[]`. TypeScript will reject passing a readonly array to a function that may mutate it.

## Issue Context
The underlying implementation (`writeArtifactIndex`) simply serializes the array and does not mutate it, so the easiest fix is to make the `ArtifactRetention` write API accept `readonly BuildArtifact[]` (or alternatively, keep `writeIndex` mutable in the provider).

## Fix Focus Areas
- src/providers/storage/local.ts[25-41]
- src/core/services/artifactRetention.ts[11-21]
- src/core/build/artifactRetention.ts[45-56]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@cubic-dev-ai cubic-dev-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.

1 issue found across 2 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/providers/storage/local.ts">

<violation number="1" location="src/providers/storage/local.ts:40">
P2: If `writeIndex` fails after the file copy has already succeeded, the copied binary at `destination` is left on disk without a corresponding index entry. Repeated failures can accumulate orphaned artifact files that `list()` can never discover. Consider cleaning up the copied file (or making the write recoverable) when index persistence fails.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

const artifactIndex = yield* readIndex();
artifactIndex.unshift({ ...artifact, path: destination });
yield* writeIndex(artifactIndex);
yield* writeIndex([{ ...artifact, path: destination }, ...artifactIndex]);

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: If writeIndex fails after the file copy has already succeeded, the copied binary at destination is left on disk without a corresponding index entry. Repeated failures can accumulate orphaned artifact files that list() can never discover. Consider cleaning up the copied file (or making the write recoverable) when index persistence fails.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/providers/storage/local.ts, line 40:

<comment>If `writeIndex` fails after the file copy has already succeeded, the copied binary at `destination` is left on disk without a corresponding index entry. Repeated failures can accumulate orphaned artifact files that `list()` can never discover. Consider cleaning up the copied file (or making the write recoverable) when index persistence fails.</comment>

<file context>
@@ -37,8 +37,7 @@ export const makeLocalStorageProvider = (directoryOverride?: string) =>
           const artifactIndex = yield* readIndex();
-          artifactIndex.unshift({ ...artifact, path: destination });
-          yield* writeIndex(artifactIndex);
+          yield* writeIndex([{ ...artifact, path: destination }, ...artifactIndex]);
           return { id: artifactId, location: destination };
         }),
</file context>

@YosefHayim

Copy link
Copy Markdown
Owner Author

Superseded by land of tip stack #386 (same 12 domain commits).

@YosefHayim YosefHayim closed this Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XS This PR changes 0-9 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant