System: Example App (npm workspaces monorepo: api/ Hono on Cloud Run, frontend/ Next.js, 12 packages/*)
Date: 2026-11-04
Scope: whole repository except audit archive/. Not examined: runtime behaviour, production data.
Data handled: Google OAuth tokens and Google Ads access, user emails, Stripe payments (from the code; you said no other sensitive data).
Online checks (circular dependencies, outdated packages): run on 2026-11-04
Method: scanner (scripts/scan.sh) + manual verification + checklist + rubric. Not worked through item by item: references/checklist.md sections on API design and observability.
Example report from a real audit of an AI-built app. Names, identifiers and customer details have been changed.
Summary
The structure is healthier than the size suggests: 207 test files for 512 source files, no circular dependencies in any of 12 packages, and clear boundaries between api, frontend and packages. The risk is concentrated in a few very large functions in the workflow workers and in copy-pasted request handling across frontend API routes. Findings: 0 Critical, 2 High, 1 Medium, 3 Low. The biggest structural risk is the workflow executor files, which are large, change together, and sit on the money path.
| Critical | High | Medium | Low |
|---|---|---|---|
| 0 | 2 | 1 | 3 |
Measurements
- Size: 512 source files, 109,418 lines; 20
package.jsonfiles; 32 runtime and 13 dev dependencies. - Largest files:
packages/scraper/src/page-scraper.ts1,958;api/src/workers/run-executor.ts1,789;packages/scrape-enricher/src/index.ts1,593;api/src/workers/wf3-executor.ts1,569;frontend/src/app/dashboard/components/cycle-card.tsx1,380 lines. - Change hotspots (6 months, 125 commits):
wf3-executor.ts7;api/src/lib/stage-executor.ts6;frontend/src/app/dashboard/run-item.tsx5;api/src/lib/cycle-stage-seeds.ts5 (docs/session-handoff-*.md12 andpackages/shared/package.json5 are not code hotspots). - Files that change together (last 500 commits):
wf2-chunk-worker.tswithwf2-executor.ts15;wf4-executor.tswithwf5-executor.ts13;run-executor.tswithpackages/scraper/src/index.ts13;wf2-executor.tswithwf5-executor.ts12, withwf4-executor.ts12, withpackages/scrape-enricher/src/index.ts9. - Ownership: single maintainer (all 125 commits in six months carry one author identity, which differs from the current git user; confirm these are the same person). Reported once here, not per finding.
- Tests: 207 test files for 512 source files; 55 test files under
api/src/libandapi/src/workers. Which flows are covered was not mapped. - Circular dependencies: 0 found in the 12 packages and
api/frontendscanned. - Debt markers: 2 TODO/FIXME/HACK, 32 lint or type suppressions, 21 uses of
any. - Outdated: 5 of 12 (api), 5 of 17 (frontend), 6 of 25 (root) packages a major version or more behind.
Top 3 actions
- This month: break
executeRunand the wf3 coordinator/chunk/finalize functions into steps (H1). - This month: extract the repeated auth/error handling in frontend API routes into one helper (H2).
- This quarter: plan the major-version upgrades, starting with the ones npm audit flags (M1).
Findings
[H1] Workflow executors are huge, tightly coupled and change together — High
- Where:
api/src/workers/run-executor.ts(1,789 lines),wf3-executor.ts(1,569),wf2-executor.ts,wf4-executor.ts,wf5-executor.ts,wf2-chunk-worker.ts - Evidence: In
run-executor.ts,executeRunruns from line 60 to about line 1,560, one function of roughly 1,500 lines. In wf3,executeWf3FeedOptimizerCoordinatorInneris ~415 lines (198-615),ChunkInner~340 (718-1059),FinalizeInner~260 (1095-1357).wf3is the most-changed file in six months (7 commits). Six coupled pairs change together 9-15 times in 500 commits. A recent bug (a finalizer read truncated at 1,000 rows) was found in this file. - Severity reasoning: Cost B (hotspot, slows safe changes), Trend 1 (each new workflow or language option adds to the same functions). No adjustment (the credit ledger, which handles money, is a separate package).
- Cost in plain words: Every change to a workflow means re-reading a 1,500-line function; changing WF2 usually also means editing WF4 and WF5, so a missed spot becomes a regression in a paid run.
- Fix: First add characterization tests around
executeRuninputs and outputs. Then extract one phase at a time (scrape, enrich, estimate, persist, finalize) into functions in new files, without behaviour changes. After that, move code the WF2/4/5 executors share into one module. - Fix prompt:
In api/src/workers/run-executor.ts, identify the distinct phases inside executeRun (progress reporting, CrUX lookup, scrape, persistence, finalization). Without changing behaviour, extract the first phase only into api/src/workers/run-executor/<phase>.ts with an explicit input/output type, keeping executeRun as the caller. First add or confirm tests that pin current behaviour. One extraction per commit; run npm test in api after each. - Verify: Re-run the scanner:
run-executor.tsline count drops and the largest function is under ~300 lines; the existing test suite still passes.
[H2] Auth and error handling copy-pasted across frontend API routes — High
- Where:
frontend/src/app/api/**/route.ts(22 files with the same error line, 18 with the same auth status mapping, 12 with the same auth early-return) - Evidence: The scanner found
{ status: auth.error === "rate_limited" ? 429 : auth.error === "csrf_rejected" ? 403 : 401in 18 files andif (auth.error) return NextResponse.json({ error: auth.error }, ...in 12 files. - Severity reasoning: Cost C (no failure traced to it yet), Trend 1 (every new route copies it again), plus one level for the authentication path = High.
- Cost in plain words: A change to how authentication failures are reported (or a bug in it) must be made in 12-18 places; a missed copy leaves one route behaving differently on a security-relevant path.
- Fix: One helper
respondToAuthError(auth)(and ahandleRoutewrapper) infrontend/src/lib/auth, adopted route by route. - Fix prompt:
Add frontend/src/lib/auth/respond-auth-error.ts exporting respondToAuthError(auth) that returns the same NextResponse the routes build today (429 rate_limited, 403 csrf_rejected, otherwise 401). Replace the duplicated inline code in the three most recently changed route.ts files under frontend/src/app/api, keeping responses byte-identical, and add a unit test. Do not touch the other routes yet. - Verify: Re-run the scanner: the
DUPLICATED_LINEcount for these lines falls from 18/12 files toward 0.
[M1] Major-version drift in dependencies — Medium
- Where:
api/package.json,frontend/package.json, rootpackage.json - Evidence: 5 of 12, 5 of 17 and 6 of 25 outdated packages are a major version or more behind (scanner
OUTDATED_MAJOR).next16.2.2 is also flagged by npm audit. - Severity reasoning: Cost C, Trend 1 (the gap and upgrade pain grow each release). No adjustment.
- Cost in plain words: Delayed upgrades pile up and eventually must be done together, under pressure from a security advisory.
- Also reported in: SECURITY-AUDIT-REPORT.md, H1 and M1 (the security side of the same packages)
- Fix: Upgrade one major per week, starting with the packages that have advisories; list them with
npm outdated. - Fix prompt:
Run npm outdated in api/, frontend/ and the repo root. For each package that is a major version behind, read its changelog and list the breaking changes that affect this codebase, in order of risk. Do not upgrade anything yet. - Verify:
npm outdatedshows fewer majors behind.
[L1] Two very large scraper files that rarely change — Low
- Where:
packages/scraper/src/page-scraper.ts(1,958 lines),packages/scrape-enricher/src/index.ts(1,593 lines) - Evidence:
scrapePagespans lines 618-1333 (~715 lines);page-scraper.tsalso holds readability maths, image checks, redirect following and challenge detection. Only 3 commits in six months. - Severity reasoning: Cost C, Trend 2. No adjustment.
- Cost in plain words: Harder to understand and test in parts, but it is not causing trouble today.
- Fix: When next touching them, move pure helpers (
fleschKincaidGrade,checkImages,followRedirectChain) into their own files. - Fix prompt:
In packages/scraper/src/page-scraper.ts move the pure helpers fleschKincaidGrade, countSyllables, stripTags, extractFirst and extractAll into packages/scraper/src/text-utils.ts and import them back. No behaviour change; run the package tests. - Verify:
page-scraper.tsis shorter and package tests pass.
[L2] Large UI component and repeated markup and fetch calls — Low
- Where:
frontend/src/app/dashboard/components/cycle-card.tsx(1,380 lines);frontend/src/app/settings/prompt-settings/page.tsxand 8 other pages repeat the same nav bar markup; 11 UI files callfetchdirectly - Evidence: Scanner
LARGE_FILE,DUPLICATED_LINE(9 and 8 files),HTTP_IN_UI(11 files). - Severity reasoning: Cost C, Trend 2. No adjustment.
- Cost in plain words: Visual or API changes need edits in many places; not yet causing failures.
- Fix: Extract a shared
PageNavcomponent and a smallapi-client.ts; splitcycle-card.tsxby section when next changed. - Fix prompt:
Create frontend/src/components/page-nav.tsx with the nav markup repeated in frontend/src/app/settings/prompt-settings/page.tsx and the 8 other pages found by grepping for 'border-b border-gray-800 px-6 py-4 flex items-center justify-between'. Replace the duplicates, keeping rendered HTML identical. - Verify: The grep returns one result;
npm run buildpasses.
[L3] No README — Low
- Where: repository root (an
ARCHITECTURE.mdexists) - Evidence: Scanner
NO_README. - Severity reasoning: Cost C, Trend 2. No adjustment.
- Cost in plain words: A new person (or you after a break) lacks a start-here page for setup, running and deploying.
- Fix: A short README linking to
ARCHITECTURE.md, the deploy scripts and the runbooks. - Fix prompt:
Write README.md at the repo root: what Example App is, repo layout (api, frontend, packages), how to install, run and test locally, how to deploy (scripts/deploy-*.sh), and links to ARCHITECTURE.md and docs/runbooks. Base it only on files in the repo. - Verify:
README.mdexists and its commands work.
Refactoring plan
- Add characterization tests for
executeRunand the wf3 executor entry points. - Extract one phase from
executeRunper commit. - Do the same for the three wf3
...Innerfunctions. - Move code shared by WF2/4/5 executors into one module so the co-changing pairs stop co-changing.
- Add
respondToAuthErrorand adopt it route by route (H2). - Plan major upgrades (M1), one per week.
- Low items when touching the area.
Checked and found fine
- No circular dependencies in
api,frontendor the 10 packages checked. - Test coverage by file count is high (207 for 512 sources) and CI runs typecheck, build, tests and structure checks as blocking jobs (
.github/workflows/ci.yml). - Workspace package boundaries are guarded by
scripts/check-workspace-package-consistency.sh. - Debt markers are low (2 TODO/FIXME, 21
any).
Not checked or not verified
- Your answers to what is most painful to change and plans for the next 12 months (not given; "everything AI-written" noted).
- Whether the system map is correct (to be confirmed).
- Which critical flows the 207 tests actually cover.
- Whether the commit author and the git user are the same person.
Appendix: scanner output
SUMMARY|HIGH|GOD_FILE|8
SUMMARY|LOW|HTTP_IN_UI|1
SUMMARY|MEDIUM|CO_CHANGE|6
SUMMARY|MEDIUM|DUPLICATED_LINE|5
SUMMARY|MEDIUM|LARGE_FILE|8
SUMMARY|MEDIUM|NO_README|1
The full scanner output is in ARCHITECTURE-AUDIT-SCAN.txt.
This report is guidance produced with an AI assistant. It does not replace a review by an engineer who knows your business.