From a8ce39ee28b0a89015b65bf67535e8b84ac10ac8 Mon Sep 17 00:00:00 2001 From: willbot Date: Wed, 15 Jul 2026 18:50:05 +0200 Subject: [PATCH] docs(rules): capture full-surface-rename codemod traps (Composer rename close-out) Lands the hard-won lessons from the Compose -> Composer rename as a repo-tracked agent rule, so they're not stuck in one session's private notes. Covers the traps that pass a naive sweep: boundary-vs-literal verification grep, regex-escaped separators evading both the sweep and the grep, multiple token emit sites (a bare 'COMPOSE' array literal alongside the RESERVED_SECRET_PREFIX constant), excluded files still losing inbound links, .agents/rules/*.mdc silently skipped by perl -pi, and rebasing a full-tree rename by redo rather than hand-resolve. Signed-off-by: willbot Signed-off-by: Will Madden --- .agents/rules/full-surface-rename.mdc | 49 +++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 .agents/rules/full-surface-rename.mdc diff --git a/.agents/rules/full-surface-rename.mdc b/.agents/rules/full-surface-rename.mdc new file mode 100644 index 00000000..1057f6d0 --- /dev/null +++ b/.agents/rules/full-surface-rename.mdc @@ -0,0 +1,49 @@ +--- +description: "Traps when doing a repo-wide token rename / codemod (renaming a product, package scope, CLI bin, or wire prefix across the whole tree). Read before running the sweep and before declaring it clean." +globs: [] +alwaysApply: false +--- + +# Full-surface rename / codemod traps + +Hard-won from the Compose → Composer rename (packages, bin, config file, wire +prefix). A repo-wide token swap looks mechanical but has sharp edges that pass a +naive sweep and a naive "did I get everything?" grep. + +## Rules + +- **Verify with a boundary grep, not a literal one.** `git grep 'old-token'` + matches the NEW token as a substring — `@prisma/compose` matches + `@prisma/composer` — so a literal grep reports false "remnants" and hides real + ones. Verify with a boundary: `old-token([^]|$)` (e.g. + `@prisma/compose([^r]|$)`). + +- **Escaped separators evade both the sweep and the grep.** Test error-message + assertions hold regex-escaped forms like `@prisma\/compose-prisma-cloud` + (backslash-slash). A literal-slash replace rule skips them, and a literal-slash + verification grep can't see them — so the source string renames but its test + regex doesn't, and the mismatch only surfaces as a test failure. Recheck + `\/`-escaped variants explicitly after the sweep. + +- **Find every emit site, not just the constant.** A token can be assembled from a + bare literal that doesn't contain the full token. The `COMPOSE_` env prefix was + built two ways: the `RESERVED_SECRET_PREFIX = 'COMPOSE_'` constant AND a + standalone `'COMPOSE'` array literal (no underscore) joined with `'_'` in + `serializer.ts` `configKey()`. Grep the bare token (`COMPOSE`), not only the + full one (`COMPOSE_`). + +- **Excluding a file from the rename does NOT protect links to it.** ADR-0026 / + ADR-0027 were excluded and kept their old filenames, but the sweep still rewrote + cross-reference link *paths* pointing at them → ~10 dead 404 links. When you + exclude a file, also protect or repair every reference to its filename. + +- **`.agents/rules/*.mdc` are canonical; the sweep can silently skip them.** The + `.claude/rules` and `.cursor/rules` trees are symlinks back into `.agents/rules`. + A naive `perl -pi` can skip the `.mdc` files (symlink/perms/CRLF). After the + sweep, verify each `.mdc` was actually rewritten, then run `pnpm rules:sync` and + confirm `pnpm lint:rules:symlinks`. + +- **Rebase by redo, not by hand-resolve.** A full-tree rename conflicts with every + concurrent merge to `main`. Do not hand-resolve — redo the deterministic codemod + on the fresh base (`git reset --hard origin/main`, then re-run the sweep) and + land it fast, freezing other merges, because `main` keeps moving underneath.