forcedotcom / forcedotcom/apex-language-support
Use Org Metadata Catalog for find-missing-artifact org lookups - W-23971749
- Dominant language
- TypeScript
- Stars
- 11
- Forks
- 3
- Avg merge
- 1d 13h
- Merged PRs (30d)
- 36
Description
## Summary
Canonical work item: [W-23971749](https://gus.lightning.force.com/a07EE00002ifA17YAE)
Migrate org-related `findMissingArtifact` lookups in `packages/apex-lsp-vscode-extension` to the production Org Metadata Catalog exposed by `@salesforce/vscode-services`.
The immediate goal is to stop the Apex language-server client from owning duplicate org discovery, Tooling queries, and remote Apex source storage where the released catalog can provide those responsibilities. The migration must preserve the existing parser-owned candidate selection, missing-artifact wire protocol, workspace-first behavior, org-switch isolation, and graceful degradation.
## Production API baseline
- `@salesforce/vscode-services@67.13.3` is the first production release whose `SalesforceVSCodeServicesApi.services` exposes `OrgMetadataCatalog`, its dependency in `prebuiltServicesDependencies`, `OrgMetadataCatalogChangePubSub`, and `ORG_METADATA_SCHEME`.
- The released catalog surface is currently:
- `getChildren(reference?, options?)`
- `getEntries(references, options?)`
- `resolveComponents(references)`
- The proposed `findComponents(...)` and `describeSObjects(...)` operations are **not** present in `67.13.3`.
- Upstream implementation: https://github.com/forcedotcom/salesforcedx-vscode/pull/7918
- Production release: https://github.com/forcedotcom/salesforcedx-vscode/releases/tag/v67.13.3
- Upstream design for the missing finder/describe APIs: https://github.com/forcedotcom/salesforcedx-vscode/blob/v67.13.3/docs/superpowers/plans/2026-08-14-org-metadata-catalog-finder-api.md
## Current Apex LS client behavior
`handleFindMissingArtifact` is workspace-first and currently composes multiple Salesforce Services APIs:
| Artifact | Workspace lookup | Org lookup | Result |
| --- | --- | --- | --- |
| Apex class | `ComponentSetService` | Direct Tooling SOQL through `ConnectionService` | Client-owned `apex-org-artifact:` document |
| Apex trigger | `ComponentSetService` | Direct Tooling SOQL through `ConnectionService` | Client-owned `apex-org-artifact:` document |
| sObject | `ComponentSetService` plus local metadata adaptation | `MetadataDescribeService.describeCustomObject` | Structured missing-artifact payload |
Relevant implementation:
- `src/missing-artifact-handler.ts`
- `src/services/org-artifact-adapter.ts`
- `src/services/org-artifact-fs.ts`
- `src/services/workspace-component-set-adapter.ts`
- `src/sobjects/org-sobject-adapter.ts`
## Recommended delivery approach
Use a phased hybrid migration.
### Phase 1: released catalog path for Apex classes and triggers
1. Upgrade the development API dependency and establish the minimum compatible Salesforce Services extension version containing the catalog.
2. Add a narrow client adapter over `api.services.OrgMetadataCatalog` and provide effects with `prebuiltServicesDependencies`.
3. Translate parser-derived exact candidates to `{ type: 'ApexClass' | 'ApexTrigger', fullName }` references.
4. Batch exact candidates through `OrgMetadataCatalog.getEntries(...)`.
5. Preserve candidate order and select the first matching component. Prefer `workspaceUri` when the entry is in the workspace; otherwise open its catalog `documentUri`.
6. Synchronize `sf-org-metadata:` Apex class and trigger documents with the language server.
7. On the catalog path, remove direct client Tooling queries and stop materializing Apex class/trigger source in `OrgArtifactFileSystem`.
8. Retain an explicitly transitional capability/version fallback for older Salesforce Services releases. Record whether catalog or legacy resolution handled the request.
### Phase 2: catalog-owned sObject descriptions
Continue using `MetadataDescribeService.describeCustomObject` until Salesforce Services exposes a catalog-owned, batch-oriented description operation such as `describeSObjects(names)`.
When available:
1. Switch org sObject lookup to the catalog operation.
2. Keep Apex-specific adaptation, definition targets, and wire-size enforcement in this repository.
3. Remove the direct public `MetadataDescribeService` dependency from the supported path.
## Options considered
### A. Use only the released `67.13.3` catalog now
- Use `getEntries` and catalog documents for Apex classes/triggers.
- Keep direct describe for sObjects.
- Fastest way to remove duplicate source discovery/storage, but it is intentionally partial.
- `getEntries` may acquire a complete metadata-type inventory and does not encapsulate workspace-first document selection as a dedicated finder contract would.
### B. Wait for `findComponents` and `describeSObjects`
- Produces the cleanest one-step migration and clearer miss/error semantics.
- Delays consuming a production catalog that already handles Apex source inventory, workspace correlation, stable documents, caching, persistence, and org isolation.
### C. Phased hybrid migration — recommended
- Delivers value from the production API now.
- Keeps the compatibility boundary narrow.
- Avoids inventing a client-side sObject catalog API that Services does not yet expose.
- Leaves a direct path to the upstream finder/describe contracts when they are published.
## Acceptance criteria
- [ ] The client detects catalog capability and uses it when available.
- [ ] Exact Apex class and trigger candidates come only from parser/symbol-derived missing-artifact data; no raw-source semantic heuristics are introduced.
- [ ] Workspace-only, org-only, and both-present Apex classes and triggers resolve with deterministic workspace-first behavior.
- [ ] Org-only source opens through `sf-org-metadata:` and synchronizes with the language server.
- [ ] Namespaced and unqualified Apex class/trigger candidates retain existing semantic ordering.
- [ ] Missing, protected, authorization-failed, no-active-org, and request-failed outcomes preserve current suppression/degradation behavior.
- [ ] Active-org changes cannot commit stale results or leak documents from the previous org.
- [ ] Concurrent and duplicate requests remain coalesced/bounded.
- [ ] Direct Tooling SOQL and client-owned Apex/trigger VFS storage are not used on the catalog path.
- [ ] Existing sObject semantic payloads and definition navigation remain unchanged while the temporary describe path remains.
- [ ] Telemetry distinguishes catalog versus legacy resolution without recording org, namespace, component, object, or field names.
- [ ] Unit/integration coverage includes catalog capability fallback and the existing missing-artifact scenarios.
- [ ] The minimum supported `salesforcedx-vscode-services` version and fallback removal condition are documented.
## Upstream/package prerequisite to verify
The published `@salesforce/vscode-services@67.13.3` tarball contains only 11 files, and `out/index.d.ts` re-exports `SalesforceVSCodeServicesApi` from `../../salesforcedx-vscode-services/out/src/index`, which is not included in that tarball. Before implementation, verify that a clean external consumer can resolve the catalog types. If not, open/fix the upstream packaging issue or temporarily maintain the existing narrow local interface without importing unpublished internals.
## Non-goals
- Changing the language-server missing-artifact wire protocol.
- Moving Apex-specific candidate ordering or semantic adaptation into Salesforce Services.
- Writing automatically resolved org source into the user workspace.
- Permanently maintaining two discovery/storage implementations.
Contributor guide
Research direction
Start at handleFindMissingArtifact in src/missing-artifact-handler.ts, then read the org and workspace adapters listed in the issue. First verify that @salesforce/vscode-services@67.13.3 exposes usable catalog types to an external consumer. Done means catalog-backed Apex class and trigger resolution preserves workspace-first behavior, fallback and degradation semantics, org isolation, telemetry, and existing sObject behavior, with unit and integration coverage.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- backend-api-design, devtools
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 28/100