Task Breakdown: Agent Zero-Friction Onboarding
Ordered, granular tasks derived from
plan.md. Each task is small enough to land in a single PR and ships with tests per Constitution Principle VI.
Feature ID: agent-zero-friction-onboarding
Plan: ./plan.md
Status: In Progress — most tickets shipped, awaiting CI green + merge to develop.
Last updated: 2026-05-05
How to use
- Tasks are sequential by default. Tasks marked
(parallel)can run alongside their predecessor. - Each task names explicit file paths so an implementer can pick it up cold.
- Use the checkbox to track progress as PRs land.
- Add new tasks at the bottom rather than renumbering.
Phase 1 — Data model & contracts
- T1. ✅
OnboardingRequestentity atpackages/agent/src/entities/onboarding-request.entity.ts, exported from the entities barrel. - T2. ✅
WebhookSubscriptionentity atpackages/agent/src/entities/webhook-subscription.entity.tswithsecretEncrypteddoc-marked asx-secret: true. - T3. ✅ Hand-written migration
apps/api/src/migrations/1746360000000-AddOnboardingAndWebhookSubscriptions.ts(since superseded — see commitd3b65c65). Forward-only, additive. Verify with apnpm typeorm migration:generaterun after pulling — should be a no-op or small diff. - T4. ✅ Onboarding contract types under
packages/contracts/src/api/onboarding/with re-exports from the API barrel:register-work.request.ts,register-work.response.ts,onboarding-status.ts,webhook-event.ts,manifest.types.ts.
- T5. ✅ Zod manifest schema at
packages/agent/src/services/works-manifest.service.ts(placed in the agent package because contracts has no zod dep). Test atpackages/agent/src/services/__tests__/works-manifest.service.spec.tscovers the happy path, every documented subcode, and the 64 KiB cap.
Phase 2 — Service layer
-
T8 (partial). ✅
WorksManifestServiceshipped with T5 above (kept together because they are one unit). Full unit-test suite included. -
T9a (validation pipeline). ✅
OnboardingServiceatapps/api/src/onboarding/onboarding.service.tsimplements: canonicalisation, GitHub identity resolution viaGitFacadeService.getUser, repo write-access check viaGitFacadeService.getRepository+ permissions, manifest fetch from root.works/works.ymlwith base64 decode, schema validation viaWorksManifestService, idempotency lookup,repo_already_ownedconflict, persistence withstatus='validated'. 12 unit tests atonboarding.service.spec.tscover the happy path and every typed error code. Decoupled from the heavy facades chain via theOnboardingGitProviderinterface +ONBOARDING_GIT_PROVIDERinjection token in@ever-works/agent/onboarding— the realGitFacadeServiceis bound viauseExistinginOnboardingModule. -
T9b (account upsert). ✅
OnboardingAccountAdapteratapps/api/src/onboarding/onboarding-account.adapter.tsimplementsOnboardingAccountUpsert(interface from the lean barrel) and mirrors theGitHubAppOnboardingService.findOrCreateLocalUserpattern acrossUserRepository,AuthAccountRepository, andGitHubAppUserLinkRepository. Wired inOnboardingService.handleafter manifest validation;accountIdpersisted on the row. -
T9c (Work creation). ✅
OnboardingWorkAdapteratapps/api/src/onboarding/onboarding-work.adapter.tsimplementsOnboardingWorkCreator(interface from the lean barrel) and translates the parsed.works/works.ymlinto aCreateWorkDto, then callsWorkLifecycleService.createWork. The lifecycle service handles the data/website/awesome repos.workIdis persisted and the row transitions toqueuedviaOnboardingRequestRepository.tryTransition(CAS). -
T9d (Trigger.dev enqueue — task scaffold). ✅
work-onboarding.taskatpackages/tasks/src/tasks/trigger/work-onboarding.task.tsregistered in the trigger barrel. The task carries the retry policy (3 attempts, exp backoff up to 5 min) and theWorkOnboardingPayloadshape. The handoff into the existingwork-import.taskorchestrator (long-running content generation + deploy) is left as a single tightly-scoped follow-up — the api-side T9b/T9c chain already creates the Work synchronously, so the agent'sstatusUrlreturns a realworkIdimmediately and existing scheduled-update / generation tasks can advance it pastqueued. -
SSRF guard utility (subtask of T10). ✅ Shared helper at
packages/agent/src/utils/ssrf-guard.tswith full IPv4 / IPv6 / metadata-host coverage. Spec atpackages/agent/src/utils/__tests__/ssrf-guard.spec.ts. -
T6. ✅
OnboardingRequestRepositoryatpackages/agent/src/database/repositories/onboarding-request.repository.tswithfindByIdentityAndRepo,findByRepo,findById,create,tryTransition(CAS),markFailure,setWorkId,setAccountId. Wired intoDatabaseModuleproviders/exports and the agentdatabasebarrel. -
T7. ✅
WebhookSubscriptionRepositoryatpackages/agent/src/database/repositories/webhook-subscription.repository.tswithcreateForAccount,listActiveForWork,listActiveForAccount,markSuccess,incrementFailure,markFailed,pause,findById. Wired the same way. -
T8. (already done as T5 in slice 1) —
WorksManifestServiceships with the lean barrel. -
T9. ✅ See T9a–T9d above —
OnboardingServicelives inapps/api/src/onboarding/(where the heavy DI surface naturally belongs) rather thanpackages/agent/src/services/. Public methodshandle()andgetStatus()cover the entire flow: identity-hash, idempotency, GitHub validation via the leanOnboardingGitProviderinterface backed byGitFacadeService, manifest fetch, account upsert via theOnboardingAccountUpsertinterface backed byOnboardingAccountAdapter, Work creation via theOnboardingWorkCreatorinterface backed byOnboardingWorkAdapter(which callsWorkLifecycleService.createWork), status transitions viaOnboardingRequestRepository.tryTransition, and Trigger.dev enqueue scaffolding viawork-onboarding.task. 12 unit tests cover happy path + every typed error code. -
T10. ✅
WebhookDeliveryServiceatpackages/agent/src/services/webhook-delivery.service.tssignsX-Hub-Signature-256HMAC-SHA256 over the raw body, emitsX-Ever-Works-EventandX-Ever-Works-Deliveryheaders, applies the SSRF guard, exposes a staticverify()for receivers, and usesfetch(Node 22+) as the default HTTP client (mockable viaWebhookHttpClient). Companion spec atwebhook-delivery.service.spec.ts— 12 cases (signature determinism, secret variance, header shape, success/failure paths, SSRF block, network error). The retry policy is provided by the Trigger.dev task that wraps deliver(). -
T11. ✅
StateMarkerServiceatpackages/agent/src/services/state-marker.service.tswrites.works/state.json(default) via aMarkerFileWriterinterface so the heavy git-write transitively imports stay out of the unit-test path. Enforces the.works/namespace per FR-26a. Companion spec — 5 cases. -
T12. ✅ Lean barrel at
packages/agent/src/onboarding/index.tsre-exportsWorksManifestService,WebhookDeliveryService,StateMarkerService, both repositories, the SSRF/redaction utilities, and the three injection-token interfaces (OnboardingGitProvider,OnboardingAccountUpsert,OnboardingWorkCreator). Registered in agentpackage.jsonexports as./onboardingso api-side code can import without pulling the heavy services chain.
Phase 3 — REST surface
- T13 + T14. ✅ Combined DTO file at
apps/api/src/onboarding/dto/register-work.dto.ts— request DTO with class-validator decorators, response DTO, and error DTO with full Swagger annotations. - T15. ✅ Controller at
apps/api/src/onboarding/onboarding.controller.tsimplementsPOST /api/register-workandGET /api/register-work/:idwith@Public(),@Throttle(), and the full@ApiOperation/@ApiHeader/@ApiResponsedecorator set so Swagger + Scalar render the contract automatically. - T16. ✅
OnboardingModuleatapps/api/src/onboarding/onboarding.module.ts, imported intoapps/api/src/api.module.ts. - T18 (controller half). ✅ Jest spec at
apps/api/src/onboarding/onboarding.controller.spec.ts— supertest-driven, runs the full Nest pipeline (validation,whitelist, header parsing, idempotency-key forwarding, missing-token rejection, Agent Card route).
- T17. ✅ Project-wide redaction utility at
packages/agent/src/utils/redaction.tswithredactHeaders,redactBody,redactString,REDACTED_HEADERS(incl.x-github-token,x-hub-signature-256,x-ever-works-signature,idempotency-key), andREDACTED_BODY_FIELDS(incl.agentPayment). The currentLoggingInterceptordoes not log bodies/headers (so it's already secret-safe); the helper is the single point of policy for any future log site. Companion spec covers headers, nested-body, arrays, and the short-secret guard. - T18. Add e2e test at
apps/api/test/onboarding.e2e-spec.ts:- 202 on happy path (mocked GitHub + mocked WorksService)
- 403 with
gh_repo_access_denied - 422 with
manifest_missingandmanifest_invalid - 409 with
repo_already_owned - Idempotent re-call returns same
onboardingId - 429 when rate-limited
X-GitHub-Tokenis never echoed in logs (assert via test logger)
Phase 4 — Background pipeline
- T19. ✅
work-onboarding.taskships atpackages/tasks/src/tasks/trigger/work-onboarding.task.tswith retry policy andWorkOnboardingPayloadshape. The api flow already creates the Work synchronously in T9b/T9c; the task is a stable enqueue point for any future generation handoff. - T20. ✅ Registered in
packages/tasks/src/tasks/trigger/index.ts. - T21. ✅
OnboardingTerminalServiceatapps/api/src/onboarding/onboarding-terminal.service.tsis the producer-agnostic fan-out point: any caller (api on synchronous failure, Trigger.dev task on async success/failure, future scheduled reconciler) invokesnotify()with aTerminalNotificationand gets webhook delivery + state marker + per-subscription failure counter. 5 unit-test cases inonboarding-terminal.service.spec.ts. - T22. ✅ Unit tests cover the terminal service end-to-end (success path, missing row, multiple subs, failure-counter increment + auto-pause at 6 failures, failure metadata in payload).
Phase 5 — Webhook delivery & state marker
- T23. ✅ Replaced the planned BullMQ queue with the leaner Trigger.dev approach (Principle IV: long-running work via Trigger.dev).
WebhookDeliveryService.deliver()is the unit of work; the calling task drives retry semantics. No new queue infra needed at v1. - T24. ✅ SSRF guard at
packages/agent/src/utils/ssrf-guard.ts— 19 cases covering IPv4 private/loopback/link-local/multicast/CGNAT, IPv6 ULA/link-local/loopback, and cloud metadata hostnames. Bracketed-host parsing matches Node's URL behaviour. - T25. ✅ Equivalent coverage via the
WebhookDeliveryServiceunit spec (signature determinism, HTTP success/failure, SSRF block, network error) plus theOnboardingTerminalServicespec asserting per-request hook URL + per-account subscription fan-out. A live e2e against a test HTTP server is queued for the integration-test pass once the api e2e harness lands.
Phase 6 — MCP tool, Agent Card, llms.txt
- T26. ✅ MCP
register_worktool atapps/mcp/src/register-work.tool.tsusing@rekog/mcp-nest's@Tooldecorator with a Zod parameter schema. Registered inapps/mcp/src/app.module.ts. The tool POSTs to${EVER_WORKS_API_URL}/api/register-workwithX-GitHub-Token(and optionalIdempotency-Key) and proxies the response. Public — no Ever Works credential required. - T27. ✅ Agent Card route at
apps/api/src/onboarding/well-known.controller.ts, servingGET /.well-known/agent.jsonwithCache-Control: public, max-age=300. URLs are env-driven (PUBLIC_API_URL,PUBLIC_MCP_URL,PUBLIC_DOCS_URL,PUBLIC_CONTACT_EMAIL). HTTP test included in the controller spec asserts shape + cache header. - T28 (deferred — separate repo PR). The directory website template lives in its own git repo (
ever-works/directory-web-template) and ships independently. The required additions are:apps/web/app/llms.txt/route.tsreturning the public llms.txt convention (orpublic/llms.txtstatic).apps/web/app/items.json/route.tsreturning the canonical item dump.- Snapshot test on a sample work in the template's existing harness.
These changes are tracked as a sibling PR to the template repo and don't block the platform-side feature shipping. The
OnboardingServicealready passes throughmanifest.spec.output.{llmsTxt,itemsJson}flags so the template can read them when implemented.
Phase 7 — CLI surface
- T29. ✅
everworks work registeratapps/cli/src/commands/work/register.ts— thin commander wrapper that POSTs to the API withX-GitHub-Token. Wired into theworkparent command atapps/cli/src/commands/work/index.ts. Reads--github-tokenor$GITHUB_TOKENand the--api-url/$EVER_WORKS_API_URLoverride. Renders the 202 response with onboarding id, work id, status, and assigned subdomain; prints typed-error code + per-field errors on failure.
Phase 8 — Docs & rollout
- T30. ✅ Public-facing doc at
docs/agent-services/zero-friction-onboarding.md. Theagent-services/category uses Docusaurus'generated-indexmode (see_category_.json), so the new page is auto-listed without a manual sidebar edit. - T31. ✅ The REST surface is annotated with
@ApiOperation/@ApiHeader/@ApiBody/@ApiResponseso it surfaces in the existing/api/openapi.json, Swagger UI, and Scalar reference. The public-facing doc atdocs/agent-services/zero-friction-onboarding.mdplus the spec/plan/manifest-schema files indocs/specs/features/agent-zero-friction-onboarding/cover the textual reference; the OpenAPI doc is the authoritative live reference. - T32. ✅ Manifest schema reference lives at
./manifest-schema.mdinside the feature spec folder (Spec Kit convention). The public doc page links to it. A mirror page on the public docs site is optional cleanup. - T33. ✅ Feature flag at
apps/api/src/config/constants.ts(config.features.zeroFrictionOnboarding) readsFEATURE_ZERO_FRICTION_ONBOARDINGenv var, defaulttrue. The controller returns 404 with codefeature_disabledwhen the flag is off so an operator can disable the public surface without redeploy. - T34. ✅ Spec, plan, and tasks status updated:
spec.mdready forIn Review, thistasks.mdreflects shipped work,plan.mdphases 1–7 implemented (phase 8 default-on flip is the rollout step). - T35. Final
pnpm format && pnpm lint && pnpm type-check && pnpm test && pnpm buildat repo root deferred to CI on the feature branch — local sweep covers the new code (agent: 59 pass, api onboarding: 32 pass), and the cross-package wiring builds cleanly viapnpm buildon each package.
Definition of Done
- All checkboxes ticked.
- All new code has matching unit / e2e tests passing locally and in CI.
pnpm format:checkandpnpm lintgreen.pnpm --filter ever-works-docs buildproduces no broken-link warnings.- The
OpenAPIdocument at/api/openapi.jsonlists/api/register-workand/api/register-work/:idwith full schemas. - Swagger UI (
/api/swagger) and Scalar reference render the new endpoints. /.well-known/agent.jsonreturns the Agent Card with the registration capability.- Constitution gates in
spec.md§9 all confirmed satisfied. - Feature flag flipped on after soak.