Skip to content

Guard factory SDK cloud provider deletes#244

Merged
kjgbot merged 1 commit into
mainfrom
factory-sdk/d7-deletefile-guard
Jun 12, 2026
Merged

Guard factory SDK cloud provider deletes#244
kjgbot merged 1 commit into
mainfrom
factory-sdk/d7-deletefile-guard

Conversation

@kjgbot

@kjgbot kjgbot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add a guarded MountClient.deleteFile surface and implement it in RelayfileCloudMountClient.
  • Default-deny provider deletes under /linear/** and /slack/**.
  • Allow provider delete only when all D7 checks pass:
    • current content is not URL-bearing and does not have a real provider key,
    • this client has a tracked create/write op for the path,
    • getOp(opId) is terminal failed/dead-lettered/canceled,
    • providerResult.externalId is absent,
    • injected isAllowedDelete(path, currentContent) also approves.
  • Add a structural invariant test so mount.deleteFile callsites cannot appear outside guarded writeback modules.

Tests

  • npx vitest run packages/factory-sdk
  • npx tsc --noEmit -p tsconfig.node.json

Notes

@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@kjgbot, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 5 minutes and 51 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more credits in the billing tab to continue.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 34e94815-bfe3-44bb-91fd-155dc59888c3

📥 Commits

Reviewing files that changed from the base of the PR and between 3693fd0 and a24c484.

📒 Files selected for processing (5)
  • packages/factory-sdk/src/__tests__/mount-delete-callsite-invariant.test.ts
  • packages/factory-sdk/src/mount/relayfile-cloud-mount-client.test.ts
  • packages/factory-sdk/src/mount/relayfile-cloud-mount-client.ts
  • packages/factory-sdk/src/ports/mount.ts
  • packages/factory-sdk/src/testing/fakes.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch factory-sdk/d7-deletefile-guard

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

❤️ Share

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

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request introduces a deleteFile method to the MountClient interface and implements it in RelayfileCloudMountClient and FakeMountClient. It includes safety invariants to restrict provider deletes (e.g., Linear or Slack) to failed, unlinked orphan drafts, backed by a new callsite invariant test and comprehensive unit tests. The review feedback suggests preserving the original write/create operation ID in a separate map (#lastWriteOpByPath) to prevent retried delete operations from overwriting it, ensuring that safety checks can still be verified correctly.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

readonly #tokenProvider: TokenProvider
readonly #baseUrl?: string
readonly #eventClient?: RelayfileEventClient
readonly #isAllowedDelete?: (path: string, currentContent: unknown) => boolean | Promise<boolean>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

To prevent retried delete operations from overwriting the tracked write/create operation ID (which is needed to verify the safety invariants of the original creation), we should introduce a separate map #lastWriteOpByPath to preserve the original write operation ID.

Suggested change
readonly #isAllowedDelete?: (path: string, currentContent: unknown) => boolean | Promise<boolean>
readonly #isAllowedDelete?: (path: string, currentContent: unknown) => boolean | Promise<boolean>
readonly #lastWriteOpByPath = new Map<string, string>()

Comment on lines +149 to +153
this.#lastOpByPath.set(path, (await this.#client.deleteFile({
workspaceId: this.workspaceId,
path,
baseRevision: current.revision,
})).opId)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Before overwriting #lastOpByPath with the new deleteOpId, preserve the original write/create operation ID in #lastWriteOpByPath if it hasn't been saved already.

    const writeOpId = this.#lastOpByPath.get(path)
    if (writeOpId && !this.#lastWriteOpByPath.has(path)) {
      this.#lastWriteOpByPath.set(path, writeOpId)
    }

    this.#lastOpByPath.set(path, (await this.#client.deleteFile({
      workspaceId: this.workspaceId,
      path,
      baseRevision: current.revision,
    })).opId)

Comment on lines +161 to +164
const opId = this.#lastOpByPath.get(path)
if (!opId || !this.#client.getOp) {
throw new Error(`Refusing provider delete for ${path}: create operation is unknown`)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Use the preserved write/create operation ID from #lastWriteOpByPath if available, falling back to #lastOpByPath.

Suggested change
const opId = this.#lastOpByPath.get(path)
if (!opId || !this.#client.getOp) {
throw new Error(`Refusing provider delete for ${path}: create operation is unknown`)
}
const opId = this.#lastWriteOpByPath.get(path) ?? this.#lastOpByPath.get(path)
if (!opId || !this.#client.getOp) {
throw new Error(`Refusing provider delete for ${path}: create operation is unknown`)
}

agent-relay-code Bot added a commit that referenced this pull request Jun 12, 2026
@agent-relay-code

Copy link
Copy Markdown
Contributor

Implemented the PR-scoped fix for the verified review finding: provider delete safety now keeps the original write/create op id separate from the latest op id, so a delete retry does not validate against a prior delete op.

Changes:

Addressed comments

  • gemini-code-assist[bot]: requested a separate preserved write/create op map so delete retries do not overwrite safety verification state; fixed in relayfile-cloud-mount-client.ts.
  • gemini-code-assist[bot]: requested preserving the write/create op before delete overwrites the latest op; fixed by storing write op ids during successful writes in relayfile-cloud-mount-client.ts.
  • gemini-code-assist[bot]: requested provider delete validation use the preserved write/create op id; fixed in relayfile-cloud-mount-client.ts.
  • gemini-code-assist[bot] review summary: same preserved-write-op issue as the inline threads; covered by the implementation above and regression assertions in relayfile-cloud-mount-client.test.ts.
  • coderabbitai[bot]: no actionable code review was provided; the comment only reported a CodeRabbit review rate/credit limit.

Advisory Notes

None.

Local verification run:

  • npm ci
  • npm run verify:mcp-resources-drift
  • npm run lint
  • npm run typecheck:web
  • npm run typecheck:node
  • npm test
  • npx vitest run passed on rerun; first run hit a transient unrelated ChatView.dom.test.ts unhandled async error after all tests passed.
  • npm run build
  • npm run build:web
  • npx playwright test --config playwright.fidelity.config.ts
  • npx playwright test --config playwright.redraw.config.ts

I did not run the macOS-only packaged CI job or verify GitHub mergeability from here.

@agent-relay-code

Copy link
Copy Markdown
Contributor

No open PR-scoped findings remain. I did not need to make new edits; the current checkout already contains the Gemini-requested preserved write-op fix and regression coverage.

Addressed comments

  • gemini-code-assist[bot]: requested a separate preserved write/create op map; already handled in current checkout at packages/factory-sdk/src/mount/relayfile-cloud-mount-client.ts:70.
  • gemini-code-assist[bot]: requested preserving the write/create op before delete retries overwrite latest op tracking; already handled by write tracking at packages/factory-sdk/src/mount/relayfile-cloud-mount-client.ts:136.
  • gemini-code-assist[bot]: requested provider delete validation use the preserved write/create op id; already handled at packages/factory-sdk/src/mount/relayfile-cloud-mount-client.ts:166.
  • gemini-code-assist[bot] review summary: same preserved-write-op issue; covered by the implementation above and regression assertions at packages/factory-sdk/src/mount/relayfile-cloud-mount-client.test.ts:434.
  • coderabbitai[bot]: no actionable code review was provided; the comment only reported a CodeRabbit review rate/credit limit.
  • agent-relay-code[bot]: prior status comment summarizing the same fix; no additional action required.

Advisory Notes

None.

Verification completed:

  • npm ci
  • npm run verify:mcp-resources-drift
  • npm run lint
  • npm run typecheck:web
  • npm run typecheck:node
  • npm test
  • npx vitest run
  • npm run build
  • npx playwright install --with-deps chromium
  • npm run build:web
  • npx playwright test --config playwright.fidelity.config.ts
  • npx playwright test --config playwright.redraw.config.ts

GitHub Actions for head 4120aa66a38ca792b29c73eeef808cfdb117c684 report CI completed successfully for checks, playwright, and packaged-mcp-smoke. GitHub PR metadata reports mergeable: true and mergeable_state: clean.

@kjgbot kjgbot force-pushed the factory-sdk/d7-deletefile-guard branch from 4120aa6 to a24c484 Compare June 12, 2026 10:38
@kjgbot kjgbot merged commit 1f8817b into main Jun 12, 2026
4 checks passed
@kjgbot kjgbot deleted the factory-sdk/d7-deletefile-guard branch June 12, 2026 11:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant