You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Overall risk: P1 / medium. The 2026-06-03 audit's 10 children are all closed, dependency state is clean (npm audit --omit=dev reports 0 vulnerabilities), CI is green (1308/1308 tests pass, lint + typecheck clean), and the new harness-agnostic agent task contract is well-tested.
However, since the last audit Dispatch absorbed a large new surface area (5 new endpoints, 4 new lib modules) and that surface has accumulated debt around authentication, persistence, and documentation drift. The most concerning issues are an unauthenticated mutating endpoint (/api/agents/[agentName]/tasks/report) and an unauthenticated queue-leaking endpoint (/api/agents/[agentName]/next-task). The .npmrc fix from issue #315 is also still emitting a deprecation warning.
No code changes or PRs were made during this audit. All 10 child issues from the 2026-06-03 umbrella (#308 → #311-#320) are closed. The 11 open umbrella children (#381–#388, #392, #399–#401) for the "configurable lanes" and "harness-agnostic agent control plane" epics are not in this audit's scope.
Top Findings
P1 — Unauthenticated mutating endpoint: POST /api/agents/[agentName]/tasks/report
A new mutating route was added since the last audit that accepts any anonymous POST, validates the body, and returns 200 { ok: true, report } — but does not persist anything to the database. The endpoint is named "report" but is a no-op echo. It has no authorizeRequest() call, no getAuthMode() awareness, and no test asserting that bad auth is rejected (because it cannot be — it accepts everything). A malicious actor can spam it for log poisoning, body-shape probing, or to confuse downstream consumers who think a reported task is real.
Evidence:
src/app/api/agents/[agentName]/tasks/report/route.ts defines POST without authorizeRequest (only Request parameter).
Route summary: auth=no verbs=POST(.
The test file src/app/api/agents/[agentName]/tasks/report/route.test.ts asserts only validation, not auth: 12 test cases, zero 401 paths.
Body shape: { taskType, outcome, repoFullName?, issueNumber?, pullRequestNumber?, pullRequestUrl?, summary?, error? } — echoes back via TaskReportBody.
Comment in code says "validated" and "report" but no prisma.auditLog.create / prisma.agentRun.create call exists.
P1 — Unauthenticated queue/data leak: GET /api/agents/[agentName]/next-task
The new next-task endpoint is the most powerful read endpoint in the system: given an agentName, it returns the next implement/followup-pr/groom task, the lane, and the full task contract. It is unauthenticated, like the older read endpoints (queue, active-work, work-summary), but it consolidates more decision-critical data into a single response than the older endpoints do. The agent queue and lease identifiers are exposed, which is useful reconnaissance for any actor probing the system.
Evidence:
src/app/api/agents/[agentName]/next-task/route.ts:1-30: no authorizeRequest import; route summary auth=no verbs=GET(.
The route file is 229 lines; reads from prisma.issue.findMany (open issues from enabled repos), listQueuedPrFixItems (PR fix queue), and findLeasedIssueIds (other agents' leases).
The test file next-task/route.test.ts has 0 cases for 401/unauthorized — confirms the design intent (intentionally public) but does not document that intent anywhere.
P1 — .npmrc "fix" from #315 emits a deprecation warning and is not the canonical solution
The previous audit (#315) added .npmrc with omit= to force-include devDependencies. The current npm CLI rejects this value as invalid: "npm warn invalid config omit="" set in .npmrc" on every install/typecheck/test/lint. The intended behaviour (force-install devDependencies) is actually achieved (because npm's default is to include dev unless omit=dev is set), so this is currently working by accident — but the warning is noisy in CI logs and the value is on the npm deprecation path. The canonical fix is include=dev (added in npm v7) or simply deleting the .npmrc.
Evidence:
cat .npmrc shows omit= on a single line.
npm install, npm run typecheck, npm run test, npm run lint, and npm audit --omit=dev all print:
npm warn invalid config omit="" set in /data/git/mispospace/dispatch/.npmrc
npm warn invalid config Must be one or more of: dev, optional, peer
Confirmed locally: replacing omit= with include=dev produces no warning and gives the same install behavior.
The fix was originally omit= → omit=dev per Restore reproducible local validation #315's resolution text in the previous audit, but the actual commit d002ee1 shipped omit=.
P1 — AGENTS.md does not document the four new agent-facing endpoints
The "OpenClaw Agent Workflow Contract" section of AGENTS.md documents POST /api/sync, POST /api/agent-runs, GET /api/issues, and GET /api/agents/[agentName]/queue, but does not mention:
GET /api/agents/[agentName]/next-task (the canonical entry point for new harnesses)
POST /api/agents/[agentName]/tasks/report (where harnesses report task outcomes)
GET /api/agents/[agentName]/work-summary (lane-aware work counts)
GET /api/agents/[agentName]/active-work (resume context)
GET /api/agents/[agentName]/queue is documented but the AgentTask contract from src/lib/agent-task.ts is not.
This drift is exactly what the previous audit flagged (#317 "Reconcile maintainer docs with implemented lane routes"), but the next wave of endpoint additions was not picked up by the same fix.
Evidence:
grep -n "/api/agents" AGENTS.md returns only queue/heartbeat/lane references.
src/lib/agent-task.ts (200+ lines) is not referenced anywhere in AGENTS.md or README.md.
docs/worker-execution-contract.md references getQueuedPrFixItems indirectly but does not show the next-task contract shape.
P2 — tasks/report is a stub: no persistence, no AgentRun row, no AuditLog row
Beyond the auth concern above, the endpoint is functionally incomplete. Other "report" surfaces in Dispatch (POST /api/agent-runs, POST /api/audit) persist data. This one returns 200 { ok: true, report } and discards the body. If a worker calls it, the operator gets no signal that work happened. The test file confirms persistence is not part of the contract.
Evidence:
src/app/api/agents/[agentName]/tasks/report/route.ts:80-100 returns NextResponse.json({ ok: true, agentName, report }) with no await prisma.* call.
The test file mocks prisma.issue.update and prisma.prFixQueueItem.update and prisma.lease.delete but never invokes any of them.
Compare with src/app/api/agents/[agentName]/heartbeat/route.ts:101-110 which calls prisma.agentRun.create.
P2 — next-task and queue duplicate 80%+ of their query logic and could be unified
The two routes share issue-fetching, lease-filtering, PR-fix-queue-fetching, and queue-building logic. next-task then takes the first item and wraps it in an AgentTask. As the lane configuration work lands (#381-#388), the two will need to be updated in lockstep. A shared helper that returns { rankedQueue, prFixItems } would let next-task and queue evolve together.
Evidence:
src/app/api/agents/[agentName]/queue/route.ts:14-87 and src/app/api/agents/[agentName]/next-task/route.ts:90-150 are structurally similar.
Both call prisma.issue.findMany({ where: { state: "open", repository: { enabled: true } }, select: { ... linkedPrHealth ... } }) with the same select shape.
Both call findLeasedIssueIds(agentName) and listQueuedPrFixItems(asPrFixQueueClient(prisma), { lane }).
P2 — lane-config.ts is added but not yet consumed by the rest of the system
src/lib/lane-config.ts was added in PR #391 as the foundation for configurable lanes, but a grep -RIn "lane-config" src/ shows that only its own test file imports from it. The hardcoded "normal" | "escalated" | "backlog" literals in src/lib/issue-reconciliation.ts:14, src/lib/issue-lane.ts:8, and src/types/index.ts:VALID_LANES are unchanged. This means the new module is dead code until #383 ("refactor: replace hardcoded lane constants with lane helpers") lands.
Evidence:
grep -RIn "lane-config" src/lib/ src/app/ 2>/dev/null returns only src/lib/lane-config.test.ts:12.
src/lib/issue-reconciliation.ts:14 and src/lib/issue-lane.ts:8 still use the inline union type.
P2 — Route coverage gap: 17 of 48 route.ts files have no co-located test
Roughly a third of API routes (mostly older ones) have no co-located route.test.ts. Several of the unauthenticated list/read routes (/api/audit, /api/repos, /api/automation/events, /api/automation/workflows, /api/issues/untriaged) leak data without auth and would benefit from regression coverage. The PR-fix-queue and PR-followup ingestion routes have route-level coverage gaps too.
P2 — Repo label drift: typos and deprecated labels still on GitHub
labels.yaml is now the source of truth (per PR #373), but several legacy/dead labels still exist on the repo and aren't tracked anywhere: priotity/p1 (typo), needs-gpt (deprecated per AGENTS.md lane routing rules), type/bug (alongside the canonical bug), several ad-hoc agent/* labels (agent/Dispatch MCP, agent/OpenCode-Mac, agent/saffron, agent/dispatch-agent).
Evidence:
gh label list --repo misospace/dispatch --limit 100 --json name minus .github/labels.yaml labels: 22 unmanaged labels.
AGENTS.md:71 says "Do NOT route to ESCALATED only because labels include… legacy needs-gpt" — so the label is documented as legacy but not removed.
P2 — npm audit is clean, but SECURITY-ACCEPTED-RISKS.md still lists advisories that may be obsolete
The accepted-risks file documents two moderate CVEs (postcss XSS, hono/node-server path bypass) with the rationale "no viable upgrade path." Both packages now have patched versions; the rationale may no longer be accurate. CI scan (aquasecurity/trivy-action@ed142fd) shows the current state but does not include npm audit output.
npm audit --omit=dev --json reports 0 vulnerabilities — confirming the underlying issues are no longer present at the installed version, but the accepted-risks doc has not been updated.
P3 — next-task test file is missing auth-coverage and a contract test for idle mode
The new next-task test file has 9 cases focused on happy-path task shaping, but no contract test for:
the idle task shape returned when neither queue nor PR-fix has work
the mode=groom groom-task-shape
the ?includeClaimed=true / ?includeRenovate=true query parameter handling
The tasks/report test file is similarly happy-path only (12 cases, no auth, no persistence).
Evidence:
wc -l src/app/api/agents/[agentName]/next-task/route.test.ts → not super high; count of it( cases: 9.
wc -l src/app/api/agents/[agentName]/tasks/report/route.test.ts → 12 cases, all validation only.
P3 — Heartbeat test file has only 2 shared-helper cases for a 184-line module
src/lib/heartbeat.test.ts is 82 lines and only covers the runSyncBestEffort / runReconcileBestEffort happy/empty paths. The 184-line heartbeat.ts includes aggregation, touchedIssueUrls collection, and warning/error bucket logic that is not covered. The integration is covered by src/app/api/agents/[agentName]/heartbeat/route.test.ts (412 lines) but the unit-level coverage is thin.
Evidence:
wc -l src/lib/heartbeat.ts src/lib/heartbeat.test.ts → 184 vs 82.
P3 — mc-client.tsresolveAgentName requires explicit agentName but the MCP server test environment may drift
The src/mcp/server.tsclaimIssueHandler and claimWorkHandler use resolveAgentName which reads DISPATCH_AGENT_NAME from env, falling back to requiring an explicit agentName argument. The src/mcp/server.test.ts (747 lines) sets DISPATCH_AGENT_NAME via process.env in the test file, but the production MCP server entrypoint (src/mcp/server.ts) does not check whether DISPATCH_AGENT_NAME is unset and log a startup warning. If a model invokes the MCP without an explicit agentName and the env is unset, the response is "agentName is required" — not a startup error.
Evidence:
src/mcp/server.ts:40-50 shows the agentName requirement handling.
src/mcp/server.ts has no startup-time validation that DISPATCH_AGENT_NAME is set.
The src/mcp/index.ts entrypoint is not present in the source tree (only src/mcp/server.ts), so the startup path needs to be re-verified.
Recommended Issue Breakdown
P1 — Add authentication to POST /api/agents/[agentName]/tasks/report or remove the endpoint until it actually persists reports.
P1 — Add authorizeRequest (Bearer or basic) to GET /api/agents/[agentName]/next-task and document the auth model in the OpenClaw workflow contract.
P1 — Replace the .npmrcomit= line with include=dev (or delete the file) and verify CI is still clean.
P1 — Reconcile AGENTS.md and docs/worker-execution-contract.md with the new next-task, tasks/report, work-summary, active-work, and AgentTask contract.
P2 — Decide the persistence model for tasks/report (audit log row, agent run row, both) and add tests.
P2 — Extract shared queue-fetching logic between agents/[agentName]/queue/route.ts and agents/[agentName]/next-task/route.ts so lane-config and lease-filter changes apply to both.
P2 — Migrate the hardcoded Lane = "normal" | "escalated" | "backlog" literals in issue-reconciliation.ts, issue-lane.ts, and types/index.ts:VALID_LANES to use the new lane-config.ts helpers.
P2 — Add co-located route.test.ts for the 17 routes currently uncovered, prioritized by data-leak risk (/api/audit, /api/automation/*, /api/issues/untriaged).
P3 — Expand src/lib/heartbeat.test.ts to cover aggregation, touchedIssueUrls, and warning/error bucketing.
P3 — Add a startup-time warning in the MCP server entrypoint if DISPATCH_AGENT_NAME is unset (mirror the DISPATCH_AUTH_MODE=disabled warning in docker-entrypoint.sh).
P3 — Add a smoke check to docs/smoke-checklist.md that exercises the next-task end-to-end happy path and an idle path (mirroring the existing health/automation/sync checks).
Not Worth Doing Yet
Do not introduce an RBAC system yet — the Bearer/Basic/OIDC contract is still being normalized. First, add auth to the new endpoints.
Do not rewrite the Prisma client or split into microservices — the current module boundaries are workable.
Do not chase a generalized plugin system for next-task task types — the three shapes (implement, followup-pr, groom) are well-defined and easy to test.
Do not remove the /api/agents/[agentName]/tasks/report endpoint until Configure Renovate #1 is triaged — it may be a placeholder for an in-flight integration.
Do not chase cosmetic refactors of the tasks/report validation block — wait for the persistence decision.
Do not create child issues manually from this umbrella; the audit decomposer cron should do that deterministically.
Do not migrate lane-config.ts to the Prisma DB (epic: make Dispatch lanes configurable #381 epic) until the in-memory helper has at least one consumer outside of its own test — that would be premature.
Weekly tech debt audit: dispatch - 2026-06-17
Summary / Overall Risk Level
Overall risk: P1 / medium. The 2026-06-03 audit's 10 children are all closed, dependency state is clean (
npm audit --omit=devreports 0 vulnerabilities), CI is green (1308/1308 tests pass, lint + typecheck clean), and the new harness-agnostic agent task contract is well-tested.However, since the last audit Dispatch absorbed a large new surface area (5 new endpoints, 4 new lib modules) and that surface has accumulated debt around authentication, persistence, and documentation drift. The most concerning issues are an unauthenticated mutating endpoint (
/api/agents/[agentName]/tasks/report) and an unauthenticated queue-leaking endpoint (/api/agents/[agentName]/next-task). The.npmrcfix from issue #315 is also still emitting a deprecation warning.No code changes or PRs were made during this audit. All 10 child issues from the 2026-06-03 umbrella (#308 → #311-#320) are closed. The 11 open umbrella children (#381–#388, #392, #399–#401) for the "configurable lanes" and "harness-agnostic agent control plane" epics are not in this audit's scope.
Top Findings
P1 — Unauthenticated mutating endpoint:
POST /api/agents/[agentName]/tasks/reportA new mutating route was added since the last audit that accepts any anonymous POST, validates the body, and returns
200 { ok: true, report }— but does not persist anything to the database. The endpoint is named "report" but is a no-op echo. It has noauthorizeRequest()call, nogetAuthMode()awareness, and no test asserting that bad auth is rejected (because it cannot be — it accepts everything). A malicious actor can spam it for log poisoning, body-shape probing, or to confuse downstream consumers who think a reported task is real.Evidence:
src/app/api/agents/[agentName]/tasks/report/route.tsdefines POST withoutauthorizeRequest(onlyRequestparameter).auth=no verbs=POST(.src/app/api/agents/[agentName]/tasks/report/route.test.tsasserts only validation, not auth: 12 test cases, zero 401 paths.{ taskType, outcome, repoFullName?, issueNumber?, pullRequestNumber?, pullRequestUrl?, summary?, error? }— echoes back viaTaskReportBody.prisma.auditLog.create/prisma.agentRun.createcall exists.P1 — Unauthenticated queue/data leak:
GET /api/agents/[agentName]/next-taskThe new
next-taskendpoint is the most powerful read endpoint in the system: given anagentName, it returns the next implement/followup-pr/groom task, the lane, and the full task contract. It is unauthenticated, like the older read endpoints (queue,active-work,work-summary), but it consolidates more decision-critical data into a single response than the older endpoints do. The agent queue and lease identifiers are exposed, which is useful reconnaissance for any actor probing the system.Evidence:
src/app/api/agents/[agentName]/next-task/route.ts:1-30: noauthorizeRequestimport; route summaryauth=no verbs=GET(.prisma.issue.findMany(open issues from enabled repos),listQueuedPrFixItems(PR fix queue), andfindLeasedIssueIds(other agents' leases).next-task/route.test.tshas 0 cases for401/unauthorized — confirms the design intent (intentionally public) but does not document that intent anywhere.P1 —
.npmrc"fix" from #315 emits a deprecation warning and is not the canonical solutionThe previous audit (#315) added
.npmrcwithomit=to force-include devDependencies. The current npm CLI rejects this value as invalid: "npm warn invalid config omit="" set in .npmrc" on every install/typecheck/test/lint. The intended behaviour (force-install devDependencies) is actually achieved (because npm's default is to include dev unlessomit=devis set), so this is currently working by accident — but the warning is noisy in CI logs and the value is on the npm deprecation path. The canonical fix isinclude=dev(added in npm v7) or simply deleting the.npmrc.Evidence:
cat .npmrcshowsomit=on a single line.npm install,npm run typecheck,npm run test,npm run lint, andnpm audit --omit=devall print:omit=withinclude=devproduces no warning and gives the same install behavior.omit= → omit=devper Restore reproducible local validation #315's resolution text in the previous audit, but the actual commitd002ee1shippedomit=.P1 — AGENTS.md does not document the four new agent-facing endpoints
The "OpenClaw Agent Workflow Contract" section of
AGENTS.mddocumentsPOST /api/sync,POST /api/agent-runs,GET /api/issues, andGET /api/agents/[agentName]/queue, but does not mention:GET /api/agents/[agentName]/next-task(the canonical entry point for new harnesses)POST /api/agents/[agentName]/tasks/report(where harnesses report task outcomes)GET /api/agents/[agentName]/work-summary(lane-aware work counts)GET /api/agents/[agentName]/active-work(resume context)GET /api/agents/[agentName]/queueis documented but the AgentTask contract fromsrc/lib/agent-task.tsis not.This drift is exactly what the previous audit flagged (#317 "Reconcile maintainer docs with implemented lane routes"), but the next wave of endpoint additions was not picked up by the same fix.
Evidence:
grep -n "/api/agents" AGENTS.mdreturns only queue/heartbeat/lane references.src/lib/agent-task.ts(200+ lines) is not referenced anywhere inAGENTS.mdorREADME.md.docs/worker-execution-contract.mdreferencesgetQueuedPrFixItemsindirectly but does not show the next-task contract shape.P2 —
tasks/reportis a stub: no persistence, noAgentRunrow, noAuditLogrowBeyond the auth concern above, the endpoint is functionally incomplete. Other "report" surfaces in Dispatch (
POST /api/agent-runs,POST /api/audit) persist data. This one returns200 { ok: true, report }and discards the body. If a worker calls it, the operator gets no signal that work happened. The test file confirms persistence is not part of the contract.Evidence:
src/app/api/agents/[agentName]/tasks/report/route.ts:80-100returnsNextResponse.json({ ok: true, agentName, report })with noawait prisma.*call.prisma.issue.updateandprisma.prFixQueueItem.updateandprisma.lease.deletebut never invokes any of them.src/app/api/agents/[agentName]/heartbeat/route.ts:101-110which callsprisma.agentRun.create.P2 —
next-taskandqueueduplicate 80%+ of their query logic and could be unifiedThe two routes share issue-fetching, lease-filtering, PR-fix-queue-fetching, and queue-building logic.
next-taskthen takes the first item and wraps it in anAgentTask. As the lane configuration work lands (#381-#388), the two will need to be updated in lockstep. A shared helper that returns{ rankedQueue, prFixItems }would letnext-taskandqueueevolve together.Evidence:
src/app/api/agents/[agentName]/queue/route.ts:14-87andsrc/app/api/agents/[agentName]/next-task/route.ts:90-150are structurally similar.prisma.issue.findMany({ where: { state: "open", repository: { enabled: true } }, select: { ... linkedPrHealth ... } })with the sameselectshape.findLeasedIssueIds(agentName)andlistQueuedPrFixItems(asPrFixQueueClient(prisma), { lane }).P2 —
lane-config.tsis added but not yet consumed by the rest of the systemsrc/lib/lane-config.tswas added in PR #391 as the foundation for configurable lanes, but agrep -RIn "lane-config" src/shows that only its own test file imports from it. The hardcoded"normal" | "escalated" | "backlog"literals insrc/lib/issue-reconciliation.ts:14,src/lib/issue-lane.ts:8, andsrc/types/index.ts:VALID_LANESare unchanged. This means the new module is dead code until #383 ("refactor: replace hardcoded lane constants with lane helpers") lands.Evidence:
grep -RIn "lane-config" src/lib/ src/app/ 2>/dev/nullreturns onlysrc/lib/lane-config.test.ts:12.src/lib/issue-reconciliation.ts:14andsrc/lib/issue-lane.ts:8still use the inline union type.P2 — Route coverage gap: 17 of 48
route.tsfiles have no co-located testRoughly a third of API routes (mostly older ones) have no co-located
route.test.ts. Several of the unauthenticated list/read routes (/api/audit,/api/repos,/api/automation/events,/api/automation/workflows,/api/issues/untriaged) leak data without auth and would benefit from regression coverage. The PR-fix-queue and PR-followup ingestion routes have route-level coverage gaps too.Evidence:
find src/app/api -name 'route.test.ts' | wc -l→ 31find src/app/api -name 'route.ts' | wc -l→ 48/api/audit/route.ts(unauthenticated, returns allAuditLogrows)/api/repos/route.ts(unauthenticated GET)/api/automation/events/route.ts(unauthenticated GET)/api/automation/workflows/route.ts(unauthenticated GET)/api/automation/workflows/[id]/route.ts(unauthenticated GET)/api/pr-fix-queue/mark/route.ts(mutating, has auth)/api/pr-fix-queue/queued/route.ts(auth)/api/pr-fix-queue/enqueue/route.ts(auth)/api/pr-followup/sync/route.ts(auth)/api/pr-followup/webhook/route.ts(auth, has signature verification)/api/issues/reconcile/route.ts(auth)/api/issues/untriaged/route.ts(unauthenticated GET)/api/issues/[issueId]/pr-health/refresh/route.ts(auth)/api/agent-runs/route.ts(auth)/api/health/route.ts(unauthenticated)/api/auth/logout/route.ts(intentionally no auth)/api/auth/[...nextauth]/route.ts(NextAuth-managed)P2 — Repo label drift: typos and deprecated labels still on GitHub
labels.yamlis now the source of truth (per PR #373), but several legacy/dead labels still exist on the repo and aren't tracked anywhere:priotity/p1(typo),needs-gpt(deprecated per AGENTS.md lane routing rules),type/bug(alongside the canonicalbug), several ad-hocagent/*labels (agent/Dispatch MCP,agent/OpenCode-Mac,agent/saffron,agent/dispatch-agent).Evidence:
gh label list --repo misospace/dispatch --limit 100 --json nameminus.github/labels.yamllabels: 22 unmanaged labels.AGENTS.md:71says "Do NOT route to ESCALATED only because labels include… legacyneeds-gpt" — so the label is documented as legacy but not removed.P2 —
npm auditis clean, butSECURITY-ACCEPTED-RISKS.mdstill lists advisories that may be obsoleteThe accepted-risks file documents two moderate CVEs (postcss XSS, hono/node-server path bypass) with the rationale "no viable upgrade path." Both packages now have patched versions; the rationale may no longer be accurate. CI scan (
aquasecurity/trivy-action@ed142fd) shows the current state but does not include npm audit output.Evidence:
cat SECURITY-ACCEPTED-RISKS.mdlistsnext@16.2.7 bundles postcss@8.4.31andprisma@7.8.0 / @hono/node-server < 1.19.13.npm audit --omit=dev --jsonreports 0 vulnerabilities — confirming the underlying issues are no longer present at the installed version, but the accepted-risks doc has not been updated.P3 —
next-tasktest file is missing auth-coverage and a contract test foridlemodeThe new
next-tasktest file has 9 cases focused on happy-path task shaping, but no contract test for:idletask shape returned when neither queue nor PR-fix has workmode=groomgroom-task-shape?includeClaimed=true/?includeRenovate=truequery parameter handlingThe
tasks/reporttest file is similarly happy-path only (12 cases, no auth, no persistence).Evidence:
wc -l src/app/api/agents/[agentName]/next-task/route.test.ts→ not super high; count ofit(cases: 9.wc -l src/app/api/agents/[agentName]/tasks/report/route.test.ts→ 12 cases, all validation only.P3 — Heartbeat test file has only 2 shared-helper cases for a 184-line module
src/lib/heartbeat.test.tsis 82 lines and only covers therunSyncBestEffort/runReconcileBestEfforthappy/empty paths. The 184-lineheartbeat.tsincludes aggregation,touchedIssueUrlscollection, and warning/error bucket logic that is not covered. The integration is covered bysrc/app/api/agents/[agentName]/heartbeat/route.test.ts(412 lines) but the unit-level coverage is thin.Evidence:
wc -l src/lib/heartbeat.ts src/lib/heartbeat.test.ts→ 184 vs 82.grep -c "describe\|it(" src/lib/heartbeat.test.ts→ 2.P3 —
mc-client.tsresolveAgentNamerequires explicitagentNamebut the MCP server test environment may driftThe
src/mcp/server.tsclaimIssueHandlerandclaimWorkHandleruseresolveAgentNamewhich readsDISPATCH_AGENT_NAMEfrom env, falling back to requiring an explicitagentNameargument. Thesrc/mcp/server.test.ts(747 lines) setsDISPATCH_AGENT_NAMEviaprocess.envin the test file, but the production MCP server entrypoint (src/mcp/server.ts) does not check whetherDISPATCH_AGENT_NAMEis unset and log a startup warning. If a model invokes the MCP without an explicitagentNameand the env is unset, the response is "agentName is required" — not a startup error.Evidence:
src/mcp/server.ts:40-50shows theagentNamerequirement handling.src/mcp/server.tshas no startup-time validation thatDISPATCH_AGENT_NAMEis set.src/mcp/index.tsentrypoint is not present in the source tree (onlysrc/mcp/server.ts), so the startup path needs to be re-verified.Recommended Issue Breakdown
POST /api/agents/[agentName]/tasks/reportor remove the endpoint until it actually persists reports.authorizeRequest(Bearer or basic) toGET /api/agents/[agentName]/next-taskand document the auth model in the OpenClaw workflow contract..npmrcomit=line withinclude=dev(or delete the file) and verify CI is still clean.AGENTS.mdanddocs/worker-execution-contract.mdwith the newnext-task,tasks/report,work-summary,active-work, andAgentTaskcontract.tasks/report(audit log row, agent run row, both) and add tests.agents/[agentName]/queue/route.tsandagents/[agentName]/next-task/route.tsso lane-config and lease-filter changes apply to both.Lane = "normal" | "escalated" | "backlog"literals inissue-reconciliation.ts,issue-lane.ts, andtypes/index.ts:VALID_LANESto use the newlane-config.tshelpers.route.test.tsfor the 17 routes currently uncovered, prioritized by data-leak risk (/api/audit,/api/automation/*,/api/issues/untriaged).priotity/p1(typo),needs-gpt,type/bug(alongsidebug), ad-hocagent/*labels.SECURITY-ACCEPTED-RISKS.mdto reflect the current cleannpm auditstate, or document why the advisories remain on the accepted list.idleandmode=groomcontract tests for/api/agents/[agentName]/next-task; add auth-coverage tests once fix(deps): update nextjs monorepo (14.2.18 → 14.2.35) #2 lands.src/lib/heartbeat.test.tsto cover aggregation,touchedIssueUrls, and warning/error bucketing.DISPATCH_AGENT_NAMEis unset (mirror theDISPATCH_AUTH_MODE=disabledwarning indocker-entrypoint.sh).docs/smoke-checklist.mdthat exercises thenext-taskend-to-end happy path and anidlepath (mirroring the existing health/automation/sync checks).Not Worth Doing Yet
next-tasktask types — the three shapes (implement,followup-pr,groom) are well-defined and easy to test./api/agents/[agentName]/tasks/reportendpoint until Configure Renovate #1 is triaged — it may be a placeholder for an in-flight integration.tasks/reportvalidation block — wait for the persistence decision.lane-config.tsto the Prisma DB (epic: make Dispatch lanes configurable #381 epic) until the in-memory helper has at least one consumer outside of its own test — that would be premature.Audit Notes
Safe read-only checks performed:
main(already at4c47335).src/app/api/,src/lib/,src/components/,src/mcp/,prisma/schema.prisma,prisma/migrations/,.github/workflows/,AGENTS.md,docs/,SECURITY-ACCEPTED-RISKS.md,docker-entrypoint.sh,Dockerfile,package.json,tsconfig.json,eslint.config.mjs,vitest.config.ts,prisma.config.ts,.env.example,.npmrc,next.config.js.npm run test→ 1308/1308 pass.npm run typecheck→ clean.npm run lint→ clean.npm audit --omit=dev --json→ 0 vulnerabilities.gh issue list(open and closed withauditlabel) and the previous umbrella (Weekly tech debt audit: dispatch - 2026-06-03 #308).gh pr listfor the merged-in-this-window surface.gh label listvs.github/labels.yamlto find unmanaged label drift..npmrcdeprecation warning by reproducing it in a throwaway project at/tmp/test-npmrc-*.Not done (would require write access or live state):
next-taskandqueueroutes against realistic data volumes.next-taskcontract.Local repo state after audit: Clean. No files modified.
Decomposed into