Files
agent-park/.planning/codebase/CONVENTIONS.md
T
2026-04-18 19:28:53 +08:00

13 KiB

Coding Conventions

Analysis Date: 2026-04-18

Naming Patterns

Files:

  • Use Next.js route filenames in src/app: page.tsx, layout.tsx, route.ts, not-found.tsx, robots.ts, and sitemap.ts as seen in src/app/[locale]/page.tsx, src/app/[locale]/layout.tsx, src/app/api/projects/route.ts, and src/app/[locale]/not-found.tsx.
  • Use PascalCase.tsx for reusable components in src/components, for example src/components/project/ProjectCard.tsx, src/components/project/TagFilterPanel.tsx, and src/components/signals/SignalFeedClient.tsx.
  • Use lower-case utility filenames in src/lib, for example src/lib/auth.ts, src/lib/cache.ts, src/lib/signal-hotness.ts, and src/lib/validations.ts.
  • src/hooks is not limited to React hooks. src/hooks/useProjects.ts and src/hooks/useHome.ts export server-side data access and aggregation functions, not hook APIs. Extend those files only when adding the same kind of server query layer.

Functions:

  • Use camelCase for functions and helpers: isValidApiKey in src/lib/auth.ts, normalizeProjectSort in src/hooks/useProjects.ts, runWithCacheFallback in src/lib/cache.ts, and collectSelectedTagSlugs in src/app/api/tags/reset-projects/route.ts.
  • Use PascalCase only for React components and error classes: ProjectCard in src/components/project/ProjectCard.tsx, ProjectsResultsClient in src/app/[locale]/projects/ProjectsResultsClient.tsx, and TagMaintenanceApiError in src/app/api/tags/maintenance/service.ts.
  • Use GET and POST named exports for route handlers in src/app/api/**/route.ts.

Variables:

  • Use UPPER_SNAKE_CASE for constants and configuration knobs, for example DB_RETRY_DELAYS_MS in src/hooks/useProjects.ts, DEFAULT_RESET_CATEGORIES in src/app/api/tags/reset-projects/route.ts, TAGS_CACHE_REVALIDATE_SECONDS in src/app/api/tags/route.ts, and N8N_WEBHOOK_URL in src/app/api/search/ai/route.ts.
  • Use descriptive typed local variables for parsed or normalized input, such as validatedQuery in src/app/api/projects/route.ts, normalizedTagSlugs in src/hooks/useProjects.ts, and validationResult in src/app/api/webhook/signals/route.ts.

Types:

  • Prefer type aliases for data shapes and Prisma payloads, for example ProjectWithFlatTags in src/hooks/useProjects.ts, HomePageData in src/hooks/useHome.ts, and ResetResultItem in src/app/api/tags/reset-projects/route.ts.
  • Use interface for component props, for example ProjectCardProps in src/components/project/ProjectCard.tsx, ProjectsPageClientProps in src/app/[locale]/projects/ProjectsPageClient.tsx, and SignalFeedClientProps in src/components/signals/SignalFeedClient.tsx.

Code Style

Formatting:

  • Prettier is configured in .prettierrc.json for 2-space indentation, semicolons, double quotes, trailing commas set to es5, and printWidth 100.
  • The repository is not uniformly formatted to that config. Files such as src/lib/validations.ts, src/hooks/useProjects.ts, and src/app/api/tags/maintenance/route.ts match the configured double-quote and semicolon style, while src/app/api/projects/route.ts, src/app/api/search/ai/route.ts, src/lib/auth.ts, and src/components/project/ProjectCard.tsx use single quotes and omit semicolons.
  • For new files, follow .prettierrc.json. When editing existing files, preserve the file-local style unless the whole file is reformatted.

Linting:

  • ESLint extends next/core-web-vitals and prettier in .eslintrc.json.
  • console.warn and console.error are allowed; other console calls are warned by no-console.
  • Quality checks on 2026-04-18: pnpm lint passed with ✔ No ESLint warnings or errors.

Import Organization

Order:

  1. Framework and platform imports first, for example next/server, next/cache, zod, @prisma/client, or react.
  2. Internal alias imports from @/, for example @/lib/prisma, @/hooks/useProjects, and @/lib/validations.
  3. Relative imports last, for example ./service in src/app/api/tags/maintenance/route.ts.

Path Aliases:

  • Use the @/* alias from tsconfig.json and vitest.config.ts for internal imports.
  • Prefer @/ over deep relative paths across src, for example @/lib/prisma in src/app/api/tags/route.ts and @/lib/tag-taxonomy in src/hooks/useProjects.ts.

Component and Module Design

React Components:

  • Default-export only route-level pages and layouts, for example src/app/[locale]/page.tsx, src/app/[locale]/projects/page.tsx, and src/app/layout.tsx.
  • Use named exports for reusable components, for example ProjectCard in src/components/project/ProjectCard.tsx, HomeRankings in src/components/home/HomeRankings.tsx, and AnnouncementBar in src/components/layout/AnnouncementBar.tsx.
  • Mark interactive components with 'use client', as seen in src/app/[locale]/projects/ProjectsResultsClient.tsx, src/app/[locale]/projects/ProjectsPageClient.tsx, src/components/project/TagFilterPanel.tsx, and src/components/signals/SignalFeedClient.tsx.
  • Keep server components async and free of client hooks, as seen in src/app/[locale]/projects/[id]/page.tsx, src/app/[locale]/signals/page.tsx, and src/components/project/ProjectSidebar.tsx.
  • Type component props explicitly with a local interface ...Props.

Service and Query Modules:

  • Centralize Prisma-backed read logic in src/hooks/useProjects.ts and src/hooks/useHome.ts. Despite the directory name, these modules act as query services for routes and server components.
  • Keep route handlers thin where possible and delegate business rules to local services for mutations. The clearest example is src/app/api/tags/maintenance/route.ts delegating to src/app/api/tags/maintenance/service.ts.
  • Reuse shared utility modules in src/lib for cross-cutting concerns: src/lib/auth.ts, src/lib/cache.ts, src/lib/prisma.ts, src/lib/signal-hotness.ts, src/lib/slug.ts, and src/lib/tag-taxonomy.ts.

Exports:

  • Prefer named exports across shared modules. No barrel files were detected under src on 2026-04-18.

Validation

Schema Placement:

  • Put broadly shared Zod schemas in src/lib/validations.ts. Examples include ProjectInputSchema, SignalWebhookPayloadSchema, SignalQuerySchema, and TagMaintenanceRequestSchema.
  • Define route-local Zod schemas only when the contract is tightly coupled to a single endpoint, as in ProjectsQuerySchema in src/app/api/projects/route.ts and N8NSearchResponseSchema in src/app/api/search/ai/route.ts.

Validation Flow:

  • Use .safeParse() when the route needs to return a custom 400 payload without exceptions, as in src/app/api/tags/maintenance/route.ts, src/app/api/tags/reset-projects/route.ts, and src/app/api/webhook/signals/route.ts.
  • Use .parse() when the route already has a ZodError catch branch, as in src/app/api/projects/route.ts, src/app/api/search/ai/route.ts, and src/app/api/signals/route.ts.
  • Use z.coerce for query-string number parsing in shared schemas, as in SignalQuerySchema and ProjectQuerySchema in src/lib/validations.ts.
  • Add cross-field validation with .superRefine() for multi-item or relation rules, as in TagMergeSchema and TagMaintenanceRequestSchema in src/lib/validations.ts.

Data Access

Prisma:

  • Use the singleton Prisma client from src/lib/prisma.ts.
  • Define Prisma payload types close to the query layer with Prisma.*GetPayload, as in ProjectWithTagsAndLinks and TagWithProjectCount in src/hooks/useProjects.ts.
  • Prefer explicit include and select clauses to control payload shape, as seen throughout src/hooks/useProjects.ts, src/hooks/useHome.ts, src/app/api/signals/route.ts, and src/app/api/tags/reset-projects/route.ts.
  • Use prisma.$transaction(...) for write paths that change multiple tables, as in src/app/api/tags/maintenance/route.ts and src/app/api/tags/reset-projects/route.ts.

Caching and Fallbacks:

  • Wrap cacheable server reads with unstable_cache, using stable key arrays and revalidate windows, as in src/app/api/tags/route.ts, src/hooks/useProjects.ts, and src/hooks/useHome.ts.
  • Route cached reads through runWithCacheFallback from src/lib/cache.ts so execution can fall back when unstable_cache is unavailable.
  • Cache fetcher functions keyed by input when the function signature varies, as in topTagsCache and fixedProjectTypeFilterCache in src/hooks/useProjects.ts.

Retry and Degrade Patterns:

  • Use retry wrappers for transient DB issues on read paths. withDbRetry and isTransientDbError in src/hooks/useProjects.ts are the current pattern.
  • Degrade to safe defaults on non-critical homepage and filter data instead of failing the whole page, as in safeQuery in src/hooks/useHome.ts and console.error fallback branches in src/hooks/useProjects.ts.

Error Handling

Patterns:

  • Wrap route handlers in try/catch and return JSON error payloads through NextResponse.json, as in src/app/api/projects/route.ts, src/app/api/tags/route.ts, src/app/api/search/ai/route.ts, and src/app/api/webhook/signals/route.ts.
  • Return 400 for schema and cursor validation failures, 401 for invalid API keys, 409 for tag conflicts inside service code, and 500 for unexpected errors.
  • Use domain-specific error classes when mutation services need to communicate status and details back to routes. TagMaintenanceApiError in src/app/api/tags/maintenance/service.ts is the established pattern.
  • Include machine-readable success, error, details, and sometimes message fields in JSON responses. The exact shape varies by route and is not fully normalized.

Logging:

  • Use console.error for failures and console.warn for degraded or summary logging, for example src/hooks/useHome.ts, src/hooks/useProjects.ts, src/app/api/signals/route.ts, and src/app/api/webhook/signals/route.ts.
  • Error-path tests currently allow log output to stderr, as verified by pnpm test on 2026-04-18 from src/app/api/tags/route.test.ts.

Comments

When to Comment:

  • Comments are sparse and usually explain non-obvious intent, numbered route steps, or bilingual product context.
  • English and Chinese comments coexist. Examples include the numbered route comments in src/app/api/tags/maintenance/route.ts, Chinese comments in src/hooks/useProjects.ts, and mixed bilingual commentary in src/components/project/ProjectCard.tsx.
  • Prefer comments only where intent is not obvious from code.

JSDoc/TSDoc:

  • JSDoc is uncommon. The clearest example is the security-sensitive note on isValidApiKey in src/lib/auth.ts.

Function Design

Size:

  • Read/query modules tolerate large files with many helpers. src/hooks/useProjects.ts is 602 lines, src/hooks/useHome.ts is 250 lines, and src/app/api/signals/route.ts is 355 lines.
  • Keep complex logic split into local helpers inside the same file before extracting a new module. Current examples include parseSlugList in src/app/api/projects/route.ts, parseSections in src/app/api/signals/route.ts, and collectValidationErrorsForProjectItem in src/app/api/tags/reset-projects/route.ts.

Parameters:

  • Prefer a single typed options object for query functions, as in getProjects in src/hooks/useProjects.ts.
  • Use small helper functions for normalization of query and payload input, such as normalizePositiveInteger in src/hooks/useProjects.ts and normalizeSlug in src/app/api/tags/reset-projects/route.ts.

Return Values:

  • Return plain serializable objects from server query helpers when the result is meant for pages or APIs, as in getProjects, getHomePageData, and getProjectsByIds.
  • Flatten Prisma relation shapes before returning to callers when the UI expects direct lists, as in getProjects, getProjectBySlug, and getProjectsByIds in src/hooks/useProjects.ts.

Environment and Configuration

Environment Files:

  • .env, .env.local, and .env.example are present at repository root. Use .env.example as the naming reference; do not commit real secrets.

Observed Variables:

  • .env.example defines DATABASE_URL, WEBHOOK_API_KEY, N8N_AI_SEARCH_WEBHOOK, NEXT_INTL_DEFAULT_LOCALE, and NEXT_INTL_SUPPORTED_LOCALES.
  • Source code reads WEBHOOK_API_KEY in src/lib/auth.ts, N8N_AI_SEARCH_WEBHOOK in src/app/api/search/ai/route.ts, NEXT_PUBLIC_SITE_URL in src/app/robots.ts and src/app/sitemap.ts, VERCEL_ENV in src/app/layout.tsx, and NODE_ENV in src/lib/prisma.ts.
  • NEXT_INTL_DEFAULT_LOCALE and NEXT_INTL_SUPPORTED_LOCALES appear in .env.example but were not detected in runtime code on 2026-04-18. Locale behavior is hard-coded in src/middleware.ts and src/i18n/request.ts.

Conventions:

  • Fail fast at module load only for truly required integration config. src/app/api/search/ai/route.ts throws immediately if N8N_AI_SEARCH_WEBHOOK is unset.
  • Use safe fallbacks for public metadata values, as in src/app/robots.ts and src/app/sitemap.ts defaulting NEXT_PUBLIC_SITE_URL to https://agentpark.ai.
  • Keep deployment and framework config in root files: next.config.js, tailwind.config.ts, postcss.config.mjs, tsconfig.json, .eslintrc.json, .prettierrc.json, and vitest.config.ts.

Convention analysis: 2026-04-18