@archrad/deterministic 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/CONTRIBUTING.md +15 -0
  3. package/LICENSE +17 -0
  4. package/README.md +284 -0
  5. package/SECURITY.md +26 -0
  6. package/biome.json +25 -0
  7. package/demo-validate.gif +0 -0
  8. package/dist/cli-findings.d.ts +23 -0
  9. package/dist/cli-findings.d.ts.map +1 -0
  10. package/dist/cli-findings.js +88 -0
  11. package/dist/cli.d.ts +7 -0
  12. package/dist/cli.d.ts.map +1 -0
  13. package/dist/cli.js +341 -0
  14. package/dist/edgeConfigCodeGenerator.d.ts +55 -0
  15. package/dist/edgeConfigCodeGenerator.d.ts.map +1 -0
  16. package/dist/edgeConfigCodeGenerator.js +249 -0
  17. package/dist/exportPipeline.d.ts +23 -0
  18. package/dist/exportPipeline.d.ts.map +1 -0
  19. package/dist/exportPipeline.js +65 -0
  20. package/dist/golden-bundle.d.ts +21 -0
  21. package/dist/golden-bundle.d.ts.map +1 -0
  22. package/dist/golden-bundle.js +166 -0
  23. package/dist/graphPredicates.d.ts +10 -0
  24. package/dist/graphPredicates.d.ts.map +1 -0
  25. package/dist/graphPredicates.js +33 -0
  26. package/dist/hostPort.d.ts +12 -0
  27. package/dist/hostPort.d.ts.map +1 -0
  28. package/dist/hostPort.js +39 -0
  29. package/dist/index.d.ts +22 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +21 -0
  32. package/dist/ir-lint.d.ts +11 -0
  33. package/dist/ir-lint.d.ts.map +1 -0
  34. package/dist/ir-lint.js +16 -0
  35. package/dist/ir-normalize.d.ts +48 -0
  36. package/dist/ir-normalize.d.ts.map +1 -0
  37. package/dist/ir-normalize.js +81 -0
  38. package/dist/ir-structural.d.ts +40 -0
  39. package/dist/ir-structural.d.ts.map +1 -0
  40. package/dist/ir-structural.js +267 -0
  41. package/dist/lint-graph.d.ts +40 -0
  42. package/dist/lint-graph.d.ts.map +1 -0
  43. package/dist/lint-graph.js +133 -0
  44. package/dist/lint-rules.d.ts +40 -0
  45. package/dist/lint-rules.d.ts.map +1 -0
  46. package/dist/lint-rules.js +290 -0
  47. package/dist/nodeExpress.d.ts +2 -0
  48. package/dist/nodeExpress.d.ts.map +1 -0
  49. package/dist/nodeExpress.js +528 -0
  50. package/dist/openapi-structural.d.ts +26 -0
  51. package/dist/openapi-structural.d.ts.map +1 -0
  52. package/dist/openapi-structural.js +82 -0
  53. package/dist/openapi-to-ir.d.ts +26 -0
  54. package/dist/openapi-to-ir.d.ts.map +1 -0
  55. package/dist/openapi-to-ir.js +131 -0
  56. package/dist/pythonFastAPI.d.ts +2 -0
  57. package/dist/pythonFastAPI.d.ts.map +1 -0
  58. package/dist/pythonFastAPI.js +664 -0
  59. package/dist/validate-drift.d.ts +54 -0
  60. package/dist/validate-drift.d.ts.map +1 -0
  61. package/dist/validate-drift.js +184 -0
  62. package/dist/yamlToIr.d.ts +14 -0
  63. package/dist/yamlToIr.d.ts.map +1 -0
  64. package/dist/yamlToIr.js +39 -0
  65. package/docs/CONCEPT_ADOPTION_AND_LIMITS.md +47 -0
  66. package/docs/CUSTOM_RULES.md +87 -0
  67. package/docs/ENGINEERING_NOTES.md +42 -0
  68. package/docs/IR_CONTRACT.md +54 -0
  69. package/docs/STRUCTURAL_VS_SEMANTIC_VALIDATION.md +86 -0
  70. package/fixtures/demo-direct-db-layered.json +37 -0
  71. package/fixtures/demo-direct-db-violation.json +22 -0
  72. package/fixtures/ecommerce-with-warnings.json +89 -0
  73. package/fixtures/invalid-cycle.json +15 -0
  74. package/fixtures/invalid-edge-unknown-node.json +14 -0
  75. package/fixtures/minimal-graph.json +14 -0
  76. package/fixtures/minimal-graph.yaml +13 -0
  77. package/fixtures/payment-retry-demo.json +43 -0
  78. package/llms.txt +99 -0
  79. package/package.json +84 -0
  80. package/schemas/archrad-ir-graph-v1.schema.json +67 -0
  81. package/scripts/DEMO_GIF_STORYBOARD.md +100 -0
  82. package/scripts/GIF_RECORDING_STEP_BY_STEP.md +125 -0
  83. package/scripts/README_DEMO_RECORDING.md +314 -0
  84. package/scripts/SOCIAL_POST_DRIFT_AND_INGESTION.md +17 -0
  85. package/scripts/golden-path-demo.ps1 +25 -0
  86. package/scripts/golden-path-demo.sh +23 -0
  87. package/scripts/invoke-drift-check.ps1 +16 -0
  88. package/scripts/record-demo-drift.tape +50 -0
  89. package/scripts/record-demo-payment-retry.tape +36 -0
  90. package/scripts/record-demo-validate.tape +34 -0
  91. package/scripts/record-demo.tape +33 -0
  92. package/scripts/run-demo-drift-sequence.ps1 +45 -0
  93. package/scripts/run-demo-drift-sequence.sh +41 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,66 @@
1
+ # Changelog
2
+
3
+ All notable changes to **`@archrad/deterministic`** are documented here.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ### Added
11
+ - **[docs/CUSTOM_RULES.md](docs/CUSTOM_RULES.md)** — custom **`IR-LINT`-style** visitors (`ParsedLintGraph` → **`IrStructuralFinding[]`**): worked **service / `config.timeout`** example, **compose** (`runArchitectureLinting` + org rules) vs **fork** (`LINT_RULE_REGISTRY`); no runtime registry mutation.
12
+ - **`archrad validate-drift`** — compare an on-disk export directory to a **fresh** deterministic export from the same IR (`DRIFT-MISSING` / `DRIFT-MODIFIED` / optional `DRIFT-EXTRA` with **`--strict-extra`**); **`--json`** for CI. Library: **`runValidateDrift`**, **`diffExpectedExportAgainstFiles`**, **`runDriftCheckAgainstFiles`**, etc. (`src/validate-drift.ts`).
13
+ - **VHS tape** **`scripts/record-demo-drift.tape`** → **`demo-drift.gif`**; **`npm run record:demo:drift`**. Storyboard and recording docs updated (**`scripts/DEMO_GIF_STORYBOARD.md`**). **Replay without VHS:** **`scripts/run-demo-drift-sequence.sh`** / **`.ps1`** for ShareX/OBS/asciinema capture; **`README_DEMO_RECORDING.md`** (**When VHS fails**).
14
+ - **`graphPredicates.ts`**: shared **`isHttpLikeType`** / **`isDbLikeType`** / **`isQueueLikeNodeType`** (exported from the package root) so structural HTTP checks and IR-LINT stay aligned.
15
+ - **`IR-STRUCT-EDGE_AMBIGUOUS_FROM` / `IR-STRUCT-EDGE_AMBIGUOUS_TO`** when an edge references a **duplicate** node id.
16
+ - **`ParsedLintGraph.inDegree`**, **`BuildParsedLintGraphResult`**, **`isParsedLintGraph()`** — `buildParsedLintGraph` returns **`{ findings }`** on parse failure instead of **`null`**.
17
+ - Richer **`IR-STRUCT-CYCLE`** message (example node on the cycle) and **`nodeId`** when detectable.
18
+ - **`archrad yaml-to-ir`** — YAML blueprint → canonical `{ "graph": … }` JSON (`-y/--yaml`, `-o/--out` or stdout). Library: **`parseYamlToCanonicalIr`**, **`canonicalIrToJsonString`**, **`YamlGraphParseError`**. Fixture **`fixtures/minimal-graph.yaml`**.
19
+ - **Five architecture lint rules** (`IR-LINT-ISOLATED-NODE-005` … **009**): isolated nodes when the graph has edges elsewhere, duplicate edges, HTTP missing `name`, datastore with no incoming edges, multiple HTTP entry nodes. CLI prints **Architecture lint (IR-LINT-*)** in a separate block from structural findings.
20
+ - Launch line: **Validate your architecture before you write code.** (README, `llms.txt`, npm `description`, `archrad --help` / `validate` description, clean `archrad validate` stdout).
21
+ - **`ir-normalize`** (`materializeNormalizedGraph`, `NormalizedGraph` / node / edge types) — parser boundary docs in **`docs/IR_CONTRACT.md`**; README **Validation levels** (JSON Schema → IR structural → export-time OpenAPI structural).
22
+ - **`llms.txt`** at package root — markdown summary for LLM/agent discovery (included in npm tarball).
23
+ - **IR structural validation** (`validateIrStructural`, `normalizeIrGraph`, `hasIrStructuralErrors`): node ids, HTTP path/method, edge endpoints, directed **cycles**; codes like `IR-STRUCT-*`.
24
+ - **Architecture lint** (`validateIrLint`, **`IR-LINT-*`**): deterministic rules only — direct **HTTP→DB** edge, **sync chain** depth from HTTP entry, **missing health** path, **high fan-out** (≥5 outbound edges). No AI, no org policy.
25
+ - **`archrad validate --ir <path>`** — structural + lint; **`--json`**; pretty output; **`--skip-lint`**, **`--fail-on-warning`**, **`--max-warnings <n>`** for CI.
26
+ - **`schemas/archrad-ir-graph-v1.schema.json`** — documented JSON shape for the graph IR (companion to code rules).
27
+ - **`runDeterministicExport`** returns **`irStructuralFindings`** and **`irLintFindings`**; **structural errors** block codegen unless **`skipIrStructuralValidation`**.
28
+ - CLI **`export`**: **`--skip-ir-structural-validation`**, **`--skip-ir-lint`**, **`--fail-on-warning`**, **`--max-warnings <n>`** (blocks writes when IR policy fails).
29
+ - Library: **`validateIrLint`**, **`sortFindings`**, **`shouldFailFromFindings`**, **`IrFindingLayer`**.
30
+ - Fixtures **`invalid-edge-unknown-node.json`**, **`invalid-cycle.json`** for negative tests; **`ecommerce-with-warnings.json`** triggers all four **`IR-LINT-*`** rules for demos and tests.
31
+
32
+ ### Changed
33
+ - **`validateIrLint`** returns **structural findings** when the IR cannot be built (same codes as **`normalizeIrGraph`** / empty graph), instead of **`[]`**.
34
+ - **`runDeterministicExport`**: when **`skipIrStructuralValidation`** is set, **`IR-STRUCT-*`** from **`validateIrLint`** are merged into **`irStructuralFindings`** (and block codegen on errors); **`irLintFindings`** stays **`IR-LINT-*`** only — aligns server logging and product “fail closed” on invalid/empty IR.
35
+ - **`normalizeEdgeSlot`**: top-level **`edge.kind`** is copied into **`metadata.kind`** when metadata does not already set **`kind`** (preserves async lint signal).
36
+ - **`edgeRepresentsAsyncBoundary`**: considers top-level **`kind`**, optional **`ParsedLintGraph`** (queue-like **target** nodes), and normalized metadata.
37
+ - **`IR-LINT-DIRECT-DB-ACCESS-002`**: one finding per **`(from,to)`** pair (deduped parallel edges).
38
+ - **`looksLikeHealthUrl`**: **`/healthcheck`**, **`/ping`**, **`/status`**, **`/alive`** (and **`ruleNoHealthcheck`** message notes gateway/BFF heuristic).
39
+ - **`isHttpLikeType`**: **`gateway`**, **`bff`**, **`graphql`**, **`grpc`**, and word-boundary matches for common API surface types.
40
+
41
+ ### Changed (engineering / safety)
42
+ - **TypeScript `strict: true`** + **`noUnusedLocals` / `noUnusedParameters`**; **`npm test`** runs **`tsc --noEmit`** before Vitest.
43
+ - **`prepare`** removed; **`prepublishOnly`** runs **`npm run build`** for npm publishes. Monorepo consumers must build this package explicitly (unchanged for InkByte CI).
44
+ - **Biome** added with a **minimal** lint ruleset (`npm run lint`); expand rules incrementally — see **`docs/ENGINEERING_NOTES.md`**.
45
+ - **IR-LINT-SYNC-CHAIN-001** uses **sync-only** adjacency; edges marked async (`metadata.protocol`, `config.async`, etc.) are excluded — see **`edgeRepresentsAsyncBoundary`** in `lint-graph.ts`.
46
+ - **CLI:** **`--danger-skip-ir-structural-validation`** documented; **`--skip-ir-structural-validation`** hidden (deprecated alias).
47
+
48
+ ### Changed (documentation / messaging)
49
+ - **`docs/CONCEPT_ADOPTION_AND_LIMITS.md`** — honest framing: strengths (IR as SoT, compiler model, tiered validation), adoption friction (IR authoring, one-way export), OSS-as-trust vs platform adoption; README + `llms.txt` summaries and links.
50
+ - Canonical **OSS positioning** line in README, `llms.txt`, monorepo/OSS docs: *Includes structural validation + basic architecture linting (rule-based, deterministic).*
51
+ - Clarified OpenAPI pass as **document shape** (parse + required top-level fields), explicitly **not** Spectral-style lint; README + `docs/STRUCTURAL_VS_SEMANTIC_VALIDATION.md` + code comments.
52
+ - Documented **codegen vs validation** for retry/timeout IR fields and **InkByte vs OSS** scope in README and structural/semantic doc.
53
+ - README positioning: **deterministic compiler and linter for system architecture**; validation layers table (OSS vs Cloud).
54
+
55
+ ## [0.1.0] - 2026-02-26
56
+
57
+ ### Added
58
+ - Deterministic **FastAPI** and **Express** generators from blueprint **IR** (JSON graph).
59
+ - **`archrad export`** CLI (`--ir`, `--target`, `--out`).
60
+ - **Structural OpenAPI** validation pass on generated bundles (warnings, no LLM repair).
61
+ - **Golden path**: `docker-compose.yml`, `Dockerfile`, `Makefile`, README section; container port **8080**; configurable **host** publish port (`--host-port` / `ARCHRAD_HOST_PORT`).
62
+ - Optional **localhost preflight** for host port (warn or `--strict-host-port`).
63
+ - Library API: `runDeterministicExport`, OpenAPI helpers, golden-layer helpers.
64
+
65
+ [Unreleased]: https://github.com/archradhq/arch-deterministic/compare/v0.1.0...HEAD
66
+ [0.1.0]: https://github.com/archradhq/arch-deterministic/releases/tag/v0.1.0
@@ -0,0 +1,15 @@
1
+ # Contributing
2
+
3
+ Development usually happens in the **private InkByte monorepo** under `packages/deterministic`, so the server and package stay in sync.
4
+
5
+ When you change this package, follow the product monorepo checklist: **`docs/MONOREPO_OSS_DETERMINISTIC_ALIGNMENT.md`** (and run **`npm run test:deterministic`** from the **InkByte repo root**).
6
+
7
+ If you clone **only** this repository (`archradhq/arch-deterministic`):
8
+
9
+ ```bash
10
+ npm ci
11
+ npm run build # required: there is no prepare script (see docs/ENGINEERING_NOTES.md)
12
+ npm test # runs tsc --noEmit then vitest
13
+ ```
14
+
15
+ PRs: keep changes **free of product-specific** imports (no Firestore, no `server/` paths). Apache-2.0 — see `LICENSE`.
package/LICENSE ADDED
@@ -0,0 +1,17 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ Copyright 2026 ArchRad / InkByte contributors
6
+
7
+ Licensed under the Apache License, Version 2.0 (the "License");
8
+ you may not use this file except in compliance with the License.
9
+ You may obtain a copy of the License at
10
+
11
+ http://www.apache.org/licenses/LICENSE-2.0
12
+
13
+ Unless required by applicable law or agreed to in writing, software
14
+ distributed under the License is distributed on an "AS IS" BASIS,
15
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ See the License for the specific language governing permissions and
17
+ limitations under the License.
package/README.md ADDED
@@ -0,0 +1,284 @@
1
+ # @archrad/deterministic
2
+
3
+ ![archrad validate — IR-LINT-DIRECT-DB-ACCESS-002 first, fix on the graph, clean gate](demo-validate.gif)
4
+
5
+ <!-- GIFs: validate hero — `npm run build && vhs scripts/record-demo-validate.tape` → demo-validate.gif. Drift — `npm run record:demo:drift` → demo-drift.gif (terminal tape) or replay scripts + capture. For “edit → save → validate-drift” (IDE + terminal, skeptic-grade), see scripts/DEMO_GIF_STORYBOARD.md “Trust loop”. scripts/README_DEMO_RECORDING.md. package.json `files`. -->
6
+
7
+ **A deterministic compiler and linter for system architecture.**
8
+
9
+ **Validate your architecture before you write code.**
10
+
11
+ **For AI agents / IDE assistants:** see **`llms.txt`** in this package (llms.txt-style project summary for tools like Claude Code, Devin, etc.).
12
+
13
+ **Apache-2.0** — blueprint **IR** (JSON graph) → **FastAPI** or **Express** + **OpenAPI**, **Docker**, **Makefile** — **no LLM**, **no account**, **offline**.
14
+
15
+ **OSS positioning:** *Includes structural validation + basic architecture linting (rule-based, deterministic).*
16
+
17
+ > This is not only a generator: **`archrad validate`** treats your graph like source code — **IR-STRUCT-*** (shape/refs/cycles) and **IR-LINT-*** (light architecture heuristics: health routes, fan-out, sync chains, HTTP→DB coupling). Then **`archrad export`** compiles to runnable projects. After you have a tree on disk, **`archrad validate-drift`** compares it to a **fresh** export from the same IR (missing/changed files — **not** semantic code review). Generated **OpenAPI** gets a **document-shape** pass (parse + required fields — **not** Spectral-style spec lint).
18
+
19
+ **Open core (OSS):** **IR structural validation**, **basic architecture lint** (rule-based, deterministic), **OpenAPI document-shape** checks. **ArchRad Cloud** adds **policy / compliance**, deeper **architecture intelligence**, and **AI remediation** — see **`docs/STRUCTURAL_VS_SEMANTIC_VALIDATION.md`**.
20
+
21
+ ### Concept, cold start, and strategy (honest scope)
22
+
23
+ The **core idea** is sound: **IR = what to build**, **codegen = how** — with compiler-style validation and deterministic output. **Adoption friction** is real: this repo is a **compiler with no bundled authoring UI** (JSON/graph in; you bring **ArchRad Cloud**, another tool, or hand-written IR). **Where the IR comes from** is intentionally **plural**: **manual** graph JSON/YAML today, plus **`archrad ingest openapi`** (and more ingestion surfaces over time — e.g. **IaC**). Treat **OpenAPI → IR** as a **starting lane**, not the whole roadmap: regenerate IR in CI, then **`archrad validate`** / **`archrad export`** — structural HTTP surface only, not full system semantics. Draft language for “drift / where does IR come from?” threads: **`scripts/SOCIAL_POST_DRIFT_AND_INGESTION.md`**. **Export is one-way** (no built-in round-trip from edited code back to IR)—best thought of as **scaffold + contract validation**, not full lifecycle architecture sync unless you own that workflow. Many teams will still treat the OSS layer as a **trust and CI artifact** that makes the Cloud/AI path auditable. Read **`docs/CONCEPT_ADOPTION_AND_LIMITS.md`** for the full framing and future directions (e.g. lightweight YAML/IDE ergonomics).
24
+
25
+ ---
26
+
27
+ ## How it works (architecture)
28
+
29
+ ```
30
+ IR (nodes/edges) → validateIrStructural (IR-STRUCT-*) → errors block export
31
+
32
+ validateIrLint (IR-LINT-*) → warnings (CI: --fail-on-warning / --max-warnings)
33
+
34
+ pythonFastAPI | nodeExpress generators
35
+
36
+ openapi.yaml + app code + package metadata
37
+
38
+ golden layer (Dockerfile, docker-compose.yml, Makefile, README; host→container e.g. 8080:8080)
39
+
40
+ validateOpenApiInBundleStructural(openapi.yaml) → document-shape warnings (not full API lint)
41
+
42
+ { files, openApiStructuralWarnings, irStructuralFindings, irLintFindings }
43
+
44
+ Optional CI: archrad validate-drift → re-export IR in-memory, diff vs existing ./out → DRIFT-MISSING / DRIFT-MODIFIED (thin deterministic gate)
45
+ ```
46
+
47
+ ### Validation levels (quick contract)
48
+
49
+ 1. **JSON Schema validation** — IR document shape vs `schemas/archrad-ir-graph-v1.schema.json` (editor/CI; optional at runtime).
50
+ 2. **IR structural validation** — `validateIrStructural`: arrays, ids, HTTP `config`, edge refs, cycles (`IR-STRUCT-*`). Uses an internal **normalized** graph (see **`docs/IR_CONTRACT.md`**).
51
+ 3. **Export-time generated OpenAPI structural validation** — Parse + required fields on the **generated** `openapi.yaml` (document shape, not Spectral).
52
+
53
+ **Architecture lint** (`IR-LINT-*`) sits after structural checks: rule visitors on the parsed graph (heuristics, not schema).
54
+
55
+ ### Validation layers (naming)
56
+
57
+ | Layer (OSS) | What it is | Codes |
58
+ |-------------|------------|--------|
59
+ | **IR structural validation** | Graph well-formedness: ids, edges, cycles, HTTP path/method | `IR-STRUCT-*` |
60
+ | **Architecture lint (basic)** | Deterministic heuristics only (no AI, no org policy) | `IR-LINT-*` |
61
+ | **OpenAPI structural validation** (document shape) | Parse + required top-level OpenAPI fields on **generated** spec | *(string warnings, not IR codes)* |
62
+
63
+ | Layer (Cloud — not this package) | Examples |
64
+ |----------------------------------|----------|
65
+ | **Policy engine** | SOC2, org rules, entitlement |
66
+ | **Architecture intelligence** | Deeper NFR / cost / security reasoning |
67
+ | **AI remediation** | Repair loops, suggested edits |
68
+
69
+ 1. **IR structural validation:** duplicate/missing node ids, bad HTTP `config.url` / `config.method`, unknown edge endpoints, directed cycles.
70
+ 2. **Architecture lint:** Implemented as a **registry of visitor functions** on a parsed graph (`buildParsedLintGraph` → **`LINT_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**. **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`**).
71
+ 3. **Generators** → `openapi.yaml`, handlers, deps.
72
+ 4. **Golden path** → `make run` / `docker compose up --build`.
73
+ 5. **OpenAPI document shape** on the bundle — **not** [Spectral](https://github.com/stoplightio/spectral)-level lint. Issues → **`openApiStructuralWarnings`**.
74
+
75
+ **IR contract:** **`schemas/archrad-ir-graph-v1.schema.json`**. **Parser boundary + normalized shapes:** **`docs/IR_CONTRACT.md`** (`normalizeIrGraph` → `materializeNormalizedGraph`).
76
+
77
+ **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.
78
+
79
+ ### Codegen vs validation (retry, timeouts, policy)
80
+
81
+ 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.
82
+
83
+ ---
84
+
85
+ ## Ways to use it
86
+
87
+ | Mode | Best for | Example |
88
+ |------|-----------|---------|
89
+ | **CLI** | Quick local scaffolding, CI, “no Node project” usage | `archrad export --ir graph.json --target python --out ./out` |
90
+ | **YAML → IR** | Author graphs in YAML, emit JSON for validate/export | `archrad yaml-to-ir -y graph.yaml -o graph.json` |
91
+ | **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` |
92
+ | **CLI validate** | CI / pre-commit: IR structural + architecture lint, no codegen | `archrad validate --ir graph.json` |
93
+ | **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` |
94
+ | **Library** (`@archrad/deterministic`) | IDPs / pipelines | `runDeterministicExport` → files + findings; **`runValidateDrift`** / **`runDriftCheckAgainstFiles`** for drift |
95
+
96
+ ### CLI
97
+
98
+ **Input is structured IR (JSON), not natural language.** There is no `archrad export --prompt "..."`. You pass a **graph file** (nodes/edges) like **`fixtures/minimal-graph.json`**. The **npm README GIF** uses **`fixtures/demo-direct-db-violation.json`** → **`fixtures/demo-direct-db-layered.json`**: **failure-first** **`IR-LINT-DIRECT-DB-ACCESS-002`**, fix on the graph, then a **clean** validate (no codegen in the clip). For a graph that hits **many** lint rules at once (stress test), use **`fixtures/ecommerce-with-warnings.json`**. For a **golden payment + retry** graph (edge `config.retry.maxAttempts` + payment node `config.retryPolicy`, both `3`), use **`fixtures/payment-retry-demo.json`** — export shows **`retry_policy`** / retry helpers in **`app/main.py`**. Record that path with **`scripts/record-demo-payment-retry.tape`** (→ **`demo-payment-retry.gif`**). **Drift clip:** **`scripts/record-demo-drift.tape`** / **`npm run record:demo:drift`** (→ **`demo-drift.gif`**) — export, **`tail`** before/after a one-line tamper on **`out/app/main.py`**, then **`validate-drift`**; if **VHS** fails on your machine, run **`scripts/run-demo-drift-sequence.ps1`** or **`.sh`** while screen-capturing (see **`scripts/README_DEMO_RECORDING.md`**). See **`scripts/DEMO_GIF_STORYBOARD.md`** for all tapes. **CLI:** `--target python` is the FastAPI bundle; there is no separate `fastapi` target name. To go from **plain English → IR**, use **ArchRad Cloud** or your own LLM step; this package only does **IR → files**.
99
+
100
+ **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:
101
+
102
+ ```bash
103
+ archrad ingest openapi --spec ./openapi.yaml --out ./graph.json
104
+ archrad validate --ir ./graph.json
105
+ archrad export --ir ./graph.json --target python --out ./out
106
+ ```
107
+
108
+ **YAML → JSON (lighter authoring):** edit **`fixtures/minimal-graph.yaml`** (or your own file) and compile to IR JSON, then validate or export:
109
+
110
+ ```bash
111
+ archrad yaml-to-ir --yaml fixtures/minimal-graph.yaml --out ./graph.json
112
+ archrad validate --ir ./graph.json
113
+ # or pipe: archrad yaml-to-ir -y fixtures/minimal-graph.yaml | archrad validate --ir /dev/stdin # on Unix; on Windows use --out then validate
114
+ ```
115
+
116
+ YAML must have either top-level **`graph:`** (object) or top-level **`nodes:`** (array); bare graphs are wrapped as `{ "graph": { ... } }` automatically.
117
+
118
+ After `npm run build` (required after `npm ci`; there is no `prepare` hook — see **`docs/ENGINEERING_NOTES.md`**):
119
+
120
+ ```bash
121
+ node dist/cli.js export --ir fixtures/minimal-graph.json --target python --out ./my-api
122
+ node dist/cli.js yaml-to-ir --yaml fixtures/minimal-graph.yaml --out /tmp/ir.json
123
+ # After global install / npx:
124
+ archrad export --ir ./graph.json --target node --out ./my-express-api
125
+
126
+ # Validate IR (structural + architecture lint). Pretty output; exit 1 on structural errors by default:
127
+ node dist/cli.js validate --ir fixtures/minimal-graph.json
128
+ # Machine-readable + CI gates:
129
+ archrad validate --ir ./graph.json --json
130
+ archrad validate --ir ./graph.json --fail-on-warning
131
+ archrad validate --ir ./graph.json --max-warnings 0
132
+ # Structural only (skip IR-LINT-*):
133
+ archrad validate --ir ./graph.json --skip-lint
134
+ ```
135
+
136
+ **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.
137
+
138
+ ```bash
139
+ archrad export -i ./graph.json -t python -o ./out
140
+ # …edit files under ./out…
141
+ archrad validate-drift -i ./graph.json -t python -o ./out --skip-host-port-check
142
+ # CI-friendly:
143
+ archrad validate-drift -i ./graph.json -t python -o ./out --skip-host-port-check --json
144
+ # Fail if the tree has extra files not in the reference export:
145
+ archrad validate-drift -i ./graph.json -t python -o ./out --strict-extra
146
+ ```
147
+
148
+ Regenerate the matching clip: **`npm run record:demo:drift`** (VHS) → **`demo-drift.gif`**, or **`scripts/run-demo-drift-sequence.ps1`** / **`.sh`** + ShareX/OBS if VHS is unavailable (see **`scripts/DEMO_GIF_STORYBOARD.md`**).
149
+
150
+ #### Example: validate architecture
151
+
152
+ ```bash
153
+ archrad validate --ir fixtures/minimal-graph.json
154
+ ```
155
+
156
+ Example output (stderr):
157
+
158
+ ```text
159
+ archrad validate:
160
+ ⚠️ IR-LINT-NO-HEALTHCHECK-003: No HTTP node exposes a typical health/readiness path (...)
161
+ Fix: Add a GET route such as /health for orchestrators and load balancers.
162
+ Suggestion: Expose liveness vs readiness separately if your platform distinguishes them.
163
+ Impact: Weaker deploy/rollback safety and harder operations automation.
164
+ ```
165
+
166
+ Structural errors look like **`❌ IR-STRUCT-...`** with **`Fix:`** lines. Use **`--json`** to consume findings in GitHub Actions or other CI.
167
+
168
+ - **`--ir`** — JSON: `{ "graph": { "nodes", "edges", "metadata" } }` or a raw graph (CLI wraps it).
169
+ - **`--target`** — `python` \| `node` \| `nodejs`
170
+ - **`--out`** — output directory (created if needed)
171
+ - **`--host-port <n>`** — host port Docker publishes (default **8080**; container still listens on **8080** inside). Same as env **`ARCHRAD_HOST_PORT`**.
172
+ - **`--skip-host-port-check`** — don’t probe `127.0.0.1` before export.
173
+ - **`--strict-host-port`** — **exit with error** if the host port appears **in use** (CI-friendly).
174
+ - **`--danger-skip-ir-structural-validation`** — **UNSAFE:** 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.
175
+ - **`--skip-ir-lint`** — skip **`validateIrLint`** during export.
176
+ - **`--fail-on-warning`** / **`--max-warnings <n>`** — if set, **no files are written** when IR structural + lint findings violate the policy (same semantics as **`validate`**).
177
+
178
+ 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.
179
+
180
+ **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.
181
+
182
+ ### Validate the package as a developer
183
+
184
+ 1. `cd packages/deterministic && npm ci && npm run build && npm test`
185
+ 2. `node dist/cli.js export --ir fixtures/minimal-graph.json --target python --out ./tmp-out`
186
+ 3. `cd tmp-out && make run` then `curl` the URL shown in the generated **README** (port matches `--host-port` if you set it).
187
+ 4. Optional: `node dist/cli.js export ... --host-port 18080` if **8080** is already taken.
188
+
189
+ ### Library
190
+
191
+ ```typescript
192
+ import {
193
+ runDeterministicExport,
194
+ runValidateDrift,
195
+ validateIrStructural,
196
+ validateIrLint,
197
+ sortFindings,
198
+ shouldFailFromFindings,
199
+ } from '@archrad/deterministic';
200
+
201
+ const { files, openApiStructuralWarnings, irStructuralFindings, irLintFindings } =
202
+ await runDeterministicExport(ir, 'python', {
203
+ hostPort: 8080,
204
+ skipIrLint: false, // default
205
+ });
206
+ // Structural errors → empty files (unless skipIrStructuralValidation). Lint is non-blocking for export unless you check policy in your pipeline.
207
+
208
+ const drift = await runValidateDrift(ir, 'python', '/path/to/existing-export', {
209
+ skipIrLint: false, // set true to match CLI --skip-ir-lint on reference export
210
+ });
211
+ // drift.ok, drift.driftFindings, drift.exportResult — same core semantics as CLI validate-drift (CLI also probes host port before calling the library)
212
+
213
+ const all = sortFindings([...validateIrStructural(ir), ...validateIrLint(ir)]);
214
+ if (shouldFailFromFindings(all, { failOnWarning: true })) {
215
+ /* gate your CI */
216
+ }
217
+ ```
218
+
219
+ Optional: `isLocalHostPortFree` / `normalizeGoldenHostPort` from the same package if you want your own preflight.
220
+
221
+ ---
222
+
223
+ ## Golden path (~60 seconds)
224
+
225
+ From the package root (after build):
226
+
227
+ ```bash
228
+ node dist/cli.js export --ir fixtures/minimal-graph.json --target python --out ./out
229
+ cd ./out
230
+ make run
231
+ # In another terminal, once the API is up:
232
+ curl -sS -X POST http://localhost:8080/signup -H "Content-Type: application/json" -d '{}'
233
+ ```
234
+
235
+ 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.
236
+
237
+ **Helper script** (prints the same flow; use when recording a terminal GIF):
238
+
239
+ ```bash
240
+ bash scripts/golden-path-demo.sh
241
+ ```
242
+
243
+ See **`scripts/README_DEMO_RECORDING.md`** for **VHS / asciinema / ttyrec** tips, **When VHS fails**, **drift** replay scripts, and the **trust loop** (IDE edit + terminal **`validate-drift`**). The hero GIF at the top is **`demo-validate.gif`**; **`demo-drift.gif`** and the **trust-loop** storyboard live in **`scripts/DEMO_GIF_STORYBOARD.md`**.
244
+
245
+ ---
246
+
247
+ ## Open source vs ArchRad Cloud
248
+
249
+ **This repository is only the deterministic engine** — local, offline, no phone-home.
250
+
251
+ | Here (OSS) | ArchRad Cloud (commercial product) |
252
+ |------------|-------------------------------------|
253
+ | 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 |
254
+ | `archrad` CLI forever, no account required for this package | Auth, orgs, **quotas**, billing |
255
+ | No proprietary **LLM** orchestration or “repair” loops | LLM generation, repair, multi-model routing |
256
+ | No Git sync, no enterprise policy injection in this repo | Git push, governance, compliance dashboards |
257
+
258
+ 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.
259
+
260
+ **InkByte vs this package:** Deeper workflow analysis, enterprise validation routes, and LLM-assisted flows may exist in the **private InkByte monorepo** (`server/`, etc.); they are **not** part of the **`@archrad/deterministic`** npm surface unless shipped here. This README describes **only** what the OSS package proves.
261
+
262
+ ---
263
+
264
+ ## Monorepo vs public OSS repo
265
+
266
+ The **canonical source** for this engine may live in a **private monorepo** next to the full product; `server` can depend on `file:../packages/deterministic`. The **public** GitHub repo should contain **only** this package — canonical clone: **`https://github.com/archradhq/arch-deterministic`**. Subtree publish: **`docs/OSS_VS_PRODUCT_REPOS.md`** and **`docs/PUBLISH_DETERMINISTIC_OSS.md`** (in the product monorepo).
267
+
268
+ ---
269
+
270
+ ## Publishing the public OSS repo
271
+
272
+ From the private monorepo root: **`docs/PUBLISH_DETERMINISTIC_OSS.md`**. This tree includes **`.github/workflows/ci.yml`** and **Dependabot**; they run when this folder is the **git root** of the public repo.
273
+
274
+ ---
275
+
276
+ ## Contributing
277
+
278
+ See **`CONTRIBUTING.md`**.
279
+
280
+ ---
281
+
282
+ ## License
283
+
284
+ Apache-2.0 — see **`LICENSE`**.
package/SECURITY.md ADDED
@@ -0,0 +1,26 @@
1
+ # Security policy
2
+
3
+ ## Supported versions
4
+
5
+ We publish **`@archrad/deterministic`** on npm from the [`archradhq/arch-deterministic`](https://github.com/archradhq/arch-deterministic) repository. Security fixes are applied on the **latest** minor release line when practical; use the newest version when possible.
6
+
7
+ ## Reporting a vulnerability
8
+
9
+ **Please do not** open a public GitHub issue for undisclosed security problems.
10
+
11
+ Instead:
12
+
13
+ 1. Prefer **GitHub private vulnerability reporting** on [`archradhq/arch-deterministic`](https://github.com/archradhq/arch-deterministic/security) if enabled; otherwise email the **repository maintainers** with:
14
+ - A short description and impact
15
+ - Steps to reproduce (if safe to share)
16
+ - Affected versions / environments (Node, OS) if known
17
+
18
+ 2. We aim to acknowledge within **a few business days** and coordinate disclosure and a fix release.
19
+
20
+ ## Scope
21
+
22
+ This package is a **local** CLI and library: **IR JSON → project files**. It does not ship a network service by default. Reports about **generated application code** (e.g. a user’s Docker image) are usually out of scope unless the issue is in **this package’s** templates or logic.
23
+
24
+ ## Dependency advisories
25
+
26
+ Run `npm audit` in your project after installing. We use **Dependabot** on the GitHub repo for dependency update PRs.
package/biome.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
3
+ "vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true },
4
+ "files": {
5
+ "ignore": ["dist", "node_modules", "*.json", "fixtures", "schemas"]
6
+ },
7
+ "formatter": {
8
+ "enabled": false
9
+ },
10
+ "organizeImports": { "enabled": false },
11
+ "linter": {
12
+ "enabled": true,
13
+ "rules": {
14
+ "recommended": false,
15
+ "suspicious": {
16
+ "noDebugger": "error"
17
+ }
18
+ }
19
+ },
20
+ "javascript": {
21
+ "formatter": {
22
+ "quoteStyle": "single"
23
+ }
24
+ }
25
+ }
Binary file
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Human-oriented CLI formatting for IR findings (structural + architecture lint).
3
+ */
4
+ import type { IrStructuralFinding } from './ir-structural.js';
5
+ /** Pretty multi-line output for terminal / CI logs */
6
+ export declare function formatFindingLines(f: IrStructuralFinding): string[];
7
+ /**
8
+ * Pretty-print findings. By default groups **IR structural (IR-STRUCT-*)** and **architecture lint (IR-LINT-*)** so both are obvious in CI logs.
9
+ */
10
+ export declare function printFindingsPretty(findings: IrStructuralFinding[], header?: string, options?: {
11
+ groupByLayer?: boolean;
12
+ }): void;
13
+ export type ValidationExitPolicy = {
14
+ /** When true, any warning fails the run (CI gate). */
15
+ failOnWarning?: boolean;
16
+ /** Fail when warning count is strictly greater than this (undefined = no limit) */
17
+ maxWarnings?: number;
18
+ };
19
+ export declare function countBySeverity(findings: IrStructuralFinding[], sev: IrStructuralFinding['severity']): number;
20
+ /** true = exit with failure */
21
+ export declare function shouldFailFromFindings(findings: IrStructuralFinding[], policy: ValidationExitPolicy): boolean;
22
+ export declare function sortFindings(findings: IrStructuralFinding[]): IrStructuralFinding[];
23
+ //# sourceMappingURL=cli-findings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-findings.d.ts","sourceRoot":"","sources":["../src/cli-findings.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAc9D,sDAAsD;AACtD,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,mBAAmB,GAAG,MAAM,EAAE,CAYnE;AAWD;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,mBAAmB,EAAE,EAC/B,MAAM,CAAC,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAE,GACnC,IAAI,CAuBN;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,sDAAsD;IACtD,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,mFAAmF;IACnF,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,wBAAgB,eAAe,CAAC,QAAQ,EAAE,mBAAmB,EAAE,EAAE,GAAG,EAAE,mBAAmB,CAAC,UAAU,CAAC,GAAG,MAAM,CAE7G;AAED,+BAA+B;AAC/B,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,mBAAmB,EAAE,EAAE,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAM7G;AAID,wBAAgB,YAAY,CAAC,QAAQ,EAAE,mBAAmB,EAAE,GAAG,mBAAmB,EAAE,CAKnF"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Human-oriented CLI formatting for IR findings (structural + architecture lint).
3
+ */
4
+ const ICON = {
5
+ error: '❌',
6
+ warning: '⚠️',
7
+ info: 'ℹ️',
8
+ };
9
+ function ttyColor() {
10
+ const noColor = process.env.NO_COLOR != null && process.env.NO_COLOR !== '';
11
+ if (noColor || !process.stderr.isTTY)
12
+ return { red: '', reset: '' };
13
+ return { red: '\u001b[31m', reset: '\u001b[0m' };
14
+ }
15
+ /** Pretty multi-line output for terminal / CI logs */
16
+ export function formatFindingLines(f) {
17
+ const icon = ICON[f.severity] ?? '•';
18
+ const { red, reset } = ttyColor();
19
+ const head = f.code.startsWith('IR-LINT-') && red
20
+ ? `${red}${icon} ${f.code}: ${f.message}${reset}`
21
+ : `${icon} ${f.code}: ${f.message}`;
22
+ const lines = [head];
23
+ if (f.fixHint)
24
+ lines.push(` Fix: ${f.fixHint}`);
25
+ if (f.suggestion)
26
+ lines.push(` Suggestion: ${f.suggestion}`);
27
+ if (f.impact)
28
+ lines.push(` Impact: ${f.impact}`);
29
+ return lines;
30
+ }
31
+ function printFindingBlock(findings) {
32
+ for (const f of findings) {
33
+ for (const line of formatFindingLines(f)) {
34
+ console.error(line);
35
+ }
36
+ console.error('');
37
+ }
38
+ }
39
+ /**
40
+ * Pretty-print findings. By default groups **IR structural (IR-STRUCT-*)** and **architecture lint (IR-LINT-*)** so both are obvious in CI logs.
41
+ */
42
+ export function printFindingsPretty(findings, header, options) {
43
+ if (!findings.length)
44
+ return;
45
+ if (header)
46
+ console.error(header);
47
+ const group = options?.groupByLayer !== false;
48
+ if (!group) {
49
+ printFindingBlock(findings);
50
+ return;
51
+ }
52
+ const structural = findings.filter((f) => f.code.startsWith('IR-STRUCT-'));
53
+ const lint = findings.filter((f) => f.code.startsWith('IR-LINT-'));
54
+ const other = findings.filter((f) => !f.code.startsWith('IR-STRUCT-') && !f.code.startsWith('IR-LINT-'));
55
+ if (structural.length) {
56
+ console.error('IR structural (IR-STRUCT-*):');
57
+ printFindingBlock(structural);
58
+ }
59
+ if (lint.length) {
60
+ console.error('Architecture lint (IR-LINT-*):');
61
+ printFindingBlock(lint);
62
+ }
63
+ if (other.length) {
64
+ console.error('Other findings:');
65
+ printFindingBlock(other);
66
+ }
67
+ }
68
+ export function countBySeverity(findings, sev) {
69
+ return findings.filter((f) => f.severity === sev).length;
70
+ }
71
+ /** true = exit with failure */
72
+ export function shouldFailFromFindings(findings, policy) {
73
+ if (findings.some((f) => f.severity === 'error'))
74
+ return true;
75
+ const w = countBySeverity(findings, 'warning');
76
+ if (Boolean(policy.failOnWarning) && w > 0)
77
+ return true;
78
+ if (policy.maxWarnings != null && w > policy.maxWarnings)
79
+ return true;
80
+ return false;
81
+ }
82
+ const SEV_ORDER = { error: 0, warning: 1, info: 2 };
83
+ export function sortFindings(findings) {
84
+ return [...findings].sort((a, b) => {
85
+ const d = (SEV_ORDER[a.severity] ?? 9) - (SEV_ORDER[b.severity] ?? 9);
86
+ return d !== 0 ? d : a.code.localeCompare(b.code);
87
+ });
88
+ }
package/dist/cli.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * archrad — deterministic export without the hosted server.
4
+ * Usage: archrad export --ir graph.json --target python --out ./my-api
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;GAGG"}