Task Breakdown: Plugins Capabilities (HTTP Surface)
Feature ID: plugins-capabilities
Plan: ./plan.md
Status: Done (retrospective; surface already shipped on develop)
Last updated: 2026-05-08
How to use
This is a retrospective task list — every numbered task already shipped on
develop. Outstanding follow-ups (T16 onward) are not blockers for the
spec backfill but are good candidates for a future hourly-tracker run.
Phase 1 — Module scaffolding
- T1.
DeployModuleatapps/api/src/plugins-capabilities/deploy/deploy.module.ts- Imports:
FacadesModule,DatabaseModule,WebsiteGeneratorModule,WorkModule,ActivityLogModule,forwardRef(() => AuthModule) - Providers:
DeployService,DeploymentVerifierService - Exports:
DeployService,DeploymentVerifierService
- Imports:
- T2.
SearchModuleatapps/api/src/plugins-capabilities/search/search.module.ts - T3.
ScreenshotModuleatapps/api/src/plugins-capabilities/screenshot/screenshot.module.ts - T4.
OAuthModuleatapps/api/src/plugins-capabilities/oauth/oauth.module.ts - T5.
GitProviderModuleatapps/api/src/plugins-capabilities/git-provider/git-provider.module.ts - T6.
DeviceAuthModuleatapps/api/src/plugins-capabilities/device-auth/device-auth.module.ts - T7. Mount all six modules in
apps/api/src/api.module.ts(lines 18-24 imports + 64-71 module list).
Phase 2 — Controllers
- T8.
DeployControllercovering 13 endpoints (providers/configured, works/:id deploy + check + lookup + teams + domains GET/POST/DELETE + verify, batch, validate-token, /teams placeholder).- Owner gate:
WorkOwnershipService.ensureCanEdit/.ensureCanViewon every:id-scoped endpoint. - Activity-log emission:
work.deployed(single deploy) +deployment.batch_started(batch) — both fire-and-forget with.catch(() => {}). - Domain regex validation on
AddDomainDto.
- Owner gate:
- T9.
SearchControllercovering 2 endpoints (/check-availability,/).- Per-call provider resolution via private
resolveConfiguredProviderhelper (no per-callproviderOverridefrom the DTO — see OQ-4). - Two distinct unconfigured-vs-not-enabled messages on
/check-availability. NoProviderErrorre-mapped; non-Error throws coerce to"Search failed".
- Per-call provider resolution via private
- T10.
ScreenshotControllercovering 3 endpoints (/check-availability,/capture,/get-url).- Three-tier sort (default → configured → name) on the provider list.
cacheUrlpreferred overimageUrlin the response.imageBuffer.toString('base64')only when present; elsenull.
- T11.
OAuthControllercovering 6 endpoints (providers list, connection, connect/url, callback/plugins, user, DELETE disconnect).- Strict-
'true'parse onforceConsent. - Missing-code
BadRequestBEFORE service call. - 200-envelope-with-error on
getUser/disconnect.
- Strict-
- T12.
GitProviderControllercovering 5 endpoints (providers list, connection, organizations, repositories, user).parseInt(_, 10)onpage/perPage, each independently undefined-able.- Two-branch auth resolution (OAuth vs PAT) with shape-discriminated
getUsercalls.
- T13.
DeviceAuthControllercovering 2 endpoints (/status,/start).- Two-line passthroughs to
DeviceAuthService; errors propagate unwrapped.
- Two-line passthroughs to
Phase 3 — Services
- T14.
DeployService.deploy(workId, userId, options)— orchestrates the dispatch chain (resolve plugin + token + settings → fetch git token → ensure repo context → enable workflows → set required + optional + cron secrets → call plugingetDeploymentSecrets→ dispatch with retry → emitDeploymentDispatchedEvent). - T15.
DeployService.deployBatch— rolling batches of 5 with 2 000 ms sleep between batches. - T16.
DeploymentVerifierService.startVerification— replaces any existing poller for the work, then registers a freshsetInterval(10s)loop (max 18 cycles + 13 min wall-clock cap). - T17.
DeploymentVerifierService.lookupExistingDeployment— one-shot lookup helper used byPOST /api/deploy/works/:id/lookup. - T18.
OAuthService— six methods (isConfigured, available providers, connection check, OAuth URL builder, callback handler, disconnect). - T19.
GitProviderService— five methods (isConfigured, available providers, connection check, get user/organizations/ repositories). - T20.
DeviceAuthService— two passthrough methods.
Phase 4 — Plugin contract evolution
- T21. Add optional
IDeploymentPlugin.getWorkflowFilenames()topackages/plugin/src/deployment/. - T22. Add optional
IDeploymentPlugin.getDeploymentSecrets(settings)topackages/plugin/src/deployment/. - T23. Implement
getWorkflowFilenamesinpackages/plugins/vercel/to return['deploy_vercel.yaml', 'deploy_prod.yaml'].
Phase 5 — Event-driven activity-log
- T24. Define
DeploymentDispatchedEvent/DeploymentCompletedEvent/DeploymentFailedEventinpackages/agent/src/events/. - T25. Register the three event types in
ActivityLogListener(consumer-side spec lives inactivity-log).
Phase 6 — Tests (per COVERAGE-TRACKER.md)
- T26.
device-auth/device-auth.service.spec.ts(6 tests) +device-auth/device-auth.controller.spec.ts(4 tests) — PR #518. - T27.
git-provider/git-provider.service.spec.ts(15 tests) +git-provider/git-provider.controller.spec.ts(9 tests) — PR #518. - T28.
oauth/oauth.service.spec.ts(29 tests) +oauth/oauth.controller.spec.ts(22 tests) — PR #518. - T29.
search/search.controller.spec.ts(17 tests) — PR #520. - T30.
screenshot/screenshot.controller.spec.ts(24 tests) — PR #520. - T31.
deploy/deploy.service.spec.ts(6 suites) — covers capability contracts (getWorkflowFilenames,getDeploymentSecrets), event emission, error handling, fallbacks for legacy plugins.
Phase 7 — Docs & rollout
- T32. Author this Spec Kit feature folder (
docs/specs/features/plugins-capabilities/). - T33. Cross-link from
COVERAGE-TRACKER.md"Pending — Medium Priority" → "Done". - T34. User-facing API doc under
docs/api/plugins-capabilities/with one page per capability (deploy, search, screenshot, oauth, git-provider, device-auth) — see follow-up T-DOCS-1.
Outstanding follow-ups
-
T-DEPLOY-CTRL.
apps/api/src/plugins-capabilities/deploy/deploy.controller.spec.tscovering all 14 endpoints — 54 tests across 13 describe-blocks. Pinned:ensureCanEdit→isConfigured→validateToken→deploy→startVerification→ activity-log emission order via sharedorder: string[]array;validateTokenrejection short-circuits with NO log emission AND NOstartVerificationcall; activity-log emission is fire-and-forget (.catch(() => {})) and a rejected log promise still returns thependingenvelope. Creator-vs-shareduserIdfallback wired throughisConfigured/validateToken/deployService.deploy/startVerification(useswork.user.idwhen caller is not creator) — log payload'suserIdis ALWAYSauth.userIdeven on shared-work deploys. PR #600. -
T-OQ-1. Decide the fate of
POST /api/deploy/teams(the workless variant). It currently returns{teams: [], message: 'To fetch teams, use the work-specific endpoint or configure your token in Plugin Settings.'}— pure placeholder. Either remove it (breaking change for the UI's empty-state hint) or rename to/api/deploy/teams/hintto clarify intent. -
T-OQ-2. Unify the error-envelope shape across all six capabilities. Today: deploy/search/screenshot throw
BadRequestException(4xx); OAuth/git-provider/device-auth wrap into{success: false, error}inside 200. Either flip the latter group to throw 4xx (breaking change for UI clients that consume the 200 envelope) or document the asymmetry as intentional. -
T-OQ-3. Migrate
DeploymentVerifierServicefrom in-memorysetIntervalto a Trigger.dev task so verification survives API restarts. The current behaviour is documented in §Plan §11 (Risks): a redeploy mid-verification silently abandons the poller; users can re-resolve viaPOST /api/deploy/works/:id/lookup. Trigger.dev migration would land underpackages/tasks/src/tasks/trigger/deploy-verification.task.tswith idempotent CAS updates onwork.deploymentState. -
T-OQ-4. Expose
providerOverridefromSearchDtoso callers can pick a specific search plugin per request (mirroring thescreenshot/captureshape). Today,SearchControlleralways resolves "the first configured plugin sorted bydefaultForCapabilities-first". -
T-OQ-5. Decide whether to emit a
'work.deployment_token_invalid'activity-log entry onvalidateTokenrejection so users can see failed-credentials attempts in their audit trail. Today the controller short-circuits silently with aBadRequest. -
T-OQ-6. Add a per-controller
@Throttledecorator stack so bulk endpoints (POST /api/deploy/batch,POST /api/screenshot/capture) cannot be hammered. Today they inherit the global throttler config only. -
T-DOCS-1. Author
docs/api/plugins-capabilities/{deploy,search, screenshot,oauth,git-providers,device-auth}.mdwith one Markdown page per capability listing endpoints, request/response shapes, and example curl invocations. -
T-DOCS-2. Add an architecture diagram to
docs/architecture/plugins-capabilities.mdshowing the controller → facade → registry → plugin call chain (similar to §Plan §1 but rendered as a static SVG). -
T-E2E-1. Add an e2e test under
apps/api/test/plugins-capabilities/that exercises the deploy + search + screenshot happy paths with a stubbed plugin registry. -
T-E2E-2. Add a kind-cluster CI scenario that exercises the full deploy chain (controller → service → verifier → terminal event → activity-log row) with a real GitHub Actions dispatch against a throwaway repo.
Definition of Done
- All checkboxes T1–T33 ticked.
- All shipped tests passing on
develop. - Spec status set to
Implemented; plan + tasks markedDone. - T34 + outstanding follow-ups tracked as separate hourly-tracker
candidates in
COVERAGE-TRACKER.md.