Skip to content

Weekly tech debt audit: dispatch - 2026-06-03 #308

@itsmiso-ai

Description

@itsmiso-ai

Umbrella issue URL: #308

Summary / Overall Risk Level

Overall risk: P1 / medium-high. Dispatch has a solid Next.js/TypeScript/Prisma foundation, broad unit coverage, centralized route authorization for most mutating APIs, and pinned CI actions. The main risk is accumulated inconsistency from many small patches around auth mode behavior, UI/API coupling, sync orchestration, and release/docs drift. No code changes or PRs were made during this audit.

Top Findings

P1 - Basic Auth browser flows are inconsistent

README says browser mutating API calls automatically include Basic Auth credentials via authedFetch(), and some board paths do. Other active UI paths still use plain fetch() for protected mutating routes, which means DISPATCH_AUTH_MODE=basic operators can hit 401s or inconsistent behavior depending on page/component.

Evidence:

  • src/lib/client-auth.ts defines authedFetch() and sessionStorage-backed Basic Auth header injection.
  • src/components/sync-issues-button.tsx:6,35 and src/components/kanban-board.tsx:26,89,118,213 use authedFetch.
  • src/app/automation/page.tsx:198,212,239, src/app/automation/repos/[...repo]/page.tsx:297, and src/components/issue-card.tsx:96,119,148,194 still use plain fetch for protected POST/DELETE routes.
  • Static scan command: grep -RIn "fetch(" src/app src/components showed the split.

P1 - PR follow-up ingestion has bypass-prone authorization/config defaults

The pull-based PR follow-up sync endpoint is a mutating POST that does not use the shared authorizeRequest() helper. The webhook endpoint can intentionally skip signature verification when WEBHOOK_SECRET is unset, which may be acceptable behind a gateway but is risky as a default unless deployment docs and runtime checks make that explicit.

Evidence:

  • Mutating routes without authorizeRequest scan returned: src/app/api/auth/logout/route.ts, src/app/api/pr-followup/sync/route.ts, src/app/api/pr-followup/webhook/route.ts.
  • src/app/api/pr-followup/sync/route.ts:88 exports POST() and scans tracked repos using GITHUB_TOKEN without a route auth check.
  • src/app/api/pr-followup/webhook/route.ts:186-196 verifies signatures only if WEBHOOK_SECRET is configured.
  • src/app/api/pr-followup/webhook/route.ts:222 persists queue work through processPrFollowupEvents(...).

P1 - Manual sync surfaces can overlap expensive GitHub/database work

Scheduled sync has a DB-backed single-row lock, but normal issue sync and automation sync do not appear to share that lock. Browser refreshes, cron overlap, or repeated clicks can trigger concurrent GitHub scans and per-run/per-job writes.

Evidence:

  • src/app/api/sync/scheduled/route.ts:13-59 has acquireLock() / releaseLock().
  • src/app/api/sync/route.ts:8-65 performs issue sync without that lock.
  • src/app/api/automation/sync/route.ts:329-356 loops through tracked repos; syncRepo() does multiple GitHub calls and upserts per workflow/run/job/release/PR/package.

P2 - Dependency/supply-chain audit needs follow-up

Runtime dependency audit found moderate advisories. CI pins most actions by SHA, but Trivy still tracks aquasecurity/trivy-action@master, which reduces reproducibility of security scanning.

Evidence:

  • npm audit --omit=dev --json reported 5 moderate advisories: next via bundled postcss, prisma via @prisma/dev / @hono/node-server.
  • .github/workflows/ci.yaml and .github/workflows/image.yaml pin checkout/setup/build actions by SHA.
  • .github/workflows/image.yaml uses aquasecurity/trivy-action@master with continue-on-error: true.

P2 - Local validation environment is not reproducible from current checkout state

The audit could not run full tests locally because the checked-out node_modules was incomplete. CI likely installs cleanly with npm ci, but the local workspace state can mislead agents during audits/fixes.

Evidence:

  • npm run typecheck failed before project checking: TS2688: Cannot find type definition file for '@testing-library/jest-dom'.
  • npm run test -- --runInBand failed: sh: 1: vitest: not found.
  • node_modules/.bin lacked vitest; node_modules/@testing-library/jest-dom was missing.
  • Repo contains 63 test files, so this is an environment/install issue rather than absence of tests.

P2 - Release/version/docs drift is visible

Version reporting and docs disagree, which undermines operational smoke checks and support diagnostics.

Evidence:

  • package.json:3 version is 0.4.4.
  • src/lib/version.ts resolves the package/build version for UI display.
  • src/app/api/health/route.ts:5 falls back to 0.1.1.
  • docs/smoke-checklist.md:30-34 still documents health response version 0.1.13.

P2 - Maintainer docs drift from implemented routes

AGENTS.md mentions endpoints that do not match the current file tree, which can misroute agents and decomposer jobs.

Evidence:

  • AGENTS.md:76-79 documents GET /api/issues/[id]/classify, POST /api/issues/[id]/classify, and POST /api/issue-lanes/classify-bulk.
  • Current route tree has src/app/api/issues/[issueId]/lane/route.ts and no src/app/api/issue-lanes/classify-bulk route.

Recommended Issue Breakdown

  1. P1 — Normalize browser API auth helpers: replace protected mutating UI fetch calls with authedFetch or a shared client wrapper and add Basic Auth mode regression tests.
  2. P1 — Harden PR follow-up ingestion auth: require authorizeRequest on pull sync and make webhook signature requirements explicit/fail-closed unless an intentional gateway mode is configured.
  3. P1 — Share sync locking/concurrency controls: extend the scheduled sync lock or a repo-scoped lock to manual issue sync and automation sync, including useful 409 responses.
  4. P2 — Resolve production dependency advisories: assess Next/PostCSS and Prisma advisory paths, upgrade or document accepted risk, and pin/upgrade Trivy action off master.
  5. P2 — Restore reproducible local validation: document/repair local install expectations so npm run typecheck and npm run test work from a clean checkout with npm ci.
  6. P2 — Unify health/version reporting: make /api/health use the shared version helper or build-time version and update smoke docs.
  7. P2 — Reconcile maintainer docs with implemented lane routes: update AGENTS.md/docs to match current /api/issues/[issueId]/lane and existing bulk/classification endpoints.
  8. P3 — Reduce automation sync write amplification: batch obvious independent writes or introduce bounded concurrency after locking is settled.
  9. P3 — Prune duplicated auth-mode wording/tests: keep one source of truth for auth mode semantics and use route-level tests for high-risk flows.
  10. P3 — Add operational runbook notes for DISPATCH_AUTH_MODE=disabled: keep it explicitly local-only and add deployment checks if feasible.

Not Worth Doing Yet

  • Do not rewrite the app architecture or replace Prisma/Next.js; current module boundaries are workable.
  • Do not introduce a full RBAC system before the Basic/OIDC/Bearer contract is made consistent.
  • Do not build a generalized job runner for sync yet; a small shared lock and clear responses should come first.
  • Do not chase low-value cosmetic refactors in UI components until auth and sync behavior are stable.
  • Do not create child issues manually from this umbrella; the audit decomposer cron should do that deterministically.

Audit Notes

Safe read-only checks performed: refreshed local main, inspected routes/libs/docs/workflows/schema, searched route auth usage, checked labels via GitHub API, ran npm audit --omit=dev --json, attempted npm run typecheck and npm run test. Local repo state after audit included a pre-existing untracked src/components/auth-status.tsx that was not touched.

Decomposed into

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions