


Change a Full-Stack TypeScript Monorepo and Test Its LLM Features by Meaning
For AI changes to a TypeScript monorepo with LLM features. It runs the same package checks as CI, tests behavior, keeps personal data out of broad logs, and judges LLM answers by meaning, not wording.
To use this AI rule
## Run Only the Package Commands and Checks That CI Uses
Discover the repository's documented development, build, lint, format, test, integration-test, and evaluation commands before running anything. Use the package-specific command for the affected workspace rather than substituting a root-wide tool.
- Run a single test by its supported path or name filter.
- Run AI evaluations through the AI package's dedicated evaluation command from the repository root.
- Use the application's CI-aligned build or type check; do not run an unsupported root type check that surfaces unrelated monorepo debt.
- Install dependencies before tests or builds only when the workspace is not already installed.
- Do not start development servers or full builds unless the task explicitly requests them.
Validation should match the changed package and the checks used by CI.
## Test What the Code Does, Not How It Is Written
Review the repository's testing guide before creating or changing tests. For core bug fixes, use red-green-refactor when practical: first reproduce the failure, then make the smallest fix, then improve structure without changing behavior.
Co-locate unit tests with source files. Put integration, end-to-end, and AI evaluation tests in their designated test trees. Use the real logging implementation unless the test specifically targets a logger boundary.
Prefer tests that catch a meaningful behavioral regression. Avoid low-value assertions that merely restate private implementation details. Prompt and model-behavior changes should have semantic evaluations or structured-contract tests in addition to ordinary code tests.
## Install Dependencies in the Workspace That Uses Them
Install a dependency in the workspace that uses it, not at the monorepo root. When adding a new workspace package, update every production and local container-build manifest that copies package metadata before dependency installation.
Keep environment variables synchronized across the example environment file, the runtime validation module, and the monorepo build configuration. Prefix variables exposed to browser code with the repository's public-variable prefix.
For version-sensitive or unclear framework behavior, read the documentation shipped with the installed dependency before changing code. Do not rely on memory or documentation for another installed version.
## Import from the Defining File and Infer Types from Schemas
Use strict null checking and infer TypeScript types from validation schemas instead of duplicating interfaces. Do not export a type used only within its defining file.
- Keep every import at the top of the file; do not add mid-file dynamic imports.
- Import from the original source rather than creating re-export chains or barrel files.
- Import individual utility functions instead of an entire utility library.
- Use the configured project-root alias for internal application imports.
- Place helper functions at the bottom of a file.
- Keep one resource per API route file.
Direct ownership makes dependencies, tree shaking, and change impact easier to understand.
## Extract Helpers Only When They Make Code Clearer
Default to inlining and co-locating logic at its use site. Small duplicated expressions are often clearer than a premature helper. Do not extract a function that only renames and forwards parameters.
Extract when a helper names a meaningful domain concept, makes surrounding control flow easier to read, or keeps correctness-sensitive behavior consistent across multiple flows. Centralize substantial duplicated logic early when copies must stay synchronized to avoid bugs.
Avoid large or nested ternaries. Prefer straightforward control flow, a focused helper, or a lookup table. Add comments for why a decision exists, not for what self-explanatory code does.
Prefer the simplest readable change. Preserve backward compatibility only when the requirement calls for it; otherwise update coordinated call sites directly instead of layering an unused migration path.
## Keep Personal Data Out of Broad Log Levels
Do not repeat logger context fields already attached higher in the call chain. Put personally identifying fields, such as sender, recipient, or message subject, only in the repository's most restricted trace level.
An explicitly documented exception may allow the authenticated user's own identifier at broader levels, but do not extend that exception to third-party data. Keep tests on the real logger so privacy filters, context propagation, and formatting remain exercised.
Before adding a log, identify its audience, retention, sensitivity, and whether existing context already supplies the value.
## Derive UI State from Data and Use the Shared Components
Do not mirror fetched props or server data into local state through an effect. Prefer derived values for read-only views and explicit edit state for user-controlled drafts.
Use the repository's approved component library before creating a custom primitive. Render asynchronous content through the shared loading-and-error wrapper so loading, failure, and success states remain consistent.
Co-locate route-specific page components beside their route entry file without adding nested component directories. Put components shared across pages in the application's shared component directory.
## Make Data Changes Through Server Actions and Shared Validation Schemas
Choose route middleware by access level: public error handling, authenticated-user access, or account-scoped access. Export response types by deriving them from the data function rather than duplicating response interfaces.
Use server actions through the repository's safe-action layer for web mutations. Reserve POST API routes for integrations that require a stable external HTTP contract, such as native clients.
Define mutation validation schemas in the designated validation modules and infer input types from those schemas. Use the established client cache for reads and invalidate the affected key after mutation. Build forms with the approved form library and action hook, and route failures through the shared action-error formatter.
Use provider abstractions for normal application logic. Check a concrete provider type only at a true integration boundary. Avoid callback-based dynamic database transactions when the repository forbids them.
## Test LLM Features by Meaning, Not Keyword Lists
Fix general model failure modes, not the wording of one evaluation case. Do not add keyword or phrase blacklists to prompts, evaluations, or tests merely to catch the model's current phrasing; text checks are especially brittle across languages.
Assert semantic outcomes with a judge criterion or structured contract, such as whether the model avoids unnecessary clarification or unsupported status claims. Never gate context injection or tool behavior on ad hoc user-text matching. Use structured state, metadata, or explicit events.
Make every tool description self-contained: purpose, parameter meanings, selection guidance, prerequisites, and tool-specific safety constraints. Keep only cross-cutting identity, confirmation, security, and formatting policies in the system prompt; put per-tool guidance in the tool description.
## Review the Diff and Report Which Checks Ran
Before reporting completion, inspect the changed diff and run the smallest CI-aligned set that proves the affected behavior. Expand to integration or evaluation tests when the contract crosses those boundaries.
Confirm that:
- the correct workspace owns every dependency and environment variable;
- tests cover behavior and new test files are discoverable;
- imports, schema-derived types, helpers, and abstractions follow repository conventions;
- logs do not broaden exposure of personal data;
- UI state, mutations, validation, cache invalidation, and loading states use established patterns;
- LLM behavior is tested semantically rather than by brittle wording;
- no unsupported root-wide check, development server, or full build was run without authorization.
Report which checks ran and which broader checks were intentionally out of scope.
