Skip to content

chore: add spec-drift detection commands and pre-commit hook#649

Open
ciscoRankush wants to merge 2 commits intonextfrom
spec-drift-tooling
Open

chore: add spec-drift detection commands and pre-commit hook#649
ciscoRankush wants to merge 2 commits intonextfrom
spec-drift-tooling

Conversation

@ciscoRankush
Copy link
Contributor

This pull request addresses

Adding automated SDD (Spec-Driven Development) documentation drift detection tooling to ensure ai-docs stay in sync with source code.

by making the following changes

  • .claude/commands/spec-drift.md — Full scan command that auto-discovers all ai-docs folders, spawns parallel checker agents, and validates 7 drift categories (file tree, method/API, types, events, architecture, links, code examples)
  • .claude/commands/spec-drift-changed.md — Incremental scan that only validates ai-docs affected by staged/unstaged file changes, with verification marker creation
  • .claude/hooks/check-ai-docs-drift.sh — Pre-commit blocking hook that prevents committing when ai-docs drift is unverified

Change Type

  • Tooling change

The following scenarios were tested

  • Verified spec-drift command discovers all ai-docs folders and produces a categorized report
  • Verified spec-drift-changed detects changed files and maps them to their ai-docs
  • Verified pre-commit hook blocks commits with unverified ai-docs changes (exit code 2)
  • Verified hook passes after spec-drift-changed creates verification marker
  • Verified hook is transparent for commits without ai-docs-related changes

The GAI Coding Policy And Copyright Annotation Best Practices

  • GAI was used to create a draft that was subsequently customized or modified
  • Tool used for AI assistance (GitHub Copilot / Other - specify)
    • Other - Claude Code (Anthropic)
  • This PR is related to
    • Automation

I certified that

  • I have read and followed contributing guidelines
  • I discussed changes with code owners prior to submitting this pull request
  • I have not skipped any automated checks
  • All existing and new tests passed
  • I have updated the documentation accordingly

Add automated SDD documentation drift detection tooling:
- /spec-drift: full scan command for validating all ai-docs against source code
- /spec-drift-changed: incremental scan for changed files only
- Pre-commit hook to block commits when ai-docs drift is unverified

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@ciscoRankush ciscoRankush requested a review from a team as a code owner March 11, 2026 08:42
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 17636199d6

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +20 to +22
case "$COMMAND" in
git\ commit*) ;; # Continue to check
*) exit 0 ;; # Not a commit, allow immediately

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Detect commit invocations beyond literal git commit

The hook only enforces validation when the command string starts with git commit, so commit flows like git -c user.name=... commit ..., HUSKY=0 git commit ..., or chained commands such as git add . && git commit ... will bypass this guard and allow unverified contact-center changes to be committed. Since this script is meant to be the blocking gate, matching only one command shape leaves a real enforcement hole.

Useful? React with 👍 / 👎.

esac

# Get staged files under the contact-center package
STAGED_CC=$(git diff --cached --name-only 2>/dev/null | grep "^${CC_PKG}/")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Bind verification marker to staged content, not paths

The marker hash is derived from git diff --cached --name-only output, which means it changes only when the set of staged file paths changes; if a developer runs /spec-drift-changed, then edits and restages the same files, the existing marker still matches and the commit is allowed without re-validating docs against the new content. This weakens the drift check and can let stale verification pass.

Useful? React with 👍 / 👎.

@aws-amplify-us-east-2
Copy link

This pull request is automatically being deployed by Amplify Hosting (learn more).

Access this pull request here: https://pr-649.d1b38q61t1z947.amplifyapp.com

Copy link
Contributor

@Shreyas281299 Shreyas281299 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

Severity Count
High 3
Medium 3
Low 6

Recommendation: Request Changes -- The 3 High severity issues should be addressed before merge:

  1. Register the hook in settings.json (otherwise the hook is dead code)
  2. Fix "Task tool" -> "Agent tool" references (otherwise the commands will fail)
  3. Add python3 availability check (otherwise the hook silently becomes a no-op)

Positive Observations

  • Well-structured command files with clear step-by-step instructions and output format templates
  • The 7-category drift detection framework is comprehensive and well-defined
  • Good use of severity levels (Blocking/Important/Medium/Minor) in the drift report
  • The incremental scan (spec-drift-changed) is a practical addition for pre-commit workflows
  • The shell hook logic is concise and handles the common cases correctly

@@ -0,0 +1,49 @@
#!/bin/bash
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH: Hook is not registered in settings.json -- dead code

How will we set this? The hook script exists but the PR does NOT include any changes to .claude/settings.json or .claude/settings.local.json to register it as a PreToolUse hook. The current .claude/settings.local.json only contains permissions -- no hooks configuration. Without registration, this hook will never execute and the entire gating mechanism is non-functional.

Suggested fix: Add hook registration to .claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "command": ".claude/hooks/check-ai-docs-drift.sh"
      }
    ]
  }
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3cf0a3f. Added .claude/settings.json with the PreToolUse hook registration for Bash commands.


# Read tool input from stdin (JSON with tool_input.command)
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | python3 -c "import sys,json; print(json.load(sys.stdin).get('tool_input',{}).get('command',''))" 2>/dev/null)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH: Silent failure when python3 is unavailable

The script uses python3 -c to parse JSON from stdin, with 2>/dev/null suppressing errors. If python3 is not installed, COMMAND silently becomes an empty string, the case statement falls through to *) exit 0, and the hook always passes -- silently disabling the gate with no warning.

Suggested fix: Add a prerequisite check before this line:

command -v python3 >/dev/null 2>&1 || { echo "python3 required for spec-drift hook" >&2; exit 0; }

Alternatively, use jq or pure bash string matching for the simpler JSON structure.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3cf0a3f. Added python3 availability check (command -v python3) and a fail-closed guard — if COMMAND is empty after parsing, the hook now exits with code 2 (block) instead of 0 (allow).


- Do NOT auto-fix anything — report findings only
- Always read actual source code to verify — never assume
- Use the Task tool with `subagent_type: "Explore"` for checker agents
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH: References non-existent "Task tool" instead of "Agent tool"

While a TaskCreate tool exists in Claude Code, it is for task list/progress tracking and does not accept a subagent_type parameter. The subagent_type: "Explore" parameter belongs to the Agent tool. Claude would fail or do the wrong thing when trying to follow this instruction.

Suggested fix:

Suggested change
- Use the Task tool with `subagent_type: "Explore"` for checker agents
- Use the Agent tool with `subagent_type: "Explore"` for checker agents

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3cf0a3f. Changed all references from "Task tool" to "Agent tool" with subagent_type: "Explore".


## Step 2: Spawn Checker Agents in Parallel

Use the Task tool to spawn agents. **All agents run in parallel.**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH: References non-existent "Task tool" instead of "Agent tool"

Same issue as in spec-drift-changed.md. TaskCreate is for task tracking, not for spawning agents. The subagent_type: "Explore" parameter belongs to the Agent tool.

Suggested fix:

Suggested change
Use the Task tool to spawn agents. **All agents run in parallel.**
Use the Agent tool to spawn agents. **All agents run in parallel.**

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3cf0a3f. Changed to "Agent tool" here as well.


- Do NOT auto-fix anything — report findings only
- Always read actual source code to verify — never assume
- Use the Task tool with `subagent_type: "Explore"` for all checker agents
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH: Same "Task tool" → "Agent tool" fix needed here

Suggested fix:

Suggested change
- Use the Task tool with `subagent_type: "Explore"` for all checker agents
- Use the Agent tool with `subagent_type: "Explore"` for all checker agents

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3cf0a3f. Updated to "Agent tool" in the Rules section as well.

COMMAND=$(echo "$INPUT" | python3 -c "import sys,json; print(json.load(sys.stdin).get('tool_input',{}).get('command',''))" 2>/dev/null)

# Only gate git commit commands
case "$COMMAND" in
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LOW: Overly broad case pattern for commit detection

The pattern git\ commit* matches any command starting with "git commit", including git plumbing commands like git commit-tree. While unlikely to cause issues in practice, a more precise pattern would be:

case "$COMMAND" in
  "git commit"|git\ commit\ *) ;;  # Match 'git commit' standalone or with args
  *) exit 0 ;;
esac

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3cf0a3f. Updated case pattern to "git commit"|git\ commit\ * to avoid matching git commit-tree and similar plumbing commands.

# Compute hash of all staged contact-center files
HASH=$(echo "$STAGED_CC" | sort | shasum | cut -d' ' -f1)
MARKER="/tmp/.spec-drift-verified-${HASH}"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LOW: shasum portability across platforms

shasum (SHA-1 default) is macOS-native. On some Linux distributions, only sha256sum is available. For cross-platform teams, consider a fallback:

HASH=$(echo "$STAGED_CC" | sort | (shasum 2>/dev/null || sha256sum) | cut -d' ' -f1)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3cf0a3f. Added (shasum 2>/dev/null || sha256sum) fallback in both the hook and the marker creation in spec-drift-changed.md.

```

For task widget source files, map to the widget-specific ai-docs:
- `packages/contact-center/task/src/widgets/CallControl/*` → `packages/contact-center/task/ai-docs/widgets/CallControl/`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LOW: Hardcoded widget path mappings

The mapping for task widget source files to ai-docs is hardcoded to 4 specific widgets (CallControl, IncomingTask, OutdialCall, TaskList). New widgets added to task/src/widgets/ would not be automatically detected.

Suggested fix: Add a dynamic pattern instruction:

For any widget under packages/contact-center/task/src/widgets/{WidgetName}/*,
map to packages/contact-center/task/ai-docs/widgets/{WidgetName}/ if the
ai-docs folder exists.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3cf0a3f. Replaced the 4 hardcoded widget paths with a dynamic pattern: packages/contact-center/task/src/widgets/{WidgetName}/*packages/contact-center/task/ai-docs/widgets/{WidgetName}/ (if ai-docs folder exists). New widgets are automatically covered.

- Always read actual source code to verify — never assume
- Use the Task tool with `subagent_type: "Explore"` for checker agents
- Run agents in parallel when multiple folders are affected
- Always create the verification marker at the end, even if there are findings (the developer decides whether to fix or commit as-is)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LOW: Marker created regardless of blocking findings (design observation)

This rule makes the tool purely advisory -- even "Blocking" severity findings (described as "wrong API that would cause runtime errors") won't actually block the commit. Consider documenting this advisory-only nature more prominently in the report output, so developers understand the tool's stance.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3cf0a3f. Updated the rule to explicitly state: "this tool is advisory: the developer decides whether to fix or commit as-is".


For each ai-docs folder found, identify its corresponding source code directory (the parent directory of `ai-docs/`).

Build an inventory like:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LOW: Hardcoded ai-docs inventory is exemplary but could be clearer

The inventory table lists 12+ specific paths and ends with "... (discover all that exist on the current branch)". While clearly intended as illustrative, consider adding an explicit label like "Example inventory (actual results may vary):" to prevent any ambiguity.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3cf0a3f. Changed to "Build an inventory (example — actual results will vary based on current branch):".

- Register hook in settings.json (was dead code without it)
- Fail-closed when python3 unavailable or JSON parse fails
- Use content-based hash (git diff --cached) instead of path-only hash
- Don't consume marker in PreToolUse — invalidated naturally by content changes
- Precise case pattern to avoid matching git commit-tree
- Add shasum/sha256sum portability fallback
- Fix "Task tool" → "Agent tool" references in command files
- Add agent timeout handling rule
- Add staged-only content note for verification marker
- Make widget path mappings dynamic instead of hardcoded
- Label inventory as example
- Document advisory-only nature of verification

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3cf0a3f6e5

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

1. **Repo detection**: This is ccWidgets — `packages/contact-center/` exists (no `@webex` scope)
2. **Root AGENTS.md**: `AGENTS.md` (repo root)
3. **Framework docs**: `ai-docs/` (README, RULES, patterns/*, templates/*)
4. **Package-level ai-docs**: Glob for `packages/contact-center/**/ai-docs/` to find all ai-docs folders

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Discover task widget doc folders explicitly

packages/contact-center/**/ai-docs/ only finds directories literally named ai-docs, so in this repo it returns packages/contact-center/task/ai-docs but not packages/contact-center/task/ai-docs/widgets/{CallControl,IncomingTask,OutdialCall,TaskList}. I checked that packages/contact-center/task/ai-docs itself has no markdown files, so /spec-drift would skip every task-widget AGENTS.md/ARCHITECTURE.md and silently miss drift in that scope.

Useful? React with 👍 / 👎.

From the changed files, build TWO lists:

### List A: Changed ai-docs files
Filter for files matching `ai-docs/*.md`. These docs changed directly and need validation.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Match nested ai-docs files in changed-doc detection

The literal ai-docs/*.md filter only matches markdown files directly under an ai-docs directory. In this repo the task widget docs are nested at packages/contact-center/task/ai-docs/widgets/*/*.md, so editing something like .../TaskList/AGENTS.md would not enter List A, and because List B only examines source files, /spec-drift-changed can incorrectly report “nothing to check” when the changed files are the docs themselves.

Useful? React with 👍 / 👎.

2. **Root AGENTS.md**: `AGENTS.md` (repo root)
3. **Framework docs**: `ai-docs/` (README, RULES, patterns/*, templates/*)
4. **Package-level ai-docs**: Glob for `packages/contact-center/**/ai-docs/` to find all ai-docs folders
5. **Samples ai-docs**: Check `widgets-samples/**/ai-docs/` as well

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include Playwright ai-docs in the full scan

The comprehensive scan only discovers package ai-docs plus widgets-samples/**/ai-docs/, but this repo also contains playwright/ai-docs/AGENTS.md and playwright/ai-docs/ARCHITECTURE.md. As written, /spec-drift never spawns a checker for the Playwright framework docs, so drift in the E2E testing guidance goes undetected even though the root AGENTS.md routes Playwright work to that scope.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants