Repository navigation
Description
Lane 0: shared contract
Base: upstream main 51e183a08e0 (fork fast-forwarded 2026-10-31). Paths and lines below are from that base; see "Revision 2026-10-31" at the end.
Goal: make dart a first-class value everywhere the shared layer enumerates providers, make every compile-forced site compile (desktop and mobile), publish the RPC contract and error codes that Lanes A, B, C and D build against, and keep existing profiles, new-profile defaults and mobile goldens unchanged. No behaviour yet.
Delivers: G3 (link model), G5 (mixed versions), G6 (small upstream diff).
Depends on: nothing to start (Phase 0's read-only findings are in #8). Lane P's later findings arrive as additions before this lane merges. Merges first; every other lane starts from its branch.
Files owned (re-check every line number before editing)
New: src/shared/dart/dart-types.ts, dart-issue-url.ts (parses Dart task URLs and validates htmlUrl: https only and host app.dartai.com, so a hostile or malformed URL in an API reply is never stored in a link or opened), dart-capabilities.ts (defines the capability constant dart.provider.v1 only; Lane A adds it to the advertised list in src/shared/task-source-runtime-capabilities.ts when the methods exist, so no server advertises Dart without methods), dart-provider-payload.ts (strips 'dart' from settings payloads for a host lacking the capability; pending open decision D2 in #8: if D2 is (a), skip this file and its test); src/shared/rpc-contract/dart-params.ts (verified: config/scripts/generate-rpc-params-catalog.mjs:38 globs rpc-contract/*.ts flat).
Edit, shared layer:
src/shared/task-providers.ts(union:1,TASK_PROVIDERS:3,isTaskProviderAvailable:89-105dart branch returning true like Jira at:100, a separateDEFAULT_VISIBLE_TASK_PROVIDERSkept at the original four, both fallbacks ofnormalizeVisibleTaskProvidersat:35and:49returning the default list). Other tests pin the four providers too (src/main/persistence/applying-settings/persistence-settings-ui-defaults.test.ts,src/main/persistence/applying-settings/persistence-source-control-ai-migration.test.ts); they must keep passing unchanged.src/shared/default-global-settings.ts:216andsrc/main/runtime/runtime-client-settings.ts:83(both use[...TASK_PROVIDERS]today; switch to the default list). Both files are hot upstream (11 and 12 commits in a week): keep each hunk to one line.src/shared/task-providers.test.tspins the empty fallback to the four providers and must stay unchanged.src/shared/task-provider-identity.ts(untouched upstream since the first draft): the type; all four switches (normalizeTaskProviderIdentityat:49-82,case 'jira'at:75;isStoredTaskProviderIdentityat:96-115,:113;taskProviderIdentityCachePartat:150-160,:158), the fields table at:118-123, andareTaskProviderIdentitiesEqualat:125-143. Missing the arm at:75makes every dartproviderIdentitynormalize to null.workspace-linked-item.tsequality (:18-20) must also comparedartIdentifier.src/shared/task-source-context.ts:239,src/shared/workspace-linked-item.ts:34and:46-61,src/shared/workspace-attachment-normalization.ts:25-102,294,src/shared/workspace-reference-identity.ts,src/shared/workspace-attachments.ts,workspace-attachment-mutation.ts,workspace-linked-item-source-context.ts,src/shared/workspace-name.ts:50,src/shared/new-workspace/workspace-source.ts,smart-workspace-*.ts: no edit, Dart is not a Create-Workspace smart-source mode in v1 (SmartNameModeatsmart-workspace-source-results.ts:21is a literal union; see Dart AI task provider (core extension): parent plan #8 "Out"). The single edit atworkspace-linked-item.ts:34covers RPC validation, IPC validation and persistence, becauseworkspace-linked-item-schema.ts:5-14and every persistence path callnormalizeWorkspaceLinkedItem; do not hunt for more call sites. Identifier-drop sites (a dart identifier is silently lost unless each copiesdartIdentifier):workspace-attachment-mutation.ts:81-94(thestripUndefinedFieldsallowlist,:88),workspace-linked-item-source-context.ts:11-22,workspace-name.tsaround:50-70and:159-210.
4.1.workspace-attachment-normalization.ts, required edits with one test each::36(the non-issue rejection names only linear/jira),:47(number === 0is allowed only for linear/jira with an identifier, so a Dart attachment is dropped),:101-102(the attachment identity uses the identifier only for linear/jira, so Dart keys collapse tonumber),:293-294(toLinkedItemcopies the identifier only for jira). Tests: a Dart attachment withnumber: 0survives; its dedupe key uses the identifier; the linked item carriesdartIdentifier.
4.2.src/shared/new-workspace/workspace-source.ts:81-96(switch point 14): add adartIdentifierarm before thenumber === 0Linear fallback; test the round tripnormalizeWorkspaceAttachment→normalizeWorkspaceLinkedItem→getWorkspaceSourceProvider→buildWorkspaceSourceSelection.
4.3.src/shared/workspace-reference-identity.ts(switch point 15): addapp.dartai.comtoHOST_PROVIDERS(:12-19), a Dart task route (parsehttps://app.dartai.com/t/<id>-<slug>, reusingdart-issue-url.ts), and dart arms in the identifier andexternalUrlswitches (:164-194). Tests: a Dart URL parses to providerdartwith the 12-character id; an unknown Dart route is rejected cleanly.
4.4.workspace-linked-item.ts: test that two Dart links withnumber: 0and differentdartIdentifierare not equal, and that a Dart link survives save and reload.src/shared/worktree/types.ts:24,40and:38-46(linked item, decision 3).src/shared/rpc-contract/client-settings-params.ts:15,109-110: verify only, no edit (TaskProviderParamis derived fromisTaskProvider, so it accepts'dart'once item 1 lands). Edit:src/shared/rpc-contract/automation-params.ts:65-100(dart arm beside the jira arm at:96) and:109(the hand-listedz.enum),src/shared/orcad-migration/orcad-migration-dormant-automation-validation.ts:245(hand-listed provider array).- New 2026-10-31, provider error codes (upstream fix(i18n): localize provider errors and shared/native UI labels stablyai/orca#34680): add this fixed seed set of
dart.*codes —dart.notConnected,dart.connectFailed,dart.unauthorized,dart.loadFailed,dart.taskNotFound,dart.createFailed,dart.updateFailed,dart.commentFailed,dart.badRequest,dart.rateLimited,dart.invalidTaskUrl— plus any extra code Lane P's findings add (Lane P only adds, it never blocks this step), toTaskProviderErrorCodeinsrc/shared/task-provider-failure.ts, following thejira.*entries.src/renderer/src/lib/task-provider-error-copy.tshasCOPY: Record<TaskProviderErrorCode, ...>, so the new codes force copy entries there: add plain English entries withtaskProviders.dart.*translate keys, asCOPYdoes for the other providers, under the compile-forced rule; after Lane 0 merges, the file belongs to Lane B for the final wording (a hand-over in sequence, not shared). No census test:COPYis aRecord<TaskProviderErrorCode, ...>, so a missing entry already failspnpm tc.
Compile-forced rule (the ownership fix): Lane 0 owns EVERY edit the compiler forces after'dart'joinsTaskProvider, in any tree (shared, main, renderer, mobile), provided the edit only adds adartkey, arm or neutral value. Lanes A to D own behaviour. A forced site that needs real behaviour gets a neutral placeholder here and a note in the lane report naming the lane that fills it in. This is what makes Lane 0's typecheck gates passable.
Edit, named exceptions in other trees: src/renderer/src/lib/agent-feature-skill-names.ts(addORCA_DART_SKILL_NAME, so Lane B's install row compiles without waiting for Lane C).src/preload/api-types.ts: declare theDartApitype and thedart: DartApimember ofPreloadApi. Becausesrc/preload/index.ts:197ends with} satisfies PreloadApi, the new member breaks the node typecheck until the preload object has it, so Lane 0 also adds a compile-forced stub: newsrc/preload/api/dart-bridge.tsexporting adartApiwhose every method resolves totaskProviderFailure('dart.notConnected'), wired asdart: dartApiinsrc/preload/index.ts. Both files then pass to Lane A in sequence (same hand-over rule astask-provider-error-copy.ts). The web preload needs nothing (web-preload-api.ts:68returnsPartial<PreloadApi>). Without this item, Lane B cannot type-check before Lane A merges.- Compile-forced sites, expected to include (verify by typechecking; the compiler on the new base is the real list):
10.1. Renderer:components/settings/use-task-source-provider-readiness.ts:17(a literalRecord<TaskProvider, TaskProviderReadiness>),components/settings/task-source-setup-state.ts(Record<TaskProvider, ...>at:103,121,131,143),components/settings/TasksPane.tsxPROVIDER_METAat:37(add a dart entry; the card body is Lane B's),components/task-source-context-summary.ts(jira at:58,71,73,220),components/task-page/use-task-page-repo-selection.ts,components/sidebar/workspace-attachment-source-result.ts(jira:55,56,82,83),lib/linked-work-item-context.ts,components/task-page/task-page-list-chrome-visibility.ts(aswitch (taskSource)at:23-32with four arms and no default; if the compiler does not force it, treat it as a silent switch and add ahasDartDetailarm anyway, with a unit test per provider, otherwise list chrome stays visible over an open Dart detail),components/automations/automation-source-display.ts:59,76,components/automations/automation-target-availability.ts:241(exhaustive switches with no default; give dart a neutral arm),store/slices/ui/ui-slice-contract-core.ts:88-96(theNewWorkspaceDraft.linkedWorkItemprovider union and identifier fields: add'dart'anddartIdentifier).
10.2. Mobile (mobile/srcchanged in about 500 files since the first draft):tasks/mobile-task-view-options.tsx:7,14(PROVIDER_LABELS,PROVIDER_EMPTY_LABELS, stillRecord<TaskProvider, string>), and the oneTaskProviderLogo.tsxcall site that passes aTaskProvider-typed value:tasks/mobile-tasks-create-drawers.tsx:60(itsprovidercomes frommobile-tasks-hydrated-view-state.ts:15). Lane 0 passes a neutral, non-Linear kind value there and writes no filtering or other logic; Lane D's first step replaces it and widens the logo kind. Not forced, so not Lane 0's:components/SmartSourceModeIcon.tsx:8(typed by the literal union inmobile-smart-source-modes.ts:7) andhost-screen/workspace-details-sheet.tsx:176-178(already narrowed byitem.provider === 'linear' || item.provider === 'jira'); Lane D checks them. No longer forced:mobile-tasks-screen-chrome.tsx(REFRESH_LABELSbecame a function returning an object literal) andmobile-tasks-provider-controls.tsx(no longer usesTaskProviderLogo). Deleted upstream (feat(mobile): rebuild Home around what needs you, with actions pinned at the bottom stablyai/orca#36520, Home rebuilt 2026-10-31):mobile/src/home/MobileHomeTasksCard.tsxandmobile-home-host-requests.ts.
10.3. The test that pins the old four providers in a compile-forced file (src/renderer/src/components/settings/task-source-setup-state.test.ts) is updated only where the compile-forced change requires it. - Classify in the lane report (may carry a Jira-only arm; decide dart arm or "not needed"):
src/shared/jira/jira-review-title.ts(readsjiraIdentifier),components/task-page/use-task-page-provider-metadata.ts(has jira branches),components/task-page/use-task-page-source-summary.ts(aPartial<Record<TaskProvider, ...>>at:38-40, so not compile-forced). Excluded as out of scope (Cmd-J), recorded as "excluded, documented", not as compile-forced edits:components/cmd-j/task-search-requests.ts(its provider loop is a literal['github', 'gitlab'] as constat:56; Cmd-J will not search Dart in v1),worktree-palette-task-url-match.ts,cmd-j-evaluation-fixture.ts. - New (OMP review), the Dart identity field list: write it in
dart-types.tsbefore step 4, and cite it from Lanes A, B and D. Minimum: the signed-in user id fromGET /meplus whatever workspace scope Lane P finds; define thetaskProviderIdentityCachePartformat from those fields. Without one written list, Lanes A and B build different identities andareTaskProviderIdentitiesEqualsilently returns false (duplicate attachments, cache misses).
Must not touch
Main client, RPC registration, the advertised capability list in task-source-runtime-capabilities.ts (Lane A's one shared-file exception) and the generated params catalog (Lane A), UI and mobile behaviour beyond the compile-forced rule, CLI. Do not extend ProjectProviderIdentity. Do not add linkedDartIssue* fields. integration-credential-errors.ts is not needed: Lane A uses createEncryptedApiKeyFileStore, which has its own label.
Decisions (write them in the lane report)
- Default visibility: dart is opt-in from Settings > Task Sources; defaults stay the original four everywhere (items 1 and 2). No migration, no edit to
src/main/persistence/loading-store/prepare-loaded-profile-settings.ts. The mobile goldens contain the literal["github","linear"]and are unaffected; no regeneration. Verify by running the mobile golden tests. Note:TasksPane.tsxandgetStalledVisibleTaskProviders(TASK_PROVIDERS, ...)iterate allTASK_PROVIDERS, so a dart card shows in Task Sources while not visible by default. That is intended; Lane B adds a UI test for it. - Protocol: adding methods and an optional capability needs no
RUNTIME_PROTOCOL_VERSIONbump. Verified on the new base: version is still 3, minimum client and server still 2, andsrc/shared/protocol-version.ts:41-43still says new methods and optional fields are additive. - Linked item:
WorkspaceLinkedItemrequires a numericnumber; Dart ids are 12-character strings. Follow Jira:number: 0plus optionaldartIdentifieron the linked-item and attachment types, copied atworkspace-linked-item.ts:46-61and compared in equality. - Availability:
isTaskProviderAvailable('dart')is true (connect happens from the Tasks surface, like Jira). Mobile hides dart until Lane D ships reads (Lane D owns that filter). - Errors: Dart uses the shared
TaskProviderErrorCodeconvention (item 7), not free-text error strings, so its messages are localized like Jira's and Linear's.
Steps
- Add
'dart'; runpnpm tc(node, cli, web) and the mobile typecheck (typecheckandtypecheck:testsinmobile/package.json;pnpm tcdoes not cover mobile); fix compile-forced sites per item 10. Record the real compiler list in the lane report and compare it with item 10; every difference is a finding. - Fix the shared-layer silent switch points from the parent plan (items 1 to 6, 8, 11, 14 and 15; items 7, 9, 10, 12, 13, 16 and 17 belong to Lanes A and B, except item 12's
PROVIDER_METAentry, which is compile-forced and yours). - Define the dart identity (Dart ids are strings, from Lane P) with normalize, equality and cache-part arms.
- Write the RPC contract in
dart-params.ts(method-name constants and param and reply schemas):dart.status|connect|disconnect|testConnection|config|listTasks|getTask|listComments|createTask|updateTask|addComment, and the rules from Lane P (below). Failures are returned astaskProviderFailureresults with thedart.*codes from item 7. State that no layer may logdart.connectparams. State a permission per method, following Jira (src/main/runtime/rpc/methods/jira.ts:71-90):dart.connect,dart.disconnectanddart.testConnectionareaccounts-admin; reads and writes areworkspace. Otherwise a paired phone with workspace access could change the credential. "Mine" has one source:dart.statusanddart.connectreturn the signed-in user's id and name (fromGET /me), andlistTaskstakes an optionalassigneeId; Lane B and Lane C both use that. Thedartboardfilter is unconfirmed (it returned 0 for every value tried): the contract says the host falls back to filtering on the returneddartboardfield if Lane P does not confirm the server filter. Write methods (createTask,updateTask,addComment) are marked "unverified against the live API" until Lane P's write checks have run. - Add the error codes (item 7). Only if D2 is (b): add the stripping helper with its unit test.
Do not regenerate the RPC params catalog: it reads the host registry, which Lane A creates. Do runpnpm verify:rpc-params-catalog(the generator globsdart-params.ts); if it drifts, stop and report, because the regenerated file belongs to Lane A.
Rules the contract must encode (from Phase 0, live API)
- Never send
no_defaults=false(workspace defaults would hide tasks); always send explicit filters. - Unknown query parameters are silently ignored by Dart, so the client validates filter names against a whitelist.
- Page size: default 100, maximum 250 (larger values are silently capped); totals come from
count. - Error bodies are not always JSON (unknown task id returns an HTML 404).
Done when
pnpm tcclean for node, cli and web (every compile-forced site fixed here under the rule above); both mobile checks clean:typecheckandtypecheck:testsinmobile/package.json(pnpm tcdoes not cover mobile).- Tests prove a dart task source and a dart link (with
dartIdentifier) survive normalize, save and reload, and a dart identity validates in the RPC params. - Only if D2 is (b): a test proves the stripping helper removes
'dart'for a host without the capability. pnpm verify:rpc-params-catalogpasses unchanged.- The preload stub (item 9) is in place and
pnpm tcpasses withdart: DartApirequired inPreloadApi. - Default and fallback visible-provider tests still pass unchanged; mobile golden tests pass unchanged.
oxlintandpnpm run check:code-quality:changedclean; the contract is readable on its own by the other lane reviewers.- The lane report lists the real compile-forced sites and the item 11 classifications.
- Tests for items 4.1 to 4.4 pass; a pasted Dart URL parses.
Revision 2026-10-31 (c): Fable 5.1 final review applied
- Item 9:
satisfies PreloadApi(src/preload/index.ts:197) means declaringdartbreaks Lane 0's own typecheck; Lane 0 now adds a compile-forced preload stub (src/preload/api/dart-bridge.tsplus thedart:line), handed to Lane A in sequence. - Item 10.2: only
mobile-tasks-create-drawers.tsx:60is compile-forced; the other two logo call sites move to Lane D as checks. - Item 6:
client-settings-params.tsis verify-only (derived fromisTaskProvider). - Step 5 and Done-when: the stripping helper is conditional on D2 (b).
- Item 7: census test dropped (the
Recordtype already enforces it). - Step 5: run
pnpm verify:rpc-params-catalog. - Item 4: Dart is not a Create-Workspace smart mode in v1; one note that the normalizer edit covers every validation and persistence path.
Revision 2026-10-31 (b): OMP review applied
- Items 4.1 to 4.4: exact normalization lines, the workspace-source Linear fallback (switch point 14), Dart URL parsing (switch point 15) and link-equality tests.
- Item 7: a fixed seed set of eleven
dart.*codes (Lane P only adds); sequential hand-over of the copy file to Lane B; census test. - Item 10.1:
task-search-requests.tsmoved to "excluded" anduse-task-page-source-summary.tsto "classify" (neither is compile-forced); list-chrome switch handled even if not forced. - Item 10.2: neutral placeholders only, no logic at mobile call sites.
- New item 12: one written Dart identity field list.
- Step 4: a permission per method (
accounts-adminfor connect, disconnect, test). - Rejected (stays as written): adding
DEFAULT_VISIBLE_TASK_PROVIDERSattask-providers.ts:27and:81. ThoseTASK_PROVIDERS.filtercalls only keep providers already on the user's list, so they cannot add Dart.
Revision 2026-10-31
- Re-based on
51e183a08e0; full paths and new line numbers throughout (task-providers.ts:35,49,89-105,runtime-client-settings.ts:83,automation-params.ts:96,109, dormant validation:245,workspace-name.ts:50,ui-slice-contract-core.ts:88-96, automation display:59,76and availability:241). - Added item 7 (provider error codes, fix(i18n): localize provider errors and shared/native UI labels stablyai/orca#34680) and decision 5.
- Added
TasksPane.tsxPROVIDER_META(switch point 12) to the compile-forced list. - Mobile compile-forced list rewritten: Home files deleted upstream,
REFRESH_LABELSandmobile-tasks-provider-controls.tsxno longer forced, newTaskProviderLogocall sites. - Removed the instruction about
protocol-version-published-capabilities.test.ts(deleted upstream, test: remove low-value capability, copy, and source inventories stablyai/orca#36114). task-source-runtime-capabilities.tsis undersrc/shared/; it is Lane A's one named shared-file exception (contradiction resolved).- Added item 11 (files to classify) and the Task Sources UI note in decision 1.
Activity