No description
  • Rust 97%
  • Nix 2%
  • Just 1%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-08-12 15:27:52 +02:00
.claude/skills Restrict commit messages to a one-line subject 2026-08-10 22:07:36 +02:00
docs Close the stacked subtree-target issue 2026-08-12 15:27:52 +02:00
examples Flip hexagonal to forbid the outward edges — invariants, not permissions 2026-08-12 10:24:33 +02:00
src Target subtrees in the stacked generator, not layer roots 2026-08-12 15:26:43 +02:00
tests Collapse the workspace into a single terminvs crate 2026-08-11 23:55:42 +02:00
.gitignore Add initial files 2026-08-10 10:26:43 +02:00
architecture.rhai Switch the domain to an allow-list; document the spec posture 2026-08-12 11:05:28 +02:00
Cargo.lock Collapse the workspace into a single terminvs crate 2026-08-11 23:55:42 +02:00
Cargo.toml Collapse the workspace into a single terminvs crate 2026-08-11 23:55:42 +02:00
cartulary.toml Add the release-procedure SR and the sr record kind 2026-08-11 23:27:03 +02:00
CLAUDE.md Add the writing rules: sharpen every text, write in the present 2026-08-12 09:36:05 +02:00
flake.lock Add initial files 2026-08-10 10:26:43 +02:00
flake.nix Add initial files 2026-08-10 10:26:43 +02:00
justfile Add first sources 2026-08-10 14:22:32 +02:00
README.md Sync the README hexagonal line to the forbid framing 2026-08-12 10:25:12 +02:00
rust-toolchain.toml Add initial files 2026-08-10 10:26:43 +02:00

terminvs

Architecture dependency-rule checker. Declare which parts of a codebase may depend on which — and fail the build when an import crosses a line it shouldn't.

Named after Terminus, the Roman god of boundary stones: the markers that fixed the limits of a field were sacred, and moving one was a crime. terminvs fixes the boundaries of an architecture the same way.

Status

Early extraction of the internal check-arch engine from cartulary. Rust-first: the rule engine is language-agnostic by design — imports come from a pluggable extractor — but only the Rust extractor exists today.

What makes it different

  • Default-deny. Nothing may depend on anything until a rule allows it. "No bounded context imports another" needs no rule; it is the baseline.
  • Captures & a generator library. One parametric rule with captures ({X..}::usecases may import {X..}::model) applies across every context; or call a generator (hexagonal, stacked, isolated, sealed) that lowers to such rules.
  • Scoped exceptions with a reason. Accepted drift is explicit, localized, and carries the why.

Design

One crate, hexagonal — terminvs enforces the split on itself, at the module level:

  • domain — the rule engine: spec, resolution, verdict. No I/O, no language knowledge; it operates on nodes and edges.
  • infra/driven — adapters behind the engine: the Rust extractor (syn) that turns source into edges, the Rhai spec front-end, the report. The seam takes another language's extractor without the engine learning it changed.
  • infra/driving — the CLI: load the spec, run an extractor, feed the core, report.

Spec library

The spec is a Rhai script that generates the low-level rules, so common shapes ship as generators (named arguments, #{ … }):

  • any() — every reference, internal and external.
  • hexagonal(#{ model, usecases, driven, driving }) — bans the outward edges (model ↛ usecases/adapters; usecases ↛ adapters; driven ↛ driving); you add the inward allows.
  • stacked(#{ scopes, base }) — an ordered layer stack.
  • isolated(#{ scopes }) — mutually-exclusive peers.
  • sealed(#{ scope, from }) — an encapsulated module: face public, internals hidden.

They carry no project names — pure patterns, portable across projects. Most named architectures are one of these shapes:

Architecture Shape
Clean / Onion / N-tier stacked (rings inner→outer)
Vertical slices / feature modules isolated
Plugin / microkernel sealed core + isolated plugins
Ports & adapters hexagonal

Worked example: examples/hexagonal.rhai.

Dogfooding

terminvs and cartulary eat each other: terminvs uses cartu to manage its own decision records and issues under docs/, and cartulary uses terminvs to check its own architecture.

Development

Nix + direnv. direnv allow drops you into a shell with the pinned toolchain; everything runs through just.

License

CeCILL.