Skip to content

C4 — Level 2: Containers / components

Hexagonal: a pure decision core, ports for everything with a side effect.

How to read this page

Every solid node below names a package that exists today — verify with go list ./internal/... ./cmd/.... Every dashed node is a planned seam that has no code: it is designed, not shipped, and unlocks only when a named consumer commits (D-012). Arrows follow the decision path, and every solid pair drawn is backed by a real import between those two packages. This is not the complete edge set: cmd/assent is the composition root and directly imports 15 of the 22 internal packages plus the root schemas package; those edges are omitted for legibility. It reaches two more — internal/glob and internal/render/locale — only transitively. The remaining five are not linked into the binary at all (see Packages with no production importer). Derive both sets with go list -f '{{.Imports}}' ./cmd/assent and go list -deps ./cmd/assent.

flowchart LR
    classDef planned fill:none,stroke:#8a8a8a,stroke-width:1px,stroke-dasharray:5 5,color:#8a8a8a;

    subgraph cli["cmd/assent — one static binary, one run per MR"]
        direction TB

        subgraph inbound["Ingestion (adapters + loaders)"]
            forgeGitlab["internal/forge/gitlab<br/>GitLab HTTP adapter"]
            forgePort["internal/forge<br/>Forge port: Snapshot / Resolve /<br/>Reconcile / PublicationReceipt"]
            forgeGithub["GitHub adapter<br/>PLANNED — E10"]
            change["internal/change<br/>value tree · structural differ<br/>JSON · YAML · HCL/tfvars<br/>positions + resource limits"]
            evaldecode["internal/evaldecode<br/>strict decode of<br/>evaluation inputs"]
            policy["internal/core/policy<br/>policy envelope types<br/>+ loader (target ref, ADR-0015)"]
            catalogue["internal/catalogue<br/>catalogue load/combine<br/>profile to pack activation"]
            remotepacks["Remote packs<br/>PLANNED — E13"]
        end

        subgraph core["Pure decision core (no I/O)"]
            aggregate["internal/core/aggregate<br/>CEL assert trees (cel-go, ADR-0013)<br/>obligations · coverage · tri-state"]
            classifyPkg["internal/core/classify<br/>change-set classification"]
            decision["internal/core/decision<br/>Decision · Findings · Pins<br/>APPROVE · REVIEW · BLOCK<br/>+ report emission"]
            hash["internal/core/hash<br/>canonical JSON digests<br/>(ADR-0017)"]
            glob["internal/glob<br/>path globs for selectors"]
            purity["internal/core<br/>purity guard (test-only):<br/>core must not import I/O"]
            rego["rego predicate backend<br/>PLANNED — E11"]
        end

        subgraph providers["Provider host (no forge write token)"]
            provider["internal/provider<br/>host: HTTP + exec transports<br/>digest pinning · sensitive tier<br/>negotiation · max-age"]
            builtin["internal/provider/builtin<br/>builtin/gitlab-groups (forge-groups)<br/>repo-file · resource-owner"]
            grpcw["gRPC (tier 3) + WASM (tier 4)<br/>PLANNED — ADR-0004"]
        end

        subgraph outbound["Publication"]
            render["internal/render<br/>Markdown renderer · redaction<br/>finding lifecycle · summary"]
            locale["internal/render/locale<br/>locale string catalog"]
            serve["serve (HTTP API)<br/>PLANNED — E12"]
        end

        subgraph quality["Authoring + quality surfaces"]
            lint["internal/lint<br/>assent lint hard errors"]
            adoptertest["internal/adoptertest<br/>assent test harness (ADR-0014)"]
            compare["internal/compare<br/>assent compare suite"]
            schemadrift["internal/schemadrift<br/>presentation/config drift"]
            forgeFake["internal/forge/fake<br/>in-memory forge fake"]
            forgeConf["internal/forge/conformance<br/>SHA-guarded reconcile suite"]
        end

        schemas["schemas (root package)<br/>embedded JSON schemas —<br/>strict-decode authority"]
    end

    forgeGitlab --> forgePort
    change --> aggregate
    evaldecode --> change
    evaldecode --> aggregate
    catalogue --> policy
    policy --> schemas
    policy --> aggregate
    glob --> aggregate
    aggregate --> classifyPkg
    aggregate --> decision
    builtin --> provider
    provider --> aggregate
    provider --> schemas
    decision --> render
    locale --> render
    forgePort --> render
    render --> forgeGitlab
    aggregate --> lint
    aggregate --> adoptertest
    aggregate --> compare

    forgeGithub -.-> forgePort
    remotepacks -.-> catalogue
    rego -.-> decision
    grpcw -.-> provider
    decision -.-> serve

    class forgeGithub,remotepacks,rego,grpcw,serve planned;

Legend

Style Meaning
Solid border Shipped — the label names a real Go package or the real binary; present in go list ./internal/... ./cmd/...
Dashed border, PLANNED — E<n> / PLANNED — ADR-0004 Planned — a designed seam with no implementation. Deferred under D-012; unlocks only when a named consumer commits. See the feature-maturity table in the repository README
Solid arrow Decision-path flow; the pair is backed by a real import between those two packages
Dashed arrow The port a planned seam would plug into — no code today

Planned elements shown: GitHub adapter (E10), Rego backend (E11), serve HTTP API (E12), remote packs (E13), gRPC / WASM provider tiers (ADR-0004 tiers 3–4). Nothing else on this page is aspirational.

Packages with no production importer

These packages exist and are exercised, but no production code imports them — which is why they carry no solid arrow above. This section dates faster than the diagram; re-derive with go list -f '{{.ImportPath}} {{.Imports}} {{.TestImports}}' ./....

Package Reality
internal/core Test-only guard package (purity_test.go); asserts the core does not import I/O
internal/core/hash Canonical JSON digests (ADR-0017). At this commit imported only by internal/change tests — not yet on the decision path; AUD-S16 wires internal/compare to it
internal/schemadrift Drift gate; imported only by the tests of cmd/assent, internal/render and internal/forge/conformance
internal/forge/fake In-memory forge fake; test support only
internal/forge/conformance Port conformance suite; runs as tests, imported by none

CLI surface

cmd/assent is the only binary. Its dispatch table today is run, doctor, lint, test, compare, catalogue, render, eval-input, version, help. The authoritative listing lives in the CLI reference, which embeds the verbatim assent help output and is drift-tested against the dispatch table (cmd/assent/main_clidoc_test.go).

Contracts (public, versioned)

Contract Consumers
PolicyInput / evaluation-input schema (incl. predicate scope) policy authors, assent test harness
Decision / Findings / Pins schema forge adapters, audit tooling, assent compare, test harness
Provider request/response (content-keyed FactQuery) plugin authors — HTTP and exec today; gRPC/WASM are planned tiers (ADR-0004)
Forge port semantics (SHA-guarded writes, thread lifecycle) adapter implementers; defined by internal/forge/conformance
Test fixture format (ADR-0014) adopters

The JSON-schema families behind these contracts are embedded in the root schemas package and are the strict-decode authority for the loaders above.

Package map

The 22 packages under internal/ plus cmd/assent, exactly as the diagram names them. Regenerate with go list ./internal/... ./cmd/...; per-package roles are tabulated in internal/README.md.

cmd/assent                      CLI dispatch: run · doctor · lint · test · compare ·
                                catalogue · render · eval-input · version · help
internal/change                 value tree, structural differ (JSON/YAML/HCL), ChangeSet
internal/glob                   path glob matching for policy selectors
internal/core                   purity guard (test-only package — no production code)
internal/core/aggregate         CEL assert trees, obligations, coverage, tri-state
internal/core/classify          change-set classification helpers
internal/core/decision          decision/findings/pins model + report emission
internal/core/hash              canonical JSON hashing (ADR-0017 digest vectors)
internal/core/policy            policy envelope types (profiles, bindings, prove blocks)
internal/catalogue              catalogue load/combine, profile to pack activation
internal/compare                comparison-suite loader, classifiers, gates, records
internal/evaldecode             strict decode of evaluation inputs from JSON/YAML
internal/lint                   policy and presentation lint (hard-error fixtures)
internal/schemadrift            presentation/config drift detection
internal/render                 Markdown renderer, redaction, summary, finding lifecycle
internal/render/locale          locale string catalog
internal/forge                  Forge port (Snapshot/Resolve/Reconcile) + shared helpers
internal/forge/gitlab           GitLab HTTP adapter
internal/forge/fake             in-memory forge fake for tests
internal/forge/conformance      SHA-guarded reconciliation conformance suite
internal/provider               provider host (HTTP/exec transport, sensitive handling)
internal/provider/builtin       gitlab-groups (forge-groups), repo-file, resource-owner
internal/adoptertest            adopter-facing policy test helpers (ADR-0014 fixtures)

There is no internal/format, internal/policy, or internal/harness package: format adapters live in internal/change, the policy envelope in internal/core/policy + internal/catalogue, and the adopter harness in internal/adoptertest.