agentsclimarketplace

Test impact

Skill Borda/AI-Rig/plugins/codemap-py/claude-skills/test-impact

A collection of personal AI coding assistant configurations, specialist agents, and automated workflows optimized for Python and ML open-source development.

Install
npx -y skills add Borda/AI-Rig --skill test-impact

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 23 stars23 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.

What its author says it does

Copied from the file, not written here

Identify which tests need rerunning after a code change — traces static call graph (function-level) or import graph (module-level) to find affected test files, then emits a ready-to-run pytest command. TRIGGER when: user asks which tests are affected by a change; phrases: "which tests are affected", "what tests cover this", "test impact of", "what tests to rerun".

SKILL.md

8.3 KB, ~2.3k tokens by cl100k_base, as published. Nobody here has run it

<objective>

Identifies minimal test set affected by changing a function or module. Uses codemap static analysis — no test execution needed. Result: ready-to-run pytest command covering only impacted tests.

Two input modes:

  • Function-level (module::symbol) — BFS over reverse call graph; finds every test calling changed function, directly or transitively. Includes tests mocking the symbol (mock_patches).
  • Module-level (bare module) — BFS over reverse import graph; finds every test importing module through any chain. Includes tests mocking any symbol in module.

not_covered: dynamic dispatch, hook callbacks, string-dispatch callers — same blind spot as fn-blast. Surface caveat, log gap.

NOT for: finding all callers of a function (use /codemap-py:query-code fn-blast <module::symbol>); querying module deps or blast radius (use /codemap-py:query-code); running/executing tests (identified here, not executed).

</objective> <inputs>
  • $ARGUMENTS: <qname> [--no-mocks]
    • qnamemodule::symbol (function-level) or bare dotted module (module-level)
    • --no-mocks — exclude mock-only test files (no call/import path)
    • Omitted → AskUserQuestion in Step 1
</inputs> <workflow>

Step 0 — Ensure index

# timeout: 10000
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
_CM_PROJ=$(git rev-parse --show-toplevel 2>/dev/null | xargs basename 2>/dev/null || basename "$PWD")
_IDX="${CODEMAP_INDEX_DIR:-.cache/codemap}"
INDEX="${_IDX}/${_CM_PROJ}.json"

SQ=$(python3 "${CLAUDE_PLUGIN_ROOT:-plugins/codemap-py}/bin/locate_scan_query.py" 2>/dev/null)
[ -z "$SQ" ] && { echo "scan-query not found — install codemap-py plugin first"; exit 1; }
echo "$SQ" > "${TMPDIR:-/tmp}/codemap-${_CM_PROJ}-ti-sq-${CSID}"

[ ! -f "$INDEX" ] && echo "No index found — will build via codemap-py:scan-codebase"

Auto-build opt-out via SCAN_NO_AUTOBUILD=1 (index used exactly as-is — no refresh, no full build); build wall-time echoed when it runs, keeps build cost separable from query cost.

If $INDEX not found:

  • SCAN_NO_AUTOBUILD=1 set → print ! codemap index missing and SCAN_NO_AUTOBUILD=1 — refusing to auto-build. Build it manually first: /codemap-py:scan-codebase and exit 1.
  • otherwise → run scan-index in the foreground (wait until it finishes) then continue. (Not the codemap-py:scan-codebase skill — it is disable-model-invocation:true, user-slash-only; build via the scan-index binary.)

If index already exists:

# timeout: 30000
_CM_PROJ=$(git rev-parse --show-toplevel 2>/dev/null | xargs basename 2>/dev/null || basename "$PWD")
_IDX="${CODEMAP_INDEX_DIR:-.cache/codemap}"
if [ "${SCAN_NO_AUTOBUILD:-0}" = "1" ]; then
    echo "[codemap] SCAN_NO_AUTOBUILD=1 — using existing index as-is (no refresh)"
else
    _CM_BUILD_T0=$(date +%s)
    # forward CODEMAP_INDEX_DIR; ensures scan-index writes to same path as INDEX
    CODEMAP_INDEX_DIR="${_IDX}" "${CLAUDE_PLUGIN_ROOT:-plugins/codemap-py}/bin/scan-index" --incremental \
        && echo "[codemap] index built in $(( $(date +%s) - _CM_BUILD_T0 ))s" \
        || printf "⚠ scan-index --incremental failed — index may be stale; continuing\n"
fi

After Skill() or incremental refresh, re-verify index still present:

# timeout: 5000
_CM_PROJ=$(git rev-parse --show-toplevel 2>/dev/null | xargs basename 2>/dev/null || basename "$PWD")
_IDX="${CODEMAP_INDEX_DIR:-.cache/codemap}"
INDEX="${_IDX}/${_CM_PROJ}.json"
[ -f "$INDEX" ] || { printf "! Index not found after refresh at %s — check CODEMAP_INDEX_DIR or re-run /codemap-py:scan-codebase\n" "$INDEX"; exit 1; }

Step 1 — Parse arguments

Extract QNAME and NO_MOCKS flag from $ARGUMENTS.

# timeout: 5000
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
_CM_PROJ=$(git rev-parse --show-toplevel 2>/dev/null | xargs basename 2>/dev/null || basename "$PWD")
ARGS="${ARGUMENTS:-}"
QNAME=$(echo "$ARGS" | awk '{print $1}')
MOCKS_FLAG=$(echo "$ARGS" | grep -q -- "--no-mocks" && echo "--no-mocks" || echo "")
echo "$QNAME" > "${TMPDIR:-/tmp}/codemap-${_CM_PROJ}-ti-qname-${CSID}"
echo "$MOCKS_FLAG" > "${TMPDIR:-/tmp}/codemap-${_CM_PROJ}-ti-mocks-${CSID}"

If $ARGUMENTS empty → AskUserQuestion: "Which function or module changed?" Options: (a) Enter module::symbol for function-level · (b) Enter bare module name for module-level · (c) Cancel — exit without running test-impact analysis. After the user answers, set QNAME from the answer and write it to the tmpfile before proceeding to Step 2:

# timeout: 5000
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
_CM_PROJ=$(git rev-parse --show-toplevel 2>/dev/null | xargs basename 2>/dev/null || basename "$PWD")
QNAME="<answer from AskUserQuestion>"
echo "$QNAME" > "${TMPDIR:-/tmp}/codemap-${_CM_PROJ}-ti-qname-${CSID}"

Multi-symbol guard: $ARGUMENTS may contain multiple space-separated tokens (e.g. mypackage.auth::validate mypackage.auth::parse). awk '{print $1}' silently truncates to first. If $ARGUMENTS has more than one token after stripping --no-mocks, print ⚠ test-impact accepts one symbol at a time — using first token only: $QNAME. Run separately for each remaining symbol.

Step 2 — Run test-impact query

# timeout: 10000
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
_CM_PROJ=$(git rev-parse --show-toplevel 2>/dev/null | xargs basename 2>/dev/null || basename "$PWD")
IFS= read -r QNAME < "${TMPDIR:-/tmp}/codemap-${_CM_PROJ}-ti-qname-${CSID}" 2>/dev/null || QNAME=""
IFS= read -r MOCKS_FLAG < "${TMPDIR:-/tmp}/codemap-${_CM_PROJ}-ti-mocks-${CSID}" 2>/dev/null || MOCKS_FLAG=""
IFS= read -r SQ < "${TMPDIR:-/tmp}/codemap-${_CM_PROJ}-ti-sq-${CSID}" 2>/dev/null || SQ=""
RESULT=$("$SQ" test-impact "$QNAME" $MOCKS_FLAG 2>/dev/null)
NOT_COVERED=$(echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(json.dumps(d.get('index',{}).get('not_covered',[])))" 2>/dev/null || echo "[]")
HINT=$(echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('index',{}).get('hint',''))" 2>/dev/null || echo "")
TOTAL=$(echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(len(d.get('test_files',[])))" 2>/dev/null || echo "0")
PYTEST_CMD=$(echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('pytest_cmd',''))" 2>/dev/null || echo "")
echo "$NOT_COVERED" > "${TMPDIR:-/tmp}/codemap-${_CM_PROJ}-ti-not-covered-${CSID}"
echo "$HINT"        > "${TMPDIR:-/tmp}/codemap-${_CM_PROJ}-ti-hint-${CSID}"
echo "$TOTAL"       > "${TMPDIR:-/tmp}/codemap-${_CM_PROJ}-ti-total-${CSID}"
echo "$PYTEST_CMD"  > "${TMPDIR:-/tmp}/codemap-${_CM_PROJ}-ti-pytest-cmd-${CSID}"

Parse JSON output from $RESULT:

  • test_files — list of test file paths
  • pytest_cmd — ready-to-run command
  • via_call / via_mock — breakdown of how tests were found
  • index.not_covered — surface as caveat if non-empty
  • index.hint — include as suggestion

haiku JSON parse guard: scan-query JSON output may be prefixed/suffixed with log/warning lines under haiku model. Always extract JSON via python3 -c "import sys,json; ..." piping stdin — never assume raw output valid JSON. Parsing fails (ValueError/JSONDecodeError) → print ! scan-query returned non-JSON output — try /codemap-py:scan-codebase to rebuild index and exit 1.

Step 3 — Output

When total == 0: report "No tests found via static analysis. Try full suite or check with grep -rn <symbol_name> tests/."

When total > 0:

## Test impact: <qname>

**Affected tests** (<total> files, <via_call> via call/import graph, <via_mock> via mocks):
<test_files as bullet list>

**Run:**

<pytest_cmd>


<if not_covered non-empty>
**Caveat:** dynamic-dispatch / hook-callback callers are not in the static graph — <hint>.
</if>

Output routing: if total >= 5 write to .temp/output-test-impact-<branch>-<YYYY-MM-DD>.md.

</workflow>

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 327,069. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.