Search/Make an AI Coding Agent Read Before It Edits a Large Repo and Ship Only Through Review

Make an AI Coding Agent Read Before It Edits a Large Repo and Ship Only Through Review

Use when an AI agent works in a big repository with many packages. It reads the code, dependencies, and tests before judging a fix, keeps one way to do each thing, never runs untrusted code locally, and changes shared code only through review and your approval.

To use this AI rule
## Read the top-level rules first, then the guide for the folder you work in Keep the root document to hard policy and routing; let scoped guides own the detailed workflow for each area. Before working inside a subtree, read the guide that owns it. ### One home for each kind of rule Hard policy, cross-cutting boundaries, and which guide owns what live at the root. Task-specific procedures live in the scoped guide next to the code they govern. Do not restate a workflow in two places where the copies can drift apart. ### Reference code the way the repo does Cite files by their repository-relative path, not by an absolute or home-directory path, so a reference means the same thing to every reader. ## Look for an existing library or tool before building your own Before proposing or building a custom system, feature, tool, integration, or automation, run a lightweight check for an existing library, plugin, or free platform that already solves it well enough. ### Prefer the proven option When an existing option is adequate, use it. Build custom only when the alternatives are unsuitable, too expensive, unmaintained, unsafe, non-compliant, or the user explicitly asked for a custom build. ### Keep the check cheap This is a brief preflight gate, not a research assignment. Do not recommend a paid service unless the user has approved the spend. ## Review by reading callers, callees, and tests, not just the diff Fixes, triage, and reviews need high confidence backed by reading, not a guess from the visible change. A diff-only review is insufficient. ### Read the whole surface before a verdict Before saying good, bad, best fix, or proof enough, read the whole changed function or module plus its callers, callees, sibling implementations, adjacent tests, scoped docs, and the contracts of anything it depends on. ### Build a small evidence map Name the changed surface, its entry point, the owner boundary, at least one caller and one callee, sibling surfaces that share the invariant, existing tests, and current shipped behavior. If any cell is missing, say the gap instead of concluding. ### If challenged, read before defending When a verdict is questioned, check the missing path first; do not defend the earlier conclusion until you have looked. ## Read a dependency's own source and test external APIs live When behavior depends on a library or an external service, verify against the real thing, not memory, a wrapper, a generated schema, or another agent's report. ### Read the dependency's own source For dependency-backed behavior, read the upstream source, docs, or types directly and cite what you checked. Most dependencies are open source, so there is no excuse for guessing a default, an error, or a timing. ### Live-test external services External API work needs a live test plus current cited proof; do not make an API claim from memory. Live-verify whenever feasible, and never print a secret in the process. ## Judge whether a fix is the best one, not just one that works A review verdict must decide whether the change is the best fix, not merely one that happens to work. Compare owner boundaries, callers, siblings, tests, and current behavior before you conclude. ### One-sided fixes need sibling proof A fix that touches one surface but not its siblings needs proof the siblings are unaffected, or an explicit follow-up that covers them. ### Do not file taste as a finding Do not raise a finding for pure repository-policy preference when the changed code follows its scoped guide and shows no user-visible, runtime, security, or maintainer risk. ## Read config in only one way and migrate old keys instead of dropping them Runtime reads only the current canonical shape of config and data. There is no silent compatibility for old or malformed keys. ### Delete the old path Prefer one canonical path and delete the old one, unless an explicit shipped contract or tagged upgrade path is cited. A refactor that leaves a second path has probably failed. ### Migrate, never silently drop If a change invalidates existing config, add a matching migration in the dedicated migration or repair step; never silently drop a key, because a dropped key is a hidden behavior change. ### Keep the config surface small The config and environment surface has a high bar. Before adding an option, prove existing behavior cannot solve it, and prefer removing or consolidating options. Treat documented setup flows as compatibility-sensitive: prefer additive flags over breaking existing snippets. ## Keep module-specific code in its own module, not in shared code Shared and core code stays generic; owner-specific behavior lives in the owning module. Cross a boundary only through a documented public seam, never by reaching into another module's internals. ### Move responsibility, do not wrap it The default fix shape is a clean bounded refactor that moves logic to the right owner and deletes stale abstractions, duplicate policy, and dead branches, not the smallest possible patch. ### Correct, but not over-engineered Correctness on real inputs and states is mandatory; extra layers and guards for imagined inputs are defects, not rigor. When you trade an unlikely edge case for real simplicity, name the tradeoff so it is a decision, not an oversight. ## Keep runtime state in the project's main store, not in side files Keep runtime state in the project's canonical store. Do not scatter sidecar files for state, caches, queues, indexes, or scratch data that the store should own. ### Migrations own the old shape Legacy file stores and retired shapes are normalized only in migration or repair code before runtime; steady-state runtime never reads a fallback location. ### Never bump a schema version alone A schema-version change that older readers cannot tolerate needs explicit user discussion and acceptance first; never advance it autonomously. A purely additive change that older builds still tolerate does not bump the version. ## Reuse facts found earlier instead of looking them up on every request A hot path should carry forward facts that were resolved earlier, not rediscover them on every request. ### Resolve once, reuse Pass identifiers, resolved paths, and capability facts forward and reuse the prepared object. Do not fix repeated request-time discovery by scattering caches; move the canonical fact earlier and delete the duplicate lookups. ### No freshness polling in the loop Process-stable metadata such as installs, manifests, and resolved paths changes only on restart or an explicit reload, so hot paths must not re-check freshness by re-reading or re-hashing. A process-local cache is fine only when it is lifecycle-owned and bounded. ## Write short, readable code with strict types Keep strict type checking on and keep the code honest. The goal is small, clear code, not the smallest diff. ### Type the boundaries, close the branches Avoid the catch-all dynamic type; prefer real types and narrow adapters. Validate external input with a schema. Branch on discriminated unions or closed result codes rather than free-form strings or sentinel values, and make impossible states unrepresentable. ### Structure for reading Prefer early returns over nested pyramids, and split work into gather, normalize, decide, and act. Keep call sites boring: complex decisions happen above, so arguments stay names, literals, or simple reads. ### Comment the non-obvious An inline comment earns its place on a non-obvious invariant, ordering constraint, ownership boundary, or cleanup coupling: one to three lines saying why the branch exists and what breaks if it is removed. No syntax narration. ## When a refactor makes code longer, trim it or explain why A refactor should delete about as much local complexity as it adds. Positive production line count is a smell to justify, not a default to accept. ### Check the size before closeout Before finishing a non-trivial refactor, compare the size of the change; if non-test code grew, trim it or state plainly why fewer paths now cost more lines. ### Make new surface pay rent A new helper or file must immediately reduce call paths, concepts, or repeated logic. Do not add one for one-off compatibility, field renaming, or speculative resilience. Keep public interfaces narrow and return the smallest useful shape. ## Test behavior instead of wording, and keep each test isolated Tests prove behavior and guard against regressions; they are not a checkbox for every internal branch. ### Test behavior, not wording Prefer behavior tests over grepping for docs or policy strings, and put operator reminders in docs rather than in test assertions. Review new tests before landing and delete low-value ones, including tests for paths you just removed. ### Keep tests isolated and honest Clean up timers, environment, globals, mocks, and temporary state. A mock must export every binding the real code touches, including error types used in identity checks. Do not edit a baseline or snapshot file to silence a check without approval. ### Test on a copy of real state When realistic data matters, copy the state into a scratch location and test the copy; never mutate a live service's state in place without explicit approval. ## Never run untrusted code locally, and send heavy builds to a remote runner Classify the source's trust before choosing how much to prove, and choose where the proof runs by that same trust. ### Never run untrusted code locally Trusted source may run a few focused tests and cheap static checks locally when dependencies are ready. Untrusted or externally contributed code runs none of its own scripts, tests, hooks, or config locally, whatever the proof size; use isolated, credential-free execution instead. ### Send heavy work to the remote runner Full suites, builds, type checks, packaging, and cross-platform proof go to a remote runner, acquired lazily at the first heavy command, reused for one session, and released before handoff. If the remote runner is unavailable, trusted work may fall back to local; untrusted work never does. ### Gate the landing on a fresh review Before landing, run a fresh automated review until no actionable findings remain; do not land on green continuous integration alone unless the scope is trivial or the user opts out. If proof is blocked, say exactly what is missing and why. ## Open issues and pull requests the project's way, and get approval before releases Changing a shared surface is a social act: follow the project's contribution process and get approval for every irreversible step. ### Follow the review and merge process Read the contribution guide, the issue and pull-request templates, and the code-owners file before opening anything. Open an issue first for bugs, user-facing features, and architecture decisions. Write a real pull-request body with the problem, the impact, and the evidence, and verify live state through the platform's own tool before any mutation. ### Do nothing irreversible unsolicited Do not label, retitle, rebase, or land without being asked. Show a public comment as a draft first unless the user explicitly asked you to post it, and name every posted comment with its link. End issue and pull-request work with the full item URL. ### Commit and branch cleanly Commit only the intended files with concise, grouped messages, rebase on the latest mainline before pushing, and never delete or rename unexpected files. Keep documentation in step with behavior in the same change, and leave the release changelog to the release process. ### Get approval for releases and secrets Releases, version bumps, and dependency or lockfile changes need explicit approval. Never commit real credentials or live config, and keep secrets in the designated store.