Use this guide to turn repository paths and dependencies into an architecture
rule. It covers the model, YAML DSL, contract choice, language capabilities and
presets. You need a GoCPG binary with the relevant language frontend and a
checkout whose paths you can classify. The complete Python rule YAML below
can be saved directly in your project checkout.
Check that gocpg architecture --help lists validate, describe and
explain. If it reports an unknown command, the installed binary does not
support this reference; install a supported build before continuing.
Read execution and governance after the rule validates, and interfaces and results when you need to inspect persisted findings. The adoption guide connects these steps into one repository workflow.
Architecture Model Reference
The model gives every eligible entity one stable component UID. A component’s
id is a readable name that may change; uid preserves identity across a
rename or path move. Allocate a new UID when the responsibility changes. Parent
components group leaves; leaf selectors classify entities. source_roots
separate production, test, generated and benchmark material. Tags such as
layer, scope or runtime are independent dimensions, so a component can be
both layer: application and scope: billing.
For enforcement, classification must be exhaustive and unambiguous. Coverage is calculated over a known eligible population; the model policy handles empty selections. The model policy
controls unclassified entities, selector conflicts, generated sources, empty
selection and minimum coverage. If a selector matches two leaves, change the
selectors or classification policy explicitly. In this example, conflict_policy: error reports overlapping selectors as a classification error.
In the example below, src/ui/** and src/storage/** are separate leaves.
The policy deliberately assumes that those are the only production directories
in the fixture. A real checkout with src/jobs/ needs a third component or an
explicit disposition.
Architecture Rule DSL Reference
An architecture rule declares spec_version: "2.0", kind: architecture, a
stable id, and an architecture body. Unknown fields and unsupported versions
are errors. A missing kind is routed to the legacy pattern-rule compiler.
Each document contains one component model and one contract. The common
contract inputs are selectors, relation, languages,
required_capabilities, and minimum_coverage; each contract adds its own
fields.
Save the complete example below as
gocpg/docs/examples/architecture-control/architecture.yaml relative to your
working directory. Create the parent directories if they do not exist. In a
CodeGraph source checkout, that file is already supplied. These compilation
commands read the YAML only; they do not require a CPG or the example Python files:
spec_version: "2.0"
kind: architecture
id: example.ui-no-storage
version: "1"
fingerprint_version: 1
languages: [python]
architecture:
source_roots:
- path: src
source_set: production
classification:
exhaustive: true
conflict_policy: error
unclassified_policy: error
generated_policy: ignore
minimum_coverage: 1.0
components:
- uid: cmp-example-ui
id: ui
name: UI
kind: leaf
match:
paths: ["src/ui/**"]
- uid: cmp-example-storage
id: storage
name: Storage
kind: leaf
match:
paths: ["src/storage/**"]
contract: forbidden_dependency
from:
components: [cmp-example-ui]
to:
components: [cmp-example-storage]
relation:
edge_types: [import]
reachability: direct
languages: [python]
required_capabilities: [imports, source_sets]
minimum_coverage: 1.0
The edge type import names a graph relation. The capability imports
asserts that the Python fact producer can supply it. Changing
reachability: direct to a transitive check changes the rule’s meaning and
can invalidate prior governance decisions.
From the repository root in PowerShell, with gocpg on PATH, check both a
selected and an unselected language:
gocpg architecture validate --file gocpg/docs/examples/architecture-control/architecture.yaml --language python --json
gocpg architecture validate --file gocpg/docs/examples/architecture-control/architecture.yaml --language c --json
gocpg architecture describe --file gocpg/docs/examples/architecture-control/architecture.yaml --component ui --json
For Python, expect rule_id: example.ui-no-storage, applicable: true and
gate_evaluated: false. For C, expect applicable: false with
language_not_selected, even though command execution may return zero.
describe must identify cmp-example-ui. These commands compile and inspect
the model. Next, run an architecture check against the project CPG to obtain results for the release decision.
For source code under that model, an import from src/ui/view.py to
src/storage/db.py is a violation of the direct dependency contract. A UI
module that does not import storage would not produce this finding. The rule
reports either outcome only after a complete architecture
execution against a CPG for the same source revision. The YAML validation
commands above prepare the model. Use architecture execution to check these imports.
Contract Catalog
Choose the contract from the question you want the graph to answer. The v1 set contains exactly 12 contracts:
| Contract | Question | Required distinction |
|---|---|---|
forbidden_dependency |
Must A avoid B? | Direct or transitive relation is explicit. |
allowed_dependencies |
May A depend only on a listed set? | The allowlist is closed for selected edges. |
required_dependency |
Must each A depend on B? | Select each_subject, each_component, at_least_one, or exactly_one. |
layers |
Which layer can call which? | Define each layer and its may_depend_on set. |
independence |
Must groups remain independent? | Group by a component or tag dimension. |
acyclic |
Is the component graph cyclic? | Decide how to treat self-loops. |
public_api_only |
Must callers use exported API? | Define the target’s public surface. |
protected_component |
Who may access an internal component? | List allowed consumers. |
exclusive_api |
Who may use a named API surface? | Select one API and its consumers. |
exhaustive_classification |
Is every eligible entity classified once? | Unknown denominator or conflict is incomplete. |
metric_threshold |
Does a component metric exceed a limit? | State metric, operator and value. |
resolution_coverage |
Are dependencies resolved sufficiently? | Evaluate each language and capability separately. |
The example’s forbidden_dependency rejects a direct import from UI to
storage. A permitted import from UI to an application layer would require a
different component model and contract. A zero-match selector must follow the
model’s empty-selection policy. Configure that policy for the project’s expected component population.
Capability Registry
FoundationCapabilityManifests in gocpg/pkg/architecture/graph.go in the CodeGraph source checkout
defines fact families by language. Current v1 qualification covers C, C++, C#,
Go, Java, JavaScript, Kotlin, PHP, Python, TypeScript and 1C independently. It includes
families such as includes, imports, calls, inheritance, implements,
annotations, type_references, source_sets and dynamic or type-only imports
where the language adapter supplies them. The language preset registry contains eleven packages. Qualification runs 264 valid
and invalid examples across the twelve contracts.
complete means all required capabilities are available. degraded means an
optional family is missing. insufficient means a required family is missing.
An enforcing check needs complete for every selected language and required
capability. The capability registry connects each language adapter to the fact families used by the contract.
Preset Authoring Guide
A language preset needs one language, an exact semantic version, a capability manifest and source-backed valid and invalid fixtures for each contract. A fixture record binds its source path and SHA-256 to an expected fact profile and evaluator outcome. Qualification rejects missing or escaping paths, symlinks, digest mismatch, aliases to another language, and unexpected outcomes. A list of fixture records directs qualification to source files, whose hashes and actual outcomes it checks.
Changing a shipped preset requires a CodeGraph source checkout and Go 1.27, as declared in gocpg/go.mod. The v1 registry loads 14 packages: three generic and eleven language packages. This is the complete gocpg/configs/architecture/presets/v1/language/python/manifest.yaml:
id: python/module-boundaries
version: 1.0.0
languages: [python]
required_capabilities: [imports, source_sets]
fixture_profile: v1-contract-pairs
fixture_catalog: fixtures.yaml
fixture_catalog locates the fixture catalog relative to the manifest. For each of the 12 contracts, it lists valid and invalid source files, their SHA-256 hashes and facts_profile. From the gocpg module root, inspect the supplied files:
Get-Content configs/architecture/presets/v1/language/python/fixtures.yaml
Get-Content configs/architecture/presets/v1/language/python/fixtures/valid.py
Get-Content configs/architecture/presets/v1/language/python/fixtures/invalid.py
The valid source imports fixture_public.api; the invalid source imports fixture_internal.private_api. Catalog source paths are relative to the directory containing fixtures.yaml. Update hashes and expected facts when changing the source files.
Qualification normalizes CRLF line endings to LF before calculating SHA-256. From the gocpg module root, calculate the hash in the same way:
$source = [IO.File]::ReadAllText("configs/architecture/presets/v1/language/python/fixtures/valid.py")
$bytes = [Text.Encoding]::UTF8.GetBytes($source.Replace("`r`n", "`n"))
$sha = [Security.Cryptography.SHA256]::Create()
($sha.ComputeHash($bytes) | ForEach-Object { $_.ToString("x2") }) -join ""
$sha.Dispose()
For the supplied valid.py, expect 2b84c4226bd23ab9625636d21826036d2fc2a3e3f0df63b010c4794293f38ec1. After changing the file, place its new hash in each corresponding source_sha256 entry in fixtures.yaml.
When preparing a preset change:
- Keep its registered
id, set an exact version and declare one language.LoadV1PresetRegistrydefines the v1 package set; extending that set includes changing the registry and its checks. - Select only capabilities proven for that adapter and enrichment profile.
- Add valid and invalid source files for every included contract.
- Record their hashes, fact profiles and expected outcomes.
- From the
gocpgmodule root, run source-backed fixture qualification and exact-version resolution checks:
go test ./pkg/architecture -run '^TestV1LanguagePresetsUseRealSourceBackedFixtureCases$' -count=1
go test ./pkg/architecture -run '^TestStory1243PresetResolutionRejectsLatestAndCrossLanguageAliases$' -count=1
Both commands should report ok. The first loads the shipped registry and checks fixture paths,
hashes, facts and outcomes. The second checks exact version 1.0.0 resolution and rejection
of latest and cross-language aliases. Resolve failures before publishing a changed package.
Generic templates provide a model shape; the project still supplies its selectors and language rules. Each language preset is qualified against its own source files and facts.
Preset Catalog
The three generic templates are generic/clean-architecture@1.0.0,
generic/layered@1.0.0 and generic/modular-monolith@1.0.0. Independent
language manifests live under gocpg/configs/architecture/presets/v1/language/ for
the eleven qualified v1 languages above. Resolve by exact id@version; latest
and cross-language aliases fail. Consult the manifests for the current
capability and fixture set before selecting one for a repository.
Executable examples for the normative topics
The commands below run implementation checks from the gocpg module root.
They check the model implementation, contract catalog, language capabilities and presets. To check your repository, run architecture analysis.
go test ./pkg/architecture -run '^TestComponentHierarchyStableUIDAndDuckDBPersistence$' -count=1
go test ./pkg/architecture -run '^TestStory1240V1ContractRegistry$' -count=1
go test ./pkg/architecture -run '^TestV1FoundationCapabilitiesAndPresetIDsMatchNormativeSource$' -count=1
go test ./pkg/architecture -run '^TestV1LanguagePresetsUseRealSourceBackedFixtureCases$' -count=1
go test ./pkg/architecture -run '^TestStory1243V1PresetRegistryIsExactAndComplete$' -count=1