| name | characterize-brownfield |
| description | Use to reverse-engineer an existing/brownfield codebase into structured, confidence-scored characterization artifacts (tech stack, feature inventory, dependency & integration map, existing API contracts, test-coverage traceability, tech debt & risks). Use before composing a PRD, or standalone to understand a legacy system with no docs. Activate on 'characterize this codebase', 'what does this system do', 'map the architecture', 'find existing contracts', 'assess tech debt', 'reverse-engineer this legacy system'. |
Characterize Brownfield
Reverse-engineers an existing codebase into structured, confidence-scored artifacts. Every
claim is either a FACT (from a tool call — S7) or an INFERENCE (LLM interpretation), and every
inference carries a confidence: High (directly observed, e.g. a parsed OpenAPI file) /
Medium (inferred from strong signals, e.g. route patterns + naming conventions) / Low
(guessed from weak or absent evidence). Honesty about confidence is the point — a brownfield
PRD built on fabricated certainty is worse than one that says "unknown."
Boundary. Read-only. Never edits code. Does not write a PRD (see compose-brownfield-prd).
Does not create issues or stories.
Inputs
- Repository path (required).
- Depth:
quick (2-5 min, pattern-based, no source reads) / deep (10-30 min, critical dirs —
default) / exhaustive (30-120 min, all source, opt-in only — warn the user of cost first).
- Focus directories (optional) — narrows deep/exhaustive scans.
Procedure
1. Structure & stack (FACT, via tool calls — S7 only, never recall)
find . -maxdepth 3 -type d (or workspace tools) for top-level layout.
- Read manifest files present:
package.json, *.csproj/*.sln, requirements.txt/pyproject.toml,
pom.xml/build.gradle, go.mod, Cargo.toml. Extract exact framework + versions — never guess.
- Read config files present:
tsconfig.json, .editorconfig, lint configs, CI workflow files.
git log --since="90 days ago" --name-only --pretty=format: to find active areas (recent
churn correlates with what still matters).
2. Feature inventory (INFERENCE, confidence-scored)
For deep/exhaustive: read entry points (controllers, route files, CLI commands, message
handlers). Group into a feature table:
| Feature | Evidence | Confidence | Category |
|---|
| {name} | {file:line or pattern} | High/Med/Low | Core / Secondary / Legacy-unused |
"Legacy-unused" = present in code but no recent git activity and no inbound references found —
flag, do not delete, do not assume dead.
3. Integration & dependency map (FACT where possible)
List outbound calls (HTTP clients, DB connections, message queues, third-party SDKs) found via
grep for client instantiation patterns, connection strings, appsettings*/.env* keys (names
only — never read secret values). Each entry: target system, protocol, confidence.
4. Existing API contracts (opt-in facet, graceful degradation)
Search for OpenAPI/Swagger/AsyncAPI/Pact files (*.yaml/*.json matching openapi:/swagger:/
asyncapi:, pacts/*.json) and framework-generated specs (Swashbuckle/NSwag output routes,
springdoc, FastAPI /openapi.json route). Light-weight v1: discover + summarize (endpoint
list, methods, request/response schema names) — not full schema validation. If none found,
record contracts: none-found and move on; this is optional, never a blocker.
5. Coverage traceability (opt-in facet)
For each feature in the inventory, check test coverage: search for test files referencing the
feature (by name, route, or class). Classify FULL (direct test asserting the behavior) /
PARTIAL (indirect coverage, e.g. integration test exercises the path but doesn't assert the
specific behavior) / NONE. When zero tests exist for a feature, use a synthetic oracle:
infer expected behavior from the code itself (read the implementation, describe what it
currently does) and mark confidence Low — this still gives downstream steps something concrete
instead of a blank. Never claim FULL coverage without reading an actual assertion.
6. Risk scoring (3x3 model)
For each tech-debt item or gap found (missing tests, undocumented integration, outdated
dependency, NONE coverage on a Core feature): score probability x impact = 1-9
(1-3 each axis). Map: 9 -> P0 (auto-flag BLOCK), 6-8 -> P1, 4-5 -> P2, 1-3 -> P3.
Action per band: P0 = BLOCK (escalate), P1 = MITIGATE, P2 = MONITOR, P3 = DOCUMENT.
7. Confidence gate (S4 — before writing artifacts)
Compute an overall confidence distribution (% High / Med / Low across all inferences) and a
coverage summary (% features with FULL/PARTIAL/NONE traceability). Verdict:
- PASS — majority High confidence, no Core feature at NONE coverage with P0/P1 risk.
- CONCERNS — meaningful Medium/Low share, or NONE coverage on a P1+ risk item.
- FAIL — majority Low confidence or critical structure (stack/entry points) unresolved.
CONCERNS or FAIL: surface an explicit validation checklist to the human (list every Low-confidence
claim and every NONE-coverage Core feature) before proceeding. This is a quality checkpoint, not a
security gate — the human confirms or corrects, then work continues.
Outputs
Written under .copilot-tracking/skraft-plans/{projectSlug}/characterization/{YYYY-MM-DD}/
(see plugins/skraft-framework/instructions/skraft-artifacts.instructions.md for the path root; this workflow is
standalone and does not claim a pipeline phase row):
index.md — summary, confidence distribution, coverage summary, gate verdict.
structure.md — stack, versions, layout (FACT).
features.md — feature inventory table.
integration.md — dependency/integration map.
contracts.md — discovered API contracts (or none-found).
coverage.md — traceability table (FULL/PARTIAL/NONE per feature).
tech-debt.md — risk-scored gaps (3x3 table).
Every file starts with <!-- markdownlint-disable-file -->. Every claim in every file carries
its confidence tag inline, e.g. (confidence: Medium — inferred from route naming).