analyzing-architecture
Runs deep codebase analysis: architecture patterns, tech stack, data models, integration points, and migration risks. Produces research artifacts consumed by…
npx skills add https://github.com/microsoft/github-copilot-modernization --skill analyzing-architectureAnalyzing Architecture
Purpose
Produce only artifacts that reduce a named failure mode. Anything that doesn't is excluded — it wastes agent context.
There are two artifact purposes:
- Implementation fidelity: behavior/contract fidelity to translate one unit correctly, plus seam contracts to integrate against the un-migrated remainder.
- Design evidence: evidence to decide unit count and splits — never pre-baked decisions.
Required References
Each reference contains the YAML schema, extraction rules, and self-check for its artifact. Read all before starting the workflow.
| Reference | Artifact | What it provides |
|---|---|---|
references/unit-graph.md | unit_graph.yaml | Schema for units, exported_signature, dynamic_entrypoints, shared_refs |
references/behavior.md | units/*/behavior.yaml | Schema for side_effects, branches, error_paths, concurrency |
references/bindings.md | units/*/bindings.yaml | Schema for framework wiring, runtime_config |
references/wire-contracts.md | wire_contracts.yaml | Schema for external contracts, target_contract, semantic_divergence |
references/shared-modules.md | shared_modules.yaml | Schema for god-class registry, fields with types, shared_refs relationship |
references/cross-unit-state.md | cross_unit_state.yaml | Schema for implicit state flows, pairing values, verification_hint |
references/migration-boundary.md | migration_boundary.yaml | Intent interpretation, must_rewrite with reasons, strategy rules |
references/seams.md | seams.yaml | Schema for frozen_contract, bridge_points, declared vs inferred rules |
references/unit-decomposition.md | units/*/unit_decomposition.yaml | Schema for candidate_splits, split-driver vocabulary |
references/project-structure.md | project-structure.md | Functional domains, layers, project type — global prose view for planning/feature-inventory |
references/tech-stack.md | tech-stack.md | Frameworks, deps, runtime versions, migration blockers — global prose view |
references/data-model.md | data-model.md | Entity inventory, relationships, key-entities summary — global prose view |
references/extraction-signals.md | (all artifacts) | Signal→artifact mapping, what to look for per signal area |
references/architecture-index.md | architecture_index.md | Implementation Guide contract, per-unit navigation template |
references/consumption-contract.md | (downstream) | How implementation agents read the artifacts |
Design Principles
- Failure-mode-driven: every field traces to a row in the Failure Mode Map (end of file). Can't name the failure it prevents → don't produce it.
- Source-loc as identifier:
source_loc: path:lineis the natural ID. Never invent stable IDs. - Self-contracting fields: each field carries its consumption contract (
must_preserve,must_appear_in_target) so the next agent needs no extra skill loaded. - Per-unit sharding + global tables: agents load one unit's small files plus global indexes, not a monolith.
- Extraction heuristics inline: tell the executing LLM HOW to find data, not just the schema.
- Evidence, never fabricated numbers: emit only values a tool actually computed, each with provenance. No invented composite scores, no made-up
confidence: 0.9. Where confidence matters, report the evidence basis (staticvsstatic+runtime), not a number nobody measured. - Analyze observes; design decides: candidate splits, candidate seams — never a committed unit count, never aggregates/BCs, never a priority ranking that pre-empts design's choice.
- Migration boundary is a first-class contract: for rewrite/migration work, always identify the smallest runtime boundary that can satisfy the user's acceptance criteria. Expand to a full rewrite only when the user asks for clean removal/no legacy residue, or when technical constraints make a partial boundary unsafe.
source_anchorsare discovery evidence, not rewrite targets. - Heuristic flag vs control gate:
- Heuristic flags allowed: a magic number that only labels something for design to re-check. Design sees the data and can overrule.
- Control gates forbidden: a number that changes what reaches the artifact set (truncating candidates at a cap, skipping a flow below a floor). Replace with raw counts + per-row semantic contracts.
- Classification vocabularies are examples to recognize by judgment, not closed enums to CI-validate.
What is a "Unit"
A unit = one externally triggerable entry point (HTTP route, scheduled job, message handler, UI page, public API surface, CLI command). Uniqueness invariant: each source file appears in at most one unit's source_anchors. Files used by ≥2 units → shared_modules.yaml.
The Artifacts
artifacts/
├── architecture_index.md top-level implementation guide
├── project-structure.md global prose, functional domains + layers + project type
├── tech-stack.md global prose, frameworks + deps + runtime versions
├── data-model.md global prose, entity inventory + key-entities summary
├── unit_graph.yaml global index, lightweight
├── migration_boundary.yaml global, minimal runnable boundary + rewrite scope contract
├── wire_contracts.yaml global, outward contracts
├── shared_modules.yaml global, files used by ≥2 units; god-class registry
├── cross_unit_state.yaml global, implicit shared-state flows
├── seams.yaml global, partial-migration cut points + bridge design
└── units/<unit_name>/
├── behavior.yaml per-unit, heavyweight
├── bindings.yaml per-unit; may be [] + reason
└── unit_decomposition.yaml per-unit, CANDIDATES only, no commit
Outputs
Base path: {artifact_root}/ (typically .github/modernize/rearchitecture/artifacts/)
Global artifacts (1 each):
unit_graph.yaml— alwaysmigration_boundary.yaml— rewrite/migration workwire_contracts.yaml— alwaysshared_modules.yaml— alwayscross_unit_state.yaml— alwaysseams.yaml— when seams exist (declared or inferred); omit file entirely if no seams foundarchitecture_index.md(top-level implementation guide) — alwaysproject-structure.md(global prose: functional domains, layers, project type) — alwaystech-stack.md(global prose: frameworks, deps, runtime versions, migration blockers) — alwaysdata-model.md(global prose: entity inventory + key-entities summary) — when project has entities/ORM/DB access
Per-unit artifacts (one set per unit in unit_graph.yaml):
units/<unit_name>/behavior.yamlunits/<unit_name>/bindings.yamlunits/<unit_name>/unit_decomposition.yaml
Completeness invariant: count(units/*/behavior.yaml) == count(units in unit_graph.yaml).
For how downstream agents consume these artifacts, load references/consumption-contract.md.
target_idiom is NOT produced here — lives in guidelines/<source>-to-<target>/.
Architecture index artifact — implementation guide
The top-level architect artifact is an implementation index, not a prose summary. It must tell implementation agents which artifact paths to read, why each matters, how to filter global rows, and what completion evidence to report.
Load references/architecture-index.md for the required Implementation Guide contract and example shape.
Implementation-Fidelity Artifacts
Core artifacts:
unit_graph.yaml(entry-point enumeration +exported_signature+dynamic_entrypoints+ per-unitshared_refs). Loadreferences/unit-graph.mdfor schema and self-check.behavior.yaml(side_effects, branches, error_paths, concurrency). Loadreferences/behavior.mdfor schema.bindings.yaml(framework wiring + runtime_config). Loadreferences/bindings.mdfor schema.wire_contracts.yaml(rest/grpc/kafka/sql/semantic_divergence). Loadreferences/wire-contracts.mdfor schema and scope boundary.shared_modules.yaml(god-class registry: kind/used_by_units/fields/split_candidate). Loadreferences/shared-modules.mdfor schema andshared_refsrelationship.cross_unit_state.yaml(implicit session/ThreadLocal/SSO flows; per-rowmust_confirm:runtimefor any unpaired flow). Loadreferences/cross-unit-state.mdfor schema andpairingvalues.
migration_boundary.yaml — minimal runnable boundary + rewrite scope contract
Records the smallest runtime-reachable implementation boundary that satisfies the user's acceptance criteria. Implementation scope comes from must_rewrite, not from all source_anchors or every legacy-framework file.
Load references/migration-boundary.md for intent interpretation rules, schema, and self-check.
seams.yaml — partial-migration cut points + bridge design
Records deliberate cuts for partial migration: which side is frozen, which side migrates, and how the bridge converts protocols/idioms. declared seams are authoritative; inferred seams are advisory.
Load references/seams.md for schema, conditional frozen_contract rules, discovery signals, and self-check.
Design-Evidence Artifact
units/<unit>/unit_decomposition.yaml (per-unit)
Records split candidates for design. It produces candidate_splits, not target units; the design phase owns the decision.
Load references/unit-decomposition.md for schema and split-driver vocabulary.
Workflow
- Load context — source/target framework, existing KG,
guidelines/<source>-to-<target>/, and any user-declared seams (cut points the user specified). 1b. Load extraction signals — readreferences/extraction-signals.mdand map discovered signals into the structured artifacts. 1c. Produce global prose views — alongside the structured per-unit artifacts, emitproject-structure.md(functional domains, layers, project type),tech-stack.md(frameworks, deps, runtime versions, migration blockers), anddata-model.md(entity inventory + key-entities summary) per their reference schemas. These global views are consumed by creating-implementation-plan, feature-inventory, and the spec-quality gate; the structured YAML artifacts do not replace them. - Build
unit_graph.yaml(spine). Resolveexported_signaturefrom public signatures only. Seedshared_modules.yamlsame pass; flag god-class + reference-cliff candidates. 2b. Buildmigration_boundary.yaml— infer user intent, acceptance criteria, cleanup requirement, and the smallest runtime-reachable rewrite boundary. Populatemust_rewrite,copy_as_is,legacy_allowed_to_remain, anddefer_cleanup. Only choosefull_rewritewhen user intent or technical evidence requires it; do not equatesource_anchorswith rewrite targets. - Per-unit files — IMMEDIATELY after unit_graph, before global tables. For EVERY unit listed in
unit_graph.yaml, createunits/<unit_name>/behavior.yaml,units/<unit_name>/bindings.yaml, andunits/<unit_name>/unit_decomposition.yaml. Do not skip units. Do not create "representative samples". Do not defer to a later step. >~200 lines per file → re-examine the unit boundary. Populateshared_refsfrom subset whitelist.unit_decompositionsetscommit: false. 3b. Verify per-unit completeness before proceeding. Run: count the units inunit_graph.yamland count theunits/*/behavior.yamlfiles. If they do not match, create the missing per-unit files NOW. Do not proceed to step 4 until every unit has all three files. - Build
wire_contracts.yaml— outward edges; cross-ref unit_graph for external interfaces. 4b. Buildcross_unit_state.yaml— scan medium patterns; pair across units only; per-rowmust_confirm:runtimeon any unpaired flow. 4c. Buildseams.yaml(skip if no declared or inferred seams exist) — emit every user-declared seam first (source: declared). Then addinferredseams from discovery signals. For each seam: recordfrozen_side+frozen_side_rule(always), and — where protocols differ — thebridge_pointsconversion design (mapping_rule + edge_cases + idempotency_retry + fallback). Addfrozen_contractonly when the migrating agent cannot read the frozen side's behavior from source (binary/private dependency, config/data-gated semantics, name-contradicts-behavior); when the frozen source is visible and self-explanatory, omit it — don't restate what the agent reads directly. Resolve declared/inferred conflicts toward declared. 4d. Build the architecture index — top-level architect artifact with anImplementation Guide. For each unit, list exact artifact paths, purpose of each file, how to filter global rows, and required completion evidence. Do not make it a prose-only summary. - Self-check before completion (hard — execute, do not skip):
- Per-unit completeness gate (MUST execute): Count units in
unit_graph.yaml(grep -c '^\s*- name:' artifacts/unit_graph.yaml). Count per-unit behavior files (ls artifacts/units/*/behavior.yaml | wc -l). If counts do not match, list missing units and create their behavior.yaml, bindings.yaml, and unit_decomposition.yaml NOW. Do not report done until counts match. "Representative samples" or "most controllers follow identical patterns" is NOT acceptable — every unit gets all three files. - Every unit in
unit_graphhas behavior/bindings/decomposition files. migration_boundary.yamlexists for rewrite/migration work;must_rewriteis the implementation scope;source_anchorsare not treated as rewrite targets.- The architecture index contains an
Implementation Guidefor every unit, with exact artifact paths, purpose, row-filter instructions, and completion evidence requirements. - The architecture index explicitly states that it is not the full contract and implementation agents must follow the listed artifact paths before implementation.
- No
TBDinwire_contracts.yaml::target_contract,seams.yaml::frozen_contracttarget form, orseams.yaml::bridge_points[].mapping_rule. - No source file in
source_anchorsof multiple units. - Every produced field maps to a Failure Mode Map row.
unit_decomposition.commit == false.- Every
shared_refs.used_fields ⊆ {f.name for f in shared_modules.fields}. - Every
cross_unit_stateflow withpairing != matchedcarriesmust_confirm: runtime. - Every
declaredseam present; every seam has afrozen_side_rule;frozen_contractpresent only where the frozen source is invisible/unrecoverable; everyprotocol_shift != nullseam has ≥1bridge_pointwith concretemapping_rule(noTBD).
- Per-unit completeness gate (MUST execute): Count units in
Rules
- No prose narrative artifacts. Reasoning lives in
notes:/rationale:. - No stable IDs.
source_loc: path:lineis the identifier. - No tests / build / deploy / infra coverage. No standalone risk register. No
target_idiom.yaml. - Per-unit files capped at ~200 lines. Larger → split.
TBDforbidden in wire contracttarget_contract, seamfrozen_contracttarget form, and seammapping_rule.unit_decomposition.yamlMUST setcommit: false.- Factual numbers (line numbers, counts, lists) recorded as-is. Quality/cohesion values MUST be tool-computed with provenance; inventing scores is a hard failure.
- A
declaredseam is authoritative — design may not overrule it; aninferredseam is a candidate. - The frozen side of a seam MUST NOT be refactored in the migrating phase; the bridge adapts to it, not the reverse.
- Implementation scope comes from
migration_boundary.yaml::must_rewrite, not fromunit_graph.source_anchors, all files of the old framework, or inventory lists.source_anchorsprove behavior exists; they do not mandate rewriting that file. - Classification lists are vocabularies for judgment, not CI-enforced enums.
Failure Mode Map
| # | Failure mode | Prevented by |
|---|---|---|
| 1 | Dropped side-effect | behavior.yaml::side_effects[must_preserve] |
| 2 | Dropped framework binding | bindings.yaml::bindings[must_appear_in_target] |
| 3 | Hallucinated target API | guidelines/<source>-to-<target>/ (out of scope) |
| 4 | Broken caller (signature unsync) | unit_graph.yaml::depends_on + exported_signature |
| 5 | Dead-code removal of reflection/DI class | unit_graph.yaml::dynamic_entrypoints |
| 6 | Wire contract break | wire_contracts.yaml::stability:frozen + target_contract |
| 7 | Tx boundary lost | behavior.yaml::concurrency.tx_boundary |
| 8 | Cross-language semantic gotcha | wire_contracts.yaml::semantic_divergence |
| 9 | Missing runtime config | bindings.yaml::runtime_config[must_appear_in_target] |
| 10 | Significant branch dropped | behavior.yaml::branches[must_preserve] |
| 11 | Error contract drift | behavior.yaml::error_paths[contract + must_preserve] |
| 12 | Shared module duplicated/lost | shared_modules.yaml::migration_strategy |
| 13 | Concurrency model mismatch | behavior.yaml::concurrency.model |
| 14 | Implicit cross-unit state lost | cross_unit_state.yaml::flows[must_preserve] |
| 15 | Static pairing missed dynamic key / external writer | cross_unit_state.yaml::pairing + must_confirm:runtime |
| 16 | God-class field drift / hallucinated fields | shared_modules.yaml::god_class + shared_refs.used_fields ⊆ |
| 17 | Premature commit to target unit count | unit_decomposition.yaml::commit:false + candidate_splits |
| 18 | Split candidate without driver/rationale | unit_decomposition.yaml::candidate_splits[].drivers + rationale |
| 19 | Fabricated score/confidence misleads design | Principle 6 + self-check: no composite_score |
| 20 | Partial-migration cut breaks at the seam (an unreadable frozen-side semantic — binary dep, config-gated, or name-contradicts-behavior — never recorded) | seams.yaml::frozen_contract[must_preserve], conditional: only when source is invisible/unrecoverable |
| 21 | Frozen side refactored, breaking un-migrated peers | seams.yaml::frozen_side_rule |
| 22 | Protocol-shift conversion left to the migrating agent's guess (wrong mapping/edge cases) | seams.yaml::bridge_points[mapping_rule + edge_cases + idempotency_retry + fallback] |
| 23 | User-specified cut point silently overruled by analyze | seams.yaml::source:declared authoritative rule |
| 24 | Inventory-driven over-rewrite: every discovered framework file or source_anchor becomes an implementation task, even though a smaller runtime boundary satisfies acceptance | migration_boundary.yaml::{strategy,must_rewrite,legacy_allowed_to_remain} + rule: source_anchors are not rewrite targets |
| 25 | User asked for clean/full rewrite but analyze silently leaves legacy runtime residue | migration_boundary.yaml::{user_intent.cleanup_required,full_rewrite_reason} |
NOT Included
- Test coverage map — tester / runtime-validation
- Build / packaging / deploy topology —
analyzing-operations - Performance baseline — cutover phase
- Standalone risk register — inline
notes/stability - Architecture summary prose — implementation agents need source-anchored contracts, not prose-only summaries
- Idiom mapping —
guidelines/<source>-to-<target>/ - Function-level call graph beyond unit boundaries —
exported_signaturesuffices - Cohesion metrics / co-access clusters (LCOM4/TCC) — structural numbers did not change design decisions. God-class smells live on
shared_modules.yaml::split_candidate. - Pure syntax migration (Py2→3, Java 8→17)
- Target unit count commitment — design phase
- Aggregate / BC / VO decisions, domain renames, migration sequencing — design phase + human EventStorming
- Composite/priority scores, decision gate ratios — design weighs evidence with full context