name: test-scaffold description: Analyze existing test coverage, identify critical untested paths, and generate characterization tests as a safety net before refactoring. Use before any modernization to pin current behavior. license: Apache-2.0 metadata: author: nightgauge version: "1.3.0" source: https://github.com/nightgauge/nightgauge allowed-tools: Read Write Edit Glob Grep Bash Task context: fork agent: test-runner model: sonnet disable-model-invocation: true
Nightgauge Test Scaffold
Description
Characterization test generator that creates a safety net of tests capturing current behavior before refactoring or modernization begins. Uses deterministic tool commands for coverage analysis and risk scoring; AI generates test files that pin existing behavior rather than asserting ideal behavior.
Use Cases:
- Creating a safety net before refactoring legacy code
- Pinning current behavior in brownfield codebases before modernization
- Identifying critical untested paths that pose refactoring risk
- Generating baseline tests for inherited or unfamiliar codebases
- Pre-modernization coverage gap analysis
When to Use:
- Before starting any refactoring or modernization effort
- When inheriting a codebase with insufficient test coverage
- Before running
/nightgauge:health-checkto establish a safety net - When you need confidence that refactoring won't break existing behavior
- After
/nightgauge:health-checkidentifies low test coverage
Relationship to Other Skills:
| Skill | Purpose | Test Type | When | | -------------- | ------------------------------------ | -------------------- | ------------------ | | Test Scaffold | Pin current behavior before refactor | Characterization | Before refactoring | | Test Gen | Generate comprehensive test suites | Unit/Integration/E2E | After feature-dev | | Health Check | Assess codebase health (6 dims) | N/A (read-only) | Any time | | Security Audit | Assess security posture (7 dims) | N/A (read-only) | Any time |
Key Differentiation from nightgauge-test-gen:
| Aspect | test-gen | test-scaffold |
| ------------- | ------------------------------------- | ------------------------------------------------- |
| Purpose | Generate comprehensive test suites | Create safety net before refactoring |
| Test Type | Unit, integration, E2E (new behavior) | Characterization tests (capture current behavior) |
| When | After feature-dev, greenfield focus | Before refactoring, brownfield focus |
| Pipeline | Between feature-dev and pr-create | Standalone utility (like health-check) |
| Approach | Coverage-driven, ideal behavior | Risk-driven, pin existing behavior |
| Modifies | May modify existing tests | NEVER modifies existing tests |
| Naming | *.test.* / *.spec.* | *.scaffold.test.* |
| Location | tests/ (alongside existing) | tests/scaffold/ (isolated) |
Invocation
| Tool | Command |
| ----------- | --------------------------------------------------------- |
| Claude Code | /nightgauge:test-scaffold [options] |
| Copilot | Invoke via Agent Skills extension |
| Cursor | Run via Agent Skills or direct SKILL.md |
| Standalone | claude --skill skills/nightgauge-test-scaffold/SKILL.md |
Arguments
| Argument | Description | Default |
| ------------------- | -------------------------------------------- | ------- |
| --path DIR | Root directory to assess | . |
| --package PKG | Assess specific monorepo package only | - |
| --format FORMAT | Output format: summary, json, both | both |
| --skip-generation | Analyze only, don't generate tests | false |
| --max-tests N | Maximum number of test files to generate | 20 |
| --output FILE | Custom output path for JSON report | auto |
| --priority LEVEL | Minimum priority for generation: critical, | high |
| | high, medium, low | |
Examples
# Full analysis + test generation for current directory
/nightgauge:test-scaffold
# Assess specific directory
/nightgauge:test-scaffold --path /path/to/project
# Analysis only, no test generation
/nightgauge:test-scaffold --skip-generation
# Generate tests only for critical gaps
/nightgauge:test-scaffold --priority critical
# Limit number of generated test files
/nightgauge:test-scaffold --max-tests 10
# JSON output only
/nightgauge:test-scaffold --format json
# Assess specific monorepo package
/nightgauge:test-scaffold --package packages/api-server
Prerequisites
- Bash shell
jqinstalled (for JSON processing)- Coverage tools are optional — the skill gracefully degrades when coverage
tools (
c8,istanbul,coverage.py,go test -cover,cargo tarpaulin,jacoco) are not installed - Test framework must be detectable (Vitest, Jest, Mocha, pytest, unittest, go test, cargo test, JUnit, TestNG)
CRITICAL: This skill runs headless. Do NOT use AskUserQuestion. Make autonomous decisions or fail with clear error.
Workflow
Phase 0: Setup & Detection
<!-- include: ../_shared/PREFLIGHT.md -->Step 0.1: Parse Arguments
Extract options from invocation:
ASSESS_PATH="."
PACKAGE_FILTER=""
OUTPUT_FORMAT="both"
SKIP_GENERATION=false
MAX_TESTS=20
OUTPUT_FILE=""
PRIORITY_FILTER="high"
# Parse arguments from invocation
# --path DIR: set ASSESS_PATH
# --package PKG: set PACKAGE_FILTER
# --format FORMAT: set OUTPUT_FORMAT
# --skip-generation: set SKIP_GENERATION=true
# --max-tests N: set MAX_TESTS
# --output FILE: set OUTPUT_FILE
# --priority LEVEL: set PRIORITY_FILTER
Step 0.2: Detect Ecosystems
Scan for ecosystem indicators in the assessment path:
cd "$ASSESS_PATH"
ECOSYSTEMS=()
# Node.js
[ -f package.json ] && ECOSYSTEMS+=("nodejs")
# Python
([ -f pyproject.toml ] || [ -f setup.py ] || \
[ -f requirements.txt ]) && ECOSYSTEMS+=("python")
# Go
[ -f go.mod ] && ECOSYSTEMS+=("go")
# Rust
[ -f Cargo.toml ] && ECOSYSTEMS+=("rust")
# Java/JVM
([ -f pom.xml ] || [ -f build.gradle ] || \
[ -f build.gradle.kts ]) && ECOSYSTEMS+=("java")
if [ ${#ECOSYSTEMS[@]} -eq 0 ]; then
echo "WARNING: No recognized ecosystem detected."
echo " Proceeding with generic file analysis only."
fi
echo "Ecosystems detected: ${ECOSYSTEMS[*]}"
Step 0.3: Detect Test Frameworks
Identify the test framework(s) in use:
TEST_FRAMEWORKS=()
# Node.js
if [ -f package.json ]; then
jq -r '.devDependencies // {} | keys[]' package.json 2>/dev/null | \
grep -qE '^vitest$' && TEST_FRAMEWORKS+=("vitest")
jq -r '.devDependencies // {} | keys[]' package.json 2>/dev/null | \
grep -qE '^jest$' && TEST_FRAMEWORKS+=("jest")
jq -r '.devDependencies // {} | keys[]' package.json 2>/dev/null | \
grep -qE '^mocha$' && TEST_FRAMEWORKS+=("mocha")
fi
# Python
([ -f pytest.ini ] || [ -f setup.cfg ] || [ -f pyproject.toml ]) && \
grep -ql 'pytest' pytest.ini setup.cfg pyproject.toml 2>/dev/null && \
TEST_FRAMEWORKS+=("pytest")
# Go (built-in)
[[ " ${ECOSYSTEMS[*]} " =~ " go " ]] && TEST_FRAMEWORKS+=("go-test")
# Rust (built-in)
[[ " ${ECOSYSTEMS[*]} " =~ " rust " ]] && TEST_FRAMEWORKS+=("cargo-test")
# Java
[ -f pom.xml ] && grep -q 'junit' pom.xml 2>/dev/null && \
TEST_FRAMEWORKS+=("junit")
[ -f build.gradle ] && grep -q 'testImplementation' build.gradle \
2>/dev/null && TEST_FRAMEWORKS+=("junit")
echo "Test frameworks: ${TEST_FRAMEWORKS[*]}"
Step 0.4: Detect Monorepo Structure
IS_MONOREPO=false
PACKAGES=()
# Node.js workspaces
if [ -f package.json ]; then
WORKSPACES=$(jq -r '.workspaces // empty' package.json 2>/dev/null)
if [ -n "$WORKSPACES" ]; then
IS_MONOREPO=true
for ws in $(jq -r '.workspaces[]? // empty' package.json 2>/dev/null); do
for dir in $ws; do
[ -d "$dir" ] && PACKAGES+=("$dir")
done
done
fi
fi
# Cargo workspace
if [ -f Cargo.toml ]; then
grep -q '\[workspace\]' Cargo.toml 2>/dev/null && IS_MONOREPO=true
fi
# Go workspace
[ -f go.work ] && IS_MONOREPO=true
echo "Monorepo: $IS_MONOREPO"
if [ "$IS_MONOREPO" = true ]; then
echo "Packages: ${PACKAGES[*]}"
fi
Step 0.5: Check for Health Check Integration
Detect existing health-check output for coverage data reuse:
HEALTH_REPORT_PATH=".nightgauge/health-report.json"
HEALTH_CHECK_AVAILABLE=false
if [ -f "$HEALTH_REPORT_PATH" ]; then
HEALTH_CHECK_AVAILABLE=true
echo "Health check report found: $HEALTH_REPORT_PATH"
HC_TEST_SCORE=$(jq -r \
'.dimensions.test_coverage.score // "N/A"' \
"$HEALTH_REPORT_PATH" 2>/dev/null || echo "N/A")
HC_TEST_RATIO=$(jq -r \
'.dimensions.test_coverage.metrics.test_to_source_ratio // "N/A"' \
"$HEALTH_REPORT_PATH" 2>/dev/null || echo "N/A")
echo " Health check test coverage score: $HC_TEST_SCORE"
echo " Health check test-to-source ratio: $HC_TEST_RATIO"
else
echo "No health check report found."
echo " Run /nightgauge:health-check for pre-computed coverage data."
fi
Phase 1: Existing Test Inventory
Step 1.1: Run Inventory via Go Binary
Steps 1.1–1.4 collapse into a single nightgauge test inventory --json
call. The verb walks --workdir, classifies each file as source or test
(basename patterns *.test.*, *.spec.*, *_test.*, test_*), derives
the test→source mapping by stripping the test suffix per the table below,
and lists source files with no matching test. Excluded directories
(.git, node_modules, vendor, dist, build, coverage) are pruned
at walk time. Audit row B39.
Test → source basename mapping:
| Test pattern | Example | Source basename |
| ------------ | ------------- | --------------- |
| *.test.* | foo.test.ts | foo.ts |
| *.spec.* | bar.spec.js | bar.js |
| *_test.* | baz_test.go | baz.go |
| test_* | test_qux.py | qux.py |
INV_JSON=$(nightgauge test inventory --workdir "$ASSESS_PATH" --json)
TEST_FILE_COUNT=$(printf '%s\n' "$INV_JSON" | jq -r '.counts.test_files')
SOURCE_FILE_COUNT=$(printf '%s\n' "$INV_JSON" | jq -r '.counts.source_files')
UNTESTED_COUNT=$(printf '%s\n' "$INV_JSON" | jq -r '.counts.untested_files')
# UNTESTED_FILES is the list piped into Phase 3 risk scoring.
printf '%s\n' "$INV_JSON" | jq -r '.untested_files[]' > /tmp/ts_untested.txt
echo "Tests: $TEST_FILE_COUNT Sources: $SOURCE_FILE_COUNT Untested: $UNTESTED_COUNT"
The full JSON contract (v, workdir, counts, source_files,
test_files, test_to_source_mapping, untested_files, warnings) is
schema version 1 — see docs/GO_BINARY.md (### Test — Inventory and Risk Scoring).
Step 1.5: Collect Framework Configurations
# Collect test configuration details
if [ -f vitest.config.ts ]; then
echo "Vitest config found: vitest.config.ts"
fi
if [ -f jest.config.js ] || [ -f jest.config.ts ]; then
echo "Jest config found"
fi
if [ -f pytest.ini ] || [ -f pyproject.toml ]; then
echo "Pytest config found"
fi
Phase 2: Coverage Analysis
Step 2.1: Run Coverage Tools
Run ecosystem-specific coverage tools. Gracefully skip if the tool is not installed.
Node.js (c8 / istanbul):
if [[ " ${ECOSYSTEMS[*]} " =~ " nodejs " ]]; then
if [ -f package.json ]; then
# Check for existing coverage report
if [ -d coverage ]; then
echo "Existing coverage directory found"
if [ -f coverage/coverage-summary.json ]; then
echo "Coverage summary available"
TOTAL_COVERAGE=$(jq -r '.total.lines.pct // 0' \
coverage/coverage-summary.json 2>/dev/null || echo 0)
echo "Line coverage: ${TOTAL_COVERAGE}%"
fi
fi
# Attempt to run coverage if no recent report
if [ "$TOTAL_COVERAGE" = "" ] || [ "$TOTAL_COVERAGE" = "0" ]; then
if command -v npx &>/dev/null; then
echo "Running coverage analysis..."
# Build the workspace flag for monorepo packages
WS_FLAG=""
if [ "$IS_MONOREPO" = true ] && [ -n "$PACKAGE_FILTER" ]; then
WS_FLAG="-w $PACKAGE_FILTER"
fi
# NOTE: When IS_MONOREPO=true but PACKAGE_FILTER is not set,
# iterate over the PACKAGES array and run coverage per-package
# (handled in Phase 7: Monorepo Aggregation).
npx $WS_FLAG vitest run --coverage --reporter=json \
2>/dev/null > /tmp/ts_coverage.json || \
npx $WS_FLAG jest --coverage --coverageReporters=json-summary \
2>/dev/null || true
fi
fi
fi
fi
Python (coverage.py):
if [[ " ${ECOSYSTEMS[*]} " =~ " python " ]]; then
if command -v coverage &>/dev/null; then
echo "Running Python coverage..."
coverage run -m pytest 2>/dev/null || true
coverage json -o /tmp/ts_pycoverage.json 2>/dev/null || true
TOTAL_COVERAGE=$(jq -r '.totals.percent_covered // 0' \
/tmp/ts_pycoverage.json 2>/dev/null || echo 0)
echo "Python coverage: ${TOTAL_COVERAGE}%"
fi
fi
Go:
if [[ " ${ECOSYSTEMS[*]} " =~ " go " ]]; then
echo "Running Go coverage..."
go test -cover ./... 2>/dev/null > /tmp/ts_gocover.txt || true
fi
Rust:
if [[ " ${ECOSYSTEMS[*]} " =~ " rust " ]]; then
if command -v cargo-tarpaulin &>/dev/null; then
echo "Running Rust coverage..."
cargo tarpaulin --out Json 2>/dev/null \
> /tmp/ts_rustcover.json || true
fi
fi
Step 2.2: Parse Coverage Reports
Extract per-file coverage data. Identify files with 0% coverage (completely untested), files with partial coverage (some functions uncovered), and fully covered files.
Step 2.3: Identify Uncovered Functions
For files with partial or zero coverage, identify specific exported functions and classes that lack test coverage. Use Grep to find exports:
# Node.js/TypeScript exports
grep -rn --include="*.ts" --include="*.tsx" --include="*.js" \
-E '^\s*(export\s+(function|class|const|async\s+function|default))\b' \
"$ASSESS_PATH" \
--exclude-dir=node_modules --exclude-dir=dist \
--exclude-dir=build --exclude-dir=.git \
2>/dev/null | head -200
Phase 3: Risk-Based Prioritization
Score each untested file via a single nightgauge test risk-score
call. The verb computes the four sub-scores below from the file content
plus git log and a basename-substring importer scan, sums them (capped
at 100), and emits a sorted list with priority bucket. Audit row B39.
The scoring tables below are the v1 contract — the binary reproduces them verbatim so consumers can audit the verb without reading Go source.
Step 3.1: Run risk-score
RISK_JSON=$(nightgauge test risk-score \
--files /tmp/ts_untested.txt \
--workdir "$ASSESS_PATH" \
--json)
# Sorted entries, highest score first
printf '%s\n' "$RISK_JSON" | jq -c '.entries[]' > /tmp/ts_risk.ndjson
# Filter by --priority (when set by the caller — defaults to "all")
PRIORITY="${PRIORITY:-all}"
case "$PRIORITY" in
critical) jq -r '.file' < /tmp/ts_risk.ndjson | head -n 0 ;; # set below
*) ;;
esac
# Examples of consuming the output:
TOP_FIVE=$(printf '%s\n' "$RISK_JSON" | jq -r '.entries[0:5][].file')
CRITICAL_FILES=$(printf '%s\n' "$RISK_JSON" | jq -r '.entries[] | select(.priority=="critical") | .file')
The full JSON contract (v, workdir, entries[] with
file/business_criticality/complexity/change_frequency/dependency_depth/score/priority,
warnings) is schema version 1 — see docs/GO_BINARY.md (### Test — Inventory and Risk Scoring).
Step 3.2: Scoring tables (v1 contract — reproduced by the binary)
Business criticality — first matching pattern wins:
| Pattern | Criticality | Score Boost | | ------------------------------- | ----------- | ----------- | | Payment/billing/checkout | Critical | +40 | | Auth/authorization/session | Critical | +35 | | API routes/handlers/controllers | High | +25 | | Middleware/interceptors | High | +20 | | Services/repositories | Medium | +15 | | Utilities/helpers | Low | +5 | | Types/interfaces/constants | Minimal | +0 |
Code complexity — count of branching keywords (if, else, switch,
case, for, while, try, catch, &&, ||):
| Branches per File | Complexity | Score Boost | | ----------------- | ---------- | ----------- | | 0-5 | Low | +5 | | 6-15 | Medium | +15 | | 16-30 | High | +25 | | 31+ | Very High | +35 |
Change frequency — git log --since="6 months ago" line count
(non-git workdirs return 0 with a single warning):
| Commits (6 months) | Frequency | Score Boost | | ------------------ | --------- | ----------- | | 0-2 | Stable | +0 | | 3-5 | Moderate | +10 | | 6-15 | Active | +20 | | 16+ | Hot | +30 |
Dependency depth — count of files in the source allowlist whose contents contain the file's basename-stem (approximation, not an import-graph traversal):
| Importers | Depth | Score Boost | | --------- | -------- | ----------- | | 0-1 | Leaf | +0 | | 2-5 | Shared | +10 | | 6-10 | Core | +20 | | 11+ | Critical | +30 |
Composite + priority:
risk_score = min(100, business_criticality + complexity + change_frequency + dependency_depth)
| Risk Score | Priority | | ---------- | -------- | | 80-100 | Critical | | 60-79 | High | | 40-59 | Medium | | 0-39 | Low |
Sort by score descending (then file path ascending for stability) — the
verb returns the entries in this order. Apply --priority filter to
select which files receive generated tests.
Phase 4: Characterization Test Generation
Only runs if --skip-generation is NOT set.
Step 4.1: Select Files for Generation
From the prioritized list, select the top --max-tests files that meet the
--priority threshold.
Step 4.2: Prepare Test Generation Context
For each selected file:
- Read the source file content
- Identify all exported functions, classes, and their signatures
- Note dependencies and imports
- Determine the test framework to use (from Phase 0)
Step 4.3: Spawn Parallel Subagents
Use the Task tool with model: "sonnet" to spawn parallel subagents for
independent test file generation. Make all independent file creation calls in a
single message.
Each subagent receives:
- Source file path and content
- Exported functions/classes to test
- Test framework (vitest, jest, pytest, etc.)
- Output path:
tests/scaffold/{FileName}.scaffold.test.{ext} - Characterization test requirements (see below)
Characterization Test Requirements:
Every generated test MUST:
- Begin with a header comment:
// CHARACTERIZATION TEST: Captures current behavior before refactoring. // Generated by nightgauge-test-scaffold. Safe to delete after // refactoring is complete and proper tests are in place. - Import the source module under test
- Test the CURRENT behavior (not ideal behavior)
- Use descriptive test names:
"characterizes: [function] [behavior]" - Mock external dependencies (network, filesystem, database)
- NOT modify any existing test files
- Be placed in
tests/scaffold/directory
Example (TypeScript/Vitest):
// CHARACTERIZATION TEST: Captures current behavior before refactoring.
// Generated by nightgauge-test-scaffold. Safe to delete after
// refactoring is complete and proper tests are in place.
import { describe, it, expect } from "vitest";
import { calculateTotal } from "../../src/services/OrderService";
describe("OrderService characterization", () => {
it("characterizes: calculateTotal returns sum with tax", () => {
const result = calculateTotal([
{ price: 10, quantity: 2 },
{ price: 5, quantity: 1 },
]);
// Captures current behavior — tax rate may change during refactoring
expect(result).toBe(26.25);
});
it("characterizes: calculateTotal handles empty array", () => {
const result = calculateTotal([]);
expect(result).toBe(0);
});
});
Example (Python/pytest):
# CHARACTERIZATION TEST: Captures current behavior before refactoring.
# Generated by nightgauge-test-scaffold. Safe to delete after
# refactoring is complete and proper tests are in place.
from services.order_service import calculate_total
class TestOrderServiceCharacterization:
def test_characterizes_calculate_total_with_items(self):
result = calculate_total([
{"price": 10, "quantity": 2},
{"price": 5, "quantity": 1},
])
# Captures current behavior
assert result == 26.25
def test_characterizes_calculate_total_empty_list(self):
result = calculate_total([])
assert result == 0
Step 4.4: Create Scaffold Directory
mkdir -p tests/scaffold
Step 4.5: Collect and Validate Results
After parallel subagents complete:
- Collect generated test file contents
- Validate syntax (no obvious parse errors)
- Check that all test files use
*.scaffold.test.*naming - Verify tests are placed in
tests/scaffold/ - Check for naming conflicts
Phase 5: Test Execution & Validation
Only runs if tests were generated in Phase 4.
Step 5.1: Run Generated Tests
Execute the generated scaffold tests to verify they pass against the current codebase:
# Build workspace flag for monorepo packages
WS_FLAG=""
if [ "$IS_MONOREPO" = true ] && [ -n "$PACKAGE_FILTER" ]; then
WS_FLAG="-w $PACKAGE_FILTER"
fi
# NOTE: When IS_MONOREPO=true but PACKAGE_FILTER is not set, iterate
# over the PACKAGES array and run tests per-package (Phase 7 handles this).
# Node.js (Vitest)
npx $WS_FLAG vitest run tests/scaffold/ 2>&1
# Node.js (Jest)
npx $WS_FLAG jest tests/scaffold/ 2>&1
# Python (pytest)
pytest tests/scaffold/ 2>&1
# Go
go test ./tests/scaffold/... 2>&1
# Rust
cargo test --test scaffold 2>&1
Step 5.2: Fix Failing Tests
Characterization tests MUST pass against current code (they capture existing behavior). If a test fails:
- Read the error output
- Adjust the test expectation to match ACTUAL behavior
- Re-run the failing test
- Repeat until passing (max 3 attempts per test)
If a test cannot be fixed after 3 attempts, mark it as skipped with a comment explaining why.
Step 5.3: Report Pass Rate
echo "Scaffold test results:"
echo " Generated: $TESTS_GENERATED"
echo " Passing: $TESTS_PASSING"
echo " Skipped: $TESTS_SKIPPED"
echo " Pass rate: ${PASS_RATE}%"
Phase 6: Gap Report & JSON Output
Step 6.1: Write JSON Report
Ensure the .nightgauge/ directory exists, then write the structured
report to .nightgauge/test-scaffold-report.json (or the custom --output
path):
mkdir -p .nightgauge
{
"schema_version": "1.0",
"assessment_date": "2026-02-21T00:00:00Z",
"codebase": {
"name": "project-name",
"ecosystems": ["nodejs", "python"],
"is_monorepo": false,
"test_frameworks": ["vitest", "jest"]
},
"summary": {
"source_files": 120,
"test_files": 45,
"test_ratio": 0.375,
"coverage_before": 62.5,
"coverage_after": 78.3,
"tests_generated": 15,
"critical_gaps_found": 8,
"critical_gaps_covered": 6
},
"gaps": [
{
"file": "src/services/PaymentService.ts",
"functions": ["processPayment", "refund"],
"priority": "critical",
"risk_score": 95,
"risk_factors": {
"business_criticality": "high",
"complexity": 12,
"change_frequency": 8,
"dependency_depth": 3
},
"test_generated": true,
"test_file": "tests/scaffold/PaymentService.scaffold.test.ts"
}
],
"tests_generated": [
{
"source_file": "src/services/PaymentService.ts",
"test_file": "tests/scaffold/PaymentService.scaffold.test.ts",
"test_count": 8,
"type": "characterization",
"status": "passing"
}
],
"recommendations": [
{
"priority": 1,
"action": "Add integration tests for payment flow",
"effort": "M",
"risk_reduction": "high"
}
],
"health_check_integration": {
"health_report_available": false,
"reused_coverage_data": false
},
"created_at": "2026-02-21T00:00:00Z"
}
Step 6.2: Write Markdown Summary
Output a human-readable report:
TEST SCAFFOLD REPORT
================================================================
Project: project-name
Ecosystems: nodejs
Assessment Date: 2026-02-21
Monorepo: No
COVERAGE SUMMARY
================================================================
Source files: 120
Existing test files: 45
Test-to-source ratio: 0.375
Coverage before: 62.5%
Coverage after: 78.3% (+15.8%)
TESTS GENERATED: 15 files
================================================================
PRIORITIZED GAPS (sorted by risk score)
----------------------------------------------------------------
[CRITICAL] src/services/PaymentService.ts (risk: 95)
Functions: processPayment, refund
Risk factors: business=high, complexity=12, changes=8, deps=3
-> Test generated: tests/scaffold/PaymentService.scaffold.test.ts
-> Tests: 8 (all passing)
[HIGH] src/services/AuthService.ts (risk: 72)
Functions: login, validateToken, refreshToken
Risk factors: business=high, complexity=8, changes=5, deps=4
-> Test generated: tests/scaffold/AuthService.scaffold.test.ts
-> Tests: 6 (all passing)
[MEDIUM] src/utils/formatters.ts (risk: 45)
Functions: formatDate, formatCurrency
Risk factors: business=low, complexity=3, changes=2, deps=8
-> Skipped: below priority threshold
SCAFFOLD TEST RESULTS
----------------------------------------------------------------
Generated: 15 test files
Passing: 14 (93%)
Skipped: 1 (could not determine current behavior)
RECOMMENDATIONS (sorted by impact)
----------------------------------------------------------------
1. Add integration tests for payment flow (medium effort)
2. Add auth token refresh edge case tests (low effort)
3. Replace scaffold tests with proper unit tests after refactoring
IMPORTANT: Scaffold tests are temporary safety nets.
Replace with proper tests after refactoring is complete.
Delete tests/scaffold/ when no longer needed.
----------------------------------------------------------------
Report saved: .nightgauge/test-scaffold-report.json
If --format json, write only JSON. If --format summary, output only the
markdown summary. If --format both, write JSON and output the markdown
summary.
Step 6.3: Verify JSON Report
python3 -m json.tool .nightgauge/test-scaffold-report.json > /dev/null && \
echo "Report written: .nightgauge/test-scaffold-report.json"
Phase 7: Monorepo Aggregation (Conditional)
Only runs if IS_MONOREPO=true and --package was NOT specified.
Step 7.1: Per-Package Assessment
For each package in PACKAGES:
- Run Phases 1-6 scoped to the package directory
- Compute per-package gap analysis and risk scores
Use the Task tool with model: "haiku" to spawn parallel subagents for
independent per-package analysis. Each subagent receives: package path,
ecosystem detected, test frameworks, and generation parameters.
Step 7.2: Aggregate Results
Combine per-package results:
- Total source files and test files across packages
- Aggregate coverage before/after
- Combined gap list sorted by risk score
- Per-package summary table
Step 7.3: Per-Package Breakdown in Report
Add per-package section to both JSON and markdown reports:
{
"packages": [
{
"name": "packages/sdk",
"source_files": 45,
"test_files": 20,
"test_ratio": 0.44,
"critical_gaps": 2,
"tests_generated": 5
},
{
"name": "packages/vscode",
"source_files": 75,
"test_files": 25,
"test_ratio": 0.33,
"critical_gaps": 6,
"tests_generated": 10
}
]
}
Self-Assessment Epilogue
<!-- include: ../_shared/SELF_ASSESSMENT_EPILOGUE.md -->Output Format
JSON Schema
See Phase 6 Step 6.1 for the complete JSON report structure.
Report Files
| File | Format | When Written |
| --------------------------------------- | -------- | -------------------------- |
| .nightgauge/test-scaffold-report.json | JSON | --format json/both |
| Console output | Markdown | --format summary/both |
| tests/scaffold/*.scaffold.test.* | Tests | Unless --skip-generation |
Error Handling
| Condition | Action |
| --------------------------- | ---------------------------------------------- |
| No ecosystem detected | Proceed with generic file analysis only |
| Coverage tool not installed | Skip coverage phase, note in report |
| Test framework not detected | Error with list of supported frameworks |
| --priority invalid value | Error with valid priority levels |
| --max-tests not a number | Error with usage instructions |
| Assessment path not found | Error with path not found message |
| jq not installed | Error with install instructions |
| Permission denied on files | Skip inaccessible files, note count in report |
| Large codebase timeout | Use head/--max-count limits, sample files |
| Monorepo package not found | Warning, skip package, continue with others |
| Generated test fails (3x) | Mark as skipped with explanation comment |
| Health-check report stale | Ignore stale data, run fresh coverage analysis |
Pipeline Position
UTILITIES (not part of main pipeline)
/nightgauge:health-check ────────────────────┐
| |
Standalone utility — run anytime (optional input)
Writes: .nightgauge/health-report.json |
v
/nightgauge:test-scaffold
|
Standalone utility — run anytime
Reads: Codebase files (read-only analysis)
Reads: .nightgauge/health-report.json (optional)
Writes: .nightgauge/test-scaffold-report.json
Writes: tests/scaffold/*.scaffold.test.* (generated tests)
This is a standalone utility skill. It does not affect pipeline state and can be run at any time without interfering with active pipeline runs.
Configuration
This skill reads configuration from .nightgauge/config.yaml if present:
| Config Key | Default | Description |
| ---------------------------------- | ------- | ------------------------------- |
| test_scaffold.default_format | both | Default --format value |
| test_scaffold.max_tests | 20 | Default --max-tests value |
| test_scaffold.priority_threshold | high | Default --priority value |
| test_scaffold.skip_generation | false | Default for --skip-generation |
Author: nightgauge License: Apache-2.0
TDD Red-Green-Refactor
Testing
Skill that guides Claude through the complete TDD cycle.
Web Accessibility Audit
Testing
Performs a comprehensive web accessibility audit following WCAG standards.
UAT Test Case Generator
Testing
Generates structured and comprehensive user acceptance test cases.