arkgate 4.8.10 → 4.8.13

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 (50) hide show
  1. package/CHANGELOG.md +74 -2
  2. package/README.md +23 -32
  3. package/bin/ark-check-runtime.mjs +8 -0
  4. package/bin/ark-check.mjs +3 -0
  5. package/bin/lib/adapter-contract-types.mjs +137 -0
  6. package/bin/lib/adapter-contract.mjs +4 -180
  7. package/bin/lib/adapter-finding-refs.mjs +63 -0
  8. package/bin/lib/agent-projection-formatters.mjs +151 -0
  9. package/bin/lib/agent-projection-merge.mjs +148 -0
  10. package/bin/lib/agent-projection-types.mjs +42 -0
  11. package/bin/lib/agent-projection.mjs +3 -309
  12. package/bin/lib/analysis-engine.mjs +4 -4
  13. package/bin/lib/ark-order-doctor.mjs +154 -0
  14. package/bin/lib/ark-order-report.mjs +64 -0
  15. package/bin/lib/ark-order-sensors.mjs +1 -1
  16. package/bin/lib/diagnostic-catalog.mjs +2 -2
  17. package/bin/lib/doctor-advisories.mjs +91 -18
  18. package/bin/lib/doctor-human.mjs +2 -10
  19. package/bin/lib/doctor-plan.mjs +4 -3
  20. package/bin/lib/extra-merge-teeth.mjs +32 -4
  21. package/bin/lib/html-report-advisories.mjs +2 -0
  22. package/bin/lib/html-report-depth.mjs +8 -18
  23. package/bin/lib/html-report.mjs +16 -0
  24. package/bin/lib/project-root.mjs +70 -4
  25. package/bin/lib/remediation.mjs +5 -5
  26. package/bin/lib/rules-under-contract.mjs +14 -0
  27. package/bin/lib/start-preview.mjs +1 -0
  28. package/bin/lib/status-command.mjs +28 -0
  29. package/bin/lib/status-manifest.mjs +23 -0
  30. package/bin/lib/violations.mjs +10 -1
  31. package/dist/{diagnosticCatalog-biferT4R.d.ts → diagnosticCatalog-DA565Lja.d.ts} +22 -39
  32. package/dist/eslint/index.cjs +4 -4
  33. package/dist/eslint/index.js +4 -4
  34. package/dist/index.cjs +31 -31
  35. package/dist/index.d.ts +116 -12
  36. package/dist/index.js +31 -31
  37. package/dist/nestjs/index.cjs +1 -1
  38. package/dist/nestjs/index.js +1 -1
  39. package/dist/runtime/index.cjs +15 -15
  40. package/dist/runtime/index.d.ts +1 -1
  41. package/dist/runtime/index.js +15 -15
  42. package/docs/README.md +5 -4
  43. package/docs/agent-guide.md +2 -1
  44. package/docs/arkorder.md +28 -10
  45. package/docs/package-surface.md +3 -1
  46. package/docs/product-voice.md +9 -8
  47. package/docs/use.md +1 -1
  48. package/package.json +3 -2
  49. package/schemas/ark.status-manifest.schema.json +47 -0
  50. package/server.json +2 -2
package/CHANGELOG.md CHANGED
@@ -3,6 +3,79 @@
3
3
  All notable changes to ArkGate (`arkgate`; formerly `ark-runtime-kernel`) are documented here or
4
4
  in the immutable pre-2.0 archive linked below.
5
5
 
6
+ ## Unreleased
7
+
8
+ ## 4.8.13 — 2026-09-06
9
+
10
+ **Patch** over **4.8.12**. Publish gate: remaining ArkOrder doctor/deny branches so
11
+ `test:confidence` / `release:npm` clears the 81% floor. Tag `v4.8.12` stands;
12
+ this is the ship. **Write. Check. Ship.** **No required config migration.** No
13
+ `schemaVersion` bump. Does not close `K01` / `Z09`. No new skill names. This
14
+ mother `ark.config.json` still does **not** turn `arkOrder` on.
15
+
16
+ **Status: prepared** (tree candidate; npm `latest` remains **4.8.11** until the
17
+ signed-tag provenance workflow succeeds).
18
+
19
+ ### Fixed
20
+ - Confidence coverage: remaining ArkOrder doctor/deny `Next:` and HTML/doctor
21
+ residual paths so the publish job is not blocked by a 0.03-point branch miss.
22
+
23
+ ## 4.8.12 — 2026-09-05
24
+
25
+ **Patch** over **4.8.11**. First-contact honesty for the optional ArkOrder extra.
26
+ Sensors, valve, and `arkgate/order` already shipped on 4.8.11; this projects
27
+ them through doctor / start / status / the check label, in plain words.
28
+ **Write. Check. Ship.** **No required config migration.** No `schemaVersion` bump.
29
+ Does not close `K01` / `Z09`. No new skill names. This mother `ark.config.json`
30
+ still does **not** turn `arkOrder` on.
31
+
32
+ **Status: prepared** (tree candidate; npm `latest` remains **4.8.11** until the
33
+ signed-tag provenance workflow succeeds).
34
+
35
+ ### Added
36
+ - Doctor / HTML / status `arkOrder` section (`notAScore`), same envelope as
37
+ ArkRun. Absence is a silent row. When on: one-breath + named choices + leftover count.
38
+ - Check prints `[ArkOrder]` on `ARKORDER_*` denies (and `[ArkRun]` on
39
+ `ARKRUN_*`) so rewriting `plan` like a seat count reads as clearly as a bad import.
40
+ - `ark start` preview says extras stay off — layers only, they stop bad imports.
41
+
42
+ ### Changed
43
+ - Merge-planes honesty now names enforced ArkOrder skip findings. The stamp
44
+ already claimed this plane; `failMergeWhen` now matches.
45
+ - First-contact copy (README, use, doctor, deny `Next:`, start, gallery) uses
46
+ the one-minute breath. No ξ / Haken / slaving on the first screen.
47
+
48
+ ## 4.8.11 — 2026-09-02
49
+
50
+ **Patch** over **4.8.10**. Nested `--config` with `--root` stays inside that
51
+ tree. Library Shape splits `adapterContract` and `agentProjection` so the AI
52
+ edits one concern at a time. **Write. Check. Ship.** **No required config migration.**
53
+ No `schemaVersion` bump. Does not close `K01` / `Z09`. No new skill names.
54
+
55
+ **Status: published** (on npm `latest`; see `docs/releases/4.8.11.md`).
56
+
57
+ ### Fixed
58
+ - Nested `--config` is a file path: `ark-check --root examples/app --config
59
+ examples/app/ark.config.json` no longer treats the nested config as a
60
+ walk-up filename and no longer scans the invoker `cwd` (`include` is relative
61
+ to `--root`). Basename walk-up still applies when `--config` is
62
+ `ark.config.json`.
63
+ - `doctorResidentWarm` CI budget ceiling is 680ms (~12% headroom after the
64
+ adapterContract split). Still far below one-shot doctor.
65
+
66
+ ### Changed
67
+ - Domain Shape: `adapterContract` types/schema and finding-ref helpers are
68
+ their own modules; the public facade stays under the god-module floors.
69
+ Callers still import `src/domain/adapterContract.ts`. CLI pure helpers follow
70
+ the DF03 split.
71
+ - Domain Shape: `agentProjection` types/markers, body/meta formatters, and
72
+ merge/stamp are their own modules; the public facade re-exports. Callers still
73
+ import `src/domain/agentProjection.ts`. Generated CLI children stay in
74
+ DomainModel so the facade does not import Tooling.
75
+ - Dev-tree pins: `browserslist` 4.28.8 (GHSA-73wf-gq98-2v4g) and `fast-uri`
76
+ 3.1.7 (GHSA-5jgf-p345-68v8 and siblings). These are lock/override pins, not a
77
+ consumer API change.
78
+
6
79
  ## 4.8.10 — 2026-09-02
7
80
 
8
81
  **Patch** over **4.8.9**. Amarilla first-aggregate sensors. Incomplete analysis
@@ -10,8 +83,7 @@ never looks green: an enforced sensor that cannot see its scope is not a pass.
10
83
  **Write. Check. Ship.** **No required config migration.** No `schemaVersion` bump.
11
84
  Does not close `K01` / `Z09`. No new skill names.
12
85
 
13
- **Status: prepared** (tree candidate; npm `latest` remains **4.8.9** until the
14
- signed-tag provenance workflow succeeds).
86
+ **Status: published** (on npm `latest`; see `docs/releases/4.8.10.md`).
15
87
 
16
88
  ### Added
17
89
  - `arkgate report` / `ark report` on the existing dual bins: draft an upstream GitHub issue for
package/README.md CHANGED
@@ -37,15 +37,15 @@ Works with Cursor, Claude, Codex, and Grok.
37
37
 
38
38
  </div>
39
39
 
40
- > **ArkGate 4.8.10** is prepared on this tree; npm `latest` remains **4.8.9**.
40
+ > **ArkGate 4.8.13** is prepared on this tree; npm `latest` remains **4.8.11**.
41
41
  > Write. Check. Ship. Adopted = required GitHub
42
42
  > status running `arkgate-check --strict-merge`, or an explicit `advisory-only` stance.
43
43
  > Status is compact (`arkgate-check --doctor`; `--all` for Details). Optional **ArkRun**
44
44
  > (`arkgate/runtime`) is an in-memory runtime — not Postgres. Optional **ArkOrder**
45
- > (`arkgate/order`) stops the agent rewriting the few slow product decisions as CRUD;
46
- > later pattern change is `proposeRelease` then `apply`.
45
+ > (`arkgate/order`) stops rewriting a big product choice like the billing plan —
46
+ > as if it were a seat count. Change those choices through a valve, not a generic update.
47
47
  > `@arkgate/runtime` is deprecated.
48
- > [4.8.10 prepared](docs/releases/4.8.10.md) · [4.8.9 published](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)
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)
49
49
 
50
50
  ---
51
51
 
@@ -114,7 +114,7 @@ once you adopt. The other three are optional.
114
114
  | **Before merge** | `arkgate-check` as a **required** CI status | Always (ArkGate) |
115
115
  | **ArkRules** | Optional policies *inside* a layer | When you ask |
116
116
  | **ArkRun** | Optional experimental runtime (`arkgate/runtime`) | Off unless you turn it on |
117
- | **ArkOrder** | Stops the agent rewriting the few slow product decisions as CRUD (`arkgate/order`). Valve: first `release()`, later ξ is `proposeRelease` then `apply` | 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 |
118
118
 
119
119
  Layers (who may import whom) always run. ArkRules, ArkRun, and ArkOrder change no
120
120
  inter-layer verdict when absent. Label leftovers **`[Layer]`** vs **`[ArkRules]`** vs
@@ -231,7 +231,7 @@ expectation.
231
231
  | Placement + preflight for multi-file changes | ✅ | ❌ |
232
232
  | Honest governed % + dual plan (edges vs shape) | ✅ | ❌ |
233
233
  | Opt-in intra-layer ArkRules (structure + invariants) | ✅ | ❌ |
234
- | Stops agents rewriting slow product decisions as CRUD (ArkOrder) | ✅ | ❌ |
234
+ | Stops rewriting a big product choice like billing plan (ArkOrder) | ✅ | ❌ |
235
235
  | Incomplete analysis cannot look green | ✅ | varies |
236
236
 
237
237
  ---
@@ -302,31 +302,20 @@ interfaces for production. Details: [docs/production-hardening.md](docs/producti
302
302
 
303
303
  ## Optional ArkOrder
304
304
 
305
- Layers stop a bad import. They do not stop a *legal* import that overwrites the
306
- billing plan.
307
-
308
- If the product can name a few slow decisions in an afternoon — plan, cycle,
309
- tenancy; a clinical protocol; match rules an agent will still ship one PUT
310
- that changes them together with seats and invoices. The write gate stays green
311
- because “what may be the plan” was never a rule.
312
-
313
- **ArkOrder** (`arkgate/order`) is that rule. Off unless you add `arkOrder`.
314
- Name the slow keys (`xiKeys`: plan, protocol, cost-code bound — not `projectId`).
315
- A status you can recompute from data you already have is not a slow decision. Derive it. Do not freeze it.
316
- Posting an invoice is absorbed. Changing plan is `proposeRelease` then `apply`.
317
- `refreshSigma`; ingest residual `absorb | escalate_up | hold` + `reasonCode`;
318
- capacity pack as data; in-memory `ReleaseStore`; `ingestTravelAction`; ArkRun
319
- `decisionTape`. A generic `update` of the plan does not land. A use-case that
320
- PATCHes those keys through Prisma is named. Same npm package.
321
- In-memory. Not durable. Does not replace ArkRun.
322
- **ArkOrder freezes the pattern through a valve. ArkRun is how the residual travels.**
323
-
324
- The billing gallery (`plan` / `cycle` / `tenancy`) lives on GitHub, not in the
325
- npm tarball:
305
+ Layers stop a bad import. ArkOrder stops rewriting a big product choice like
306
+ the billing plan — as if it were a seat count. Change those choices through a
307
+ valve, not a generic update.
308
+
309
+ Off unless you add `arkOrder`. Same npm package (`arkgate/order`). In-memory.
310
+ Not a service. Does not replace ArkGate or ArkRun.
311
+
312
+ Name the few choices (`xiKeys`: plan, cycle, tenancy — not seat counts). First
313
+ freeze is `release()`. Later change is `proposeRelease` then `apply`. A generic
314
+ `update` does not land.
315
+
316
+ The billing gallery lives on GitHub, not in the npm tarball:
326
317
  [examples/arkorder-billing](https://github.com/pedroknigge/arkgate/tree/main/examples/arkorder-billing).
327
- Rename the three keys. Compact starters leave it off. Details:
328
- [ArkOrder](docs/arkorder.md) · [configuration](docs/configuration.md) ·
329
- [package surface](docs/package-surface.md).
318
+ Compact starters leave it off. Details: [ArkOrder](docs/arkorder.md).
330
319
 
331
320
  ---
332
321
 
@@ -342,8 +331,10 @@ Rename the three keys. Compact starters leave it off. Details:
342
331
  | Config · package surface · TS | [configuration](docs/configuration.md) · [package-surface](docs/package-surface.md) · [typescript-support](docs/typescript-support.md) |
343
332
  | Brownfield | [docs/brownfield-adoption.md](docs/brownfield-adoption.md) |
344
333
  | Security | [SECURITY.md](SECURITY.md) |
345
- | Prepared (4.8.10; not published) | [docs/releases/4.8.10.md](docs/releases/4.8.10.md) · [CHANGELOG](CHANGELOG.md) |
346
- | Current published (4.8.9 on npm `latest`) | [docs/releases/4.8.9.md](docs/releases/4.8.9.md) · [CHANGELOG](CHANGELOG.md) |
334
+ | Prepared (4.8.13; not published) | [CHANGELOG](CHANGELOG.md) |
335
+ | Current published (4.8.11 on npm `latest`) | [docs/releases/4.8.11.md](docs/releases/4.8.11.md) · [CHANGELOG](CHANGELOG.md) |
336
+ | Prior published (4.8.10) | [docs/releases/4.8.10.md](docs/releases/4.8.10.md) · [CHANGELOG](CHANGELOG.md) |
337
+ | Prior published (4.8.9) | [docs/releases/4.8.9.md](docs/releases/4.8.9.md) · [CHANGELOG](CHANGELOG.md) |
347
338
  | Prior published (4.8.7) | [docs/releases/4.8.7.md](docs/releases/4.8.7.md) · [CHANGELOG](CHANGELOG.md) |
348
339
  | Prior published (4.8.6) | [docs/releases/4.8.6.md](docs/releases/4.8.6.md) |
349
340
  | Prior published (4.8.5) | [docs/releases/4.8.5.md](docs/releases/4.8.5.md) |
@@ -1066,6 +1066,12 @@ function applyConfigRootWalkUp(args) {
1066
1066
  args.configRoot = effective.configRoot;
1067
1067
  args.configFound = effective.configFound;
1068
1068
  args.writeRoot = effective.writeRoot;
1069
+ // Nested relative --config (`examples/app/ark.config.json`) must stay a resolved
1070
+ // file path under --root. Leaving the relative string makes readConfig join it
1071
+ // again and miss; walk-up used to latch the parent instead.
1072
+ if (effective.configFound && typeof effective.configPath === 'string') {
1073
+ args.config = effective.configPath;
1074
+ }
1069
1075
  if (effective.walkedUp && effective.root !== path.resolve(args.root)) {
1070
1076
  // Read paths (or write + --follow-config-root): adopt discovered config root.
1071
1077
  args.root = effective.root;
@@ -1815,6 +1821,7 @@ async function main() {
1815
1821
  mode: fitness.mode,
1816
1822
  improvementCompass: reportCompass,
1817
1823
  arkRun: designDepth?.arkRun ?? null,
1824
+ arkOrder: designDepth?.arkOrder ?? null,
1818
1825
  }),
1819
1826
  leftoverDesignWork: designDepth?.designFitness?.designWeak === true,
1820
1827
  };
@@ -1857,6 +1864,7 @@ async function main() {
1857
1864
  ? { deepModuleCoach: designDepth.deepModuleCoach }
1858
1865
  : {}),
1859
1866
  ...(designDepth?.arkRun ? { arkRun: designDepth.arkRun } : {}),
1867
+ ...(designDepth?.arkOrder ? { arkOrder: designDepth.arkOrder } : {}),
1860
1868
  },
1861
1869
  };
1862
1870
  const html = args.beginner
package/bin/ark-check.mjs CHANGED
@@ -41,6 +41,9 @@ function residentArgs(argv) {
41
41
  // Same monorepo walk-up as full runtime (NEW-MONOREPO-CWD-WALKUP).
42
42
  const effective = resolveEffectiveProjectRoot(args.root, { configName: args.config });
43
43
  args.configRoot = effective.configRoot;
44
+ if (effective.configFound && typeof effective.configPath === 'string') {
45
+ args.config = effective.configPath;
46
+ }
44
47
  if (effective.walkedUp) {
45
48
  args.root = effective.root;
46
49
  args.configWalkedUp = true;
@@ -0,0 +1,137 @@
1
+ /**
2
+ * GENERATED FILE — do not edit by hand.
3
+ *
4
+ * Canonical algorithm: src/domain/adapterContractTypes.ts
5
+ * Regenerate: node scripts/generate-cli-pure.mjs
6
+ * Drift check: node scripts/generate-cli-pure.mjs --check
7
+ *
8
+ * Pure CLI helper (bin/lib/adapter-contract-types.mjs). Zero Node I/O.
9
+ */
10
+
11
+ /**
12
+ * 1.5 adds stable finding refs on every factory-emitted diagnostic (ACS06):
13
+ * `findingRef`, `targetKey` (baseline-compatible), `docsCodePath`.
14
+ * 1.4 added optional evidence.arkruleId + evidence.arkruleSource (ADR 0012 / AR03).
15
+ */
16
+ export const ARK_ANALYSIS_RESULT_SCHEMA_VERSION = '1.5';
17
+ /** Repo-relative diagnostics docs path (parity with ACS02 diagnostic catalog). */
18
+ export const ADAPTER_DIAGNOSTIC_DOCS_RELATIVE_PATH = 'docs/diagnostics.md';
19
+ export const ARK_ANALYSIS_RESULT_SCHEMA = {
20
+ $schema: 'https://json-schema.org/draft/2020-12/schema',
21
+ $id: 'https://unpkg.com/arkgate@3/schemas/ark.analysis-result.schema.json',
22
+ title: 'ArkGate analysis result',
23
+ type: 'object',
24
+ additionalProperties: false,
25
+ required: [
26
+ 'schemaVersion',
27
+ 'mode',
28
+ 'valid',
29
+ 'completeness',
30
+ 'completenessReasons',
31
+ 'diagnostics',
32
+ ],
33
+ allOf: [
34
+ {
35
+ if: {
36
+ properties: { completeness: { enum: ['partial', 'unavailable'] } },
37
+ required: ['completeness'],
38
+ },
39
+ then: { properties: { valid: { const: false } } },
40
+ },
41
+ {
42
+ if: {
43
+ properties: {
44
+ mode: { const: 'resolved-candidate-facts' },
45
+ completeness: { enum: ['complete', 'partial'] },
46
+ },
47
+ required: ['mode', 'completeness'],
48
+ },
49
+ then: {
50
+ required: ['policyHash', 'resolverIdentity', 'factsHash', 'candidateTreeHash'],
51
+ },
52
+ },
53
+ {
54
+ if: {
55
+ properties: { completeness: { const: 'complete' } },
56
+ required: ['completeness'],
57
+ },
58
+ then: { properties: { completenessReasons: { maxItems: 0 } } },
59
+ else: { properties: { completenessReasons: { minItems: 1 } } },
60
+ },
61
+ ],
62
+ properties: {
63
+ schemaVersion: { const: ARK_ANALYSIS_RESULT_SCHEMA_VERSION },
64
+ mode: { enum: ['lexical-compatibility', 'resolved-candidate-facts'] },
65
+ valid: { type: 'boolean' },
66
+ completeness: { enum: ['complete', 'partial', 'unavailable'] },
67
+ completenessReasons: {
68
+ type: 'array',
69
+ items: {
70
+ type: 'object',
71
+ additionalProperties: false,
72
+ required: ['code', 'message'],
73
+ properties: {
74
+ code: { type: 'string', minLength: 1 },
75
+ message: { type: 'string', minLength: 1 },
76
+ file: { type: 'string', minLength: 1 },
77
+ },
78
+ },
79
+ },
80
+ policyHash: { type: 'string', minLength: 1 },
81
+ resolverIdentity: { type: 'string', minLength: 1 },
82
+ factsHash: { type: 'string', minLength: 1 },
83
+ candidateTreeHash: { type: 'string', minLength: 1 },
84
+ diagnostics: {
85
+ type: 'array',
86
+ items: {
87
+ type: 'object',
88
+ additionalProperties: false,
89
+ required: ['ruleId', 'severity', 'message', 'location', 'evidence'],
90
+ properties: {
91
+ ruleId: { type: 'string', minLength: 1 },
92
+ severity: { enum: ['error', 'warning'] },
93
+ message: { type: 'string', minLength: 1 },
94
+ location: {
95
+ type: 'object',
96
+ additionalProperties: false,
97
+ required: ['file', 'line', 'column'],
98
+ properties: {
99
+ file: { type: 'string', minLength: 1 },
100
+ line: { type: 'integer', minimum: 1 },
101
+ column: { type: 'integer', minimum: 1 },
102
+ },
103
+ },
104
+ evidence: {
105
+ type: 'object',
106
+ additionalProperties: false,
107
+ properties: {
108
+ target: { type: 'string' },
109
+ fromLayer: { type: 'string' },
110
+ toLayer: { type: 'string' },
111
+ typeOnly: { type: 'boolean' },
112
+ targetTypeOnlyExports: { type: 'boolean' },
113
+ sourcePureTypeModule: { type: 'boolean' },
114
+ namedBindingsTypeOnly: { type: 'boolean' },
115
+ portProofEligible: { type: 'boolean' },
116
+ peerIsolation: { type: 'boolean' },
117
+ capability: { type: 'string', minLength: 1 },
118
+ edgeKind: { type: 'string', minLength: 1 },
119
+ arkruleId: { type: 'string', minLength: 1 },
120
+ arkruleSource: { type: 'string', minLength: 1 },
121
+ },
122
+ },
123
+ nextAction: { type: 'string', minLength: 1 },
124
+ /** ACS06 — compact multi-turn id; always derived from targetKey when emitted. */
125
+ findingRef: { type: 'string', minLength: 1, pattern: '^fnv1a-[0-9a-f]{8}$' },
126
+ /**
127
+ * ACS06 — baseline-compatible freeze identity
128
+ * (`ruleId|file|from|to|target` with optional `#N` occurrence suffix).
129
+ */
130
+ targetKey: { type: 'string', minLength: 1 },
131
+ /** ACS06 — package-relative diagnostics anchor path. */
132
+ docsCodePath: { type: 'string', minLength: 1 },
133
+ },
134
+ },
135
+ },
136
+ },
137
+ };
@@ -9,73 +9,16 @@
9
9
  */
10
10
 
11
11
  import { deterministicNextAction } from './remediation.mjs';
12
- /** Versioned public result contract shared by every ArkGate enforcement adapter. */
13
- /**
14
- * 1.5 adds stable finding refs on every factory-emitted diagnostic (ACS06):
15
- * `findingRef`, `targetKey` (baseline-compatible), `docsCodePath`.
16
- * 1.4 added optional evidence.arkruleId + evidence.arkruleSource (ADR 0012 / AR03).
17
- */
18
- export const ARK_ANALYSIS_RESULT_SCHEMA_VERSION = '1.5';
19
- /** Repo-relative diagnostics docs path (parity with ACS02 diagnostic catalog). */
20
- export const ADAPTER_DIAGNOSTIC_DOCS_RELATIVE_PATH = 'docs/diagnostics.md';
12
+ import { ARK_ANALYSIS_RESULT_SCHEMA_VERSION, } from './adapter-contract-types.mjs';
13
+ import { adapterDocsCodePath, adapterFindingOccurrenceTargetKeys, adapterFindingRefFromTargetKey, adapterFindingTargetKey, } from './adapter-finding-refs.mjs';
14
+ export { ADAPTER_DIAGNOSTIC_DOCS_RELATIVE_PATH, ARK_ANALYSIS_RESULT_SCHEMA, ARK_ANALYSIS_RESULT_SCHEMA_VERSION, } from './adapter-contract-types.mjs';
15
+ export { adapterDocsCodePath, adapterFindingOccurrenceTargetKeys, adapterFindingRefFromTargetKey, adapterFindingTargetKey, } from './adapter-finding-refs.mjs';
21
16
  function text(value) {
22
17
  return typeof value === 'string' && value.length > 0 ? value : undefined;
23
18
  }
24
19
  function positiveInteger(value, fallback) {
25
20
  return Number.isInteger(value) && Number(value) > 0 ? Number(value) : fallback;
26
21
  }
27
- /**
28
- * Baseline-compatible target key for a violation input.
29
- * Field order and empty-string fallbacks **must** match `baselineKey` in
30
- * `baselineKey.ts` — parity tests guard this so finding refs never orphan freezes.
31
- *
32
- * Note: uses raw ruleId/file strings (including empty) the same way baseline does;
33
- * display `ruleId` / `location.file` may still normalize to ARK_UNKNOWN / `<unknown>`.
34
- */
35
- export function adapterFindingTargetKey(violation) {
36
- const ruleId = typeof violation.ruleId === 'string'
37
- ? violation.ruleId
38
- : typeof violation.code === 'string'
39
- ? violation.code
40
- : undefined;
41
- const file = typeof violation.file === 'string' ? violation.file : undefined;
42
- const fromLayer = typeof violation.fromLayer === 'string' ? violation.fromLayer : undefined;
43
- const toLayer = typeof violation.toLayer === 'string' ? violation.toLayer : undefined;
44
- const target = typeof violation.target === 'string' ? violation.target : undefined;
45
- return [
46
- ruleId,
47
- file,
48
- fromLayer ?? '',
49
- toLayer ?? '',
50
- target ?? '',
51
- ].join('|');
52
- }
53
- /**
54
- * Occurrence-aware target keys for a violation list (parity with baselineOccurrenceKeys).
55
- * First occurrence keeps the historical base key; duplicates get `#N`.
56
- */
57
- export function adapterFindingOccurrenceTargetKeys(violations) {
58
- const counts = new Map();
59
- return violations.map((violation) => {
60
- const base = adapterFindingTargetKey(violation);
61
- const occurrence = (counts.get(base) ?? 0) + 1;
62
- counts.set(base, occurrence);
63
- return occurrence === 1 ? base : `${base}#${occurrence}`;
64
- });
65
- }
66
- /** FNV-1a finding ref from a baseline-compatible targetKey (not a security hash). */
67
- export function adapterFindingRefFromTargetKey(targetKey) {
68
- let hash = 0x811c9dc5;
69
- for (let index = 0; index < targetKey.length; index += 1) {
70
- hash ^= targetKey.charCodeAt(index);
71
- hash = Math.imul(hash, 0x01000193);
72
- }
73
- return `fnv1a-${(hash >>> 0).toString(16).padStart(8, '0')}`;
74
- }
75
- /** Package-relative docs path with fragment for a public ruleId. */
76
- export function adapterDocsCodePath(ruleId) {
77
- return `${ADAPTER_DIAGNOSTIC_DOCS_RELATIVE_PATH}#${ruleId}`;
78
- }
79
22
  function nextActionForDiagnostic(ruleId, evidence, violation) {
80
23
  return deterministicNextAction({
81
24
  ruleId,
@@ -240,122 +183,3 @@ export function createAdapterResult(input) {
240
183
  }
241
184
  return { ...base, mode, valid: false, completeness, ...evidence };
242
185
  }
243
- export const ARK_ANALYSIS_RESULT_SCHEMA = {
244
- $schema: 'https://json-schema.org/draft/2020-12/schema',
245
- $id: 'https://unpkg.com/arkgate@3/schemas/ark.analysis-result.schema.json',
246
- title: 'ArkGate analysis result',
247
- type: 'object',
248
- additionalProperties: false,
249
- required: [
250
- 'schemaVersion',
251
- 'mode',
252
- 'valid',
253
- 'completeness',
254
- 'completenessReasons',
255
- 'diagnostics',
256
- ],
257
- allOf: [
258
- {
259
- if: {
260
- properties: { completeness: { enum: ['partial', 'unavailable'] } },
261
- required: ['completeness'],
262
- },
263
- then: { properties: { valid: { const: false } } },
264
- },
265
- {
266
- if: {
267
- properties: {
268
- mode: { const: 'resolved-candidate-facts' },
269
- completeness: { enum: ['complete', 'partial'] },
270
- },
271
- required: ['mode', 'completeness'],
272
- },
273
- then: {
274
- required: ['policyHash', 'resolverIdentity', 'factsHash', 'candidateTreeHash'],
275
- },
276
- },
277
- {
278
- if: {
279
- properties: { completeness: { const: 'complete' } },
280
- required: ['completeness'],
281
- },
282
- then: { properties: { completenessReasons: { maxItems: 0 } } },
283
- else: { properties: { completenessReasons: { minItems: 1 } } },
284
- },
285
- ],
286
- properties: {
287
- schemaVersion: { const: ARK_ANALYSIS_RESULT_SCHEMA_VERSION },
288
- mode: { enum: ['lexical-compatibility', 'resolved-candidate-facts'] },
289
- valid: { type: 'boolean' },
290
- completeness: { enum: ['complete', 'partial', 'unavailable'] },
291
- completenessReasons: {
292
- type: 'array',
293
- items: {
294
- type: 'object',
295
- additionalProperties: false,
296
- required: ['code', 'message'],
297
- properties: {
298
- code: { type: 'string', minLength: 1 },
299
- message: { type: 'string', minLength: 1 },
300
- file: { type: 'string', minLength: 1 },
301
- },
302
- },
303
- },
304
- policyHash: { type: 'string', minLength: 1 },
305
- resolverIdentity: { type: 'string', minLength: 1 },
306
- factsHash: { type: 'string', minLength: 1 },
307
- candidateTreeHash: { type: 'string', minLength: 1 },
308
- diagnostics: {
309
- type: 'array',
310
- items: {
311
- type: 'object',
312
- additionalProperties: false,
313
- required: ['ruleId', 'severity', 'message', 'location', 'evidence'],
314
- properties: {
315
- ruleId: { type: 'string', minLength: 1 },
316
- severity: { enum: ['error', 'warning'] },
317
- message: { type: 'string', minLength: 1 },
318
- location: {
319
- type: 'object',
320
- additionalProperties: false,
321
- required: ['file', 'line', 'column'],
322
- properties: {
323
- file: { type: 'string', minLength: 1 },
324
- line: { type: 'integer', minimum: 1 },
325
- column: { type: 'integer', minimum: 1 },
326
- },
327
- },
328
- evidence: {
329
- type: 'object',
330
- additionalProperties: false,
331
- properties: {
332
- target: { type: 'string' },
333
- fromLayer: { type: 'string' },
334
- toLayer: { type: 'string' },
335
- typeOnly: { type: 'boolean' },
336
- targetTypeOnlyExports: { type: 'boolean' },
337
- sourcePureTypeModule: { type: 'boolean' },
338
- namedBindingsTypeOnly: { type: 'boolean' },
339
- portProofEligible: { type: 'boolean' },
340
- peerIsolation: { type: 'boolean' },
341
- capability: { type: 'string', minLength: 1 },
342
- edgeKind: { type: 'string', minLength: 1 },
343
- arkruleId: { type: 'string', minLength: 1 },
344
- arkruleSource: { type: 'string', minLength: 1 },
345
- },
346
- },
347
- nextAction: { type: 'string', minLength: 1 },
348
- /** ACS06 — compact multi-turn id; always derived from targetKey when emitted. */
349
- findingRef: { type: 'string', minLength: 1, pattern: '^fnv1a-[0-9a-f]{8}$' },
350
- /**
351
- * ACS06 — baseline-compatible freeze identity
352
- * (`ruleId|file|from|to|target` with optional `#N` occurrence suffix).
353
- */
354
- targetKey: { type: 'string', minLength: 1 },
355
- /** ACS06 — package-relative diagnostics anchor path. */
356
- docsCodePath: { type: 'string', minLength: 1 },
357
- },
358
- },
359
- },
360
- },
361
- };
@@ -0,0 +1,63 @@
1
+ /**
2
+ * GENERATED FILE — do not edit by hand.
3
+ *
4
+ * Canonical algorithm: src/domain/adapterFindingRefs.ts
5
+ * Regenerate: node scripts/generate-cli-pure.mjs
6
+ * Drift check: node scripts/generate-cli-pure.mjs --check
7
+ *
8
+ * Pure CLI helper (bin/lib/adapter-finding-refs.mjs). Zero Node I/O.
9
+ */
10
+
11
+ import { ADAPTER_DIAGNOSTIC_DOCS_RELATIVE_PATH, } from './adapter-contract-types.mjs';
12
+ /**
13
+ * Baseline-compatible target key for a violation input.
14
+ * Field order and empty-string fallbacks **must** match `baselineKey` in
15
+ * `baselineKey.ts` — parity tests guard this so finding refs never orphan freezes.
16
+ *
17
+ * Note: uses raw ruleId/file strings (including empty) the same way baseline does;
18
+ * display `ruleId` / `location.file` may still normalize to ARK_UNKNOWN / `<unknown>`.
19
+ */
20
+ export function adapterFindingTargetKey(violation) {
21
+ const ruleId = typeof violation.ruleId === 'string'
22
+ ? violation.ruleId
23
+ : typeof violation.code === 'string'
24
+ ? violation.code
25
+ : undefined;
26
+ const file = typeof violation.file === 'string' ? violation.file : undefined;
27
+ const fromLayer = typeof violation.fromLayer === 'string' ? violation.fromLayer : undefined;
28
+ const toLayer = typeof violation.toLayer === 'string' ? violation.toLayer : undefined;
29
+ const target = typeof violation.target === 'string' ? violation.target : undefined;
30
+ return [
31
+ ruleId,
32
+ file,
33
+ fromLayer ?? '',
34
+ toLayer ?? '',
35
+ target ?? '',
36
+ ].join('|');
37
+ }
38
+ /**
39
+ * Occurrence-aware target keys for a violation list (parity with baselineOccurrenceKeys).
40
+ * First occurrence keeps the historical base key; duplicates get `#N`.
41
+ */
42
+ export function adapterFindingOccurrenceTargetKeys(violations) {
43
+ const counts = new Map();
44
+ return violations.map((violation) => {
45
+ const base = adapterFindingTargetKey(violation);
46
+ const occurrence = (counts.get(base) ?? 0) + 1;
47
+ counts.set(base, occurrence);
48
+ return occurrence === 1 ? base : `${base}#${occurrence}`;
49
+ });
50
+ }
51
+ /** FNV-1a finding ref from a baseline-compatible targetKey (not a security hash). */
52
+ export function adapterFindingRefFromTargetKey(targetKey) {
53
+ let hash = 0x811c9dc5;
54
+ for (let index = 0; index < targetKey.length; index += 1) {
55
+ hash ^= targetKey.charCodeAt(index);
56
+ hash = Math.imul(hash, 0x01000193);
57
+ }
58
+ return `fnv1a-${(hash >>> 0).toString(16).padStart(8, '0')}`;
59
+ }
60
+ /** Package-relative docs path with fragment for a public ruleId. */
61
+ export function adapterDocsCodePath(ruleId) {
62
+ return `${ADAPTER_DIAGNOSTIC_DOCS_RELATIVE_PATH}#${ruleId}`;
63
+ }