Files
orca/docs/reference/task-provider-identity-validation.md
Jinwoo Hong c853e10e0c fix(rpc): validate provider-specific fields in TaskProviderIdentity (#20284)
* fix(rpc): validate task provider identity fields

Validate provider-specific field types while preserving nullable scopes and unknown identity fields. Record the producer census and pin validation with regression tests.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* fix(rpc): reject a blank GitHub owner or repo

normalizeTaskProviderIdentity treats a blank owner or repo as no identity at
all, but the schema accepted '' and whitespace-only, so the two disagreed about
the same payload. Refined rather than trimmed: trimming would rewrite the
parsed value and change what the handler receives.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* docs(rpc): re-measure the identity evidence counts

The blank-field commit added seven tests, so the recorded 74/16/58 described the
commit before it. Re-ran both: 81 tests, and the discriminant-only mutation now
gives 17 failures / 64 passes.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* docs(rpc): correct the remaining stale gate count

The blank-field commit moved the full-RPC total too; 2,463 was the count before
it. Re-ran: 278 files, 2,470 passed, one skipped.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
2026-09-13 19:05:42 -04:00

8.3 KiB

Task provider identity RPC validation

The automation RPC identity schema follows src/shared/task-provider-identity.ts, re-exported by src/shared/task-source-context.ts. Only GitHub requires fields beyond provider: owner and repo are strings, and host is optional. GitLab, Linear, and Jira fields are optional nullable strings. Requiring a GitLab project or a Linear workspace/Jira site would contradict the domain type and account-wide scopes.

The discriminated union validates these existing field types without trimming, coercing, or stripping identity fields. Unknown fields pass through as they did under z.custom, including fields from newer clients. Absent and explicit-null identities remain distinct. The schema does not infer providers from owner/repo or require a git worktree, repository slug, or local execution host for a source context.

Producer census

Paths below are relative to the repository root. Searches covered production providerIdentity, TaskProviderIdentity, sourceContext, and linkedTaskSourceContext uses across desktop, shared code, mobile, and CLI.

Producer or forwarding path Populated verdict
src/shared/project-host-setup-projection.ts: getProjectProviderIdentity GitHub owner/repo populated together, or no identity. Supplies project identities consumed by desktop and migration.
src/renderer/src/components/task-page-source-context.tsx: getTaskPageRepoSourceContext GitHub fields populated through projection, or null. GitLab uses explicit provider and buildGitLabProviderIdentity; projectId/project/webUrl populated, namespace can be null.
Same file: buildGitLabProviderIdentity GitLab fields come from project path/host; missing path components become null. No required GitLab field is invented.
src/renderer/src/hooks/composer-state/source-context-state.ts Derived GitHub context carries a complete project identity or null. Jira folder/project-group context explicitly has null identity. Draft/linked contexts are forwarded.
src/renderer/src/components/use-task-page-source-availability.ts Linear workspaceId/name and Jira siteId/URL can be null for account-wide selections; team/project fields are not populated. Valid under the existing optional-field contract.
src/renderer/src/components/task-page-jira-item-source-context.ts Bound issue context populates siteId, siteUrl, projectKey.
src/renderer/src/components/new-workspace/use-jira-url-source.ts Bound URL issue context populates siteId, siteUrl, projectKey.
src/renderer/src/components/worktree-jump-palette-create-worktree.ts Linear teamId/key populated; workspaceId/name may be null.
src/shared/task-source-context.ts: normalize/build functions GitHub missing owner/repo becomes null identity; other providers' missing fields become null. Provider mismatch becomes null, never an inferred provider.
src/cli/handlers/automation-handler-flags.ts, src/cli/handlers/automations.ts Explicit JSON source-context input is normalized before create/update. GitHub fields populated or null identity; other fields nullable. Omitted/null context preserved by flag handling.
src/main/persistence/scheduling-automations/automation-context-migration.ts Builds source context from projected complete GitHub identity, or null context.
Desktop automation save/scoped-list/host clients and web transport Forward existing source contexts, not new identity constructors. automation-orca-save.ts forwards the current automation context or null. Legacy arbitrary malformed RPC input is deliberately rejected by the new schema.
Mobile No task-provider identity/source-context constructor or sender found. mobile/src/components/new-workspace-project-targets.ts uses project identity solely for display.

Compatibility evidence and limits

Read docs/reference/remote-wire-compatibility.md before changing validation. A source search against release tag v1.4.199 also finds no mobile sourceContext/linkedTaskSourceContext sender; its sole providerIdentity use is the display-only project target above. The released CLI flag reader also calls normalizeTaskSourceContext. This is source-level evidence for the checked release, not a claim to have executed every historical mobile binary.

No shipped mobile producer with a newly rejected payload was found. No new required field was added to the domain contract. GitLab, Linear, and Jira discriminant-only identities remain valid. Folder-workspace null/absent identities remain valid on both local and SSH hosts. No execution/status logic or client-side parsing changed.

Regression evidence

src/main/runtime/rpc/methods/task-provider-identity.test.ts checks unchanged valid identities for all four providers, required GitHub fields, every declared field's type, optional/null non-GitHub fields, unknown-field preservation, explicit GitLab discrimination with owner/repo present, local/SSH folder contexts, and update patches.

The focused run passed 81 tests. Temporarily replacing GitHub's field validators with optional z.unknown() validators (discriminant-only acceptance) caused 17 failures and 64 passes. The mutation was restored before running the gates.

Counts re-measured at cf4f77f after the blank-field commit added seven tests; the earlier 74/16/58 figures described the commit before it.

Gate results

All commands ran with ORCA_BACKGROUND_LAUNCH=1.

  • pnpm tc: exit 0; completed the repository typecheck runner.
  • pnpm exec vitest run src/main/runtime/rpc: exit 1; 277 files passed, one failed; 2,462 tests passed, one failed, one skipped. The only failure was the unrelated structured-agent-session-adoption-replay.test.ts hitting its 5,000 ms timeout. All identity tests passed.
  • pnpm --dir mobile typecheck: exit 0; tsc --noEmit passed.
  • pnpm run check:code-quality:changed: exit 0; zero new code-quality, type-aware, or React Doctor findings across the two changed code files.
  • Isolated retry of structured-agent-session-adoption-replay.test.ts: exit 0; one test passed, with the test body completing in 358 ms.
  • Full RPC retry with pnpm exec vitest run src/main/runtime/rpc --maxWorkers=4: exit 0; all 278 files passed, 2,470 tests passed, one skipped (97.24 seconds). The bounded-concurrency rerun resolved the timeout without changing test code.