Skip to content

refactor(types): readonly domain shapes with MutableDeep builders - #307

Open
YosefHayim wants to merge 1 commit into
mainfrom
review/readonly-domain-types
Open

refactor(types): readonly domain shapes with MutableDeep builders#307
YosefHayim wants to merge 1 commit into
mainfrom
review/readonly-domain-types

Conversation

@YosefHayim

@YosefHayim YosefHayim commented Aug 4, 2026

Copy link
Copy Markdown
Owner

Summary

  • Domain types become Readonly / readonly arrays at the boundary.
  • Adds MutableDeep for local builder drafts (Apple/Google adapters, pipeline, store reconcilers).
  • PlannedAction stays hybrid: readonly description/destructive, mutable status/error for apply paths.

Review focus

Finding 1: immutable domain shapes without weakening public types via assertions.

Note: Intentionally excludes the thin-CLI facade removals and asyncPool deletion so those can land (or fail) independently.

Test plan

  • Pre-commit typecheck + lint:style on this branch
  • Full gate / CI green
  • Spot-check MutableDeep only at builder boundaries, not on public domain returns

Summary by cubic

Make domain types immutable at the boundary and introduce MutableDeep for safe, local builder drafts. Tightens many APIs to use readonly arrays/records; PlannedAction stays hybrid. No runtime behavior changes.

  • Refactors

    • Wrapped public domain types in Readonly and switched params to readonly arrays across core (build pipeline, credentials, planning, snapshot, store reconcilers).
    • Added MutableDeep and updated Apple/Google adapters, pipeline, and tests to build drafts mutably, then return readonly results.
    • Kept PlannedAction with readonly description/destructive and mutable status/error.
    • Removed unnecessary Schema.mutable usage and normalized helpers to accept readonly inputs.
  • Migration

    • Use MutableDeep<Foo> for local builders and return Foo (readonly) at boundaries.
    • Update function signatures to readonly T[]; callers can still pass normal arrays, but don’t mutate inside.
    • Only mutate PlannedAction.status/error; treat other fields as readonly.

Written for commit b8b86e3. Summary will update on new commits.

Review in cubic

Why: Domain types should be immutable at the boundary; local builders need an explicit mutable draft type instead of weakening public shapes.
What: Wrap domain types in Readonly / readonly arrays; add MutableDeep; PlannedAction hybrid (readonly description/destructive, mutable status/error); update consumers, Apple/Google adapters, and builders to use MutableDeep drafts.
Impact: Stricter types across core; no intentional runtime behavior change (finding 1).
@codeant-ai

codeant-ai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Skipping CodeAnt AI review — this PR changes more than 100 files, which usually means a migration, codemod, or vendored drop. Line-level review on diffs this large produces duplicate findings on the same rewrite pattern and drowns out anything that actually matters.

If you still want a review, comment @codeant-ai : review. For better signal, consider splitting the PR into smaller chunks.

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 144 files, which is 44 over the limit of 100.

To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch.

Upgrade to a paid plan to raise the limit.

This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: cb0275f6-9efd-40ed-821e-eb597eaad813

📥 Commits

Reviewing files that changed from the base of the PR and between a57cdb1 and b8b86e3.

⛔ Files ignored due to path filters (1)
  • src/apple/generated/specPatch.ts is excluded by !**/generated/**
📒 Files selected for processing (144)
  • src/apple/ascClient.ts
  • src/cli/commands/completion.ts
  • src/cli/commands/testflight.ts
  • src/cli/options.ts
  • src/core/adopt/capabilities.ts
  • src/core/adopt/certs.ts
  • src/core/adopt/configWriter.ts
  • src/core/adopt/orchestrator.test.ts
  • src/core/adopt/orchestrator.ts
  • src/core/adopt/products.ts
  • src/core/agents/render.ts
  • src/core/build/appleTargets.ts
  • src/core/build/artifactRetention.ts
  • src/core/build/buildDiagnostics.ts
  • src/core/build/buildHistoryCommand.ts
  • src/core/build/pipeline.ts
  • src/core/build/pipelineEnv.ts
  • src/core/build/pipelineSigning.ts
  • src/core/build/pipelineTypes.ts
  • src/core/build/pipelineVersion.ts
  • src/core/build/remotePipeline.ts
  • src/core/build/resignCommand.ts
  • src/core/config/config.ts
  • src/core/config/configScaffold.ts
  • src/core/config/env.ts
  • src/core/config/jsonSchema.ts
  • src/core/config/schema.ts
  • src/core/config/toolchain.ts
  • src/core/credentials/accounts.ts
  • src/core/credentials/appleSigning.test.ts
  • src/core/credentials/appleSigning.ts
  • src/core/credentials/capabilities.ts
  • src/core/credentials/pushKeyStore.ts
  • src/core/credentials/signingPreflight.ts
  • src/core/dashboard/render.ts
  • src/core/distribution/updateHistory.ts
  • src/core/docs/commandDocs/commandReference.ts
  • src/core/docs/commandDocs/common.ts
  • src/core/doctor/command.ts
  • src/core/insights/command.ts
  • src/core/listing/apply.ts
  • src/core/listing/generator.ts
  • src/core/mcp/gate.test.ts
  • src/core/mcp/tools.test.ts
  • src/core/migrate/command.ts
  • src/core/migrate/eas.test.ts
  • src/core/migrate/eas.ts
  • src/core/migrate/fastlane.test.ts
  • src/core/migrate/fastlane.ts
  • src/core/migrate/scaffold.ts
  • src/core/plan/orchestrator.test.ts
  • src/core/plan/orchestrator.ts
  • src/core/plan/planners/appStoreSurface.ts
  • src/core/plan/planners/euDistribution.ts
  • src/core/plan/planners/gameCenter.ts
  • src/core/plan/planners/playProducts.ts
  • src/core/plan/planners/playSubscriptions.test.ts
  • src/core/plan/planners/playSubscriptions.ts
  • src/core/plan/planners/wallet.ts
  • src/core/privacy/parse.ts
  • src/core/privacy/reconcile.ts
  • src/core/readiness/appScopes.ts
  • src/core/readiness/orchestrator.test.ts
  • src/core/readiness/orchestrator.ts
  • src/core/readiness/probes/iapCodeReference.ts
  • src/core/readiness/probes/profileEntitlements.ts
  • src/core/release/betaReview.ts
  • src/core/release/statusCommand.test.ts
  • src/core/release/testflightFeedback.ts
  • src/core/release/version.ts
  • src/core/releaseTrain/engine.ts
  • src/core/releaseTrain/orchestrator.ts
  • src/core/releaseTrain/record.ts
  • src/core/services/appleCredentialsClient.ts
  • src/core/services/artifactRetention.ts
  • src/core/services/localCredentialsStore.ts
  • src/core/services/logger.ts
  • src/core/services/progress.ts
  • src/core/services/sandbox.ts
  • src/core/services/ssh.ts
  • src/core/snapshot/orchestrator.test.ts
  • src/core/snapshot/orchestrator.ts
  • src/core/snapshot/snapshotCommand.ts
  • src/core/snapshot/sources/appleListing.ts
  • src/core/snapshot/sources/appleProducts.ts
  • src/core/snapshot/sources/appleSubscriptions.ts
  • src/core/snapshot/sources/playProducts.ts
  • src/core/snapshot/sources/playRestore.ts
  • src/core/snapshot/sources/playSubscriptions.ts
  • src/core/snapshot/store.ts
  • src/core/store/accessibility.ts
  • src/core/store/appEvents.ts
  • src/core/store/ascScreenshots.ts
  • src/core/store/ascSync.ts
  • src/core/store/availability.ts
  • src/core/store/gameCenter.ts
  • src/core/store/offers.test.ts
  • src/core/store/offers.ts
  • src/core/store/offersCommand.ts
  • src/core/store/playProducts.ts
  • src/core/store/playSubscriptions.ts
  • src/core/store/playTracks.ts
  • src/core/store/reconcile.ts
  • src/core/store/reportsCommand.ts
  • src/core/store/syncJobs.ts
  • src/core/store/syncRun.ts
  • src/core/store/team.ts
  • src/core/store/walletIds.ts
  • src/core/terminal/completion.ts
  • src/core/terminal/halfblock.ts
  • src/core/terminal/wizardCommand.test.ts
  • src/core/terminal/wizardCommand.ts
  • src/core/types/adopt.ts
  • src/core/types/agents.ts
  • src/core/types/app.ts
  • src/core/types/appleCatalog.ts
  • src/core/types/artifacts.ts
  • src/core/types/catalog.ts
  • src/core/types/commandDocs.ts
  • src/core/types/config.ts
  • src/core/types/credentials.ts
  • src/core/types/dashboard.ts
  • src/core/types/doctor.ts
  • src/core/types/googlePlay.ts
  • src/core/types/insights.ts
  • src/core/types/listing.ts
  • src/core/types/mcp.ts
  • src/core/types/migrate.ts
  • src/core/types/mutable.ts
  • src/core/types/plan.ts
  • src/core/types/playPricing.ts
  • src/core/types/privacy.ts
  • src/core/types/providers.ts
  • src/core/types/readiness.ts
  • src/core/types/reconcile.ts
  • src/core/types/releaseTrain.ts
  • src/core/types/remote.ts
  • src/core/types/snapshot.ts
  • src/core/types/storeSurface.ts
  • src/core/types/vitals.ts
  • src/google/playClient.ts
  • src/google/playReporting.ts
  • src/providers/credentials/local.test.ts
  • src/providers/storage/local.ts

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


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.

@changeset-bot

changeset-bot Bot commented Aug 4, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: b8b86e3

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

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Refactor: make domain types readonly and add MutableDeep draft builders

✨ Enhancement 🕐 40+ Minutes

Grey Divider

AI Description

• Make exported domain shapes immutable via Readonly and readonly arrays at boundaries.
• Introduce MutableDeep for explicit mutable drafts in builders/adapters without type assertions.
• Keep PlannedAction hybrid mutability: readonly plan metadata, mutable apply status/error.
Diagram

graph TD
  A["Domain types (core/types)"] --> B["Readonly boundary"] --> C["Builders & reconcilers"] --> D["Apple ASC client"] --> X{{"External stores"}}
  B --> E["Build pipeline"] --> D
  B --> F["Google Play client"] --> X
  G["MutableDeep utility"] --> C
  G --> D
  G --> F
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Adopt a standard deep-writable type (e.g., type-fest WritableDeep)
  • ➕ Less custom type-level code to maintain
  • ➕ Well-tested edge-case handling across TS versions
  • ➖ Adds a new dependency (or larger dependency surface) for a small utility
  • ➖ May not align perfectly with project’s intended mutability semantics
2. Builder functions returning fresh immutable values (no mutable drafts)
  • ➕ Avoids any mutable typing; simplest mental model
  • ➕ Eliminates risk of leaking mutable drafts across boundaries
  • ➖ More verbose refactors across many call sites
  • ➖ Can be harder to incrementally adopt in code that currently constructs objects stepwise
3. Use structuredClone (or explicit deep copy) for external-client request shapes
  • ➕ Preserves more JS values than JSON clone; fewer surprises with undefined/null
  • ➕ Makes intent explicit for boundary conversion
  • ➖ Runtime/environment constraints depending on Node version
  • ➖ Still requires deciding where cloning occurs and can increase perf cost

Recommendation: The PR’s approach is the best fit for this codebase: enforce readonly at the domain boundary, and explicitly opt into deep mutability only for local drafts via MutableDeep. This keeps public types strict (no broad assertions) while still supporting stepwise builders required by API adapters and reconcilers. The only area worth double-checking is that deep-clone boundaries (e.g., Google request shaping) are used narrowly and consistently to avoid accidental runtime shape changes.

Files changed (145) +1449 / -1254

Refactor (131) +1415 / -1228
ascClient.tsAdopt readonly inputs and mutable drafts for ASC request/normalization +16/-12

Adopt readonly inputs and mutable drafts for ASC request/normalization

• Updates ASC client helpers and method signatures to accept 'readonly' arrays and uses 'MutableDeep' for locally-constructed response/request objects. Copies readonly arrays (e.g., roles, eligibilities) into mutable arrays when building request payloads.

src/apple/ascClient.ts

specPatch.tsAdjust generated spec patch typing for readonly compatibility +1/-1

Adjust generated spec patch typing for readonly compatibility

• Small typing tweak to keep generated App Store Connect OpenAPI patch compatible with readonly domain shapes.

src/apple/generated/specPatch.ts

completion.tsMake completion command args readonly +1/-1

Make completion command args readonly

• Changes the completion command handler to accept 'readonly string[]' for typed CLI word lists.

src/cli/commands/completion.ts

testflight.tsMake TestFlight email args readonly +2/-2

Make TestFlight email args readonly

• Updates TestFlight command handlers to accept 'readonly' email argument arrays, matching readonly boundary typing.

src/cli/commands/testflight.ts

options.tsReadonly-ify CLI option list typing +1/-1

Readonly-ify CLI option list typing

• Adjusts CLI option typing to use readonly arrays where arguments are treated as immutable inputs.

src/cli/options.ts

capabilities.tsAdopt flow updated for readonly domain inputs +9/-7

Adopt flow updated for readonly domain inputs

• Updates adopter capability planning/building to work with readonly domain shapes and avoids mutating boundary values.

src/core/adopt/capabilities.ts

certs.tsCert adopter typing aligned with readonly domain shapes +5/-5

Cert adopter typing aligned with readonly domain shapes

• Adjusts certificate adoption logic to accept readonly inputs and avoid mutating domain arrays/objects.

src/core/adopt/certs.ts

configWriter.tsConfig writer updated for readonly writes/plans +5/-5

Config writer updated for readonly writes/plans

• Updates config writer helpers to work with readonly plan/write shapes, keeping mutation confined to local drafts.

src/core/adopt/configWriter.ts

orchestrator.tsAdopt orchestrator uses readonly plan context/shapes +3/-3

Adopt orchestrator uses readonly plan context/shapes

• Adjusts orchestrator plumbing to pass readonly domain values through without mutation.

src/core/adopt/orchestrator.ts

products.tsProducts adopter uses readonly configs and mutable drafts +17/-13

Products adopter uses readonly configs and mutable drafts

• Updates product adoption logic to accept readonly config arrays and uses explicit draft-building where mutation is required.

src/core/adopt/products.ts

render.tsAgent rendering updated for readonly domain shapes +9/-9

Agent rendering updated for readonly domain shapes

• Adjusts agent rendering functions/types to consume readonly arrays/objects for skills/steps and related content.

src/core/agents/render.ts

appleTargets.tsReadonly typing for Apple build target lists +3/-1

Readonly typing for Apple build target lists

• Updates Apple target selection helpers to accept/pass readonly arrays without in-place mutation.

src/core/build/appleTargets.ts

artifactRetention.tsArtifact retention APIs accept readonly indices +7/-4

Artifact retention APIs accept readonly indices

• Refactors retention helpers to take 'readonly' artifact lists and avoid mutating index arrays directly.

src/core/build/artifactRetention.ts

buildDiagnostics.tsReadonly typing tweak in build diagnostics +1/-1

Readonly typing tweak in build diagnostics

• Minor typing adjustment to align diagnostics structures with readonly domain shapes.

src/core/build/buildDiagnostics.ts

buildHistoryCommand.tsBuild history command updated for readonly results +3/-3

Build history command updated for readonly results

• Adjusts build history rendering/collection to treat returned collections as readonly.

src/core/build/buildHistoryCommand.ts

pipeline.tsBuild pipeline constructs mutable draft context from readonly app/config +20/-4

Build pipeline constructs mutable draft context from readonly app/config

• Introduces explicit draft cloning for 'AppDescriptor' and 'ResolvedBuildContext' using 'MutableDeep', keeping boundary types readonly while allowing internal stepwise assembly.

src/core/build/pipeline.ts

pipelineEnv.tsPipeline env helpers updated for readonly exclude lists +3/-3

Pipeline env helpers updated for readonly exclude lists

• Updates environment processing to accept readonly arrays (e.g., exclusion lists) without mutating inputs.

src/core/build/pipelineEnv.ts

pipelineSigning.tsSigning pipeline uses readonly inputs and local drafts +6/-3

Signing pipeline uses readonly inputs and local drafts

• Adjusts signing pipeline data flow to consume readonly domain shapes and only mutate local draft objects where needed.

src/core/build/pipelineSigning.ts

pipelineTypes.tsPipeline type surfaces readonly-ified +3/-2

Pipeline type surfaces readonly-ified

• Updates pipeline-related type definitions to wrap exported structures with 'Readonly'/readonly arrays.

src/core/build/pipelineTypes.ts

pipelineVersion.tsPipeline version typing aligned with readonly boundary +3/-2

Pipeline version typing aligned with readonly boundary

• Minor type refactor to ensure version/pipeline data is treated as readonly at the boundary.

src/core/build/pipelineVersion.ts

remotePipeline.tsRemote pipeline typing updated for readonly inputs +2/-1

Remote pipeline typing updated for readonly inputs

• Adjusts remote pipeline interfaces to accept readonly collections where applicable.

src/core/build/remotePipeline.ts

resignCommand.tsResign command updated for readonly typing +1/-1

Resign command updated for readonly typing

• Small signature/type changes to conform to readonly domain types.

src/core/build/resignCommand.ts

config.tsConfig loader returns readonly-shaped config types +2/-1

Config loader returns readonly-shaped config types

• Adjusts config loading/types to match newly readonly 'LaunchConfig' and related domain shapes.

src/core/config/config.ts

configScaffold.tsConfig scaffolding updated for readonly config shapes +1/-1

Config scaffolding updated for readonly config shapes

• Updates scaffolded config typing to align with readonly domain exports.

src/core/config/configScaffold.ts

env.tsEnv config helpers updated for readonly config inputs +4/-4

Env config helpers updated for readonly config inputs

• Refactors env helper signatures to accept readonly domain values without mutation.

src/core/config/env.ts

jsonSchema.tsJSON schema typing updated for readonly domain models +1/-1

JSON schema typing updated for readonly domain models

• Minor type tweaks to keep schema generation/usage compatible with readonly domain types.

src/core/config/jsonSchema.ts

schema.tsConfig schema types aligned with readonly domain exports +2/-2

Config schema types aligned with readonly domain exports

• Updates schema typings to reflect 'Readonly'-wrapped domain interfaces.

src/core/config/schema.ts

toolchain.tsToolchain config typing updated for readonly shapes +3/-3

Toolchain config typing updated for readonly shapes

• Adjusts toolchain config surfaces to accept/read readonly domain values.

src/core/config/toolchain.ts

accounts.tsAccounts storage schema becomes immutable + readonly arrays +22/-25

Accounts storage schema becomes immutable + readonly arrays

• Removes 'Schema.mutable' usage, updates selectors to accept readonly arrays, and uses 'MutableDeep' plus array copies when constructing/updating persisted account records.

src/core/credentials/accounts.ts

appleSigning.tsSigning code updated for readonly shapes and mutable drafts +5/-4

Signing code updated for readonly shapes and mutable drafts

• Adjusts signing record construction and helper signatures to avoid mutating readonly domain values.

src/core/credentials/appleSigning.ts

capabilities.tsCapabilities credential typing updated for readonly boundary +1/-1

Capabilities credential typing updated for readonly boundary

• Minor type refactor to keep capabilities credential code compatible with readonly domain exports.

src/core/credentials/capabilities.ts

pushKeyStore.tsPush key store typing updated for readonly shapes +2/-1

Push key store typing updated for readonly shapes

• Small typing updates to treat collections as readonly at boundaries.

src/core/credentials/pushKeyStore.ts

signingPreflight.tsSigning preflight updated for readonly domain inputs +5/-3

Signing preflight updated for readonly domain inputs

• Adjusts preflight logic to accept readonly arrays/objects and only mutate local draft state.

src/core/credentials/signingPreflight.ts

render.tsDashboard render uses readonly domain shapes +4/-4

Dashboard render uses readonly domain shapes

• Updates dashboard rendering to consume readonly report/value collections without mutation.

src/core/dashboard/render.ts

updateHistory.tsDistribution history typing updated for readonly inputs +1/-1

Distribution history typing updated for readonly inputs

• Minor signature/type changes for readonly domain compatibility.

src/core/distribution/updateHistory.ts

commandReference.tsDocs command reference updated for readonly typing +1/-1

Docs command reference updated for readonly typing

• Adjusts docs command-reference plumbing to consume readonly command metadata shapes.

src/core/docs/commandDocs/commandReference.ts

common.tsDocs common types updated for readonly arrays +1/-1

Docs common types updated for readonly arrays

• Minor readonly typing changes for doc-model collections.

src/core/docs/commandDocs/common.ts

command.tsDoctor command updated for readonly typing +1/-1

Doctor command updated for readonly typing

• Small type changes to align doctor command surfaces with readonly domain exports.

src/core/doctor/command.ts

command.tsInsights command updated for readonly types +3/-2

Insights command updated for readonly types

• Updates insights command wiring/types to consume readonly domain data structures.

src/core/insights/command.ts

apply.tsListing draft application uses MutableDeep and safe locale merges +40/-8

Listing draft application uses MutableDeep and safe locale merges

• Adds 'MutableDeep' for clamped/brief objects, makes keyword inputs readonly, and introduces an explicit Apple locale merge that copies keyword arrays to avoid mutating readonly values.

src/core/listing/apply.ts

generator.tsListing generator updated for readonly boundary types +2/-1

Listing generator updated for readonly boundary types

• Minor typing updates so generated listing structures are treated as readonly outputs.

src/core/listing/generator.ts

command.tsMigrate command updated for readonly domain typing +1/-1

Migrate command updated for readonly domain typing

• Minor type/signature updates for readonly compatibility in migrate command wiring.

src/core/migrate/command.ts

eas.tsEAS migration outputs use readonly domain shapes +10/-6

EAS migration outputs use readonly domain shapes

• Refactors migration parsing/output types to use 'Readonly' wrappers and readonly arrays.

src/core/migrate/eas.ts

fastlane.tsFastlane migration typing updated for readonly shapes +18/-15

Fastlane migration typing updated for readonly shapes

• Adjusts migration parse/build logic to treat domain values as readonly at boundaries.

src/core/migrate/fastlane.ts

scaffold.tsMigration scaffolding typing aligned with readonly exports +1/-1

Migration scaffolding typing aligned with readonly exports

• Minor type adjustments for readonly config/domain outputs.

src/core/migrate/scaffold.ts

orchestrator.tsPlan orchestrator updated for readonly plan context/types +1/-1

Plan orchestrator updated for readonly plan context/types

• Adjusts plan orchestration to pass readonly app lists and readonly plan structures.

src/core/plan/orchestrator.ts

appStoreSurface.tsApp Store surface planner updated for readonly arrays +5/-2

App Store surface planner updated for readonly arrays

• Refactors planner inputs/outputs to use readonly app lists and readonly planned action collections.

src/core/plan/planners/appStoreSurface.ts

euDistribution.tsEU distribution planner updated for readonly typing +2/-1

EU distribution planner updated for readonly typing

• Minor typing changes to treat planner inputs/outputs as readonly.

src/core/plan/planners/euDistribution.ts

gameCenter.tsGame Center planner updated for readonly typing +2/-1

Game Center planner updated for readonly typing

• Minor readonly typing adjustments in planner implementation.

src/core/plan/planners/gameCenter.ts

playProducts.tsPlay products planner updated for readonly typing +1/-1

Play products planner updated for readonly typing

• Minor type updates to align Play planner surfaces with readonly domain exports.

src/core/plan/planners/playProducts.ts

playSubscriptions.tsPlay subscriptions planner updated for readonly typing +4/-1

Play subscriptions planner updated for readonly typing

• Adjusts planner logic to accept readonly config arrays and emit readonly plan results.

src/core/plan/planners/playSubscriptions.ts

wallet.tsWallet planner updated for readonly typing +2/-1

Wallet planner updated for readonly typing

• Minor typing adjustments to use readonly domain shapes.

src/core/plan/planners/wallet.ts

parse.tsPrivacy parser typing updated for readonly shapes +1/-1

Privacy parser typing updated for readonly shapes

• Minor type tweaks to keep privacy parsing compatible with readonly domain exports.

src/core/privacy/parse.ts

reconcile.tsPrivacy reconciliation typing updated for readonly shapes +1/-1

Privacy reconciliation typing updated for readonly shapes

• Minor readonly typing updates in privacy reconcile path.

src/core/privacy/reconcile.ts

appScopes.tsReadiness app scopes updated for readonly arrays +2/-2

Readiness app scopes updated for readonly arrays

• Updates readiness scope types/logic to accept readonly collections.

src/core/readiness/appScopes.ts

orchestrator.tsReadiness orchestrator updated for readonly context/types +1/-1

Readiness orchestrator updated for readonly context/types

• Refactors orchestrator plumbing to pass readonly app lists and readonly probe results.

src/core/readiness/orchestrator.ts

iapCodeReference.tsIAP code reference probe updated for readonly typing +1/-1

IAP code reference probe updated for readonly typing

• Minor readonly typing update for probe inputs/outputs.

src/core/readiness/probes/iapCodeReference.ts

profileEntitlements.tsProfile entitlements probe updated for readonly typing +1/-1

Profile entitlements probe updated for readonly typing

• Minor type changes to treat scanned values as readonly at boundaries.

src/core/readiness/probes/profileEntitlements.ts

betaReview.tsBeta review logic updated for readonly typing +2/-1

Beta review logic updated for readonly typing

• Adjusts beta review types/collections to use readonly arrays.

src/core/release/betaReview.ts

testflightFeedback.tsTestFlight feedback typing updated for readonly domain shapes +1/-1

TestFlight feedback typing updated for readonly domain shapes

• Minor typing changes for readonly 'BetaFeedback' structures (including screenshot arrays).

src/core/release/testflightFeedback.ts

version.tsRelease version typing aligned with readonly boundary +1/-1

Release version typing aligned with readonly boundary

• Minor type adjustments to treat release/version data as readonly outputs.

src/core/release/version.ts

engine.tsRelease-train engine updated for readonly typing +1/-1

Release-train engine updated for readonly typing

• Minor type changes to align engine inputs/outputs with readonly record shapes.

src/core/releaseTrain/engine.ts

orchestrator.tsRelease-train orchestrator updated for readonly domain shapes +8/-3

Release-train orchestrator updated for readonly domain shapes

• Adjusts orchestrator flows to treat train record structures and collections as readonly at boundaries.

src/core/releaseTrain/orchestrator.ts

record.tsRelease-train record schemas made immutable (remove Schema.mutable) +33/-39

Release-train record schemas made immutable (remove Schema.mutable)

• Removes 'Schema.mutable' wrappers and redefines schemas as immutable 'Schema.Struct'/'Schema.Array' to match readonly record types.

src/core/releaseTrain/record.ts

appleCredentialsClient.tsApple credentials client typing updated for readonly arrays +1/-1

Apple credentials client typing updated for readonly arrays

• Minor signature updates to accept/pass readonly collections.

src/core/services/appleCredentialsClient.ts

artifactRetention.tsArtifact retention service updated for readonly indices +2/-2

Artifact retention service updated for readonly indices

• Updates service surface to accept readonly artifact index arrays and avoid in-place mutation.

src/core/services/artifactRetention.ts

localCredentialsStore.tsLocal credentials store typing aligned with readonly domain shapes +1/-1

Local credentials store typing aligned with readonly domain shapes

• Minor type changes to treat stored collections as readonly at the boundary.

src/core/services/localCredentialsStore.ts

logger.tsLogger typing updated for readonly argument lists +2/-2

Logger typing updated for readonly argument lists

• Minor signature changes to use readonly arrays where logs take list inputs.

src/core/services/logger.ts

progress.tsProgress service uses readonly inputs and draft state +6/-2

Progress service uses readonly inputs and draft state

• Adjusts progress/reporting utilities to accept readonly collections and avoid mutating boundary values.

src/core/services/progress.ts

sandbox.tsSandbox service typing updated for readonly arrays +1/-1

Sandbox service typing updated for readonly arrays

• Minor signature change to accept readonly id lists consistent with ASC client updates.

src/core/services/sandbox.ts

ssh.tsSSH service typing aligned with readonly boundary shapes +1/-1

SSH service typing aligned with readonly boundary shapes

• Minor type changes to treat inputs as readonly at boundaries.

src/core/services/ssh.ts

orchestrator.tsSnapshot orchestrator updated for readonly capture structures +1/-1

Snapshot orchestrator updated for readonly capture structures

• Refactors orchestrator to emit/handle readonly 'Snapshot'/'CaptureReport' structures.

src/core/snapshot/orchestrator.ts

snapshotCommand.tsSnapshot command updated for readonly snapshot types +3/-3

Snapshot command updated for readonly snapshot types

• Adjusts command wiring/output typing to match readonly snapshot domain models.

src/core/snapshot/snapshotCommand.ts

appleListing.tsApple listing snapshot source updated for readonly localizations + JsonValue narrowing +22/-10

Apple listing snapshot source updated for readonly localizations + JsonValue narrowing

• Updates capture helpers to accept readonly localization arrays and adds a JSON-object type guard to safely read captured fields under the new readonly 'JsonValue' type.

src/core/snapshot/sources/appleListing.ts

appleProducts.tsApple products snapshot source updated for readonly typing +5/-7

Apple products snapshot source updated for readonly typing

• Adjusts Apple products capture/normalize helpers to treat returned collections as readonly arrays.

src/core/snapshot/sources/appleProducts.ts

appleSubscriptions.tsApple subscriptions snapshot source updated for readonly typing +5/-7

Apple subscriptions snapshot source updated for readonly typing

• Updates subscription capture typing to use readonly arrays/objects for normalized entities.

src/core/snapshot/sources/appleSubscriptions.ts

playProducts.tsPlay products snapshot source updated for readonly typing +3/-2

Play products snapshot source updated for readonly typing

• Minor typing changes to accept/return readonly collections from Play reader surface.

src/core/snapshot/sources/playProducts.ts

playRestore.tsPlay restore updated for readonly snapshot entities +16/-5

Play restore updated for readonly snapshot entities

• Adjusts restore logic to accept readonly snapshot entities/app lists and to build mutable request drafts only where required.

src/core/snapshot/sources/playRestore.ts

playSubscriptions.tsPlay subscriptions snapshot source updated for readonly typing +3/-3

Play subscriptions snapshot source updated for readonly typing

• Minor typing updates to treat Play subscription resources as readonly collections at boundaries.

src/core/snapshot/sources/playSubscriptions.ts

store.tsSnapshot store schemas made immutable (remove Schema.mutable) +36/-50

Snapshot store schemas made immutable (remove Schema.mutable)

• Removes 'Schema.mutable' usage throughout snapshot persistence schemas and switches to immutable 'Schema.Array'/'Schema.Record'/'Schema.Struct' to align with readonly snapshot types.

src/core/snapshot/store.ts

accessibility.tsAccessibility store surface updated for readonly typing +2/-1

Accessibility store surface updated for readonly typing

• Minor type adjustments to treat support field collections as readonly at boundaries.

src/core/store/accessibility.ts

appEvents.tsApp events store surface updated for readonly typing +3/-2

App events store surface updated for readonly typing

• Adjusts inputs/outputs to use readonly collections consistent with readonly domain types.

src/core/store/appEvents.ts

ascScreenshots.tsASC screenshots reconciler updated for readonly typing +5/-5

ASC screenshots reconciler updated for readonly typing

• Refactors screenshot reconcile helpers to accept readonly arrays and avoid mutating domain inputs.

src/core/store/ascScreenshots.ts

ascSync.tsASC sync surface updated for readonly planned actions/config inputs +8/-7

ASC sync surface updated for readonly planned actions/config inputs

• Updates the ASC sync API surface and reconcile plumbing to accept readonly domain models and planned action lists.

src/core/store/ascSync.ts

availability.tsAvailability reconciler updated for readonly territory lists +1/-1

Availability reconciler updated for readonly territory lists

• Updates territory list inputs to 'readonly' arrays and avoids mutating boundary values.

src/core/store/availability.ts

gameCenter.tsGame Center reconciler updated for readonly typing +6/-5

Game Center reconciler updated for readonly typing

• Refactors reconcile inputs/outputs to use readonly domain shapes and local draft state where necessary.

src/core/store/gameCenter.ts

offers.tsOffers reconciler updated for readonly configs and MutableDeep actions/request drafts +27/-20

Offers reconciler updated for readonly configs and MutableDeep actions/request drafts

• Refactors offer reconcile functions to accept readonly config arrays, uses 'MutableDeep<PlannedAction>' for apply-time status mutation, and builds request payload drafts without mutating readonly inputs.

src/core/store/offers.ts

offersCommand.tsOffers command updated for readonly outputs +1/-1

Offers command updated for readonly outputs

• Minor type updates in offers command wiring/rendering to consume readonly plan/apply outputs.

src/core/store/offersCommand.ts

playProducts.tsPlay products reconciler updated for readonly typing +2/-1

Play products reconciler updated for readonly typing

• Adjusts Play product reconciliation to accept readonly config arrays and treat live-state lists as readonly inputs.

src/core/store/playProducts.ts

playSubscriptions.tsPlay subscriptions reconciler updated for readonly configs and mutable request drafts +23/-18

Play subscriptions reconciler updated for readonly configs and mutable request drafts

• Updates listing/base-plan/offer helpers to accept readonly arrays and uses 'MutableDeep' to re-encode resendable base plans and to mutate 'PlannedAction' status during apply.

src/core/store/playSubscriptions.ts

playTracks.tsPlay tracks reconciler updated for readonly typing +2/-1

Play tracks reconciler updated for readonly typing

• Minor typing updates to accept readonly releases/version-code lists without mutation.

src/core/store/playTracks.ts

reconcile.tsReconcile context uses MutableDeep PlannedAction handles +17/-5

Reconcile context uses MutableDeep PlannedAction handles

• Changes reconcile context/actions to store mutable 'PlannedAction' drafts while keeping summarization APIs accepting readonly planned-action lists.

src/core/store/reconcile.ts

reportsCommand.tsReports command updated for readonly report structures +3/-2

Reports command updated for readonly report structures

• Minor updates to treat report collections as readonly in command output paths.

src/core/store/reportsCommand.ts

syncJobs.tsSync jobs typing updated for readonly inputs/outputs +3/-3

Sync jobs typing updated for readonly inputs/outputs

• Adjusts sync job structures to use readonly arrays and avoid mutating boundary values.

src/core/store/syncJobs.ts

syncRun.tsSync run uses readonly collections and local drafts +9/-3

Sync run uses readonly collections and local drafts

• Updates sync run plumbing to pass readonly action lists/config inputs while mutating only local run state.

src/core/store/syncRun.ts

team.tsTeam surface updated for readonly typing +1/-1

Team surface updated for readonly typing

• Minor signature/type changes for readonly compatibility.

src/core/store/team.ts

walletIds.tsWallet IDs surface updated for readonly typing +1/-1

Wallet IDs surface updated for readonly typing

• Minor typing changes to treat ID lists as readonly at boundaries.

src/core/store/walletIds.ts

completion.tsTerminal completion utilities updated for readonly inputs +1/-1

Terminal completion utilities updated for readonly inputs

• Adjusts completion helpers to accept readonly word/command lists consistent with CLI changes.

src/core/terminal/completion.ts

halfblock.tsHalfblock rendering typing updated for readonly boundary +1/-1

Halfblock rendering typing updated for readonly boundary

• Minor typing change to align terminal rendering helpers with readonly domain types.

src/core/terminal/halfblock.ts

wizardCommand.tsWizard command updated for readonly inputs/outputs +2/-2

Wizard command updated for readonly inputs/outputs

• Adjusts wizard command typing to treat collections as readonly at boundaries.

src/core/terminal/wizardCommand.ts

adopt.tsAdopt domain types wrapped in Readonly + readonly collections +36/-30

Adopt domain types wrapped in Readonly + readonly collections

• Refactors adopt domain types and API surfaces to be 'Readonly'/'readonly' arrays, enforcing immutability for adopt planning outputs and read surfaces.

src/core/types/adopt.ts

agents.tsAgent domain types wrapped in Readonly + readonly arrays +33/-33

Agent domain types wrapped in Readonly + readonly arrays

• Wraps agent-related types (skills, steps, guardrails, etc.) in 'Readonly' and converts list fields to readonly arrays.

src/core/types/agents.ts

app.tsApp/build domain types wrapped in Readonly + readonly arrays +12/-12

App/build domain types wrapped in Readonly + readonly arrays

• Makes 'AppDescriptor', 'BuildProfile', feedback shapes, and related list fields readonly at the boundary.

src/core/types/app.ts

appleCatalog.tsApple catalog resource types become Readonly shapes +179/-178

Apple catalog resource types become Readonly shapes

• Converts Apple catalog resource/domain shapes to 'Readonly' objects and readonly arrays to enforce immutability for read models.

src/core/types/appleCatalog.ts

artifacts.tsArtifact domain types updated for readonly collections +11/-11

Artifact domain types updated for readonly collections

• Wraps artifact-related types with readonly list fields to prevent boundary mutation.

src/core/types/artifacts.ts

catalog.tsCatalog config types wrapped in Readonly + readonly arrays +69/-66

Catalog config types wrapped in Readonly + readonly arrays

• Makes product/subscription/offer config shapes 'Readonly' and converts nested arrays to readonly arrays throughout the catalog model.

src/core/types/catalog.ts

commandDocs.tsCommand docs types updated for readonly lists +13/-13

Command docs types updated for readonly lists

• Wraps command-doc domain types in 'Readonly' and uses readonly arrays for command/list fields.

src/core/types/commandDocs.ts

config.tsConfig domain types become Readonly and readonly arrays +15/-14

Config domain types become Readonly and readonly arrays

• Wraps 'LaunchConfig', 'ResolvedBuildContext', and related config types in 'Readonly' and converts list fields to readonly arrays.

src/core/types/config.ts

credentials.tsCredentials domain types updated for readonly boundary +26/-24

Credentials domain types updated for readonly boundary

• Adjusts credentials domain models to be 'Readonly' and to use readonly collections where applicable.

src/core/types/credentials.ts

dashboard.tsDashboard domain types updated for readonly collections +21/-21

Dashboard domain types updated for readonly collections

• Wraps dashboard/report domain types to be immutable at boundaries via 'Readonly' and readonly arrays.

src/core/types/dashboard.ts

doctor.tsDoctor domain types updated for readonly boundary +25/-24

Doctor domain types updated for readonly boundary

• Makes doctor findings/results types readonly, ensuring boundary immutability for CLI/rendering consumers.

src/core/types/doctor.ts

googlePlay.tsGoogle Play domain types updated for readonly collections +41/-41

Google Play domain types updated for readonly collections

• Wraps Play resource types and list fields to be 'Readonly'/readonly arrays for immutable boundary contracts.

src/core/types/googlePlay.ts

insights.tsInsights domain types updated for readonly shapes +12/-12

Insights domain types updated for readonly shapes

• Wraps insights domain models in 'Readonly' and converts list fields to readonly arrays.

src/core/types/insights.ts

listing.tsListing domain types updated for readonly arrays +9/-9

Listing domain types updated for readonly arrays

• Makes listing types immutable at boundaries (readonly arrays for keywords/locales, etc.).

src/core/types/listing.ts

mcp.tsMCP domain types updated for readonly boundary +9/-8

MCP domain types updated for readonly boundary

• Wraps MCP tool types/structures in 'Readonly' and uses readonly arrays for collections.

src/core/types/mcp.ts

migrate.tsMigration domain types updated for readonly boundary +40/-40

Migration domain types updated for readonly boundary

• Refactors migration domain model types to be readonly, including nested list fields.

src/core/types/migrate.ts

mutable.tsAdd MutableDeep utility type +7/-0

Add MutableDeep utility type

• Introduces 'MutableDeep<T>' to create deep-writable drafts from readonly domain types, including readonly arrays and readonly object keys.

src/core/types/mutable.ts

plan.tsPlan domain types become Readonly + readonly arrays +20/-20

Plan domain types become Readonly + readonly arrays

• Makes plan context/surfaces/app plans readonly, converts action/app lists to readonly arrays, and simplifies interface intersections.

src/core/types/plan.ts

playPricing.tsPlay pricing domain types updated for readonly boundary +9/-9

Play pricing domain types updated for readonly boundary

• Wraps Play pricing domain types with readonly list fields for immutability at the boundary.

src/core/types/playPricing.ts

privacy.tsPrivacy domain types updated for readonly boundary +11/-11

Privacy domain types updated for readonly boundary

• Wraps privacy domain model types in 'Readonly' and uses readonly arrays where applicable.

src/core/types/privacy.ts

providers.tsProvider domain types updated for readonly typing +21/-21

Provider domain types updated for readonly typing

• Adjusts provider interfaces/types to accept readonly config inputs and return readonly structures as appropriate.

src/core/types/providers.ts

readiness.tsReadiness domain types become Readonly + readonly arrays +61/-61

Readiness domain types become Readonly + readonly arrays

• Wraps readiness types (probes, reports, context) in 'Readonly' and converts list fields to readonly arrays.

src/core/types/readiness.ts

reconcile.tsPlannedAction becomes hybrid readonly/mutable; reports become Readonly +9/-6

PlannedAction becomes hybrid readonly/mutable; reports become Readonly

• Makes 'PlannedAction.description' and 'destructive' readonly while leaving 'status'/'error' mutable for apply paths; wraps 'ReconcileReport' and action lists as readonly.

src/core/types/reconcile.ts

releaseTrain.tsRelease-train domain types updated for readonly boundary +7/-7

[Comment truncated to fit github's 65,536-char limit.]

@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

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

Context used
✅ Compliance rules (platform): 47 rules

Grey Divider


Remediation recommended

1. pickSpecEntry uses function declaration 📘 Rule violation ⚙ Maintainability
Description
pickSpecEntry is a module-level exported function declaration instead of a const arrow
function. This violates the code-style rule requiring module-level functions to be declared as
const arrow functions before first use.
Code

src/apple/generated/specPatch.ts[56]

+export function pickSpecEntry(entries: readonly string[]): string | null {
Evidence
PR Compliance ID 2513447 requires module-level functions to be declared as const arrow functions.
The changed line in src/apple/generated/specPatch.ts declares pickSpecEntry using `export
function`, which does not comply with that requirement.

Rule 2513447: Declare module-level functions as const arrow functions before first use
src/apple/generated/specPatch.ts[56-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
The module-level exported function `pickSpecEntry` is declared using `export function ...`, but the style rule requires module-level functions to be declared as `export const ... = (...) => {}` (const arrow) before first use.

## Issue Context
This is in `src/apple/generated/specPatch.ts`, and the PR modified the `pickSpecEntry` signature.

## Fix Focus Areas
- src/apple/generated/specPatch.ts[56-65]

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


Grey Divider

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

Qodo Logo

* skipping the macOS resource-fork sibling. Returns the matching entry or null when absent.
*/
export function pickSpecEntry(entries: string[]): string | null {
export function pickSpecEntry(entries: readonly string[]): string | null {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

1. pickspecentry uses function declaration 📘 Rule violation ⚙ Maintainability

pickSpecEntry is a module-level exported function declaration instead of a const arrow
function. This violates the code-style rule requiring module-level functions to be declared as
const arrow functions before first use.
Agent Prompt
## Issue description
The module-level exported function `pickSpecEntry` is declared using `export function ...`, but the style rule requires module-level functions to be declared as `export const ... = (...) => {}` (const arrow) before first use.

## Issue Context
This is in `src/apple/generated/specPatch.ts`, and the PR modified the `pickSpecEntry` signature.

## Fix Focus Areas
- src/apple/generated/specPatch.ts[56-65]

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

19 issues found across 145 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/core/types/adopt.ts">

<violation number="1" location="src/core/types/adopt.ts:33">
P2: `EntitlementValue` is still partially mutable because array values remain `EntitlementValue[]`. That undermines the new readonly boundary model and allows post-read mutation of entitlement collections.</violation>
</file>

<file name="src/google/playClient.ts">

<violation number="1" location="src/google/playClient.ts:856">
P2: The Google request builder is now fed an entire readonly domain object via a JSON clone, but the clone is not a type-safe conversion to `Schema$Subscription`: `JSON.parse` returns arbitrary runtime data and the generic annotation only asserts the result's type. If the domain shape contains fields whose wire representation differs from Google's generated DTO (or unsupported nested fields), this sends them unvalidated. A dedicated mutable request builder that explicitly maps the supported fields would preserve the public readonly boundary without weakening the generated request type.</violation>
</file>

<file name="src/core/types/migrate.ts">

<violation number="1" location="src/core/types/migrate.ts:38">
P2: `Record<string, string>` entries remain mutable inside the `Readonly<>` wrapper — `Readonly` is shallow and only protects the `env` property from reassignment, not individual key mutations (`profile.env!["key"] = "value"` still compiles). The codebase already uses `Readonly<Record<string, string>>` elsewhere (e.g. fastlane.ts:294, playTracks.ts:43). Wrap in `Readonly<Record<string, string>>` to match the boundary-immutability goal.</violation>

<violation number="2" location="src/core/types/migrate.ts:69">
P2: `Record<string, EasBuildProfile>` and `Record<string, EasSubmitProfile>` entries are still mutable inside `Readonly<EasJson>` — `build` and `submit` can't be reassigned, but their entries (`easJson.build["profile"] = {...}`) still compile. Since `EasJson` is a boundary shape consumed by the pipeline and report modules, wrap the Records to enforce full immutability.</violation>
</file>

<file name="src/core/types/doctor.ts">

<violation number="1" location="src/core/types/doctor.ts:46">
P2: `DoctorAscApi` still returns a mutable capabilities array: `Readonly<{ capabilityType: string }>[]` freezes the row shape but not the collection, so a doctor consumer can mutate the API result with `.push()` or index assignment. Since this is explicitly documented as a read-only App Store Connect surface, the return type should be `readonly Readonly<{ ... }>[]`.</violation>

<violation number="2" location="src/core/types/doctor.ts:74">
P3: `DoctorContext` is the only domain type in this file that is not fully wrapped in `Readonly<>`, breaking the PR convention. Only `apps: readonly AppDescriptor[]` was made readonly; `config`, `platform`, `os`, `cwd`, `androidSdk`, and `shellLocale` stay mutable. Wrapping the whole type in `Readonly<{…}>` would be consistent with `DoctorCheck`, `DoctorReport`, `DoctorPlayApi`, and `DoctorAscApi`. Builders needing mutable access can use `MutableDeep<DoctorContext<…>>` per the PR strategy.</violation>
</file>

<file name="src/core/types/catalog.ts">

<violation number="1" location="src/core/types/catalog.ts:99">
P3: The readonly config wrappers leave the Google Play price maps mutable. `introPrices`, subscription override `prices`, and product override `prices` are still `Record<...>` values, so callers can mutate a config after it has crossed the public boundary; these maps should be `Readonly<Record<string, PlayPriceConfig>>`.</violation>
</file>

<file name="src/core/types/googlePlay.ts">

<violation number="1" location="src/core/types/googlePlay.ts:8">
P2: Several Google Play domain collections remain mutable despite the readonly refactor. `releaseNotes`, `countries`, and both `offerTags` fields use `Readonly<element>[]`, which still permits `push`/`splice` on arrays returned to callers; they should use `readonly Readonly<element>[]`.</violation>
</file>

<file name="src/core/listing/apply.ts">

<violation number="1" location="src/core/listing/apply.ts:139">
P3: The `applyDraft` API documentation is now separated from its declaration by `mergeAppleLocale`, so editor/TypeScript documentation associates the wrong description or leaves `applyDraft` undocumented. Keeping the helper's JSDoc with the helper and moving the `applyDraft` comment immediately above `export const applyDraft` preserves accurate API documentation.</violation>
</file>

<file name="src/core/snapshot/sources/appleListing.ts">

<violation number="1" location="src/core/snapshot/sources/appleListing.ts:52">
P3: `isJsonObject` (appleListing.ts) and `isJsonRecord` (playRestore.ts) are structurally identical type-narrowing helpers for `JsonValue` → `Readonly<{ [key: string]: JsonValue }>`. Since both are introduced in this batch, consider deduplicating to keep the codebase consistent. If they need to stay separate for architectural boundaries (Apple vs Play), that's fine — just a heads-up that they're byte-for-byte the same logic.</violation>
</file>

<file name="src/core/types/readiness.ts">

<violation number="1" location="src/core/types/readiness.ts:61">
P2: The readiness API is still exposing mutable result arrays even though this contract is documented as read-only: every `list*` method uses `Readonly<{ ... }>[]`, which makes each row readonly but still allows callers to call `.push`, `.splice`, or assign an index on the returned array. This leaves the public boundary weaker than the new readonly domain shapes and is inconsistent with `AdoptCatalogApi`/`SnapshotAscApi`, which use `readonly T[]`; the array modifier should be added to each list return type.</violation>
</file>

<file name="src/core/types/storeSurface.ts">

<violation number="1" location="src/core/types/storeSurface.ts:56">
P3: The readonly surface/config wrappers do not protect their nested keyed maps. `localizations`, `clips`, `releaseNotes`, and `ageRating` remain mutable `Record` values, allowing callers to edit a config through an index assignment despite the new readonly boundary; these should use `Readonly<Record<...>>`.</violation>
</file>

<file name="src/core/release/betaReview.ts">

<violation number="1" location="src/core/release/betaReview.ts:132">
P3: The apply helper unnecessarily widens `PlannedAction` so its fixed `description` and `destructive` fields become writable. Since this helper only updates `status` and `error`, keeping the parameter as `PlannedAction` preserves the hybrid contract and still accepts the value returned by `plan`.</violation>
</file>

<file name="src/core/store/gameCenter.ts">

<violation number="1" location="src/core/store/gameCenter.ts:381">
P2: The Game Center apply path can rewrite an action's description after it has been planned, which defeats `PlannedAction`'s new readonly/fixed-description contract. Keeping this parameter as `PlannedAction` and carrying the no-version-id explanation separately (or determining the final description before creating the action) would preserve the domain invariant instead of widening the action to `MutableDeep<PlannedAction>`.</violation>
</file>

<file name="src/core/store/reconcile.ts">

<violation number="1" location="src/core/store/reconcile.ts:8">
P2: Reconcile contexts now expose actions with mutable `description` and `destructive` fields, so callers can change the identity of an action after it has been planned and the readonly domain contract is no longer enforced. Keeping the context as `PlannedAction[]` preserves mutability for `status`/`error` while preventing edits to the fixed fields.</violation>

<violation number="2" location="src/core/store/reconcile.ts:51">
P2: The exported `plan` helper weakens the public `PlannedAction` contract by returning `MutableDeep<PlannedAction>`; callers can mutate `description` and `destructive` instead of only the apply-path fields. A narrow action handle that exposes just `status`/`error` (and a separate way to represent the exceptional Game Center message) would preserve the readonly domain boundary.</violation>
</file>

<file name="src/core/types/appleCatalog.ts">

<violation number="1" location="src/core/types/appleCatalog.ts:44">
P2: Consumers can still mutate the nested capability options array (`resource.settings?.[0].options?.push(...)`) because `Readonly<{ ... }>[]` makes each element readonly but leaves the array itself mutable. This weakens the readonly domain boundary; the array should be declared as `readonly Readonly<{ key: string }>[]` (and the same correction is needed for `screenshots` below).</violation>

<violation number="2" location="src/core/types/appleCatalog.ts:128">
P3: The new `Readonly` wrapper does not make the dynamic listing fields map immutable. A caller can still execute `listing.fields['description'] = '...'`, so the returned domain resource can be changed through a nested map; the map should be typed as `Readonly<Record<string, string>>` (and the other dynamic attribute maps in this file should follow the same pattern).</violation>

<violation number="3" location="src/core/types/appleCatalog.ts:236">
P2: The screenshot collection is still mutable: `Readonly<{ ... }>[]` protects the screenshot elements but not the array, so callers can append or remove feedback attachments from a returned domain resource. Using a `readonly ...[]` collection preserves the intended immutable boundary.</violation>
</file>

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

Re-trigger cubic

Comment thread src/core/types/adopt.ts
| null
| EntitlementValue[]
| {
| Readonly<{

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: EntitlementValue is still partially mutable because array values remain EntitlementValue[]. That undermines the new readonly boundary model and allows post-read mutation of entitlement collections.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/core/types/adopt.ts, line 33:

<comment>`EntitlementValue` is still partially mutable because array values remain `EntitlementValue[]`. That undermines the new readonly boundary model and allows post-read mutation of entitlement collections.</comment>

<file context>
@@ -30,104 +30,110 @@ export type EntitlementValue =
   | null
   | EntitlementValue[]
-  | {
+  | Readonly<{
       [key: string]: EntitlementValue;
-    };
</file context>

Comment thread src/google/playClient.ts
productId: subscription.productId,
'regionsVersion.version': REGIONS_VERSION,
requestBody: { ...subscription, packageName },
requestBody: mutableGoogleRequest<androidpublisher_v3.Schema$Subscription>({

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: The Google request builder is now fed an entire readonly domain object via a JSON clone, but the clone is not a type-safe conversion to Schema$Subscription: JSON.parse returns arbitrary runtime data and the generic annotation only asserts the result's type. If the domain shape contains fields whose wire representation differs from Google's generated DTO (or unsupported nested fields), this sends them unvalidated. A dedicated mutable request builder that explicitly maps the supported fields would preserve the public readonly boundary without weakening the generated request type.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/google/playClient.ts, line 856:

<comment>The Google request builder is now fed an entire readonly domain object via a JSON clone, but the clone is not a type-safe conversion to `Schema$Subscription`: `JSON.parse` returns arbitrary runtime data and the generic annotation only asserts the result's type. If the domain shape contains fields whose wire representation differs from Google's generated DTO (or unsupported nested fields), this sends them unvalidated. A dedicated mutable request builder that explicitly maps the supported fields would preserve the public readonly boundary without weakening the generated request type.</comment>

<file context>
@@ -840,7 +853,10 @@ export class GooglePlayClient {
         productId: subscription.productId,
         'regionsVersion.version': REGIONS_VERSION,
-        requestBody: { ...subscription, packageName },
+        requestBody: mutableGoogleRequest<androidpublisher_v3.Schema$Subscription>({
+          ...subscription,
+          packageName,
</file context>

Comment thread src/core/types/migrate.ts
* only one of them (or neither) still migrates cleanly; `cli` is optional.
*/
export type EasJson = {
export type EasJson = Readonly<{

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: Record<string, EasBuildProfile> and Record<string, EasSubmitProfile> entries are still mutable inside Readonly<EasJson>build and submit can't be reassigned, but their entries (easJson.build["profile"] = {...}) still compile. Since EasJson is a boundary shape consumed by the pipeline and report modules, wrap the Records to enforce full immutability.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/core/types/migrate.ts, line 69:

<comment>`Record<string, EasBuildProfile>` and `Record<string, EasSubmitProfile>` entries are still mutable inside `Readonly<EasJson>` — `build` and `submit` can't be reassigned, but their entries (`easJson.build["profile"] = {...}`) still compile. Since `EasJson` is a boundary shape consumed by the pipeline and report modules, wrap the Records to enforce full immutability.</comment>

<file context>
@@ -8,130 +8,130 @@ export type MigrationSource = 'eas' | 'fastlane';
  * only one of them (or neither) still migrates cleanly; `cli` is optional.
  */
-export type EasJson = {
+export type EasJson = Readonly<{
   cli?: EasCli;
   build: Record<string, EasBuildProfile>;
</file context>

Comment thread src/core/types/migrate.ts
* `channel`/`distribution`/`developmentClient` become report notes, `env` keys seed `.env.example`.
*/
export type EasBuildProfile = {
export type EasBuildProfile = Readonly<{

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: Record<string, string> entries remain mutable inside the Readonly<> wrapper — Readonly is shallow and only protects the env property from reassignment, not individual key mutations (profile.env!["key"] = "value" still compiles). The codebase already uses Readonly<Record<string, string>> elsewhere (e.g. fastlane.ts:294, playTracks.ts:43). Wrap in Readonly<Record<string, string>> to match the boundary-immutability goal.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/core/types/migrate.ts, line 38:

<comment>`Record<string, string>` entries remain mutable inside the `Readonly<>` wrapper — `Readonly` is shallow and only protects the `env` property from reassignment, not individual key mutations (`profile.env!["key"] = "value"` still compiles). The codebase already uses `Readonly<Record<string, string>>` elsewhere (e.g. fastlane.ts:294, playTracks.ts:43). Wrap in `Readonly<Record<string, string>>` to match the boundary-immutability goal.</comment>

<file context>
@@ -8,130 +8,130 @@ export type MigrationSource = 'eas' | 'fastlane';
  * `channel`/`distribution`/`developmentClient` become report notes, `env` keys seed `.env.example`.
  */
-export type EasBuildProfile = {
+export type EasBuildProfile = Readonly<{
   channel?: string;
   distribution?: string;
</file context>

};
export type PlayTrackInfo = { track: string; releases: PlayRelease[] };
export type PlayCountryAvailability = {
releaseNotes?: Readonly<{ language: string; text: string }>[];

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: Several Google Play domain collections remain mutable despite the readonly refactor. releaseNotes, countries, and both offerTags fields use Readonly<element>[], which still permits push/splice on arrays returned to callers; they should use readonly Readonly<element>[].

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/core/types/googlePlay.ts, line 8:

<comment>Several Google Play domain collections remain mutable despite the readonly refactor. `releaseNotes`, `countries`, and both `offerTags` fields use `Readonly<element>[]`, which still permits `push`/`splice` on arrays returned to callers; they should use `readonly Readonly<element>[]`.</comment>

<file context>
@@ -1,77 +1,77 @@
-};
-export type PlayTrackInfo = { track: string; releases: PlayRelease[] };
-export type PlayCountryAvailability = {
+  releaseNotes?: Readonly<{ language: string; text: string }>[];
+}>;
+export type PlayTrackInfo = Readonly<{ track: string; releases: readonly PlayRelease[] }>;
</file context>

Comment thread src/core/listing/apply.ts
* fields (so untouched fields and other locales survive), per targeted platform. The App Store fields
* map 1:1; the Play fields are derived via {@link deriveAndroidLocale}. Returns a new config.
*/
/** Merge a draft over one locale's existing App Store listing, copying keywords into a mutable array. */

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: The applyDraft API documentation is now separated from its declaration by mergeAppleLocale, so editor/TypeScript documentation associates the wrong description or leaves applyDraft undocumented. Keeping the helper's JSDoc with the helper and moving the applyDraft comment immediately above export const applyDraft preserves accurate API documentation.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/core/listing/apply.ts, line 139:

<comment>The `applyDraft` API documentation is now separated from its declaration by `mergeAppleLocale`, so editor/TypeScript documentation associates the wrong description or leaves `applyDraft` undocumented. Keeping the helper's JSDoc with the helper and moving the `applyDraft` comment immediately above `export const applyDraft` preserves accurate API documentation.</comment>

<file context>
@@ -135,6 +136,38 @@ export const deriveAndroidLocale = (listingDraft: DraftListing): AndroidLocaleIn
  * fields (so untouched fields and other locales survive), per targeted platform. The App Store fields
  * map 1:1; the Play fields are derived via {@link deriveAndroidLocale}. Returns a new config.
  */
+/** Merge a draft over one locale's existing App Store listing, copying keywords into a mutable array. */
+const mergeAppleLocale = (
+  existingLocale: AppleLocaleInfo | undefined,
</file context>

.map(([locale, fields]) => toEntity(locale, fields));
});
/** Narrow a captured {@link JsonValue} to a plain object (rejecting arrays and null). */
const isJsonObject = (

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: isJsonObject (appleListing.ts) and isJsonRecord (playRestore.ts) are structurally identical type-narrowing helpers for JsonValueReadonly<{ [key: string]: JsonValue }>. Since both are introduced in this batch, consider deduplicating to keep the codebase consistent. If they need to stay separate for architectural boundaries (Apple vs Play), that's fine — just a heads-up that they're byte-for-byte the same logic.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/core/snapshot/sources/appleListing.ts, line 52:

<comment>`isJsonObject` (appleListing.ts) and `isJsonRecord` (playRestore.ts) are structurally identical type-narrowing helpers for `JsonValue` → `Readonly<{ [key: string]: JsonValue }>`. Since both are introduced in this batch, consider deduplicating to keep the codebase consistent. If they need to stay separate for architectural boundaries (Apple vs Play), that's fine — just a heads-up that they're byte-for-byte the same logic.</comment>

<file context>
@@ -48,8 +48,20 @@ const captureListing = (
       .map(([locale, fields]) => toEntity(locale, fields));
   });
+/** Narrow a captured {@link JsonValue} to a plain object (rejecting arrays and null). */
+const isJsonObject = (
+  capturedNode: JsonValue,
+): capturedNode is Readonly<{ [key: string]: JsonValue }> => {
</file context>

}>;
/** One App Clip's card metadata. */
export type AppClipConfig = {
export type AppClipConfig = Readonly<{

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: The readonly surface/config wrappers do not protect their nested keyed maps. localizations, clips, releaseNotes, and ageRating remain mutable Record values, allowing callers to edit a config through an index assignment despite the new readonly boundary; these should use Readonly<Record<...>>.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/core/types/storeSurface.ts, line 56:

<comment>The readonly surface/config wrappers do not protect their nested keyed maps. `localizations`, `clips`, `releaseNotes`, and `ageRating` remain mutable `Record` values, allowing callers to edit a config through an index assignment despite the new readonly boundary; these should use `Readonly<Record<...>>`.</comment>

<file context>
@@ -32,66 +32,66 @@ export type AchievementConfig = {
+}>;
 /** One App Clip's card metadata. */
-export type AppClipConfig = {
+export type AppClipConfig = Readonly<{
   action?: (typeof APP_CLIP_ACTIONS)[number];
   localizations?: Record<string, AppClipLocalizationConfig>;
</file context>

/** Apply one note write while retaining per-action failures in the reconciliation report. */
const applyNote = (
action: PlannedAction,
action: MutableDeep<PlannedAction>,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: The apply helper unnecessarily widens PlannedAction so its fixed description and destructive fields become writable. Since this helper only updates status and error, keeping the parameter as PlannedAction preserves the hybrid contract and still accepts the value returned by plan.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/core/release/betaReview.ts, line 132:

<comment>The apply helper unnecessarily widens `PlannedAction` so its fixed `description` and `destructive` fields become writable. Since this helper only updates `status` and `error`, keeping the parameter as `PlannedAction` preserves the hybrid contract and still accepts the value returned by `plan`.</comment>

<file context>
@@ -128,7 +129,7 @@ const selectBuild = (
 /** Apply one note write while retaining per-action failures in the reconciliation report. */
 const applyNote = (
-  action: PlannedAction,
+  action: MutableDeep<PlannedAction>,
   noteWrite: Effect.Effect<void, unknown>,
 ): Effect.Effect<void> =>
</file context>
Suggested change
action: MutableDeep<PlannedAction>,
action: PlannedAction,

* against desired config is a plain key-by-key comparison.
*/
export type ListingLocalization = {
export type ListingLocalization = Readonly<{

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: The new Readonly wrapper does not make the dynamic listing fields map immutable. A caller can still execute listing.fields['description'] = '...', so the returned domain resource can be changed through a nested map; the map should be typed as Readonly<Record<string, string>> (and the other dynamic attribute maps in this file should follow the same pattern).

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/core/types/appleCatalog.ts, line 128:

<comment>The new `Readonly` wrapper does not make the dynamic listing fields map immutable. A caller can still execute `listing.fields['description'] = '...'`, so the returned domain resource can be changed through a nested map; the map should be typed as `Readonly<Record<string, string>>` (and the other dynamic attribute maps in this file should follow the same pattern).</comment>

<file context>
@@ -74,184 +74,185 @@ export type SandboxTesterResource = {
  * against desired config is a plain key-by-key comparison.
  */
-export type ListingLocalization = {
+export type ListingLocalization = Readonly<{
   id: string;
   locale: string;
</file context>

@YosefHayim YosefHayim added the domain:foundation Shared kernel: tooling, services, types, style label Aug 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

domain:foundation Shared kernel: tooling, services, types, style

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant