### Generated main.N package versions
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/reference/release-process
Example installable package versions generated by the Main NuGet Builds workflow from the ArchLinterDevelopmentVersion value and the workflow run number.
```text
0.8.0-main.421
0.8.0-main.422
```
--------------------------------
### Initialize tool manifest and install ArchLinterNet.Cli
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/guides/real-repository-workflow
Creates a new .NET tool manifest and installs the ArchLinterNet CLI locally. Use when first adopting the tool to pin the version in the repository.
```bash
dotnet new tool-manifest
dotnet tool install ArchLinterNet.Cli
```
--------------------------------
### Install and invoke ArchLinterNet CLI
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/getting-started
Install the ArchLinterNet CLI as a local tool using a .NET tool manifest, then invoke the pinned repository tool with the --help flag. Prefer this approach for repository and CI use over a global install.
```bash
dotnet new tool-manifest
dotnet tool install ArchLinterNet.Cli
dotnet tool restore
```
```bash
dotnet arch-linter-net --help
```
--------------------------------
### Install ArchLinterNet.Cli globally
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/installation
Installs ArchLinterNet.Cli as a global .NET tool and verifies it with the help command. A global install follows the caller's package source and version resolution, so it should not be treated as an implicit CI version policy.
```shell
dotnet tool install --global ArchLinterNet.Cli
arch-linter-net --help
```
--------------------------------
### Pin the tool with a local manifest
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/guides/upgrading
Creates a local tool manifest, installs ArchLinterNet.Cli, restores the tool, and verifies the version. The first install records the exact selected version in `.config/dotnet-tools.json`; review and commit that manifest.
```bash
dotnet new tool-manifest
dotnet tool install ArchLinterNet.Cli
dotnet tool restore
dotnet arch-linter-net --version
```
--------------------------------
### Example strict_project_metadata contracts
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/policy-format/project-metadata
Example configuration with two contracts: one enforcing packaging and visibility defaults, and one forbidding production projects from referencing test projects.
```yaml
analysis:
solution: ArchLinterNet.slnx
contracts:
strict_project_metadata:
- id: package-project-defaults
name: package-project-defaults
projects:
- src/ArchLinterNet.Core/ArchLinterNet.Core.csproj
- src/ArchLinterNet.Cli/ArchLinterNet.Cli.csproj
required_properties:
Nullable: enable
TreatWarningsAsErrors: true
allowed_friend_assemblies:
- ArchLinterNet.Core.Tests
reason: Package-facing projects must preserve packaging and visibility defaults.
- id: production-projects-must-not-reference-tests
name: production-projects-must-not-reference-tests
projects:
- src/ArchLinterNet.Core/ArchLinterNet.Core.csproj
forbidden_project_references:
- tests/**/*.csproj
reason: Production projects must not depend on test projects.
```
--------------------------------
### Discover the policy schema from the installed package
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/guides/upgrading
Lists available schemas and prints the `policy-root` schema to a file. Do not copy a release-qualified schema URL from a web page; the installed package bytes are the compatibility authority.
```bash
dotnet arch-linter-net schema list
dotnet arch-linter-net schema print policy-root > policy-root.schema.json
```
--------------------------------
### Install pinned ArchLinterNet.Cli version into tool path
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/guides/extended-governance-adoption
Install the exact local-manifest version of ArchLinterNet.Cli into a workflow-owned tool path. Uses the repository's configured NuGet sources and credentials. The installed executable is exposed through one absolute path and does not choose a new version.
```bash
BASE_WORKTREE="../architecture-base"
TOOL_PATH="$ARTIFACTS/tool"
ARCHLINTERNET_VERSION="$({
dotnet tool list --local ArchLinterNet.Cli |
awk 'tolower($1) == "archlinternet.cli" { print $2 }'
})"
test -n "$ARCHLINTERNET_VERSION"
dotnet tool install ArchLinterNet.Cli \
--tool-path "$TOOL_PATH" \
--version "$ARCHLINTERNET_VERSION"
export PATH="$TOOL_PATH:$PATH"
arch-linter-net --version
```
--------------------------------
### List and print packaged schemas
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/cli
List installed schemas and print the policy-root schema. Use these commands for offline schema discovery instead of deriving schema identity from package SemVer.
```bash
arch-linter-net schema list
arch-linter-net schema print policy-root
```
--------------------------------
### Reproduce v0.6.5 → v0.7.0 evidence with ArchLinterNet.Cli 0.7.0
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/guides/real-repository-workflow
Install the historical 0.7.0 CLI into a caller-owned directory, then run the history analyze command from a detached worktree to reproduce the recorded v0.6.5 → v0.7.0 evidence. The first block sets up the tool and verifies its version; the second creates the worktree, runs the analysis with the specified policy and format, and hashes the output artifact. Use these steps only for reproducing the recorded evidence; do not substitute the current manifest inputs.
```bash
export ARCH_LINTER_TOOLS="$(mktemp -d)"
dotnet tool install \
--tool-path "$ARCH_LINTER_TOOLS" \
ArchLinterNet.Cli \
--version 0.7.0
if [[ "${OS:-}" == "Windows_NT" ]]; then
export ARCH_LINTER_NET="$ARCH_LINTER_TOOLS/arch-linter-net.exe"
else
export ARCH_LINTER_NET="$ARCH_LINTER_TOOLS/arch-linter-net"
fi
"$ARCH_LINTER_NET" --version
```
```bash
git worktree add --detach ../example-v070 v0.7.0
cd ../example-v070
export ARCH_LINTER_ARTIFACTS="$(mktemp -d)"
"$ARCH_LINTER_NET" history analyze \
--from v0.6.5 \
--to v0.7.0 \
--repository . \
--policy architecture/dependencies.arch.yml \
--format json > "$ARCH_LINTER_ARTIFACTS/release-forensics.json"
sha256sum "$ARCH_LINTER_ARTIFACTS/release-forensics.json"
```
--------------------------------
### Complete ArchLinterNet policy example
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/getting-started/first-policy
A complete ArchLinterNet policy file (version: 2) that combines layers, target assemblies, a strict rule, and a strict layer rule. The v2 version indicates the default waiver lifecycle is used.
```yaml
version: 2
name: My Architecture Contract
layers:
application:
namespace: MyApp.Application
domain:
namespace: MyApp.Domain
infrastructure:
namespace: MyApp.Infrastructure
analysis:
target_assemblies:
- MyApp.Application
- MyApp.Domain
- MyApp.Infrastructure
contracts:
strict:
- id: application-not-infrastructure
name: application-must-not-depend-on-infrastructure
source: application
forbidden: [infrastructure]
reason: Application code must depend on abstractions, not concrete infrastructure.
strict_layers:
- id: clean-architecture-layering
name: clean-architecture-layering
layers:
- infrastructure
- application
- domain
reason: Dependencies must point inward toward the domain.
```
--------------------------------
### strict_project_metadata contract example
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/contracts/project-metadata
Example YAML configuration for a strict_project_metadata contract. It requires the solution ArchLinterNet.slnx, specifies the core project, requires Nullable=enable and TreatWarningsAsErrors=true, allows the friend assembly ArchLinterNet.Core.Tests, forbids references to test projects, and provides a reason.
```yaml
analysis:
solution: ArchLinterNet.slnx
contracts:
strict_project_metadata:
- id: core-project-metadata
name: core-project-metadata
projects:
- src/ArchLinterNet.Core/ArchLinterNet.Core.csproj
required_properties:
Nullable: enable
TreatWarningsAsErrors: true
allowed_friend_assemblies:
- ArchLinterNet.Core.Tests
forbidden_project_references:
- tests/**/*.csproj
reason: Core packaging and friend-assembly boundaries must stay intentional.
```
--------------------------------
### Unity/client: `sourceDirectoryPrefixes` with baseTypeNames
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/policy-format/cel-expressions
Unity/client example that selects types deriving from `UnityEngine.MonoBehaviour` and located under the `Assets/Game/Client` directory prefix. Use `sourceDirectoryPrefixes` (not `sourcePaths`) because a directory string is never equal to a full file path.
```yaml
layers:
unity_presenters:
selector:
role: PresentationLayer
when: >
"UnityEngine.MonoBehaviour" in subject.baseTypeNames
&& "Assets/Game/Client" in subject.sourceDirectoryPrefixes
```
--------------------------------
### Coverage summary output example
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/contracts/coverage
Example of the human-readable coverage summary output. It shows the `Coverage summary:` section with per-contract lines and evidence sub-lines labeled `uncovered:`, `stale:`, `unknown:`, and `optional-empty:`. Use this to understand the format of the summary that appears after `Coverage findings:`.
```text
Coverage summary:
- [validation-namespace-coverage] [validation-namespace-coverage] scope: namespace covered=0 excluded=0 uncovered=1 stale=0 unknown=0 optional-empty=0
uncovered: ArchLinterNet.Core.Validation (ArchLinterNet.Core.Validation.ArchitectureBaselineService)
- [rule-input-coverage] [rule-input-coverage] scope: rule_input covered=2 excluded=0 uncovered=0 stale=1 unknown=1 optional-empty=1
stale: ghost-rule (ghost)
unknown: typo-rule (does_not_exist_layer)
optional-empty: future-rule:forbidden:future (Future module is planned.; future)
```
--------------------------------
### Example supplemental coverage sections output
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/usage/output-formats
Shows the human output for coverage findings and summaries, including uncovered namespaces and dependency edges with counts.
```text
Coverage findings:
- [feature-namespace-coverage] [feature-namespace-coverage] MyApp.Features.Payments -> uncovered namespace: MyApp.Features.Payments.PaymentsRepresentative
- [layer-edge-coverage] [layer-edge-coverage] MyApp.Cli.Commands -> MyApp.Testing.Fixtures -> uncovered dependency edge: MyApp.Cli.Commands.DeployCommand
Coverage summary:
- [feature-namespace-coverage] [feature-namespace-coverage] scope: namespace covered=4 excluded=1 uncovered=1 stale=0 unknown=0
uncovered: MyApp.Features.Payments (MyApp.Features.Payments.PaymentsRepresentative)
- [layer-edge-coverage] [layer-edge-coverage] scope: dependency_edge covered=1 excluded=0 uncovered=1 stale=0 unknown=0
uncovered: MyApp.Cli.Commands -> MyApp.Testing.Fixtures (MyApp.Cli.Commands.DeployCommand)
```
--------------------------------
### Example topology declaration for a server/library
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/guides/topology-review-workflow
Declares an exhaustive topology with three assemblies mapped to nodes and allowed edges from server to application to domain. Use this as a template for hand-authoring a policy; a reviewer must author these choices, not the capture command.
```yaml
topology:
mode: exhaustive
subject_kind: assembly
scope:
allow_empty: false
selectors:
- assembly: MyProduct.Server
- assembly: MyProduct.Application
- assembly: MyProduct.Domain
nodes:
- id: server
mappings:
- assembly: MyProduct.Server
- id: application
mappings:
- assembly: MyProduct.Application
- id: domain
mappings:
- assembly: MyProduct.Domain
allowed_edges:
- from: server
to: application
- from: application
to: domain
```
--------------------------------
### Cycle diagnostic example
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/contracts/cycles
Shows the format of a cycle diagnostic, which describes the layer path of the cycle.
```text
application -> infrastructure -> application
```
--------------------------------
### ArchLinterNet root policy example (architecture/arch.yml)
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/ai/policy-authoring-guide
Recommended root policy convention for ArchLinterNet. The selected path makes this file the root; imports define fragments. Keep layer/package/external-dependency/condition-set keys global and contract IDs unique across the graph.
```yaml
# architecture/arch.yml — recommended root convention, not a required name
version: 1
name: Product Architecture
imports:
- policy/shared/layers.arch.yml
- policy/bounded-contexts/sales.arch.yml
layers: {}
analysis: {}
contracts: {}
```
--------------------------------
### NUnit example for architecture validation
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/usage/test-adapter
Shows how to use ArchitectureAssertions to validate strict and audit architecture contracts from a policy file in an NUnit test fixture.
```csharp
using ArchLinterNet.Testing;
using NUnit.Framework;
[TestFixture]
public sealed class ArchitectureTests
{
[Test]
public void StrictArchitectureContractsMustPass()
{
ArchitectureAssertions
.FromPolicy("architecture/dependencies.arch.yml")
.ValidateStrict()
.ShouldPass();
}
[Test]
public void AuditArchitectureContractsMustPassWhenTeamMakesThemBlocking()
{
ArchitectureAssertions
.FromPolicy("architecture/dependencies.arch.yml")
.ValidateAudit()
.ShouldPass();
}
}
```
--------------------------------
### Generate base and current architecture change snapshots and report
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/guides/single-tool-workflow
Create a base snapshot from the base worktree, then a current snapshot from the current state, and finally generate a change report. Use the same CLI version, mode, condition set, and build selectors for both snapshots. The example selects strict mode to match the later Health artifact. Base and candidate baselines are detected independently; do not point one snapshot at a baseline from the other revision.
```bash
(
cd "$BASE_WORKTREE"
baseline_args=()
if [[ -f architecture/baseline.arch.yml ]]; then
baseline_args=(--baseline architecture/baseline.arch.yml)
fi
arch-linter-net change snapshot \
--policy architecture/arch.yml \
--mode strict \
"${baseline_args[@]}" \
--ensure-built \
--output "$ARTIFACTS/architecture-base.json"
)
baseline_args=()
if [[ -f architecture/baseline.arch.yml ]]; then
baseline_args=(--baseline architecture/baseline.arch.yml)
fi
arch-linter-net change snapshot \
--policy architecture/arch.yml \
--mode strict \
"${baseline_args[@]}" \
--ensure-built \
--output "$ARTIFACTS/architecture-current.json"
arch-linter-net change report \
--base "$ARTIFACTS/architecture-base.json" \
--current "$ARTIFACTS/architecture-current.json" \
--execution-context pr-123 \
--format json \
--output "$ARTIFACTS/architecture-change.json"
```
--------------------------------
### Sample timing report output
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/usage/timings
Example output of the timing report showing phase-level timings, including total, load_and_setup, configuration_check, contract_checks, and post_processing. Timing values are machine-dependent and should not be used as golden test data.
```text
Validation timings:
total 452 ms
load_and_setup 51 ms
yaml_loading 12 ms
root_resolution 3 ms
condition_set_resolution 2 ms
assembly_resolution 34 ms
configuration_check 8 ms
contract_checks 389 ms
dependency count=1 9 ms
layer count=1 12 ms
allow_only count=0 0 ms
cycle count=1 20 ms
method_body count=2 345 ms
post_processing 2 ms
```
--------------------------------
### Discover and export offline schemas with schema list and schema print
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/guides/upgrading
In a prepared offline environment, list all available schemas and print each one to a file. `schema list` reports the logical ID, document version, immutable `$id`, and packaged path; `schema print` writes the exact installed bytes.
```bash
dotnet arch-linter-net schema list
```
```bash
dotnet arch-linter-net schema print policy-root > policy-root.schema.json
```
```bash
dotnet arch-linter-net schema print policy-fragment > policy-fragment.schema.json
```
```bash
dotnet arch-linter-net schema print baseline > baseline.schema.json
```
```bash
dotnet arch-linter-net schema print api-snapshot > api-snapshot.schema.json
```
```bash
dotnet arch-linter-net schema print normalized-finding > finding.schema.json
```
```bash
dotnet arch-linter-net schema print analysis-build-state > build-state.schema.json
```
```bash
dotnet arch-linter-net schema print analysis-cache > cache.schema.json
```
```bash
dotnet arch-linter-net schema print analysis-profile > profile.schema.json
```
--------------------------------
### Inspect graph and explain dependency with arch-linter-net
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/guides/real-repository-workflow
Investigate the current dependency graph and get an explanation for a specific dependency. Uses the same policy and namespace level. Outputs a Mermaid diagram and an explanation for the source-target pair.
```bash
arch-linter-net graph \
--policy architecture/arch.yml \
--mode all \
--level namespace \
--format mermaid
arch-linter-net explain \
--policy architecture/arch.yml \
--source MyApp.Application \
--target MyApp.Infrastructure \
--level namespace
```
--------------------------------
### Deterministic expansion order example
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/policy-format/imports
Shows the composed source order when the root imports A and B, and A imports C and D. The order is root, A, C, D, B. Moving the imports key within a YAML document does not change this order.
```text
root, A, C, D, B
```
--------------------------------
### Example fragment with layers and contracts
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/policy-format/imports
A fragment containing partial policy sections with the normal top-level wrappers. It includes a layers section and a contracts section with a strict contract that forbids a dependency. The fragment must contain a mergeable section or a non-empty imports list.
```yaml
layers:
shared_kernel:
namespace: Shop.SharedKernel
contracts:
strict:
- id: shared-kernel-does-not-use-sales
name: shared-kernel-does-not-use-sales
source: shared_kernel
forbidden: [sales_domain]
reason: Shared kernel code must not depend on a bounded context.
```
--------------------------------
### Restore, build, and run arch-linter-net in strict mode
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/guides/single-tool-workflow
Prepare build outputs (restore and build) then run arch-linter-net with the architecture policy in strict mode. Normal validation does not silently build, so outputs must be prepared manually unless using the --ensure-built option.
```bash
dotnet restore
```
```bash
dotnet build Example.Product.slnx --no-restore
```
```bash
dotnet arch-linter-net --policy architecture/arch.yml --mode strict
```
--------------------------------
### Remediation guidance structured data example
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/usage/output-formats
Example JSON structure for a port-boundary finding with a declared seam, showing relevant fields including contract identity, evidence, and caveat.
```json
"remediation_guidance": {
"category": "use_declared_port",
"summary": "Use the declared port seam instead of the direct cross-context dependency.",
"contract_identity": "orders-boundary",
"finding_identity": { "source_assembly": "App", "source_type": "App.Orders.OrderService" },
"evidence": [
{ "kind": "evidence_kind", "value": "direct_edge" },
{ "kind": "expected_seam", "value": "role:Port, name: Orders" }
],
"expected_seam_or_direction": "role:Port, name: Orders",
"caveat": "The declared seam is the only supported alternative; do not add a broad exception.",
"requires_review": false
}
```
--------------------------------
### Run arch-linter-net with cache, profile, and concurrency options
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/guides/upgrading
Opt-in execution controls for caching, profiling, and concurrency. The first command runs with defaults; the second enables automatic caching and writes a profile; the third uses a custom cache path and limits parallelism to 1. These flags do not change canonical findings, identity, order, or exit categories.
```bash
dotnet arch-linter-net --policy architecture/arch.yml --mode strict
```
```bash
dotnet arch-linter-net --policy architecture/arch.yml --mode strict \
--cache auto --profile artifacts/architecture-profile.json
```
```bash
dotnet arch-linter-net --policy architecture/arch.yml --mode strict \
--cache .architecture-cache --max-parallelism 1
```
--------------------------------
### JSON output example for validation result
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/usage/output-formats
Example JSON output shape for a validation result. Shows the structure of coverage_findings, policy_inventory, waivers, policy_consistency_findings, and coverage_summary. Use as a reference for expected fields and nesting.
```json
{
"passed": false,
"mode": "strict",
"violations": [],
"cycles": [],
"coverage_findings": [
{
"contract": "feature-namespace-coverage",
"contract_id": "feature-namespace-coverage",
"source": "MyApp.Features.Payments",
"forbidden_namespace": "uncovered namespace",
"forbidden_references": ["MyApp.Features.Payments.PaymentsRepresentative"]
},
{
"contract": "layer-edge-coverage",
"contract_id": "layer-edge-coverage",
"source": "MyApp.Cli.Commands -> MyApp.Testing.Fixtures",
"forbidden_namespace": "uncovered dependency edge",
"forbidden_references": ["MyApp.Cli.Commands.DeployCommand"]
}
],
"unmatched_ignored_violations": [],
"policy_inventory": {
"schema": "architecture-policy-inventory/v1",
"effective_rule_count": 42,
"rules": { "strict": 31, "audit": 7, "coverage": 4 },
"ignore_debt": {
"total": 7,
"active": 5,
"stale": 1,
"expired": 1,
"metadata_incomplete": 0,
"invalid": 0
},
"waivers": []
},
"waivers": [
{
"id": "ARCH-IGN-042",
"state": "active",
"target_fingerprint": "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"owner": "architecture-team",
"issue": "ARCH-231",
"introduced": "2026-08-01",
"expires": "2026-10-01",
"evaluation_date": "2026-08-02",
"matches_governed_finding": true
}
],
"policy_consistency_findings": [
{
"kind": "policy_consistency",
"check_kind": "duplicate-id",
"contract": "domain-boundaries",
"contract_id": "domain-boundaries",
"reason": "Contract ID is used more than once.",
"conflicting_contract_ids": ["domain-boundaries", "domain-boundaries"],
"conflicting_contract_names": ["domain-boundaries", "domain-boundaries-copy"],
"layers": []
}
],
"coverage_summary": [
{
"contract": "feature-namespace-coverage",
"contract_id": "feature-namespace-coverage",
"scope": "namespace",
"counts": { "covered": 4, "excluded": 1, "uncovered": 1, "stale": 0, "unknown": 0 },
"excluded_items": [
{ "item": "MyApp.Features.Video.Generated", "reason": "Generated code is excluded from manual architecture coverage." }
],
"uncovered_items": [
{ "item": "MyApp.Features.Payments", "evidence": "MyApp.Features.Payments.PaymentsRepresentative" }
],
"stale_items": [],
"unknown_items": [],
"covered_items": [
{ "item": "MyApp.Features.Billing", "evidence": "MyApp.Features.Billing.BillingRepresentative" }
]
},
{
"contract": "layer-edge-coverage",
"contract_id": "layer-edge-coverage",
"scope": "dependency_edge",
"counts": { "covered": 1, "excluded": 0, "uncovered": 1, "stale": 0, "unknown": 0 },
"excluded_items": [],
"uncovered_items": [
{ "item": "MyApp.Cli.Commands -> MyApp.Testing.Fixtures", "evidence": "MyApp.Cli.Commands.DeployCommand" }
],
"stale_items": [],
"unknown_items": [],
"covered_items": [
{ "item": "MyApp.Cli.Commands -> MyApp.Core.Deployment", "evidence": "MyApp.Cli.Commands.DeployCommand" }
]
}
]
}
```
--------------------------------
### arch-linter-net explain command and output
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/policy-format/cel-expressions
Command-line invocation of `arch-linter-net explain` with source, target, and policy arguments, followed by its output showing the resolved path, contract IDs, and the matched `when` expression.
```text
$ arch-linter-net explain --source Acme.Sales.Checkout --target Acme.Inventory.StockItem --policy architecture/arch.yml
Acme.Sales.Checkout -> Acme.Inventory.StockItem
Contract IDs: cross-domain-when
[cross-domain-when] when: target.metadataText["domain"] != source.metadataText["domain"] (matched)
```
--------------------------------
### Discovery configuration with projects list
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/policy-format/project-metadata
Alternative discovery configuration using an explicit list of project paths.
```yaml
analysis:
projects:
- src/MyApp/MyApp.csproj
```
--------------------------------
### Create and compare architecture snapshots with arch-linter-net change
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/cli
Creates two snapshots with the same policy, mode, and build options, then generates a human-readable change report comparing them. Use --ensure-built for both snapshots when the policy opts into a shared framework, and keep --configuration, --framework, --platform, and --runtime consistent across them. --no-restore preserves an offline, fail-closed build when the consumer has already restored its prerequisites.
```bash
arch-linter-net change snapshot \
--policy architecture/arch.yml \
--mode strict \
--ensure-built --configuration Debug --framework net10.0 \
--output base-snapshot.json
arch-linter-net change snapshot \
--policy architecture/arch.yml \
--mode strict \
--ensure-built --configuration Debug --framework net10.0 \
--output current-snapshot.json
arch-linter-net change report \
--base base-snapshot.json \
--current current-snapshot.json \
--execution-context local-review \
--format human
```
--------------------------------
### Version 2 baseline output example
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/guides/migration-baselines
Example version-2 baseline output. Shows the structured identity fields (identity_version, contract_family, kind, source_assembly, target_assembly, target_member, occurrence) that distinguish same-named types and multiple occurrences. source_type and forbidden_reference remain as display fields but are not the identity.
```yaml
version: 2
baseline:
strict:
- id: app-boundaries
ignored_violations:
- source_type: MyApp.App.Legacy.LegacyService
forbidden_reference: MyApp.Infrastructure.LegacyDb
reason: "Initial baseline — migration tracked in #123"
identity_version: 2
contract_family: strict
kind: dependency
source_assembly: MyApp.App
target_assembly: MyApp.Infrastructure
target_member: MyApp.Infrastructure.LegacyDb
occurrence: 0
- source_type: MyApp.App.Old.OldController
forbidden_reference: MyApp.Infrastructure.SqlRepo
reason: "Initial baseline — migration tracked in #123"
identity_version: 2
contract_family: strict
kind: dependency
source_assembly: MyApp.App
target_assembly: MyApp.Infrastructure
target_member: MyApp.Infrastructure.SqlRepo
occurrence: 0
```
--------------------------------
### Invalid / unsupported-feature examples: syntax error, deferred matches, deferred arithmetic
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/policy-format/cel-expressions
Invalid and unsupported-feature examples. The first is a syntax error; the second is valid standard CEL but `matches`/regex is deferred under Profile v1 (UnsupportedFeature); the third is valid standard CEL but arithmetic is deferred under Profile v1.
```yaml
# Invalid CEL — SyntaxError.
when: subject.role ==
# Valid standard CEL, but `matches`/regex is deferred under Profile v1 —
# UnsupportedFeature, not a syntax error.
when: subject.namespace.matches("^Acme\\.Sales\\.")
# Valid standard CEL, but arithmetic is deferred under Profile v1.
when: subject.metadataText["priority"] + 1 > 5
```
--------------------------------
### Example architecture-health badge messages
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/reference/architecture-health
Example badge messages produced by the architecture-health badge projection. The exact values and badge color are CLI-owned. Missing or mismatched trusted promotion evidence must publish UNASSESSABLE · ? ignores · ? rules rather than retain an older healthy badge as current.
```text
architecture | HEALTHY · 0 ignores · 42 rules
architecture | DEBT · 3 ignores · 42 rules
```
--------------------------------
### Inspect manifest and verify schema
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/guides/release-provenance-verification
Displays the manifest's schema, version, source commit, and package/symbol inventory, then checks the schema and source commit match.
```bash
jq '{schema, version, source_commit,
packages: [.packages[] | {id, version, package: .package.file, symbols: .symbols.file}]}' \
package-manifest.json
jq -e --arg commit "$SOURCE_COMMIT" \
'.schema == "checkpoint-b-candidate-manifest/v2" and .source_commit == $commit' \
package-manifest.json
```
--------------------------------
### Run ArchLinterNet.Cli from source
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/installation
Runs the CLI from source inside the repository. The first command shows help; the second runs architecture validation with a policy file and strict mode.
```shell
dotnet run --project src/ArchLinterNet.Cli -- --help
dotnet run --project src/ArchLinterNet.Cli -- \
--policy architecture/arch.yml \
--mode strict
```
--------------------------------
### Evaluate combined new-debt gate with baseline and policy contexts
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/usage/test-adapter
Shows how to evaluate the combined new-debt gate using a baseline and policy weakening contexts, and assert that the gate passed.
```csharp
ArchitectureDebtGateOutcome gate = ArchitectureAssertions
.FromPolicy("architecture/dependencies.arch.yml")
.WithBaseline("architecture/baseline.arch.yml")
.WithPolicyWeakeningContexts("base-policy-context.json", "current-policy-context.json")
.EvaluateDebtGate();
Assert.That(gate.Passed, Is.True);
```
--------------------------------
### Strict Interface Implementation Configuration
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/contracts/interface-implementation
Example configuration for restricting application port implementations to the infrastructure layer.
```yaml
contracts:
strict_interface_implementation:
- id: ports-implemented-only-by-adapters
name: application-ports-implemented-only-by-adapters
interface_prefixes: [MyApp.Application.Ports.]
allowed_only_in_layers: [infrastructure]
reason: Port implementations belong to the infrastructure boundary.
```
--------------------------------
### Discovery configuration with solution
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/policy-format/project-metadata
Configures project discovery using a solution file. Without discovery, the contract cannot evaluate and CheckConfiguration reports 'no project metadata discovered'.
```yaml
analysis:
solution: MyApp.slnx
```
--------------------------------
### Preview baseline migration with --dry-run
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/guides/upgrading
Previews a baseline migration without writing, so you can review the proposed identities before writing. Use this before the actual migration.
```bash
dotnet arch-linter-net baseline migrate \
--config architecture/arch.yml \
--baseline architecture/baseline-v1.yml \
--output architecture/baseline.yml \
--dry-run
```
--------------------------------
### Inheritance contract configuration example
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/contracts/inheritance
YAML configuration for a strict_inheritance contract that forbids domain types from inheriting framework base types like UnityEngine.MonoBehaviour and Microsoft.EntityFrameworkCore.DbContext.
```yaml
contracts:
strict_inheritance:
- id: domain-no-framework-base-types
name: domain-must-not-inherit-framework-types
source_layers: [domain]
forbidden_base_types:
- UnityEngine.MonoBehaviour
- Microsoft.EntityFrameworkCore.DbContext
reason: Domain types must stay framework-independent.
```
--------------------------------
### Example human diagnostic output shape
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/usage/output-formats
Shows the format of a single diagnostic line in human output, including the policy and rule IDs with a dependency arrow between types.
```text
- [application-not-infrastructure] [application-must-not-depend-on-infrastructure] MyApp.Application.Services.LegacyService -> MyApp.Infrastructure: MyApp.Infrastructure.Repositories.UserRepository
```
--------------------------------
### Modular-monolith: `when` with namespace refinement
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/policy-format/cel-expressions
Modular-monolith example that combines a literal `metadata` selector with a `when` expression. The `when` adds namespace refinement beyond what the literal `role`/`metadata` boundary can express.
```yaml
layers:
sales_domain:
selector:
role: DomainLayer
metadata:
domain: Sales
when: >
subject.metadataText.containsKey("domain")
&& subject.metadataText["domain"] == "Sales"
&& (
subject.namespace == "Acme.Sales.Domain"
|| subject.namespace.startsWith("Acme.Sales.Domain.")
)
```
--------------------------------
### Restrict project to allowed framework groups
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/policy-format/framework-references
Restricts a project to only an allow-listed set of framework groups using 'framework_allow_only'. This example allows 'MyApp.Worker' to reference only the 'core_only' group.
```yaml
contracts:
strict_framework_allow_only:
- id: worker-allowed-frameworks
name: worker-may-only-reference-core
source: MyApp.Worker
allowed: [core_only]
reason: Worker projects may only reference the core shared framework.
```
--------------------------------
### Strict allow-only contract example
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/contracts/allow-only
Defines a strict_allow_only contract that allows no first-party dependencies (allowed: []). Typically used for layers like domain models or pure libraries.
```yaml
contracts:
strict_allow_only:
- id: domain-pure
name: domain-allowed-dependencies
source: domain
allowed: []
reason: Domain must not depend on other first-party layers.
```
--------------------------------
### Generate base and current policy contexts and compare
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/ai/policy-weakening-review
Produce base and current policy-context JSON artifacts from their own repository states, then compare them. Do not pass two policy YAML files from the current checkout and call one historical evidence. The guardrail proves direction for same-ID strict-to-audit/removal, resolved source-set and explicit analysis scope reduction, required-to-optional source sets, source expansions, or rule inputs, matched subtractive exclusions, explicit permission/prohibition inventories, and universal ignores.
```bash
# In the base repository/policy state
arch-linter-net policy context --policy architecture/dependencies.arch.yml --format json > base.json
# In the current repository/policy state
arch-linter-net policy context --policy architecture/dependencies.arch.yml --format json > current.json
arch-linter-net policy weakening --base-context base.json --current-context current.json --format human
```
--------------------------------
### Tilt: run ArchLinterNet with fixed argv
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/guides/reference-entrypoints
Tilt local resource that invokes ArchLinterNet with a fixed argv list. Use this instead of a shell string to avoid interpolating repository or environment values. Tilt only orchestrates the run and does not alter policy, baseline, API snapshot, cache, or report semantics.
```shell
local([
"dotnet",
"arch-linter-net",
"--policy", "architecture/arch.yml",
"--mode", "strict",
])
```
--------------------------------
### External allow-only contract example
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/contracts/external-allow-only
Shows the YAML structure for an external allow-only contract. It declares external dependency groups and a contract that allows only the 'approved_sdk' group for the 'infrastructure_adapter' source layer.
```yaml
external_dependencies:
approved_sdk:
namespace_prefixes:
- MyApp.Vendor.ApprovedSdk
unsupported_sdk:
namespace_prefixes:
- MyApp.Vendor.LegacySdk
contracts:
strict_external_allow_only:
- id: adapter-approved-sdk-only
name: adapter-may-only-reference-approved-sdk
source: infrastructure_adapter
allowed: [approved_sdk]
reason: This adapter may only use the approved SDK, not any other declared vendor dependency group.
```
--------------------------------
### Configure NuGet.org source and trust with nuget.config and dotnet nuget trust
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/guides/release-provenance-verification
Creates a nuget.config that pins the NuGet.org source and requires signature validation, then trusts the nuget.org source and lists trusted sources. Use in a fresh empty directory to avoid machine- or user-level configuration. The identity check uses only the Python 3 standard library.
```bash
cat > nuget.config <<'EOF'
EOF
dotnet nuget trust source nuget.org \
--source-url https://api.nuget.org/v3/index.json \
--configfile ./nuget.config
dotnet nuget trust list --configfile ./nuget.config
```
--------------------------------
### Allow-only contract with allowed_types example
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/contracts/allow-only
Shows a strict_allow_only contract that allows dependencies on a specific type using allowed_types, in addition to allowed layers. Use when exact type exceptions are needed.
```yaml
contracts:
strict_allow_only:
- id: application-allowed-dependencies
name: application-allowed-dependencies
source: application
allowed: [domain]
allowed_types:
- MyApp.Infrastructure.Abstractions.IClock
reason: Application depends on Domain plus a temporary clock abstraction exception.
```
--------------------------------
### Illustrative attribute usage shape
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/policy-format/semantic-role-catalog
Example C# attribute usage showing how user-defined attributes are applied to types and assemblies. The attribute names are illustrative candidates, not types supplied by the current product.
```csharp
[DomainLayer("Sales")]
public sealed class Order { }
[assembly: SharedKernel("Billing")]
```
--------------------------------
### Upgrade ArchLinterNet CLI and verify policy
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/guides/extended-governance-adoption
Run these commands to update the repository-local tool, restore tools, check the version, and verify the existing policy still works in strict mode.
```bash
dotnet tool update ArchLinterNet.Cli
dotnet tool restore
dotnet arch-linter-net --version
dotnet arch-linter-net policy check --policy architecture/arch.yml
dotnet arch-linter-net --policy architecture/arch.yml --mode strict --ensure-built
```
--------------------------------
### VS Code YAML language server directive
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/policy-format/imports
Inline directive for VS Code with Red Hat YAML, works with any filename. The example shows a minimal fragment with an empty layers map.
```yaml
# yaml-language-server: $schema=../../../schema/dependencies.arch.fragment.schema.json
layers: {}
```
--------------------------------
### Ignored violations example
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/contracts/dependency
Shows how to use `ignored_violations` to allow a specific forbidden reference as frozen debt. The `source_type` and `forbidden_reference` fields match the exact types, and `reason` documents the tracking issue.
```yaml
ignored_violations:
- source_type: MyApp.Application.Legacy.LegacyUseCase
forbidden_reference: MyApp.Infrastructure.LegacyGateway
reason: Existing debt tracked in #123.
```
--------------------------------
### Capture architecture snapshots and generate change report
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/guides/real-repository-workflow
Capture complete architecture snapshots from base and candidate repository states, then generate a change report. Use identical build-state selection for both snapshots; add --no-restore when the consumer has already restored build prerequisites and the workflow must remain offline.
```bash
# Base checkout/worktree
arch-linter-net change snapshot \
--policy architecture/arch.yml \
--mode strict \
--ensure-built --configuration Debug --framework net10.0 \
--output ../base.architecture-change.json
# Candidate checkout
arch-linter-net change snapshot \
--policy architecture/arch.yml \
--mode strict \
--ensure-built --configuration Debug --framework net10.0 \
--output current.architecture-change.json
arch-linter-net change report \
--base ../base.architecture-change.json \
--current current.architecture-change.json \
--execution-context pr-123 \
--format json
```
--------------------------------
### Run strict gate with explicit build preparation
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/guides/upgrading
For a clean checkout where the CLI owns preparation, runs strict validation with `--ensure-built` to explicitly prepare the build.
```bash
dotnet arch-linter-net --policy architecture/arch.yml --mode strict --ensure-built
```
--------------------------------
### Transitive dependency contract example
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/contracts/dependency
Adds `dependency_depth: transitive` to a strict contract to also fail on indirect dependency paths. Transitive checks are more expensive and should be used when the dependency path matters architecturally.
```yaml
contracts:
strict:
- id: application-not-transitively-infrastructure
name: application-must-not-transitively-depend-on-infrastructure
source: application
forbidden: [infrastructure]
dependency_depth: transitive
reason: Application must not have any dependency path into Infrastructure.
```
--------------------------------
### Run arch-linter-net gate with baseline and policy
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/guides/real-repository-workflow
Run the CI gate that combines no-new-debt and weakening checks. Requires a reviewed baseline and policy files. Outputs JSON evidence for architecture validation, change report, policy weakening, debt gate, and remediation.
```bash
arch-linter-net gate \
--policy architecture/arch.yml \
--baseline architecture/baseline.arch.yml \
--base-context ../base-policy-context.json \
--current-context current-policy-context.json \
--mode all \
--ensure-built \
--format json
```
--------------------------------
### Basic dependency contract example
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/contracts/dependency
Defines a strict dependency contract that forbids the application layer from referencing the infrastructure layer. The `source` and `forbidden` fields specify the layers, and `reason` provides justification.
```yaml
contracts:
strict:
- id: application-not-infrastructure
name: application-must-not-depend-on-infrastructure
source: application
forbidden: [infrastructure]
reason: Application code must depend on abstractions, not concrete infrastructure.
```
--------------------------------
### Set ArchLinterDevelopmentVersion in Directory.Build.props
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/reference/release-process
Sets the development version for installable main builds. The Main NuGet Builds workflow reads this value and produces a unique package version from its monotonic workflow run number.
```xml
0.8.0
```
--------------------------------
### Run arch-linter-net health with baseline and contexts
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/reference/architecture-health
Run the canonical Health summary with required baseline and optional context files. Use --mode strict and --ensure-built for strict validation and build enforcement. Supply --base-context and --current-context together when policy-weakening evidence must be included.
```bash
arch-linter-net health \
--policy architecture/arch.yml \
--baseline architecture/baseline.arch.yml \
--base-context artifacts/policy-base.json \
--current-context artifacts/policy-current.json \
--mode strict \
--ensure-built \
--execution-context local-review \
--format json
```
--------------------------------
### when_expressions JSON output
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/policy-format/cel-expressions
Example of the `when_expressions` array included in JSON output for a violation involving `when`-bearing selectors. Shows entries for both `source` and `forbidden` locations with their source expressions, results, and YAML paths.
```json
{
"when_expressions": [
{
"location": "source",
"source": "source.metadataText[\"tier\"] == \"premium\"",
"result": "matched",
"yaml_path": "contracts.strict_context_dependencies[0].source"
},
{
"location": "forbidden",
"source": "target.metadataText[\"domain\"] != source.metadataText[\"domain\"]",
"result": "matched",
"yaml_path": "contracts.strict_context_dependencies[0].forbidden[0]"
}
]
}
```
--------------------------------
### Explain dependency path with arch-linter-net explain
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/cli
Explains the dependency path between two namespaces or types. Supports namespace/type granularity; for assembly-level topology use graph --level assembly. Use history analyze when the question is about how architecture evidence changed over repository history rather than the current graph.
```bash
arch-linter-net explain \
--policy architecture/arch.yml \
--source MyApp.Application \
--target MyApp.Infrastructure \
--level namespace
```
--------------------------------
### Restore, build, and run the first strict gate
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/guides/upgrading
For an ordinary checkout, restores dependencies, builds the solution without restoring, and runs strict validation against the policy.
```bash
dotnet restore
dotnet build Example.Product.slnx --no-restore
dotnet arch-linter-net --policy architecture/arch.yml --mode strict
```
--------------------------------
### New-debt gate with policy-weakening guardrails
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/guides/ci-integration
Exports base and current policy contexts, then runs the gate command to reject new architecture debt and policy weakening. Requires explicit baseline path and same CLI version for both contexts. Returns 1 for debt or error findings, 2 for missing inputs.
```yaml
- name: Export base policy context
run: git worktree add --detach .ci-base origin/main && dotnet arch-linter-net policy context --policy .ci-base/architecture/dependencies.arch.yml --format json > base-policy-context.json
```
```yaml
- name: Export current policy context
run: dotnet arch-linter-net policy context --policy architecture/dependencies.arch.yml --format json > current-policy-context.json
```
```yaml
- name: Reject new architecture debt and policy weakening
run: dotnet arch-linter-net gate \
--policy architecture/dependencies.arch.yml \
--baseline architecture/baseline.arch.yml \
--base-context base-policy-context.json \
--current-context current-policy-context.json \
--format json > architecture-debt-gate.json
```
--------------------------------
### Literal namespace prefix in layers
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/policy-format/layers-and-namespaces
Use a literal namespace value to match the exact namespace and all child namespaces. For example, MyApp.Domain matches MyApp.Domain.Models and deeper descendants, but not unrelated prefixes like MyApp.DomainLegacy.
```yaml
layers:
domain:
namespace: MyApp.Domain
```
--------------------------------
### Pin archlinternet.cli main build in dotnet-tools.json
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/reference/release-process
Use this JSON configuration to pin an exact main build of the archlinternet.cli tool in a consumer repository. The consumer must have Read access to the package's Manage Actions access and authenticate with its own GITHUB_TOKEN. Keep any read:packages PAT in user-level NuGet credentials and never commit it.
```json
{
"version": 1,
"isRoot": true,
"tools": {
"archlinternet.cli": {
"version": "0.8.0-main.421",
"commands": ["arch-linter-net"]
}
}
}
```
--------------------------------
### Create explicit empty baseline and run gate
Source: https://eugenemalaschuk-source.github.io/arch-linter-net/guides/extended-governance-adoption
Creates an explicit workflow-local empty v3 baseline when no reviewed baseline exists, then runs gate with that baseline. The empty file is explicit zero-debt authority, not a hidden mutation of repository policy.
```bash
ARTIFACTS="$(pwd)/artifacts"
mkdir -p "$ARTIFACTS"
if [[ -f architecture/baseline.arch.yml ]]; then
CURRENT_BASELINE="$(pwd)/architecture/baseline.arch.yml"
dotnet arch-linter-net baseline verify \
--policy architecture/arch.yml \
--baseline "$CURRENT_BASELINE"
else
CURRENT_BASELINE="$ARTIFACTS/empty-baseline.arch.yml"
cat > "$CURRENT_BASELINE" <<'YAML'
version: 3
baseline: {}
metric_baselines: []
YAML
fi
dotnet arch-linter-net gate \
--policy architecture/arch.yml \
--baseline "$CURRENT_BASELINE" \
--mode all
```