Skip to content

Weekly tech debt audit: misospace/dispatch - 2026-07-22 #648

Description

@itsmiso-ai

Weekly tech debt audit: misospace/dispatch - 2026-07-22

Repo health snapshot

Last audit findings carried over

# Title Status
#620 Remove pervasive as any casts in projects page OPEN — P2, not yet addressed

Last audit findings resolved since 2026-07-15

All 6 findings created in the previous audit were closed, though #618 (GitHub client extraction) was resolved via re-export facades rather than actual module splitting.


Met findings

Partial GitHub module extraction — 1049-line monolith still intact

The src/lib/github.ts module remains 1049 lines despite issue #618 being closed. The "fix" added four re-export facade files (github-auth.ts, github-issues.ts, github-ci.ts, github-code-search.ts) that each re-export everything from the original github.ts via export * from "./github". No code was moved into the domain files. The monolith still spans JWT auth, token management, issue CRUD, PR queries, CI log fetching, code search, and repository metadata.

The acceptance criteria from #618 listed "each new file under 400 lines" — none of the facade files contain any new code, and github.ts remains at 1049 lines.

Evidence:

  • src/lib/github.ts — 1049 lines
  • src/lib/github-auth.ts — 2 lines: export * from "./github" (empty facade)
  • src/lib/github-issues.ts — 2 lines: export * from "./github" (empty facade)
  • src/lib/github-ci.ts — 2 lines: export * from "./github" (empty facade)
  • src/lib/github-code-search.ts — 2 lines: export * from "./github" (empty facade)

Acceptance: Either close #618 with a note that the current approach (typed return + entry-point facades) is sufficient, or reopen it and actually extract the domain logic into separate files under 400 lines each.


Stale @hono/node-server package override in package.json

The overrides block in package.json pins @hono/node-server to ^2.0.0 with a comment noting it was added to "remediate npm advisories" as a Prisma transitive dependency. However, @hono/node-server no longer comes through Prisma — it's a dependency of @modelcontextprotocol/sdk. Prisma v7.8.0+ does not depend on @hono/node-server (confirmed by npm ls @hono/node-server which shows only the MCP SDK path). The comment also says the override "can be removed once Prisma upgrades to include the patched version" — Prisma has shipped several upgrades (now v7.9.0) and the pin is no longer needed for its original purpose.

The postcss override is still valid since next and @tailwindcss/postcss both depend on it and an advisory was noted.

Evidence:

  • package.json line ~45: comment explaining override origin ("Prisma transitive dependency — can be removed once Prisma upgrades to include the patched version")
  • npm ls @hono/node-server shows only @modelcontextprotocol/sdk@1.29.0 path, no Prisma path
  • Prisma v7.9.0 is current

Acceptance: Remove @hono/node-server from overrides and update the postcss override comment to remove stale references to @hono/node-server.


Missing tests: 16 untested utility modules

While overall test coverage is good (108 passing test files, 2033 passing tests), 16 utility modules lack dedicated test files. These include core modules like automation-sync.ts, resolve-actor.ts, session.ts, tracked-repos.ts, version-client.ts, issue-status.ts, the deprecated facade files (github-auth.ts, etc.), 2 groomer modules (groomer-lock.ts, enum-config.ts/enum-configs.ts), and general utilities (utils.ts, callback-url.ts). Most of these are small or trivial, but automation-sync.ts (382 lines), groomer-lock.ts (DB-backed lock, subtle race logic), and resolve-actor.ts (audit attribution) would benefit from unit tests.

Additionally, 6 UI components lack tests: agent-work-panel.tsx, kanban-board-client.tsx, status-badge.tsx, and 4 ui/ primitives (badge.tsx, button.tsx, card.tsx, input.tsx, table.tsx). The ui/ primitives are shadcn-style wrappers and arguably don't need their own tests, but kanban-board-client.tsx and status-badge.tsx are domain components.

Evidence:

  • Untested lib modules: automation-sync.ts, callback-url.ts, github-auth.ts, github-ci.ts, github-code-search.ts, github-issues.ts, groomer/enum-config.ts, groomer/enum-configs.ts, groomer/groomer-lock.ts, issue-status.ts, resolve-actor.ts, session.ts, tracked-repos.ts, utils.ts, version-client.ts (15 modules)
  • Untested components: agent-work-panel.tsx, kanban-board-client.tsx, status-badge.tsx (3 domain components)
  • automation-sync.ts — 382 lines, no tests

Acceptance: Add unit tests for automation-sync.ts, groomer-lock.ts, and resolve-actor.ts as a starting point (these are the highest-risk untested modules).


In-app scheduler uses fixed 127.0.0.1:3000 loopback address

The in-app periodic scheduler in src/lib/scheduler.ts constructs loopback POST URLs using http://127.0.0.1:${port} where port defaults to 3000. This works when the default port is used, but if PORT is set to a different value (e.g., container orchestrators sometimes set PORT to a random port), the scheduler POSTs to the wrong port and all scheduled jobs silently fail (401s from the loopback listener). The 401 is then swallowed by runJob's error handling — only non-409 non-OK responses are logged.

The HOSTNAME=0.0.0.0 override in the Dockerfile (to fix the Kubernetes pod-name resolution issue) interacts here: if the app binds to 0.0.0.0:8080 but the scheduler uses 127.0.0.1:3000, POSTs never reach the server.

Evidence:

  • src/lib/scheduler.ts line ~89: baseUrl: \http://127.0.0.1:${port}\``
  • src/lib/scheduler.ts line ~42: const port = env.PORT && env.PORT.trim() !== "" ? env.PORT.trim() : "3000";
  • Dockerfile line ~53: ENV HOSTNAME=0.0.0.0 — binds to all interfaces

Acceptance: Derive the scheduler's base URL from the app's actual listening address, or validate at startup that the scheduler can reach its own endpoints. At minimum, log a warning when PORT differs from 3000.


Webhook handler reads raw body after authorization

The PR follow-up webhook handler in src/app/api/pr-followup/webhook/route.ts calls request.arrayBuffer() to get the raw body for HMAC verification, then parses the JSON from that buffer. However, this happens after the authorizeRequest() call, which may also consume the request body (depending on implementation). If authorizeRequest consumes the body, the subsequent request.arrayBuffer() returns an empty buffer, making signature verification impossible.

This was likely not an issue because authorizeRequest checks headers (Authorization header) rather than the body, but it's a latent bug if authorizeRequest ever changes to read the body.

Evidence:

  • src/app/api/pr-followup/webhook/route.ts line ~208: const auth = await authorizeRequest(request); (reads body? No — reads headers only)
  • src/app/api/pr-followup/webhook/route.ts line ~211: const rawBody = await request.arrayBuffer(); (reads body again)
  • Next.js web API allows reading Request body once — calling both would return empty for the second reader

Acceptance: Read raw body first (const rawBody = await request.arrayBuffer()), then authorizeRequest with a cloned copy or reconstruct the body from the buffer. Document that authorizeRequest must not consume the body.


Audit: last week's issue #616 missing required labels

The previous weekly audit issue (#616) was created without the required audit and status/backlog labels. It has no labels at all. This means it won't appear in label-based filtering for the audit workflow or the decomposer.

Evidence:

  • gh issue view 616 --json labels{"labels":[]} (no labels set)

Acceptance: Add audit and status/backlog labels to #616 retroactively, or close it and verify the creation pipeline adds labels.


Recommended Issue Breakdown

[P1] Address the stale @hono/node-server override

Problem: The @hono/node-server package override in package.json was added for a Prisma-hosted advisory that Prisma no longer depends on. The override comment is stale and misleading.

Evidence:

  • package.json — override block with stale comment referencing "Prisma transitive dependency"
  • npm ls @hono/node-server — only through @modelcontextprotocol/sdk, not Prisma

Acceptance: Remove @hono/node-server from overrides and update the comment to accurately describe the remaining postcss override's purpose. Verify with npm audit that no new advisories appear.

[P2] Prevent silent scheduler failure on non-default PORT

Problem: The in-app scheduler hardcodes 127.0.0.1:3000 for loopback POSTs, but the actual server port may differ (via PORT env or container runtime). All periodic jobs (sync, groomer, pr-followup, pruning) silently fail if the port doesn't match.

Evidence:

  • src/lib/scheduler.tsconst port = env.PORT?.trim() || "3000" but if PORT=8080, scheduler POSTs to 127.0.0.1:3000
  • DockerfileENV HOSTNAME=0.0.0.0 ensures binding but doesn't affect scheduling URL

Acceptance: Use 127.0.0.1:${port} consistently with port resolved from PORT env, or detect the actual listening port at startup. Add a startup health check that verifies the scheduler can reach its own endpoints.

[P2] Reconcile #618: close as declined or actually split github.ts

Problem: Issue #618 was closed with acceptance criteria unmet — github.ts is still 1049 lines and the domain facades are re-export wrappers that add no value. This creates confusion about what "extracted" means.

Evidence:

  • src/lib/github.ts — 1049 lines
  • 4 facade files with export * from "./github"

Acceptance: Either close #618 as "sufficient" and document the decision in the issue, or re-open it and split the module into domain files under 400 lines each.

[P3] Add tests for high-risk untested modules

Problem: automation-sync.ts (382 lines, sync orchestration logic with error handling), groomer-lock.ts (DB-backed concurrency lock), and resolve-actor.ts (audit attribution) lack test coverage. These are risk areas where a regression would cause data integrity issues.

Evidence:

  • src/lib/automation-sync.ts — 382 lines, no test file
  • src/lib/groomer/groomer-lock.ts — DB transaction with concurrent lock acquisition, no test
  • src/lib/resolve-actor.ts — audit trail attribution, no test

Acceptance: Add unit tests covering the main execution paths and error conditions for these three modules.

[P3] Prevent potential request body double-read in webhook handler

Problem: The PR follow-up webhook reads request.arrayBuffer() after authorization. If authorizeRequest ever consumes the body, the HMAC verification silently succeeds on an empty buffer.

Evidence:

  • src/app/api/pr-followup/webhook/route.tsauthorizeRequest(request) called before request.arrayBuffer()

Acceptance: Move the body read before authorization, or document that authorizeRequest must not consume the body. Add a test that verifies body integrity after authorization.

Metadata

Metadata

Assignees

No one assigned

    Labels

    auditAudit, review, or investigation work.status/doneWork is complete.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions