spec 009: PM integration hardening — make the next provider boring#1143
Merged
zbigniewsobiecki merged 18 commits intodevfrom Apr 18, 2026
Merged
spec 009: PM integration hardening — make the next provider boring#1143zbigniewsobiecki merged 18 commits intodevfrom
zbigniewsobiecki merged 18 commits intodevfrom
Conversation
Spec 009 turns the PMProviderManifest contract from wiring-only to behavioral. Decomposes into 5 plans: infra + 3 provider migrations + cleanup, mirroring spec 006's proven shape. Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
Task 1: Branded StateId, LabelId, ContainerId + parsers + InvalidIdError.
Task 2: Extend PMProvider with optional discover? method + associated
DiscoveryCapability / DiscoveryArgs / DiscoveryResult machinery.
Task 3: Extend PMProviderManifest with optional configSchema,
discoveryCapabilities, wizardSpec, and lifecycle fields +
validateManifestAgainstSchema helper.
Plan divergence note: plan 1's claim that switching the PMProvider method
signatures from string to branded types was non-breaking is incorrect —
branded types are NOT assignable from plain strings. Resolved by keeping
PMProvider method signatures as string at the interface level and
deferring per-adapter narrowing to plans 2/3/4, which matches both plan 1's
"dormant" intent and plans 2/3/4's per-adapter adoption ACs.
All new fields are optional → existing manifests compile unchanged. 44
existing pm-conformance tests still pass.
Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
Task 4: src/integrations/entrypoint.ts centralises every PM/SCM/alerting registration barrel. Router, worker, CLI bootstrap, and dashboard all side-effect-import this single file — no per-surface list of imports that can drift when a new provider is added. Previously dashboard.ts didn't register any PM providers at all, which is also fixed here. Task 5: tests/unit/integrations/entrypoint-usage.test.ts grep-asserts the invariant that every process entry file imports src/integrations/entrypoint.js. Missing this import was the root cause of #1097, #1118, #1131, #1134 — the test references those bug numbers in its failure message. Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
Task 6: tests/helpers/fakePMProvider.ts ships an in-memory FakePMProvider, its matching PMProviderManifest (opts into configSchema, every discoveryCapability, wizardSpec, lifecycle.enabled), and a shared runLifecycleScenario runner. All seven fake-lifecycle tests pass. Task 7: tests/unit/integrations/pm-conformance.test.ts extended with 5 new behavioral assertion groups: - config round-trip identity (guarded by manifest.configSchema) - discovery shape (guarded by manifest.discoveryCapabilities) - full lifecycle scenario (guarded by manifest.lifecycle.enabled) - trigger self-hook filter (guarded by manifest.isSelfAuthoredHook) - webhook verify accept/reject (runs against the fake's HMAC-SHA256) Legacy providers skip the new groups until they opt in during plans 2/3/4. Harness now runs 80 tests (59 pass, 21 skip — the skips are legacy providers pending migration, as designed). Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
Task 8: tests/unit/integrations/auth-header-provenance.test.ts greps the
src tree for `Bearer ${...}` / bare string-concat auth-header patterns
outside src/integrations/pm/_shared/auth-headers.ts. Post-#1119, the PM
provider code is clean; 4 non-PM files (Sentry, GitHub SCM, OpenRouter
LLM) are explicitly accept-listed with reasons — all out of spec 009
scope.
Task 9: Biome can't natively express the required string-pattern rule,
so lefthook.yml runs the provenance test at pre-commit (~250ms) —
failures surface at commit time, not just test time. Equivalent to a
custom Biome rule; matches plan 009/1 task 9's fallback guidance.
Also tightened the expanded conformance harness + fake-lifecycle test
to eliminate Biome complexity/non-null-assertion warnings.
Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
Task 10: src/api/routers/pm-discovery.ts now exposes a generic
`discover({ providerId, capability, args, credentials? })` mutation that
dispatches through the registry. Manifest must declare both
`discoveryCapabilities` and `createDiscoveryProvider`. Returns NOT_FOUND
for unknown providers, NOT_IMPLEMENTED for undeclared capabilities or
missing factories. 7 tests pass.
Task 11: web/src/components/projects/pm-providers/generator.tsx ships
`renderStandardStep(step, ctx)` as dormant scaffolding — returns a
typed placeholder for every StandardStepKind + custom steps. Plans 2/3/4
swap each placeholder for the shared component. Unknown kinds log a
warn-once and render a placeholder instead of crashing.
Task 12: tests/README.md and src/integrations/README.md document the
new fake provider fixture, the lifecycle scenario runner, the behavioral
contract fields on PMProviderManifest, the auth-header provenance test,
and the single-entrypoint invariant.
Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
Ships branded ID types, PMProviderManifest behavioral contract fields, single registration entrypoint, FakePMProvider fixture + lifecycle runner, 5 new behavioral conformance groups, auth-header provenance enforcement, generic pm.discover tRPC endpoint, and wizard step generator scaffolding. All primitives dormant — no provider migrated yet. Plans 2/3/4 flip each real provider on; plan 5 cleans up legacy surfaces. 415 test files / 7963 tests pass / 21 intentional skips. Lint, typecheck, and build all green. Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
Trello opts into configSchema, discoveryCapabilities, wizardSpec, lifecycle, createDiscoveryProvider, and branded-ID narrowing on adapter methods. Inline trello schema in src/config/schema.ts marked @deprecated; plan 5 deletes it. 420 test files, 7988 pass, 19 skip. Lint + typecheck + build green. Plan divergence notes in the .done file document 5 resolved issues. Co-Authored-By: Claude Opus 4 (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
Implements spec 009 — PM Integration Hardening across 5 plans. Hardens the PM manifest pattern introduced by spec 006 from a wiring convention into a behavioral contract, eliminating every drift vector that produced the 18-PR tail on Linear's rollout (#1112–#1142).
What lands:
StateId/LabelId/ContainerIdinsrc/pm/ids.ts) — state-name-vs-ID confusion is now a compile error at adapter call sites (locks fix(linear): store state IDs in status mappings #1117 / fix(linear): create issues directly in backlog state via stateId #1137 / fix(linear): pass stateId when creating checklist sub-issues #1139).src/integrations/pm/<provider>/config-schema.tsand the central schema imports it. Round-trip conformance asserted in CI (locks fix(linear): preserve projectId through config mapper #1138 / fix(config): add projectId to LinearConfigSchema (Zod was stripping it) #1142).src/integrations/entrypoint.ts— router, worker, CLI, dashboard all pull the same module. Usage guard (tests/unit/integrations/entrypoint-usage.test.ts) greps every process-entry file (locks feat(linear): register Linear in bootstrap and add DB lookup functions #1097 / fix(linear): pass projectId for credential resolution to worker #1118 / fix(cli,backlog-mgr): register PM providers in CLI; load Linear pipeline lists #1131 / fix(cli): synthesize Linear PM scope so cascade-tools pm works for Linear projects #1134).src/integrations/pm/_shared/auth-headers.ts) with a provenance test that forbids re-implementations (locks fix(linear): remove Bearer prefix from API key auth + fix biome config #1112 / fix(linear): send personal API keys without Bearer prefix #1119).FakePMProviderregression bed.pm.discovertRPC endpoint + declarativewizardSpec→ shared wizard step generator. Replaces bespoke per-provider discovery tRPC sprawl (fix(linear): wire up lookupProject to loadProjectConfigByLinearTeamId #1103–feat(api): add Linear tRPC discovery endpoints #1105) and wizard duplication (fix(linear): unblock wizard Save + UX polish for Linear PM setup #1113 / feat(linear): full label parity with Trello — dropdown + create-label UX #1121).Scope guarantee: adding a new PM provider should now be a single PR touching only `src/integrations/pm//` + `web/src/components/projects/pm-providers//` + one import line. Enforced by `tests/unit/integrations/new-provider-surface.test.ts`.
Commits (18): `32987ac9` … `1b5b2cf6` — full list in `git log origin/dev..HEAD`. Pre-push ran 4445 tests passed / 15 skipped locally.
Test plan
🤖 Generated with Claude Code