Skip to content

feat(006/1): PM provider manifest + conformance harness (dormant infra)#1125

Merged
zbigniewsobiecki merged 2 commits intodevfrom
feat/006-infrastructure
Apr 16, 2026
Merged

feat(006/1): PM provider manifest + conformance harness (dormant infra)#1125
zbigniewsobiecki merged 2 commits intodevfrom
feat/006-infrastructure

Conversation

@zbigniewsobiecki
Copy link
Copy Markdown
Member

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.tsPMProviderManifest interface with 11 required surfaces: id, label, category, credentialRoles, webhookRoute, verifyWebhookSignature, parseWebhookPayload, routerAdapter, extractProjectIdFromJob, pmIntegration, triggerHandlers, platformClientFactory. Optional isSelfAuthoredHook + createLabel.
  • src/integrations/pm/registry.tspmProviderRegistry singleton with registerPMProvider, getPMProvider, listPMProviders. Duplicate-id registration throws.

Shared helpers (src/integrations/pm/_shared/)

  • auth-headers.tslinearAuthHeader (bare key — no Bearer prefix), githubAuthHeader (Bearer + Accept + api-version), jiraAuthHeader (Basic email:token). Guards against the Bearer-prefix regression fixed in fix(linear): send personal API keys without Bearer prefix #1119.
  • webhook-verifier.tsmakeHmacSha256Verifier({ headerName, headerPrefix? }) factory. Opt-out semantics when secret is null; header-prefix tolerant (sha256=-style).
  • label-id-resolver.tsresolveLabelId(slot, mapping, ctx) returns a UUID or null (with a warn log). Encapsulates the check currently duplicated in src/pm/linear/adapter.ts.
  • project-id-extractor.tsextractProjectIdFromJobViaRegistry iterates the registry. Wired into src/router/worker-env.ts::extractProjectIdFromJob as 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 — iterates listPMProviders() 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.tsTestProvider fixture that exercises every contract surface. Kept post-migration as a reference implementation for future provider authors.

tRPC

  • src/api/routers/pm-discovery.ts — new pm.discovery router with listProviders and providerCredentialRoles. Registered at pm.discovery.* in appRouter. Lives alongside the legacy integrationsDiscovery router during the migration window.

Frontend

  • web/src/components/projects/pm-providers/types.tsProviderWizardDefinition + ProviderWizardStep types.
  • web/src/components/projects/pm-providers/registry.ts — frontend registry keyed by the same id as the backend manifest.
  • web/src/components/projects/pm-providers/render.tsrenderManifestStep(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

Category Count Notes
New unit tests (plan 006/1) 42 contract types (4), registry (5), auth-headers (3), webhook-verifier (5), label-id-resolver (4), project-id-extractor (3), pm-discovery (3), conformance harness (11), frontend registry (2), generic renderer (2)
Pre-existing tests 7687 all pass unchanged
Total 7729 / 7729

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.tsxrenderManifestStep returns 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

  • Local: npm run build, npm run lint, npm run typecheck, npm test all green
  • 42 new tests exercise every contract surface + the shared helpers
  • Existing provider tests untouched and green
  • Post-merge: no operator-facing change to verify — this PR is dormant by design. Next PR (006/2) migrates Trello and exercises the contract end-to-end.

🤖 Generated with Claude Code

zbigniewsobiecki and others added 2 commits April 16, 2026 08:01
…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
Copy link
Copy Markdown

codecov Bot commented Apr 16, 2026

Codecov Report

❌ Patch coverage is 95.90164% with 5 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/integrations/pm/_shared/webhook-verifier.ts 88.88% 2 Missing and 1 partial ⚠️
src/api/routers/pm-discovery.ts 95.00% 1 Missing ⚠️
src/router/worker-env.ts 66.66% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@zbigniewsobiecki zbigniewsobiecki merged commit e7cc117 into dev Apr 16, 2026
9 checks passed
@zbigniewsobiecki zbigniewsobiecki deleted the feat/006-infrastructure branch April 16, 2026 08:26
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