Repository navigation
Weekly tech debt audit: dispatch - 2026-06-03 #308
Description
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.tsdefinesauthedFetch()and sessionStorage-backed Basic Auth header injection.src/components/sync-issues-button.tsx:6,35andsrc/components/kanban-board.tsx:26,89,118,213useauthedFetch.src/app/automation/page.tsx:198,212,239,src/app/automation/repos/[...repo]/page.tsx:297, andsrc/components/issue-card.tsx:96,119,148,194still use plainfetchfor protected POST/DELETE routes.- Static scan command:
grep -RIn "fetch(" src/app src/componentsshowed 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
authorizeRequestscan 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:88exportsPOST()and scans tracked repos usingGITHUB_TOKENwithout a route auth check.src/app/api/pr-followup/webhook/route.ts:186-196verifies signatures only ifWEBHOOK_SECRETis configured.src/app/api/pr-followup/webhook/route.ts:222persists queue work throughprocessPrFollowupEvents(...).
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-59hasacquireLock()/releaseLock().src/app/api/sync/route.ts:8-65performs issue sync without that lock.src/app/api/automation/sync/route.ts:329-356loops 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 --jsonreported 5 moderate advisories:nextvia bundledpostcss,prismavia@prisma/dev/@hono/node-server..github/workflows/ci.yamland.github/workflows/image.yamlpin checkout/setup/build actions by SHA..github/workflows/image.yamlusesaquasecurity/trivy-action@masterwithcontinue-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 typecheckfailed before project checking:TS2688: Cannot find type definition file for '@testing-library/jest-dom'.npm run test -- --runInBandfailed:sh: 1: vitest: not found.node_modules/.binlackedvitest;node_modules/@testing-library/jest-domwas 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:3version is0.4.4.src/lib/version.tsresolves the package/build version for UI display.src/app/api/health/route.ts:5falls back to0.1.1.docs/smoke-checklist.md:30-34still documents health response version0.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-79documentsGET /api/issues/[id]/classify,POST /api/issues/[id]/classify, andPOST /api/issue-lanes/classify-bulk.- Current route tree has
src/app/api/issues/[issueId]/lane/route.tsand nosrc/app/api/issue-lanes/classify-bulkroute.
Recommended Issue Breakdown
- P1 — Normalize browser API auth helpers: replace protected mutating UI
fetchcalls withauthedFetchor a shared client wrapper and add Basic Auth mode regression tests. - P1 — Harden PR follow-up ingestion auth: require
authorizeRequeston pull sync and make webhook signature requirements explicit/fail-closed unless an intentional gateway mode is configured. - 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.
- P2 — Resolve production dependency advisories: assess Next/PostCSS and Prisma advisory paths, upgrade or document accepted risk, and pin/upgrade Trivy action off
master. - P2 — Restore reproducible local validation: document/repair local install expectations so
npm run typecheckandnpm run testwork from a clean checkout withnpm ci. - P2 — Unify health/version reporting: make
/api/healthuse the shared version helper or build-time version and update smoke docs. - P2 — Reconcile maintainer docs with implemented lane routes: update AGENTS.md/docs to match current
/api/issues/[issueId]/laneand existing bulk/classification endpoints. - P3 — Reduce automation sync write amplification: batch obvious independent writes or introduce bounded concurrency after locking is settled.
- 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.
- 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
- Normalize browser API auth helpers #311 — Normalize browser API auth helpers
- Harden PR follow-up ingestion auth #312 — Harden PR follow-up ingestion auth
- Share sync locking/concurrency controls #313 — Share sync locking/concurrency controls
- Resolve production dependency advisories #314 — Resolve production dependency advisories
- Restore reproducible local validation #315 — Restore reproducible local validation
- Unify health/version reporting #316 — Unify health/version reporting
- Reconcile maintainer docs with implemented lane routes #317 — Reconcile maintainer docs with implemented lane routes
- Reduce automation sync write amplification #318 — Reduce automation sync write amplification
- Prune duplicated auth-mode wording/tests #319 — Prune duplicated auth-mode wording/tests
- Add operational runbook notes for DISPATCHAUTHMODE=disabled #320 — Add operational runbook notes for DISPATCHAUTHMODE=disabled
Activity