Skip to content

fix(config): disambiguate credential role resolution by provider#1115

Merged
zbigniewsobiecki merged 4 commits intodevfrom
zs/004-credential-role-disambiguation
Apr 15, 2026
Merged

fix(config): disambiguate credential role resolution by provider#1115
zbigniewsobiecki merged 4 commits intodevfrom
zs/004-credential-role-disambiguation

Conversation

@zbigniewsobiecki
Copy link
Copy Markdown
Member

Summary

Closes spec 004-credential-role-provider-disambiguation. Fixes two silent bugs that both trace to the same root cause.

What the operator sees

Before: opening or refreshing the Linear PM wizard on a project that already has LINEAR_API_KEY stored shows "Linear credentials not configured" in the Board / Project Selection step, forcing the operator to re-type the API key on every edit.

After: the wizard populates the teams dropdown from the stored credential; no re-typing required.

What else gets fixed silently

The router's resolveWebhookSecret('linear') was affected by the same bug: it was calling getIntegrationCredentialOrNull('pm', 'webhook_secret'), which resolved to whichever provider registered webhook_secret first in the Map (JIRA), so Linear webhook signature verification was silently resolving to JIRA_WEBHOOK_SECRET or null on Linear-only projects — meaning signatures have been silently skipping verification in production. This ships fixed in the same PR.

Root cause

roleToEnvVarKey(category, role) iterated all providers in a category and returned the first role-name match. Since api_key is declared by both Trello and Linear, and webhook_secret is declared by JIRA, Linear, GitHub, and Sentry, the helper returned the wrong env-var key for any call that happened to target a provider registered later than the first match.

Fix

  • roleToEnvVarKey(category, provider, role) — required provider parameter; indexes directly, no iteration.
  • getIntegrationCredential(projectId, category, provider, role) and getIntegrationCredentialOrNull(...) — same change. Compiler rejects callers that omit the provider.
  • 42 call sites updated across 13 src/ files to pass the literal provider ('linear', 'trello', 'jira', 'github', 'sentry').
  • Dashboard *ByProject discovery endpoints now look up project_integrations.provider first. On missing row, throws NOT_FOUND with a distinguishable message ('No PM integration configured for this project yet') — not conflated with 'credentials not configured', so the frontend can surface a clearer state.
  • Router's resolveWebhookSecret branches each pass the explicit provider per-branch.

Tests

  • 9 new unit tests for roleToEnvVarKey disambiguation (tests/unit/config/role-resolver.test.ts).
  • 6 new unit tests for resolveWebhookSecret proving each of the 5 providers' branches return the right env-var key and Linear's case no longer falls back to JIRA's (tests/unit/router/resolveWebhookSecret.test.ts).
  • Existing integrationsDiscovery tests (~99 tests) updated to mock getIntegrationByProjectAndCategory with the right provider; exercises the new distinguishable-error path on missing-row cases.

Totals: 7612 unit + 522 integration — all green. Lint + typecheck (root + web) clean.

Not touched

  • Credential storage schema, encryption, env-var-key naming.
  • Credential role definitions themselves (integrationRoles.ts).
  • Worker's flat env-var-map builder (unaffected — resolves by env-var key directly).
  • Wizard UI — no visible changes.
  • Database — no migration.

Out of scope

  • Linear project selection in the wizard (deferred to a future spec).

Test plan

  • npm test — 7612 passing
  • TEST_DATABASE_URL=... npm run test:integration — 522 passing
  • npm run lint — clean
  • npm run typecheck (root + web) — clean
  • On dev after deploy: open a Linear-configured project's wizard, confirm teams dropdown loads without the red banner; refresh the page, confirm it still loads.
  • On dev after deploy: send a Linear webhook to a project with LINEAR_WEBHOOK_SECRET set; confirm the router log shows signature verified (not "skipped").
  • On dev after deploy: attempt a *ByProject call on a project with no PM integration row — confirm the error reads "No PM integration configured for this project yet".

🤖 Generated with Claude Code

zbigniewsobiecki and others added 4 commits April 15, 2026 21:57
Spec 004 targets the credential role-resolution ambiguity that breaks
Linear wizard reopens and silently skips Linear webhook signature
verification in the router.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Plan 004/1 (disambiguate-role-resolver).

The credential-resolution helper iterated all providers in a category
looking for a role-name match and returned the first hit. Because the
built-in PM registration order is trello → jira → linear, this caused:

1. Dashboard: Linear wizard reopens demanded a re-typed API key every
   time. ('pm', 'api_key') resolved to TRELLO_API_KEY on a Linear-only
   project, which isn't configured, so 'Linear credentials not
   configured' surfaced in the Board/Project Selection step.

2. Router (production): Linear webhook signature verification silently
   returned the JIRA webhook secret (or null on Linear-only projects),
   causing signatures to silently skip verification.

Fix:
- roleToEnvVarKey(category, role) -> roleToEnvVarKey(category, provider, role)
- getIntegrationCredential[OrNull] now take (projectId, category,
  provider, role) — compiler rejects callers that omit provider.
- 42 call sites updated across 13 src/ files to pass the explicit
  provider.
- *ByProject dashboard endpoints look up project_integrations.provider
  first; throw NOT_FOUND with distinguishable message on missing row.
- resolveWebhookSecret branches pass the explicit provider per branch.

New tests:
- tests/unit/config/role-resolver.test.ts (9 tests)
- tests/unit/router/resolveWebhookSecret.test.ts (6 tests)
- integrationsDiscovery tests augmented with default provider mock.

Totals: 7612 unit + 522 integration all green. Lint + typecheck clean.

Closes spec 004.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…— all plans complete

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@zbigniewsobiecki zbigniewsobiecki merged commit 5a7d741 into dev Apr 15, 2026
8 checks passed
@zbigniewsobiecki zbigniewsobiecki deleted the zs/004-credential-role-disambiguation branch April 15, 2026 20:21
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 15, 2026

Codecov Report

❌ Patch coverage is 65.14286% with 61 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/api/routers/integrationsDiscovery.ts 53.38% 55 Missing ⚠️
src/pm/linear/integration.ts 57.14% 3 Missing ⚠️
src/config/provider.ts 84.61% 2 Missing ⚠️
src/router/platformClients/credentials.ts 90.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

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