docs: refresh codebase map
This commit is contained in:
+146
-108
@@ -1,178 +1,216 @@
|
||||
# Codebase Concerns
|
||||
|
||||
**Analysis Date:** 2026-04-18
|
||||
**Analysis Date:** 2026-04-20
|
||||
|
||||
## Tech Debt
|
||||
|
||||
**Oversized mixed-responsibility modules:**
|
||||
- Issue: Data access, cache policy, retry logic, query construction, and response shaping are combined in single files instead of being split into smaller modules.
|
||||
- Issue: Database access, retry policy, cache fallback, query normalization, and response shaping are combined in the same modules instead of being split into smaller server-side layers.
|
||||
- Files: `src/hooks/useProjects.ts`, `src/hooks/useHome.ts`, `src/app/[locale]/projects/ProjectsResultsClient.tsx`, `src/components/project/TagFilterPanel.tsx`, `src/components/signals/SignalFeedClient.tsx`, `src/app/[locale]/layout.tsx`
|
||||
- Impact: Safe changes require understanding many unrelated branches at once; regressions are likely because query behavior, cache behavior, and UI state are tightly coupled.
|
||||
- Fix approach: Split server-side data access from presentation and client state; isolate URL/query builders, Prisma queries, cache wrappers, and presentational subcomponents into separate files before adding more behavior.
|
||||
- Impact: Safe changes require understanding unrelated concerns at once, so regressions in caching, pagination, or rendering are easy to introduce.
|
||||
- Fix approach: Split server data access into `src/lib/` or `src/server/`, keep client state in client components only, and extract URL/query helpers plus cache wrappers into dedicated modules.
|
||||
|
||||
**Misleading server utilities under `hooks/`:**
|
||||
- Issue: `src/hooks/useProjects.ts` and `src/hooks/useHome.ts` are not React hooks. They contain Prisma queries, `unstable_cache`, retry logic, and server-only behavior.
|
||||
**Server-only data modules are mislabeled as hooks:**
|
||||
- Issue: `src/hooks/useProjects.ts` and `src/hooks/useHome.ts` are not React hooks. They contain Prisma queries, `unstable_cache`, retry logic, and server-only fallback behavior.
|
||||
- Files: `src/hooks/useProjects.ts`, `src/hooks/useHome.ts`
|
||||
- Impact: The naming invites accidental client imports and makes server/client boundaries harder to reason about during refactors.
|
||||
- Fix approach: Move these modules to a server-oriented location such as `src/lib/` or `src/server/`, then keep React hooks in `src/hooks/` only.
|
||||
- Impact: The naming invites accidental client imports and hides the real server/client boundary during refactors.
|
||||
- Fix approach: Move these modules to a server-oriented location and reserve `src/hooks/` for actual React hooks.
|
||||
|
||||
**Schema-drift tolerance around signal hotness columns:**
|
||||
- Issue: The application carries compatibility code for `hotScore` and `isHot` column absence even though the Prisma schema declares both fields.
|
||||
- Files: `src/lib/signal-hotness.ts`, `src/app/api/signals/route.ts`, `src/app/api/webhook/signals/route.ts`, `prisma/schema.prisma`, `prisma/migrations/20260224120000_add_signal_hot_fields/migration.sql`
|
||||
- Impact: Deployments can run with partially applied migrations without failing fast. That reduces blast radius in production but also hides schema drift and makes behavior environment-dependent.
|
||||
- Fix approach: Treat missing columns as deployment errors after rollout is stable, or move the compatibility branch behind an explicit feature flag with clear removal criteria.
|
||||
**n8n workflow state is documented, but executable exports are still missing:**
|
||||
- Issue: The repository now carries rich n8n metadata and generated context, but `docs/integrations/n8n/exports/` contains only `.gitkeep` and every workflow entry currently records `Export file: not recorded`.
|
||||
- Files: `docs/integrations/n8n/README.md`, `docs/integrations/n8n/registry.json`, `docs/integrations/n8n/CONTEXT.generated.md`, `docs/integrations/n8n/exports/.gitkeep`, `scripts/generate-n8n-context.mjs`
|
||||
- Impact: The repo captures contracts and touchpoints, but not the actual workflow logic. Incident response, review, and reproducibility still depend on external n8n access.
|
||||
- Fix approach: Commit workflow exports alongside `registry.json`, treat them as versioned artifacts, and keep `pnpm n8n:context` as a verification step instead of the primary source of truth.
|
||||
|
||||
**Boot-time failure for AI search route configuration:**
|
||||
- Issue: The AI search route reads `process.env.N8N_AI_SEARCH_WEBHOOK!` at module scope and throws immediately if the variable is missing.
|
||||
- Files: `src/app/api/search/ai/route.ts`
|
||||
- Impact: A missing env var breaks route initialization instead of returning a controlled runtime error. This is fragile in local setup, preview deployments, and tests.
|
||||
- Fix approach: Read the env var inside the request handler, return a structured `500`, and cover the missing-env path with tests.
|
||||
**Repository instructions drift from the actual toolchain:**
|
||||
- Issue: `AGENTS.md` documents `pnpm test:e2e` and an `e2e/` suite, but `package.json` has no `test:e2e` script and the repo contains no `playwright.config.*` or `e2e/` directory.
|
||||
- Files: `AGENTS.md`, `package.json`
|
||||
- Impact: Contributors can assume browser-level regression coverage exists when it does not.
|
||||
- Fix approach: Either add the documented Playwright harness or remove the claim from `AGENTS.md` so verification expectations match reality.
|
||||
|
||||
## Known Bugs
|
||||
|
||||
**Related projects are not selected from the actual related set:**
|
||||
- Symptoms: The project detail page fetches only the latest three projects and then filters that tiny set for shared tags.
|
||||
**Related projects are picked from the newest three projects, not the actual related set:**
|
||||
- Symptoms: The project detail page fetches `getProjects({ limit: 3 })` and then filters that tiny subset for shared tags.
|
||||
- Files: `src/app/[locale]/projects/[id]/page.tsx`, `src/hooks/useProjects.ts`
|
||||
- Trigger: Open a project whose related items are not in the newest three active projects.
|
||||
- Workaround: None in code. The page simply renders fewer or zero related projects.
|
||||
- Trigger: Open a project whose genuinely related items are not among the newest three active projects.
|
||||
- Workaround: None in code. The page simply shows fewer or zero related projects.
|
||||
|
||||
**Interactive navigation and CTA elements are placeholders:**
|
||||
- Symptoms: Several visible controls navigate to `#`, the mobile menu button has no behavior, and the newsletter form has no action handler.
|
||||
**AI search route crashes at module initialization when its env var is missing:**
|
||||
- Symptoms: Importing the route throws before handling a request because `process.env.N8N_AI_SEARCH_WEBHOOK` is read at module scope and hard-fails.
|
||||
- Files: `src/app/api/search/ai/route.ts`
|
||||
- Trigger: Start the app, build, or run tests without `N8N_AI_SEARCH_WEBHOOK` configured.
|
||||
- Workaround: Provide the env var in every environment that loads the route.
|
||||
|
||||
**Visible navigation and conversion entry points are placeholders:**
|
||||
- Symptoms: Submit-project, footer resource/legal links, social links, and the newsletter form do not connect to real destinations or handlers. The mobile menu button also has no behavior.
|
||||
- Files: `src/app/[locale]/layout.tsx`, `src/components/project/ProjectCard.tsx`
|
||||
- Trigger: Click `submitProject`, footer/legal/social links, the mobile menu button, or submit the newsletter form.
|
||||
- Workaround: None in code. Users stay on the same page or submit a form with no integration.
|
||||
- Trigger: Click the submit CTA, footer links, social buttons, or submit the newsletter form on any localized page.
|
||||
- Workaround: None in code. Users stay on the same page or submit to a no-op form target.
|
||||
|
||||
## Security Considerations
|
||||
|
||||
**Admin and ingestion endpoints rely on a single shared API key in request bodies:**
|
||||
- Risk: The code checks only `apiKey` equality. There is no request signature, timestamp, nonce, replay protection, or rate limiting in application code.
|
||||
- Files: `src/lib/auth.ts`, `src/app/api/webhook/signals/route.ts`, `src/app/api/tags/maintenance/route.ts`, `src/app/api/tags/reset-projects/route.ts`
|
||||
- Current mitigation: Timing-safe comparison via `crypto.timingSafeEqual` in `src/lib/auth.ts`
|
||||
- Recommendations: Prefer HMAC-signed requests or provider-native webhook signatures, reject stale timestamps, add rate limiting, and keep network allowlisting outside the app if that is part of deployment.
|
||||
**Admin and ingestion endpoints depend on a single shared API key passed in request bodies:**
|
||||
- Risk: The code checks only equality against `WEBHOOK_API_KEY`. There is no request signature, timestamp, nonce, replay protection, or application-side rate limiting.
|
||||
- Files: `src/lib/auth.ts`, `src/app/api/webhook/signals/route.ts`, `src/app/api/tags/maintenance/route.ts`, `src/app/api/tags/reset-projects/route.ts`, `docs/integrations/n8n/CONTEXT.generated.md`, `docs/integrations/n8n/workflows/07-signals-aggregation.md`, `docs/integrations/n8n/workflows/08-project-tag-reset.md`
|
||||
- Current mitigation: `src/lib/auth.ts` uses `crypto.timingSafeEqual`, and the n8n docs call out the shared-secret problem explicitly.
|
||||
- Recommendations: Move to HMAC-signed requests or provider-native webhook verification, reject stale timestamps, and apply rate limiting at the app or edge layer.
|
||||
|
||||
**AI search leaks query text through URL-based webhook forwarding:**
|
||||
- Risk: The route forwards user search text and filters to an external service with a `GET` request query string.
|
||||
- Files: `src/app/api/search/ai/route.ts`
|
||||
**AI search forwards user queries to n8n in a GET query string:**
|
||||
- Risk: Search text and filters are serialized into the webhook URL, which is more likely to be logged by reverse proxies, platforms, and third-party tooling.
|
||||
- Files: `src/app/api/search/ai/route.ts`, `docs/integrations/n8n/workflows/06-rag-project-search.md`
|
||||
- Current mitigation: Not detected in code.
|
||||
- Recommendations: Send the payload with `POST`, avoid placing search text in URLs, and document the data handling expectations for the external n8n workflow.
|
||||
- Recommendations: Switch to `POST`, move request data into the body, and document the retention/logging expectations for the n8n side.
|
||||
|
||||
**Raw project markdown can load third-party images:**
|
||||
- Risk: Markdown rendering sanitizes HTML, but image URLs still render through plain `<img>` elements. If project content is not fully trusted, remote images can leak user IPs and referrers to arbitrary hosts.
|
||||
**Runtime secret handling spreads certificate material and credentials outside the repo boundary:**
|
||||
- Risk: Database TLS assets are reconstructed from env vars onto disk at runtime and the client identity password is appended to the Prisma datasource URL. The n8n docs also confirm workflows that need app secrets or raw database access outside this repository.
|
||||
- Files: `src/lib/prisma-url.ts`, `src/lib/prisma.ts`, `docs/integrations/n8n/CONTEXT.generated.md`, `docs/integrations/n8n/workflows/04-github-star-refresh.md`, `docs/integrations/n8n/workflows/05-project-description-vectorization.md`
|
||||
- Current mitigation: `src/lib/prisma-url.ts` writes files with restrictive permissions and uses an environment-configurable certificate directory.
|
||||
- Recommendations: Prefer mounted secrets over reconstructing certs in temp storage, keep passwords out of DSN strings where possible, scope database users per workflow, and document secret ownership/rotation outside the app.
|
||||
|
||||
**Raw markdown content can load remote images from arbitrary hosts:**
|
||||
- Risk: Markdown rendering sanitizes HTML, but it still renders remote `<img>` URLs directly. If project content is not fully trusted, remote hosts can observe client IPs and referrers.
|
||||
- Files: `src/components/project/MarkdownContent.tsx`
|
||||
- Current mitigation: `rehype-sanitize` removes unsafe HTML, and external links use `rel="noopener noreferrer"`.
|
||||
- Recommendations: Proxy images, restrict allowed image hosts, or disable markdown images for untrusted content. This concern depends on whether project content is curated or user-submitted; the code alone does not establish that trust boundary.
|
||||
- Current mitigation: `rehype-sanitize` removes unsafe HTML and external links use `rel="noopener noreferrer"`.
|
||||
- Recommendations: Proxy images, restrict allowed hosts, or disable markdown image rendering for untrusted content.
|
||||
|
||||
## Performance Bottlenecks
|
||||
|
||||
**Signal ingestion performs per-item existence checks and upserts sequentially:**
|
||||
- Problem: Each signal performs validation, `findUnique`, and `upsert` inside a loop. The route processes up to 100 signals per request and does not batch database writes.
|
||||
- Problem: Each signal performs validation, `findUnique`, and `upsert` inside a loop, with optional retry into a second upsert path when hotness columns are unavailable.
|
||||
- Files: `src/app/api/webhook/signals/route.ts`
|
||||
- Cause: The ingestion path is written for straightforward correctness and partial failure reporting, not throughput.
|
||||
- Improvement path: Preload existing records in bulk, batch inserts/updates where possible, and use a transaction or job queue if partial writes are unacceptable.
|
||||
- Cause: The route is optimized for straightforward per-item error reporting, not for throughput.
|
||||
- Improvement path: Bulk-load existing keys, batch inserts/updates, and move high-volume ingestion to a queue or transactional batch writer.
|
||||
|
||||
**Project and signal text search use `contains` scans without matching full-text indexes:**
|
||||
- Problem: Search endpoints query multiple text fields with case-insensitive `contains`, but the Prisma schema indexes only sorting/filter columns such as `slug`, `status`, `createdAt`, `githubStars`, `publishedAt`, `hotScore`, and `engagement`.
|
||||
**Project and signal search rely on case-insensitive `contains` scans instead of search-specific indexes:**
|
||||
- Problem: Search touches multiple text fields with `contains`, while `prisma/schema.prisma` indexes sorting/filter columns but no text-search structures.
|
||||
- Files: `src/hooks/useProjects.ts`, `src/app/api/signals/route.ts`, `prisma/schema.prisma`
|
||||
- Cause: Search logic is application-level string matching with no dedicated full-text search index.
|
||||
- Improvement path: Add PostgreSQL full-text search or trigram indexes for project and signal search fields, or move search to a dedicated search service.
|
||||
- Cause: Search is implemented as application-level substring matching over Prisma filters.
|
||||
- Improvement path: Add PostgreSQL full-text or trigram indexes, or move search to a dedicated retrieval service.
|
||||
|
||||
**Silent fallback paths can cache outage-shaped responses:**
|
||||
- Problem: Several server-side queries catch database errors and return empty arrays or zero counts instead of surfacing failure.
|
||||
- Files: `src/hooks/useProjects.ts`, `src/hooks/useHome.ts`
|
||||
- Cause: The code prefers graceful degradation, combined with `unstable_cache` wrappers in the same modules.
|
||||
- Improvement path: Separate fallback behavior from cached fetchers, emit structured telemetry, and avoid caching degraded empty results for homepage and listing data.
|
||||
|
||||
**AI search hydrates external results with local DB lookups and in-memory reordering:**
|
||||
- Problem: The AI route requests candidate IDs from n8n, fetches projects from the database, then matches records back to the remote order with repeated `find` calls.
|
||||
**AI search does extra in-memory filtering and O(n²) result hydration after the webhook call:**
|
||||
- Problem: `src/app/api/search/ai/route.ts` fetches candidate IDs from n8n, loads projects from the database, repeatedly calls `projects.find(...)` to restore ranking order, and then re-applies tag/domain/product-form filtering in memory.
|
||||
- Files: `src/app/api/search/ai/route.ts`, `src/hooks/useProjects.ts`
|
||||
- Cause: Remote ranking and local hydration are stitched together in the route layer instead of a dedicated search service.
|
||||
- Improvement path: Preserve order with an ID-to-project map, keep pagination logic on one side, and avoid fetching more rows than the final page requires.
|
||||
- Cause: Responsibility is split awkwardly between n8n ranking and route-side post-processing.
|
||||
- Improvement path: Use an ID-to-project map, keep filtering ownership on one side of the contract, and avoid loading more rows than the final page needs.
|
||||
|
||||
**Graceful-degradation fallbacks can return empty or zero-shaped data under DB failure:**
|
||||
- Problem: Homepage and project metadata helpers catch database errors and degrade to empty arrays or zero counts.
|
||||
- Files: `src/hooks/useProjects.ts`, `src/hooks/useHome.ts`
|
||||
- Cause: The code prefers availability over surfacing failures and uses the same modules for cached and uncached access.
|
||||
- Improvement path: Separate failure-aware fetchers from UI fallback shaping, emit explicit telemetry, and avoid treating outage-shaped responses as normal product data.
|
||||
|
||||
## Fragile Areas
|
||||
|
||||
**Projects results UI has duplicated URL, pagination, and fetch state machines:**
|
||||
**Projects results UI maintains two overlapping state machines in one client component:**
|
||||
- Files: `src/app/[locale]/projects/ProjectsResultsClient.tsx`, `src/app/api/projects/route.ts`, `src/app/api/search/ai/route.ts`
|
||||
- Why fragile: Traditional search and AI search maintain separate state, pagination, URL sync, and fetch flows inside one client component. The file also manually uses `window.history.replaceState`, which is easy to desynchronize from server-rendered state.
|
||||
- Safe modification: Change one mode at a time, verify deep-linking and pagination after every edit, and extract shared query/pagination logic before adding more filters or sorts.
|
||||
- Why fragile: Traditional search and AI search keep separate pagination, sort, loading, error, and URL-sync state inside the same file. The component also mutates the browser URL with `window.history.replaceState`, which is easy to desynchronize from server-rendered state.
|
||||
- Safe modification: Change only one search mode at a time, verify deep-linking after every edit, and extract shared query-state helpers before adding more filters or sorts.
|
||||
- Test coverage: No tests detected for `src/app/[locale]/projects/ProjectsResultsClient.tsx`, `src/app/api/projects/route.ts`, or `src/app/api/search/ai/route.ts`
|
||||
|
||||
**Tag maintenance endpoints mix validation, mutation, and cache invalidation in request handlers:**
|
||||
- Files: `src/app/api/tags/maintenance/route.ts`, `src/app/api/tags/maintenance/service.ts`, `src/app/api/tags/reset-projects/route.ts`
|
||||
- Why fragile: Business rules, Prisma writes, per-project iteration, and `revalidatePath` calls are tightly coupled. Bulk operations can change many rows and many pages in one request.
|
||||
- Safe modification: Keep schema validation and mutation rules under tests, preserve transaction boundaries, and review all `revalidatePath` targets before changing route semantics.
|
||||
- Test coverage: Route and service tests exist for these files, but there are no broader integration tests across Prisma, cache invalidation, and localized page rendering.
|
||||
**Direct database-writing n8n jobs bypass repository API routes and cache invalidation:**
|
||||
- Files: `docs/integrations/n8n/CONTEXT.generated.md`, `docs/integrations/n8n/workflows/04-github-star-refresh.md`, `docs/integrations/n8n/workflows/05-project-description-vectorization.md`, `prisma/schema.prisma`, `src/hooks/useProjects.ts`, `src/app/api/search/ai/route.ts`
|
||||
- Why fragile: The documented star-refresh and vectorization workflows write straight to Postgres instead of going through repository routes. That bypasses app-level validation, audit points, and any future route-based revalidation logic.
|
||||
- Safe modification: Treat the Prisma schema and n8n registry as one shared contract, and change database columns/indexes only with coordinated workflow updates plus runtime verification.
|
||||
- Test coverage: No contract or integration tests detected for these cross-system write paths
|
||||
|
||||
**Shared layout contains production UI plus unfinished placeholders:**
|
||||
- Files: `src/app/[locale]/layout.tsx`
|
||||
- Why fragile: The same layout file owns metadata, locale setup, navigation, announcement bar, newsletter section, footer, and placeholder interactions.
|
||||
- Safe modification: Extract navigation, newsletter, and footer into dedicated components before wiring real integrations.
|
||||
- Test coverage: No tests detected for this file.
|
||||
**The project ingestion loop is not self-contained in this repository:**
|
||||
- Files: `docs/integrations/n8n/DATAFLOW.md`, `docs/integrations/n8n/CONTEXT.generated.md`, `docs/integrations/n8n/workflows/01-topic-discovery.md`, `docs/integrations/n8n/workflows/02-github-trending-discovery.md`, `docs/integrations/n8n/workflows/03-project-ingestion-multi-source.md`
|
||||
- Why fragile: The docs explicitly depend on `/api/discovery/check-duplicates` and `/api/discovery/tasks*`, but there is no `src/app/api/discovery/` implementation in this repo. The repo depends on an upstream discovery service to stay operational.
|
||||
- Safe modification: Treat discovery endpoints as an external contract, version their request/response shapes, and avoid assuming local end-to-end reproducibility for ingestion work.
|
||||
- Test coverage: No in-repo tests can cover the full discovery-to-ingestion flow because the required service is absent here
|
||||
|
||||
**Bulk tag mutation endpoints couple validation, writes, and cache invalidation at the route layer:**
|
||||
- Files: `src/app/api/tags/maintenance/route.ts`, `src/app/api/tags/maintenance/service.ts`, `src/app/api/tags/reset-projects/route.ts`
|
||||
- Why fragile: A single request can mutate many tags or projects and immediately trigger localized page revalidation. Business rules, transactional writes, and cache invalidation are tightly coupled.
|
||||
- Safe modification: Preserve transaction boundaries, keep mutation rules covered with focused tests, and review every `revalidatePath` target before changing route semantics.
|
||||
- Test coverage: Unit-style route and service tests exist, but there are no broader integration tests across Prisma writes, cache invalidation, and localized page rendering
|
||||
|
||||
## Scaling Limits
|
||||
|
||||
**Offset pagination on projects will degrade as data grows:**
|
||||
- Current capacity: `src/hooks/useProjects.ts` uses `skip` and `take`; `src/app/api/projects/route.ts` allows `limit` up to `100`.
|
||||
- Limit: High page numbers require larger offset scans in PostgreSQL, especially when combined with multi-join tag filters and text search.
|
||||
- Scaling path: Move to cursor-based pagination for project listings or restrict deep paging with indexed sort keys.
|
||||
**Offset pagination on projects degrades with table size:**
|
||||
- Current capacity: `src/hooks/useProjects.ts` uses `skip` and `take`, and `src/app/api/projects/route.ts` allows `limit` up to `100`.
|
||||
- Limit: Deep pages require larger offset scans in PostgreSQL, especially when combined with tag joins and text filters.
|
||||
- Scaling path: Move project listings to cursor pagination keyed by indexed sort fields or restrict deep paging.
|
||||
|
||||
**Signal search and sort scale with table growth, not just page size:**
|
||||
- Current capacity: `src/app/api/signals/route.ts` limits pages to `50` items, but search still scans multiple text columns and sort-by-hot depends on computed/indexed metadata.
|
||||
- Limit: Query latency rises as the `signals` table grows because there is no text-search index for `q`.
|
||||
- Scaling path: Add dedicated search indexes and keep cursor pagination tied to indexed sort orders only.
|
||||
**Signals pagination is ordered by mutable ranking fields:**
|
||||
- Current capacity: `src/app/api/signals/route.ts` sorts hot feeds by `isHot`, `hotScore`, `engagement`, `publishedAt`, and `id`, but cursors by `id` only.
|
||||
- Limit: As new signals arrive or hotness changes, clients can observe duplicates or skips between pages because the rank can move independently of the cursor key.
|
||||
- Scaling path: Use a stable compound cursor that includes the sort fields or snapshot the ranking inputs for pagination windows.
|
||||
|
||||
**Bulk tag reset revalidates pages per updated project:**
|
||||
- Current capacity: `src/app/api/tags/reset-projects/route.ts` accepts up to `100` projects and revalidates both locale list pages plus two detail pages per updated slug.
|
||||
- Limit: A large batch creates many cache invalidations and can amplify request time.
|
||||
- Scaling path: Batch revalidation, use broader tag-based invalidation if available, or move bulk operations to a background job.
|
||||
**Bulk tag reset scales linearly in both writes and cache invalidations:**
|
||||
- Current capacity: `src/app/api/tags/reset-projects/route.ts` can process many projects in one request and revalidate both locale list pages plus every updated detail page.
|
||||
- Limit: Large batch operations increase request time and invalidation fan-out.
|
||||
- Scaling path: Batch revalidation, shift large maintenance jobs to background execution, or use broader cache-tag invalidation when available.
|
||||
|
||||
## Dependencies at Risk
|
||||
|
||||
**`next/cache` `unstable_cache` behavior is runtime-sensitive:**
|
||||
- Risk: Both server data modules include custom fallback logic for missing incremental cache support, which means cache behavior is not consistent across every execution context.
|
||||
- Impact: The same function can behave differently in local development, tests, and production-like runtimes.
|
||||
- Migration plan: Centralize cache wrappers in one server utility, document expected runtimes, and replace `unstable_cache` usage with stable APIs when the project upgrades to a supported alternative.
|
||||
**External discovery services are mandatory but not versioned here:**
|
||||
- Risk: Project discovery, dedupe, and task lifecycle all depend on endpoints that are documented but not implemented in this repo.
|
||||
- Impact: Local development, replay, debugging, and disaster recovery are incomplete without a second system.
|
||||
- Migration plan: Either bring `src/app/api/discovery/*` into the repo or maintain a separately versioned API contract with tests and operational ownership.
|
||||
|
||||
**External AI search depends on an n8n webhook contract with no local fallback:**
|
||||
- Risk: The project assumes an external response shape and throws or fails requests when the webhook is unavailable or changes shape.
|
||||
- Impact: `/api/search/ai` becomes a single external point of failure for AI search.
|
||||
- Migration plan: Version the webhook contract, add contract tests, and consider a local adapter layer that can degrade more predictably.
|
||||
**AI search depends on an external n8n webhook contract with no local fallback:**
|
||||
- Risk: `/api/search/ai` assumes the remote workflow exists, responds quickly, and preserves the `results[].id` plus `similarity` schema.
|
||||
- Impact: AI search becomes a single external point of failure and contract drift breaks the feature immediately.
|
||||
- Migration plan: Version the webhook contract in `docs/integrations/n8n/registry.json`, add contract tests around `src/app/api/search/ai/route.ts`, and return controlled degraded responses when the webhook is unavailable.
|
||||
|
||||
**Workflow metadata is committed, but workflow behavior still lives outside git:**
|
||||
- Risk: The repo carries registry entries and generated context, but not the executable n8n exports.
|
||||
- Impact: Reviewers can understand intent, but they still cannot reproduce or diff actual workflow logic from the repository alone.
|
||||
- Migration plan: Start committing workflow exports to `docs/integrations/n8n/exports/` and reference them from `docs/integrations/n8n/registry.json`.
|
||||
|
||||
**`unstable_cache` behavior still depends on runtime capabilities:**
|
||||
- Risk: The app uses `runWithCacheFallback` to catch environments where `unstable_cache` is unavailable.
|
||||
- Impact: Cache behavior differs across local development, tests, and deployed runtimes, which complicates debugging and performance expectations.
|
||||
- Migration plan: Centralize cache policy, document supported runtimes, and replace `unstable_cache` with stable APIs when the stack allows it.
|
||||
|
||||
## Missing Critical Features
|
||||
|
||||
**End-to-end test harness is absent from the repository:**
|
||||
- Problem: `package.json` exposes only `pnpm test` for Vitest. No `playwright.config.*`, no `e2e/` directory, and no `test:e2e` script are present in the repository.
|
||||
- Blocks: Critical user flows such as localized routing, project filtering, AI search, signals pagination, and webhook-backed content updates have no browser-level regression protection.
|
||||
**The repository does not contain a self-contained discovery ingestion loop:**
|
||||
- Problem: The n8n docs depend on discovery task endpoints that do not exist under `src/app/api/`, so the repo cannot run its own project-intake workflow end to end.
|
||||
- Blocks: End-to-end ingestion tests, local replay of failed discovery tasks, and full incident debugging from this repo alone.
|
||||
|
||||
**User-facing submission and newsletter flows are not implemented:**
|
||||
- Problem: The visible submission CTA and newsletter UI are placeholders with no connected backend or external provider.
|
||||
- Blocks: Users cannot actually submit projects, subscribe to updates, or access legal/resource destinations from the shipped UI.
|
||||
**Browser-level regression coverage is missing:**
|
||||
- Problem: The repo ships no `playwright.config.*`, no `e2e/` directory, and no `test:e2e` script even though repository guidance claims they exist.
|
||||
- Blocks: Localized routing, multi-step filtering, AI search UX, and layout-level interaction regressions are not protected at the browser layer.
|
||||
|
||||
**User-facing submission and newsletter flows are still not implemented:**
|
||||
- Problem: The visible submit-project and newsletter UI does not connect to backend handlers or third-party providers.
|
||||
- Blocks: Users cannot submit projects, subscribe for updates, or rely on footer resource/legal/social destinations.
|
||||
|
||||
## Test Coverage Gaps
|
||||
|
||||
**Search and listing APIs are untested:**
|
||||
**Search and listing APIs remain untested:**
|
||||
- What's not tested: `GET /api/projects`, `GET /api/projects/[slug]`, `POST /api/search/ai`, and `GET /api/signals`
|
||||
- Files: `src/app/api/projects/route.ts`, `src/app/api/projects/[slug]/route.ts`, `src/app/api/search/ai/route.ts`, `src/app/api/signals/route.ts`
|
||||
- Risk: Pagination, filtering, sort correctness, env-missing behavior, and external-service failure handling can break unnoticed.
|
||||
- Risk: Pagination, sorting, env-missing behavior, cursor correctness, and external-service error handling can break unnoticed.
|
||||
- Priority: High
|
||||
|
||||
**Signal ingestion path is untested:**
|
||||
- What's not tested: Validation, per-item failure accounting, schema-drift fallback, and write behavior in `POST /api/webhook/signals`
|
||||
**Signal ingestion still has no route-level tests:**
|
||||
- What's not tested: Validation, per-item error accounting, hotness fallback, and write behavior in `POST /api/webhook/signals`
|
||||
- Files: `src/app/api/webhook/signals/route.ts`, `src/lib/signal-hotness.ts`
|
||||
- Risk: Ingestion regressions can silently drop, mis-rank, or partially write signals.
|
||||
- Priority: High
|
||||
|
||||
**Large client components and shared layout have no regression tests:**
|
||||
- What's not tested: Filter toggling, URL synchronization, pagination controls, AI/traditional mode switching, signal feed interactions, and layout placeholders
|
||||
- Files: `src/app/[locale]/projects/ProjectsResultsClient.tsx`, `src/components/project/TagFilterPanel.tsx`, `src/components/signals/SignalFeedClient.tsx`, `src/app/[locale]/layout.tsx`
|
||||
- Risk: UI regressions are likely because these files are state-heavy and have many interaction branches.
|
||||
**n8n and discovery contracts have no automated verification in this repo:**
|
||||
- What's not tested: That `docs/integrations/n8n/registry.json`, `docs/integrations/n8n/CONTEXT.generated.md`, and route expectations stay aligned with live n8n workflows and the external discovery service.
|
||||
- Files: `docs/integrations/n8n/registry.json`, `docs/integrations/n8n/CONTEXT.generated.md`, `scripts/generate-n8n-context.mjs`, `src/app/api/search/ai/route.ts`, `src/app/api/tags/reset-projects/route.ts`, `src/app/api/webhook/signals/route.ts`
|
||||
- Risk: Cross-system contract drift is detected late, usually only after production failures.
|
||||
- Priority: High
|
||||
|
||||
**Current tests focus narrowly on tag maintenance and schema validation:**
|
||||
- What's not tested: Most database-backed pages and non-tag APIs outside a few route/service units
|
||||
- Files: `src/app/api/tags/maintenance/route.test.ts`, `src/app/api/tags/maintenance/service.test.ts`, `src/app/api/tags/reset-projects/route.test.ts`, `src/app/api/tags/route.test.ts`, `src/lib/auth.test.ts`, `src/lib/validations.test.ts`, `src/lib/validations.tag-maintenance.test.ts`
|
||||
- Risk: The test suite gives confidence for tag admin flows but not for the main product surfaces.
|
||||
**Large client components and shared layout have no regression tests:**
|
||||
- What's not tested: Filter toggling, URL synchronization, AI/traditional search mode switching, signal feed paging, and layout-level placeholder interactions
|
||||
- Files: `src/app/[locale]/projects/ProjectsResultsClient.tsx`, `src/components/project/TagFilterPanel.tsx`, `src/components/signals/SignalFeedClient.tsx`, `src/app/[locale]/layout.tsx`
|
||||
- Risk: UI regressions are likely because these files are state-heavy and branchy.
|
||||
- Priority: High
|
||||
|
||||
**Current tests focus on tag admin flows and low-level helpers, not main user journeys:**
|
||||
- What's not tested: Most database-backed pages and operational boundaries outside tag maintenance, auth comparison, validation schemas, and Prisma URL construction
|
||||
- Files: `src/app/api/tags/route.test.ts`, `src/app/api/tags/reset-projects/route.test.ts`, `src/app/api/tags/maintenance/route.test.ts`, `src/app/api/tags/maintenance/service.test.ts`, `src/lib/auth.test.ts`, `src/lib/validations.test.ts`, `src/lib/validations.tag-maintenance.test.ts`, `src/lib/prisma-url.test.ts`
|
||||
- Risk: The suite provides confidence for admin mutation helpers but not for the main product surfaces or external integration edges.
|
||||
- Priority: Medium
|
||||
|
||||
---
|
||||
|
||||
*Concerns audit: 2026-04-18*
|
||||
*Concerns audit: 2026-04-20*
|
||||
|
||||
Reference in New Issue
Block a user