Claude Skill

naming-refactor

Refactor naming and repository structure exhaustively while preserving behavior and external contracts.

LLM Mart · 0 points · 0 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download paulrberg-agent-skills-skills_naming-refactor-913232a.zip · 8 KB
Part of paulrberg/agent-skills — 42 skills

Install

skills CLI npx skills add https://github.com/PaulRBerg/agent-skills/tree/main/skills/naming-refactor
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install paulrberg-agent-skills@llmmart
Git git clone https://github.com/PaulRBerg/agent-skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole paulrberg/agent-skills collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

Naming Refactor

If these instructions are already present in the conversation from a slash or dollar invocation, follow them directly; do not invoke this skill again through a skill tool.

Make every name in the current repository communicate one coherent domain model, regardless of refactor cost.

Contract

  • Cover the complete current Git repository. Do not narrow the run to selected files or stop after a candidate cap.
  • Preserve features, runtime behavior, side effects, performance-sensitive characteristics, and externally consumed contracts. Large diffs, path moves, and repository-controlled interface migrations are allowed.
  • Treat an exported surface as repository-controlled only when every consumer is in scope and can migrate atomically. Preserve other API names, CLI flags, environment variables, configuration keys, wire fields, routes, database names, and import paths unless the user explicitly authorizes a breaking migration.
  • Fix a bug discovered during the refactor only when the defect and intended behavior are clear and a regression check can prove the fix. Keep bug fixes in a distinct change wave and report them separately.
  • Preserve pre-existing work. Local edits, file moves, directory restructuring, and non-destructive validation are authorized. Do not commit, push, publish, or write externally unless the user or repository instructions require it.
  • Serialize implementation and verification against intersecting repository writes. Complete codebase analysis and the refactor plan first, then claim the cumulative write set before each wave as defined under Acquire the Implementation Scope. Claim the complete worktree only when consumers cannot be enumerated. If no reliable coordination mechanism is available, require the user to confirm an exclusive write window instead.
  • A verified no-op is valid only after exhaustive coverage. Do not rename a clear, conventional name merely to create churn, but do not retain a weak name to minimize diff size.

Coverage Ledger

Resolve scripts/naming-ledger.py relative to this SKILL.md and create its ledger outside the repository:

uv run "<skill-dir>/scripts/naming-ledger.py" init --root <repo> --ledger <scratch.json>

The helper maps every tracked and non-ignored untracked path and records pre-existing worktree state. Account for a path only after inspecting its name, relevant contents, and role:

uv run "<skill-dir>/scripts/naming-ledger.py" mark \
  --ledger <scratch.json> --status <pending|retained|renamed|excluded|blocked> \
  --path <path> [--path <path>...] [--reason <text>]

Use retained when the current name is justified, renamed when the path or its contents joined a verified rename, excluded for generated, vendored, binary, or bulk artifacts validated through their source or invariant, and blocked when behavior or contract safety cannot be established. excluded and blocked require reasons.

For delegated runs, have each subagent write a dispositions TSV outside the repository with one record per line: status<TAB>repo-relative-path<TAB>reason. Apply each file in one batch:

uv run "<skill-dir>/scripts/naming-ledger.py" mark \
  --ledger <scratch.json> --from-file <dispositions.tsv>

A subagent reports a planned but unverified rename as pending. After the wave is verified, apply its file with --pending-as renamed (or another final status) instead of rewriting the TSV; reasons carry over, and excluded or blocked still require one.

Dispositions paths are relative to the repository root and must match ledger entries exactly. The ledger maps only tracked and non-ignored untracked files; gitignored artifacts, cache files, and bare directories are never ledger paths. Unknown paths fail the batch closed. Rerun with --skip-unknown only after reviewing the reported paths and confirming that every skip is intentional, then verify the result's skipped list.

uv run "<skill-dir>/scripts/naming-ledger.py" pending --ledger <scratch.json> [--limit <n>]
uv run "<skill-dir>/scripts/naming-ledger.py" refresh --ledger <scratch.json>
uv run "<skill-dir>/scripts/naming-ledger.py" summary --ledger <scratch.json>

Refresh after path moves and before final validation. New paths become pending; removed paths remain in the ledger and must be accounted as renamed, excluded, or blocked. The run is complete only when the helper reports no pending or blocked paths.

Ground the Refactor

  1. Read applicable repository instructions. Record the repository root, starting commit and status, build and check commands, generated sources, and ownership boundary for pre-existing changes.
  2. Initialize the ledger and establish baseline format, lint, type, test, build, codegen, API-snapshot, or smoke checks appropriate to the repository. Attribute existing failures before editing.
  3. Identify external contracts, repository-controlled consumers, reflection and serialization surfaces, dynamic imports, case-insensitive filesystem constraints, and language-aware rename tooling.
  4. Derive canonical domain vocabulary from behavior, types, data flow, documentation, tests, and relevant history. History resolves unclear intent; it does not override the current design.

Completion of this phase requires a recorded baseline, explicit contract boundaries, and a ledger covering the entire repository.

Build the Rename Map

Inspect every ledger path and build an evidence-backed map before changing each coherent domain slice. Cover directories, files, packages, modules, namespaces, exports, types, classes, functions, methods, parameters, variables, booleans, constants, tests, fixtures, documentation, configuration, scripts, and CI.

Apply these rules together:

  • Give one concept one canonical term; give distinct concepts distinct terms.
  • Name by domain role, behavior, ownership, lifecycle, units, and polarity rather than incidental implementation.
  • Replace misleading, overloaded, contextless, or generic names such as data, info, item, manager, process, handle, and utils when a specific name is supported by evidence.
  • Align directory, filename, primary export, and module responsibility. Move paths when the current structure obscures ownership or forces names to compensate for poor context.
  • Preserve required language, framework, protocol, and ecosystem idioms. Do not perform a repository-wide casing or synonym rewrite when existing terminology is already coherent.

For every rename group, record the old and new concept, rationale, contract classification, affected consumers, collision risks, dynamic string references, migration order, and proving checks. Resolve ambiguity through symbol and reference inspection before choosing a name. Never use blind global replacement for an overloaded term.

Completion of this phase requires every non-excluded path to be retained with a reasoned naming model, assigned to a validated rename group, or marked blocked with concrete evidence.

Acquire the Implementation Scope

After completing the codebase analysis and rename map, present the evidence-backed refactor plan, including its rename groups, dependency waves, contract boundaries, risks, and proving checks. Before each wave, use the repository's coordination mechanism to claim the cumulative write set: every path already written plus the wave's definitions, consumers found by search, moved paths on both sides, and moved directories recursively. A claim that drops an earlier wave's paths is invalid. Claim the complete worktree (--recursive '.') only when a wave's consumers cannot be enumerated, such as a rename of a pervasive identifier or a top-level directory move. For ai-coord, run ai-coord start 'naming refactor' <paths>... [--recursive <dir>]... and proceed only after it returns READY; BLOCKED, UNKNOWN, and pathless INTENT results do not authorize edits and cannot be overridden by user confirmation. Hold the claim through final verification.

After acquiring the claim, re-read the current commit and worktree status, refresh the ledger, and compare the repository with the recorded baseline. Reinspect every path whose content or presence changed during analysis, then update the rename map, contract boundaries, and proving checks before implementing. The ledger refresh detects path changes, not content changes to existing paths. Because other writers may touch unclaimed paths, re-search for the old names immediately before each wave and during final verification, and extend the claim to any new consumer.

If the repository has no reliable coordination mechanism, ask the user to confirm that no other coding agent will write to the repository through implementation and verification. Stop until the user explicitly confirms that fallback window; do not infer it from a stable worktree, absent processes, or the initial invocation.

Execute in Verified Waves

With the implementation scope active, apply rename groups in coherent dependency waves. Keep every delegated write within the claimed write set. If scope ownership is lost or another writer appears in a claimed path, stop before continuing.

  • Prefer language-server, compiler, or AST-aware rename support for symbols. Use exact text replacement only after proving each occurrence has the same meaning.
  • Move a tracked path with a repository-safe mechanism that does not stage unrelated work. Use an intermediate path for case-only renames on case-insensitive filesystems.
  • Update definitions, consumers, imports, re-exports, tests, fixtures, docs, examples, configuration, CI, selectors, reflection, serialization, and generated sources in the same wave.
  • Change generated output through its generator or schema, then regenerate and verify it. Do not edit vendored sources.
  • Run the narrowest proving checks after each wave. Fix attributable failures before continuing; if parity cannot be established, revert only that wave's edits without repository-wide reset, clean, checkout, or stash commands.
  • Mark ledger paths only after the wave is verified. Refresh the ledger after moves so new paths enter coverage.

Continue until every planned rename is applied or blocked; refactor cost, diff size, and elapsed time are not stopping criteria.

Final Verification and Report

Refresh the ledger, inspect every new path, and repeat the semantic naming pass until it finds no material naming issue. Search for stale old names and paths, including case variants and non-code literals. Run aggregate repository checks and compare them with baseline; no new unexplained failure is acceptable. Verify stable external contracts through available API snapshots, schemas, CLI help, import surfaces, or focused smoke tests.

Lead success with ### ✅ Naming refactor complete — <rename groups> groups · <accounted>/<mapped> paths accounted. Report exact file and directory moves, every public or exported rename, compact local-identifier group counts, baseline-versus-final checks, intentional retained external names, incidental bug fixes, and residual risks. Keep commands, paths, names, diagnostics, and contract identifiers exact and undecorated.

If the ledger is incomplete, behavior parity is unproven, or an external contract would require unapproved breakage, lead with ### ⛔ Naming refactor incomplete and report the blocking evidence and required decision. Do not describe the run as complete while any path remains pending or blocked.

Files (agent-skills)
  • agents
    • openai.yaml 43 B
      policy:
        allow_implicit_invocation: false
      
  • scripts
    • naming-ledger.py 14.7 KB
      #!/usr/bin/env python3
      """Maintain deterministic path coverage for a repository-wide naming refactor."""
      
      from __future__ import annotations
      
      import argparse
      import json
      import os
      import subprocess
      import sys
      import tempfile
      from pathlib import Path
      from typing import Any
      
      
      STATUSES = ("pending", "retained", "renamed", "excluded", "blocked")
      REASON_REQUIRED = ("excluded", "blocked")
      
      
      class LedgerError(ValueError):
          pass
      
      
      def git(root: Path, *args: str) -> bytes:
          result = subprocess.run(["git", *args], cwd=root, capture_output=True)
          if result.returncode:
              message = result.stderr.decode(errors="replace").strip()
              raise LedgerError(message or f"git {' '.join(args)} failed")
          return result.stdout
      
      
      def repo_root(path: Path) -> Path:
          return Path(git(path, "rev-parse", "--show-toplevel").decode().strip()).resolve()
      
      
      def nul_paths(data: bytes) -> list[str]:
          return [value.decode(errors="surrogateescape") for value in data.split(b"\0") if value]
      
      
      def worktree_status(root: Path) -> dict[str, list[str]]:
          fields = nul_paths(git(root, "status", "--porcelain=v1", "-z", "--untracked-files=all"))
          statuses: dict[str, list[str]] = {}
          index = 0
          while index < len(fields):
              entry = fields[index]
              if len(entry) < 4:
                  raise LedgerError("unexpected git status record")
              code, path = entry[:2], entry[3:]
              statuses.setdefault(path, []).append(code)
              if "R" in code or "C" in code:
                  index += 1
                  if index >= len(fields):
                      raise LedgerError("incomplete git rename status record")
                  statuses.setdefault(fields[index], []).append(f"{code}:source")
              index += 1
          return statuses
      
      
      def repository_paths(root: Path) -> tuple[set[str], set[str]]:
          tracked = set(nul_paths(git(root, "ls-files", "-z")))
          untracked = set(nul_paths(git(root, "ls-files", "--others", "--exclude-standard", "-z")))
          return tracked, untracked
      
      
      def path_present(root: Path, path: str) -> bool:
          return os.path.lexists(root / path)
      
      
      def ensure_external_ledger(root: Path, ledger: Path) -> Path:
          resolved = ledger.resolve()
          try:
              resolved.relative_to(root)
          except ValueError:
              return resolved
          raise LedgerError(f"ledger must be outside the repository: {resolved}")
      
      
      def atomic_write(path: Path, payload: dict[str, Any]) -> None:
          encoded = (json.dumps(payload, indent=2, ensure_ascii=False) + "\n").encode()
          descriptor, temporary_name = tempfile.mkstemp(prefix=f".{path.name}.", dir=path.parent)
          temporary = Path(temporary_name)
          try:
              with os.fdopen(descriptor, "wb") as handle:
                  handle.write(encoded)
                  handle.flush()
                  os.fsync(handle.fileno())
              if path.exists():
                  os.chmod(temporary, path.stat().st_mode)
              os.replace(temporary, path)
          finally:
              if temporary.exists():
                  temporary.unlink()
      
      
      def validate_record(item: Any) -> None:
          if not isinstance(item, dict):
              raise LedgerError("ledger contains an invalid path record")
          if not isinstance(item.get("path"), str) or not item["path"]:
              raise LedgerError("ledger contains an invalid path")
          if item.get("status") not in STATUSES:
              raise LedgerError(f"ledger contains an invalid status for {item['path']}")
          if not isinstance(item.get("tracked"), bool) or not isinstance(item.get("present"), bool):
              raise LedgerError(f"ledger contains invalid path state for {item['path']}")
          if not isinstance(item.get("preexistingStatus"), list) or not isinstance(item.get("discoveredStatus"), list):
              raise LedgerError(f"ledger contains invalid worktree state for {item['path']}")
          if not isinstance(item.get("firstSeenRevision"), int) or item["firstSeenRevision"] < 0:
              raise LedgerError(f"ledger contains an invalid discovery revision for {item['path']}")
          if item["status"] in REASON_REQUIRED and not item.get("reason"):
              raise LedgerError(f"{item['status']} path has no reason: {item['path']}")
      
      
      def load(path: Path) -> dict[str, Any]:
          try:
              payload = json.loads(path.read_text(encoding="utf-8"))
          except (OSError, json.JSONDecodeError) as exc:
              raise LedgerError(f"cannot read ledger: {exc}") from exc
          if (
              payload.get("schemaVersion") != 1
              or not isinstance(payload.get("revision"), int)
              or not isinstance(payload.get("repoRoot"), str)
              or not isinstance(payload.get("files"), list)
          ):
              raise LedgerError("unsupported ledger schema")
          paths = set()
          for item in payload["files"]:
              validate_record(item)
              if item["path"] in paths:
                  raise LedgerError(f"ledger contains a duplicate path: {item['path']}")
              paths.add(item["path"])
          return payload
      
      
      def counts(payload: dict[str, Any]) -> dict[str, int]:
          result = {status: 0 for status in STATUSES}
          for item in payload["files"]:
              result[item["status"]] += 1
          result["mapped"] = len(payload["files"])
          result["accounted"] = result["mapped"] - result["pending"]
          result["present"] = sum(item["present"] for item in payload["files"])
          result["missing"] = result["mapped"] - result["present"]
          return result
      
      
      def summary(payload: dict[str, Any]) -> dict[str, Any]:
          summary_counts = counts(payload)
          return {
              "schemaVersion": 1,
              "revision": payload["revision"],
              "counts": summary_counts,
              "complete": summary_counts["pending"] == 0 and summary_counts["blocked"] == 0,
              "preexistingChangedPaths": sum(bool(item["preexistingStatus"]) for item in payload["files"]),
          }
      
      
      def load_dispositions(path: Path, pending_as: str | None = None) -> list[tuple[str, str, str]]:
          try:
              lines = path.read_text(encoding="utf-8").splitlines()
          except (OSError, UnicodeError) as exc:
              raise LedgerError(f"cannot read dispositions file: {exc}") from exc
      
          dispositions: list[tuple[str, str, str]] = []
          first_line_by_path: dict[str, int] = {}
          for line_number, line in enumerate(lines, start=1):
              if not line.strip() or line.lstrip().startswith("#"):
                  continue
              fields = line.split("\t")
              if len(fields) < 2:
                  raise LedgerError(
                      f"dispositions line {line_number}: expected status<TAB>path<TAB>reason"
                  )
              if len(fields) > 3:
                  raise LedgerError(f"dispositions line {line_number}: reason cannot contain a tab")
              status, path = fields[:2]
              reason = fields[2] if len(fields) == 3 else ""
              if status not in STATUSES:
                  raise LedgerError(f"dispositions line {line_number}: unknown status: {status}")
              if status == "pending" and pending_as is not None:
                  status = pending_as
              if not path:
                  raise LedgerError(f"dispositions line {line_number}: path cannot be empty")
              if path in first_line_by_path:
                  raise LedgerError(
                      f"dispositions line {line_number}: duplicate path: {path} "
                      f"(first seen on line {first_line_by_path[path]})"
                  )
              if status in REASON_REQUIRED and not reason:
                  raise LedgerError(f"dispositions line {line_number}: {status} status requires a reason")
              first_line_by_path[path] = line_number
              dispositions.append((status, path, reason))
          return dispositions
      
      
      def new_record(
          root: Path,
          path: str,
          tracked: set[str],
          status: dict[str, list[str]],
          revision: int,
      ) -> dict[str, Any]:
          discovered = status.get(path, [])
          return {
              "path": path,
              "tracked": path in tracked,
              "present": path_present(root, path),
              "preexistingStatus": discovered if revision == 0 else [],
              "discoveredStatus": discovered,
              "firstSeenRevision": revision,
              "status": "pending",
              "reason": None,
          }
      
      
      def init_command(args: argparse.Namespace) -> dict[str, Any]:
          root = repo_root(args.root.resolve())
          ledger = ensure_external_ledger(root, args.ledger)
          if ledger.exists():
              raise LedgerError(f"ledger already exists: {ledger}")
          tracked, untracked = repository_paths(root)
          status = worktree_status(root)
          paths = sorted(tracked | untracked)
          payload = {
              "schemaVersion": 1,
              "revision": 0,
              "repoRoot": str(root),
              "files": [new_record(root, path, tracked, status, 0) for path in paths],
          }
          ledger.parent.mkdir(parents=True, exist_ok=True)
          atomic_write(ledger, payload)
          return {"schemaVersion": 1, "ledger": str(ledger), **summary(payload)}
      
      
      def mark_command(args: argparse.Namespace) -> dict[str, Any]:
          payload = load(args.ledger)
          if args.from_file is not None:
              dispositions = load_dispositions(args.from_file, args.pending_as)
              by_path = {item["path"]: item for item in payload["files"]}
              missing = [path for _, path, _ in dispositions if path not in by_path]
              if missing and not args.skip_unknown:
                  raise LedgerError(f"paths are not in the ledger: {', '.join(missing)}")
              skipped = missing if args.skip_unknown else []
              applicable = [item for item in dispositions if item[1] in by_path]
              absent_retained = [
                  path
                  for status, path, _ in applicable
                  if status == "retained" and not by_path[path]["present"]
              ]
              if absent_retained:
                  raise LedgerError(f"absent paths cannot be retained: {', '.join(absent_retained)}")
              for status, path, reason in applicable:
                  by_path[path]["status"] = status
                  by_path[path]["reason"] = reason or None
              if applicable:
                  payload["revision"] += 1
                  atomic_write(args.ledger, payload)
              return {**summary(payload), "applied": len(applicable), "skipped": skipped}
      
          paths = list(dict.fromkeys(args.path))
          if not paths:
              raise LedgerError("mark requires at least one --path")
          if args.status in REASON_REQUIRED and not args.reason:
              raise LedgerError(f"{args.status} status requires --reason")
          by_path = {item["path"]: item for item in payload["files"]}
          missing = [path for path in paths if path not in by_path]
          if missing:
              raise LedgerError(f"paths are not in the ledger: {', '.join(missing)}")
          absent_retained = [path for path in paths if args.status == "retained" and not by_path[path]["present"]]
          if absent_retained:
              raise LedgerError(f"absent paths cannot be retained: {', '.join(absent_retained)}")
          for path in paths:
              by_path[path]["status"] = args.status
              by_path[path]["reason"] = args.reason
          payload["revision"] += 1
          atomic_write(args.ledger, payload)
          return {"schemaVersion": 1, "updated": paths, "status": args.status, **summary(payload)}
      
      
      def pending_command(args: argparse.Namespace) -> dict[str, Any]:
          payload = load(args.ledger)
          paths = [item["path"] for item in payload["files"] if item["status"] == "pending"]
          if args.limit is not None:
              paths = paths[: args.limit]
          return {
              "schemaVersion": 1,
              "revision": payload["revision"],
              "paths": paths,
              "remaining": counts(payload)["pending"],
          }
      
      
      def refresh_command(args: argparse.Namespace) -> dict[str, Any]:
          payload = load(args.ledger)
          root = repo_root(Path(payload["repoRoot"]))
          if str(root) != payload["repoRoot"]:
              raise LedgerError(f"repository root changed: {root}")
          tracked, untracked = repository_paths(root)
          status = worktree_status(root)
          current_paths = tracked | untracked
          by_path = {item["path"]: item for item in payload["files"]}
          changed = False
          for item in payload["files"]:
              present = path_present(root, item["path"])
              if item["present"] != present:
                  item["present"] = present
                  changed = True
          next_revision = payload["revision"] + 1
          added_paths = sorted(current_paths - by_path.keys())
          for path in added_paths:
              payload["files"].append(new_record(root, path, tracked, status, next_revision))
              changed = True
          if changed:
              payload["files"].sort(key=lambda item: item["path"])
              payload["revision"] = next_revision
              atomic_write(args.ledger, payload)
          missing_paths = [item["path"] for item in payload["files"] if not item["present"]]
          return {
              "schemaVersion": 1,
              "addedPaths": added_paths,
              "missingPaths": missing_paths,
              **summary(payload),
          }
      
      
      def build_parser() -> argparse.ArgumentParser:
          parser = argparse.ArgumentParser(description=__doc__)
          subparsers = parser.add_subparsers(dest="command", required=True)
          init = subparsers.add_parser("init")
          init.add_argument("--root", type=Path, default=Path.cwd())
          init.add_argument("--ledger", type=Path, required=True)
          init.set_defaults(handler=init_command)
          mark = subparsers.add_parser("mark")
          mark.add_argument("--ledger", type=Path, required=True)
          mark_mode = mark.add_mutually_exclusive_group(required=True)
          mark_mode.add_argument("--status", choices=STATUSES)
          mark_mode.add_argument("--from-file", type=Path)
          mark.add_argument("--path", action="append", default=[])
          mark.add_argument("--reason")
          mark.add_argument("--skip-unknown", action="store_true")
          mark.add_argument("--pending-as", choices=[status for status in STATUSES if status != "pending"])
          mark.set_defaults(handler=mark_command)
          pending = subparsers.add_parser("pending")
          pending.add_argument("--ledger", type=Path, required=True)
          pending.add_argument("--limit", type=int)
          pending.set_defaults(handler=pending_command)
          refresh = subparsers.add_parser("refresh")
          refresh.add_argument("--ledger", type=Path, required=True)
          refresh.set_defaults(handler=refresh_command)
          show = subparsers.add_parser("summary")
          show.add_argument("--ledger", type=Path, required=True)
          show.set_defaults(handler=lambda args: summary(load(args.ledger)))
          return parser
      
      
      def main() -> int:
          parser = build_parser()
          args = parser.parse_args()
          if args.command == "mark" and args.from_file is not None and (args.path or args.reason is not None):
              parser.error("mark --from-file cannot be used with --path or --reason")
          if args.command == "mark" and args.from_file is None and args.skip_unknown:
              parser.error("mark --skip-unknown requires --from-file")
          if args.command == "mark" and args.from_file is None and args.pending_as is not None:
              parser.error("mark --pending-as requires --from-file")
          try:
              if getattr(args, "limit", 1) is not None and getattr(args, "limit", 1) < 1:
                  raise LedgerError("--limit must be positive")
              result = args.handler(args)
          except LedgerError as exc:
              print(f"ERROR: {exc}", file=sys.stderr)
              return 64
          print(json.dumps(result, indent=2, ensure_ascii=False))
          return 0
      
      
      if __name__ == "__main__":
          raise SystemExit(main())
      
  • SKILL.md 11.5 KB
    ---
    compatibility: Requires Git, uv, and local command and edit access.
    disable-model-invocation: true
    name: naming-refactor
    description: Refactor naming and repository structure exhaustively while preserving behavior and external contracts.
    ---
    
    # Naming Refactor
    
    If these instructions are already present in the conversation from a slash or dollar invocation, follow them directly;
    do not invoke this skill again through a skill tool.
    
    Make every name in the current repository communicate one coherent domain model, regardless of refactor cost.
    
    ## Contract
    
    - Cover the complete current Git repository. Do not narrow the run to selected files or stop after a candidate cap.
    - Preserve features, runtime behavior, side effects, performance-sensitive characteristics, and externally consumed
      contracts. Large diffs, path moves, and repository-controlled interface migrations are allowed.
    - Treat an exported surface as repository-controlled only when every consumer is in scope and can migrate atomically.
      Preserve other API names, CLI flags, environment variables, configuration keys, wire fields, routes, database names,
      and import paths unless the user explicitly authorizes a breaking migration.
    - Fix a bug discovered during the refactor only when the defect and intended behavior are clear and a regression check
      can prove the fix. Keep bug fixes in a distinct change wave and report them separately.
    - Preserve pre-existing work. Local edits, file moves, directory restructuring, and non-destructive validation are
      authorized. Do not commit, push, publish, or write externally unless the user or repository instructions require it.
    - Serialize implementation and verification against intersecting repository writes. Complete codebase analysis and the
      refactor plan first, then claim the cumulative write set before each wave as defined under Acquire the Implementation
      Scope. Claim the complete worktree only when consumers cannot be enumerated. If no reliable coordination mechanism is
      available, require the user to confirm an exclusive write window instead.
    - A verified no-op is valid only after exhaustive coverage. Do not rename a clear, conventional name merely to create
      churn, but do not retain a weak name to minimize diff size.
    
    ## Coverage Ledger
    
    Resolve `scripts/naming-ledger.py` relative to this `SKILL.md` and create its ledger outside the repository:
    
    ```sh
    uv run "<skill-dir>/scripts/naming-ledger.py" init --root <repo> --ledger <scratch.json>
    ```
    
    The helper maps every tracked and non-ignored untracked path and records pre-existing worktree state. Account for a path
    only after inspecting its name, relevant contents, and role:
    
    ```sh
    uv run "<skill-dir>/scripts/naming-ledger.py" mark \
      --ledger <scratch.json> --status <pending|retained|renamed|excluded|blocked> \
      --path <path> [--path <path>...] [--reason <text>]
    ```
    
    Use `retained` when the current name is justified, `renamed` when the path or its contents joined a verified rename,
    `excluded` for generated, vendored, binary, or bulk artifacts validated through their source or invariant, and `blocked`
    when behavior or contract safety cannot be established. `excluded` and `blocked` require reasons.
    
    For delegated runs, have each subagent write a dispositions TSV outside the repository with one record per line:
    `status<TAB>repo-relative-path<TAB>reason`. Apply each file in one batch:
    
    ```sh
    uv run "<skill-dir>/scripts/naming-ledger.py" mark \
      --ledger <scratch.json> --from-file <dispositions.tsv>
    ```
    
    A subagent reports a planned but unverified rename as `pending`. After the wave is verified, apply its file with
    `--pending-as renamed` (or another final status) instead of rewriting the TSV; reasons carry over, and `excluded` or
    `blocked` still require one.
    
    Dispositions paths are relative to the repository root and must match ledger entries exactly. The ledger maps only
    tracked and non-ignored untracked files; gitignored artifacts, cache files, and bare directories are never ledger paths.
    Unknown paths fail the batch closed. Rerun with `--skip-unknown` only after reviewing the reported paths and confirming
    that every skip is intentional, then verify the result's `skipped` list.
    
    ```sh
    uv run "<skill-dir>/scripts/naming-ledger.py" pending --ledger <scratch.json> [--limit <n>]
    uv run "<skill-dir>/scripts/naming-ledger.py" refresh --ledger <scratch.json>
    uv run "<skill-dir>/scripts/naming-ledger.py" summary --ledger <scratch.json>
    ```
    
    Refresh after path moves and before final validation. New paths become pending; removed paths remain in the ledger and
    must be accounted as renamed, excluded, or blocked. The run is complete only when the helper reports no pending or
    blocked paths.
    
    ## Ground the Refactor
    
    1. Read applicable repository instructions. Record the repository root, starting commit and status, build and check
       commands, generated sources, and ownership boundary for pre-existing changes.
    2. Initialize the ledger and establish baseline format, lint, type, test, build, codegen, API-snapshot, or smoke checks
       appropriate to the repository. Attribute existing failures before editing.
    3. Identify external contracts, repository-controlled consumers, reflection and serialization surfaces, dynamic imports,
       case-insensitive filesystem constraints, and language-aware rename tooling.
    4. Derive canonical domain vocabulary from behavior, types, data flow, documentation, tests, and relevant history.
       History resolves unclear intent; it does not override the current design.
    
    Completion of this phase requires a recorded baseline, explicit contract boundaries, and a ledger covering the entire
    repository.
    
    ## Build the Rename Map
    
    Inspect every ledger path and build an evidence-backed map before changing each coherent domain slice. Cover
    directories, files, packages, modules, namespaces, exports, types, classes, functions, methods, parameters, variables,
    booleans, constants, tests, fixtures, documentation, configuration, scripts, and CI.
    
    Apply these rules together:
    
    - Give one concept one canonical term; give distinct concepts distinct terms.
    - Name by domain role, behavior, ownership, lifecycle, units, and polarity rather than incidental implementation.
    - Replace misleading, overloaded, contextless, or generic names such as `data`, `info`, `item`, `manager`, `process`,
      `handle`, and `utils` when a specific name is supported by evidence.
    - Align directory, filename, primary export, and module responsibility. Move paths when the current structure obscures
      ownership or forces names to compensate for poor context.
    - Preserve required language, framework, protocol, and ecosystem idioms. Do not perform a repository-wide casing or
      synonym rewrite when existing terminology is already coherent.
    
    For every rename group, record the old and new concept, rationale, contract classification, affected consumers,
    collision risks, dynamic string references, migration order, and proving checks. Resolve ambiguity through symbol and
    reference inspection before choosing a name. Never use blind global replacement for an overloaded term.
    
    Completion of this phase requires every non-excluded path to be retained with a reasoned naming model, assigned to a
    validated rename group, or marked blocked with concrete evidence.
    
    ## Acquire the Implementation Scope
    
    After completing the codebase analysis and rename map, present the evidence-backed refactor plan, including its rename
    groups, dependency waves, contract boundaries, risks, and proving checks. Before each wave, use the repository's
    coordination mechanism to claim the cumulative write set: every path already written plus the wave's definitions,
    consumers found by search, moved paths on both sides, and moved directories recursively. A claim that drops an earlier
    wave's paths is invalid. Claim the complete worktree (`--recursive '.'`) only when a wave's consumers cannot be
    enumerated, such as a rename of a pervasive identifier or a top-level directory move. For `ai-coord`, run
    `ai-coord start 'naming refactor' <paths>... [--recursive <dir>]...` and proceed only after it returns `READY`;
    `BLOCKED`, `UNKNOWN`, and pathless `INTENT` results do not authorize edits and cannot be overridden by user
    confirmation. Hold the claim through final verification.
    
    After acquiring the claim, re-read the current commit and worktree status, refresh the ledger, and compare the
    repository with the recorded baseline. Reinspect every path whose content or presence changed during analysis, then
    update the rename map, contract boundaries, and proving checks before implementing. The ledger refresh detects path
    changes, not content changes to existing paths. Because other writers may touch unclaimed paths, re-search for the old
    names immediately before each wave and during final verification, and extend the claim to any new consumer.
    
    If the repository has no reliable coordination mechanism, ask the user to confirm that no other coding agent will write
    to the repository through implementation and verification. Stop until the user explicitly confirms that fallback window;
    do not infer it from a stable worktree, absent processes, or the initial invocation.
    
    ## Execute in Verified Waves
    
    With the implementation scope active, apply rename groups in coherent dependency waves. Keep every delegated write
    within the claimed write set. If scope ownership is lost or another writer appears in a claimed path, stop before
    continuing.
    
    - Prefer language-server, compiler, or AST-aware rename support for symbols. Use exact text replacement only after
      proving each occurrence has the same meaning.
    - Move a tracked path with a repository-safe mechanism that does not stage unrelated work. Use an intermediate path for
      case-only renames on case-insensitive filesystems.
    - Update definitions, consumers, imports, re-exports, tests, fixtures, docs, examples, configuration, CI, selectors,
      reflection, serialization, and generated sources in the same wave.
    - Change generated output through its generator or schema, then regenerate and verify it. Do not edit vendored sources.
    - Run the narrowest proving checks after each wave. Fix attributable failures before continuing; if parity cannot be
      established, revert only that wave's edits without repository-wide reset, clean, checkout, or stash commands.
    - Mark ledger paths only after the wave is verified. Refresh the ledger after moves so new paths enter coverage.
    
    Continue until every planned rename is applied or blocked; refactor cost, diff size, and elapsed time are not stopping
    criteria.
    
    ## Final Verification and Report
    
    Refresh the ledger, inspect every new path, and repeat the semantic naming pass until it finds no material naming issue.
    Search for stale old names and paths, including case variants and non-code literals. Run aggregate repository checks and
    compare them with baseline; no new unexplained failure is acceptable. Verify stable external contracts through available
    API snapshots, schemas, CLI help, import surfaces, or focused smoke tests.
    
    Lead success with `### ✅ Naming refactor complete — <rename groups> groups · <accounted>/<mapped> paths accounted`.
    Report exact file and directory moves, every public or exported rename, compact local-identifier group counts,
    baseline-versus-final checks, intentional retained external names, incidental bug fixes, and residual risks. Keep
    commands, paths, names, diagnostics, and contract identifiers exact and undecorated.
    
    If the ledger is incomplete, behavior parity is unproven, or an external contract would require unapproved breakage,
    lead with `### ⛔ Naming refactor incomplete` and report the blocking evidence and required decision. Do not describe
    the run as complete while any path remains pending or blocked.
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related