* feat: auto-remap codebase after significant phase execution (#2003) Adds a post-phase structural drift detector that compares the committed tree against `.planning/codebase/STRUCTURE.md` and either warns or auto-remaps the affected subtrees when drift exceeds a configurable threshold. ## Summary - New `bin/lib/drift.cjs` — pure detector covering four drift categories: new directories outside mapped paths, new barrel exports at `(packages|apps)/*/src/index.*`, new migration files, and new route modules. Prioritizes the most-specific category per file. - New `verify codebase-drift` CLI subcommand + SDK handler, registered as `gsd-sdk query verify.codebase-drift`. - New `codebase_drift_gate` step in `execute-phase` between `schema_drift_gate` and `verify_phase_goal`. Non-blocking by contract — any error logs and the phase continues. - Two new config keys: `workflow.drift_threshold` (int, default 3) and `workflow.drift_action` (`warn` | `auto-remap`, default `warn`), with enum/integer validation in `config-set`. - `gsd-codebase-mapper` learns an optional `--paths <p1,p2,...>` scope hint for incremental remapping; agent/workflow docs updated. - `last_mapped_commit` lives in YAML frontmatter on each `.planning/codebase/*.md` file; `readMappedCommit`/`writeMappedCommit` round-trip helpers ship in `drift.cjs`. ## Tests - 55 new tests in `tests/drift-detection.test.cjs` covering: classification, threshold gating at 2/3/4 elements, warn vs. auto-remap routing, affected-path scoping, `--paths` sanitization (traversal, absolute, shell metacharacter rejection), frontmatter round-trip, defensive paths (missing STRUCTURE.md, malformed input, non-git repos), CLI JSON output, and documentation parity. - Full suite: 5044 pass / 0 fail. ## Documentation - `docs/CONFIGURATION.md` — rows for both new keys. - `docs/ARCHITECTURE.md` — section on the post-execute drift gate. - `docs/AGENTS.md` — `--paths` flag on `gsd-codebase-mapper`. - `docs/USER-GUIDE.md` — user-facing behavior note + toggle commands. - `docs/FEATURES.md` — new 27a section with REQ-DRIFT-01..06. - `docs/INVENTORY.md` + `docs/INVENTORY-MANIFEST.json` — drift.cjs listed. - `get-shit-done/workflows/execute-phase.md` — `codebase_drift_gate` step. - `get-shit-done/workflows/map-codebase.md` — `parse_paths_flag` step. - `agents/gsd-codebase-mapper.md` — `--paths` directive under parse_focus. ## Design decisions - **Frontmatter over sidecar JSON** for `last_mapped_commit`: keeps the baseline attached to the file, survives git moves, survives per-doc regeneration, no extra file lifecycle. - **Substring match against STRUCTURE.md** for `isPathMapped`: the map is free-form markdown, not a structured manifest; any mention of a path prefix counts as "mapped territory". Cheap, no parser, zero false negatives on reasonable maps. - **Category priority migration > route > barrel > new_dir** so a file matching multiple rules counts exactly once at the most specific level. - **Empty-tree SHA fallback** (`4b825dc6…`) when `last_mapped_commit` is absent — semantically correct (no baseline means everything is drift) and deterministic across repos. - **Four layers of non-blocking** — detector try/catch, CLI try/catch, SDK handler try/catch, and workflow `|| echo` shell fallback. Any single layer failing still returns a valid skipped result. - **SDK handler delegates to `gsd-tools.cjs`** rather than re-porting the detector to TypeScript, keeping drift logic in one canonical place. Closes #2003 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(mapper): tag --paths fenced block as text (CodeRabbit MD040) Comment 3127255172. * docs(config): use /gsd- dash command syntax in drift_action row (CodeRabbit) Comment 3127255180. Matches the convention used by every other command reference in docs/CONFIGURATION.md. * fix(execute-phase): initialize AGENT_SKILLS_MAPPER + tag fenced blocks Two CodeRabbit findings on the auto-remap branch of the drift gate: - 3127255186 (must-fix): the mapper Task prompt referenced ${AGENT_SKILLS_MAPPER} but only AGENT_SKILLS (for gsd-executor) is loaded at init_context (line 72). Without this fix the literal placeholder string would leak into the spawned mapper's prompt. Add an explicit gsd-sdk query agent-skills gsd-codebase-mapper step right before the Task spawn. - 3127255183: tag the warn-message and Task() fenced code blocks as text to satisfy markdownlint MD040. * docs(map-codebase): wire PATH_SCOPE_HINT through every mapper prompt CodeRabbit (review id 4158286952, comment 3127255190) flagged that the parse_paths_flag step defined incremental-remap semantics but did not inject a normalized variable into the spawn_agents and sequential_mapping mapper prompts, so incremental remap could silently regress to a whole-repo scan. - Define SCOPED_PATHS / PATH_SCOPE_HINT in parse_paths_flag. - Inject ${PATH_SCOPE_HINT} into all four spawn_agents Task prompts. - Document the same scope contract for sequential_mapping mode. * fix(drift): writeMappedCommit tolerates missing target file CodeRabbit (review id 4158286952, drift.cjs:349-355 nitpick) noted that readMappedCommit returns null on ENOENT but writeMappedCommit threw — an asymmetry that breaks first-time stamping of a freshly produced doc that the caller has not yet written. - Catch ENOENT on the read; treat absent file as empty content. - Add a regression test that calls writeMappedCommit on a non-existent path and asserts the file is created with correct frontmatter. Test was authored to fail before the fix (ENOENT) and passes after. --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -27,6 +27,44 @@ Documents are reference material for Claude when planning/executing. Always incl
|
||||
|
||||
<process>
|
||||
|
||||
<step name="parse_paths_flag" priority="first">
|
||||
Parse an optional `--paths <p1,p2,...>` argument. When supplied (by the
|
||||
post-execute codebase-drift gate in `/gsd:execute-phase` or by a user running
|
||||
`/gsd:map-codebase --paths apps/accounting,packages/ui`), the workflow
|
||||
operates in **incremental-remap mode**:
|
||||
|
||||
- Pass `--paths <p1>,<p2>,...` through to each spawned `gsd-codebase-mapper`
|
||||
agent's prompt. Agents scope their Glob/Grep/Bash exploration to the listed
|
||||
repo-relative prefixes only — no whole-repo scan.
|
||||
- Reject path values that contain `..`, start with `/`, or include shell
|
||||
metacharacters (`;`, `` ` ``, `$`, `&`, `|`, `<`, `>`). If all provided
|
||||
paths are invalid, fall back to a normal whole-repo run.
|
||||
- On write, each mapper stamps `last_mapped_commit: <HEAD sha>` into the YAML
|
||||
frontmatter of every document it produces (see `bin/lib/drift.cjs:writeMappedCommit`).
|
||||
|
||||
**Explicit contract — propagate `--paths` through a single normalized
|
||||
variable.** Downstream steps (`spawn_agents`, `sequential_mapping`, and any
|
||||
Task-mode prompt construction) MUST use `${PATH_SCOPE_HINT}` to ensure every
|
||||
mapper receives the same deterministic scope. Without this contract
|
||||
incremental-remap can silently regress to a whole-repo scan.
|
||||
|
||||
```bash
|
||||
# Validated, comma-separated paths (empty if --paths absent or all rejected):
|
||||
SCOPED_PATHS="<validated paths or empty>"
|
||||
if [ -n "$SCOPED_PATHS" ]; then
|
||||
PATH_SCOPE_HINT="--paths $SCOPED_PATHS"
|
||||
else
|
||||
PATH_SCOPE_HINT=""
|
||||
fi
|
||||
```
|
||||
|
||||
All mapper prompts built later in this workflow MUST include
|
||||
`${PATH_SCOPE_HINT}` (expanded to empty when full-repo mode is in effect).
|
||||
|
||||
When `--paths` is absent, behave exactly as before: full-repo scan, all 7
|
||||
documents refreshed.
|
||||
</step>
|
||||
|
||||
<step name="init_context" priority="first">
|
||||
Load codebase mapping context:
|
||||
|
||||
@@ -124,6 +162,8 @@ Write these documents to .planning/codebase/:
|
||||
|
||||
IMPORTANT: Use {date} for all [YYYY-MM-DD] date placeholders in documents.
|
||||
|
||||
Scope: ${PATH_SCOPE_HINT:-(full repo)} — when --paths is supplied, restrict exploration to those prefixes only.
|
||||
|
||||
Explore thoroughly. Write documents directly using templates. Return confirmation only.
|
||||
${AGENT_SKILLS_MAPPER}"
|
||||
)
|
||||
@@ -148,6 +188,8 @@ Write these documents to .planning/codebase/:
|
||||
|
||||
IMPORTANT: Use {date} for all [YYYY-MM-DD] date placeholders in documents.
|
||||
|
||||
Scope: ${PATH_SCOPE_HINT:-(full repo)} — when --paths is supplied, restrict exploration to those prefixes only.
|
||||
|
||||
Explore thoroughly. Write documents directly using templates. Return confirmation only.
|
||||
${AGENT_SKILLS_MAPPER}"
|
||||
)
|
||||
@@ -172,6 +214,8 @@ Write these documents to .planning/codebase/:
|
||||
|
||||
IMPORTANT: Use {date} for all [YYYY-MM-DD] date placeholders in documents.
|
||||
|
||||
Scope: ${PATH_SCOPE_HINT:-(full repo)} — when --paths is supplied, restrict exploration to those prefixes only.
|
||||
|
||||
Explore thoroughly. Write documents directly using templates. Return confirmation only.
|
||||
${AGENT_SKILLS_MAPPER}"
|
||||
)
|
||||
@@ -195,6 +239,8 @@ Write this document to .planning/codebase/:
|
||||
|
||||
IMPORTANT: Use {date} for all [YYYY-MM-DD] date placeholders in documents.
|
||||
|
||||
Scope: ${PATH_SCOPE_HINT:-(full repo)} — when --paths is supplied, restrict exploration to those prefixes only.
|
||||
|
||||
Explore thoroughly. Write document directly using template. Return confirmation only.
|
||||
${AGENT_SKILLS_MAPPER}"
|
||||
)
|
||||
@@ -246,6 +292,8 @@ When the `Task` tool is unavailable, perform codebase mapping sequentially in th
|
||||
|
||||
**IMPORTANT:** Use `{date}` from init context for all `[YYYY-MM-DD]` date placeholders in documents. NEVER guess the date.
|
||||
|
||||
**SCOPE:** When `${PATH_SCOPE_HINT}` is non-empty (i.e. `--paths` was supplied), restrict every pass below to the validated path prefixes in `${SCOPED_PATHS}`. Do NOT scan files outside those prefixes. When `${PATH_SCOPE_HINT}` is empty, perform a full-repo scan.
|
||||
|
||||
Perform all 4 mapping passes sequentially:
|
||||
|
||||
**Pass 1: Tech Focus**
|
||||
|
||||
Reference in New Issue
Block a user