feat(006/1): PM provider manifest + conformance harness (dormant infra)#1125
Merged
zbigniewsobiecki merged 2 commits intodevfrom Apr 16, 2026
Merged
feat(006/1): PM provider manifest + conformance harness (dormant infra)#1125zbigniewsobiecki merged 2 commits intodevfrom
zbigniewsobiecki merged 2 commits intodevfrom
Conversation
…arness (dormant)
Landed the dormant manifest infrastructure that spec 006 is built on:
- PMProviderManifest interface + sub-types (src/integrations/pm/manifest.ts)
One declarative contract per provider: id, label, category, credential
roles, webhook route + verifier, payload parser, router adapter,
extractProjectIdFromJob hook, PMIntegration, trigger handlers, platform
client factory, optional isSelfAuthoredHook + createLabel.
- pmProviderRegistry (src/integrations/pm/registry.ts)
Process-singleton registry; registerPMProvider enforces unique ids so
forgotten renames surface as runtime errors at startup.
- Shared helpers in src/integrations/pm/_shared/:
- auth-headers.ts — linearAuthHeader (bare key), githubAuthHeader
(Bearer + Accept + api-version), jiraAuthHeader (Basic). Guards
against the Bearer-prefix regression fixed in PR #1119.
- webhook-verifier.ts — makeHmacSha256Verifier factory; header-prefix
tolerant, opt-out semantics when secret is null.
- label-id-resolver.ts — UUID-validating label resolver, encapsulates
the check currently duplicated in src/pm/linear/adapter.ts.
- project-id-extractor.ts — extractProjectIdFromJobViaRegistry iterates
the manifest registry; wired into src/router/worker-env.ts as a
first-check fallback so the legacy per-provider branches still fire
for Trello/JIRA/Linear until plans 006/2-006/4 migrate them.
- Conformance harness (tests/unit/integrations/pm-conformance.test.ts)
Iterates listPMProviders() and asserts 11 contract invariants per
manifest. Exercised against TestProvider fixture (tests/helpers/
testPMProvider.ts) in this PR; real providers join in plans 006/2-4.
- pm.discovery tRPC router (src/api/routers/pm-discovery.ts)
Registry-driven listProviders + providerCredentialRoles endpoints.
Lives alongside the legacy integrationsDiscovery router during the
migration window. Mounted at pm.discovery.* in appRouter.
- Frontend provider-wizard registry + generic step renderer
(web/src/components/projects/pm-providers/)
Parallel frontend registry keyed by the same id as the backend
manifest. renderManifestStep helper wired into pm-wizard.tsx ahead of
the legacy per-provider branches; falls back when no wizard is
registered. In this PR, zero providers are registered, so the wizard
behavior is byte-for-byte identical to main.
- src/integrations/README.md — full rewrite as the manifest-first
author's guide with a transitional note that Trello/JIRA/Linear are
migrating in plans 006/2-4. Legacy section kept at the bottom.
- CLAUDE.md — integration abstraction pointer updated to reflect the
hybrid state (manifest for PM, legacy IntegrationModule for SCM +
alerting).
Tests: 42 new (7687 pre-existing + 42 = 7729 total, all green).
Build, lint, typecheck all pass.
No operator-visible changes. Trello/JIRA/Linear continue to register
through bootstrap.ts and builtins.ts; the new registry is consulted
first and returns empty, so legacy paths handle every real request.
Plan: docs/plans/006-pm-integration-plug-and-play/1-infrastructure.md.done
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Plan 006/1 of spec 006: Refactor PM integration layer for plug-and-play extensibility.
Lands the dormant infrastructure that the PM integration refactor rests on. Zero operator-visible change. Trello, JIRA, and Linear continue to register through the legacy path; the new manifest registry is consulted first on every hot path (
extractProjectIdFromJob, wizard step render) and falls through cleanly because no real provider is registered yet.Follow-up PRs (plans 006/2–006/4) migrate each provider onto the manifest; plan 006/5 deletes the legacy infrastructure once all three are migrated.
What's landed
Contract
src/integrations/pm/manifest.ts—PMProviderManifestinterface with 11 required surfaces: id, label, category, credentialRoles, webhookRoute, verifyWebhookSignature, parseWebhookPayload, routerAdapter, extractProjectIdFromJob, pmIntegration, triggerHandlers, platformClientFactory. OptionalisSelfAuthoredHook+createLabel.src/integrations/pm/registry.ts—pmProviderRegistrysingleton withregisterPMProvider,getPMProvider,listPMProviders. Duplicate-id registration throws.Shared helpers (
src/integrations/pm/_shared/)auth-headers.ts—linearAuthHeader(bare key — no Bearer prefix),githubAuthHeader(Bearer + Accept + api-version),jiraAuthHeader(Basic email:token). Guards against theBearer-prefix regression fixed in fix(linear): send personal API keys without Bearer prefix #1119.webhook-verifier.ts—makeHmacSha256Verifier({ headerName, headerPrefix? })factory. Opt-out semantics when secret is null; header-prefix tolerant (sha256=-style).label-id-resolver.ts—resolveLabelId(slot, mapping, ctx)returns a UUID or null (with a warn log). Encapsulates the check currently duplicated insrc/pm/linear/adapter.ts.project-id-extractor.ts—extractProjectIdFromJobViaRegistryiterates the registry. Wired intosrc/router/worker-env.ts::extractProjectIdFromJobas a first-check fallback; in this PR the registry is empty so the existing legacy branches fire unchanged.Conformance harness
tests/unit/integrations/pm-conformance.test.ts— iterateslistPMProviders()and asserts 11 contract invariants per manifest (id format, category literal, webhookRoute convention, adapter type, credential-role hygiene, extractProjectIdFromJob isolation + happy-path, trigger-handler unique names, platform-client shape, parseWebhookPayload robustness).tests/helpers/testPMProvider.ts—TestProviderfixture that exercises every contract surface. Kept post-migration as a reference implementation for future provider authors.tRPC
src/api/routers/pm-discovery.ts— newpm.discoveryrouter withlistProvidersandproviderCredentialRoles. Registered atpm.discovery.*inappRouter. Lives alongside the legacyintegrationsDiscoveryrouter during the migration window.Frontend
web/src/components/projects/pm-providers/types.ts—ProviderWizardDefinition+ProviderWizardSteptypes.web/src/components/projects/pm-providers/registry.ts— frontend registry keyed by the sameidas the backend manifest.web/src/components/projects/pm-providers/render.ts—renderManifestStep(providerId, stepIndex, state, dispatch)helper.web/src/components/projects/pm-wizard.tsx— helper wired into all three per-provider render sites (credentials/board/fields). Falls through to legacy branches when no wizard is registered.Docs
src/integrations/README.md— full rewrite. Manifest-first author's guide with a transitional note. Legacy section retained at the bottom until plan 006/5.CLAUDE.md— Integration abstraction pointer updated to describe the hybrid state.CHANGELOG.md— Internal: infrastructure entry.Tests
Lint, typecheck, build all clean.
Byte-for-byte parity verification
src/router/worker-env.ts::extractProjectIdFromJob— registry returns empty → existing legacy branches fire identically. All 48 container-manager tests pass unchanged.web/src/components/projects/pm-wizard.tsx—renderManifestStepreturns null for unregistered providers → legacy render branches fire identically. All 296 web tests pass unchanged (including the 11 Linear field-mapping SSR tests, 6 Linear team step tests, etc.).Out of scope
Migrating Trello (plan 006/2), JIRA (006/3), Linear (006/4), and deleting the legacy infrastructure (006/5). SCM/alerting also explicitly deferred per spec.
Test plan
npm run build,npm run lint,npm run typecheck,npm testall green🤖 Generated with Claude Code