Search/Make Small, Tested Changes in a Large TypeScript Monorepo

Make Small, Tested Changes in a Large TypeScript Monorepo

Use this when an AI agent edits a TypeScript workspace with many packages. It first learns the repository's layout and tools, runs only the builds and tests the change touches, leaves generated files alone, and checks that the change works on both Unix and Windows.

To use this AI rule
## Learn the Repository's Layout and Tools Before Editing Treat the repository’s canonical agent guide as the single instruction source. Keep alternate agent entrypoints as thin references, and update the canonical guide when architecture, tooling, versions, workflows, or contributor rules change. Identify: - the actual repository root, main source area, build-script area, documentation, test applications, and generated workspaces; - the required base branch, runtime version, package manager, task orchestrator, linter, formatter, and CI operating systems; - public exports versus explicitly internal exports; - package-specific type-checkers and any compiler-version split between repository checks, editor tooling, and downstream API consumers. Keep shared configuration valid for every supported compiler path. Do not adopt an option or syntax merely because the newest checker accepts it. For modular UI tooling, distinguish runtime renderers, bundling engines, and framework integrations that combine both with framework configuration. Preserve fast startup architecture such as static indexing before runtime loading; one invalid user module must not collapse the entire navigation or management interface. ## Run Only the Builds and Checks the Change Needs Run commands from the documented working directory and use workspace project names, not published package names, when addressing the task graph. Start with cached, dependency-aware, non-production tasks: 1. compile the affected project or package set; 2. run scoped type checks; 3. format and lint changed files; 4. execute the closest relevant tests; 5. validate behavior in the internal development UI. Escalate to production configuration, generated sandboxes, or CI-parity flows only when the change depends on template integration, production task wiring, or cross-package behavior. Respect task dependencies unless deliberately resuming from a later verified phase. Do not start long-running development servers as a default action. Commands that require a template, target, or environment must receive it explicitly. When a generated sandbox fails, use the documented internal UI as the diagnostic fallback before changing build architecture. ## Choose Tests That Fit Each Kind of Code and Keep Tests Isolated Match the test form to the behavior: - test rendered components through co-located stories and interaction functions, including accessibility and user behavior; - use unit tests for pure utilities, hooks, and non-rendering modules; - use visual tooling for appearance; - use end-to-end, smoke, or runner suites for integration contracts. Test exported behavior and observable side effects, not private implementation details. Do not duplicate guarantees already enforced by types, lint rules, story interactions, or higher-level browser tests. For filesystem code, prefer an in-memory filesystem over real temporary directories or wholesale module replacement. Reset virtual state before each test, redirect only the required filesystem methods, and assert virtual disk state when useful. Never assign ambient globals directly in tests. Use the test framework’s tracked global stubs and restore them after each test, including keys that did not previously exist. Direct assignment creates order-dependent leaks across files. Normalize expected paths with the runtime path utilities so filesystem assertions pass on both Unix and Windows. ## Check Assumptions First and Skip Unneeded Abstractions Verify environment assumptions with a small disposable probe before basing architecture on them. Do not turn an untested belief about bundlers, metadata, loaders, or generated output into a permanent comment or abstraction. Encode durable assumptions with types and existing lint rules first. If static enforcement is impractical, add a cheap runtime assertion at the boundary so violations fail close to their source. Prefer deletion and directness over speculative flexibility. Do not add abstractions, fallbacks, or extension points for consumers that do not exist. Comments are maintenance documentation, not investigation transcripts. Explain the enduring reason in one or two sentences; omit ticket codes, probe histories, byte-comparison narratives, and fragile line-number references. Use broad edge-case coverage for security-sensitive paths and legacy migrations. Scope compatibility code to an explicit version window so it can be removed when support ends. ## Leave Generated Files Alone and Use the Repo's Formatter and Logger Run the repository formatter after editing, then lint changed files and run relevant tests. Use the configured tools; do not substitute retired linters or hand-format code that the formatter owns. Execute new TypeScript scripts through the repository’s current native path and use explicit TypeScript import extensions where configured. Migrate legacy loaders only within the touched scope. Use the project logging abstraction in normal server and client code. Reserve raw console calls for isolated scripts where importing the logger is unreasonable. Never commit accidental changes to generated files. A generated artifact may change only when it is a deterministic consequence of an intentional source change in the same work. Otherwise leave it untouched and report recurring generator drift. Account for both Unix and Windows in paths, subprocesses, and tests. Treat CI platform coverage as a compatibility requirement, not an optional cleanup. ## Finish a Change by Checking Its Behavior, Not Just the Compile For a normal change: 1. install dependencies only when needed; 2. compile the relevant dependency graph; 3. make the smallest coherent edit; 4. recompile affected packages; 5. run scoped type checks, formatting, lint, and contract-appropriate tests; 6. validate in the internal UI; 7. add sandbox or production-parity checks only when directly relevant. For a renderer, framework, builder, or plugin integration, edit the owning package, compile without production mode first, then test through a matching generated application and the relevant runner or end-to-end flow. If a build fails, refresh dependencies and retry the scoped compile before increasing memory or changing architecture. Inspect generated outputs and caches when sandbox behavior differs from the internal UI. Do not claim readiness from compilation alone. The required evidence is the smallest set that covers the changed package boundary, supported compiler paths, observable behavior, cross-platform assumptions, and directly affected integration flow.