### 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 ```