io.github.archradhq/deterministic
Deterministic IR/IR-LINT validation, policy packs, drift vs exports (archrad). Apache-2.0.
Ask AI about io.github.archradhq/deterministic
Powered by Claude Β· Grounded in docs
I know everything about io.github.archradhq/deterministic. Ask me about installation, configuration, usage, or troubleshooting.
0/500
Reviews
Documentation
@archrad/deterministic

Your architecture drifts before you write a single line of code. archrad validate catches it β deterministically, in CI, before the PR merges.
Define your system as a graph. ArchRAD compiles it, lints it against architecture rules, and tells you exactly what's wrong β with rule codes, not opinions.
Quick start (60 seconds)
npm install -g @archrad/deterministic
archrad validate --ir fixtures/demo-direct-db-violation.json
You should see something like (exact wording may vary slightly by version):
β οΈ IR-LINT-DIRECT-DB-ACCESS-002: API node "orders-api" connects directly to datastore node "orders-db"
Fix: Introduce a service or domain layer between HTTP handlers and persistence.
For a smaller graph (single endpoint, no DB edge), try fixtures/minimal-graph.json β you will get different warnings (e.g. health/auth heuristics), not IR-LINT-DIRECT-DB-ACCESS-002.
No IR file yet? Cold-start from an existing OpenAPI spec:
archrad ingest openapi --spec ./openapi.yaml --out ./graph.json
archrad validate --ir ./graph.json
What it does
ArchRAD is a blueprint compiler and governance layer. You define your architecture as an IR β nodes, edges, allowed connections β and ArchRAD validates it against a deterministic rule engine. The same IR, the same rules, the same inputs always produce the same findings.
| Command | What it checks | Codes |
|---|---|---|
archrad validate | Graph structure + architecture lint | IR-STRUCT-* IR-LINT-* |
archrad lint | Architecture lint only (fast inner-loop; skips structural) | IR-LINT-* |
archrad explain <code> | Canonical rule guidance without running a pass | β |
archrad policies-sha256 --dir <policies> | Generate a archrad-policy-pack.sha256 manifest for signed PolicyPacks | β |
archrad validate-drift | IR vs generated code on disk | DRIFT-* |
archrad ingest openapi | Derive IR from OpenAPI (local path or https URL for --spec; -H for URL auth headers) | β |
archrad ingest backstage | Backstage catalog-info.yaml β IR (Component, Resource, API, System; Location file targets) | β |
archrad fragment merge | Merge 2+ IR files β union by node.id (conflicts β stderr); --prefix-fragments for disjoint union | β |
archrad export | Compile IR β FastAPI or Express + Docker | β |
Ingest + merge workflows: docs/INGEST.md. All commands / flags: docs/CLI_REFERENCE.md. Codegen (export): docs/EXPORT.md.
Project config (archrad.yml)
Drop an archrad.yml at the root of your repo and skip re-typing flags:
# archrad.yml
ir: ./archrad-graph.json
target: python
output: ./generated
failOn: warning
policies: ./policies
archrad validate # uses ir, failOn, policies
archrad export # uses ir, target, output
archrad validate-drift # uses ir, target, output
archrad walks upward from the CWD looking for archrad.yml (or
archrad.yaml). Explicit CLI flags always override the config. Use
--no-config to ignore any discovered file, or --config <path> to
point at a non-standard location. Full schema: docs/CONFIG.md.
Fast inner loop: archrad lint + archrad explain
# Iterate on lint without re-checking IR structure every run:
archrad lint --ir ./graph.json
# Focus on a single rule while fixing it:
archrad lint --ir ./graph.json --rule IR-LINT-MISSING-AUTH-010
# Understand a rule code without running a pass:
archrad explain IR-LINT-DIRECT-DB-ACCESS-002
archrad explain --list # every known rule code
archrad lint is the fast inner loop; use archrad validate once before the CI gate to also enforce IR structural shape. With archrad.yml at repo root, both run with no flags.
CI integration
# Fail on any structural error (default):
archrad validate --ir ./graph.json
# Also fail on lint warnings:
archrad validate --ir ./graph.json --fail-on-warning
# Machine-readable output for GitHub Actions:
archrad validate --ir ./graph.json --json
MCP server (Cursor / Claude Desktop)
After install, archrad-mcp is on your PATH. Add it to your IDE:
{
"mcpServers": {
"archrad": { "command": "archrad-mcp" }
}
}
Your agent can call the same engine as the CLI via six MCP tools (e.g. archrad_validate_ir, archrad_lint_summary, archrad_validate_drift, archrad_policy_packs_load, archrad_list_rule_codes, archrad_suggest_fix). See docs/MCP.md for parameters and local testing.
How it works (architecture)
IR (nodes/edges) β validateIrStructural (IR-STRUCT-*) β errors block export
β
validateIrLint (IR-LINT-*) β warnings (CI: --fail-on-warning / --max-warnings)
β
pythonFastAPI | nodeExpress generators
β
openapi.yaml + app code + package metadata
β
golden layer (Dockerfile, docker-compose.yml, Makefile, README; hostβcontainer e.g. 8080:8080)
β
validateOpenApiInBundleStructural(openapi.yaml) β document-shape warnings (not full API lint)
β
{ files, openApiStructuralWarnings, irStructuralFindings, irLintFindings }
Optional CI: archrad validate-drift β re-export IR in-memory, diff vs existing ./out β DRIFT-MISSING / DRIFT-MODIFIED (thin deterministic gate)
Validation levels (quick contract)
- JSON Schema validation β IR document shape vs
schemas/archrad-ir-graph-v1.schema.json(editor/CI; optional at runtime). - IR structural validation β
validateIrStructural: arrays, ids, HTTPconfig, edge refs, cycles (IR-STRUCT-*). Uses an internal normalized graph (seedocs/IR_CONTRACT.md). - Export-time generated OpenAPI structural validation β Parse + required fields on the generated
openapi.yaml(document shape, not Spectral).
Architecture lint (IR-LINT-*) sits after structural checks: rule visitors on the parsed graph (heuristics, not schema).
Validation layers (naming)
| Layer (OSS) | What it is | Codes |
|---|---|---|
| IR structural validation | Graph well-formedness: ids, edges, cycles, HTTP path/method | IR-STRUCT-* |
| Architecture lint (basic) | Deterministic heuristics only (no AI, no org policy) | IR-LINT-* |
| OpenAPI structural validation (document shape) | Parse + required top-level OpenAPI fields on generated spec | (string warnings, not IR codes) |
| Layer (Cloud β not this package) | Examples |
|---|---|
| Policy engine | SOC2, org rules, entitlement |
| Architecture intelligence | Deeper NFR / cost / security reasoning |
| AI remediation | Repair loops, suggested edits |
- IR structural validation: duplicate/missing node ids, bad HTTP
config.url/config.method, unknown edge endpoints, directed cycles. - Architecture lint: Implemented as a registry of visitor functions on a parsed graph (
buildParsedLintGraphβLINT_RULE_REGISTRYinsrc/lint-rules.ts). If the IR cannot be parsed,buildParsedLintGraphreturns{ findings }(IR-STRUCT-) instead ofnull; useisParsedLintGraph()or callvalidateIrLint, which forwards those findings. Each rule returnsIrStructuralFinding[];runArchitectureLinting/validateIrLintflatten them. Custom org rules: composerunArchitectureLintingwith your own(g) => findingsin CI (worked example:docs/CUSTOM_RULES.md), or fork and append toLINT_RULE_REGISTRYif the stockarchrad validateCLI must emit your codes. CLIarchrad validate/archrad exportprint lint under **Architecture lint (IR-LINT-)** (grouped separately from structural). Codes include IR-LINT-DIRECT-DB-ACCESS-002, IR-LINT-SYNC-CHAIN-001, IR-LINT-NO-HEALTHCHECK-003, IR-LINT-HIGH-FANOUT-004, IR-LINT-ISOLATED-NODE-005, IR-LINT-DUPLICATE-EDGE-006, IR-LINT-HTTP-MISSING-NAME-007, IR-LINT-DATASTORE-NO-INCOMING-008, IR-LINT-MULTIPLE-HTTP-ENTRIES-009, IR-LINT-MISSING-AUTH-010, IR-LINT-DEAD-NODE-011. Sync-chain depth counts synchronous edges only; mark message/queue/async hops viaedge.metadata.protocol/config.async(seeedgeRepresentsAsyncBoundaryinlint-graph.tsanddocs/ENGINEERING_NOTES.md). - Generators β
openapi.yaml, handlers, deps. - Golden path β
make run/docker compose up --build. - OpenAPI document shape on the bundle β not Spectral-level lint. Issues β
openApiStructuralWarnings.
IR contract: schemas/archrad-ir-graph-v1.schema.json. Parser boundary + normalized shapes: docs/IR_CONTRACT.md (normalizeIrGraph β materializeNormalizedGraph).
Trust builder: IR-STRUCT-* errors block export; IR-LINT-* warnings are visible and can gate CI via --fail-on-warning / --max-warnings; OpenAPI shape issues surface as export warnings.
Reference (OSS): docs/DRIFT.md (deterministic validate-drift), docs/RULE_CODES.md (finding codes; MCP docsUrl targets GitHub anchors), docs/MCP.md (MCP tools + local testing).
Codegen vs validation (retry, timeouts, policy)
Generators may emit retry/timeout/circuit-breaker code when the IR carries matching edge or node config (e.g. retryPolicy). That is code generation, not a guarantee. OSS does not currently require or lint βevery external call must have timeout/retryβ β that class of rule is semantic / policy and fits ArchRad Cloud or custom linters on top of the IR.
Ways to use it
| Mode | Best for | Example |
|---|---|---|
| CLI | Quick local scaffolding, CI, βno Node projectβ usage | archrad export --ir graph.json --target python --out ./out |
| YAML β IR | Author graphs in YAML, emit JSON for validate/export | archrad yaml-to-ir -y graph.yaml -o graph.json |
| OpenAPI β IR | Derive HTTP nodes from OpenAPI 3.x (same IR shape as YAML path); ArchRad Cloud merge uses the same library | archrad ingest openapi --spec openapi.yaml -o graph.json |
| CLI validate | CI / pre-commit: IR structural + architecture lint, no codegen | archrad validate --ir graph.json |
| CLI validate-drift | After export or merges: on-disk tree vs fresh deterministic export from same IR | archrad validate-drift -i graph.json -t python -o ./out |
Library (@archrad/deterministic) | IDPs / pipelines | runDeterministicExport β files + findings; runValidateDrift / runDriftCheckAgainstFiles for drift |
MCP (archrad-mcp) | Cursor / Claude Desktop / other MCP hosts | stdio server: validate IR, lint summary, drift, policy packs, static archrad_suggest_fix β see docs/MCP.md |
MCP (Cursor example): after npm i -g @archrad/deterministic (or npx), add a server with command archrad-mcp and no args (stdio). Pass ir inline or irPath to a JSON file for large graphs. archrad_suggest_fix returns curated text for a finding code (e.g. IR-LINT-MISSING-AUTH-010) β not machine-generated IR patches. Step-by-step testing (smoke script, MCP Inspector, Cursor chat prompts): docs/MCP.md, section Local testing.
CLI
Input is structured IR (JSON), not natural language. There is no archrad export --prompt "...". Pass a graph file (nodes/edges).
Fixtures (in this repo): fixtures/minimal-graph.json (small); fixtures/demo-direct-db-violation.json / fixtures/demo-direct-db-layered.json (before/after IR-LINT-DIRECT-DB-ACCESS-002); fixtures/ecommerce-with-warnings.json (many lint rules); fixtures/payment-retry-demo.json (retry-related codegen in export). --target python is the FastAPI bundle; there is no separate fastapi target. To go from plain English β IR, use ArchRad Cloud or your own LLM step; this package only does IR β files.
Recording demos and GIFs (VHS, storyboards, drift replay): scripts/README_DEMO_RECORDING.md only β not required to use the CLI.
OpenAPI β JSON (spec as source of truth): each operation under paths becomes an http node (config.url + config.method). Then validate and export like any other IR:
archrad ingest openapi --spec ./openapi.yaml --out ./graph.json
archrad validate --ir ./graph.json
archrad export --ir ./graph.json --target python --out ./out
OpenAPI security β IR β lint: ingestion copies global and per-operation security requirement names onto each HTTP node as config.security (sorted, deterministic). An operation with explicit security: [] becomes config.authRequired: false (intentionally public). If the spec declares no security at any level, nodes are left without those fields β then archrad validate can surface IR-LINT-MISSING-AUTH-010 on HTTP-like entry nodes (compliance gap from the spec artifact alone).
YAML β JSON (lighter authoring): edit fixtures/minimal-graph.yaml (or your own file) and compile to IR JSON, then validate or export:
archrad yaml-to-ir --yaml fixtures/minimal-graph.yaml --out ./graph.json
archrad validate --ir ./graph.json
# or pipe: archrad yaml-to-ir -y fixtures/minimal-graph.yaml | archrad validate --ir /dev/stdin # on Unix; on Windows use --out then validate
YAML must have either top-level graph: (object) or top-level nodes: (array); bare graphs are wrapped as { "graph": { ... } } automatically.
After npm install -g or npx (typical):
archrad export --ir ./graph.json --target node --out ./my-express-api
# Validate IR (structural + architecture lint). Pretty output; exit 1 on structural errors by default:
archrad validate --ir ./graph.json
# Machine-readable + CI gates:
archrad validate --ir ./graph.json --json
archrad validate --ir ./graph.json --fail-on-warning
archrad validate --ir ./graph.json --max-warnings 0
# Structural only (skip IR-LINT-*):
archrad validate --ir ./graph.json --skip-lint
# Declarative PolicyPack YAML/JSON in a directory (after IR-LINT-*; skipped with --skip-lint):
archrad validate --ir ./graph.json --policies ./policy-packs
From a git clone (contributors): run npm ci && npm run build in the package root (there is no prepare hook β see docs/ENGINEERING_NOTES.md), then use node dist/cli.js the same way you would use archrad (e.g. node dist/cli.js validate --ir fixtures/minimal-graph.json).
Deterministic drift (thin, OSS): compare an existing export tree on disk to a fresh export from the same IR. Detects missing / changed generated files (line endings normalized). Optional --strict-extra flags files present on disk but not in the reference export. Not semantic βdoes code match intentβ β ArchRad Cloud adds builder/UI drift checks and broader governance.
archrad export -i ./graph.json -t python -o ./out
# β¦edit files under ./outβ¦
archrad validate-drift -i ./graph.json -t python -o ./out --skip-host-port-check
# CI-friendly:
archrad validate-drift -i ./graph.json -t python -o ./out --skip-host-port-check --json
# Fail if the tree has extra files not in the reference export:
archrad validate-drift -i ./graph.json -t python -o ./out --strict-extra
Example: validate architecture
archrad validate --ir fixtures/minimal-graph.json
Example output (stderr):
archrad validate:
β οΈ IR-LINT-NO-HEALTHCHECK-003: No HTTP node exposes a typical health/readiness path (...)
Fix: Add a GET route such as /health for orchestrators and load balancers.
Suggestion: Expose liveness vs readiness separately if your platform distinguishes them.
Impact: Weaker deploy/rollback safety and harder operations automation.
Structural errors look like β IR-STRUCT-... with Fix: lines. Use --json to consume findings in GitHub Actions or other CI.
--irβ JSON:{ "graph": { "nodes", "edges", "metadata" } }or a raw graph (CLI wraps it).--targetβpython|node|nodejs--outβ output directory (created if needed)--host-port <n>β host port Docker publishes (default 8080; container still listens on 8080 inside). Same as envARCHRAD_HOST_PORT.--skip-host-port-checkβ donβt probe127.0.0.1before export.--strict-host-portβ exit with error if the host port appears in use (CI-friendly).--danger-skip-ir-structural-validationβ UNSAFE: skipvalidateIrStructuralbefore export (never in CI). Parse/normalize failures (invalid root, empty graph) are still detected viavalidateIrLintand block export withIR-STRUCT-*inirStructuralFindings. A hidden--skip-ir-structural-validationremains as a deprecated alias.--skip-ir-lintβ skipvalidateIrLintduring export.--fail-on-warning/--max-warnings <n>β if set, no files are written when IR structural + lint findings violate the policy (same semantics asvalidate).
By default, if 8080 (or your --host-port) looks busy on localhost, the CLI warns so you can change the port before docker compose fails with a bind error.
Export runs IR structural validation, then architecture lint, then codegen. Structural errors abort with no files written. irLintFindings contains only IR-LINT-*; IR-STRUCT-* from a failed parse always appear under irStructuralFindings (including when structural validation was skipped). Lint warnings print by default; use --fail-on-warning / --max-warnings to block writes for CI.
Library
import {
runDeterministicExport,
runValidateDrift,
validateIrStructural,
validateIrLint,
sortFindings,
shouldFailFromFindings,
} from '@archrad/deterministic';
const { files, openApiStructuralWarnings, irStructuralFindings, irLintFindings } =
await runDeterministicExport(ir, 'python', {
hostPort: 8080,
skipIrLint: false, // default
});
// Structural errors β empty files (unless skipIrStructuralValidation). Lint is non-blocking for export unless you check policy in your pipeline.
const drift = await runValidateDrift(ir, 'python', '/path/to/existing-export', {
skipIrLint: false, // set true to match CLI --skip-ir-lint on reference export
});
// drift.ok, drift.driftFindings, drift.exportResult β same core semantics as CLI validate-drift (CLI also probes host port before calling the library)
const all = sortFindings([...validateIrStructural(ir), ...validateIrLint(ir)]);
if (shouldFailFromFindings(all, { failOnWarning: true })) {
/* gate your CI */
}
Optional: isLocalHostPortFree / normalizeGoldenHostPort from the same package if you want your own preflight.
Golden path (contributors β local clone)
This path assumes you cloned the repo and ran npm ci && npm run build in the package root. If you only installed with npm install -g @archrad/deterministic, use archrad instead of node dist/cli.js (same flags).
node dist/cli.js export --ir fixtures/minimal-graph.json --target python --out ./out
cd ./out
make run
# In another terminal, once the API is up:
curl -sS -X POST http://localhost:8080/signup -H "Content-Type: application/json" -d '{}'
You should see 422 Unprocessable Entity (FastAPI/Pydantic) or 400 with a clear body β proof the stack is live and validation matches the spec, not a silent 500.
Quick check from a clone: cd packages/deterministic && npm ci && npm run build && npm test, then export to ./tmp-out, cd tmp-out && make run, curl as above. Use --host-port 18080 (or node dist/cli.js export ... --host-port 18080) if 8080 is busy.
Optional: bash scripts/golden-path-demo.sh runs the same flow. Demo recording (GIFs, tapes, drift replays): scripts/README_DEMO_RECORDING.md.
Open source vs ArchRad Cloud
This repository is only the deterministic engine β local, offline, no phone-home.
| Here (OSS) | ArchRad Cloud (commercial product) |
|---|---|
IR structural + architecture lint (validate, IR-STRUCT-*, IR-LINT-*), compiler (export), validate-drift (on-disk vs fresh export), OpenAPI document-shape warnings, golden Docker/Makefile | Policy engine, deeper architecture intelligence, AI remediation, richer drift / sync UX in the builder |
archrad CLI forever, no account required for this package | Auth, orgs, quotas, billing |
| No proprietary LLM orchestration or βrepairβ loops | LLM generation, repair, multi-model routing |
| No Git sync, no enterprise policy injection in this repo | Git push, governance, compliance dashboards |
You can depend on this CLI and library without ArchRad Cloud. The cloud product stacks collaboration and AI on top of the same deterministic contract.
Contributing
See CONTRIBUTING.md.
License
Apache-2.0 β see LICENSE.
