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-architecture

Analyzing 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.

ReferenceArtifactWhat it provides
references/unit-graph.mdunit_graph.yamlSchema for units, exported_signature, dynamic_entrypoints, shared_refs
references/behavior.mdunits/*/behavior.yamlSchema for side_effects, branches, error_paths, concurrency
references/bindings.mdunits/*/bindings.yamlSchema for framework wiring, runtime_config
references/wire-contracts.mdwire_contracts.yamlSchema for external contracts, target_contract, semantic_divergence
references/shared-modules.mdshared_modules.yamlSchema for god-class registry, fields with types, shared_refs relationship
references/cross-unit-state.mdcross_unit_state.yamlSchema for implicit state flows, pairing values, verification_hint
references/migration-boundary.mdmigration_boundary.yamlIntent interpretation, must_rewrite with reasons, strategy rules
references/seams.mdseams.yamlSchema for frozen_contract, bridge_points, declared vs inferred rules
references/unit-decomposition.mdunits/*/unit_decomposition.yamlSchema for candidate_splits, split-driver vocabulary
references/project-structure.mdproject-structure.mdFunctional domains, layers, project type — global prose view for planning/feature-inventory
references/tech-stack.mdtech-stack.mdFrameworks, deps, runtime versions, migration blockers — global prose view
references/data-model.mddata-model.mdEntity 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.mdarchitecture_index.mdImplementation Guide contract, per-unit navigation template
references/consumption-contract.md(downstream)How implementation agents read the artifacts

Design Principles

  1. 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.
  2. Source-loc as identifier: source_loc: path:line is the natural ID. Never invent stable IDs.
  3. Self-contracting fields: each field carries its consumption contract (must_preserve, must_appear_in_target) so the next agent needs no extra skill loaded.
  4. Per-unit sharding + global tables: agents load one unit's small files plus global indexes, not a monolith.
  5. Extraction heuristics inline: tell the executing LLM HOW to find data, not just the schema.
  6. 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 (static vs static+runtime), not a number nobody measured.
  7. 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.
  8. 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_anchors are discovery evidence, not rewrite targets.
  9. 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 — always
  • migration_boundary.yaml — rewrite/migration work
  • wire_contracts.yaml — always
  • shared_modules.yaml — always
  • cross_unit_state.yaml — always
  • seams.yaml — when seams exist (declared or inferred); omit file entirely if no seams found
  • architecture_index.md (top-level implementation guide) — always
  • project-structure.md (global prose: functional domains, layers, project type) — always
  • tech-stack.md (global prose: frameworks, deps, runtime versions, migration blockers) — always
  • data-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.yaml
  • units/<unit_name>/bindings.yaml
  • units/<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-unit shared_refs). Load references/unit-graph.md for schema and self-check.
  • behavior.yaml (side_effects, branches, error_paths, concurrency). Load references/behavior.md for schema.
  • bindings.yaml (framework wiring + runtime_config). Load references/bindings.md for schema.
  • wire_contracts.yaml (rest/grpc/kafka/sql/semantic_divergence). Load references/wire-contracts.md for schema and scope boundary.
  • shared_modules.yaml (god-class registry: kind/used_by_units/fields/split_candidate). Load references/shared-modules.md for schema and shared_refs relationship.
  • cross_unit_state.yaml (implicit session/ThreadLocal/SSO flows; per-row must_confirm:runtime for any unpaired flow). Load references/cross-unit-state.md for schema and pairing values.

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

  1. 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 — read references/extraction-signals.md and map discovered signals into the structured artifacts. 1c. Produce global prose views — alongside the structured per-unit artifacts, emit project-structure.md (functional domains, layers, project type), tech-stack.md (frameworks, deps, runtime versions, migration blockers), and data-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.
  2. Build unit_graph.yaml (spine). Resolve exported_signature from public signatures only. Seed shared_modules.yaml same pass; flag god-class + reference-cliff candidates. 2b. Build migration_boundary.yaml — infer user intent, acceptance criteria, cleanup requirement, and the smallest runtime-reachable rewrite boundary. Populate must_rewrite, copy_as_is, legacy_allowed_to_remain, and defer_cleanup. Only choose full_rewrite when user intent or technical evidence requires it; do not equate source_anchors with rewrite targets.
  3. Per-unit files — IMMEDIATELY after unit_graph, before global tables. For EVERY unit listed in unit_graph.yaml, create units/<unit_name>/behavior.yaml, units/<unit_name>/bindings.yaml, and units/<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. Populate shared_refs from subset whitelist. unit_decomposition sets commit: false. 3b. Verify per-unit completeness before proceeding. Run: count the units in unit_graph.yaml and count the units/*/behavior.yaml files. 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.
  4. Build wire_contracts.yaml — outward edges; cross-ref unit_graph for external interfaces. 4b. Build cross_unit_state.yaml — scan medium patterns; pair across units only; per-row must_confirm:runtime on any unpaired flow. 4c. Build seams.yaml (skip if no declared or inferred seams exist) — emit every user-declared seam first (source: declared). Then add inferred seams from discovery signals. For each seam: record frozen_side + frozen_side_rule (always), and — where protocols differ — the bridge_points conversion design (mapping_rule + edge_cases + idempotency_retry + fallback). Add frozen_contract only 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 an Implementation 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.
  5. 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_graph has behavior/bindings/decomposition files.
    • migration_boundary.yaml exists for rewrite/migration work; must_rewrite is the implementation scope; source_anchors are not treated as rewrite targets.
    • The architecture index contains an Implementation Guide for 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 TBD in wire_contracts.yaml::target_contract, seams.yaml::frozen_contract target form, or seams.yaml::bridge_points[].mapping_rule.
    • No source file in source_anchors of 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_state flow with pairing != matched carries must_confirm: runtime.
    • Every declared seam present; every seam has a frozen_side_rule; frozen_contract present only where the frozen source is invisible/unrecoverable; every protocol_shift != null seam has ≥1 bridge_point with concrete mapping_rule (no TBD).

Rules

  • No prose narrative artifacts. Reasoning lives in notes: / rationale:.
  • No stable IDs. source_loc: path:line is 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.
  • TBD forbidden in wire contract target_contract, seam frozen_contract target form, and seam mapping_rule.
  • unit_decomposition.yaml MUST set commit: 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 declared seam is authoritative — design may not overrule it; an inferred seam 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 from unit_graph.source_anchors, all files of the old framework, or inventory lists. source_anchors prove behavior exists; they do not mandate rewriting that file.
  • Classification lists are vocabularies for judgment, not CI-enforced enums.

Failure Mode Map

#Failure modePrevented by
1Dropped side-effectbehavior.yaml::side_effects[must_preserve]
2Dropped framework bindingbindings.yaml::bindings[must_appear_in_target]
3Hallucinated target APIguidelines/<source>-to-<target>/ (out of scope)
4Broken caller (signature unsync)unit_graph.yaml::depends_on + exported_signature
5Dead-code removal of reflection/DI classunit_graph.yaml::dynamic_entrypoints
6Wire contract breakwire_contracts.yaml::stability:frozen + target_contract
7Tx boundary lostbehavior.yaml::concurrency.tx_boundary
8Cross-language semantic gotchawire_contracts.yaml::semantic_divergence
9Missing runtime configbindings.yaml::runtime_config[must_appear_in_target]
10Significant branch droppedbehavior.yaml::branches[must_preserve]
11Error contract driftbehavior.yaml::error_paths[contract + must_preserve]
12Shared module duplicated/lostshared_modules.yaml::migration_strategy
13Concurrency model mismatchbehavior.yaml::concurrency.model
14Implicit cross-unit state lostcross_unit_state.yaml::flows[must_preserve]
15Static pairing missed dynamic key / external writercross_unit_state.yaml::pairing + must_confirm:runtime
16God-class field drift / hallucinated fieldsshared_modules.yaml::god_class + shared_refs.used_fields ⊆
17Premature commit to target unit countunit_decomposition.yaml::commit:false + candidate_splits
18Split candidate without driver/rationaleunit_decomposition.yaml::candidate_splits[].drivers + rationale
19Fabricated score/confidence misleads designPrinciple 6 + self-check: no composite_score
20Partial-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
21Frozen side refactored, breaking un-migrated peersseams.yaml::frozen_side_rule
22Protocol-shift conversion left to the migrating agent's guess (wrong mapping/edge cases)seams.yaml::bridge_points[mapping_rule + edge_cases + idempotency_retry + fallback]
23User-specified cut point silently overruled by analyzeseams.yaml::source:declared authoritative rule
24Inventory-driven over-rewrite: every discovered framework file or source_anchor becomes an implementation task, even though a smaller runtime boundary satisfies acceptancemigration_boundary.yaml::{strategy,must_rewrite,legacy_allowed_to_remain} + rule: source_anchors are not rewrite targets
25User asked for clean/full rewrite but analyze silently leaves legacy runtime residuemigration_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_signature suffices
  • 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