Skip to content

chore: audit remediation — dead code, real bugs, test gaps, doc drift#8

Merged
Koopa0 merged 31 commits into
mainfrom
chore/audit-remediation-2026-06-24
Jun 24, 2026
Merged

chore: audit remediation — dead code, real bugs, test gaps, doc drift#8
Koopa0 merged 31 commits into
mainfrom
chore/audit-remediation-2026-06-24

Conversation

@Koopa0

@Koopa0 Koopa0 commented Jun 24, 2026

Copy link
Copy Markdown
Owner

Summary

A full read-only audit (backend: 18 package/concern agents; frontend: 6 agents; every consequential finding independently re-derived from source) followed by gated remediation. 29 atomic commits, each verified by re-running the build/test gates — not subagent self-reports. No CRITICAL/HIGH from L1 (go-reviewer) or L2 (review-code).

Net: +2,090 / −9,751 across backend (Go) and frontend (Angular 22).

Dead code / tech debt (the bulk of the deletions)

  • Removed Server.registry (write-only field), dead types/sentinels, 18 zero-reference store methods + their orphaned sqlc queries (regenerated), and the never-supported search_knowledge project filter.
  • Frontend: pruned 33 unused design-system components and a dead graph API.

Real bugs

  • Graceful shutdown: catch SIGTERM (was SIGINT-only — Docker/VPS stops skipped the drain); drain background workers on a listener error instead of leaking them and closing the pool out from under in-flight queries.
  • goal CHECK violation → 400 (was a wrapped 500); narrowed PutPlan to the plan_day state allowlist (admin could plan done/someday/archived todos); reject control chars in admin content Create/Update including slug; surface review_note in the admin detail read; purge expired refresh tokens periodically.
  • Frontend: explicit SSR HTTP transfer cache (no hydration double-fetch); home-page error state (a failed load rendered a false "Nothing published yet"); removed the contradictory zone provider from the server bootstrap (zoneless target); command-palette focus trap; prerender static legal pages.

Test gaps filled (real coverage, not padding)

  • validateProposeContent table + fuzz; replaced the fictional feed handler test with real *feed.Handler integration coverage; replaced the stats DB-mock with testcontainers; todo interval-recurrence arithmetic; goal blank-title; auth refresh-token consume cycle (single-use / expiry / rotation); markdown XSS sanitization.
  • Deleted tautological / skipped-stub / DB-mock tests.

Doc drift

  • Corrected wrong route/param comments, references to removed features, and the Option-B attribution model in code comments.
  • README (EN + zh-TW): the "three-axis authorization (internal/mcp/authz.go)" paragraph described a removed Option-A gate in a non-existent file — corrected to the actual transport-boundary + attribution model; fixed the stale tool count (14 → 15) and added the missing set_todo_recurrence.

Test Plan

Backend (all green, run locally):

  • go build ./... · go vet ./... · golangci-lint run ./...0 issues
  • go test ./... (unit) → all pass
  • go test -tags integration ./...all 22 packages pass (real PostgreSQL via testcontainers)

Frontend (all green):

  • ng build · ng lint → pass · ng test570/570 pass

Review:

  • L1 go-reviewer + L2 review-code independent passes: 0 CRITICAL, 0 HIGH. Surfaced items (errors.Join on the shutdown path; admin slug control-char gap) addressed with tests.

Koopa0 added 29 commits June 24, 2026 15:37
The registry field was injected via WithRegistry and stored but never
dereferenced: callerIdentity resolves the caller from the as value alone
(Option B — attribution only, no tool-layer authz). The accompanying doc
comments described a caller-resolution gate that does not exist. The agent
roster sync that genuinely needs a registry runs in cmd/mcp/main.go via
agent.SyncToTable on its own agentRegistry, untouched here.
- activity.DiffStats: never constructed, serialized, or read (Event.Metadata
  carries the GitHub payload as raw json.RawMessage).
- activity.ErrNotFound / ErrConflict: no handler branches on them and the
  package is read-only (two SELECT :many queries), so neither condition arises.
- agent.RegistryRow.SyncedAt / RetiredAt: populated from the DB but read by no
  consumer; the SQL columns stay (the upsert/retire SQL maintains them).
- todo.ProjectCompletion: type referenced nowhere in the tree.
Residue from retired features (learning subsystem, rss-highlight aggregates).
Each removed method had zero callers tree-wide; each removed query had no
remaining s.q.* consumer after the method went. Regenerated sqlc.

- feed/entry: RecentFeedEntries, LatestFeedEntries, TopUnreadFeedEntriesRecent,
  DeleteOldIgnored, TopItems (+ their queries)
- feed: EnabledFeeds no-schedule variant (kept EnabledFeedsBySchedule)
- todo: PendingItemsByTitle, PendingItems, ItemsCreatedSince, StaleSomedayCount,
  InboxCount, InboxItems, PendingItemsWithProject (+ queries + orphaned Pending type)
- goal: legacy CreateGoal 7-arg, bare GoalByID, GoalByTitle, CreateMilestoneSimple
  (kept shared CreateGoal/GoalByID/CreateMilestoneWithPosition queries)
- daily: ItemByID method + query + dead ErrNotFound sentinel

Shared queries still consumed by live code were deliberately preserved.
signal.NotifyContext registered only os.Interrupt (SIGINT), which arrives
from a TTY Ctrl-C. Docker/VPS deployment (CLAUDE.md tech stack) stops
containers with SIGTERM, which was not caught — so production stops would
skip the drain path and hard-kill mid-request/mid-pass. Register SIGTERM
alongside SIGINT in all three entrypoints (app run, app embed-backfill, mcp).
The run() select returned immediately on the errCh branch, before
srv.Shutdown and wg.Wait. The scheduler and reconciler goroutines select on
ctx, which the errCh branch never cancels (only a signal does), so they kept
running while the deferred pool.Close() closed the pool out from under their
in-flight queries. Cancel ctx via stop() on the error branch so both workers
observe cancellation and drain before the pool closes; the captured error is
still returned.
mapWriteError mapped only unique (23505) and foreign-key (23503) violations,
so a blank title hitting chk_goal_title_not_blank (23514) fell through to a
wrapped 500. The sibling mapProposeError already maps CheckViolation to
ErrInvalidInput; mirror it so the owner create/update paths return 400 for a
blank title. A handler-level trim+reject and the asserting integration case
follow in the goal test pass.
…lter

The Project input field was declared in the wire schema only to be rejected
as unsupported_filter on every non-empty value — the canonical catalog never
advertised it and content.SearchFilter has no project dimension. Per the
owner's call, drop the reserved field (and its reject-stub validator + test)
rather than carry a parameter that can only ever error. The result struct's
Project (parent project title) is unrelated and kept.
The HTTP plan-write guard rejected only inbox, so the admin API accepted a
done/someday/archived todo onto today's plan while the MCP plan_day tool
(execution.go) rejected the same todo via a todo|in_progress allowlist —
despite PutPlan's doc calling itself the human equivalent of plan_day. Mirror
the MCP allowlist and fix the doc. Integration test seeds a someday todo and
asserts 400 + zero plan rows (real Postgres).
The MCP write path (propose_content/revise_content) and admin SendBack already
reject control characters, but admin Create/Update validated only length, type,
and status — so a control char smuggled in via a JSON \u escape reached the DB
through the admin boundary while the MCP boundary caught it. Apply the same
strict-for-title/excerpt, prose-for-body check to both handlers, matching the
input-validation-checklist handler-consistency rule. Table-driven tests assert
a 400 per field.
ConsumeRefreshToken only deletes the exact presented token, so expired-but-
unconsumed rows accumulated forever — the expires_at index and the schema
'eligible for cleanup' comment promised a sweep that did not exist. Add
DeleteExpiredRefreshTokens (served by that index) and a background worker
wired through the same wg.Go/ctx drain as the scheduler and reconciler: it
purges once at startup, then hourly, and exits on ctx cancellation. Integration
test asserts only the expired token is removed.
The owner's review_note (set when sending content back for changes) was
selected by ContentsByCreator (the MCP list_content readback) but not by
ContentByID, so reopening a changes_requested item in admin showed no note.
Add review_note to the ContentByID query and hydrate Content.ReviewNote in the
detail read. ListContents is left unchanged (owner decided detail-read only).
Integration test sends a draft back with a note and asserts ContentByID
returns it.
…al pages, zoneless server

- app.config.ts: make the HTTP transfer cache explicit via
  withHttpTransferCacheOptions so Server-rendered pages do not refetch their
  API data on hydration (the v22 default, now self-documented; withHttpTransferCache
  does not exist in v22).
- app.routes.server.ts: prerender /privacy and /terms (static legal text) instead
  of paying per-request SSR via the ** fallback, matching /about.
- main.server.ts: drop provideZoneChangeDetection — it contradicted the zoneless
  browser config and required zone.js, which is not installed; the server now
  inherits provideZonelessChangeDetection from the shared config.
home had only a loading flag, so a failed content/topics load fell through to
the @else branch and rendered the false empty-state 'Nothing published yet' — a
backend outage looked like 'the author published nothing'. Add a hasError
computed (mirroring the articles page) and an error/retry branch, and gate the
recent band on !hasError. Spec flushes a 500 and asserts the error UI renders.
The palette is role=dialog aria-modal but had no focus trap, so Tab escaped to
the background page. Add cdkTrapFocus + cdkTrapFocusAutoCapture (A11yModule),
mirroring the modal component, and mark the backdrop aria-hidden.
…dead graph API

The shared/components barrel held a Claude Design ingest of 41 components; 33
had zero template usage and zero TS imports anywhere in the app (this app is a
consumer, not a component-library producer). Delete them and trim the barrel to
the 8 actually used (data-table, empty-state, energy-meter, form-field, kbd,
loading-spinner, modal, status-badge). Also remove content.service.getKnowledgeGraph
and its now-orphaned ApiKnowledgeGraph/ApiGraphNode/ApiGraphLink types (zero callers).
…ic-page specs

markdown.service binds DOMPurify output to [innerHTML] but had no sanitization
test — add cases asserting a <script> tag, an onerror attribute, and a
javascript: scheme are stripped from rendered output. Delete the about/privacy/
terms specs whose entire body was expect(component).toBeTruthy() (zero-logic
static pages).
validateProposeContent gates untrusted agent input (required title/type/body,
content-type enum, C0/C1 control-char rejection per field, slug derivation,
topic-UUID parsing) and had no direct test. Add a table-driven test naming the
bug each case catches, plus fuzz over the slug-derivation and parseTopicIDs
paths (must not panic). The C1 case uses string(rune(0x80)) — a raw \x80 byte
is invalid UTF-8 and would decode to RuneError, never exercising the C1 check.
…erage

scheduler_test.go drove a hand-rolled feedTestHandler — a parallel
reimplementation of the production handler (via a test-only feedHandlerStore
interface) that had diverged, so the real handler's behavior was asserted
nowhere. Delete feedTestHandler/feedHandlerStore and the TestFeedHandler_*
tests, plus the TestMaxConsecutiveFailures const==5 tautology. Add real
*feed.Handler Create/Update/Delete/Fetch tests and an IncrementFailure
auto-disable test against a testcontainers store. Extract the scheduler's
due predicate into a pure due() function (zero behaviour change — the skip
condition is identical) and unit-test it directly via TestDue.
…ation

stats_test.go hand-rolled a db.DBTX fake (stubDBTX/emptyRows/zeroRow) and the
handler tests asserted zero-in/zero-out through it — a forbidden DB mock that
verified nothing about the real SQL aggregation. Delete the fake and those
tests; add an integration test seeding real rows and asserting Overview/
SystemHealth/ProcessRuns/Drift produce correct non-zero counts. Extract the
days-clamp into a pure parseDays() (zero behaviour change) and keep the
clamp/fuzz + computeAreaDrift + state-mapper unit tests against it.
The only interval-recurrence seed had last_completed_on=NULL (the trivial
branch), so the interval-boundary computation was untested. Add cases with
last_completed_on exactly interval-days ago (due), interval-1 days ago (not
due), and weeks/months units, with hand-computed dates.
Pins the CHECK-violation mapping fix: a whitespace-only title hits
chk_goal_title_not_blank (23514) and must surface as ErrInvalidInput (400),
not a wrapped 500.
The security-critical persistence half of auth had no test. Add consume
happy-path, double-consume returns ErrNotFound (single-use), expired-token
rejection, and rotation (old hash gone). Delete the skipped stub
TestRefresh_InvalidToken_ReturnsUnauthorized, which asserted nothing while
claiming coverage that now actually exists.
- content: TestRegression_BySlug_PrivateContentReturns404 only asserted a
  Content zero value has IsPublic==false — a struct-zero tautology that never
  invokes the handler (real coverage is TestHandler_PublicBySlug_HidesNonPublic).
- api: TestStatusCapturingWriter_Decision re-implemented the commit predicate
  in the test body and compared it to itself; TestActorMiddleware_TxAvailableInContext
  asserted only that context.WithValue round-trips, not the middleware (the real
  wiring is covered by the integration test's ActorMiddleware+TxFromContext path).
Comment-only fixes where the comment contradicted the code (code is authority):
- stats/topic/todo handler doc comments named wrong routes / query params
  (per_page not limit; /api/admin/system/stats*; /api/admin/knowledge/topics).
- ops/types.go Destructive example referenced the removed learning-plan feature.
- stats process_runs.kind comments claimed 'agent_schedule' but the CHECK allows
  only 'crawl'.
- migrations refresh_tokens.token_hash comment said 'Bcrypt or SHA256'; the code
  only stores a SHA256 base64url hash.
- mcp.go package doc described a per-mutation authz gate that does not exist
  (Option B is attribution-only; access control is the MCP transport).
- brief.go/catalog.go 'tasks' section docs omitted the emitted recurring_todos.
- api integration test request path literal matched the real content route.
TestIntegration_SearchKnowledge_ProjectRejected exercised the search_knowledge
project field removed earlier; it is dead now that the field is gone. The
field-removal commit dropped the unit test but missed this integration one
(behind the integration build tag, so the unit gate did not catch it).
The error path overwrote a captured listener error with a shutdown error only
when the former was nil, silently dropping one of two independent failures.
Use errors.Join so both surface (errors.Join(nil, x) is just x).
Create/Update validated control chars in title/excerpt/body but not slug, which
becomes a URL path segment. The DB chk_content_slug_format CHECK rejects
whitespace and slashes but not non-whitespace control chars (C0-non-ws, DEL,
C1), so a control-char slug passed both the handler and the DB. Add slug to the
strict control-char check (which still allows the intentionally-supported
Unicode/CJK slugs). Surfaced by L2 review; tests cover slug rejection in
Create and Update.
- feed: rename the unexported due() predicate to isDue() (clearer boolean
  predicate; avoids a future name collision in the package).
- content: co-locate containsProseControlChars with containsControlChars and
  checkContentControlChars so the three control-char helpers sit together (they
  cross-reference each other; this stops them drifting 375 lines apart).
- content: drop the dead wantTag table field and the obsolete 'tag' filter test
  case + benchmark param (tags were removed in favour of topics; parsePublicFilter
  no longer parses tag).
Both READMEs described a three-axis authorization gate in a non-existent
internal/mcp/authz.go — the removed Option-A design. The actual model is
Option B: as is attribution only (created_by / activity actor / caller-scope),
and access control is the MCP transport (admin-email OAuth + bearer on HTTP
/mcp; OS process boundary on stdio); a fabricated as is caught by the created_by
FK. Also: the tool count was stale (fourteen → fifteen) and the toolset table
omitted set_todo_recurrence.
@augmentcode

augmentcode Bot commented Jun 24, 2026

Copy link
Copy Markdown

This pull request is abnormally large and would use a significant amount of tokens to review. If you still wish to review it, comment "augment review" and we will review it.

Koopa0 added 2 commits June 24, 2026 17:26
The doc-comment pass edited the refresh_tokens.token_hash column comment and
the StatsProcessRunsByStatus query comment; sqlc propagates SQL comments into
the generated Go doc comments, so the generated files needed regeneration.
Comment-only — no type or query change.
The hero narrative led with a planner-on-Cowork morning briefing as if it ran
daily; the actual drivers today are Koopa (decisions/router), Claude Code (dev
sessions), and hermes (Obsidian curation). Lead with those three, present the
planner as the daily-driver the system is designed around — wired but executed
by an external scheduler, not yet a daily habit — and reorder the roster to put
the active actors first. Roster facts unchanged (all agents are registered);
this is emphasis, matching the 'README describes what-is' rule. EN + zh-TW.
@Koopa0 Koopa0 merged commit 26538e5 into main Jun 24, 2026
5 checks passed
@Koopa0 Koopa0 deleted the chore/audit-remediation-2026-06-24 branch June 24, 2026 09:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant