arkgate 4.8.13 → 4.8.15

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 (126) hide show
  1. package/CHANGELOG.md +219 -4
  2. package/README.md +33 -22
  3. package/SECURITY.md +5 -3
  4. package/bin/ark-check-runtime.mjs +25 -57
  5. package/bin/ark-mcp-runtime.mjs +111 -2
  6. package/bin/ark-shared.mjs +140 -11
  7. package/bin/ark.mjs +7 -25
  8. package/bin/lib/adr-presence.mjs +97 -0
  9. package/bin/lib/agent-projection-formatters.mjs +2 -0
  10. package/bin/lib/agent-skills-package.mjs +241 -8
  11. package/bin/lib/analysis-completeness.mjs +38 -2
  12. package/bin/lib/analysis-engine.mjs +6 -6
  13. package/bin/lib/architecture-scan.mjs +33 -5
  14. package/bin/lib/ark-order-doctor.mjs +7 -1
  15. package/bin/lib/ark-order-report.mjs +2 -1
  16. package/bin/lib/ark-run-doctor.mjs +6 -0
  17. package/bin/lib/ark-run-report.mjs +6 -2
  18. package/bin/lib/arkrules-sensors.mjs +81 -0
  19. package/bin/lib/baseline-key.mjs +4 -1
  20. package/bin/lib/check-args.mjs +52 -2
  21. package/bin/lib/ci-and-commands.mjs +7 -2
  22. package/bin/lib/config-contract.mjs +64 -1
  23. package/bin/lib/design-smells.mjs +21 -1
  24. package/bin/lib/diagnostic-catalog.mjs +5 -3
  25. package/bin/lib/doctor-advisories.mjs +109 -17
  26. package/bin/lib/doctor-human.mjs +20 -6
  27. package/bin/lib/doctor-next-actions.mjs +14 -0
  28. package/bin/lib/doctor-plan.mjs +32 -2
  29. package/bin/lib/enforcement-honesty.mjs +47 -2
  30. package/bin/lib/first-run-help.mjs +19 -3
  31. package/bin/lib/gate-files.mjs +40 -3
  32. package/bin/lib/host-support-matrix.mjs +9 -1
  33. package/bin/lib/html-report-depth.mjs +2 -0
  34. package/bin/lib/html-report.mjs +13 -2
  35. package/bin/lib/import-resolve.mjs +74 -13
  36. package/bin/lib/improvement-compass-doctor.mjs +6 -1
  37. package/bin/lib/improvement-compass-map.mjs +3 -1
  38. package/bin/lib/install-migrate.mjs +23 -0
  39. package/bin/lib/layer-description.mjs +90 -8
  40. package/bin/lib/mcp-hook-payload.mjs +57 -1
  41. package/bin/lib/no-domain-frontend.mjs +91 -0
  42. package/bin/lib/presets.mjs +3 -4
  43. package/bin/lib/product-copy.mjs +19 -0
  44. package/bin/lib/projected-governed-coverage.mjs +114 -0
  45. package/bin/lib/recommend-cli.mjs +54 -0
  46. package/bin/lib/remediation.mjs +6 -2
  47. package/bin/lib/resolved-candidate-facts.mjs +225 -100
  48. package/bin/lib/rules-under-contract.mjs +117 -2
  49. package/bin/lib/scan-files.mjs +39 -0
  50. package/bin/lib/snippet-analysis.mjs +14 -8
  51. package/bin/lib/start-preview.mjs +8 -2
  52. package/bin/lib/states-transitions-presence.mjs +212 -0
  53. package/bin/lib/status-command.mjs +2 -0
  54. package/bin/lib/status-transition-catalog.mjs +410 -0
  55. package/bin/lib/team-parliament-io.mjs +10 -0
  56. package/bin/lib/upgrade-whats-new.mjs +3 -3
  57. package/bin/lib/violations.mjs +43 -1
  58. package/bin/lib/write-path-capabilities.mjs +20 -5
  59. package/bin/lib/write-path-detect.mjs +27 -2
  60. package/dist/{configTypes-j7so8B4O.d.ts → configTypes-Dt3DpVbd.d.ts} +19 -0
  61. package/dist/{diagnosticCatalog-DA565Lja.d.ts → diagnosticCatalog-BEg85XlE.d.ts} +3 -3
  62. package/dist/eslint/index.cjs +4 -4
  63. package/dist/eslint/index.d.ts +1 -1
  64. package/dist/eslint/index.js +4 -4
  65. package/dist/index.cjs +31 -31
  66. package/dist/index.d.ts +126 -19
  67. package/dist/index.js +31 -31
  68. package/dist/nestjs/index.cjs +5 -5
  69. package/dist/nestjs/index.d.ts +3 -3
  70. package/dist/nestjs/index.js +5 -5
  71. package/dist/runtime/index.cjs +15 -15
  72. package/dist/runtime/index.d.ts +6 -6
  73. package/dist/runtime/index.js +15 -15
  74. package/dist/{types-Djbs3KjE.d.ts → types-CN9tVMPz.d.ts} +3 -1
  75. package/dist/{types-tGhZUiGX.d.ts → types-TBiv0WHL.d.ts} +1 -1
  76. package/docs/README.md +8 -6
  77. package/docs/agent-guide.md +35 -17
  78. package/docs/ai-gates.md +13 -4
  79. package/docs/arkorder.md +11 -4
  80. package/docs/brownfield-adoption.md +4 -1
  81. package/docs/configuration.md +63 -14
  82. package/docs/develop.md +4 -1
  83. package/docs/diagnostics.md +30 -10
  84. package/docs/enthusiast/README.md +6 -1
  85. package/docs/enthusiast/how-to-agent-gates.md +5 -0
  86. package/docs/enthusiast/how-to-gallery-starter.md +2 -1
  87. package/docs/enthusiast/how-to-pick-shape.md +1 -1
  88. package/docs/package-surface.md +10 -6
  89. package/docs/product-voice.md +32 -6
  90. package/docs/threat-model.md +2 -2
  91. package/docs/typescript-support.md +3 -3
  92. package/docs/use.md +23 -11
  93. package/package.json +1 -1
  94. package/schemas/ark.config.schema.json +23 -2
  95. package/server.json +2 -2
  96. package/templates/agent-skills/README.md +7 -4
  97. package/templates/agent-skills/ark-adopt/SKILL.md +91 -6
  98. package/templates/agent-skills/ark-architect/SKILL.md +5 -18
  99. package/templates/agent-skills/ark-autopilot/SKILL.md +14 -6
  100. package/templates/agent-skills/ark-contract/SKILL.md +9 -20
  101. package/templates/agent-skills/ark-coverage/SKILL.md +13 -9
  102. package/templates/agent-skills/ark-explain/SKILL.md +11 -5
  103. package/templates/agent-skills/ark-explore/SKILL.md +39 -5
  104. package/templates/agent-skills/ark-fix/SKILL.md +15 -20
  105. package/templates/agent-skills/ark-loop/SKILL.md +14 -20
  106. package/templates/agent-skills/ark-order/SKILL.md +200 -0
  107. package/templates/agent-skills/ark-place/SKILL.md +49 -11
  108. package/templates/agent-skills/ark-runtime/SKILL.md +21 -6
  109. package/templates/agent-skills/ark-think/SKILL.md +24 -126
  110. package/templates/agent-skills/ark-upgrade/SKILL.md +14 -3
  111. package/templates/arkrules/DomainModel.json +14 -1
  112. package/templates/skills/ark-adopt.md +91 -6
  113. package/templates/skills/ark-architect.md +5 -18
  114. package/templates/skills/ark-autopilot.md +14 -6
  115. package/templates/skills/ark-contract.md +9 -20
  116. package/templates/skills/ark-coverage.md +13 -9
  117. package/templates/skills/ark-explain.md +11 -5
  118. package/templates/skills/ark-explore.md +39 -5
  119. package/templates/skills/ark-fix.md +15 -20
  120. package/templates/skills/ark-loop.md +14 -20
  121. package/templates/skills/ark-order.md +200 -0
  122. package/templates/skills/ark-place.md +49 -11
  123. package/templates/skills/ark-runtime.md +21 -6
  124. package/templates/skills/ark-think.md +24 -126
  125. package/templates/skills/ark-upgrade.md +14 -3
  126. package/templates/tests/ark-adoption-gaps.test.ts +5 -4
package/CHANGELOG.md CHANGED
@@ -5,6 +5,218 @@ in the immutable pre-2.0 archive linked below.
5
5
 
6
6
  ## Unreleased
7
7
 
8
+ ### Added
9
+ - Soft doctor residual when Domain is declared but empty and the UI holds the
10
+ rules (`noDomainFrontend`). Projects empty Domain + presentation share, or
11
+ the existing `domain-logic-in-ui` smell. Friendly next step: one Domain file
12
+ (`/ark-place`) then one small refactor. Not a fail. Silent when there is no
13
+ frontend, Domain already has files, or the UI bag is too thin. No new skill,
14
+ schema, smell id, or config key.
15
+ - Narrow opt-in status/transition catalog: when Domain-role code already
16
+ names a closed status/state vocabulary and a domain doc is in play without
17
+ a table (or one link), doctor JSON may add `statusTransitionCatalog` with
18
+ those names. Friendly next step. Not a fail. Silent when Domain has no
19
+ status vocabulary, or the domain doc is absent, or the map is already
20
+ present. No new skill, schema, or config key.
21
+ - Doctor residual when a product-domain doc is already in play
22
+ (`docs/domain.md` or a sibling) and there is no states → transitions table
23
+ (or one link) yet. Friendly next step. Not a fail. Silent when that doc is
24
+ absent. No new skill, schema, or status-field sensor.
25
+ - Soft ADR / decision-note presence when `--require-gates` (or
26
+ `--strict` / `--strict-merge` / doctor adopted-strict) is on and the tree
27
+ has no `docs/adr/` or `docs/decisions/` note yet. Doctor residual + a
28
+ friendly next step. Not a fail. Silent when require-gates is off. No new
29
+ skill, schema, or config key. Policy weaken still uses `--policy-ack`.
30
+ - Optional `layers[].owners` on each layer (GitHub handle or email, same
31
+ identity as `stewards`) and optional `requireLayerOwners`. Absence is
32
+ silent. When the require flag is on, doctor names the first house without
33
+ an owner, `--strict-config` refuses, and the write gate denies a write into
34
+ that house. Owners are stripped from `policyHash`; the require flag stays
35
+ in the hash. Invalid identity fails the schema. No new skill or
36
+ `schemaVersion` bump. Compact starters may omit.
37
+ - Optional `layers[].trustBoundary` on each layer: `public` | `auth` | `admin` |
38
+ `internal`. Who this folder is for. Schema validates the closed list; a wrong
39
+ value fails. Absence is silent — never a doctor residual, never
40
+ `--strict-config`, never a score. Place / prepare-write / MCP / doctor /
41
+ coverage / HTML show `trust: public` when present. Stripped from `policyHash`
42
+ like `stewards` and `layers[].description`. No import-rule matrix. Not host/CI
43
+ TLS. No new skill or `schemaVersion` bump. Compact starters may omit.
44
+ - Doctor residual when `arkRules` is on, Domain has code, and `invariants[]` is
45
+ empty (`INVARIANT_CATALOG_EMPTY`). Points at the file to fill. Advisory unless
46
+ a domain structure rule is already enforced — then `--strict-merge` can refuse.
47
+ No `arkRules` map, or no Domain files, stays silent. Domain starters ship two
48
+ short phrase templates so the shape is visible. No new skill, schema, or flag.
49
+
50
+ ### Changed
51
+ - Compact `--doctor` names ArkRules only when the `arkRules` map is on
52
+ (one breath + counts, not a score). Absence stays silent. Reuses
53
+ `rulesUnderContract` — no new schema, flag, or skill. `--doctor --all`
54
+ prints the same lines under **ArkRules (not a score)**.
55
+ - PR / CI skill gate: `npm run check:agent-skills` now fails closed when a
56
+ product plane (Layers, ArkRules, ArkRun, ArkOrder) or Contener · Guiar ·
57
+ Ordenar drops out of first-class skill bodies or living hubs. Reuses
58
+ `ARK_SKILL_CAPACITY` via Domain `validateSkillProductCapacity` — no new
59
+ schema or skill. Deny copy stays plain. Skills never enforce.
60
+ - Write-gate host parity: Cursor hard-write evidence now requires the host-native
61
+ `failClosed: true` flag (same idea as a file permission — if the checker cannot
62
+ run, the write must not land). A Write/StrReplace hook without that flag is
63
+ fail-open: doctor, `--require-write-hook`, and honesty JSON say so and do not
64
+ claim a hard block. Hosts without a native flag stay as before (deny when the
65
+ hook runs; hook-crash behavior is host-owned). Soft hosts stay advisory.
66
+ Required CI is still the shared merge line. No new skill, schema, or host.
67
+
68
+ ### Fixed
69
+ - Write hook and `ark-check` now agree on overlapping layer globs: the hook
70
+ probes the same specifier extensions as `ark-check` and classifies with
71
+ `layerForRelativePath` (explicit `money.ts` beats `src/lib/**`). Field
72
+ case: Persistence importing `@/lib/finance/money` is allowed when the
73
+ contract says DomainModel, not denied as ApplicationOrchestration
74
+ (issue [#237](https://github.com/pedroknigge/arkgate/issues/237)).
75
+ `LEXICAL_EVIDENCE_INCOMPLETE` no longer calls the hook the final verdict —
76
+ `ark-check` is the authority. No new schema, flag, or skill.
77
+ - An `include` that matches files but classifies none of them no longer prints
78
+ a green check. `ANALYSIS_COVERS_NO_FILES` already said include **and** layer;
79
+ the refusal now counts classified files, not only the include walk. The write
80
+ hook denies those same files (`CONFIG_UNCLASSIFIED_FILES`) so write and
81
+ `--strict-merge` agree. Partial unclassified stays a warning on a plain check.
82
+ `--plan` / `--coverage` / `--doctor` still diagnose. No new flag or schema.
83
+ - Library / package-monorepo `arkgate start` no longer writes Next App Router /
84
+ Pages API / `app/api` layer captions when the tree has no Next
85
+ (issue [#213](https://github.com/pedroknigge/arkgate/issues/213)). Next-flavored
86
+ doctor copy still appears when Next is detected. Same start command; no new flag.
87
+ - Cold `--doctor` no longer treats a full governed list as a `--changed` scope
88
+ (issue [#212](https://github.com/pedroknigge/arkgate/issues/212)). Full-tree
89
+ status uses the unbounded facts path; import-closure + a second
90
+ `resolveModuleName` pass stay on `--changed` only. Extra-plane extractors
91
+ (ArkRun / ArkOrder / class-shape) stay silent when that plane is off. Same
92
+ compact first screen. No new flag or cache.
93
+ - Fresh `arkgate start --apply` (and every other path that stamps a new
94
+ `ark.config.json`) now writes `$schema` at `arkgate@4` on unpkg, not the stale
95
+ `@2` pin (issue [#211](https://github.com/pedroknigge/arkgate/issues/211)).
96
+ Existing configs keep whatever URL they already have. Editor completion
97
+ matches the 4.x line you just installed.
98
+
99
+ ## 4.8.15 — 2026-09-13
100
+
101
+ **Patch** over **4.8.14**. Pre-adoption ship bar: opt-in `--local` /
102
+ `ARK_CHECK_LOCAL` cheap multi-worktree check
103
+ ([#251](https://github.com/pedroknigge/arkgate/pull/251)) and dogfood
104
+ honesty
105
+ ([#252](https://github.com/pedroknigge/arkgate/pull/252) / [#243](https://github.com/pedroknigge/arkgate/issues/243) /
106
+ [#246](https://github.com/pedroknigge/arkgate/issues/246) /
107
+ [#247](https://github.com/pedroknigge/arkgate/issues/247)).
108
+ **Write. Check. Ship.** **No required config migration.** No
109
+ `schemaVersion` bump. Does not close `K01` / `Z09`. This mother
110
+ `ark.config.json` still does **not** turn `arkOrder` on.
111
+
112
+ **Status: prepared** (npm `latest` remains **4.8.14** until Actions `publish-npm`
113
+ runs with `tag=v4.8.15` and `dry_run=false`).
114
+
115
+ ### Added
116
+ - Opt-in local / multi-worktree cheap check: `--local` or `ARK_CHECK_LOCAL=1`
117
+ reuses `--changed` (touched files + import closure) and stays per `--root`.
118
+ Explicit `--local --strict-merge` is refused so CI Contener stays fail-closed.
119
+ The env var is ignored under `--strict-merge` and under `--doctor` /
120
+ `--coverage` / `--plan` / `--report` / `--promote`. JSON may add `local`,
121
+ `scope: "changed"`, `analysisRoot`. Write hooks stay lexical. No new schema,
122
+ cache, skill, or second analysis engine. Design:
123
+ [docs/plans/local-check-worktree-dx/README.md](docs/plans/local-check-worktree-dx/README.md).
124
+
125
+ ### Fixed
126
+ - Publish confidence gate: retarget drifted critical mutation islands
127
+ (`config-loading`, `empty-analysis-refusal`, host fail-open / none-gap)
128
+ after owners + classified-file growth, and add tests that kill those
129
+ mutants. Threshold stays 90% with zero NoCoverage. Does not change
130
+ runtime behavior.
131
+ - Dogfood honesty: type-only `LAYER_IMPORT_VIOLATION` prints `⚠` (not `✖`)
132
+ when the check still exits 0 ([#243](https://github.com/pedroknigge/arkgate/issues/243));
133
+ one-minute `start --apply` copy and the start preview footer name the
134
+ deliberate lock flags (`--archetype`, `--preset`, `--force`,
135
+ `ark-check --recommend`) without softening the refuse gate
136
+ ([#246](https://github.com/pedroknigge/arkgate/issues/246)); start,
137
+ `--recommend`, and doctor share one `computeCoverage` definition of
138
+ projected governed coverage
139
+ ([#247](https://github.com/pedroknigge/arkgate/issues/247)).
140
+
141
+ ## 4.8.14 — 2026-09-06
142
+
143
+ **Patch** over **4.8.13**. Catch npm `latest` up to `main`: first-class
144
+ `/ark-order` (#217 / #216) and the other ships that landed after tag
145
+ `v4.8.13`. **Write. Check. Ship.** **No required config migration.** No
146
+ `schemaVersion` bump. Does not close `K01` / `Z09`. This mother
147
+ `ark.config.json` still does **not** turn `arkOrder` on.
148
+
149
+ **Status: prepared** (npm `latest` remains **4.8.13** until Actions `publish-npm`
150
+ runs with `tag=v4.8.14` and `dry_run=false`).
151
+
152
+ ### Added
153
+ - Skill catalog covers 100% of product capacity (issue [#216](https://github.com/pedroknigge/arkgate/issues/216) /
154
+ [ADR 0036](docs/adr/0036-skill-catalog-product-capacity.md) / [#217](https://github.com/pedroknigge/arkgate/pull/217)).
155
+ First-class `/ark-order` mirrors `/ark-runtime` for ArkOrder. Leftover
156
+ `/ark-architect` / `/ark-contract` / `/ark-fix` / `/ark-loop` / `/ark-think`
157
+ stay as one-release redirect stubs. Remaining doors speak Contener · Guiar ·
158
+ Ordenar and name the sibling for each job. ACS05 “no new skill names” freeze
159
+ opened; the catalog stays closed in Domain (`ARK_SKILL_NAMES`). Leftover
160
+ `/ark-fix` / `/ark-loop` stubs keep the Y04 mechanical-edit hygiene outcomes.
161
+ Skills never enforce.
162
+
163
+ ### Changed
164
+ - First-contact leftovers after the north-star / billing / one-breath ships
165
+ ([#225](https://github.com/pedroknigge/arkgate/pull/225)): gallery index and
166
+ the hexagonal demo open in ordinary English; `arkgate-check --help` leads with
167
+ the deny; the write-gate fallback and the two newcomer deny codes
168
+ (`LAYER_IMPORT_VIOLATION`, `ARKORDER_UNVALVED_RELEASE`) drop ξ /
169
+ “architecture contract” from the first line.
170
+ - Doctor / start first-contact always tell the ArkOrder one-breath (what it
171
+ is, when to use it, next step) even when the extra is off ([#224](https://github.com/pedroknigge/arkgate/pull/224)).
172
+ Reuses `/ark-order` and `examples/arkorder-billing`. Verdicts stay silent (not a
173
+ score; Layers unchanged).
174
+ - First-contact now names **Contener · Guiar · Ordenar** in ordinary English
175
+ (contain the write, guide the next step, order leftover mess)
176
+ ([#222](https://github.com/pedroknigge/arkgate/pull/222)). README, doctor
177
+ one-light, CLI help, host projections, and first-class skills share the same
178
+ breath. Write. Check. Ship. stays the deny.
179
+ - Billing gallery is the undeniable ArkOrder proof path
180
+ ([#223](https://github.com/pedroknigge/arkgate/pull/223)): site/docs
181
+ (`docs/use.md`, docs hub, enthusiast) →
182
+ [examples/README.md](https://github.com/pedroknigge/arkgate/blob/main/examples/README.md)
183
+ → [examples/arkorder-billing](https://github.com/pedroknigge/arkgate/tree/main/examples/arkorder-billing)
184
+ (first freeze `release()`, later `proposeRelease` then `apply`). Still GitHub-only;
185
+ not added to the npm tarball or `GALLERY_STARTERS`.
186
+ - Living docs (AGENTS hub, claims matrix, agent-guide skill install, product-voice
187
+ filter) restate the catalog after `#217`: first-class `/ark-order`, leftover
188
+ shortcuts as one-release stubs, ACS05 freeze opened, Domain still closes
189
+ `ARK_SKILL_NAMES`. The skill *set* covers Layers + ArkRules + ArkRun + ArkOrder
190
+ (Contener · Guiar · Ordenar). Historical 14/14 100% and last 4.8.3 C-020 stay
191
+ Planned.
192
+ - Pre-adoption publish is the thin path: slim CI on prepare PRs (no `release`
193
+ label or branch-name full-matrix), annotated tags (unsigned allowed),
194
+ CHANGELOG + `publish-npm` provenance. MCP registry, site, companion republish,
195
+ and `docs/releases/` notes stay optional.
196
+
197
+ ### Fixed
198
+ - `--changed` no longer builds facts for the whole include tree (issue
199
+ [#205](https://github.com/pedroknigge/arkgate/issues/205) /
200
+ [#221](https://github.com/pedroknigge/arkgate/pull/221)). The check now
201
+ resolves touched sources plus their import closure, skips the unused
202
+ tsconfig file walk, and does not list every governed file just to filter
203
+ the diff. File-local ArkRules sensors stay on the touched set. Full-tree
204
+ `ark-check` is unchanged. Dual gate (write + PR) stays the merge line.
205
+ - Short `arkgate-check --help` now names `--changed --base <ref>` as the local /
206
+ pre-push fast path (issue [#204](https://github.com/pedroknigge/arkgate/issues/204) /
207
+ [#220](https://github.com/pedroknigge/arkgate/pull/220)).
208
+ Encyclopedia `--help --all` is unchanged.
209
+ - `ark-check` human warnings now print `path:line` the way denies already do
210
+ (ArkOrder / ArkRun advisory findings already carried the location; the
211
+ printer dropped it). Findings with no file say so — they are about the
212
+ project rules, not a line you just edited. The green `--plan` pointer names
213
+ the first evidence path next to each design smell (issue #203).
214
+ - `start --apply` / `--install-agent-gates` now merge the Ark write-path
215
+ contract into a project-owned `AGENTS.md` and upsert `mcpServers.ark` in
216
+ existing `.mcp.json` (and host copies) without `--force`. Doctor no longer
217
+ reports `gatesMissing: ["AGENTS.md", ".mcp.json"]` after the stranger happy
218
+ path on a repo that already had those files (issue #210).
219
+
8
220
  ## 4.8.13 — 2026-09-06
9
221
 
10
222
  **Patch** over **4.8.12**. Publish gate: remaining ArkOrder doctor/deny branches so
@@ -13,8 +225,9 @@ this is the ship. **Write. Check. Ship.** **No required config migration.** No
13
225
  `schemaVersion` bump. Does not close `K01` / `Z09`. No new skill names. This
14
226
  mother `ark.config.json` still does **not** turn `arkOrder` on.
15
227
 
16
- **Status: prepared** (tree candidate; npm `latest` remains **4.8.11** until the
17
- signed-tag provenance workflow succeeds).
228
+ **Status: prepared** (tag `v4.8.13` and the GitHub Release already exist;
229
+ npm `latest` remains **4.8.11** until Actions `publish-npm` runs with
230
+ `tag=v4.8.13` and `dry_run=false`).
18
231
 
19
232
  ### Fixed
20
233
  - Confidence coverage: remaining ArkOrder doctor/deny `Next:` and HTML/doctor
@@ -29,8 +242,10 @@ them through doctor / start / status / the check label, in plain words.
29
242
  Does not close `K01` / `Z09`. No new skill names. This mother `ark.config.json`
30
243
  still does **not** turn `arkOrder` on.
31
244
 
32
- **Status: prepared** (tree candidate; npm `latest` remains **4.8.11** until the
33
- signed-tag provenance workflow succeeds).
245
+ **Status: tagged, not published** (tag `v4.8.12` stands; `publish-npm` failed
246
+ the 81% branch floor at 80.97%. Do not retag. Do not ship this version to npm.
247
+ Consumers stay on **4.8.11** until **4.8.13** is published.
248
+ npm `latest` remains **4.8.11**).
34
249
 
35
250
  ### Added
36
251
  - Doctor / HTML / status `arkOrder` section (`notAScore`), same envelope as
package/README.md CHANGED
@@ -11,10 +11,12 @@ AI can build fast—and make a mess just as fast.
11
11
 
12
12
  Keep the product easy to understand, change, and trust.
13
13
 
14
- ArkGate stops bad shortcuts. ArkRules protects how each part should behave. ArkRun keeps work moving. ArkOrder protects the few big choices that should not change by accident.
14
+ Contain what the AI may write, and in what shape. Guide you with proven patterns and one next step. Order leftover mess toward a clean tree, a little at a time.
15
15
 
16
16
  Safer changes, fewer surprises, and extra protection only when you choose it.
17
17
 
18
+ That is **Contener · Guiar · Ordenar**.
19
+
18
20
  Works with Cursor, Claude, Codex, and Grok.
19
21
 
20
22
  [![Website](https://img.shields.io/badge/website-arkgate.online-0a0a0a)](https://www.arkgate.online/)
@@ -37,7 +39,7 @@ Works with Cursor, Claude, Codex, and Grok.
37
39
 
38
40
  </div>
39
41
 
40
- > **ArkGate 4.8.13** is prepared on this tree; npm `latest` remains **4.8.11**.
42
+ > **ArkGate 4.8.15** is prepared on this tree; npm `latest` remains **4.8.14** until `publish-npm` for `v4.8.15`.
41
43
  > Write. Check. Ship. Adopted = required GitHub
42
44
  > status running `arkgate-check --strict-merge`, or an explicit `advisory-only` stance.
43
45
  > Status is compact (`arkgate-check --doctor`; `--all` for Details). Optional **ArkRun**
@@ -45,7 +47,7 @@ Works with Cursor, Claude, Codex, and Grok.
45
47
  > (`arkgate/order`) stops rewriting a big product choice — like the billing plan —
46
48
  > as if it were a seat count. Change those choices through a valve, not a generic update.
47
49
  > `@arkgate/runtime` is deprecated.
48
- > [4.8.13 prepared](CHANGELOG.md) · [4.8.11 published](docs/releases/4.8.11.md) · [4.8.10](docs/releases/4.8.10.md) · [4.8.9](docs/releases/4.8.9.md) · [4.8.8](docs/releases/4.8.8.md) · [4.8.7](docs/releases/4.8.7.md) · [4.8.6](docs/releases/4.8.6.md) · [4.8.5](docs/releases/4.8.5.md) · [4.8.4](docs/releases/4.8.4.md) · [4.8.3](docs/releases/4.8.3.md) · [4.8.2](docs/releases/4.8.2.md) · [4.8.1](docs/releases/4.8.1.md) · [4.8.0](docs/releases/4.8.0.md) · [Docs hub](docs/README.md) · [Voice](docs/product-voice.md)
50
+ > [4.8.15 prepared](CHANGELOG.md) · [4.8.11 published](docs/releases/4.8.11.md) · [4.8.10](docs/releases/4.8.10.md) · [4.8.9](docs/releases/4.8.9.md) · [4.8.8](docs/releases/4.8.8.md) · [4.8.7](docs/releases/4.8.7.md) · [4.8.6](docs/releases/4.8.6.md) · [4.8.5](docs/releases/4.8.5.md) · [4.8.4](docs/releases/4.8.4.md) · [4.8.3](docs/releases/4.8.3.md) · [4.8.2](docs/releases/4.8.2.md) · [4.8.1](docs/releases/4.8.1.md) · [4.8.0](docs/releases/4.8.0.md) · [Docs hub](docs/README.md) · [Voice](docs/product-voice.md)
49
51
 
50
52
  ---
51
53
 
@@ -71,6 +73,11 @@ npx arkgate-check --doctor # status — one next step
71
73
  npx arkgate-check --doctor --all # full details
72
74
  ```
73
75
 
76
+ `start --apply` refuses when projected governed coverage is below 50% or
77
+ shape confidence is weak (below 0.6 with coverage under 80%). That lock is
78
+ deliberate. Lock the shape with `--archetype <id>`, `--preset <name>`, or
79
+ `--force`, or inspect ranked shapes with `npx arkgate-check --recommend`.
80
+
74
81
  That is the product. Stuck? Run status (`--doctor`) and do action **#1**.
75
82
 
76
83
  ```text
@@ -79,7 +86,8 @@ start → doctor → new files in the right folder
79
86
  ```
80
87
 
81
88
  Keep the rules file out of product PRs. Local check:
82
- `ark-check --changed --base origin/dev`. Changing the rules themselves uses `--contract-session`.
89
+ `ark-check --local --base origin/dev` (same as `--changed`; refused with
90
+ `--strict-merge`). Changing the rules themselves uses `--contract-session`.
83
91
 
84
92
  Aliases `ark` / `ark-check` / `ark-mcp` still work. npm / pnpm / yarn. No install lifecycle scripts
85
93
  — and none on pack or prepare either, so `pnpm add git+https://github.com/pedroknigge/arkgate`
@@ -106,15 +114,15 @@ See [docs/package-surface.md](docs/package-surface.md#installing-from-git).
106
114
 
107
115
  When the agent writes a bad import, the write doesn’t land.
108
116
  The same check fails the pull request. That is **ArkGate** — import rules, always on
109
- once you adopt. The other three are optional.
117
+ once you adopt (**Contener**). The other three are optional.
110
118
 
111
119
  | | Role | When |
112
120
  |--|------|------|
113
121
  | **While the agent writes** | The write doesn’t land on supported hosts; warning only elsewhere | Always (ArkGate) |
114
122
  | **Before merge** | `arkgate-check` as a **required** CI status | Always (ArkGate) |
115
- | **ArkRules** | Optional policies *inside* a layer | When you ask |
116
- | **ArkRun** | Optional experimental runtime (`arkgate/runtime`) | Off unless you turn it on |
117
- | **ArkOrder** | Stops rewriting a big product choice (like billing plan) as if it were a seat count. Change it through a valve, not a generic update (`arkgate/order`) | Off unless you turn it on |
123
+ | **ArkRules** | Optional policies *inside* a layer (**Contener** — shape) | When you ask |
124
+ | **ArkRun** | Optional experimental runtime (`arkgate/runtime`) (**Guiar**) | Off unless you turn it on |
125
+ | **ArkOrder** | Extra for the few big choices billing plan, not seat counts (`arkgate/order`) (**Ordenar**) | Off unless you turn it on |
118
126
 
119
127
  Layers (who may import whom) always run. ArkRules, ArkRun, and ArkOrder change no
120
128
  inter-layer verdict when absent. Label leftovers **`[Layer]`** vs **`[ArkRules]`** vs
@@ -142,15 +150,13 @@ check: [4.3.0 — What ArkGate is / isn't](docs/releases/4.3.0.md#what-arkgate-i
142
150
 
143
151
  ## Why it exists
144
152
 
145
- AI coding agents generate code at unprecedented speeds. However, they tend to take the shortest path to solve a problem. If an agent needs data in a Domain layer, it might directly import a database adapter. Left unchecked, this creates spaghetti code and technical debt at light speed.
146
-
147
- Traditional linters catch these architectural violations in CI *after* the agent has finished its work, breaking the flow.
148
-
149
- ArkGate solves this by shifting the check to the exact moment of writing:
153
+ AI takes the shortest path. Contain the write so a bad import never lands. Guide
154
+ the next step so a non-expert is not stuck. Order leftover mess toward a clean
155
+ tree, one small refactor at a time.
150
156
 
151
- 1. **Fail fast at the write boundary.** Through IDE hooks and MCP, ArkGate intercepts the file write. If the agent writes a bad import, the write doesn't land. The agent gets immediate feedback and can self-correct before saving to disk.
152
- 2. **The check is the single source of truth.** A simple `ark.config.json` defines your layers and allowed edges.
153
- 3. **Honest reporting.** Green imports do not equal elegant design. ArkGate separates structural correctness from design smells, providing an improvement compass to guide leftover design work without blocking the PR.
157
+ Traditional linters catch this only in CI after the agent finished. ArkGate
158
+ checks at write time and again on the PR. Green imports are not elegant design
159
+ leftover mess is **needs a refactor**, not “done”.
154
160
 
155
161
  ---
156
162
 
@@ -188,7 +194,9 @@ Details: [docs/use.md](docs/use.md).
188
194
  **Read the CI column:** for every host, the repository-wide hard guarantee is a **required**
189
195
  GitHub **status context** that runs the CLI — not “CI file present,” and not the CLI binary name alone.
190
196
  Codex hard write covers only a complete local `apply_patch`; Cursor covers only listed
191
- `preToolUse` ops. In both cases the project hook must be installed + trusted, while shell/direct
197
+ `preToolUse` ops with `failClosed: true`. A Cursor hook without that flag is fail-open
198
+ (if the checker cannot run, the write still lands). In both cases the project hook must be
199
+ installed + trusted, while shell/direct
192
200
  filesystem writes, hosted or specialized opt-out paths, and human edits still rely on CI.
193
201
 
194
202
  This table describes the supported profile **after its files are installed and the host loads/trusts them**. A hard local boundary covers only the listed hook operations; alternate tools, direct filesystem writes, and human edits still rely on CI. MCP validation is advisory because the agent must call it. The CI check blocks a merge only when the repository makes that status required. Repair **envelopes** may be emitted without reinjection being guaranteed; silent auto-apply never happens. Run `arkgate-check --doctor` (or `ark-check --doctor`) for the evidence actually detected in the current repository.
@@ -251,7 +259,7 @@ npx arkgate-check --strict-merge # CI / required status
251
259
  npx arkgate-check --install-agent-gates --tools claude,cursor,codex,grok,antigravity
252
260
  # optional: refresh shared home skills (Claude/Grok/Antigravity/Codex; never downgrades)
253
261
  # npx arkgate-check --install-agent-gates --skills-only --agent-homes --force
254
- # optional: same 13 skills via Agent Skills ecosystem (no new names)
262
+ # optional: closed skill catalog via Agent Skills ecosystem (ARK_SKILL_NAMES)
255
263
  # npx skills add ./node_modules/arkgate/templates/agent-skills
256
264
  # optional ArkRun: poll the loopback inspector (ANSI TUI — not a gate)
257
265
  # npx ark-dashboard --url http://127.0.0.1:<port>/snapshot
@@ -313,9 +321,12 @@ Name the few choices (`xiKeys`: plan, cycle, tenancy — not seat counts). First
313
321
  freeze is `release()`. Later change is `proposeRelease` then `apply`. A generic
314
322
  `update` does not land.
315
323
 
316
- The billing gallery lives on GitHub, not in the npm tarball:
317
- [examples/arkorder-billing](https://github.com/pedroknigge/arkgate/tree/main/examples/arkorder-billing).
318
- Compact starters leave it off. Details: [ArkOrder](docs/arkorder.md).
324
+ Proof path (GitHub, not the npm tarball):
325
+ [examples/arkorder-billing](https://github.com/pedroknigge/arkgate/tree/main/examples/arkorder-billing)
326
+ first freeze is `release()`; later plan change is `proposeRelease` then `apply`.
327
+ Gallery index:
328
+ [examples/README.md](https://github.com/pedroknigge/arkgate/blob/main/examples/README.md).
329
+ Compact starters leave the extra off. Details: [ArkOrder](docs/arkorder.md).
319
330
 
320
331
  ---
321
332
 
@@ -331,7 +342,7 @@ Compact starters leave it off. Details: [ArkOrder](docs/arkorder.md).
331
342
  | Config · package surface · TS | [configuration](docs/configuration.md) · [package-surface](docs/package-surface.md) · [typescript-support](docs/typescript-support.md) |
332
343
  | Brownfield | [docs/brownfield-adoption.md](docs/brownfield-adoption.md) |
333
344
  | Security | [SECURITY.md](SECURITY.md) |
334
- | Prepared (4.8.13; not published) | [CHANGELOG](CHANGELOG.md) |
345
+ | Prepared (4.8.15; not published) | [CHANGELOG](CHANGELOG.md) |
335
346
  | Current published (4.8.11 on npm `latest`) | [docs/releases/4.8.11.md](docs/releases/4.8.11.md) · [CHANGELOG](CHANGELOG.md) |
336
347
  | Prior published (4.8.10) | [docs/releases/4.8.10.md](docs/releases/4.8.10.md) · [CHANGELOG](CHANGELOG.md) |
337
348
  | Prior published (4.8.9) | [docs/releases/4.8.9.md](docs/releases/4.8.9.md) · [CHANGELOG](CHANGELOG.md) |
package/SECURITY.md CHANGED
@@ -28,9 +28,11 @@ a private security contact without including exploit details.
28
28
  ArkGate releases are GitHub-first:
29
29
 
30
30
  1. Changes land on GitHub and must pass CI plus the dedicated security workflow.
31
- 2. A GitHub Release is created from a signed `vX.Y.Z` tag (`git tag -s`). The publish
32
- workflow verifies the signature and fails closed for unsigned tags. The local override
33
- exists only for explicit emergency use and is not enabled in CI.
31
+ 2. A GitHub Release is created from an **annotated** `vX.Y.Z` tag (`git tag -a` is
32
+ enough). The publish workflow checks that the tag matches `package.json` and is
33
+ annotated. Unsigned annotated tags are allowed. Signed tags still verify when
34
+ present. Set `ARK_REQUIRE_SIGNED_RELEASE_TAG=true` on the publish job only if
35
+ you want signed-only again.
34
36
  3. The manual `Publish npm` workflow verifies the tag, requires the GitHub Release to
35
37
  exist, reruns release checks, publishes npm with provenance, and uploads a SHA-256
36
38
  checksum for the npm tarball to the GitHub Release.
@@ -12,17 +12,13 @@ import {
12
12
  DEFAULT_RULES,
13
13
  applyFrameworkLayoutOverlays,
14
14
  arkCommand,
15
- ADOPTION_PLAN_FILENAME,
16
- buildArchitectureRecommendation,
17
15
  createElevenLayerConfig,
18
16
  enrichViolationWithFixClass,
19
17
  listPolicyPackIds,
20
18
  loadPolicyPackMeta,
21
- writeAdoptionPlan,
22
19
  detectWorkspaces,
23
20
  detectTsPackageRoots,
24
21
  resolveIncludeRoots,
25
- formatArchitectureRecommendationHuman,
26
22
  installDevHint,
27
23
  layerForFile,
28
24
  } from './ark-shared.mjs';
@@ -67,6 +63,7 @@ import {
67
63
  runCoverage,
68
64
  runPlan,
69
65
  runDoctor,
66
+ printAdrPresenceHint,
70
67
  } from './lib/doctor-plan.mjs';
71
68
  import { runRatchetCores } from './lib/core-ratchet.mjs';
72
69
  import {
@@ -77,6 +74,7 @@ import {
77
74
  writeBaseline,
78
75
  printViolation,
79
76
  printViolationBreakdown,
77
+ printWarning,
80
78
  CONCENTRATION_MIN_VIOLATIONS,
81
79
  } from './lib/violations.mjs';
82
80
  import {
@@ -102,6 +100,8 @@ import {
102
100
  UNGOVERNED_PROBE_CAP,
103
101
  collectGovernedFiles,
104
102
  countUngovernedSourceFiles,
103
+ governedFilesFromRelativePaths,
104
+ isGovernedSourcePath,
105
105
  normalize,
106
106
  walk,
107
107
  } from './lib/scan-files.mjs';
@@ -110,7 +110,7 @@ import { runArchitectureScan } from './lib/architecture-scan.mjs';
110
110
  import {
111
111
  ANALYSIS_COMPLETENESS,
112
112
  analysisIncompleteStatement,
113
- emptyAnalysisRefusal,
113
+ emptyAnalysisRefusal, classifiedCountFromFiles,
114
114
  } from './lib/analysis-completeness.mjs';
115
115
  import { reportUnavailableAnalysis } from './lib/unavailable-analysis.mjs';
116
116
  import { validateHardWriteRequest } from './lib/enforcement-profiles.mjs';
@@ -123,7 +123,6 @@ import {
123
123
  applyAgainstRatchet,
124
124
  bindTeamBaseRefs,
125
125
  contractSessionFrom,
126
- filterChangedGovernedFiles,
127
126
  runTeamPreflight,
128
127
  ungovernedDumpMessage,
129
128
  } from './lib/team-parliament-io.mjs';
@@ -135,7 +134,7 @@ import {
135
134
  resolveEffectiveProjectRoot,
136
135
  } from './lib/project-root.mjs';
137
136
  import { demoteArkRuleTeethUnderClassificationFloor } from './lib/rules-under-contract.mjs';
138
- import { parseArgs, resolveDesignDeltaBaseRef } from './lib/check-args.mjs';
137
+ import { localCheckEnvelope, parseArgs, resolveDesignDeltaBaseRef } from './lib/check-args.mjs';
139
138
  import { detectConfig, proposeForUncovered } from './lib/check-config-detect.mjs';
140
139
  import { runWatchMode } from './lib/check-watch.mjs';
141
140
 
@@ -1151,42 +1150,8 @@ async function main() {
1151
1150
  }
1152
1151
 
1153
1152
  if (args.recommend) {
1154
- try {
1155
- const recommendation = buildArchitectureRecommendation(args.root);
1156
- let planWritten;
1157
- if (args.writePlan) {
1158
- const result = writeAdoptionPlan(args.root, recommendation);
1159
- planWritten = result.path;
1160
- }
1161
- if (args.json) {
1162
- console.log(
1163
- JSON.stringify(
1164
- {
1165
- ...recommendation,
1166
- ...(planWritten
1167
- ? { adoptionPlanPath: path.relative(args.root, planWritten) || ADOPTION_PLAN_FILENAME }
1168
- : {}),
1169
- },
1170
- null,
1171
- 2
1172
- )
1173
- );
1174
- } else {
1175
- console.log(formatArchitectureRecommendationHuman(recommendation));
1176
- if (planWritten) {
1177
- console.log('');
1178
- console.log(`Wrote ${path.relative(args.root, planWritten) || ADOPTION_PLAN_FILENAME}`);
1179
- }
1180
- }
1181
- } catch (error) {
1182
- const message = error instanceof Error ? error.message : String(error);
1183
- if (args.json) {
1184
- console.log(JSON.stringify({ ok: false, error: message }, null, 2));
1185
- } else {
1186
- console.error(`ark-check --recommend failed: ${message}`);
1187
- }
1188
- process.exitCode = 2;
1189
- }
1153
+ const { runRecommend } = await import('./lib/recommend-cli.mjs');
1154
+ runRecommend(args);
1190
1155
  return;
1191
1156
  }
1192
1157
 
@@ -1207,9 +1172,11 @@ async function main() {
1207
1172
  let governedCache = null;
1208
1173
  const loadGovernedFiles = () => (governedCache ??= collectGovernedFiles(root, loadConfig()));
1209
1174
  const emptyAnalysisRefusalNow = () => {
1210
- const governedCount = loadGovernedFiles().length;
1175
+ const governedFiles = loadGovernedFiles();
1176
+ const governedCount = governedFiles.length;
1211
1177
  return emptyAnalysisRefusal({
1212
1178
  governedFileCount: governedCount,
1179
+ classifiedFileCount: classifiedCountFromFiles(governedFiles, loadConfig().layers, layerForFile, root),
1213
1180
  // Probed only when nothing is governed, and never through the contract's own
1214
1181
  // exclude: the config under suspicion must not get to answer the question about
1215
1182
  // itself (`exclude: ["**"]` would otherwise read as greenfield and pass).
@@ -1347,7 +1314,7 @@ async function main() {
1347
1314
  (compactHost
1348
1315
  ? `AGENTS.md, compact host registration (${compactHost})`
1349
1316
  : REQUIRED_GATE_FILES.join(', '))
1350
- );
1317
+ ); printAdrPresenceHint(args.root, (line) => console.log(line));
1351
1318
  }
1352
1319
  if (writeRequest?.host) {
1353
1320
  console.log(`Ark hard-write hook present for ${writeRequest.host}.`);
@@ -1382,7 +1349,7 @@ async function main() {
1382
1349
  JSON.stringify(
1383
1350
  {
1384
1351
  ok: preflight.halt.exitCode === 0,
1385
- ...(preflight.halt.cheap ? { cheap: true } : {}),
1352
+ ...(preflight.halt.cheap ? { cheap: true } : {}), ...localCheckEnvelope(args, root),
1386
1353
  teamParliament: preflight.halt.teamParliament,
1387
1354
  ...(policyDelta ? { policyDelta } : {}),
1388
1355
  },
@@ -1405,8 +1372,7 @@ async function main() {
1405
1372
  }
1406
1373
  const manifest = readManifest(root, args.manifest);
1407
1374
  const rules = manifest?.architecture?.rules ?? config.rules;
1408
- const allGovernedFiles = loadGovernedFiles();
1409
- if (verdictPath) {
1375
+ if (verdictPath && !args.changed && !isMutatingCliCommand(args)) {
1410
1376
  const refusal = emptyAnalysisRefusalNow();
1411
1377
  if (refusal) {
1412
1378
  reportEmptyAnalysis(refusal);
@@ -1414,10 +1380,9 @@ async function main() {
1414
1380
  }
1415
1381
  }
1416
1382
  if (args.failUngoverned && teamParliament?.changeSet?.productPaths?.length) {
1417
- const governedRel = new Set(
1418
- allGovernedFiles.map((abs) => normalize(path.relative(root, abs)))
1383
+ const dumped = teamParliament.changeSet.productPaths.filter(
1384
+ (rel) => !isGovernedSourcePath(root, rel, config)
1419
1385
  );
1420
- const dumped = teamParliament.changeSet.productPaths.filter((rel) => !governedRel.has(rel));
1421
1386
  if (dumped.length > 0) {
1422
1387
  const message = ungovernedDumpMessage(dumped);
1423
1388
  if (args.json) {
@@ -1430,8 +1395,8 @@ async function main() {
1430
1395
  }
1431
1396
  }
1432
1397
  const files = args.changed
1433
- ? filterChangedGovernedFiles(allGovernedFiles, root, changedPaths, normalize)
1434
- : allGovernedFiles;
1398
+ ? governedFilesFromRelativePaths(root, changedPaths, config)
1399
+ : loadGovernedFiles();
1435
1400
 
1436
1401
  // --coverage is a pure glob/report view (no TypeScript resolver), so serve it BEFORE the
1437
1402
  // TS import: the report must work — and exit 0 — even when typescript isn't installed.
@@ -1473,7 +1438,10 @@ async function main() {
1473
1438
  config,
1474
1439
  manifest,
1475
1440
  rules,
1476
- files,
1441
+ // Full-tree callers already have the governed list. Passing it as `files`
1442
+ // used to trigger the --changed scoped path (import-closure + a second
1443
+ // resolveModuleName pass) on every doctor/check (#212). Empty = unbounded.
1444
+ files: args.changed ? files : [],
1477
1445
  ts,
1478
1446
  args,
1479
1447
  });
@@ -1534,7 +1502,7 @@ async function main() {
1534
1502
  configWalkedUp: args.configWalkedUp === true,
1535
1503
  safety, designDelta,
1536
1504
  ts, parseHealth, completeness,
1537
- all: args.all === true,
1505
+ all: args.all === true, requireGates: args.requireGates === true,
1538
1506
  });
1539
1507
  if (designDelta) process.exitCode = !designDelta.complete ? 2 : designDelta.valid ? 0 : 1; return;
1540
1508
  }
@@ -1946,7 +1914,7 @@ async function main() {
1946
1914
  });
1947
1915
  console.log(JSON.stringify({
1948
1916
  ...adapterResult,
1949
- ok,
1917
+ ok, ...localCheckEnvelope(args, root),
1950
1918
  violations: activeViolations.map(enrichViolationWithFixClass),
1951
1919
  suppressedViolations: suppressed.length,
1952
1920
  staleBaselineKeys,
@@ -1966,7 +1934,7 @@ async function main() {
1966
1934
  }, null, 2));
1967
1935
  } else {
1968
1936
  for (const warning of warnings) {
1969
- console.error(`${color.yellow('warning')} ${warning.ruleId} ${warning.message}`);
1937
+ printWarning(warning);
1970
1938
  }
1971
1939
  for (const violation of activeViolations) {
1972
1940
  printViolation(violation);