MCP Hub
Back to servers

deterministic

Validation Failed

Deterministic IR/IR-LINT validation, policy packs, drift vs exports (archrad). Apache-2.0.

Registry
Stars
1
Forks
1
Updated
Apr 29, 2026
Validated
May 1, 2026

Validation Error:

Process exited with code 1. stderr: npm error could not determine executable to run npm error A complete log of this run can be found in: /home/runner/.npm/_logs/2026-05-01T03_04_14_495Z-debug-0.log

Quick Install

npx -y @archrad/deterministic

@archrad/deterministic

archrad validate — IR-LINT-DIRECT-DB-ACCESS-002 first, fix on the graph, clean gate

Apache-2.0 no LLM no account

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.

CommandWhat it checksCodes
archrad validateGraph structure + architecture lintIR-STRUCT-* IR-LINT-*
archrad lintArchitecture 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-driftIR vs generated code on diskDRIFT-*
archrad ingest openapiDerive IR from OpenAPI (local path or https URL for --spec; -H for URL auth headers)
archrad ingest backstageBackstage catalog-info.yaml → IR (Component, Resource, API, System; Location file targets)
archrad fragment mergeMerge 2+ IR files — union by node.id (conflicts → stderr); --prefix-fragments for disjoint union
archrad exportCompile 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)

  1. JSON Schema validation — IR document shape vs schemas/archrad-ir-graph-v1.schema.json (editor/CI; optional at runtime).
  2. IR structural validationvalidateIrStructural: arrays, ids, HTTP config, edge refs, cycles (IR-STRUCT-*). Uses an internal normalized graph (see docs/IR_CONTRACT.md).
  3. 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 isCodes
IR structural validationGraph well-formedness: ids, edges, cycles, HTTP path/methodIR-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 engineSOC2, org rules, entitlement
Architecture intelligenceDeeper NFR / cost / security reasoning
AI remediationRepair loops, suggested edits
  1. IR structural validation: duplicate/missing node ids, bad HTTP config.url / config.method, unknown edge endpoints, directed cycles.
  2. Architecture lint: Implemented as a registry of visitor functions on a parsed graph (buildParsedLintGraphLINT_RULE_REGISTRY in src/lint-rules.ts). If the IR cannot be parsed, buildParsedLintGraph returns { findings } (IR-STRUCT-) instead of null; use isParsedLintGraph() or call validateIrLint, which forwards those findings. Each rule returns IrStructuralFinding[]; runArchitectureLinting / validateIrLint flatten them. Custom org rules: compose runArchitectureLinting with your own (g) => findings in CI (worked example: docs/CUSTOM_RULES.md), or fork and append to LINT_RULE_REGISTRY if the stock archrad validate CLI must emit your codes. CLI archrad validate / archrad export print 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 via edge.metadata.protocol / config.async (see edgeRepresentsAsyncBoundary in lint-graph.ts and docs/ENGINEERING_NOTES.md).
  3. Generatorsopenapi.yaml, handlers, deps.
  4. Golden pathmake run / docker compose up --build.
  5. 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 (normalizeIrGraphmaterializeNormalizedGraph).

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

ModeBest forExample
CLIQuick local scaffolding, CI, “no Node project” usagearchrad export --ir graph.json --target python --out ./out
YAML → IRAuthor graphs in YAML, emit JSON for validate/exportarchrad yaml-to-ir -y graph.yaml -o graph.json
OpenAPI → IRDerive HTTP nodes from OpenAPI 3.x (same IR shape as YAML path); ArchRad Cloud merge uses the same libraryarchrad ingest openapi --spec openapi.yaml -o graph.json
CLI validateCI / pre-commit: IR structural + architecture lint, no codegenarchrad validate --ir graph.json
CLI validate-driftAfter export or merges: on-disk tree vs fresh deterministic export from same IRarchrad validate-drift -i graph.json -t python -o ./out
Library (@archrad/deterministic)IDPs / pipelinesrunDeterministicExport → files + findings; runValidateDrift / runDriftCheckAgainstFiles for drift
MCP (archrad-mcp)Cursor / Claude Desktop / other MCP hostsstdio 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).
  • --targetpython | 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 env ARCHRAD_HOST_PORT.
  • --skip-host-port-check — don’t probe 127.0.0.1 before export.
  • --strict-host-portexit with error if the host port appears in use (CI-friendly).
  • --danger-skip-ir-structural-validationUNSAFE: skip validateIrStructural before export (never in CI). Parse/normalize failures (invalid root, empty graph) are still detected via validateIrLint and block export with IR-STRUCT-* in irStructuralFindings. A hidden --skip-ir-structural-validation remains as a deprecated alias.
  • --skip-ir-lint — skip validateIrLint during export.
  • --fail-on-warning / --max-warnings <n> — if set, no files are written when IR structural + lint findings violate the policy (same semantics as validate).

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/MakefilePolicy engine, deeper architecture intelligence, AI remediation, richer drift / sync UX in the builder
archrad CLI forever, no account required for this packageAuth, orgs, quotas, billing
No proprietary LLM orchestration or “repair” loopsLLM generation, repair, multi-model routing
No Git sync, no enterprise policy injection in this repoGit 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.

Reviews

No reviews yet

Sign in to write a review