Verification engine
Module: packages/core/src/verification/engine.ts Worker: apps/mcp/src/workers.ts (BullMQ queue verification)
Pipeline
complete_task(taskId)
│
│ Task lands in `needs_review` with verification_status='pending'.
│ completeTask enqueues a verification job (jobId=`verify-{taskId}`).
▼
[ verification worker ]
│
│ 1. SELECT task by id (loads snapshot verificationContract)
│ 2. isBlockedByReview? → pending(blocked_by_review), retry later
│ 3. switch (contract.kind):
│ none → skipped (state already done)
│ manual → skipped (human gate stays)
│ github_pr → verifyGitHubPr(client, ref)
│ gitlab_mr → verifyGitLabMr(client, ref)
│ commands → failed (unsupported until sandboxed exec lands)
│ 4. verdict:
│ passed → UPDATE tasks SET state='done', verification_status='passed'
│ failed → markNeedsReview(reason='verification.<code>')
│ pending → return; worker re-enqueues with exponential backoff
│ 5. After MAX_ATTEMPTS=10 pending in a row → failed(reason=verification.timeout)
│
▼
[ pub/sub: pubsub:org:{orgId}:tasks ]
task.verification.{passed|failed|pending|skipped}external_ref parsing
GitHub PRs come in as owner/repo#123, GitLab MRs as group/proj!42. The default parser accepts either separator. Tasks without an external_ref on a github_pr/gitlab_mr contract fail with reason verification.missing_external_ref.
Reasons (the needs_review_reason codes)
| Code | When |
|---|---|
verification.checks_failed | required check returned failure |
verification.pr_not_merged | PR/MR closed without merge |
verification.missing_external_ref | task linked to PR/MR but external_ref is null |
verification.bad_external_ref | the ref doesn't parse |
verification.blocked_by_review | open blocker comment on the task |
verification.timeout | still pending after MAX_ATTEMPTS |
verification.commands_kind_unsupported | Phase-0 placeholder |
verification.unknown_kind | contract.kind not in the catalogue |
Provider clients
The orchestrator is dependency-injected — the worker passes a { github, gitlab } deps bag. Today the bag is empty in the runtime worker (apps/mcp/src/workers.ts), so any task with kind='github_pr' or kind='gitlab_mr' will throw with the explicit message verification: github_pr task requires deps.github client. Wiring the Octokit / gitbeaker adapters happens when an installation is provisioned (task 30/31 runbooks). The throw is intentional — we don't want a silent "passes everything" stub in production.
Retry & backoff
The default BullMQ options in packages/shared/src/queues.ts give exponential backoff with 5 attempts; the engine itself caps at MAX_ATTEMPTS=10 if the caller passes attempt. Real wall-clock budget is ~1 hour for a PR to merge after complete_task — beyond that the task goes to needs_review for human attention.