Skip to content

refactor(services): readonly-domain-types (stack 11/12, re-split #307) - #384

Closed
YosefHayim wants to merge 1 commit into
refactor/types/readonly-stack-10-readinessfrom
refactor/types/readonly-stack-11-services
Closed

refactor(services): readonly-domain-types (stack 11/12, re-split #307)#384
YosefHayim wants to merge 1 commit into
refactor/types/readonly-stack-10-readinessfrom
refactor/types/readonly-stack-11-services

Conversation

@YosefHayim

@YosefHayim YosefHayim commented Aug 7, 2026

Copy link
Copy Markdown
Owner

Stack 11/12 of re-split HOLD #307

Domain: services
Base: refactor/types/readonly-stack-10-readiness
Full green tip: refactor/foundation/readonly-types-full

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


Summary by cubic

Make array parameters and return types in core service interfaces readonly to prevent accidental mutation and align with the readonly-domain-types refactor. No runtime behavior changes.

  • Refactors
    • Apple credentials: deviceIds is now readonly string[] in createAdhocProfile.
    • Artifact retention: readIndex and writeIndex use readonly BuildArtifact[].
    • Local credentials store: extensions? is readonly string[].
    • Logger: box and shipped accept readonly string[] receipt lines.
    • Progress: reportFailure tail and runWithProgress args are readonly string[].
    • Sandbox: clearSandboxTesterPurchaseHistory takes readonly string[].
    • SSH: rsyncUp excludes is readonly string[].

Written for commit fad98ea. 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 fad98ea Aug 07, 2026 · 11:07 11:10

@changeset-bot

changeset-bot Bot commented Aug 7, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: fad98ea

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

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

Copy link
Copy Markdown

PR Summary by Qodo

refactor(services): use readonly array types in service interfaces

✨ Enhancement 🕐 10-20 Minutes

Grey Divider

AI Description

• Tighten services domain APIs to use readonly arrays for list-like inputs/outputs.
• Update progress/logger/ssh helpers to accept readonly argument/line/exclude lists.
• Preserve runtime behavior while enforcing stronger immutability at type-level.
Diagram

graph TD
  A["Core programs / callers"] --> B["Services domain types"] --> C["AppleCredentialsClient"] --> D["App Store Connect"]
  A --> E["Progress runner"] --> F["Logger"]
  A --> G["SSH utilities"] --> H["rsync/ssh"]
  A --> I["Sandbox API"] --> D
Loading
High-Level Assessment

This is the appropriate approach for a readonly-types migration: update the service contracts to readonly T[] and adjust consumers accordingly. Alternatives like introducing a ReadonlyArray alias or wrapper types are mostly stylistic and add churn without improving guarantees beyond the current pattern.

Files changed (7) +14 / -10

Refactor (7) +14 / -10
appleCredentialsClient.tsMake ad-hoc profile device ID list readonly +1/-1

Make ad-hoc profile device ID list readonly

• Updates the AppleCredentialsClient contract so 'createAdHocProfile' accepts 'deviceIds' as 'readonly string[]'. This strengthens immutability expectations for callers without changing behavior.

src/core/services/appleCredentialsClient.ts

artifactRetention.tsReturn/accept readonly artifact indexes +2/-2

Return/accept readonly artifact indexes

• Changes ArtifactRetentionService 'readIndex' to return 'readonly BuildArtifact[]' and 'writeIndex' to accept 'readonly BuildArtifact[]'. This prevents consumers from relying on mutating the returned/accepted collections.

src/core/services/artifactRetention.ts

localCredentialsStore.tsMake signing asset extension list readonly +1/-1

Make signing asset extension list readonly

• Updates 'loadAppleSigningAssets' to accept an optional 'extensions?: readonly string[]'. Aligns the credentials-store facade with readonly domain types.

src/core/services/localCredentialsStore.ts

logger.tsUse readonly receipt lines for boxed/shipped output +2/-2

Use readonly receipt lines for boxed/shipped output

• Updates Logger service signatures so 'box' and 'shipped' take 'receiptLines: readonly string[]'. This matches the readonly-types convention for list parameters.

src/core/services/logger.ts

progress.tsAdopt readonly arrays for failure tails and command args +6/-2

Adopt readonly arrays for failure tails and command args

• Changes 'reportFailure' to accept a readonly tail of log lines and updates 'runWithProgress' to accept 'args: readonly string[]'. No runtime logic changes; this is a type-level immutability tightening.

src/core/services/progress.ts

sandbox.tsMake sandbox tester ID batch readonly +1/-1

Make sandbox tester ID batch readonly

• Updates AscSandboxApi so 'clearSandboxTesterPurchaseHistory' accepts 'testerIds: readonly string[]'. Maintains the batched-clear semantics while preventing accidental caller mutation assumptions.

src/core/services/sandbox.ts

ssh.tsMake rsync exclude list readonly +1/-1

Make rsync exclude list readonly

• Updates 'rsyncUp' to accept 'excludes: readonly string[]'. This aligns CLI helper APIs with readonly list typing used across services.

src/core/services/ssh.ts

* sometimes precedes the trailing lines.
*/
const reportFailure = (label: string, tail: string[], logFile: string) =>
const reportFailure = (label: string, tail: readonly string[], logFile: 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.

Suggestion: The failure tail is populated from the raw child-process output, while build logs are only redacted when persisted. Reporting tail directly can therefore print API keys, bearer tokens, JWTs, or other secret-looking values to the terminal whenever a build fails. Redact each tail line before adding it to the failure report, especially when buildLog is active. [security]

Severity Level: Critical 🚨
- ❌ Interactive failed builds can print raw credentials.
- ❌ Fastlane and Gradle build diagnostics may expose secrets.
- ⚠️ Persisted logs are redacted but terminal output is not.

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/core/services/progress.ts
**Line:** 138:138
**Comment:**
	*Security: The failure tail is populated from the raw child-process output, while build logs are only redacted when persisted. Reporting `tail` directly can therefore print API keys, bearer tokens, JWTs, or other secret-looking values to the terminal whenever a build fails. Redact each tail line before adding it to the failure report, especially when `buildLog` is active.

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
👍 | 👎

Comment on lines +13 to 18
readonly readIndex: (indexPath: string) => Effect.Effect<readonly BuildArtifact[]>;
readonly writeIndex: (
artifactIndex: BuildArtifact[],
artifactIndex: readonly BuildArtifact[],
indexPath: string,
) => Effect.Effect<void, PlatformError>;
readonly prune: (

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: The separate readIndex and writeIndex operations allow local storage uploads to perform an unsynchronized read-modify-write. Concurrent put effects can both read the same index and then overwrite each other, losing one artifact entry. Serialize index updates or provide an atomic append/update operation. [race condition]

Severity Level: Major ⚠️
- ⚠️ Concurrent local builds can lose history entries.
-`builds history` omits successfully stored artifacts.
- ⚠️ Retention decisions use incomplete artifact indexes.

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/core/services/artifactRetention.ts
**Line:** 13:18
**Comment:**
	*Race Condition: The separate `readIndex` and `writeIndex` operations allow local storage uploads to perform an unsynchronized read-modify-write. Concurrent `put` effects can both read the same index and then overwrite each other, losing one artifact entry. Serialize index updates or provide an atomic append/update operation.

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
👍 | 👎

keyId: string,
bundleId: string,
extensions?: string[],
extensions?: readonly 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.

Suggestion: The cache lookup now accepts extension bundle IDs, but the local credentials provider supplies only the statically configured app.iosExtensions. The build pipeline separately discovers extension targets from the generated Xcode project, so a project with an unconfigured/discovered extension can reuse signing assets without an extensionProfiles entry for that target; the generated export options then omits the extension profile and Xcode export fails. Pass the resolved extension set into credential resolution or make cache reuse validate all discovered targets before returning assets. [api mismatch]

Severity Level: Major ⚠️
- ❌ Local iOS builds can fail during signing export.
- ❌ Discovered extensions lack export profile mappings.
- ⚠️ Bare projects bypass static extension configuration.

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/core/services/localCredentialsStore.ts
**Line:** 38:38
**Comment:**
	*Api Mismatch: The cache lookup now accepts extension bundle IDs, but the local credentials provider supplies only the statically configured `app.iosExtensions`. The build pipeline separately discovers extension targets from the generated Xcode project, so a project with an unconfigured/discovered extension can reuse signing assets without an `extensionProfiles` entry for that target; the generated export options then omits the extension profile and Xcode export fails. Pass the resolved extension set into credential resolution or make cache reuse validate all discovered targets before returning assets.

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
👍 | 👎

@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider

Great, no issues found!

Qodo reviewed your code and found no material issues that require review

Grey Divider

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

Qodo Logo

@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 7 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/services/progress.ts">

<violation number="1" location="src/core/services/progress.ts:138">
P1: reportFailure prints the raw child-process tail directly to the terminal on build failure. Since build logs are only redacted when persisted to disk, any secret-looking values (API keys, bearer tokens, JWTs) present in the last lines of output will be echoed unredacted to the console. Consider redacting tail lines the same way persisted logs are redacted before including them in the failure report.</violation>
</file>

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

Re-trigger cubic

* sometimes precedes the trailing lines.
*/
const reportFailure = (label: string, tail: string[], logFile: string) =>
const reportFailure = (label: string, tail: readonly string[], logFile: 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.

P1: reportFailure prints the raw child-process tail directly to the terminal on build failure. Since build logs are only redacted when persisted to disk, any secret-looking values (API keys, bearer tokens, JWTs) present in the last lines of output will be echoed unredacted to the console. Consider redacting tail lines the same way persisted logs are redacted before including them in the failure report.

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

<comment>reportFailure prints the raw child-process tail directly to the terminal on build failure. Since build logs are only redacted when persisted to disk, any secret-looking values (API keys, bearer tokens, JWTs) present in the last lines of output will be echoed unredacted to the console. Consider redacting tail lines the same way persisted logs are redacted before including them in the failure report.</comment>

<file context>
@@ -135,7 +135,7 @@ const logStamp = (epochMilliseconds: number): string => {
  * sometimes precedes the trailing lines.
  */
-const reportFailure = (label: string, tail: string[], logFile: string) =>
+const reportFailure = (label: string, tail: readonly string[], logFile: string) =>
   Effect.gen(function* () {
     const lines = [
</file context>

@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: 0872f01a-50c7-41e5-b876-467827d0f14c

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.

@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:S This PR changes 10-29 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant