Dependency Matrix & Layer Rules

August 11, 2026 · View on GitHub

Status: Ratified (Sprint 006.7) Version: 1.0.0 Owner: HunterX Architecture Council

This document is the human-readable explanation of the machine-readable matrix in config/architecture.yaml. That file is the single source of truth; where this document conflicts with it, the YAML wins (the linter reads only the YAML).

Direction rule

Dependencies point inward. A module may only import a module from the same layer or a more foundational layer. There is exactly one way to read the matrix: rules[source_layer] lists every target layer source_layer is allowed to reach. Any edge not listed is a violation (ARCH-001) unless it matches a conditional_imports rule or a waivers entry.

Foundational layers are foundational in the sense of the dependency arrow only — for example shared and domain are the purest layers, while platform is the composition root that may reach everything.

Layer catalogue

LayerPrefixResponsibilityOwner
sharedhunterx.sharedCross-cutting helpers, importable by allArchitecture Council
domainhunterx.domainPure entities, value objects, ports, services, events, exceptionsArchitecture Council
confighunterx.configConfiguration loading and typed settingsPlatform Team
securityhunterx.securityCross-cutting security servicesSecurity Team
infrastructurehunterx.infrastructureAdapters implementing domain portsPlatform Team
applicationhunterx.applicationUse-case services and DTOsApplication Team
knowledgehunterx.knowledgeKnowledge base runtime, knowledge graph clientIntelligence Team
reportinghunterx.reportingReport views, renderers, evidence packagingReporting Team
schedulerhunterx.schedulerMission scheduling and job definitionsPlatform Team
engineshunterx.enginesEngine facades (mission, workflow, planner, ...)Engine Team
toolshunterx.toolsTool runtime, Tool Integration Factory, Tool Intelligence, SDKTooling Team
pluginshunterx.pluginsPlugin host, registry, loader, public plugin SDKPlugin Team
agentshunterx.agentsMulti-agent platformAI Team
apihunterx.apiREST API frameworkAPI Team
clihunterx.cliCLI framework and command registryCLI Team
platformhunterx.platformComposition root, wires every subsystemArchitecture Council
architecturehunterx.architectureThis enforcement framework (leaf)Architecture Council
facadepackage-root facade modulesConvenience re-export modulesArchitecture Council
roothunterxThe package itself (side-effect-free)Architecture Council

The matrix

shared:        shared
domain:        domain, shared
config:        config, domain, shared
security:      security, domain, shared, infrastructure
infrastructure:infrastructure, domain, shared, config
application:   application, domain, shared, engines, tools
knowledge:     knowledge, domain, shared
reporting:     reporting, domain, shared
scheduler:     scheduler, domain, shared
engines:       engines, domain, shared, tools, reporting, infrastructure, application
tools:         tools, domain, shared, plugins
plugins:       plugins, domain, shared
agents:        agents, domain, shared
api:           api, domain, shared, config, platform, application, engines
cli:           cli, domain, shared, config, platform, application, engines
platform:      platform, domain, shared, config, security, infrastructure,
               application, knowledge, reporting, scheduler, engines, tools,
               plugins, agents, api, cli, facade
facade:        facade, domain, shared, infrastructure, managers, config
architecture:  architecture

Notes:

  • domain is pure: it may only reach shared. Anything that needs domain types must depend on them, never the other way around.
  • engines orchestrate: they may reach tools, reporting, infrastructure and application but not the delivery layers (api, cli) and not the composition root.
  • api / cli may reach platform so delivery commands can ask the composition root to build real objects; they must not re-wire subsystems themselves.
  • tools may reach plugins (the Tool SDK builds on plugin SDK types).
  • The legacy resolution: any module outside src/hunterx that does not match a declared prefix (e.g. core.*) resolves to the legacy layer, which no shipping v7 module may import (ARCH-002, blocked_prefixes).

Shared contracts

Contracts deliberately importable across layer boundaries:

  • hunterx.domain.ports — abstract ports implemented by infrastructure adapters.
  • hunterx.domain.exceptions — the exception hierarchy and error codes.
  • hunterx.shared — ids, masking, time, result, DI helpers.

Conditional imports

Fine-grained module-level exceptions to the matrix (ARCH-001 does not fire):

  • hunterx.shared.di → hunterx.domain.exceptions — the DI container raises domain exceptions for registration errors.

Waivers (known issues, time-boxed)

Waivers never fail CI while active but appear in every report; expired waivers fail CI (ARCH-011).

IDCodeModule → TargetReason
ARCH-W-001ARCH-001hunterx.domain.executionhunterx.plugins.sdk.resultsDomain references SDK result types; contracts should move into domain (tracked debt).

Known cycles (benign wiring, time-boxed)

Two existing cycles are recorded and excluded. Any new cycle fails CI (ARCH-003).

  • ARCH-W-002hunterx.tools.sdk / sdk.engine / sdk.pipeline: package __init__ re-export wiring, harmless at runtime.
  • ARCH-W-003hunterx.cli / cli.app / cli.commands: wiring cycle, resolve by moving main() into a leaf module.

Changing the matrix

The matrix is Architecture Council territory. To propose a change:

  1. Edit config/architecture.yaml (rules / conditional_imports / waivers).
  2. Update this document so the two stay consistent.
  3. Record the reason, and for waivers a deadline, in the YAML.
  4. Run hunterx-arch lint and hunterx-arch report to confirm the intended effect.