Find concept divergence
Skill KhurrumMahmood/senior-vibe-engineer/.claude/skills/find-concept-divergence
Advisory SUSPECT scan that surfaces concept-name drift between the canonical glossary at `.claude/contracts/concepts.yaml` and the codebase / docs. Detects three drift bands: `avoid_term_hit` (code uses a phrasing the glossary explicitly says is wrong), `competing_term_coexistence` (a `flagged_ambiguities` entry has multiple `competing_terms` present in the same file — drift the glossary author has not yet resolved), and `superseded_co_occurrence` (both a deprecated name and its `superseded_by:` replacement appear in the same file — rename transition drift). Detection only — never edits code or docs.From its SKILL.md
npx -y skills add KhurrumMahmood/senior-vibe-engineer --skill find-concept-divergenceAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
17.5 KB, ~4.0k tokens by cl100k_base, as published. Nobody here has run it
/find-concept-divergence
Kotlin/JVM 2.4.10 branch
Trigger this branch only for a manifest-selected .kt target and an explicit
host glossary. Keep sibling _kotlin, read
../_kotlin/GUIDE.md, and enter through
scripts/scan_kotlin.py. Exact authored identifier-token hits are lexical
review evidence only; aliases, extensions, operators, delegated members,
strings/comments, symbol identity, generated sources, Java, Gradle variants,
reflection, runtime behavior, and rename completeness remain unresolved.
C# 14 / .NET 10 branch
Use scripts/scan_csharp.py with the sibling _csharp provider and an
explicit glossary; run it with --help for the exact CLI. It matches strict
glossary terms against authored Roslyn identifier tokens from the current
compiled manifest closure. A hit is spelling evidence, not resolved symbol
identity or rename completeness.
C++20 branch
Use scripts/scan_cpp.py with the sibling _cpp provider; run the script with
--help for the exact CLI. It reports strict glossary spelling only across a
current complete C++20 compiler-owned source/header snapshot while retaining
namespace, signature, and overload context. Text hits do not prove symbol
identity, ODR/ABI, specializations, dispatch, or external variants.
C17 branch
Use scripts/scan_c.py with the sibling _c/c_lexical_facts.py provider; run
python3 scripts/scan_c.py --help for the exact CLI. This external-library
branch emits strict glossary-backed authored-text evidence only. Comments and
strings may be review noise; macro expansion, symbol identity, rename
completeness, C++, and Objective-C remain unresolved.
Dart v1
Dart v1 is strict glossary-backed text evidence over authored library source.
Copy the sibling _dart/dart_project_snapshot.py; the scan excludes generated,
test, example, vendor, build, report, part, barrel, and symlink roles and makes
no symbol-identity or rename-completeness claim.
SKILL_ROOT=".agents/skills/on-demand/find-concept-divergence"
python3 "${SKILL_ROOT}/scripts/scan_dart.py" \
--project-root "$PWD" --dart-root lib \
--glossary "$PWD/.claude/contracts/concepts.yaml" \
--output "$PWD/reports/find-concept-divergence/dart/findings.jsonl" \
--report "$PWD/reports/find-concept-divergence/dart/report.md" \
--direct-test "${DART_DIRECT_TEST:?Set a dependency-free direct test path}" \
--smoke-entrypoint "${DART_SMOKE:?Set a direct smoke entrypoint}" \
--expected-smoke "${DART_EXPECTED_SMOKE:?Set its exact stdout without the newline}"
You are running an advisory concept-glossary divergence audit. The goal is to surface places in the codebase or docs where:
- avoid-term hits — code uses a phrasing that the glossary
explicitly lists under a concept's
avoid:block (the glossary author has said "do not use this phrasing for this concept"). - competing-term coexistence — a file contains multiple
competing_termsfrom aflagged_ambiguitiesentry (an open ambiguity has bled into runtime — two competing names for the same concept are co-occurring in the same module). - superseded co-occurrence — a file mentions both a deprecated
concept name and its
superseded_by:replacement (rename transition drift; redundant withcoverage_lint:-declared lints).
This skill is detection-only. It never edits code, docs, or the
glossary; it writes a report under
reports/find-concept-divergence/scan-<UTC>/ so a follow-up audit can
act on the findings.
Host-language boundary
The host code language is not a routing constraint: this is a strict textual
glossary scan. It reads .py, .js, .jsx, .mjs, .cjs, .ts, .tsx,
template/HTML, and prose files. It does not parse JavaScript or TypeScript,
resolve imports, infer types, or judge JSX runtime behavior. Generated,
vendor, test, minified, and symlinked sources remain excluded by strict
path-level rules; identifier-like terms use lexical word boundaries, not fuzzy
or substring matching.
Go support is an explicit --language go strict-text mode, not an inference
from the generic suffix walk. It inventories every selected .go and
_test.go file before excluding test, generated, vendor, and symlink surfaces.
The python-strict-text analyzer applies the same exact term boundaries to
eligible UTF-8 source; it does not parse Go syntax, packages, build tags,
identifiers, comments, or string literals.
The automatic non-Go surface remains
scans: [python, javascript, typescript, markdown, templates]; only explicit
Go mode promotes Go files into the supported inventory/status contract.
Java support is likewise explicit rather than part of the automatic suffix
walk. Read references/java.md only for a Java run; it
defines the source-role inventory, native fixture check, and strict-text
non-claims.
This extends the preserved
scans: [python, javascript, typescript, go, markdown, templates] contract
with one separately selected Java band.
Rust is a separate strict-text mode over authored .rs source. It uses the
copied _rust/rust_lexical_facts.py inventory/native gate and records exact
glossary-term spans and hashes; generated, vendor, target/build, test,
auxiliary, configuration, and symlink roles remain visible but cannot fire.
This is textual evidence, not symbol identity or semantic equivalence.
SKILL_ROOT=".agents/skills/on-demand/find-concept-divergence"
REPORT_DIR="$PWD/reports/find-concept-divergence/rust"
python3 "${SKILL_ROOT}/scripts/scan_rust.py" \
--project-root "$PWD" \
--glossary "$PWD/.claude/contracts/concepts.yaml" \
--output "${REPORT_DIR}/findings.jsonl" \
--report "${REPORT_DIR}/report.md" .
The copied closure must include sibling _rust/rust_lexical_facts.py.
External strict-text variants
For PHP, Ruby, or Swift, load the selected skill with its sibling language provider and read that provider's on-demand guide before execution:
../_php-project-lexical/GUIDE.md../_ruby-project-lexical/GUIDE.md../_swift-project-lexical/GUIDE.md
These are glossary-backed authored-text scans, not symbol or conceptual identity. The guides own the exact commands, native gates, and non-claims.
How success is judged
- The run is graded only by artifacts: pasted command output plus
findings.jsonlandreport.md. Do not claim concept drift was checked without those files. - The scan verdict is one of
clean,real-drift,glossary-gap,noise-only, orscan-blocked. A mixed report may name multiple row-level triage labels, but the run-level verdict should state the most important next action. - Every row-level triage decision cites the report row and the relevant glossary entry. Code claims without the glossary evidence are not enough for a concept-divergence verdict.
- The skill remains read-only. It can recommend a rename, glossary update, ADR, exclusion, or lint handoff; it never edits the glossary, code, docs, or lint rules in this run.
- Go
scan.jsonstatus is exactly one ofcomplete,partial,unsupported, orfailed. The Markdown report repeats it. Missing/old Go, unreadable eligible source, and Go tool failures must never becomeclean. - Java uses the same status vocabulary in
scan.json, but strict-text scanning does not depend on a JDK. Unreadable eligible source cannot becomeclean.
Glossary source
The canonical glossary is .claude/contracts/concepts.yaml. The
detector reads:
concepts[].name,concepts[].aliases,concepts[].avoid,concepts[].superseded_by,concepts[].source,concepts[].coverage_lintflagged_ambiguities[].competing_terms
If the glossary is missing or unparseable the scan exits with a clear error rather than degrading silently — concept-divergence detection has no meaningful default behavior without it.
Copied installs use a schema-specific stdlib profile, not a general YAML engine: normal block lists and scalar flow lists are supported; quoted flow values preserve commas (single quotes or JSON-style double quotes). Nested flow collections and other unsupported shapes stop the scan with a parse error rather than becoming different search terms. Normalize those entries to the documented block-list shape before running the scan.
Schema convention: competing_terms: vs overload ambiguities
competing_terms: is for terms that genuinely compete for the same
concept slot — rename transitions, parallel implementations of the
same thing, or two names that mean the same thing fighting for
canonicality. Band 2 (competing_term_coexistence) treats a file
containing 2+ of these as drift, which is the right semantics for
true competition.
Do not use competing_terms: for overload cases where the
named terms are typed names for distinct things that share a noun
(e.g. distinct stores with overlapping prose, or distinct entities
that each carry the same generic English word in their slug). For
those, document the typed senses under where: and let the
canonical concept's avoid: block catch the actual drift signal
(bare generic-noun usage in prose / log strings) via band 1.
Default target
If the caller does not provide paths, the scanner walks a portable set of common project roots — language- and framework-agnostic — and auto-skips any that don't exist in the host repo:
app/, src/, lib/, scripts/, tests/, docs/,
.claude/skills/, .claude/docs/,
CONTEXT.md, README.md, ONBOARDING.md,
CLAUDE.md, .claude/CLAUDE.md
Out-of-scope paths (always excluded): .venv/, node_modules/,
.git/, dist/, build/, __pycache__/, .pytest_cache/,
.mypy_cache/, .ruff_cache/, */migrations/ (Django-shaped),
ai-docs/decisions/ (ADRs intentionally name both sides of a rename),
.claude/worktrees/ (agent worktrees), reports/ (runtime output),
*.worktree/. Host projects extend the prefix exclusion list via
.claude/skills/find-concept-divergence/host_excludes.txt (one
path-prefix per line; comments via #).
Exclusions are evaluated against the logical path relative to --project-root,
including a directly named file or directory. The walk never traverses a
directory symlink, even when that symlink or one of its descendants is the
direct target; an alias below an excluded logical directory cannot bypass that
exclusion. Files/symlinks resolving outside the project root are rejected.
Therefore a host may itself live below an ancestor named node_modules, while
its own dependency tree and escaped symlink targets remain out of scope.
Pipeline
SCAN_ID="scan-$(date -u +%Y%m%d-%H%M%S)"
REPORT_DIR="reports/find-concept-divergence/$SCAN_ID"
mkdir -p "$REPORT_DIR"
.venv/bin/python .claude/skills/find-concept-divergence/scripts/scan.py \
--output "$REPORT_DIR/findings.jsonl" \
--report "$REPORT_DIR/report.md"
Scan targets, finding labels, and the glossary default
(<project-root>/.claude/contracts/concepts.yaml) anchor on
--project-root, which defaults to the git toplevel of the cwd (else
the cwd).
For a bounded target or non-default glossary, pass the flags explicitly:
.venv/bin/python .claude/skills/find-concept-divergence/scripts/scan.py \
--output "$REPORT_DIR/findings.jsonl" \
--report "$REPORT_DIR/report.md" \
--glossary .claude/contracts/concepts.yaml \
.claude/skills/find-concept-divergence
The scan writes:
findings.jsonl— one record per hit; fieldsband,conceptorambiguity_id,file,line,match,term.report.md— grouped summary by band, sorted by concept then file.scan.json— Go tool/version evidence, full Go inventory, eligibility reasons, and analysis status when--language gois selected.
Go copied-closure pipeline
Use Go >= 1.22.0 discovered from PATH; the fixture/native oracle remains the
host's own go test ./...:
CONCEPT_SKILL="$PWD/.agents/skills/find-concept-divergence"
CONCEPT_REPORT="$PWD/reports/find-concept-divergence/scan-go"
mkdir -p "$CONCEPT_REPORT"
python3 "${CONCEPT_SKILL}/scripts/scan.py" \
--project-root "$PWD" --language go \
--output "$CONCEPT_REPORT/findings.jsonl" \
--report "$CONCEPT_REPORT/report.md" .
go test ./...
The strict-text outcome remains valid when eligible Go source is syntactically
malformed because the claim is textual co-occurrence, not parse validity.
Invalid UTF-8 or a read failure is different: it leaves a failed inventory row
and makes the result partial.
Output triage
Classify each finding into one of:
- real drift — code says one thing, glossary says another;
promote to a
quality/findings.jsonlentry (or the host project's equivalent) with band-appropriate recommendation (rename, glossary update, ADR). - glossary gap — the term in code is fine but the glossary lists
a stale
avoid:phrasing or hasn't registered a real synonym; updateconcepts.yamland re-run. - noise — false-positive (term appears inside a string literal or
comment that's intentionally quoting deprecated naming). Skip; if
the same noise recurs across multiple scans, narrow the
avoid:phrase, add the file's prefix tohost_excludes.txt, or move the case to a typed-namewhere:block instead ofcompeting_terms:.
If you dispatch an Agent to triage the report, give it this verdict
contract: each row must be labeled real drift, glossary gap, or
noise, and each label must cite both the report row and the glossary
entry. Agent output without those citations is not evidence.
Strict-first principle
v1 is strict only: it grep-matches the canonical names, aliases, avoid phrasings, and competing-terms slugs verbatim (word-boundary, case-insensitive). It does not do similarity matching, stem analysis, or fuzzy identifier comparison. Add similarity flagging only if the strict pass produces nothing real over multiple cycles.
If the strict pass produces noise, narrow the avoid: phrases in
concepts.yaml (more specific, more identifier-like) or add a
host_excludes.txt entry — don't add heuristics to the body.
Replay check
After editing this skill or its detector contract, run:
.venv/bin/python .claude/skills/find-concept-divergence/scripts/scan.py --help
SCAN_ID="scan-replay"
REPORT_DIR="/tmp/find-concept-divergence-${SCAN_ID}"
mkdir -p "$REPORT_DIR"
.venv/bin/python .claude/skills/find-concept-divergence/scripts/scan.py \
--output "$REPORT_DIR/findings.jsonl" \
--report "$REPORT_DIR/report.md" \
.claude/skills/find-concept-divergence
This bounded self-scan is the replay case for text/contract repairs. It proves the documented argparse surface and output contract execute against the current glossary; it does not prove the full repo is clean.
When things go sideways
| Symptom | Action |
|---|---|
.claude/contracts/concepts.yaml is missing or unparseable | Mark scan-blocked, paste the error, and stop; concept-divergence has no useful fallback without the glossary. |
| The report has hits but the glossary entry is ambiguous | Use row label glossary gap, cite the entry, and recommend updating concepts.yaml before renaming code. |
| A hit appears only in a deliberate quote of deprecated terminology | Label it noise, cite the surrounding line, and only add an exclusion if the same noise recurs. |
A superseded concept declares coverage_lint | Treat that lint as owning the rename guard; do not double-count skipped co-occurrence noise as scanner drift. |
| Agent triage omits glossary citations | Reject the dispatch output and read the report/glossary directly. |
| Go is missing or older than 1.22.0 | Preserve the unsupported report/scan.json, restore Go >= 1.22.0 on PATH, and re-run. |
| An eligible Go file cannot be decoded | Keep the report partial and cite the failed inventory row; never treat the remaining scan as complete. |
| An eligible Java file cannot be decoded | Keep the report partial and cite the failed inventory row; malformed Java syntax remains valid strict-text input. |
What ships with it: 11 files
100.4 KB alongside SKILL.md, 10 of them executable
references/
- java.md2.2 KB
scripts/
- scan_cpp.pyruns341 B
- scan_c.pyruns8.2 KB
- scan_csharp.pyruns354 B
- scan_dart.pyruns11.1 KB
- scan_kotlin.pyruns358 B
- scan_php.phpruns11.5 KB
- scan.pyruns37.3 KB
- scan_ruby.pyruns9.9 KB
- scan_rust.pyruns10.0 KB
- scan_swift.pyruns9.2 KB