arkgate 3.4.0 → 3.5.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.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,41 @@ All notable changes to ArkGate (`arkgate`; formerly `ark-runtime-kernel`) are do
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 3.5.0 — 2026-07-16
8
+
9
+ Field-feedback release (Phase X, from the amarilla adoption session): the HTML report reaches
10
+ parity with the doctor and stays there by an executable rule, contract-smell acknowledgments gain
11
+ a lifecycle so migration acks cannot fossilize, and the lateral-adapter smell stops firing on a
12
+ family's own infrastructure base. Everything remains **advisory** — no verdict, `designFitness`,
13
+ or gate behavior changes. **No breaking** CLI or `ark.config.json` changes. **No gate weaken.**
14
+
15
+ ### Added
16
+
17
+ - **Report parity (X01):** `ark-check --report` now renders every doctor advisory — contract
18
+ health (smells with evidence/fix, acknowledgment honesty, invalid-sidecar warning), governance
19
+ weight, ambient state (idle/clean/findings with honest overflow), and capability-wall badges
20
+ (`pure` / `walls: …`) in the layers table. The rule is **executable**: `reportParity.test.ts`
21
+ enumerates the advisory keys `computeDoctorAdvisories` returns and fails CI when any key lacks
22
+ a `data-advisory` section — the report can never silently fall behind the product again.
23
+ - **Acknowledgment lifecycle (X02):** a contract-smell ack may carry an optional `reviewBy`
24
+ (`YYYY-MM-DD`, strict round-trip validation). Past that date the ack **stops applying** and the
25
+ smell returns with `(ack expired …)` annotated evidence; a re-ack with a fresh date wins over a
26
+ dead entry, and once any dated ack exists for an edge the dated entries govern — a leftover
27
+ undated duplicate cannot resurrect an expired exception.
28
+ Undated acks keep applying (backward compatible) but are counted and surfaced —
29
+ doctor line, report note — even when every smell is suppressed. Malformed dates never apply
30
+ (fail-loud, like a sloppy edge); non-string `reviewBy` invalidates the file. Doctor JSON gains
31
+ `contractHealth.ackLifecycle` (`{ undated, malformed, expiredCount, expired[] }`).
32
+
33
+ ### Changed
34
+
35
+ - **Lateral-adapter smell (X03):** `contract-lateral-adapter-allow` no longer fires when an
36
+ adapter layer reaches its **own family's infra base** (same leading name token and every
37
+ remaining target token an infra word — `Infra`/`Base`/`Core`/`Shared`/`Common`/`Kernel`/
38
+ `Platform`/`Foundation` — e.g. `PaymentsAdapters -> PaymentsInfra`; `PaymentsCoreAdapters` is
39
+ still a sibling). Cross-family edges, same-family non-infra siblings, and the reverse
40
+ direction (base → member) still fire.
41
+
7
42
  ## 3.4.0 — 2026-07-16
8
43
 
9
44
  Understandable execution, second slice (Phase U: U04–U07): the capability evidence shipped in
package/bin/ark-check.mjs CHANGED
@@ -59,6 +59,7 @@ import {
59
59
  reportsDir,
60
60
  readJsonSafe,
61
61
  } from './lib/html-report.mjs';
62
+ import { computeDoctorAdvisories } from './lib/doctor-advisories.mjs';
62
63
  import { buildReportDepthPayload } from './lib/html-report-depth.mjs';
63
64
  import { shouldOpenHtmlReport, openHtmlInBrowser } from './lib/open-html.mjs';
64
65
  import {
@@ -101,7 +102,6 @@ import { runArchitectureScan } from './lib/architecture-scan.mjs';
101
102
  import { validateHardWriteRequest } from './lib/enforcement-profiles.mjs';
102
103
  import { analyzePolicyTransition } from './lib/policy-delta-io.mjs';
103
104
 
104
-
105
105
  function parseArgs(argv) {
106
106
  const args = {
107
107
  root: process.cwd(),
@@ -1320,6 +1320,7 @@ async function main() {
1320
1320
  originJustCreated: !existingOrigin,
1321
1321
  adoption: adoptionForReport,
1322
1322
  designDepth,
1323
+ advisories: computeDoctorAdvisories(root, config, coverage, rules, files, ts),
1323
1324
  };
1324
1325
  const html = args.beginner
1325
1326
  ? renderBeginnerHtmlReport(reportPayload)
@@ -11,6 +11,12 @@
11
11
  * Q03 golden-pattern precedent) so the versioned config contract is untouched.
12
12
  * A malformed ack file (or a malformed edge inside it) never suppresses a smell.
13
13
  *
14
+ * X02 — acks have a lifecycle: an optional `reviewBy` (YYYY-MM-DD) marks when a
15
+ * deliberate exception must be re-reviewed. Past that date the ack stops
16
+ * applying and the smell returns annotated — migration acks cannot fossilize.
17
+ * Undated acks keep applying (backward compatible) but are counted and
18
+ * reported so they can be given a date.
19
+ *
14
20
  * Known limit (documented, deliberate): layer roles are inferred from layer NAMES
15
21
  * via substring heuristics — a name like "Auditorium" reads as audit-ish. The
16
22
  * surface is advisory, so a miss costs a warning line, never a verdict.
@@ -49,6 +55,29 @@ const MAX_MESSAGE_EDGES = 6;
49
55
  const PERIPHERAL_LAYER_RE = /observab|audit|telemetry|monitor|logging|metric|tracing/i;
50
56
  const CORE_TARGET_RE = /application|orchestr|persist|repositor/i;
51
57
  const ADAPTER_LAYER_RE = /adapter|persist|integrat|infra|gateway/i;
58
+ const FAMILY_INFRA_RE = /^(infra(structure)?|base|core|shared|common|kernel|platform|foundation)$/i;
59
+
60
+ /** Split a layer name into words: camelCase boundaries, digits, delimiters. */
61
+ function nameTokens(name) {
62
+ return String(name).match(/[A-Z]?[a-z]+|[A-Z]+(?![a-z])|\d+/g) ?? [];
63
+ }
64
+
65
+ /**
66
+ * X03 — an adapter reaching its OWN family's infrastructure base is not a
67
+ * lateral peer: same leading family token and EVERY remaining target token
68
+ * reads as an infra base (Infra/Base/Core/Shared/…) — `PaymentsCoreAdapters`
69
+ * is still a sibling, not a base. Field origin: amarilla, where
70
+ * `<Family>Adapters -> <Family>Infra` fired as adapter-to-adapter.
71
+ * Name heuristic like the role regexes above — a miss costs a warning line.
72
+ */
73
+ function isFamilyInfrastructureEdge(from, to) {
74
+ const fromTokens = nameTokens(from);
75
+ const toTokens = nameTokens(to);
76
+ if (fromTokens.length === 0 || toTokens.length < 2) return false;
77
+ const family = toTokens[0];
78
+ if (family.length < 2 || family.toLowerCase() !== fromTokens[0].toLowerCase()) return false;
79
+ return toTokens.slice(1).every((t) => FAMILY_INFRA_RE.test(t));
80
+ }
52
81
 
53
82
  /** Collision-safe internal key for a directed edge (layer names are arbitrary strings). */
54
83
  function directedKey(from, to) {
@@ -100,9 +129,12 @@ export function loadContractSmellAcks(root) {
100
129
  typeof a === 'object' &&
101
130
  typeof a.id === 'string' &&
102
131
  typeof a.edge === 'string' &&
103
- a.edge.trim().length > 0
132
+ a.edge.trim().length > 0 &&
133
+ (a.reviewBy === undefined || typeof a.reviewBy === 'string')
104
134
  );
105
- if (!wellFormed) return invalid('every ack needs string id and non-empty string edge');
135
+ if (!wellFormed) {
136
+ return invalid('every ack needs string id, non-empty string edge, and string reviewBy when present');
137
+ }
106
138
  return { path: relPath, exists: true, acks };
107
139
  }
108
140
 
@@ -121,10 +153,44 @@ function normalizeAckEdge(id, edge) {
121
153
  return raw;
122
154
  }
123
155
 
124
- function isAcknowledged(ackState, id, canonicalEdge) {
125
- if (!ackState || ackState.invalid || !Array.isArray(ackState.acks)) return false;
126
- if (canonicalEdge == null) return false;
127
- return ackState.acks.some((a) => a.id === id && normalizeAckEdge(id, a.edge) === canonicalEdge);
156
+ /**
157
+ * X02 lifecycle status of one ack entry. `undated` and `current` apply
158
+ * (suppress); `expired` and `malformed` do not — fail-loud like a sloppy edge.
159
+ * Strict round-trip date check: `2026-02-30` must not pass as valid.
160
+ */
161
+ function ackLifecycleStatus(ack, today) {
162
+ const rb = ack.reviewBy;
163
+ if (rb === undefined || rb === null) return 'undated';
164
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(rb)) return 'malformed';
165
+ const parsed = new Date(`${rb}T00:00:00.000Z`);
166
+ if (Number.isNaN(parsed.getTime()) || parsed.toISOString().slice(0, 10) !== rb) {
167
+ return 'malformed';
168
+ }
169
+ return typeof today === 'string' && rb < today ? 'expired' : 'current';
170
+ }
171
+
172
+ /**
173
+ * Match a detected edge against the ack sidecar with lifecycle applied.
174
+ * Returns how the edge resolves: `none` (no matching ack), an applying status
175
+ * (`current` | `undated`), or a non-applying one (`expired` | `malformed`,
176
+ * with the reviewBy that failed). Once ANY dated ack exists for the edge, the
177
+ * dated entries govern — a leftover undated duplicate cannot resurrect an
178
+ * expired exception. Among dated entries a fresh re-ack wins over a dead one.
179
+ */
180
+ function resolveAck(ackState, id, canonicalEdge, today) {
181
+ if (!ackState || ackState.invalid || !Array.isArray(ackState.acks)) return { status: 'none' };
182
+ if (canonicalEdge == null) return { status: 'none' };
183
+ let dead = null;
184
+ let undated = false;
185
+ for (const a of ackState.acks) {
186
+ if (a.id !== id || normalizeAckEdge(id, a.edge) !== canonicalEdge) continue;
187
+ const status = ackLifecycleStatus(a, today);
188
+ if (status === 'current') return { status };
189
+ if (status === 'undated') undated = true;
190
+ else dead ??= { status, reviewBy: a.reviewBy };
191
+ }
192
+ if (dead) return dead;
193
+ return undated ? { status: 'undated' } : { status: 'none' };
128
194
  }
129
195
 
130
196
  /**
@@ -140,7 +206,8 @@ export function analyzeContractSmells(
140
206
  config,
141
207
  coverage = null,
142
208
  ackState = { exists: false, acks: [] },
143
- effectiveRules = null
209
+ effectiveRules = null,
210
+ today = null
144
211
  ) {
145
212
  const layers = Array.isArray(config?.layers) ? config.layers : [];
146
213
  const rules = wellFormedRules(config, effectiveRules);
@@ -188,10 +255,12 @@ export function analyzeContractSmells(
188
255
  }
189
256
  }
190
257
 
191
- // 3) Lateral adapter-to-adapter explicit allows. Skip only edges the peripheral
192
- // sensor already flagged (peripheral source AND core-ish target).
258
+ // 3) Lateral adapter-to-adapter explicit allows. Skip edges the peripheral
259
+ // sensor already flagged (peripheral source AND core-ish target), and
260
+ // X03: an adapter reaching its own family's infra base is not a peer.
193
261
  for (const r of explicitAllows) {
194
262
  if (PERIPHERAL_LAYER_RE.test(r.from) && CORE_TARGET_RE.test(r.to)) continue;
263
+ if (isFamilyInfrastructureEdge(r.from, r.to)) continue;
195
264
  if (ADAPTER_LAYER_RE.test(r.from) && ADAPTER_LAYER_RE.test(r.to)) {
196
265
  add(
197
266
  'contract-lateral-adapter-allow',
@@ -223,6 +292,7 @@ export function analyzeContractSmells(
223
292
 
224
293
  const smells = [];
225
294
  let matchedAcks = 0;
295
+ const ackLifecycle = { undated: 0, malformed: 0, expired: [] };
226
296
  for (const id of CONTRACT_SMELL_IDS) {
227
297
  const entries = findings[id];
228
298
  if (!entries || entries.length === 0) continue;
@@ -232,8 +302,20 @@ export function analyzeContractSmells(
232
302
  for (const entry of entries) {
233
303
  if (seenDetail.has(entry.detail)) continue;
234
304
  seenDetail.add(entry.detail);
235
- if (isAcknowledged(ackState, id, entry.edge)) {
305
+ const ack = resolveAck(ackState, id, entry.edge, today);
306
+ if (ack.status === 'current' || ack.status === 'undated') {
236
307
  acknowledgedEdges += 1;
308
+ if (ack.status === 'undated') ackLifecycle.undated += 1;
309
+ continue;
310
+ }
311
+ if (ack.status === 'expired') {
312
+ ackLifecycle.expired.push({ id, edge: entry.edge, reviewBy: ack.reviewBy });
313
+ kept.push({ ...entry, detail: `${entry.detail} (ack expired ${ack.reviewBy})` });
314
+ continue;
315
+ }
316
+ if (ack.status === 'malformed') {
317
+ ackLifecycle.malformed += 1;
318
+ kept.push({ ...entry, detail: `${entry.detail} (ack review-by malformed)` });
237
319
  continue;
238
320
  }
239
321
  kept.push(entry);
@@ -255,20 +337,23 @@ export function analyzeContractSmells(
255
337
  acknowledgedEdges,
256
338
  });
257
339
  }
258
- return { smells, matchedAcks };
340
+ return { smells, matchedAcks, ackLifecycle };
259
341
  }
260
342
 
261
343
  /**
262
344
  * Detect contract smells (compat wrapper over analyzeContractSmells).
345
+ * Defaults `today` to the real clock so expired acks stop applying on every
346
+ * public path, not only through the doctor; pass `null` to disable expiry.
263
347
  * @returns {Array<{id: string, severity: 'warn', message: string, outcome: string, evidence: string[], fix: string, acknowledgedEdges: number}>}
264
348
  */
265
349
  export function detectContractSmells(
266
350
  config,
267
351
  coverage = null,
268
352
  ackState = { exists: false, acks: [] },
269
- effectiveRules = null
353
+ effectiveRules = null,
354
+ today = todayUtc()
270
355
  ) {
271
- return analyzeContractSmells(config, coverage, ackState, effectiveRules).smells;
356
+ return analyzeContractSmells(config, coverage, ackState, effectiveRules, today).smells;
272
357
  }
273
358
 
274
359
  function messageFor(id, entries) {
@@ -399,6 +484,11 @@ export function computeGovernanceWeight(config, coverage = null, effectiveRules
399
484
  };
400
485
  }
401
486
 
487
+ /** Today as UTC YYYY-MM-DD — the only clock read; tests inject `today` instead. */
488
+ function todayUtc() {
489
+ return new Date().toISOString().slice(0, 10);
490
+ }
491
+
402
492
  /**
403
493
  * One-call compute for doctor: acks + smells + governance weight + JSON-ready summary.
404
494
  * `rules` should be the rules actually in force (manifest-aware callers pass them).
@@ -407,12 +497,19 @@ export function computeGovernanceWeight(config, coverage = null, effectiveRules
407
497
  * @param {object} config
408
498
  * @param {object|null} coverage
409
499
  * @param {object[]|null} [rules]
500
+ * @param {string} [today] UTC YYYY-MM-DD for ack lifecycle; defaults to the real clock
410
501
  */
411
- export function computeContractHealth(root, config, coverage, rules = null) {
502
+ export function computeContractHealth(root, config, coverage, rules = null, today = todayUtc()) {
412
503
  const ackState = loadContractSmellAcks(root);
413
- const { smells, matchedAcks } = analyzeContractSmells(config, coverage, ackState, rules);
504
+ const { smells, matchedAcks, ackLifecycle } = analyzeContractSmells(
505
+ config,
506
+ coverage,
507
+ ackState,
508
+ rules,
509
+ today
510
+ );
414
511
  return {
415
- ...summarizeContractHealth(smells, ackState, matchedAcks),
512
+ ...summarizeContractHealth(smells, ackState, matchedAcks, ackLifecycle),
416
513
  governanceWeight: computeGovernanceWeight(config, coverage, rules),
417
514
  smells,
418
515
  };
@@ -447,7 +544,13 @@ export function formatContractHealthLines(smells, health) {
447
544
  const list = smells ?? [];
448
545
  const gw = health?.governanceWeight;
449
546
  const weightNoteworthy = gw?.weight === 'heavy' || gw?.weight === 'light';
450
- if (list.length === 0 && !health?.ackFile?.invalid && !weightNoteworthy) return rows;
547
+ const lc = health?.ackLifecycle;
548
+ // Undated acks must surface even when every smell is suppressed — that is
549
+ // exactly the fossilization case X02 exists to catch.
550
+ const lifecycleNoteworthy = (lc?.undated ?? 0) > 0;
551
+ if (list.length === 0 && !health?.ackFile?.invalid && !weightNoteworthy && !lifecycleNoteworthy) {
552
+ return rows;
553
+ }
451
554
  if (health?.ackFile?.invalid) {
452
555
  rows.push({
453
556
  mark: 'warn',
@@ -470,6 +573,24 @@ export function formatContractHealthLines(smells, health) {
470
573
  if ((health?.acknowledged ?? 0) > 0) {
471
574
  rows.push({ mark: 'dim', text: `acknowledged edges applied: ${health.acknowledged}` });
472
575
  }
576
+ if ((lc?.expiredCount ?? 0) > 0) {
577
+ rows.push({
578
+ mark: 'warn',
579
+ text: `${lc.expiredCount} acknowledgment(s) past review-by — the smell is active again; re-review the edge and re-ack with a new date, or fix the contract.`,
580
+ });
581
+ }
582
+ if ((lc?.malformed ?? 0) > 0) {
583
+ rows.push({
584
+ mark: 'warn',
585
+ text: `${lc.malformed} acknowledgment(s) have a malformed review-by (expected YYYY-MM-DD) — they are ignored, not silently applied.`,
586
+ });
587
+ }
588
+ if ((lc?.undated ?? 0) > 0) {
589
+ rows.push({
590
+ mark: 'dim',
591
+ text: `${lc.undated} applied acknowledgment(s) have no review-by date — add one so migration acks cannot fossilize.`,
592
+ });
593
+ }
473
594
  if (weightNoteworthy) {
474
595
  rows.push({
475
596
  mark: 'warn',
@@ -487,18 +608,33 @@ export function formatContractHealthLines(smells, health) {
487
608
  /**
488
609
  * Contract-health summary for doctor JSON / human output. Advisory only.
489
610
  * `acknowledged` counts ack entries that MATCHED a detected edge (stale acks count 0).
611
+ * X02 — `ackLifecycle` reports how applied acks age: `undated` applied without
612
+ * a review-by, `expired` past it (no longer applied), `malformed` bad dates.
490
613
  *
491
614
  * @param {ReturnType<typeof detectContractSmells>} smells
492
615
  * @param {ReturnType<typeof loadContractSmellAcks>} ackState
493
616
  * @param {number} [matchedAcks]
617
+ * @param {{ undated: number, malformed: number, expired: Array<{id: string, edge: string, reviewBy: string}> }} [ackLifecycle]
494
618
  */
495
- export function summarizeContractHealth(smells, ackState = { exists: false, acks: [] }, matchedAcks = 0) {
619
+ export function summarizeContractHealth(
620
+ smells,
621
+ ackState = { exists: false, acks: [] },
622
+ matchedAcks = 0,
623
+ ackLifecycle = { undated: 0, malformed: 0, expired: [] }
624
+ ) {
496
625
  const list = Array.isArray(smells) ? smells : [];
626
+ const expired = Array.isArray(ackLifecycle?.expired) ? ackLifecycle.expired : [];
497
627
  return {
498
628
  status: list.length > 0 ? 'contract-smells' : 'ok',
499
629
  smellCount: list.length,
500
630
  ids: list.map((s) => s.id),
501
631
  acknowledged: ackState?.invalid ? 0 : matchedAcks,
632
+ ackLifecycle: {
633
+ undated: ackLifecycle?.undated ?? 0,
634
+ malformed: ackLifecycle?.malformed ?? 0,
635
+ expiredCount: expired.length,
636
+ expired: expired.slice(0, MAX_EVIDENCE),
637
+ },
502
638
  advisory: true,
503
639
  label:
504
640
  list.length > 0
@@ -0,0 +1,149 @@
1
+ /**
2
+ * X01 — advisory sections for the HTML report (report parity with doctor).
3
+ *
4
+ * The report is a RENDERING of doctor truth: every advisory surface the doctor
5
+ * emits must have a section here, marked with `data-advisory="<key>"`. The
6
+ * parity guard (reportParity.test.ts) enumerates the doctor's advisory keys
7
+ * and fails when one has no section — that is the standing rule that keeps
8
+ * this report from falling behind the product again.
9
+ */
10
+ import { effectiveCapabilityDeny } from './analysis-engine.mjs';
11
+
12
+ // htmlEscape is injected by the caller (html-report.mjs) — importing it back
13
+ // would be a dependency cycle, and the repo's own gate blocks that. The
14
+ // fallback still escapes so a caller that forgets to inject cannot ship XSS.
15
+ let esc = (v) =>
16
+ String(v).replace(/[&<>"']/g, (c) => `&#${c.charCodeAt(0)};`);
17
+
18
+ /** Layer badges for the layers table: purity walls next to forbidden globals. */
19
+ export function capabilityBadgesFor(layer, escape = esc) {
20
+ const previous = esc;
21
+ esc = escape;
22
+ try {
23
+ return badgesInner(layer);
24
+ } finally {
25
+ esc = previous;
26
+ }
27
+ }
28
+
29
+ function badgesInner(layer) {
30
+ const deny = effectiveCapabilityDeny(layer ?? {});
31
+ if (deny.length === 0) return '';
32
+ if (layer?.pure === true) {
33
+ return '<span class="tag warn" title="pure: true — all seven effect capabilities denied (ADR 0009)">pure</span>';
34
+ }
35
+ return `<span class="tag warn" title="capabilities.deny (ADR 0009)">walls: ${deny.map(esc).join(', ')}</span>`;
36
+ }
37
+
38
+ function governanceWeightHtml(gw) {
39
+ if (!gw || gw.weight === 'unknown') {
40
+ return '<p class="muted">Governance weight: unknown (no governed files or layers).</p>';
41
+ }
42
+ const label = `${gw.weight} — ${gw.declaredLayers} layer(s), ${gw.rules} rule(s), ${gw.governedFiles} governed file(s)` +
43
+ (gw.filesPerLayer != null ? ` (${gw.filesPerLayer} files/layer · ${gw.rulesPerLayer} rules/layer)` : '');
44
+ return `
45
+ <div data-advisory="governanceWeight">
46
+ <p><b>Governance weight:</b> <span class="tag ${gw.weight === 'typical' ? 'ok' : 'warn'}">${esc(String(gw.weight))}</span> ${esc(label)}</p>
47
+ <p class="muted">${esc(gw.note ?? '')} Facts, never a score or gate input (<code>notAScore</code>).</p>
48
+ </div>`;
49
+ }
50
+
51
+ function ackLifecycleHtml(lc) {
52
+ if (!lc) return '';
53
+ const rows = [];
54
+ if ((lc.expiredCount ?? 0) > 0) {
55
+ const edges = (lc.expired ?? [])
56
+ .map((e) => `<code>${esc(e.edge)}</code> (review-by ${esc(e.reviewBy)})`)
57
+ .join(' · ');
58
+ rows.push(
59
+ `<p><span class="tag warn">expired</span> ${lc.expiredCount} acknowledgment(s) past review-by — no longer applied, the smell is active again: ${edges}</p>`
60
+ );
61
+ }
62
+ if ((lc.malformed ?? 0) > 0) {
63
+ rows.push(
64
+ `<p><span class="tag warn">malformed</span> ${lc.malformed} acknowledgment(s) have a malformed review-by (expected YYYY-MM-DD) — ignored, not silently applied.</p>`
65
+ );
66
+ }
67
+ if ((lc.undated ?? 0) > 0) {
68
+ rows.push(
69
+ `<p class="muted">${lc.undated} applied acknowledgment(s) have no review-by date — add one so migration acks cannot fossilize.</p>`
70
+ );
71
+ }
72
+ return rows.join('\n');
73
+ }
74
+
75
+ function contractHealthHtml(health) {
76
+ if (!health) return '';
77
+ const smells = Array.isArray(health.smells) ? health.smells : [];
78
+ const acked = health.acknowledged ?? 0;
79
+ const ackNote = acked > 0
80
+ ? `<p class="muted">Acknowledged edges applied: <b>${acked}</b> (${esc(health.ackFile?.path ?? '.ark/contract-smell-acks.json')}) — deliberate loops recorded with a reason; review them when their migrations finish.</p>`
81
+ : '';
82
+ const invalid = health.ackFile?.invalid
83
+ ? `<p class="tag warn">Acknowledgment sidecar present but invalid — acks are ignored, never silently applied.</p>`
84
+ : '';
85
+ const body = smells.length === 0
86
+ ? `<p class="muted">No contract smells detected — no explicitly bidirectional allows, peripheral-into-core allows, lateral adapter allows, or dead rules beyond what is acknowledged.</p>`
87
+ : smells
88
+ .map(
89
+ (s) => `
90
+ <div class="finding">
91
+ <p><span class="tag warn">${esc(s.id)}</span> ${esc(s.outcome ?? s.message ?? '')}</p>
92
+ <p class="muted">${esc(s.message ?? '')}</p>
93
+ <p class="muted">evidence: <code>${(s.evidence ?? []).slice(0, 6).map(esc).join('</code> · <code>')}</code></p>
94
+ <p class="muted">fix: ${esc(s.fix ?? '')}</p>
95
+ </div>`
96
+ )
97
+ .join('\n');
98
+ return `
99
+ <section data-advisory="contractHealth">
100
+ <h2>Contract health <span class="muted">(advisory — meta-lint of the contract itself; never changes the verdict)</span></h2>
101
+ ${invalid}
102
+ ${body}
103
+ ${ackNote}
104
+ ${ackLifecycleHtml(health.ackLifecycle)}
105
+ ${governanceWeightHtml(health.governanceWeight)}
106
+ </section>`;
107
+ }
108
+
109
+ function ambientStateHtml(state) {
110
+ if (!state) return '';
111
+ if (state.available === false) {
112
+ return `
113
+ <section data-advisory="ambientState">
114
+ <h2>Ambient state <span class="muted">(advisory)</span></h2>
115
+ <p class="muted">${esc(state.note ?? 'Sensor unavailable in this run.')}</p>
116
+ </section>`;
117
+ }
118
+ const findings = Array.isArray(state.findings) ? state.findings : [];
119
+ const body = !state.active
120
+ ? '<p class="muted">Idle — no <code>pure: true</code> layer opted in. Declare a pure layer to scan module-scope mutable state.</p>'
121
+ : findings.length === 0
122
+ ? '<p class="muted">Active and clean — no module-scope <code>let</code>/<code>var</code> in pure layers.</p>'
123
+ : `<ul>${findings
124
+ .slice(0, 10)
125
+ .map(
126
+ (f) => `<li><code>${esc(f.file)}:${f.line}</code> — <b>${esc(f.name)}</b> <span class="tag warn">${esc(f.kind)}</span></li>`
127
+ )
128
+ .join('')}</ul>` +
129
+ (state.findingCount > 10 ? `<p class="muted">…(+${state.findingCount - 10} more in doctor JSON)</p>` : '') +
130
+ (state.acknowledged > 0 ? `<p class="muted">acknowledged module state: ${state.acknowledged}</p>` : '');
131
+ return `
132
+ <section data-advisory="ambientState">
133
+ <h2>Ambient state <span class="muted">(advisory — opt-in via pure layers; no strict mode exists)</span></h2>
134
+ ${body}
135
+ </section>`;
136
+ }
137
+
138
+ /**
139
+ * Render every doctor advisory as report sections. Keys must cover everything
140
+ * `computeDoctorAdvisories` returns — the parity guard enforces it.
141
+ * @param escape injected HTML escaper (dependency points html-report → here only)
142
+ */
143
+ export function renderAdvisorySections(advisories, escape) {
144
+ if (!advisories || typeof advisories !== 'object') return '';
145
+ if (typeof escape === 'function') esc = escape;
146
+ return [contractHealthHtml(advisories.contractHealth), ambientStateHtml(advisories.ambientState)]
147
+ .filter(Boolean)
148
+ .join('\n');
149
+ }
@@ -19,6 +19,7 @@ import {
19
19
  renderWritePathAdoptionBlock,
20
20
  } from './html-report-depth.mjs';
21
21
  import { FIX_HINTS } from './violations.mjs';
22
+ import { capabilityBadgesFor, renderAdvisorySections } from './html-report-advisories.mjs';
22
23
 
23
24
  export function detectEnforcement(root) {
24
25
  const has = (rel) => fs.existsSync(path.join(root, rel));
@@ -455,6 +456,8 @@ export function renderHtmlReport({
455
456
  adoption = null,
456
457
  /** Optional design-depth (doctor parity): designFitness, designSmells, pilotLoop, postGreenPath, goldenPattern */
457
458
  designDepth = null,
459
+ /** Doctor advisory parity (X01): contractHealth (+governanceWeight), ambientState — guarded by reportParity.test.ts */
460
+ advisories = null,
458
461
  }) {
459
462
  const layers = Array.isArray(config.layers) ? config.layers : [];
460
463
  const rules = Array.isArray(config.rules) ? config.rules : [];
@@ -710,6 +713,7 @@ export function renderHtmlReport({
710
713
  Array.isArray(layer.forbiddenGlobals) && layer.forbiddenGlobals.length
711
714
  ? `<span class="tag warn">no ${layer.forbiddenGlobals.map(esc).join(', ')}</span>`
712
715
  : '',
716
+ capabilityBadgesFor(layer, esc),
713
717
  layer.mayImportInfrastructure ? '<span class="tag">may import infra</span>' : '',
714
718
  Array.isArray(layer.intentPrefixes) && layer.intentPrefixes.length
715
719
  ? `<span class="tag">${layer.intentPrefixes.map(esc).join(' ')}</span>`
@@ -1187,6 +1191,8 @@ export function renderHtmlReport({
1187
1191
  ${violationBlocks}
1188
1192
  </div>
1189
1193
 
1194
+ ${renderAdvisorySections(advisories, esc)}
1195
+
1190
1196
  <div class="section card">
1191
1197
  <h2>Enforcement points</h2>
1192
1198
  <p class="dim" style="margin:.15rem 0 .85rem;font-size:.88rem">Write-time · merge-time · editor · ratchet. Same contract everywhere.</p>
package/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";var Se=Object.defineProperty;var yt=Object.getOwnPropertyDescriptor;var gt=Object.getOwnPropertyNames;var mt=Object.prototype.hasOwnProperty;var ht=(e,t)=>{for(var n in t)Se(e,n,{get:t[n],enumerable:!0})},At=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of gt(t))!mt.call(e,i)&&i!==n&&Se(e,i,{get:()=>t[i],enumerable:!(r=yt(t,i))||r.enumerable});return e};var bt=e=>At(Se({},"__esModule",{value:!0}),e);var an={};ht(an,{ANALYSIS_IR_SCHEMA_VERSION:()=>ge,ARK_ANALYSIS_RESULT_SCHEMA:()=>Ke,ARK_ANALYSIS_RESULT_SCHEMA_VERSION:()=>Ye,ARK_CONFIG_SCHEMA:()=>fe,ARK_CONFIG_SCHEMA_VERSION:()=>at,POLICY_DELTA_SCHEMA_VERSION:()=>Ae,analyzeArchitectureConvergence:()=>ee,analyzeChange:()=>re,analyzePolicyDelta:()=>Ve,analyzeProject:()=>U,classifyArkPolicyDelta:()=>be,collectAnalysisConfigWarnings:()=>Ue,collectForbiddenCapabilityUses:()=>N,createAICodeGate:()=>Te,createAdapterResult:()=>qe,createArchitectureProfile:()=>Q,createArchitectureProfileFromArkConfig:()=>Fe,createElevenLayerArkConfig:()=>je,detectArchitectureCycles:()=>Ie,deterministicHash:()=>P,elevenLayerProfile:()=>Z,evaluateArchitectureGraph:()=>ie,explainViolation:()=>Ge,extractSemanticDependencies:()=>_,loadArkConfigContract:()=>J,loadContract:()=>ne,parseArkConfigJson:()=>ye,policyDeltaAcknowledgementMatches:()=>Ce,preflightChange:()=>He,stableSerialize:()=>L,toAdapterDiagnostic:()=>oe,version:()=>Be});module.exports=bt(an);var Be="3.4.0";var Ye="1.1";function k(e){return typeof e=="string"&&e.length>0?e:void 0}function ze(e,t){return Number.isInteger(e)&&Number(e)>0?Number(e):t}function Ct(e,t,n){return e==="LAYER_IMPORT_VIOLATION"?t.typeOnly||n.targetTypeOnlyExports===!0||n.namedBindingsTypeOnly===!0?"Move the referenced type to a mutually allowed layer, use `import type`, then preflight again.":n.peerIsolation===!0?"Extract the shared dependency to a shared layer, then preflight again.":`Define a port in ${t.fromLayer??"the source layer"}, inject the ${t.toLayer??"outer-layer"} implementation, then preflight again.`:e==="FORBIDDEN_GLOBAL"?`Inject ${t.target??"the capability"} through a port, then preflight again.`:e==="CAPABILITY_VIOLATION"?`Define a ${k(n.capability)??"capability"} port in ${t.fromLayer??"the walled layer"}, bind the implementation outside it, then preflight again.`:e==="CIRCULAR_DEPENDENCY"?"Extract the shared dependency into a third module, then preflight again.":e==="RAW_EVENT_PUBLISH"?"Publish through a registered intent creator, then run Ark again.":e==="PUBLISH_MISSING_SOURCE"?"Add metadata.source to the publish call, then run Ark again.":`Resolve ${e} without weakening ark.config.json, then run Ark again.`}function oe(e,t="error"){let n=k(e.ruleId)??k(e.code)??"ARK_UNKNOWN",r=e.severity==="warning"?"warning":t,i={...k(e.target)?{target:k(e.target)}:{},...k(e.fromLayer)?{fromLayer:k(e.fromLayer)}:{},...k(e.toLayer)?{toLayer:k(e.toLayer)}:{},...typeof e.typeOnly=="boolean"?{typeOnly:e.typeOnly}:{}};return{ruleId:n,severity:r,message:k(e.message)??n,location:{file:k(e.file)??"<unknown>",line:ze(e.line,1),column:ze(e.column,1)},evidence:i,nextAction:k(e.nextAction)??Ct(n,i,e)}}function qe(e){return{schemaVersion:"1.1",valid:e.valid,diagnostics:[...(e.violations??[]).map(t=>oe(t,"error")),...(e.warnings??[]).map(t=>oe(t,"warning"))]}}var Ke={$schema:"https://json-schema.org/draft/2020-12/schema",$id:"https://unpkg.com/arkgate@2/schemas/ark.analysis-result.schema.json",title:"ArkGate analysis result",type:"object",additionalProperties:!1,required:["schemaVersion","valid","diagnostics"],properties:{schemaVersion:{const:"1.1"},valid:{type:"boolean"},diagnostics:{type:"array",items:{type:"object",additionalProperties:!1,required:["ruleId","severity","message","location","evidence"],properties:{ruleId:{type:"string",minLength:1},severity:{enum:["error","warning"]},message:{type:"string",minLength:1},location:{type:"object",additionalProperties:!1,required:["file","line","column"],properties:{file:{type:"string",minLength:1},line:{type:"integer",minimum:1},column:{type:"integer",minimum:1}}},evidence:{type:"object",additionalProperties:!1,properties:{target:{type:"string"},fromLayer:{type:"string"},toLayer:{type:"string"},typeOnly:{type:"boolean"}}},nextAction:{type:"string",minLength:1}}}}}};var Ee=Object.freeze(["network","filesystem","clock","randomness","environment","process","persistence"]),we=Object.freeze({fetch:"network",XMLHttpRequest:"network",Date:"clock","Date.now":"clock","Math.random":"randomness","process.env":"environment",process:"process"}),Le=Object.freeze(Object.keys(we).sort()),ke=Object.freeze({fs:"filesystem","node:fs":"filesystem","fs/promises":"filesystem","node:fs/promises":"filesystem","fs-extra":"filesystem","graceful-fs":"filesystem",memfs:"filesystem",chokidar:"filesystem",http:"network",https:"network",http2:"network",net:"network",tls:"network",dgram:"network",dns:"network","node:http":"network","node:https":"network","node:http2":"network","node:net":"network","node:tls":"network","node:dgram":"network","node:dns":"network",axios:"network",undici:"network","node-fetch":"network",got:"network",ky:"network",superagent:"network",ws:"network",process:"process","node:process":"process",child_process:"process","node:child_process":"process","@prisma/client":"persistence",prisma:"persistence",pg:"persistence",mysql:"persistence",mysql2:"persistence",mongodb:"persistence",mongoose:"persistence",sqlite3:"persistence","better-sqlite3":"persistence",redis:"persistence",ioredis:"persistence",typeorm:"persistence",knex:"persistence","drizzle-orm":"persistence",sequelize:"persistence",kysely:"persistence","@supabase/supabase-js":"persistence"});function B(e){if(!e||e.startsWith(".")||e.startsWith("/"))return null;let t=ke[e];if(t)return t;let n=e.indexOf("/");if(n<0)return null;let r=e.slice(0,n),i=ke[r];if(i)return i;let a=e.indexOf("/",n+1);return a<0?null:ke[e.slice(0,a)]??null}function se(e){let t=e.split(".");for(let n=t.length;n>=1;n-=1){let r=t.slice(0,n).join("."),i=we[r];if(i)return i}return null}function ce(e){if(e?.pure===!0)return[...Ee].sort();let n=(e?.capabilities?.deny??[]).filter(r=>Ee.includes(r));return[...new Set(n)].sort()}function Pe(e,t){if(t.length===0)return!1;let n=new Set(t),r=e.split(".");for(let i=r.length;i>=1;i-=1)if(n.has(r.slice(0,i).join(".")))return!0;return!1}function le(e){let t=new Set,n=new Set,r=Object.keys(we);for(let i of e?.forbiddenGlobals??[]){let a=r.filter(s=>s===i||s.startsWith(`${i}.`));if(a.length===0)n.add(i);else for(let s of a)t.add(`ambient:${s}`)}for(let i of ce(e)){t.add(`import:${i}`);for(let a of r)se(a)===i&&t.add(`ambient:${a}`)}return{atoms:[...t].sort(),rawGlobals:[...n].sort()}}var We=new Map;function Je(e){return/[.*+?^${}()|[\]\\]/.test(e)?`\\${e}`:e}function Oe(e){let t="";for(let n=0;n<e.length;n+=1){let r=e[n];if(r==="\\"&&n+1<e.length){let i=e[n+1];if("*?{}[],".includes(i)||i==="\\"){t+="\\"+i,n+=1;continue}t+="/";continue}t+=r}return t}function It(e){let t=0;for(let n=0;n<e.length;n+=1){let r=e[n];if(r==="\\"){n+=1;continue}if(r==="{")t+=1;else if(r==="}"&&(t-=1,t<0))return!1}return t===0}function z(e){let t=We.get(e);if(t)return t;let n=Oe(e),r=It(n),i="",a=0;for(let c=0;c<n.length;c+=1){let p=n[c];p==="\\"&&c+1<n.length?(i+=Je(n[c+1]),c+=1):p==="*"?n[c+1]==="*"?n[c+2]==="/"?(i+="(?:.*/)?",c+=2):(i+=".*",c+=1):i+="[^/]*":p==="?"?i+="[^/]":p==="{"&&r?(i+="(?:",a+=1):p==="}"&&r&&a>0?(i+=")",a-=1):p===","&&r&&a>0?i+="|":i+=Je(p)}let s=new RegExp(`^${i}$`);return We.set(e,s),s}function Re(e){let t=Oe(String(e)),r=t.split("*")[0].split("/").filter(Boolean).length,i=t.replace(/\*/g,"").length;return r*1e4+i}function Y(e,t){let n=String(e).split(/[/\\]/).join("/"),r,i=-1;for(let a of t??[])if(!(a.exclude??[]).some(s=>z(s).test(n))){for(let s of a.patterns??[])if(z(s).test(n)){let c=Re(s);c>i&&(i=c,r=a.name)}}return r}function Ze(e,t){if(!t?.length)return;let n=String(e).split(/[/\\]/).filter(Boolean),r=new Set(t.map(i=>String(i).toLowerCase()));for(let i=0;i<n.length-1;i+=1)if(r.has(n[i].toLowerCase()))return`${n[i]}/${n[i+1]}`}function xt(e){let t=new Set;for(let n of e??[]){let i=Oe(String(n)).split("/").filter(Boolean);for(let a=0;a<i.length;a+=1){let s=i[a];if((s==="**"||s==="*")&&a>0){let c=i[a-1];c&&!c.includes("*")&&!c.includes("{")&&!c.includes("}")&&t.add(c)}}}return[...t]}function St(e,t,n){if(Array.isArray(e.sliceFolders)&&e.sliceFolders.length>0)return e.sliceFolders.filter(i=>typeof i=="string"&&i.length>0);let r=(n??[]).find(i=>i.name===t);return xt(r?.patterns)}function v(e,t,n,r){for(let i of e??[])if(!(i.from!==t||i.to!==n)&&i.allowed===!1){if(i.peerIsolation){let a=r?.fromPath,s=r?.toPath;if(!a||!s)continue;let c=St(i,t,r?.layers);if(c.length===0)continue;let p=Ze(a,c),y=Ze(s,c);if(!p||!y)continue;if(p!==y)return i;continue}if(t!==n)return i}}function j(e,t){return t&&e.isStringLiteralLike(t)?t.text:void 0}function et(e,t){return e.getLineAndCharacterOfPosition(t.getStart(e)).line+1}function Qe(e,t){if(e.isImportDeclaration(t)){let n=t.importClause;if(!n)return!1;if(n.isTypeOnly)return!0;let r=n.namedBindings;return!!(r&&e.isNamedImports(r)&&r.elements.length>0&&r.elements.every(i=>i.isTypeOnly))}if(e.isExportDeclaration(t)){if(t.isTypeOnly)return!0;let n=t.exportClause;return!!(n&&e.isNamedExports(n)&&n.elements.length>0&&n.elements.every(r=>r.isTypeOnly))}return!1}function tt(e,t){let n={noLib:!0,noResolve:!0,target:e.ScriptTarget.Latest},r=e.createCompilerHost(n,!0);return r.getSourceFile=i=>i===t.fileName?t:void 0,r.fileExists=i=>i===t.fileName,r.readFile=i=>i===t.fileName?t.text:void 0,e.createProgram([t.fileName],n,r).getTypeChecker()}function $e(e,t){try{return e.getSymbolAtLocation(t)}catch{return}}function ve(e,t,n,r){let i=r.parent&&e.isShorthandPropertyAssignment(r.parent)&&r.parent.name===r,a;try{a=i?t.getShorthandAssignmentValueSymbol(r.parent):$e(t,r)}catch{a=void 0}return!!a?.declarations?.some(s=>s.getSourceFile().fileName===n.fileName)}function _(e,t){let n,r=[],i=(s,c,p,y=!1)=>r.push({specifier:p,kind:c,line:et(t,s),typeOnly:y,unresolved:p===void 0,node:s}),a=s=>{if(e.isImportDeclaration(s))i(s,"import",j(e,s.moduleSpecifier),Qe(e,s));else if(e.isExportDeclaration(s)&&s.moduleSpecifier)i(s,"export",j(e,s.moduleSpecifier),Qe(e,s));else if(e.isImportEqualsDeclaration(s)&&e.isExternalModuleReference(s.moduleReference))i(s,"require",j(e,s.moduleReference.expression));else if(e.isCallExpression(s)){let c=s.expression.kind===e.SyntaxKind.ImportKeyword,y=e.isIdentifier(s.expression)&&s.expression.text==="require"&&!ve(e,n??(n=tt(e,t)),t,s.expression);(c||y)&&i(s,y?"require":"dynamic-import",j(e,s.arguments[0]))}e.forEachChild(s,a)};return a(t),r}function kt(e,t){let n=[],r=t;for(;e.isPropertyAccessExpression(r)||e.isElementAccessExpression(r);){if(e.isPropertyAccessExpression(r))n.unshift(r.name.text);else{let i=j(e,r.argumentExpression);if(i===void 0)return;n.unshift(i)}r=r.expression}if(e.isIdentifier(r))return n.unshift(r.text),{root:r,segments:n}}function Et(e,t){let n=t.parent;return e.isPropertyAccessExpression(n)||e.isElementAccessExpression(n)?!1:e.isExpressionNode(t)&&!e.isInTypeQuery(t)||e.isShorthandPropertyAssignment(n)&&n.name===t}function Xe(e,t){let n=t[0]==="globalThis"?t.slice(1):t;for(let r=n.length;r>=1;r-=1){let i=n.slice(0,r).join(".");if(e.has(i))return i}}function N(e,t,n){if(n.length===0)return[];let r=new Set(n),i=tt(e,t),a=new Map,s=new Set;for(let l of t.statements)if(e.isVariableStatement(l))for(let o of l.declarationList.declarations)e.isIdentifier(o.name)&&s.add(o.name.text);let c=l=>{let o=kt(e,l);if(!o)return;let g=$e(i,o.root),f=g?a.get(g):void 0;return f?[...f,...o.segments.slice(1)]:ve(e,i,t,o.root)||s.has(o.root.text)?void 0:o.segments};for(let l of t.statements)if(e.isVariableStatement(l))for(let o of l.declarationList.declarations){if(!o.initializer||!e.isIdentifier(o.name))continue;let g=c(o.initializer),f=$e(i,o.name);!g||!f||a.set(f,g)}let p=[],y=new Set,u=(l,o)=>{let g=et(t,o),f=`${l}:${o.getStart(t)}`;y.has(f)||(y.add(f),p.push({name:l,line:g,node:o}))},m=l=>{let o=l.parent&&(e.isPropertyAccessExpression(l.parent)||e.isElementAccessExpression(l.parent))&&l.parent.expression===l;if((e.isPropertyAccessExpression(l)||e.isElementAccessExpression(l))&&!o){let g=c(l),f=g?Xe(r,g):void 0;f&&u(f,l)}else e.isIdentifier(l)&&r.has(l.text)&&Et(e,l)&&!ve(e,i,t,l)&&u(l.text,l);if(e.isVariableDeclaration(l)&&e.isObjectBindingPattern(l.name)&&l.initializer){let g=c(l.initializer);if(g)for(let f of l.name.elements){if(!e.isIdentifier(f.name))continue;let I=f.propertyName?j(e,f.propertyName)??f.propertyName.text:f.name.text,x=Xe(r,[...g,I]);x&&u(x,l.initializer)}}e.forEachChild(l,m)};return m(t),p}function _e(e,t){let n=[];for(let r of _(e,t)){if(r.typeOnly||!r.specifier)continue;let i=B(r.specifier);i&&n.push({capability:i,symbol:r.specifier,line:r.line,source:"import-based"})}for(let r of N(e,t,Le)){let i=se(r.name);i&&n.push({capability:i,symbol:r.name,line:r.line,source:"ambient-global"})}return n.sort((r,i)=>r.line-i.line||r.capability.localeCompare(i.capability)||r.symbol.localeCompare(i.symbol))}var Ne={RAW_EVENT_PUBLISH:"Publish through a registered intent creator; raw event objects or intent strings bypass Ark contracts and tooling.",PUBLISH_MISSING_SOURCE:"Strict Ark publish calls must include metadata.source."};function M(e){return/^(Domain|Application|Adapter|Workflow|Job|Presentation|Reporting|Metadata|Security|Audit|Observability|Kernel)\.[A-Za-z0-9_.]+$/.test(e)}function Me(e){if(!e.publishCall)return[];let t=[];return(e.rawIntentName!==void 0&&M(e.rawIntentName)||e.objectHasIntent)&&t.push({ruleId:"RAW_EVENT_PUBLISH",message:Ne.RAW_EVENT_PUBLISH}),e.arkPublishCandidate&&!e.hasSource&&t.push({ruleId:"PUBLISH_MISSING_SOURCE",message:Ne.PUBLISH_MISSING_SOURCE}),t}function b(e,t,n){return{ruleId:e,code:e,message:t,...n}}function V(e,t){return e.slice(0,t).split(`
1
+ "use strict";var Se=Object.defineProperty;var yt=Object.getOwnPropertyDescriptor;var gt=Object.getOwnPropertyNames;var mt=Object.prototype.hasOwnProperty;var ht=(e,t)=>{for(var n in t)Se(e,n,{get:t[n],enumerable:!0})},At=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of gt(t))!mt.call(e,i)&&i!==n&&Se(e,i,{get:()=>t[i],enumerable:!(r=yt(t,i))||r.enumerable});return e};var bt=e=>At(Se({},"__esModule",{value:!0}),e);var an={};ht(an,{ANALYSIS_IR_SCHEMA_VERSION:()=>ge,ARK_ANALYSIS_RESULT_SCHEMA:()=>Ke,ARK_ANALYSIS_RESULT_SCHEMA_VERSION:()=>Ye,ARK_CONFIG_SCHEMA:()=>fe,ARK_CONFIG_SCHEMA_VERSION:()=>at,POLICY_DELTA_SCHEMA_VERSION:()=>Ae,analyzeArchitectureConvergence:()=>ee,analyzeChange:()=>re,analyzePolicyDelta:()=>Ve,analyzeProject:()=>U,classifyArkPolicyDelta:()=>be,collectAnalysisConfigWarnings:()=>Ue,collectForbiddenCapabilityUses:()=>N,createAICodeGate:()=>Te,createAdapterResult:()=>qe,createArchitectureProfile:()=>Q,createArchitectureProfileFromArkConfig:()=>Fe,createElevenLayerArkConfig:()=>je,detectArchitectureCycles:()=>Ie,deterministicHash:()=>P,elevenLayerProfile:()=>Z,evaluateArchitectureGraph:()=>ie,explainViolation:()=>Ge,extractSemanticDependencies:()=>_,loadArkConfigContract:()=>J,loadContract:()=>ne,parseArkConfigJson:()=>ye,policyDeltaAcknowledgementMatches:()=>Ce,preflightChange:()=>He,stableSerialize:()=>L,toAdapterDiagnostic:()=>oe,version:()=>Be});module.exports=bt(an);var Be="3.5.0";var Ye="1.1";function k(e){return typeof e=="string"&&e.length>0?e:void 0}function ze(e,t){return Number.isInteger(e)&&Number(e)>0?Number(e):t}function Ct(e,t,n){return e==="LAYER_IMPORT_VIOLATION"?t.typeOnly||n.targetTypeOnlyExports===!0||n.namedBindingsTypeOnly===!0?"Move the referenced type to a mutually allowed layer, use `import type`, then preflight again.":n.peerIsolation===!0?"Extract the shared dependency to a shared layer, then preflight again.":`Define a port in ${t.fromLayer??"the source layer"}, inject the ${t.toLayer??"outer-layer"} implementation, then preflight again.`:e==="FORBIDDEN_GLOBAL"?`Inject ${t.target??"the capability"} through a port, then preflight again.`:e==="CAPABILITY_VIOLATION"?`Define a ${k(n.capability)??"capability"} port in ${t.fromLayer??"the walled layer"}, bind the implementation outside it, then preflight again.`:e==="CIRCULAR_DEPENDENCY"?"Extract the shared dependency into a third module, then preflight again.":e==="RAW_EVENT_PUBLISH"?"Publish through a registered intent creator, then run Ark again.":e==="PUBLISH_MISSING_SOURCE"?"Add metadata.source to the publish call, then run Ark again.":`Resolve ${e} without weakening ark.config.json, then run Ark again.`}function oe(e,t="error"){let n=k(e.ruleId)??k(e.code)??"ARK_UNKNOWN",r=e.severity==="warning"?"warning":t,i={...k(e.target)?{target:k(e.target)}:{},...k(e.fromLayer)?{fromLayer:k(e.fromLayer)}:{},...k(e.toLayer)?{toLayer:k(e.toLayer)}:{},...typeof e.typeOnly=="boolean"?{typeOnly:e.typeOnly}:{}};return{ruleId:n,severity:r,message:k(e.message)??n,location:{file:k(e.file)??"<unknown>",line:ze(e.line,1),column:ze(e.column,1)},evidence:i,nextAction:k(e.nextAction)??Ct(n,i,e)}}function qe(e){return{schemaVersion:"1.1",valid:e.valid,diagnostics:[...(e.violations??[]).map(t=>oe(t,"error")),...(e.warnings??[]).map(t=>oe(t,"warning"))]}}var Ke={$schema:"https://json-schema.org/draft/2020-12/schema",$id:"https://unpkg.com/arkgate@2/schemas/ark.analysis-result.schema.json",title:"ArkGate analysis result",type:"object",additionalProperties:!1,required:["schemaVersion","valid","diagnostics"],properties:{schemaVersion:{const:"1.1"},valid:{type:"boolean"},diagnostics:{type:"array",items:{type:"object",additionalProperties:!1,required:["ruleId","severity","message","location","evidence"],properties:{ruleId:{type:"string",minLength:1},severity:{enum:["error","warning"]},message:{type:"string",minLength:1},location:{type:"object",additionalProperties:!1,required:["file","line","column"],properties:{file:{type:"string",minLength:1},line:{type:"integer",minimum:1},column:{type:"integer",minimum:1}}},evidence:{type:"object",additionalProperties:!1,properties:{target:{type:"string"},fromLayer:{type:"string"},toLayer:{type:"string"},typeOnly:{type:"boolean"}}},nextAction:{type:"string",minLength:1}}}}}};var Ee=Object.freeze(["network","filesystem","clock","randomness","environment","process","persistence"]),we=Object.freeze({fetch:"network",XMLHttpRequest:"network",Date:"clock","Date.now":"clock","Math.random":"randomness","process.env":"environment",process:"process"}),Le=Object.freeze(Object.keys(we).sort()),ke=Object.freeze({fs:"filesystem","node:fs":"filesystem","fs/promises":"filesystem","node:fs/promises":"filesystem","fs-extra":"filesystem","graceful-fs":"filesystem",memfs:"filesystem",chokidar:"filesystem",http:"network",https:"network",http2:"network",net:"network",tls:"network",dgram:"network",dns:"network","node:http":"network","node:https":"network","node:http2":"network","node:net":"network","node:tls":"network","node:dgram":"network","node:dns":"network",axios:"network",undici:"network","node-fetch":"network",got:"network",ky:"network",superagent:"network",ws:"network",process:"process","node:process":"process",child_process:"process","node:child_process":"process","@prisma/client":"persistence",prisma:"persistence",pg:"persistence",mysql:"persistence",mysql2:"persistence",mongodb:"persistence",mongoose:"persistence",sqlite3:"persistence","better-sqlite3":"persistence",redis:"persistence",ioredis:"persistence",typeorm:"persistence",knex:"persistence","drizzle-orm":"persistence",sequelize:"persistence",kysely:"persistence","@supabase/supabase-js":"persistence"});function B(e){if(!e||e.startsWith(".")||e.startsWith("/"))return null;let t=ke[e];if(t)return t;let n=e.indexOf("/");if(n<0)return null;let r=e.slice(0,n),i=ke[r];if(i)return i;let a=e.indexOf("/",n+1);return a<0?null:ke[e.slice(0,a)]??null}function se(e){let t=e.split(".");for(let n=t.length;n>=1;n-=1){let r=t.slice(0,n).join("."),i=we[r];if(i)return i}return null}function ce(e){if(e?.pure===!0)return[...Ee].sort();let n=(e?.capabilities?.deny??[]).filter(r=>Ee.includes(r));return[...new Set(n)].sort()}function Pe(e,t){if(t.length===0)return!1;let n=new Set(t),r=e.split(".");for(let i=r.length;i>=1;i-=1)if(n.has(r.slice(0,i).join(".")))return!0;return!1}function le(e){let t=new Set,n=new Set,r=Object.keys(we);for(let i of e?.forbiddenGlobals??[]){let a=r.filter(s=>s===i||s.startsWith(`${i}.`));if(a.length===0)n.add(i);else for(let s of a)t.add(`ambient:${s}`)}for(let i of ce(e)){t.add(`import:${i}`);for(let a of r)se(a)===i&&t.add(`ambient:${a}`)}return{atoms:[...t].sort(),rawGlobals:[...n].sort()}}var We=new Map;function Je(e){return/[.*+?^${}()|[\]\\]/.test(e)?`\\${e}`:e}function Oe(e){let t="";for(let n=0;n<e.length;n+=1){let r=e[n];if(r==="\\"&&n+1<e.length){let i=e[n+1];if("*?{}[],".includes(i)||i==="\\"){t+="\\"+i,n+=1;continue}t+="/";continue}t+=r}return t}function It(e){let t=0;for(let n=0;n<e.length;n+=1){let r=e[n];if(r==="\\"){n+=1;continue}if(r==="{")t+=1;else if(r==="}"&&(t-=1,t<0))return!1}return t===0}function z(e){let t=We.get(e);if(t)return t;let n=Oe(e),r=It(n),i="",a=0;for(let c=0;c<n.length;c+=1){let p=n[c];p==="\\"&&c+1<n.length?(i+=Je(n[c+1]),c+=1):p==="*"?n[c+1]==="*"?n[c+2]==="/"?(i+="(?:.*/)?",c+=2):(i+=".*",c+=1):i+="[^/]*":p==="?"?i+="[^/]":p==="{"&&r?(i+="(?:",a+=1):p==="}"&&r&&a>0?(i+=")",a-=1):p===","&&r&&a>0?i+="|":i+=Je(p)}let s=new RegExp(`^${i}$`);return We.set(e,s),s}function Re(e){let t=Oe(String(e)),r=t.split("*")[0].split("/").filter(Boolean).length,i=t.replace(/\*/g,"").length;return r*1e4+i}function Y(e,t){let n=String(e).split(/[/\\]/).join("/"),r,i=-1;for(let a of t??[])if(!(a.exclude??[]).some(s=>z(s).test(n))){for(let s of a.patterns??[])if(z(s).test(n)){let c=Re(s);c>i&&(i=c,r=a.name)}}return r}function Ze(e,t){if(!t?.length)return;let n=String(e).split(/[/\\]/).filter(Boolean),r=new Set(t.map(i=>String(i).toLowerCase()));for(let i=0;i<n.length-1;i+=1)if(r.has(n[i].toLowerCase()))return`${n[i]}/${n[i+1]}`}function xt(e){let t=new Set;for(let n of e??[]){let i=Oe(String(n)).split("/").filter(Boolean);for(let a=0;a<i.length;a+=1){let s=i[a];if((s==="**"||s==="*")&&a>0){let c=i[a-1];c&&!c.includes("*")&&!c.includes("{")&&!c.includes("}")&&t.add(c)}}}return[...t]}function St(e,t,n){if(Array.isArray(e.sliceFolders)&&e.sliceFolders.length>0)return e.sliceFolders.filter(i=>typeof i=="string"&&i.length>0);let r=(n??[]).find(i=>i.name===t);return xt(r?.patterns)}function v(e,t,n,r){for(let i of e??[])if(!(i.from!==t||i.to!==n)&&i.allowed===!1){if(i.peerIsolation){let a=r?.fromPath,s=r?.toPath;if(!a||!s)continue;let c=St(i,t,r?.layers);if(c.length===0)continue;let p=Ze(a,c),y=Ze(s,c);if(!p||!y)continue;if(p!==y)return i;continue}if(t!==n)return i}}function j(e,t){return t&&e.isStringLiteralLike(t)?t.text:void 0}function et(e,t){return e.getLineAndCharacterOfPosition(t.getStart(e)).line+1}function Qe(e,t){if(e.isImportDeclaration(t)){let n=t.importClause;if(!n)return!1;if(n.isTypeOnly)return!0;let r=n.namedBindings;return!!(r&&e.isNamedImports(r)&&r.elements.length>0&&r.elements.every(i=>i.isTypeOnly))}if(e.isExportDeclaration(t)){if(t.isTypeOnly)return!0;let n=t.exportClause;return!!(n&&e.isNamedExports(n)&&n.elements.length>0&&n.elements.every(r=>r.isTypeOnly))}return!1}function tt(e,t){let n={noLib:!0,noResolve:!0,target:e.ScriptTarget.Latest},r=e.createCompilerHost(n,!0);return r.getSourceFile=i=>i===t.fileName?t:void 0,r.fileExists=i=>i===t.fileName,r.readFile=i=>i===t.fileName?t.text:void 0,e.createProgram([t.fileName],n,r).getTypeChecker()}function $e(e,t){try{return e.getSymbolAtLocation(t)}catch{return}}function ve(e,t,n,r){let i=r.parent&&e.isShorthandPropertyAssignment(r.parent)&&r.parent.name===r,a;try{a=i?t.getShorthandAssignmentValueSymbol(r.parent):$e(t,r)}catch{a=void 0}return!!a?.declarations?.some(s=>s.getSourceFile().fileName===n.fileName)}function _(e,t){let n,r=[],i=(s,c,p,y=!1)=>r.push({specifier:p,kind:c,line:et(t,s),typeOnly:y,unresolved:p===void 0,node:s}),a=s=>{if(e.isImportDeclaration(s))i(s,"import",j(e,s.moduleSpecifier),Qe(e,s));else if(e.isExportDeclaration(s)&&s.moduleSpecifier)i(s,"export",j(e,s.moduleSpecifier),Qe(e,s));else if(e.isImportEqualsDeclaration(s)&&e.isExternalModuleReference(s.moduleReference))i(s,"require",j(e,s.moduleReference.expression));else if(e.isCallExpression(s)){let c=s.expression.kind===e.SyntaxKind.ImportKeyword,y=e.isIdentifier(s.expression)&&s.expression.text==="require"&&!ve(e,n??(n=tt(e,t)),t,s.expression);(c||y)&&i(s,y?"require":"dynamic-import",j(e,s.arguments[0]))}e.forEachChild(s,a)};return a(t),r}function kt(e,t){let n=[],r=t;for(;e.isPropertyAccessExpression(r)||e.isElementAccessExpression(r);){if(e.isPropertyAccessExpression(r))n.unshift(r.name.text);else{let i=j(e,r.argumentExpression);if(i===void 0)return;n.unshift(i)}r=r.expression}if(e.isIdentifier(r))return n.unshift(r.text),{root:r,segments:n}}function Et(e,t){let n=t.parent;return e.isPropertyAccessExpression(n)||e.isElementAccessExpression(n)?!1:e.isExpressionNode(t)&&!e.isInTypeQuery(t)||e.isShorthandPropertyAssignment(n)&&n.name===t}function Xe(e,t){let n=t[0]==="globalThis"?t.slice(1):t;for(let r=n.length;r>=1;r-=1){let i=n.slice(0,r).join(".");if(e.has(i))return i}}function N(e,t,n){if(n.length===0)return[];let r=new Set(n),i=tt(e,t),a=new Map,s=new Set;for(let l of t.statements)if(e.isVariableStatement(l))for(let o of l.declarationList.declarations)e.isIdentifier(o.name)&&s.add(o.name.text);let c=l=>{let o=kt(e,l);if(!o)return;let g=$e(i,o.root),f=g?a.get(g):void 0;return f?[...f,...o.segments.slice(1)]:ve(e,i,t,o.root)||s.has(o.root.text)?void 0:o.segments};for(let l of t.statements)if(e.isVariableStatement(l))for(let o of l.declarationList.declarations){if(!o.initializer||!e.isIdentifier(o.name))continue;let g=c(o.initializer),f=$e(i,o.name);!g||!f||a.set(f,g)}let p=[],y=new Set,u=(l,o)=>{let g=et(t,o),f=`${l}:${o.getStart(t)}`;y.has(f)||(y.add(f),p.push({name:l,line:g,node:o}))},m=l=>{let o=l.parent&&(e.isPropertyAccessExpression(l.parent)||e.isElementAccessExpression(l.parent))&&l.parent.expression===l;if((e.isPropertyAccessExpression(l)||e.isElementAccessExpression(l))&&!o){let g=c(l),f=g?Xe(r,g):void 0;f&&u(f,l)}else e.isIdentifier(l)&&r.has(l.text)&&Et(e,l)&&!ve(e,i,t,l)&&u(l.text,l);if(e.isVariableDeclaration(l)&&e.isObjectBindingPattern(l.name)&&l.initializer){let g=c(l.initializer);if(g)for(let f of l.name.elements){if(!e.isIdentifier(f.name))continue;let I=f.propertyName?j(e,f.propertyName)??f.propertyName.text:f.name.text,x=Xe(r,[...g,I]);x&&u(x,l.initializer)}}e.forEachChild(l,m)};return m(t),p}function _e(e,t){let n=[];for(let r of _(e,t)){if(r.typeOnly||!r.specifier)continue;let i=B(r.specifier);i&&n.push({capability:i,symbol:r.specifier,line:r.line,source:"import-based"})}for(let r of N(e,t,Le)){let i=se(r.name);i&&n.push({capability:i,symbol:r.name,line:r.line,source:"ambient-global"})}return n.sort((r,i)=>r.line-i.line||r.capability.localeCompare(i.capability)||r.symbol.localeCompare(i.symbol))}var Ne={RAW_EVENT_PUBLISH:"Publish through a registered intent creator; raw event objects or intent strings bypass Ark contracts and tooling.",PUBLISH_MISSING_SOURCE:"Strict Ark publish calls must include metadata.source."};function M(e){return/^(Domain|Application|Adapter|Workflow|Job|Presentation|Reporting|Metadata|Security|Audit|Observability|Kernel)\.[A-Za-z0-9_.]+$/.test(e)}function Me(e){if(!e.publishCall)return[];let t=[];return(e.rawIntentName!==void 0&&M(e.rawIntentName)||e.objectHasIntent)&&t.push({ruleId:"RAW_EVENT_PUBLISH",message:Ne.RAW_EVENT_PUBLISH}),e.arkPublishCandidate&&!e.hasSource&&t.push({ruleId:"PUBLISH_MISSING_SOURCE",message:Ne.PUBLISH_MISSING_SOURCE}),t}function b(e,t,n){return{ruleId:e,code:e,message:t,...n}}function V(e,t){return e.slice(0,t).split(`
2
2
  `).length}function wt(e){let t=[],n=/['"`]([A-Za-z][A-Za-z0-9_.]*)['"`]/g,r;for(;(r=n.exec(e))!==null;)t.push({value:r[1],index:r.index});return t}function Lt(e){let t=[],n=[{kind:"import",re:/\bimport\s+(?:type\s+)?(?:[^'"]*?\s+from\s*)?['"]([^'"]+)['"]/g},{kind:"export",re:/\bexport\s+(?:type\s+)?[^'"]*?\s+from\s*['"]([^'"]+)['"]/g},{kind:"dynamic-import",re:/\bimport\s*\(\s*['"]([^'"]+)['"]\s*\)/g},{kind:"require",re:/\brequire\s*\(\s*['"]([^'"]+)['"]\s*\)/g}];for(let r of n){let i;for(;(i=r.re.exec(e))!==null;){let a=i.index+i[0].indexOf(i[1]),s=i[0],c=r.kind==="import"&&/\bimport\s+type\b/.test(s)||r.kind==="export"&&/\bexport\s+type\b/.test(s);t.push({value:i[1],index:a,kind:r.kind,typeOnly:c})}}return t.sort((r,i)=>r.index-i.index)}function Pt(e,t){let n=e.createSourceFile("generated.ts",t,e.ScriptTarget.Latest,!0),r=[],i=a=>{e.isStringLiteralLike(a)&&r.push({value:a.text,index:a.getStart(n)}),e.forEachChild(a,i)};return i(n),r}function Ot(e){let t=e.toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);return["adapter","adapters","infra","infrastructure","persistence","repository","repositories","integration","database","db"].some(n=>t.includes(n))}function Rt(e){let t=e.toLowerCase();return["sequelize","prisma","typeorm","mongoose","knex"].some(n=>t===n||t.startsWith(`${n}/`))}function $t(e){let t=e.toLowerCase();return["adapter","infra","persistence","repository","repositories","integration","database"].some(n=>t.includes(n))}function K(e,t){return t&&e.isStringLiteralLike(t)?t.text:void 0}function vt(e,t){if(t&&(e.isIdentifier(t)||e.isStringLiteralLike(t)))return t.text}function nt(e,t,n){if(!(!t||!e.isObjectLiteralExpression(t)))return t.properties.find(r=>!e.isPropertyAssignment(r)&&!e.isShorthandPropertyAssignment(r)?!1:vt(e,r.name)===n)}function W(e,t,n){return nt(e,t,n)!==void 0}function q(e,t,n){let r=nt(e,t,n);return r&&e.isPropertyAssignment(r)?r.initializer:void 0}function _t(e,t){let n=q(e,t,"metadata");return W(e,n,"source")}function rt(e,t){return t?e.isIdentifier(t)?/^[A-Z]/.test(t.text):e.isPropertyAccessExpression(t)?rt(e,t.name):!1:!1}function Nt(e,t){if(!e.isCallExpression(t))return!1;let n=t.expression;return e.isPropertyAccessExpression(n)?n.name.text==="publish":e.isIdentifier(n)&&n.text==="publish"}function Mt(e,t){if(!e.isCallExpression(t))return!1;let n=t.arguments[0],r=K(e,n);return r!==void 0&&M(r)||W(e,n,"intent")||rt(e,n)}function Tt(e,t){if(!e.isCallExpression(t))return!1;let[n,r,i]=t.arguments;return _t(e,n)||W(e,r,"source")||W(e,i,"source")}function Dt(e,t){if(!e.isCallExpression(t))return;let[n,r,i]=t.arguments,a=q(e,n,"metadata");return K(e,q(e,a,"source"))??K(e,q(e,r,"source"))??K(e,q(e,i,"source"))}function Ft(e,t,n,r){let i=e.createSourceFile("generated.ts",t,e.ScriptTarget.Latest,!0),a=n,s=a?.filePath,c=a?.layer,p=[],y=m=>i.getLineAndCharacterOfPosition(m.getStart(i)).line+1,u=m=>{if(Nt(e,m)){let l=m.arguments[0],o=K(e,l);for(let f of Me({publishCall:!0,rawIntentName:o,objectHasIntent:W(e,l,"intent"),arkPublishCandidate:Mt(e,m),hasSource:Tt(e,m)}))p.push(b(f.ruleId,f.message,{line:y(m),filePath:s}));let g=Dt(e,m);if(r&&c&&g&&M(g)){let f=r.resolveLayer(g);f&&f!==c&&p.push(b("PUBLISH_SOURCE_LAYER_MISMATCH",`Publish source "${g}" resolves to ${f}, but the target file is classified as ${c}.`,{line:y(m),filePath:s,target:g,fromLayer:c,toLayer:f}))}}e.forEachChild(m,u)};return u(i),p}function Te(e={}){let t=new Set((e.intents||[]).map(a=>typeof a=="string"?a:a.name)),n=e.forbiddenPatterns||[],r=new Set(e.infrastructureLayers??[]),i=e.enforceIntentAllowlist??t.size>0;return{validate(a,s){let c=[],p=s,y=p?.filePath,u=p?.layer,m=e.typescript,l=m?m.createSourceFile(y??"generated.ts",a,m.ScriptTarget.Latest,!0):void 0,o=l?_(e.typescript,l):void 0,g=o?o.filter(d=>d.specifier!==void 0).map(d=>({value:d.specifier,index:d.node.getStart(l),kind:d.kind,typeOnly:d.typeOnly})):Lt(a),f=e.typescript?Pt(e.typescript,a):wt(a);if(e.typescript&&!e.allowNonLiteralDynamicImport?.(y))for(let d of o?.filter(({unresolved:h})=>h)??[]){let h=d.kind==="require";c.push(b(h?"DYNAMIC_REQUIRE_NOT_ALLOWLISTED":"DYNAMIC_IMPORT_NOT_ALLOWLISTED",`Non-literal ${h?"require call":"dynamic import"} cannot be resolved statically; add the reviewed file to dynamicImportAllowlist.`,{line:d.line,filePath:y}))}let I=u!==void 0&&(r.has(u)||$t(u)),x=u!==void 0?` If "${u}" is an infrastructure layer, mark it in ark.config.json with "mayImportInfrastructure": true (or name it with an infra token like Adapters/Persistence/Repository).`:"";for(let d of n)if(d instanceof RegExp){d.lastIndex=0;let h=d.exec(a);d.lastIndex=0,h&&c.push(b("FORBIDDEN_PATTERN",`Forbidden pattern matched: ${d}`,{line:h.index===void 0?void 0:V(a,h.index),filePath:y,suggestion:"Remove infrastructure imports from domain/application layers."+x}))}else a.includes(d)&&c.push(b("FORBIDDEN_SUBSTRING",`Forbidden substring: ${d}`,{line:V(a,a.indexOf(d)),filePath:y}));for(let d of g){let h=e.resolveImportTarget?.(d.value,y)??(e.resolveImportLayer?{layer:e.resolveImportLayer(d.value,y)}:void 0),A=typeof y=="string"?e.resolveImportTarget?.(y)??(e.resolveImportLayer?{layer:u,relPath:void 0}:void 0):void 0,F=h?.layer;if(F&&u){let ae=v(e.architectureProfile?.rules,u,F,{fromPath:A?.relPath,toPath:h?.relPath,layers:e.architectureLayers});if(ae){if(d.typeOnly&&!ae.peerIsolation)continue;let xe=!!ae.peerIsolation;c.push(b("LAYER_IMPORT_VIOLATION",ae.message??(xe?`Layer "${u}" must not import across slices into "${F}".`:`Layer "${u}" must not import "${F}".`),{line:V(a,d.index),source:d.value,target:d.value,filePath:y,fromLayer:u,toLayer:F,suggestion:xe?"Extract shared code to a shared layer, or coordinate slices via events/ports \u2014 do not import across feature/context slices.":"Depend on a port/interface owned by an inner layer instead, or move this code to a layer allowed to make this import.",details:{importKind:d.kind,peerIsolation:xe,...d.typeOnly?{typeOnly:!0}:{}}}));continue}if(F!==u)continue}I||d.typeOnly||!Ot(d.value)&&!Rt(d.value)||c.push(b("FORBIDDEN_IMPORT",`Forbidden ${d.kind} target: "${d.value}".`,{line:V(a,d.index),source:d.value,target:d.value,filePath:y,suggestion:"Route infrastructure access through an allowed adapter or port boundary."+x,details:{importKind:d.kind}}))}if(e.policies)for(let d of e.policies){let h=d.check({source:a,context:s});if(h!==!0)if(Array.isArray(h))for(let A of h)c.push(b("POLICY_VIOLATION",A.message,{filePath:y,suggestion:`Fix violation of policy "${d.name}".`}));else h===!1?c.push(b("POLICY_VIOLATION",`Policy ${d.name} failed on generated code`)):c.push(b("POLICY_VIOLATION",h.message))}if(i&&t.size>0)for(let d of f)M(d.value)&&!t.has(d.value)&&c.push(b("UNKNOWN_INTENT",`Unknown intent reference: "${d.value}"`,{line:V(a,d.index),filePath:y,target:d.value,suggestion:`Register intent "${d.value}" via defineIntent() or remove the reference.`}));if(e.architectureProfile&&u)for(let d of f){if(!M(d.value))continue;let h=e.architectureProfile.resolveLayer(d.value);if(!h)continue;let A=v(e.architectureProfile.rules,u,h);A&&c.push(b("LAYER_REFERENCE_VIOLATION",A.message??`Layer "${u}" must not reference "${h}" through "${d.value}".`,{line:V(a,d.index),filePath:y,target:d.value,fromLayer:u,toLayer:h,suggestion:"Route the dependency through an allowed intent, port, or event.",details:{rule:A}}))}if(e.extensions)for(let d of e.extensions)try{let h=d.analyze(a,s);c.push(...h)}catch(h){c.push(b("EXTENSION_ERROR",`Extension "${d.name}" failed: ${h instanceof Error?h.message:String(h)}`))}if(e.typescript&&l&&u&&e.forbiddenGlobals?.[u]?.length)try{c.push(...N(e.typescript,l,e.forbiddenGlobals[u]).map(d=>b("FORBIDDEN_GLOBAL",`${u} must not use the ambient global "${d.name}".`,{line:d.line,filePath:y,target:d.name,fromLayer:u,suggestion:"Inject the capability through a port (e.g. a Clock, IdGenerator, or HttpPort) instead of reaching for the ambient global."})))}catch(d){c.push(b("AST_ANALYZER_ERROR",`TypeScript AST analyzer failed: ${d instanceof Error?d.message:String(d)}`))}if(e.typescript&&l&&u&&e.capabilityWalls?.[u]?.length)try{let d=new Set(e.capabilityWalls[u]),h=e.forbiddenGlobals?.[u]??[];for(let A of _e(e.typescript,l))d.has(A.capability)&&(A.source==="ambient-global"&&Pe(A.symbol,h)||c.push(b("CAPABILITY_VIOLATION",A.source==="import-based"?`${u} denies the ${A.capability} capability; found import of "${A.symbol}".`:`${u} denies the ${A.capability} capability; found ambient "${A.symbol}".`,{line:A.line,filePath:y,target:A.symbol,capability:A.capability,fromLayer:u,suggestion:"Define a small port (ClockPort, HttpPort, StoragePort) and bind the implementation in an adapter layer."})))}catch(d){c.push(b("AST_ANALYZER_ERROR",`TypeScript AST analyzer failed: ${d instanceof Error?d.message:String(d)}`))}if(e.typescript)try{c.push(...Ft(e.typescript,a,s,e.architectureProfile))}catch(d){c.push(b("AST_ANALYZER_ERROR",`TypeScript AST analyzer failed: ${d instanceof Error?d.message:String(d)}`))}return{valid:c.length===0,violations:c}}}}var at="1.0",de="https://unpkg.com/arkgate@2/schemas/ark.config.schema.json",it=["DomainModel","ApplicationOrchestration","PersistenceAdapters","IntegrationAdapters","WorkflowSagaEngine","BackgroundJobsScheduling","PresentationAdapters","ReportingReadModels","ExtensibilityMetadata","SecurityAuditObservability","Kernel"],jt=new Set(["PresentationAdapters->ApplicationOrchestration","ApplicationOrchestration->DomainModel","WorkflowSagaEngine->ApplicationOrchestration","WorkflowSagaEngine->DomainModel","BackgroundJobsScheduling->ApplicationOrchestration"]);function Vt(){let e=[];for(let t of it)for(let n of it)t===n||jt.has(`${t}->${n}`)||e.push({from:t,to:n,allowed:!1});return e}var ue=Vt();var R={type:"array",items:{type:"string",minLength:1},uniqueItems:!0},fe={$schema:"https://json-schema.org/draft/2020-12/schema",$id:de,title:"ArkGate architecture contract",description:"Versioned contract consumed identically by ArkGate CLI, MCP, and ESLint surfaces.",type:"object",additionalProperties:!1,required:["$schema","schemaVersion","include","layers","rules"],properties:{$schema:{type:"string",minLength:1,default:de,description:"Editor-facing URL or local path for this JSON Schema."},schemaVersion:{type:"string",const:"1.0",default:"1.0"},name:{type:"string",minLength:1},include:{...R,minItems:1,default:["src"]},exclude:{...R,default:[]},excludeGenerated:{type:"boolean",default:!0},frameworkOverlay:{type:"string",minLength:1},layers:{type:"array",default:[],items:{$ref:"#/$defs/layer"}},rules:{type:"array",default:ue,items:{$ref:"#/$defs/rule"}},cyclePolicy:{type:"string",enum:["strict","soft","framework-soft","off"],default:"strict"},dynamicImportAllowlist:{...R,default:[]},safety:{$ref:"#/$defs/safety",default:{maxTsSuppressions:0,maxAnyCasts:0,allowInMemory:!1,allowDisabledPeerIsolation:!1}}},$defs:{layer:{type:"object",additionalProperties:!1,required:["name","patterns"],properties:{name:{type:"string",minLength:1},patterns:{...R,minItems:1},exclude:R,intentPrefixes:R,description:{type:"string",minLength:1},forbiddenGlobals:R,capabilities:{type:"object",additionalProperties:!1,properties:{deny:{type:"array",uniqueItems:!0,items:{type:"string",enum:["network","filesystem","clock","randomness","environment","process","persistence"]}}}},pure:{type:"boolean"},mayImportInfrastructure:{type:"boolean"},optional:{type:"boolean"}}},rule:{type:"object",additionalProperties:!1,required:["from","to","allowed"],properties:{from:{type:"string",minLength:1},to:{type:"string",minLength:1},allowed:{type:"boolean"},message:{type:"string",minLength:1},peerIsolation:{type:"boolean"},sliceFolders:{...R,minItems:1}}},safety:{type:"object",additionalProperties:!1,properties:{maxTsSuppressions:{type:"integer",minimum:0,default:0},maxAnyCasts:{type:"integer",minimum:0,default:0},allowInMemory:{type:"boolean",default:!1},allowDisabledPeerIsolation:{type:"boolean",default:!1}}}}},H=class extends Error{issues;source;constructor(t,n){super(`Invalid ArkGate config (${t}):
3
3
  ${n.map(r=>`- ${r.path}: ${r.message}`).join(`
4
4
  `)}`),this.name="ArkConfigValidationError",this.source=t,this.issues=n}};function ot(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function De(e,t){return/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(t)?`${e}.${t}`:`${e}[${JSON.stringify(t)}]`}function G(e){return e===null?"null":Array.isArray(e)?"array":typeof e}function Gt(e,t){let n="#/$defs/";if(e.startsWith(n))return t.$defs[e.slice(n.length)]}function pe(e,t,n,r,i){if(t.$ref){let a=Gt(t.$ref,r);if(!a){i.push({path:n,message:`schema reference ${t.$ref} cannot be resolved`});return}pe(e,a,n,r,i);return}if(t.const!==void 0&&!Object.is(e,t.const)){i.push({path:n,message:`must equal ${JSON.stringify(t.const)}`});return}if(t.enum&&!t.enum.some(a=>Object.is(a,e))){i.push({path:n,message:`must be one of ${t.enum.map(String).join(", ")}`});return}if(t.type==="object"){if(!ot(e)){i.push({path:n,message:`must be an object; received ${G(e)}`});return}let a=t.properties??{};for(let s of t.required??[])e[s]===void 0&&i.push({path:De(n,s),message:"is required"});if(t.additionalProperties===!1)for(let s of Object.keys(e))s in a||i.push({path:De(n,s),message:"unknown field"});for(let[s,c]of Object.entries(a))e[s]!==void 0&&pe(e[s],c,De(n,s),r,i);return}if(t.type==="array"){if(!Array.isArray(e)){i.push({path:n,message:`must be an array; received ${G(e)}`});return}if(t.minItems!==void 0&&e.length<t.minItems&&i.push({path:n,message:`must contain at least ${t.minItems} item(s)`}),t.uniqueItems){let a=e.map(s=>JSON.stringify(s));new Set(a).size!==a.length&&i.push({path:n,message:"must not contain duplicate items"})}t.items&&e.forEach((a,s)=>pe(a,t.items,`${n}[${s}]`,r,i));return}if(t.type==="string"){if(typeof e!="string"){i.push({path:n,message:`must be a string; received ${G(e)}`});return}t.minLength!==void 0&&e.length<t.minLength&&i.push({path:n,message:`must contain at least ${t.minLength} character(s)`});return}if(t.type==="boolean"){typeof e!="boolean"&&i.push({path:n,message:`must be a boolean; received ${G(e)}`});return}if(t.type==="integer"){if(!Number.isInteger(e)){i.push({path:n,message:`must be an integer; received ${G(e)}`});return}t.minimum!==void 0&&e<t.minimum&&i.push({path:n,message:`must be at least ${t.minimum}`})}}function Ht(e){return{...e,$schema:e.$schema===void 0?de:e.$schema,schemaVersion:e.schemaVersion===void 0?"1.0":e.schemaVersion,include:e.include===void 0?["src"]:e.include,layers:e.layers===void 0?[]:e.layers,rules:e.rules===void 0?ue.map(t=>({...t})):e.rules}}function Ut(e,t="ark.config.json"){if(!ot(e))throw new H(t,[{path:"$",message:`must be an object; received ${G(e)}`}]);let n=e.schemaVersion===void 0?"unversioned":null;if(e.schemaVersion!==void 0&&e.schemaVersion!=="1.0")throw new H(t,[{path:"$.schemaVersion",message:`unsupported version ${JSON.stringify(e.schemaVersion)}; expected 1.0`}]);return{candidate:Ht(e),migratedFrom:n}}function J(e,t="ark.config.json"){let{candidate:n,migratedFrom:r}=Ut(e,t),i=[];if(pe(n,fe,"$",fe,i),i.length>0)throw new H(t,i);return{config:n,migratedFrom:r}}function ye(e,t="ark.config.json"){let n;try{n=JSON.parse(e)}catch(r){throw new H(t,[{path:"$",message:`invalid JSON: ${r instanceof Error?r.message:String(r)}`}])}return J(n,t)}function st(e){let t={$schema:typeof e.$schema=="string"&&e.$schema.length>0?e.$schema:de,schemaVersion:"1.0"};for(let[n,r]of Object.entries(e))n!=="$schema"&&n!=="schemaVersion"&&(t[n]=r);return t}function Bt(e){return e.endsWith(".")?e:`${e}.`}function zt(e,t){let n=e.prefixes.length?Math.max(...e.prefixes.map(i=>i.length)):0;return(t.prefixes.length?Math.max(...t.prefixes.map(i=>i.length)):0)-n}function Q(e){let t=e.layers.map(i=>({...i,prefixes:i.prefixes.map(Bt)})),n=[...t].sort(zt),r=[...e.rules??[]];return{name:e.name,layers:t,rules:r,resolveLayer(i){return t.find(a=>a.match?.(i))?.name??n.find(a=>a.prefixes.some(s=>i.startsWith(s)))?.name}}}function Fe(e,t={}){return Q({name:t.name??e.name??"ark.config.json",layers:e.layers.map((n,r)=>({name:n.name,prefixes:n.intentPrefixes??[],description:n.description,order:r+1})),rules:e.rules??[]})}var Yt=[{name:"DomainModel",prefixes:["Domain"],description:"Rich domain model, business rules, and domain events.",order:1},{name:"ApplicationOrchestration",prefixes:["Application"],description:"Use cases and command orchestration.",order:2},{name:"PersistenceAdapters",prefixes:["Adapter.Persistence","Adapter.Repository"],description:"Database, repository, and storage adapters.",order:3},{name:"IntegrationAdapters",prefixes:["Adapter.Integration","Adapter.External"],description:"External systems, APIs, and integration adapters.",order:4},{name:"WorkflowSagaEngine",prefixes:["Workflow"],description:"Sagas, workflows, and long-running processes.",order:5},{name:"BackgroundJobsScheduling",prefixes:["Job"],description:"Background jobs, scheduled work, and async processors.",order:6},{name:"PresentationAdapters",prefixes:["Presentation","Adapter.Presentation","Adapter.Api"],description:"API, UI, controller, and presentation adapters.",order:7},{name:"ReportingReadModels",prefixes:["Reporting"],description:"Read models, projections, and reporting surfaces.",order:8},{name:"ExtensibilityMetadata",prefixes:["Metadata"],description:"Metadata, extensions, and schema contracts.",order:9},{name:"SecurityAuditObservability",prefixes:["Security","Audit","Observability"],description:"Security, audit, and observability concerns.",order:10},{name:"Kernel",prefixes:["Kernel"],description:"Ark-owned governance and kernel signals.",order:11}],Z=Q({name:"Ark 11-layer Hexagonal Event-Driven Profile",layers:Yt,rules:ue.map(e=>({...e}))}),qt={DomainModel:["domain"],ApplicationOrchestration:["application","app"],PersistenceAdapters:["adapters/persistence","adapters/repository","repositories","infra/persistence"],IntegrationAdapters:["adapters/integration","adapters/external","integrations"],WorkflowSagaEngine:["workflows","sagas"],BackgroundJobsScheduling:["jobs","schedules"],PresentationAdapters:["presentation","adapters/presentation","adapters/api"],ReportingReadModels:["reporting","read-models","projections"],ExtensibilityMetadata:["metadata","extensions"],SecurityAuditObservability:["security","audit","observability"],Kernel:["kernel"]};function je(e={}){let t=e.rootDir??"src",n=e.optionalLayers??!0,r=t==="."?"":`${t}/`;return st({include:e.include??[t],layers:Z.layers.map(i=>({name:i.name,patterns:(qt[i.name]??[i.name]).map(a=>`${r}${a}/**`),intentPrefixes:i.prefixes,optional:n})),rules:[...Z.rules]})}var ge="1.0";function P(e){let t=2166136261;for(let n=0;n<e.length;n+=1)t^=e.charCodeAt(n),t=Math.imul(t,16777619);return`fnv1a-${(t>>>0).toString(16).padStart(8,"0")}`}function L(e){if(e===null||typeof e!="object")return JSON.stringify(e);if(Array.isArray(e))return`[${e.map(L).join(",")}]`;let t=e;return`{${Object.keys(t).sort().map(n=>`${JSON.stringify(n)}:${L(t[n])}`).join(",")}}`}function E(e){return`${e.from}->${e.to}`}function X(e,t,n,r="dependency"){return{id:`${e}:${r}:${E(t)}`,classification:e,subject:"dependency",from:t.from,to:t.to,message:n,...e==="missing"?{nextAction:`Add the planned dependency ${E(t)} to the candidate, then preflight again.`}:e==="contradictory"?{nextAction:`Replace the reverse dependency with ${E(t)}, then preflight again.`}:e==="unplanned"?{nextAction:`Remove the unplanned dependency ${E(t)} from the candidate, then preflight again.`}:{}}}function ee(e){let t=[],n=new Map(e.changeMap.map.files.map(l=>[l.path,l])),r=new Map(e.changes.map(l=>[l.path,l])),i=new Map(e.changeMap.map.dependencies.map(l=>[E(l),l])),a=new Map(e.baseDependencies.map(l=>[E(l),l])),s=new Map(e.candidateDependencies.map(l=>[E(l),l]));for(let l of[...n.values()].sort((o,g)=>o.path.localeCompare(g.path))){let o=r.get(l.path);o?o.operation!==l.operation?t.push({id:`contradictory:file:${l.path}`,classification:"contradictory",subject:"file",path:l.path,expectedOperation:l.operation,actualOperation:o.operation,message:`${l.path} was planned as ${l.operation} but the actual operation is ${o.operation}.`,nextAction:`Change ${l.path} to the planned ${l.operation} operation, then preflight again.`}):t.push({id:`satisfied:file:${l.path}`,classification:"satisfied",subject:"file",path:l.path,expectedOperation:l.operation,actualOperation:o.operation,message:`${l.path} matches the planned ${l.operation} operation.`}):t.push({id:`missing:file:${l.path}`,classification:"missing",subject:"file",path:l.path,expectedOperation:l.operation,message:`${l.path} was planned as ${l.operation} but is absent from the actual change.`,nextAction:`${l.operation[0].toUpperCase()}${l.operation.slice(1)} ${l.path} in the complete change set, then preflight again.`})}for(let l of[...r.values()].sort((o,g)=>o.path.localeCompare(g.path)))n.has(l.path)||t.push({id:`unplanned:file:${l.path}`,classification:"unplanned",subject:"file",path:l.path,actualOperation:l.operation,message:`${l.path} has an unplanned ${l.operation} operation.`,nextAction:`Remove ${l.path} from the change set, then preflight again.`});let c=new Set;for(let l of[...i.values()].sort((o,g)=>E(o).localeCompare(E(g)))){if(s.has(E(l))){t.push(X("satisfied",l,`${l.from} -> ${l.to} exists in the candidate architecture.`));continue}let o={from:l.to,to:l.from};s.has(E(o))?(c.add(E(o)),t.push(X("contradictory",l,`${l.from} -> ${l.to} was planned, but the candidate contains the reverse edge.`))):t.push(X("missing",l,`${l.from} -> ${l.to} is absent from the candidate architecture.`))}let p=new Set([...n.keys(),...r.keys()]);for(let[l,o]of[...s].sort(([g],[f])=>g.localeCompare(f)))a.has(l)||i.has(l)||c.has(l)||!p.has(o.from)&&!p.has(o.to)||t.push({...X("unplanned",o,`${o.from} -> ${o.to} was added without a matching planned dependency.`,"dependency-added"),actualOperation:"added"});let y=new Set(e.changeMap.map.files.filter(l=>l.operation==="delete").map(l=>l.path));for(let[l,o]of[...a].sort(([g],[f])=>g.localeCompare(f)))s.has(l)||y.has(o.from)||y.has(o.to)||!p.has(o.from)&&!p.has(o.to)||t.push({...X("unplanned",o,`${o.from} -> ${o.to} was removed without a planned file deletion.`,"dependency-removed"),actualOperation:"removed"});let u={satisfied:0,missing:1,contradictory:2,unplanned:3};t.sort((l,o)=>u[l.classification]-u[o.classification]||(l.subject===o.subject?0:l.subject==="file"?-1:1)||l.id.localeCompare(o.id));let m={satisfied:t.filter(l=>l.classification==="satisfied").length,missing:t.filter(l=>l.classification==="missing").length,contradictory:t.filter(l=>l.classification==="contradictory").length,unplanned:t.filter(l=>l.classification==="unplanned").length};return{schemaVersion:"1.0",readOnly:!0,changeMapHash:e.changeMap.hash,structurallyConverged:m.missing===0&&m.contradictory===0&&m.unplanned===0,behavioralCompletion:"not-evaluated",summary:m,findings:t}}var Ae="1.0";function C(e,t){e.push({id:`${t.classification}:${t.path}:${t.kind}`,path:t.path,classification:t.classification,message:t.message,...t.classification==="weakening"||t.classification==="judgment-required"?{nextAction:`Restore the previous protection at ${t.path}, then run ArkGate again.`}:{},...t.before===void 0?{}:{before:t.before},...t.after===void 0?{}:{after:t.after}})}function w(e){return[...new Set(e??[])].sort()}function T(e,t,n,r,i){let a=w(n),s=w(r),c=new Set(a),p=new Set(s),y=s.filter(m=>!c.has(m)),u=a.filter(m=>!p.has(m));y.length===0&&u.length===0||(y.length>0&&C(e,{kind:"added",path:t,classification:i.added,message:i.addedMessage,before:a,after:s}),u.length>0&&C(e,{kind:"removed",path:t,classification:i.removed,message:i.removedMessage,before:a,after:s}))}function me(e,t,n,r,i,a,s){if(n===r)return;C(e,{kind:r?"enabled":"disabled",path:t,classification:r?i:i==="strengthening"?"weakening":"strengthening",message:r?a:s,before:n,after:r})}function he(e,t){let n=new Map,r=new Set;for(let i of e){let a=t(i);n.has(a)?r.add(a):n.set(a,i)}return{values:n,duplicates:[...r].sort()}}function Kt(e,t,n){let r=he(t,a=>a.name),i=he(n,a=>a.name);(r.duplicates.length>0||i.duplicates.length>0)&&C(e,{kind:"duplicate-layer",path:"$.layers",classification:"judgment-required",message:"Duplicate layer names make policy ownership ambiguous.",before:r.duplicates,after:i.duplicates});for(let a of[...new Set([...r.values.keys(),...i.values.keys()])].sort()){let s=r.values.get(a),c=i.values.get(a),p=`$.layers[${a}]`;if(!s&&c){C(e,{kind:"layer-added",path:p,classification:"judgment-required",message:"A layer was added; verify overlap, ownership, and rule coverage.",after:c});continue}if(s&&!c){C(e,{kind:"layer-removed",path:p,classification:"weakening",message:"Removing a layer can leave its source paths ungoverned.",before:s});continue}if(!s||!c)continue;T(e,`${p}.patterns`,s.patterns,c.patterns,{added:"strengthening",removed:"weakening",addedMessage:"Additional paths are governed by this layer.",removedMessage:"Paths were removed from this layer and may become ungoverned."}),T(e,`${p}.exclude`,s.exclude,c.exclude,{added:"weakening",removed:"strengthening",addedMessage:"Additional paths are excluded from this layer.",removedMessage:"Fewer paths are excluded from this layer."});let y=le(s),u=le(c);T(e,`${p}.forbiddenGlobals`,y.rawGlobals,u.rawGlobals,{added:"strengthening",removed:"weakening",addedMessage:"Additional forbidden globals are enforced in this layer.",removedMessage:"A forbidden-global protection was removed from this layer."}),T(e,`${p}.capabilities`,y.atoms,u.atoms,{added:"strengthening",removed:"weakening",addedMessage:"Additional ambient/import protection is enforced in this layer (coverage atoms).",removedMessage:"An ambient/import protection was lost from this layer (coverage atoms)."}),w(s.intentPrefixes).join("\0")!==w(c.intentPrefixes).join("\0")&&C(e,{kind:"intent-prefixes-changed",path:`${p}.intentPrefixes`,classification:"judgment-required",message:"Intent ownership changed and must be reviewed against publishers and consumers.",before:w(s.intentPrefixes),after:w(c.intentPrefixes)}),me(e,`${p}.mayImportInfrastructure`,s.mayImportInfrastructure===!0,c.mayImportInfrastructure===!0,"weakening","The layer may now import infrastructure directly.","Direct infrastructure imports are no longer allowed for this layer."),me(e,`${p}.optional`,s.optional===!0,c.optional===!0,"weakening","The layer is now optional and can be absent without a strict warning.","The layer is now required when its contract is active.")}}function Wt(e,t,n){let r=s=>`${s.from}->${s.to}`,i=he(t,r),a=he(n,r);(i.duplicates.length>0||a.duplicates.length>0)&&C(e,{kind:"duplicate-rule",path:"$.rules",classification:"judgment-required",message:"Duplicate rule edges make the effective verdict order-dependent.",before:i.duplicates,after:a.duplicates});for(let s of[...new Set([...i.values.keys(),...a.values.keys()])].sort()){let c=i.values.get(s),p=a.values.get(s),y=`$.rules[${s}]`;if(!c&&p){p.allowed===!1&&C(e,{kind:"deny-added",path:y,classification:"strengthening",message:"A denied dependency edge was added.",after:p});continue}if(c&&!p){c.allowed===!1&&C(e,{kind:"deny-removed",path:y,classification:"weakening",message:"A denied dependency edge was removed.",before:c});continue}if(!c||!p)continue;c.allowed!==p.allowed&&C(e,{kind:p.allowed?"deny-disabled":"deny-enabled",path:`${y}.allowed`,classification:p.allowed?"weakening":"strengthening",message:p.allowed?"A previously denied dependency edge is now allowed.":"A dependency edge is now denied.",before:c.allowed,after:p.allowed});let u=c.peerIsolation===!0,m=p.peerIsolation===!0;if(u!==m){let l=c.from===c.to&&p.from===p.to;C(e,{kind:m?"peer-isolation-enabled":"peer-isolation-disabled",path:`${y}.peerIsolation`,classification:l?m?"strengthening":"weakening":"judgment-required",message:l?m?"Cross-slice dependencies inside this layer are now denied.":"Cross-slice dependencies inside this layer are no longer denied.":"Changing peer isolation on a cross-layer edge changes the denial scope.",before:u,after:m})}w(c.sliceFolders).join("\0")!==w(p.sliceFolders).join("\0")&&C(e,{kind:"slice-folders-changed",path:`${y}.sliceFolders`,classification:"judgment-required",message:"Slice ownership folders changed and can reclassify existing dependencies.",before:w(c.sliceFolders),after:w(p.sliceFolders)})}}function Jt(e,t,n){let r=t.safety??{},i=n.safety??{};for(let a of["maxTsSuppressions","maxAnyCasts"]){let s=r[a]??0,c=i[a]??0;s!==c&&C(e,{kind:c>s?"threshold-raised":"threshold-lowered",path:`$.safety.${a}`,classification:c>s?"weakening":"strengthening",message:c>s?"The safety threshold allows more violations.":"The safety threshold allows fewer violations.",before:s,after:c})}for(let a of["allowInMemory","allowDisabledPeerIsolation"])me(e,`$.safety.${a}`,r[a]===!0,i[a]===!0,"weakening","A safety exception was enabled.","A safety exception was disabled.")}function Zt(e){return e.some(t=>t.classification==="weakening")?"weakening":e.some(t=>t.classification==="judgment-required")?"judgment-required":e.some(t=>t.classification==="strengthening")?"strengthening":"neutral"}function be(e,t){let n=[];T(n,"$.include",e.include,t.include,{added:"strengthening",removed:"weakening",addedMessage:"Additional project roots are governed.",removedMessage:"Project roots were removed from governance."}),T(n,"$.exclude",e.exclude,t.exclude,{added:"weakening",removed:"strengthening",addedMessage:"Additional project paths are excluded from governance.",removedMessage:"Fewer project paths are excluded from governance."}),T(n,"$.dynamicImportAllowlist",e.dynamicImportAllowlist,t.dynamicImportAllowlist,{added:"weakening",removed:"strengthening",addedMessage:"Additional files may use non-literal dynamic imports.",removedMessage:"Fewer files may use non-literal dynamic imports."}),me(n,"$.excludeGenerated",e.excludeGenerated!==!1,t.excludeGenerated!==!1,"weakening","Generated source is now excluded from governance.","Generated source is now governed.");let r={off:0,soft:1,"framework-soft":1,strict:2},i=e.cyclePolicy??"strict",a=t.cyclePolicy??"strict";if(i!==a){let s=r[a]===r[i]?"judgment-required":r[a]>r[i]?"strengthening":"weakening";C(n,{kind:"cycle-policy-changed",path:"$.cyclePolicy",classification:s,message:"The cycle enforcement level changed.",before:i,after:a})}return(e.frameworkOverlay??null)!==(t.frameworkOverlay??null)&&C(n,{kind:"framework-overlay-changed",path:"$.frameworkOverlay",classification:"judgment-required",message:"The framework overlay changed and may alter effective layer matching.",before:e.frameworkOverlay??null,after:t.frameworkOverlay??null}),Kt(n,e.layers,t.layers),Wt(n,e.rules,t.rules),Jt(n,e,t),n.sort((s,c)=>s.path.localeCompare(c.path)||s.id.localeCompare(c.id)),{schemaVersion:Ae,classification:Zt(n),findings:n}}function Ce(e,t){if(!e||e.schemaVersion!==Ae||typeof e.basePolicyHash!="string"||typeof e.candidatePolicyHash!="string"||typeof e.reason!="string"||!Array.isArray(e.findingIds)||e.findingIds.some(i=>typeof i!="string")||e.reason.trim().length===0||e.basePolicyHash!==t.basePolicyHash||e.candidatePolicyHash!==t.candidatePolicyHash)return!1;let n=w(e.findingIds),r=w(t.findingIds);return n.length===r.length&&n.every((i,a)=>i===r[a])}function D(e){let t=[];for(let n of e.replace(/\\/g,"/").split("/"))!n||n==="."||(n===".."&&t.length>0&&t.at(-1)!==".."?t.pop():t.push(n));return t.join("/")}function ct(e){return e!==void 0&&/[A-Za-z0-9_$]/.test(e)}function $(e,t){for(;t<e.length&&/\s/.test(e[t]);)t+=1;return t}function te(e,t){let n=e[t];if(n!=="'"&&n!=='"')return;let r=t,i="";for(t+=1;t<e.length;t+=1){let a=e[t];if(a===n)return{value:i,offset:r,excerpt:e.slice(r,t+1)};a==="\\"&&t+1<e.length?(i+=e[t+1],t+=1):i+=a}}function O(e,t,n){return e.startsWith(t,n)&&!ct(e[n-1])&&!ct(e[n+t.length])}function Qt(e,t){if(t=$(e,t+6),e[t]==="(")return te(e,$(e,t+1));let n=!1;if(O(e,"type",t)){let i=$(e,t+4);e[i]!==","&&!O(e,"from",i)&&(n=!0)}let r=lt(e,t,!0);return r&&n?{...r,typeOnly:!0}:r}function Xt(e,t){t=t+6;let n=$(e,t),r=!1;if(O(e,"type",n)){let a=$(e,n+4);(e[a]==="{"||e[a]==="*")&&(r=!0)}let i=lt(e,t,!1);return i&&r?{...i,typeOnly:!0}:i}function lt(e,t,n){for(;t<e.length;t+=1){if(e[t]===";")return;if(O(e,"from",t))return te(e,$(e,t+4));if(n&&(e[t]==="'"||e[t]==='"'))return te(e,t);if(t>0&&(O(e,"import",t)||O(e,"export",t)))return}}function en(e,t){for(t+=1;t<e.length;t+=1){let n=e[t];if(n==="\\")t+=1;else if(n==="`")return t}return e.length}function tn(e,t){let n=t-1;for(;n>=0&&/\s/.test(e[n]);)n-=1;if(e[n]===".")return;let r=$(e,t+7);if(e[r]!=="(")return;r=$(e,r+1);let i=te(e,r);return i?{...i,requireCall:!0}:void 0}function nn(e){let t=[];for(let n=0;n<e.length;n+=1){let r=e[n];if(r==="/"&&e[n+1]==="/"){if(n=e.indexOf(`
package/dist/index.d.cts CHANGED
@@ -234,7 +234,7 @@ declare function loadArkConfigContract(input: unknown, source?: string): ArkConf
234
234
  declare function parseArkConfigJson(json: string, source?: string): ArkConfigLoadResult;
235
235
 
236
236
  /** ArkGate library version — single source of truth. */
237
- declare const version = "3.4.0";
237
+ declare const version = "3.5.0";
238
238
 
239
239
  /** Versioned public result contract shared by every ArkGate enforcement adapter. */
240
240
  declare const ARK_ANALYSIS_RESULT_SCHEMA_VERSION: "1.1";
package/dist/index.d.ts CHANGED
@@ -234,7 +234,7 @@ declare function loadArkConfigContract(input: unknown, source?: string): ArkConf
234
234
  declare function parseArkConfigJson(json: string, source?: string): ArkConfigLoadResult;
235
235
 
236
236
  /** ArkGate library version — single source of truth. */
237
- declare const version = "3.4.0";
237
+ declare const version = "3.5.0";
238
238
 
239
239
  /** Versioned public result contract shared by every ArkGate enforcement adapter. */
240
240
  declare const ARK_ANALYSIS_RESULT_SCHEMA_VERSION: "1.1";
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- var ct="3.4.0";var lt="1.1";function k(e){return typeof e=="string"&&e.length>0?e:void 0}function Te(e,t){return Number.isInteger(e)&&Number(e)>0?Number(e):t}function pt(e,t,n){return e==="LAYER_IMPORT_VIOLATION"?t.typeOnly||n.targetTypeOnlyExports===!0||n.namedBindingsTypeOnly===!0?"Move the referenced type to a mutually allowed layer, use `import type`, then preflight again.":n.peerIsolation===!0?"Extract the shared dependency to a shared layer, then preflight again.":`Define a port in ${t.fromLayer??"the source layer"}, inject the ${t.toLayer??"outer-layer"} implementation, then preflight again.`:e==="FORBIDDEN_GLOBAL"?`Inject ${t.target??"the capability"} through a port, then preflight again.`:e==="CAPABILITY_VIOLATION"?`Define a ${k(n.capability)??"capability"} port in ${t.fromLayer??"the walled layer"}, bind the implementation outside it, then preflight again.`:e==="CIRCULAR_DEPENDENCY"?"Extract the shared dependency into a third module, then preflight again.":e==="RAW_EVENT_PUBLISH"?"Publish through a registered intent creator, then run Ark again.":e==="PUBLISH_MISSING_SOURCE"?"Add metadata.source to the publish call, then run Ark again.":`Resolve ${e} without weakening ark.config.json, then run Ark again.`}function ge(e,t="error"){let n=k(e.ruleId)??k(e.code)??"ARK_UNKNOWN",r=e.severity==="warning"?"warning":t,i={...k(e.target)?{target:k(e.target)}:{},...k(e.fromLayer)?{fromLayer:k(e.fromLayer)}:{},...k(e.toLayer)?{toLayer:k(e.toLayer)}:{},...typeof e.typeOnly=="boolean"?{typeOnly:e.typeOnly}:{}};return{ruleId:n,severity:r,message:k(e.message)??n,location:{file:k(e.file)??"<unknown>",line:Te(e.line,1),column:Te(e.column,1)},evidence:i,nextAction:k(e.nextAction)??pt(n,i,e)}}function dt(e){return{schemaVersion:"1.1",valid:e.valid,diagnostics:[...(e.violations??[]).map(t=>ge(t,"error")),...(e.warnings??[]).map(t=>ge(t,"warning"))]}}var ft={$schema:"https://json-schema.org/draft/2020-12/schema",$id:"https://unpkg.com/arkgate@2/schemas/ark.analysis-result.schema.json",title:"ArkGate analysis result",type:"object",additionalProperties:!1,required:["schemaVersion","valid","diagnostics"],properties:{schemaVersion:{const:"1.1"},valid:{type:"boolean"},diagnostics:{type:"array",items:{type:"object",additionalProperties:!1,required:["ruleId","severity","message","location","evidence"],properties:{ruleId:{type:"string",minLength:1},severity:{enum:["error","warning"]},message:{type:"string",minLength:1},location:{type:"object",additionalProperties:!1,required:["file","line","column"],properties:{file:{type:"string",minLength:1},line:{type:"integer",minimum:1},column:{type:"integer",minimum:1}}},evidence:{type:"object",additionalProperties:!1,properties:{target:{type:"string"},fromLayer:{type:"string"},toLayer:{type:"string"},typeOnly:{type:"boolean"}}},nextAction:{type:"string",minLength:1}}}}}};var he=Object.freeze(["network","filesystem","clock","randomness","environment","process","persistence"]),Ae=Object.freeze({fetch:"network",XMLHttpRequest:"network",Date:"clock","Date.now":"clock","Math.random":"randomness","process.env":"environment",process:"process"}),be=Object.freeze(Object.keys(Ae).sort()),me=Object.freeze({fs:"filesystem","node:fs":"filesystem","fs/promises":"filesystem","node:fs/promises":"filesystem","fs-extra":"filesystem","graceful-fs":"filesystem",memfs:"filesystem",chokidar:"filesystem",http:"network",https:"network",http2:"network",net:"network",tls:"network",dgram:"network",dns:"network","node:http":"network","node:https":"network","node:http2":"network","node:net":"network","node:tls":"network","node:dgram":"network","node:dns":"network",axios:"network",undici:"network","node-fetch":"network",got:"network",ky:"network",superagent:"network",ws:"network",process:"process","node:process":"process",child_process:"process","node:child_process":"process","@prisma/client":"persistence",prisma:"persistence",pg:"persistence",mysql:"persistence",mysql2:"persistence",mongodb:"persistence",mongoose:"persistence",sqlite3:"persistence","better-sqlite3":"persistence",redis:"persistence",ioredis:"persistence",typeorm:"persistence",knex:"persistence","drizzle-orm":"persistence",sequelize:"persistence",kysely:"persistence","@supabase/supabase-js":"persistence"});function U(e){if(!e||e.startsWith(".")||e.startsWith("/"))return null;let t=me[e];if(t)return t;let n=e.indexOf("/");if(n<0)return null;let r=e.slice(0,n),i=me[r];if(i)return i;let a=e.indexOf("/",n+1);return a<0?null:me[e.slice(0,a)]??null}function X(e){let t=e.split(".");for(let n=t.length;n>=1;n-=1){let r=t.slice(0,n).join("."),i=Ae[r];if(i)return i}return null}function ee(e){if(e?.pure===!0)return[...he].sort();let n=(e?.capabilities?.deny??[]).filter(r=>he.includes(r));return[...new Set(n)].sort()}function Ce(e,t){if(t.length===0)return!1;let n=new Set(t),r=e.split(".");for(let i=r.length;i>=1;i-=1)if(n.has(r.slice(0,i).join(".")))return!0;return!1}function te(e){let t=new Set,n=new Set,r=Object.keys(Ae);for(let i of e?.forbiddenGlobals??[]){let a=r.filter(s=>s===i||s.startsWith(`${i}.`));if(a.length===0)n.add(i);else for(let s of a)t.add(`ambient:${s}`)}for(let i of ee(e)){t.add(`import:${i}`);for(let a of r)X(a)===i&&t.add(`ambient:${a}`)}return{atoms:[...t].sort(),rawGlobals:[...n].sort()}}var De=new Map;function Fe(e){return/[.*+?^${}()|[\]\\]/.test(e)?`\\${e}`:e}function Ie(e){let t="";for(let n=0;n<e.length;n+=1){let r=e[n];if(r==="\\"&&n+1<e.length){let i=e[n+1];if("*?{}[],".includes(i)||i==="\\"){t+="\\"+i,n+=1;continue}t+="/";continue}t+=r}return t}function ut(e){let t=0;for(let n=0;n<e.length;n+=1){let r=e[n];if(r==="\\"){n+=1;continue}if(r==="{")t+=1;else if(r==="}"&&(t-=1,t<0))return!1}return t===0}function B(e){let t=De.get(e);if(t)return t;let n=Ie(e),r=ut(n),i="",a=0;for(let c=0;c<n.length;c+=1){let p=n[c];p==="\\"&&c+1<n.length?(i+=Fe(n[c+1]),c+=1):p==="*"?n[c+1]==="*"?n[c+2]==="/"?(i+="(?:.*/)?",c+=2):(i+=".*",c+=1):i+="[^/]*":p==="?"?i+="[^/]":p==="{"&&r?(i+="(?:",a+=1):p==="}"&&r&&a>0?(i+=")",a-=1):p===","&&r&&a>0?i+="|":i+=Fe(p)}let s=new RegExp(`^${i}$`);return De.set(e,s),s}function xe(e){let t=Ie(String(e)),r=t.split("*")[0].split("/").filter(Boolean).length,i=t.replace(/\*/g,"").length;return r*1e4+i}function z(e,t){let n=String(e).split(/[/\\]/).join("/"),r,i=-1;for(let a of t??[])if(!(a.exclude??[]).some(s=>B(s).test(n))){for(let s of a.patterns??[])if(B(s).test(n)){let c=xe(s);c>i&&(i=c,r=a.name)}}return r}function je(e,t){if(!t?.length)return;let n=String(e).split(/[/\\]/).filter(Boolean),r=new Set(t.map(i=>String(i).toLowerCase()));for(let i=0;i<n.length-1;i+=1)if(r.has(n[i].toLowerCase()))return`${n[i]}/${n[i+1]}`}function yt(e){let t=new Set;for(let n of e??[]){let i=Ie(String(n)).split("/").filter(Boolean);for(let a=0;a<i.length;a+=1){let s=i[a];if((s==="**"||s==="*")&&a>0){let c=i[a-1];c&&!c.includes("*")&&!c.includes("{")&&!c.includes("}")&&t.add(c)}}}return[...t]}function gt(e,t,n){if(Array.isArray(e.sliceFolders)&&e.sliceFolders.length>0)return e.sliceFolders.filter(i=>typeof i=="string"&&i.length>0);let r=(n??[]).find(i=>i.name===t);return yt(r?.patterns)}function v(e,t,n,r){for(let i of e??[])if(!(i.from!==t||i.to!==n)&&i.allowed===!1){if(i.peerIsolation){let a=r?.fromPath,s=r?.toPath;if(!a||!s)continue;let c=gt(i,t,r?.layers);if(c.length===0)continue;let p=je(a,c),y=je(s,c);if(!p||!y)continue;if(p!==y)return i;continue}if(t!==n)return i}}function D(e,t){return t&&e.isStringLiteralLike(t)?t.text:void 0}function He(e,t){return e.getLineAndCharacterOfPosition(t.getStart(e)).line+1}function Ve(e,t){if(e.isImportDeclaration(t)){let n=t.importClause;if(!n)return!1;if(n.isTypeOnly)return!0;let r=n.namedBindings;return!!(r&&e.isNamedImports(r)&&r.elements.length>0&&r.elements.every(i=>i.isTypeOnly))}if(e.isExportDeclaration(t)){if(t.isTypeOnly)return!0;let n=t.exportClause;return!!(n&&e.isNamedExports(n)&&n.elements.length>0&&n.elements.every(r=>r.isTypeOnly))}return!1}function Ue(e,t){let n={noLib:!0,noResolve:!0,target:e.ScriptTarget.Latest},r=e.createCompilerHost(n,!0);return r.getSourceFile=i=>i===t.fileName?t:void 0,r.fileExists=i=>i===t.fileName,r.readFile=i=>i===t.fileName?t.text:void 0,e.createProgram([t.fileName],n,r).getTypeChecker()}function Se(e,t){try{return e.getSymbolAtLocation(t)}catch{return}}function ke(e,t,n,r){let i=r.parent&&e.isShorthandPropertyAssignment(r.parent)&&r.parent.name===r,a;try{a=i?t.getShorthandAssignmentValueSymbol(r.parent):Se(t,r)}catch{a=void 0}return!!a?.declarations?.some(s=>s.getSourceFile().fileName===n.fileName)}function F(e,t){let n,r=[],i=(s,c,p,y=!1)=>r.push({specifier:p,kind:c,line:He(t,s),typeOnly:y,unresolved:p===void 0,node:s}),a=s=>{if(e.isImportDeclaration(s))i(s,"import",D(e,s.moduleSpecifier),Ve(e,s));else if(e.isExportDeclaration(s)&&s.moduleSpecifier)i(s,"export",D(e,s.moduleSpecifier),Ve(e,s));else if(e.isImportEqualsDeclaration(s)&&e.isExternalModuleReference(s.moduleReference))i(s,"require",D(e,s.moduleReference.expression));else if(e.isCallExpression(s)){let c=s.expression.kind===e.SyntaxKind.ImportKeyword,y=e.isIdentifier(s.expression)&&s.expression.text==="require"&&!ke(e,n??(n=Ue(e,t)),t,s.expression);(c||y)&&i(s,y?"require":"dynamic-import",D(e,s.arguments[0]))}e.forEachChild(s,a)};return a(t),r}function mt(e,t){let n=[],r=t;for(;e.isPropertyAccessExpression(r)||e.isElementAccessExpression(r);){if(e.isPropertyAccessExpression(r))n.unshift(r.name.text);else{let i=D(e,r.argumentExpression);if(i===void 0)return;n.unshift(i)}r=r.expression}if(e.isIdentifier(r))return n.unshift(r.text),{root:r,segments:n}}function ht(e,t){let n=t.parent;return e.isPropertyAccessExpression(n)||e.isElementAccessExpression(n)?!1:e.isExpressionNode(t)&&!e.isInTypeQuery(t)||e.isShorthandPropertyAssignment(n)&&n.name===t}function Ge(e,t){let n=t[0]==="globalThis"?t.slice(1):t;for(let r=n.length;r>=1;r-=1){let i=n.slice(0,r).join(".");if(e.has(i))return i}}function j(e,t,n){if(n.length===0)return[];let r=new Set(n),i=Ue(e,t),a=new Map,s=new Set;for(let l of t.statements)if(e.isVariableStatement(l))for(let o of l.declarationList.declarations)e.isIdentifier(o.name)&&s.add(o.name.text);let c=l=>{let o=mt(e,l);if(!o)return;let g=Se(i,o.root),f=g?a.get(g):void 0;return f?[...f,...o.segments.slice(1)]:ke(e,i,t,o.root)||s.has(o.root.text)?void 0:o.segments};for(let l of t.statements)if(e.isVariableStatement(l))for(let o of l.declarationList.declarations){if(!o.initializer||!e.isIdentifier(o.name))continue;let g=c(o.initializer),f=Se(i,o.name);!g||!f||a.set(f,g)}let p=[],y=new Set,u=(l,o)=>{let g=He(t,o),f=`${l}:${o.getStart(t)}`;y.has(f)||(y.add(f),p.push({name:l,line:g,node:o}))},m=l=>{let o=l.parent&&(e.isPropertyAccessExpression(l.parent)||e.isElementAccessExpression(l.parent))&&l.parent.expression===l;if((e.isPropertyAccessExpression(l)||e.isElementAccessExpression(l))&&!o){let g=c(l),f=g?Ge(r,g):void 0;f&&u(f,l)}else e.isIdentifier(l)&&r.has(l.text)&&ht(e,l)&&!ke(e,i,t,l)&&u(l.text,l);if(e.isVariableDeclaration(l)&&e.isObjectBindingPattern(l.name)&&l.initializer){let g=c(l.initializer);if(g)for(let f of l.name.elements){if(!e.isIdentifier(f.name))continue;let I=f.propertyName?D(e,f.propertyName)??f.propertyName.text:f.name.text,x=Ge(r,[...g,I]);x&&u(x,l.initializer)}}e.forEachChild(l,m)};return m(t),p}function Ee(e,t){let n=[];for(let r of F(e,t)){if(r.typeOnly||!r.specifier)continue;let i=U(r.specifier);i&&n.push({capability:i,symbol:r.specifier,line:r.line,source:"import-based"})}for(let r of j(e,t,be)){let i=X(r.name);i&&n.push({capability:i,symbol:r.name,line:r.line,source:"ambient-global"})}return n.sort((r,i)=>r.line-i.line||r.capability.localeCompare(i.capability)||r.symbol.localeCompare(i.symbol))}var we={RAW_EVENT_PUBLISH:"Publish through a registered intent creator; raw event objects or intent strings bypass Ark contracts and tooling.",PUBLISH_MISSING_SOURCE:"Strict Ark publish calls must include metadata.source."};function _(e){return/^(Domain|Application|Adapter|Workflow|Job|Presentation|Reporting|Metadata|Security|Audit|Observability|Kernel)\.[A-Za-z0-9_.]+$/.test(e)}function Le(e){if(!e.publishCall)return[];let t=[];return(e.rawIntentName!==void 0&&_(e.rawIntentName)||e.objectHasIntent)&&t.push({ruleId:"RAW_EVENT_PUBLISH",message:we.RAW_EVENT_PUBLISH}),e.arkPublishCandidate&&!e.hasSource&&t.push({ruleId:"PUBLISH_MISSING_SOURCE",message:we.PUBLISH_MISSING_SOURCE}),t}function b(e,t,n){return{ruleId:e,code:e,message:t,...n}}function V(e,t){return e.slice(0,t).split(`
1
+ var ct="3.5.0";var lt="1.1";function k(e){return typeof e=="string"&&e.length>0?e:void 0}function Te(e,t){return Number.isInteger(e)&&Number(e)>0?Number(e):t}function pt(e,t,n){return e==="LAYER_IMPORT_VIOLATION"?t.typeOnly||n.targetTypeOnlyExports===!0||n.namedBindingsTypeOnly===!0?"Move the referenced type to a mutually allowed layer, use `import type`, then preflight again.":n.peerIsolation===!0?"Extract the shared dependency to a shared layer, then preflight again.":`Define a port in ${t.fromLayer??"the source layer"}, inject the ${t.toLayer??"outer-layer"} implementation, then preflight again.`:e==="FORBIDDEN_GLOBAL"?`Inject ${t.target??"the capability"} through a port, then preflight again.`:e==="CAPABILITY_VIOLATION"?`Define a ${k(n.capability)??"capability"} port in ${t.fromLayer??"the walled layer"}, bind the implementation outside it, then preflight again.`:e==="CIRCULAR_DEPENDENCY"?"Extract the shared dependency into a third module, then preflight again.":e==="RAW_EVENT_PUBLISH"?"Publish through a registered intent creator, then run Ark again.":e==="PUBLISH_MISSING_SOURCE"?"Add metadata.source to the publish call, then run Ark again.":`Resolve ${e} without weakening ark.config.json, then run Ark again.`}function ge(e,t="error"){let n=k(e.ruleId)??k(e.code)??"ARK_UNKNOWN",r=e.severity==="warning"?"warning":t,i={...k(e.target)?{target:k(e.target)}:{},...k(e.fromLayer)?{fromLayer:k(e.fromLayer)}:{},...k(e.toLayer)?{toLayer:k(e.toLayer)}:{},...typeof e.typeOnly=="boolean"?{typeOnly:e.typeOnly}:{}};return{ruleId:n,severity:r,message:k(e.message)??n,location:{file:k(e.file)??"<unknown>",line:Te(e.line,1),column:Te(e.column,1)},evidence:i,nextAction:k(e.nextAction)??pt(n,i,e)}}function dt(e){return{schemaVersion:"1.1",valid:e.valid,diagnostics:[...(e.violations??[]).map(t=>ge(t,"error")),...(e.warnings??[]).map(t=>ge(t,"warning"))]}}var ft={$schema:"https://json-schema.org/draft/2020-12/schema",$id:"https://unpkg.com/arkgate@2/schemas/ark.analysis-result.schema.json",title:"ArkGate analysis result",type:"object",additionalProperties:!1,required:["schemaVersion","valid","diagnostics"],properties:{schemaVersion:{const:"1.1"},valid:{type:"boolean"},diagnostics:{type:"array",items:{type:"object",additionalProperties:!1,required:["ruleId","severity","message","location","evidence"],properties:{ruleId:{type:"string",minLength:1},severity:{enum:["error","warning"]},message:{type:"string",minLength:1},location:{type:"object",additionalProperties:!1,required:["file","line","column"],properties:{file:{type:"string",minLength:1},line:{type:"integer",minimum:1},column:{type:"integer",minimum:1}}},evidence:{type:"object",additionalProperties:!1,properties:{target:{type:"string"},fromLayer:{type:"string"},toLayer:{type:"string"},typeOnly:{type:"boolean"}}},nextAction:{type:"string",minLength:1}}}}}};var he=Object.freeze(["network","filesystem","clock","randomness","environment","process","persistence"]),Ae=Object.freeze({fetch:"network",XMLHttpRequest:"network",Date:"clock","Date.now":"clock","Math.random":"randomness","process.env":"environment",process:"process"}),be=Object.freeze(Object.keys(Ae).sort()),me=Object.freeze({fs:"filesystem","node:fs":"filesystem","fs/promises":"filesystem","node:fs/promises":"filesystem","fs-extra":"filesystem","graceful-fs":"filesystem",memfs:"filesystem",chokidar:"filesystem",http:"network",https:"network",http2:"network",net:"network",tls:"network",dgram:"network",dns:"network","node:http":"network","node:https":"network","node:http2":"network","node:net":"network","node:tls":"network","node:dgram":"network","node:dns":"network",axios:"network",undici:"network","node-fetch":"network",got:"network",ky:"network",superagent:"network",ws:"network",process:"process","node:process":"process",child_process:"process","node:child_process":"process","@prisma/client":"persistence",prisma:"persistence",pg:"persistence",mysql:"persistence",mysql2:"persistence",mongodb:"persistence",mongoose:"persistence",sqlite3:"persistence","better-sqlite3":"persistence",redis:"persistence",ioredis:"persistence",typeorm:"persistence",knex:"persistence","drizzle-orm":"persistence",sequelize:"persistence",kysely:"persistence","@supabase/supabase-js":"persistence"});function U(e){if(!e||e.startsWith(".")||e.startsWith("/"))return null;let t=me[e];if(t)return t;let n=e.indexOf("/");if(n<0)return null;let r=e.slice(0,n),i=me[r];if(i)return i;let a=e.indexOf("/",n+1);return a<0?null:me[e.slice(0,a)]??null}function X(e){let t=e.split(".");for(let n=t.length;n>=1;n-=1){let r=t.slice(0,n).join("."),i=Ae[r];if(i)return i}return null}function ee(e){if(e?.pure===!0)return[...he].sort();let n=(e?.capabilities?.deny??[]).filter(r=>he.includes(r));return[...new Set(n)].sort()}function Ce(e,t){if(t.length===0)return!1;let n=new Set(t),r=e.split(".");for(let i=r.length;i>=1;i-=1)if(n.has(r.slice(0,i).join(".")))return!0;return!1}function te(e){let t=new Set,n=new Set,r=Object.keys(Ae);for(let i of e?.forbiddenGlobals??[]){let a=r.filter(s=>s===i||s.startsWith(`${i}.`));if(a.length===0)n.add(i);else for(let s of a)t.add(`ambient:${s}`)}for(let i of ee(e)){t.add(`import:${i}`);for(let a of r)X(a)===i&&t.add(`ambient:${a}`)}return{atoms:[...t].sort(),rawGlobals:[...n].sort()}}var De=new Map;function Fe(e){return/[.*+?^${}()|[\]\\]/.test(e)?`\\${e}`:e}function Ie(e){let t="";for(let n=0;n<e.length;n+=1){let r=e[n];if(r==="\\"&&n+1<e.length){let i=e[n+1];if("*?{}[],".includes(i)||i==="\\"){t+="\\"+i,n+=1;continue}t+="/";continue}t+=r}return t}function ut(e){let t=0;for(let n=0;n<e.length;n+=1){let r=e[n];if(r==="\\"){n+=1;continue}if(r==="{")t+=1;else if(r==="}"&&(t-=1,t<0))return!1}return t===0}function B(e){let t=De.get(e);if(t)return t;let n=Ie(e),r=ut(n),i="",a=0;for(let c=0;c<n.length;c+=1){let p=n[c];p==="\\"&&c+1<n.length?(i+=Fe(n[c+1]),c+=1):p==="*"?n[c+1]==="*"?n[c+2]==="/"?(i+="(?:.*/)?",c+=2):(i+=".*",c+=1):i+="[^/]*":p==="?"?i+="[^/]":p==="{"&&r?(i+="(?:",a+=1):p==="}"&&r&&a>0?(i+=")",a-=1):p===","&&r&&a>0?i+="|":i+=Fe(p)}let s=new RegExp(`^${i}$`);return De.set(e,s),s}function xe(e){let t=Ie(String(e)),r=t.split("*")[0].split("/").filter(Boolean).length,i=t.replace(/\*/g,"").length;return r*1e4+i}function z(e,t){let n=String(e).split(/[/\\]/).join("/"),r,i=-1;for(let a of t??[])if(!(a.exclude??[]).some(s=>B(s).test(n))){for(let s of a.patterns??[])if(B(s).test(n)){let c=xe(s);c>i&&(i=c,r=a.name)}}return r}function je(e,t){if(!t?.length)return;let n=String(e).split(/[/\\]/).filter(Boolean),r=new Set(t.map(i=>String(i).toLowerCase()));for(let i=0;i<n.length-1;i+=1)if(r.has(n[i].toLowerCase()))return`${n[i]}/${n[i+1]}`}function yt(e){let t=new Set;for(let n of e??[]){let i=Ie(String(n)).split("/").filter(Boolean);for(let a=0;a<i.length;a+=1){let s=i[a];if((s==="**"||s==="*")&&a>0){let c=i[a-1];c&&!c.includes("*")&&!c.includes("{")&&!c.includes("}")&&t.add(c)}}}return[...t]}function gt(e,t,n){if(Array.isArray(e.sliceFolders)&&e.sliceFolders.length>0)return e.sliceFolders.filter(i=>typeof i=="string"&&i.length>0);let r=(n??[]).find(i=>i.name===t);return yt(r?.patterns)}function v(e,t,n,r){for(let i of e??[])if(!(i.from!==t||i.to!==n)&&i.allowed===!1){if(i.peerIsolation){let a=r?.fromPath,s=r?.toPath;if(!a||!s)continue;let c=gt(i,t,r?.layers);if(c.length===0)continue;let p=je(a,c),y=je(s,c);if(!p||!y)continue;if(p!==y)return i;continue}if(t!==n)return i}}function D(e,t){return t&&e.isStringLiteralLike(t)?t.text:void 0}function He(e,t){return e.getLineAndCharacterOfPosition(t.getStart(e)).line+1}function Ve(e,t){if(e.isImportDeclaration(t)){let n=t.importClause;if(!n)return!1;if(n.isTypeOnly)return!0;let r=n.namedBindings;return!!(r&&e.isNamedImports(r)&&r.elements.length>0&&r.elements.every(i=>i.isTypeOnly))}if(e.isExportDeclaration(t)){if(t.isTypeOnly)return!0;let n=t.exportClause;return!!(n&&e.isNamedExports(n)&&n.elements.length>0&&n.elements.every(r=>r.isTypeOnly))}return!1}function Ue(e,t){let n={noLib:!0,noResolve:!0,target:e.ScriptTarget.Latest},r=e.createCompilerHost(n,!0);return r.getSourceFile=i=>i===t.fileName?t:void 0,r.fileExists=i=>i===t.fileName,r.readFile=i=>i===t.fileName?t.text:void 0,e.createProgram([t.fileName],n,r).getTypeChecker()}function Se(e,t){try{return e.getSymbolAtLocation(t)}catch{return}}function ke(e,t,n,r){let i=r.parent&&e.isShorthandPropertyAssignment(r.parent)&&r.parent.name===r,a;try{a=i?t.getShorthandAssignmentValueSymbol(r.parent):Se(t,r)}catch{a=void 0}return!!a?.declarations?.some(s=>s.getSourceFile().fileName===n.fileName)}function F(e,t){let n,r=[],i=(s,c,p,y=!1)=>r.push({specifier:p,kind:c,line:He(t,s),typeOnly:y,unresolved:p===void 0,node:s}),a=s=>{if(e.isImportDeclaration(s))i(s,"import",D(e,s.moduleSpecifier),Ve(e,s));else if(e.isExportDeclaration(s)&&s.moduleSpecifier)i(s,"export",D(e,s.moduleSpecifier),Ve(e,s));else if(e.isImportEqualsDeclaration(s)&&e.isExternalModuleReference(s.moduleReference))i(s,"require",D(e,s.moduleReference.expression));else if(e.isCallExpression(s)){let c=s.expression.kind===e.SyntaxKind.ImportKeyword,y=e.isIdentifier(s.expression)&&s.expression.text==="require"&&!ke(e,n??(n=Ue(e,t)),t,s.expression);(c||y)&&i(s,y?"require":"dynamic-import",D(e,s.arguments[0]))}e.forEachChild(s,a)};return a(t),r}function mt(e,t){let n=[],r=t;for(;e.isPropertyAccessExpression(r)||e.isElementAccessExpression(r);){if(e.isPropertyAccessExpression(r))n.unshift(r.name.text);else{let i=D(e,r.argumentExpression);if(i===void 0)return;n.unshift(i)}r=r.expression}if(e.isIdentifier(r))return n.unshift(r.text),{root:r,segments:n}}function ht(e,t){let n=t.parent;return e.isPropertyAccessExpression(n)||e.isElementAccessExpression(n)?!1:e.isExpressionNode(t)&&!e.isInTypeQuery(t)||e.isShorthandPropertyAssignment(n)&&n.name===t}function Ge(e,t){let n=t[0]==="globalThis"?t.slice(1):t;for(let r=n.length;r>=1;r-=1){let i=n.slice(0,r).join(".");if(e.has(i))return i}}function j(e,t,n){if(n.length===0)return[];let r=new Set(n),i=Ue(e,t),a=new Map,s=new Set;for(let l of t.statements)if(e.isVariableStatement(l))for(let o of l.declarationList.declarations)e.isIdentifier(o.name)&&s.add(o.name.text);let c=l=>{let o=mt(e,l);if(!o)return;let g=Se(i,o.root),f=g?a.get(g):void 0;return f?[...f,...o.segments.slice(1)]:ke(e,i,t,o.root)||s.has(o.root.text)?void 0:o.segments};for(let l of t.statements)if(e.isVariableStatement(l))for(let o of l.declarationList.declarations){if(!o.initializer||!e.isIdentifier(o.name))continue;let g=c(o.initializer),f=Se(i,o.name);!g||!f||a.set(f,g)}let p=[],y=new Set,u=(l,o)=>{let g=He(t,o),f=`${l}:${o.getStart(t)}`;y.has(f)||(y.add(f),p.push({name:l,line:g,node:o}))},m=l=>{let o=l.parent&&(e.isPropertyAccessExpression(l.parent)||e.isElementAccessExpression(l.parent))&&l.parent.expression===l;if((e.isPropertyAccessExpression(l)||e.isElementAccessExpression(l))&&!o){let g=c(l),f=g?Ge(r,g):void 0;f&&u(f,l)}else e.isIdentifier(l)&&r.has(l.text)&&ht(e,l)&&!ke(e,i,t,l)&&u(l.text,l);if(e.isVariableDeclaration(l)&&e.isObjectBindingPattern(l.name)&&l.initializer){let g=c(l.initializer);if(g)for(let f of l.name.elements){if(!e.isIdentifier(f.name))continue;let I=f.propertyName?D(e,f.propertyName)??f.propertyName.text:f.name.text,x=Ge(r,[...g,I]);x&&u(x,l.initializer)}}e.forEachChild(l,m)};return m(t),p}function Ee(e,t){let n=[];for(let r of F(e,t)){if(r.typeOnly||!r.specifier)continue;let i=U(r.specifier);i&&n.push({capability:i,symbol:r.specifier,line:r.line,source:"import-based"})}for(let r of j(e,t,be)){let i=X(r.name);i&&n.push({capability:i,symbol:r.name,line:r.line,source:"ambient-global"})}return n.sort((r,i)=>r.line-i.line||r.capability.localeCompare(i.capability)||r.symbol.localeCompare(i.symbol))}var we={RAW_EVENT_PUBLISH:"Publish through a registered intent creator; raw event objects or intent strings bypass Ark contracts and tooling.",PUBLISH_MISSING_SOURCE:"Strict Ark publish calls must include metadata.source."};function _(e){return/^(Domain|Application|Adapter|Workflow|Job|Presentation|Reporting|Metadata|Security|Audit|Observability|Kernel)\.[A-Za-z0-9_.]+$/.test(e)}function Le(e){if(!e.publishCall)return[];let t=[];return(e.rawIntentName!==void 0&&_(e.rawIntentName)||e.objectHasIntent)&&t.push({ruleId:"RAW_EVENT_PUBLISH",message:we.RAW_EVENT_PUBLISH}),e.arkPublishCandidate&&!e.hasSource&&t.push({ruleId:"PUBLISH_MISSING_SOURCE",message:we.PUBLISH_MISSING_SOURCE}),t}function b(e,t,n){return{ruleId:e,code:e,message:t,...n}}function V(e,t){return e.slice(0,t).split(`
2
2
  `).length}function At(e){let t=[],n=/['"`]([A-Za-z][A-Za-z0-9_.]*)['"`]/g,r;for(;(r=n.exec(e))!==null;)t.push({value:r[1],index:r.index});return t}function bt(e){let t=[],n=[{kind:"import",re:/\bimport\s+(?:type\s+)?(?:[^'"]*?\s+from\s*)?['"]([^'"]+)['"]/g},{kind:"export",re:/\bexport\s+(?:type\s+)?[^'"]*?\s+from\s*['"]([^'"]+)['"]/g},{kind:"dynamic-import",re:/\bimport\s*\(\s*['"]([^'"]+)['"]\s*\)/g},{kind:"require",re:/\brequire\s*\(\s*['"]([^'"]+)['"]\s*\)/g}];for(let r of n){let i;for(;(i=r.re.exec(e))!==null;){let a=i.index+i[0].indexOf(i[1]),s=i[0],c=r.kind==="import"&&/\bimport\s+type\b/.test(s)||r.kind==="export"&&/\bexport\s+type\b/.test(s);t.push({value:i[1],index:a,kind:r.kind,typeOnly:c})}}return t.sort((r,i)=>r.index-i.index)}function Ct(e,t){let n=e.createSourceFile("generated.ts",t,e.ScriptTarget.Latest,!0),r=[],i=a=>{e.isStringLiteralLike(a)&&r.push({value:a.text,index:a.getStart(n)}),e.forEachChild(a,i)};return i(n),r}function It(e){let t=e.toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);return["adapter","adapters","infra","infrastructure","persistence","repository","repositories","integration","database","db"].some(n=>t.includes(n))}function xt(e){let t=e.toLowerCase();return["sequelize","prisma","typeorm","mongoose","knex"].some(n=>t===n||t.startsWith(`${n}/`))}function St(e){let t=e.toLowerCase();return["adapter","infra","persistence","repository","repositories","integration","database"].some(n=>t.includes(n))}function q(e,t){return t&&e.isStringLiteralLike(t)?t.text:void 0}function kt(e,t){if(t&&(e.isIdentifier(t)||e.isStringLiteralLike(t)))return t.text}function Be(e,t,n){if(!(!t||!e.isObjectLiteralExpression(t)))return t.properties.find(r=>!e.isPropertyAssignment(r)&&!e.isShorthandPropertyAssignment(r)?!1:kt(e,r.name)===n)}function K(e,t,n){return Be(e,t,n)!==void 0}function Y(e,t,n){let r=Be(e,t,n);return r&&e.isPropertyAssignment(r)?r.initializer:void 0}function Et(e,t){let n=Y(e,t,"metadata");return K(e,n,"source")}function ze(e,t){return t?e.isIdentifier(t)?/^[A-Z]/.test(t.text):e.isPropertyAccessExpression(t)?ze(e,t.name):!1:!1}function wt(e,t){if(!e.isCallExpression(t))return!1;let n=t.expression;return e.isPropertyAccessExpression(n)?n.name.text==="publish":e.isIdentifier(n)&&n.text==="publish"}function Lt(e,t){if(!e.isCallExpression(t))return!1;let n=t.arguments[0],r=q(e,n);return r!==void 0&&_(r)||K(e,n,"intent")||ze(e,n)}function Pt(e,t){if(!e.isCallExpression(t))return!1;let[n,r,i]=t.arguments;return Et(e,n)||K(e,r,"source")||K(e,i,"source")}function Ot(e,t){if(!e.isCallExpression(t))return;let[n,r,i]=t.arguments,a=Y(e,n,"metadata");return q(e,Y(e,a,"source"))??q(e,Y(e,r,"source"))??q(e,Y(e,i,"source"))}function Rt(e,t,n,r){let i=e.createSourceFile("generated.ts",t,e.ScriptTarget.Latest,!0),a=n,s=a?.filePath,c=a?.layer,p=[],y=m=>i.getLineAndCharacterOfPosition(m.getStart(i)).line+1,u=m=>{if(wt(e,m)){let l=m.arguments[0],o=q(e,l);for(let f of Le({publishCall:!0,rawIntentName:o,objectHasIntent:K(e,l,"intent"),arkPublishCandidate:Lt(e,m),hasSource:Pt(e,m)}))p.push(b(f.ruleId,f.message,{line:y(m),filePath:s}));let g=Ot(e,m);if(r&&c&&g&&_(g)){let f=r.resolveLayer(g);f&&f!==c&&p.push(b("PUBLISH_SOURCE_LAYER_MISMATCH",`Publish source "${g}" resolves to ${f}, but the target file is classified as ${c}.`,{line:y(m),filePath:s,target:g,fromLayer:c,toLayer:f}))}}e.forEachChild(m,u)};return u(i),p}function Ye(e={}){let t=new Set((e.intents||[]).map(a=>typeof a=="string"?a:a.name)),n=e.forbiddenPatterns||[],r=new Set(e.infrastructureLayers??[]),i=e.enforceIntentAllowlist??t.size>0;return{validate(a,s){let c=[],p=s,y=p?.filePath,u=p?.layer,m=e.typescript,l=m?m.createSourceFile(y??"generated.ts",a,m.ScriptTarget.Latest,!0):void 0,o=l?F(e.typescript,l):void 0,g=o?o.filter(d=>d.specifier!==void 0).map(d=>({value:d.specifier,index:d.node.getStart(l),kind:d.kind,typeOnly:d.typeOnly})):bt(a),f=e.typescript?Ct(e.typescript,a):At(a);if(e.typescript&&!e.allowNonLiteralDynamicImport?.(y))for(let d of o?.filter(({unresolved:h})=>h)??[]){let h=d.kind==="require";c.push(b(h?"DYNAMIC_REQUIRE_NOT_ALLOWLISTED":"DYNAMIC_IMPORT_NOT_ALLOWLISTED",`Non-literal ${h?"require call":"dynamic import"} cannot be resolved statically; add the reviewed file to dynamicImportAllowlist.`,{line:d.line,filePath:y}))}let I=u!==void 0&&(r.has(u)||St(u)),x=u!==void 0?` If "${u}" is an infrastructure layer, mark it in ark.config.json with "mayImportInfrastructure": true (or name it with an infra token like Adapters/Persistence/Repository).`:"";for(let d of n)if(d instanceof RegExp){d.lastIndex=0;let h=d.exec(a);d.lastIndex=0,h&&c.push(b("FORBIDDEN_PATTERN",`Forbidden pattern matched: ${d}`,{line:h.index===void 0?void 0:V(a,h.index),filePath:y,suggestion:"Remove infrastructure imports from domain/application layers."+x}))}else a.includes(d)&&c.push(b("FORBIDDEN_SUBSTRING",`Forbidden substring: ${d}`,{line:V(a,a.indexOf(d)),filePath:y}));for(let d of g){let h=e.resolveImportTarget?.(d.value,y)??(e.resolveImportLayer?{layer:e.resolveImportLayer(d.value,y)}:void 0),A=typeof y=="string"?e.resolveImportTarget?.(y)??(e.resolveImportLayer?{layer:u,relPath:void 0}:void 0):void 0,T=h?.layer;if(T&&u){let Q=v(e.architectureProfile?.rules,u,T,{fromPath:A?.relPath,toPath:h?.relPath,layers:e.architectureLayers});if(Q){if(d.typeOnly&&!Q.peerIsolation)continue;let ye=!!Q.peerIsolation;c.push(b("LAYER_IMPORT_VIOLATION",Q.message??(ye?`Layer "${u}" must not import across slices into "${T}".`:`Layer "${u}" must not import "${T}".`),{line:V(a,d.index),source:d.value,target:d.value,filePath:y,fromLayer:u,toLayer:T,suggestion:ye?"Extract shared code to a shared layer, or coordinate slices via events/ports \u2014 do not import across feature/context slices.":"Depend on a port/interface owned by an inner layer instead, or move this code to a layer allowed to make this import.",details:{importKind:d.kind,peerIsolation:ye,...d.typeOnly?{typeOnly:!0}:{}}}));continue}if(T!==u)continue}I||d.typeOnly||!It(d.value)&&!xt(d.value)||c.push(b("FORBIDDEN_IMPORT",`Forbidden ${d.kind} target: "${d.value}".`,{line:V(a,d.index),source:d.value,target:d.value,filePath:y,suggestion:"Route infrastructure access through an allowed adapter or port boundary."+x,details:{importKind:d.kind}}))}if(e.policies)for(let d of e.policies){let h=d.check({source:a,context:s});if(h!==!0)if(Array.isArray(h))for(let A of h)c.push(b("POLICY_VIOLATION",A.message,{filePath:y,suggestion:`Fix violation of policy "${d.name}".`}));else h===!1?c.push(b("POLICY_VIOLATION",`Policy ${d.name} failed on generated code`)):c.push(b("POLICY_VIOLATION",h.message))}if(i&&t.size>0)for(let d of f)_(d.value)&&!t.has(d.value)&&c.push(b("UNKNOWN_INTENT",`Unknown intent reference: "${d.value}"`,{line:V(a,d.index),filePath:y,target:d.value,suggestion:`Register intent "${d.value}" via defineIntent() or remove the reference.`}));if(e.architectureProfile&&u)for(let d of f){if(!_(d.value))continue;let h=e.architectureProfile.resolveLayer(d.value);if(!h)continue;let A=v(e.architectureProfile.rules,u,h);A&&c.push(b("LAYER_REFERENCE_VIOLATION",A.message??`Layer "${u}" must not reference "${h}" through "${d.value}".`,{line:V(a,d.index),filePath:y,target:d.value,fromLayer:u,toLayer:h,suggestion:"Route the dependency through an allowed intent, port, or event.",details:{rule:A}}))}if(e.extensions)for(let d of e.extensions)try{let h=d.analyze(a,s);c.push(...h)}catch(h){c.push(b("EXTENSION_ERROR",`Extension "${d.name}" failed: ${h instanceof Error?h.message:String(h)}`))}if(e.typescript&&l&&u&&e.forbiddenGlobals?.[u]?.length)try{c.push(...j(e.typescript,l,e.forbiddenGlobals[u]).map(d=>b("FORBIDDEN_GLOBAL",`${u} must not use the ambient global "${d.name}".`,{line:d.line,filePath:y,target:d.name,fromLayer:u,suggestion:"Inject the capability through a port (e.g. a Clock, IdGenerator, or HttpPort) instead of reaching for the ambient global."})))}catch(d){c.push(b("AST_ANALYZER_ERROR",`TypeScript AST analyzer failed: ${d instanceof Error?d.message:String(d)}`))}if(e.typescript&&l&&u&&e.capabilityWalls?.[u]?.length)try{let d=new Set(e.capabilityWalls[u]),h=e.forbiddenGlobals?.[u]??[];for(let A of Ee(e.typescript,l))d.has(A.capability)&&(A.source==="ambient-global"&&Ce(A.symbol,h)||c.push(b("CAPABILITY_VIOLATION",A.source==="import-based"?`${u} denies the ${A.capability} capability; found import of "${A.symbol}".`:`${u} denies the ${A.capability} capability; found ambient "${A.symbol}".`,{line:A.line,filePath:y,target:A.symbol,capability:A.capability,fromLayer:u,suggestion:"Define a small port (ClockPort, HttpPort, StoragePort) and bind the implementation in an adapter layer."})))}catch(d){c.push(b("AST_ANALYZER_ERROR",`TypeScript AST analyzer failed: ${d instanceof Error?d.message:String(d)}`))}if(e.typescript)try{c.push(...Rt(e.typescript,a,s,e.architectureProfile))}catch(d){c.push(b("AST_ANALYZER_ERROR",`TypeScript AST analyzer failed: ${d instanceof Error?d.message:String(d)}`))}return{valid:c.length===0,violations:c}}}}var $t="1.0",re="https://unpkg.com/arkgate@2/schemas/ark.config.schema.json",qe=["DomainModel","ApplicationOrchestration","PersistenceAdapters","IntegrationAdapters","WorkflowSagaEngine","BackgroundJobsScheduling","PresentationAdapters","ReportingReadModels","ExtensibilityMetadata","SecurityAuditObservability","Kernel"],vt=new Set(["PresentationAdapters->ApplicationOrchestration","ApplicationOrchestration->DomainModel","WorkflowSagaEngine->ApplicationOrchestration","WorkflowSagaEngine->DomainModel","BackgroundJobsScheduling->ApplicationOrchestration"]);function _t(){let e=[];for(let t of qe)for(let n of qe)t===n||vt.has(`${t}->${n}`)||e.push({from:t,to:n,allowed:!1});return e}var ie=_t();var O={type:"array",items:{type:"string",minLength:1},uniqueItems:!0},Oe={$schema:"https://json-schema.org/draft/2020-12/schema",$id:re,title:"ArkGate architecture contract",description:"Versioned contract consumed identically by ArkGate CLI, MCP, and ESLint surfaces.",type:"object",additionalProperties:!1,required:["$schema","schemaVersion","include","layers","rules"],properties:{$schema:{type:"string",minLength:1,default:re,description:"Editor-facing URL or local path for this JSON Schema."},schemaVersion:{type:"string",const:"1.0",default:"1.0"},name:{type:"string",minLength:1},include:{...O,minItems:1,default:["src"]},exclude:{...O,default:[]},excludeGenerated:{type:"boolean",default:!0},frameworkOverlay:{type:"string",minLength:1},layers:{type:"array",default:[],items:{$ref:"#/$defs/layer"}},rules:{type:"array",default:ie,items:{$ref:"#/$defs/rule"}},cyclePolicy:{type:"string",enum:["strict","soft","framework-soft","off"],default:"strict"},dynamicImportAllowlist:{...O,default:[]},safety:{$ref:"#/$defs/safety",default:{maxTsSuppressions:0,maxAnyCasts:0,allowInMemory:!1,allowDisabledPeerIsolation:!1}}},$defs:{layer:{type:"object",additionalProperties:!1,required:["name","patterns"],properties:{name:{type:"string",minLength:1},patterns:{...O,minItems:1},exclude:O,intentPrefixes:O,description:{type:"string",minLength:1},forbiddenGlobals:O,capabilities:{type:"object",additionalProperties:!1,properties:{deny:{type:"array",uniqueItems:!0,items:{type:"string",enum:["network","filesystem","clock","randomness","environment","process","persistence"]}}}},pure:{type:"boolean"},mayImportInfrastructure:{type:"boolean"},optional:{type:"boolean"}}},rule:{type:"object",additionalProperties:!1,required:["from","to","allowed"],properties:{from:{type:"string",minLength:1},to:{type:"string",minLength:1},allowed:{type:"boolean"},message:{type:"string",minLength:1},peerIsolation:{type:"boolean"},sliceFolders:{...O,minItems:1}}},safety:{type:"object",additionalProperties:!1,properties:{maxTsSuppressions:{type:"integer",minimum:0,default:0},maxAnyCasts:{type:"integer",minimum:0,default:0},allowInMemory:{type:"boolean",default:!1},allowDisabledPeerIsolation:{type:"boolean",default:!1}}}}},H=class extends Error{issues;source;constructor(t,n){super(`Invalid ArkGate config (${t}):
3
3
  ${n.map(r=>`- ${r.path}: ${r.message}`).join(`
4
4
  `)}`),this.name="ArkConfigValidationError",this.source=t,this.issues=n}};function Ke(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function Pe(e,t){return/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(t)?`${e}.${t}`:`${e}[${JSON.stringify(t)}]`}function G(e){return e===null?"null":Array.isArray(e)?"array":typeof e}function Nt(e,t){let n="#/$defs/";if(e.startsWith(n))return t.$defs[e.slice(n.length)]}function ne(e,t,n,r,i){if(t.$ref){let a=Nt(t.$ref,r);if(!a){i.push({path:n,message:`schema reference ${t.$ref} cannot be resolved`});return}ne(e,a,n,r,i);return}if(t.const!==void 0&&!Object.is(e,t.const)){i.push({path:n,message:`must equal ${JSON.stringify(t.const)}`});return}if(t.enum&&!t.enum.some(a=>Object.is(a,e))){i.push({path:n,message:`must be one of ${t.enum.map(String).join(", ")}`});return}if(t.type==="object"){if(!Ke(e)){i.push({path:n,message:`must be an object; received ${G(e)}`});return}let a=t.properties??{};for(let s of t.required??[])e[s]===void 0&&i.push({path:Pe(n,s),message:"is required"});if(t.additionalProperties===!1)for(let s of Object.keys(e))s in a||i.push({path:Pe(n,s),message:"unknown field"});for(let[s,c]of Object.entries(a))e[s]!==void 0&&ne(e[s],c,Pe(n,s),r,i);return}if(t.type==="array"){if(!Array.isArray(e)){i.push({path:n,message:`must be an array; received ${G(e)}`});return}if(t.minItems!==void 0&&e.length<t.minItems&&i.push({path:n,message:`must contain at least ${t.minItems} item(s)`}),t.uniqueItems){let a=e.map(s=>JSON.stringify(s));new Set(a).size!==a.length&&i.push({path:n,message:"must not contain duplicate items"})}t.items&&e.forEach((a,s)=>ne(a,t.items,`${n}[${s}]`,r,i));return}if(t.type==="string"){if(typeof e!="string"){i.push({path:n,message:`must be a string; received ${G(e)}`});return}t.minLength!==void 0&&e.length<t.minLength&&i.push({path:n,message:`must contain at least ${t.minLength} character(s)`});return}if(t.type==="boolean"){typeof e!="boolean"&&i.push({path:n,message:`must be a boolean; received ${G(e)}`});return}if(t.type==="integer"){if(!Number.isInteger(e)){i.push({path:n,message:`must be an integer; received ${G(e)}`});return}t.minimum!==void 0&&e<t.minimum&&i.push({path:n,message:`must be at least ${t.minimum}`})}}function Mt(e){return{...e,$schema:e.$schema===void 0?re:e.$schema,schemaVersion:e.schemaVersion===void 0?"1.0":e.schemaVersion,include:e.include===void 0?["src"]:e.include,layers:e.layers===void 0?[]:e.layers,rules:e.rules===void 0?ie.map(t=>({...t})):e.rules}}function Tt(e,t="ark.config.json"){if(!Ke(e))throw new H(t,[{path:"$",message:`must be an object; received ${G(e)}`}]);let n=e.schemaVersion===void 0?"unversioned":null;if(e.schemaVersion!==void 0&&e.schemaVersion!=="1.0")throw new H(t,[{path:"$.schemaVersion",message:`unsupported version ${JSON.stringify(e.schemaVersion)}; expected 1.0`}]);return{candidate:Mt(e),migratedFrom:n}}function ae(e,t="ark.config.json"){let{candidate:n,migratedFrom:r}=Tt(e,t),i=[];if(ne(n,Oe,"$",Oe,i),i.length>0)throw new H(t,i);return{config:n,migratedFrom:r}}function Re(e,t="ark.config.json"){let n;try{n=JSON.parse(e)}catch(r){throw new H(t,[{path:"$",message:`invalid JSON: ${r instanceof Error?r.message:String(r)}`}])}return ae(n,t)}function We(e){let t={$schema:typeof e.$schema=="string"&&e.$schema.length>0?e.$schema:re,schemaVersion:"1.0"};for(let[n,r]of Object.entries(e))n!=="$schema"&&n!=="schemaVersion"&&(t[n]=r);return t}function Dt(e){return e.endsWith(".")?e:`${e}.`}function Ft(e,t){let n=e.prefixes.length?Math.max(...e.prefixes.map(i=>i.length)):0;return(t.prefixes.length?Math.max(...t.prefixes.map(i=>i.length)):0)-n}function se(e){let t=e.layers.map(i=>({...i,prefixes:i.prefixes.map(Dt)})),n=[...t].sort(Ft),r=[...e.rules??[]];return{name:e.name,layers:t,rules:r,resolveLayer(i){return t.find(a=>a.match?.(i))?.name??n.find(a=>a.prefixes.some(s=>i.startsWith(s)))?.name}}}function Je(e,t={}){return se({name:t.name??e.name??"ark.config.json",layers:e.layers.map((n,r)=>({name:n.name,prefixes:n.intentPrefixes??[],description:n.description,order:r+1})),rules:e.rules??[]})}var jt=[{name:"DomainModel",prefixes:["Domain"],description:"Rich domain model, business rules, and domain events.",order:1},{name:"ApplicationOrchestration",prefixes:["Application"],description:"Use cases and command orchestration.",order:2},{name:"PersistenceAdapters",prefixes:["Adapter.Persistence","Adapter.Repository"],description:"Database, repository, and storage adapters.",order:3},{name:"IntegrationAdapters",prefixes:["Adapter.Integration","Adapter.External"],description:"External systems, APIs, and integration adapters.",order:4},{name:"WorkflowSagaEngine",prefixes:["Workflow"],description:"Sagas, workflows, and long-running processes.",order:5},{name:"BackgroundJobsScheduling",prefixes:["Job"],description:"Background jobs, scheduled work, and async processors.",order:6},{name:"PresentationAdapters",prefixes:["Presentation","Adapter.Presentation","Adapter.Api"],description:"API, UI, controller, and presentation adapters.",order:7},{name:"ReportingReadModels",prefixes:["Reporting"],description:"Read models, projections, and reporting surfaces.",order:8},{name:"ExtensibilityMetadata",prefixes:["Metadata"],description:"Metadata, extensions, and schema contracts.",order:9},{name:"SecurityAuditObservability",prefixes:["Security","Audit","Observability"],description:"Security, audit, and observability concerns.",order:10},{name:"Kernel",prefixes:["Kernel"],description:"Ark-owned governance and kernel signals.",order:11}],oe=se({name:"Ark 11-layer Hexagonal Event-Driven Profile",layers:jt,rules:ie.map(e=>({...e}))}),Vt={DomainModel:["domain"],ApplicationOrchestration:["application","app"],PersistenceAdapters:["adapters/persistence","adapters/repository","repositories","infra/persistence"],IntegrationAdapters:["adapters/integration","adapters/external","integrations"],WorkflowSagaEngine:["workflows","sagas"],BackgroundJobsScheduling:["jobs","schedules"],PresentationAdapters:["presentation","adapters/presentation","adapters/api"],ReportingReadModels:["reporting","read-models","projections"],ExtensibilityMetadata:["metadata","extensions"],SecurityAuditObservability:["security","audit","observability"],Kernel:["kernel"]};function Ze(e={}){let t=e.rootDir??"src",n=e.optionalLayers??!0,r=t==="."?"":`${t}/`;return We({include:e.include??[t],layers:oe.layers.map(i=>({name:i.name,patterns:(Vt[i.name]??[i.name]).map(a=>`${r}${a}/**`),intentPrefixes:i.prefixes,optional:n})),rules:[...oe.rules]})}var $e="1.0";function R(e){let t=2166136261;for(let n=0;n<e.length;n+=1)t^=e.charCodeAt(n),t=Math.imul(t,16777619);return`fnv1a-${(t>>>0).toString(16).padStart(8,"0")}`}function L(e){if(e===null||typeof e!="object")return JSON.stringify(e);if(Array.isArray(e))return`[${e.map(L).join(",")}]`;let t=e;return`{${Object.keys(t).sort().map(n=>`${JSON.stringify(n)}:${L(t[n])}`).join(",")}}`}function E(e){return`${e.from}->${e.to}`}function W(e,t,n,r="dependency"){return{id:`${e}:${r}:${E(t)}`,classification:e,subject:"dependency",from:t.from,to:t.to,message:n,...e==="missing"?{nextAction:`Add the planned dependency ${E(t)} to the candidate, then preflight again.`}:e==="contradictory"?{nextAction:`Replace the reverse dependency with ${E(t)}, then preflight again.`}:e==="unplanned"?{nextAction:`Remove the unplanned dependency ${E(t)} from the candidate, then preflight again.`}:{}}}function ce(e){let t=[],n=new Map(e.changeMap.map.files.map(l=>[l.path,l])),r=new Map(e.changes.map(l=>[l.path,l])),i=new Map(e.changeMap.map.dependencies.map(l=>[E(l),l])),a=new Map(e.baseDependencies.map(l=>[E(l),l])),s=new Map(e.candidateDependencies.map(l=>[E(l),l]));for(let l of[...n.values()].sort((o,g)=>o.path.localeCompare(g.path))){let o=r.get(l.path);o?o.operation!==l.operation?t.push({id:`contradictory:file:${l.path}`,classification:"contradictory",subject:"file",path:l.path,expectedOperation:l.operation,actualOperation:o.operation,message:`${l.path} was planned as ${l.operation} but the actual operation is ${o.operation}.`,nextAction:`Change ${l.path} to the planned ${l.operation} operation, then preflight again.`}):t.push({id:`satisfied:file:${l.path}`,classification:"satisfied",subject:"file",path:l.path,expectedOperation:l.operation,actualOperation:o.operation,message:`${l.path} matches the planned ${l.operation} operation.`}):t.push({id:`missing:file:${l.path}`,classification:"missing",subject:"file",path:l.path,expectedOperation:l.operation,message:`${l.path} was planned as ${l.operation} but is absent from the actual change.`,nextAction:`${l.operation[0].toUpperCase()}${l.operation.slice(1)} ${l.path} in the complete change set, then preflight again.`})}for(let l of[...r.values()].sort((o,g)=>o.path.localeCompare(g.path)))n.has(l.path)||t.push({id:`unplanned:file:${l.path}`,classification:"unplanned",subject:"file",path:l.path,actualOperation:l.operation,message:`${l.path} has an unplanned ${l.operation} operation.`,nextAction:`Remove ${l.path} from the change set, then preflight again.`});let c=new Set;for(let l of[...i.values()].sort((o,g)=>E(o).localeCompare(E(g)))){if(s.has(E(l))){t.push(W("satisfied",l,`${l.from} -> ${l.to} exists in the candidate architecture.`));continue}let o={from:l.to,to:l.from};s.has(E(o))?(c.add(E(o)),t.push(W("contradictory",l,`${l.from} -> ${l.to} was planned, but the candidate contains the reverse edge.`))):t.push(W("missing",l,`${l.from} -> ${l.to} is absent from the candidate architecture.`))}let p=new Set([...n.keys(),...r.keys()]);for(let[l,o]of[...s].sort(([g],[f])=>g.localeCompare(f)))a.has(l)||i.has(l)||c.has(l)||!p.has(o.from)&&!p.has(o.to)||t.push({...W("unplanned",o,`${o.from} -> ${o.to} was added without a matching planned dependency.`,"dependency-added"),actualOperation:"added"});let y=new Set(e.changeMap.map.files.filter(l=>l.operation==="delete").map(l=>l.path));for(let[l,o]of[...a].sort(([g],[f])=>g.localeCompare(f)))s.has(l)||y.has(o.from)||y.has(o.to)||!p.has(o.from)&&!p.has(o.to)||t.push({...W("unplanned",o,`${o.from} -> ${o.to} was removed without a planned file deletion.`,"dependency-removed"),actualOperation:"removed"});let u={satisfied:0,missing:1,contradictory:2,unplanned:3};t.sort((l,o)=>u[l.classification]-u[o.classification]||(l.subject===o.subject?0:l.subject==="file"?-1:1)||l.id.localeCompare(o.id));let m={satisfied:t.filter(l=>l.classification==="satisfied").length,missing:t.filter(l=>l.classification==="missing").length,contradictory:t.filter(l=>l.classification==="contradictory").length,unplanned:t.filter(l=>l.classification==="unplanned").length};return{schemaVersion:"1.0",readOnly:!0,changeMapHash:e.changeMap.hash,structurallyConverged:m.missing===0&&m.contradictory===0&&m.unplanned===0,behavioralCompletion:"not-evaluated",summary:m,findings:t}}var ve="1.0";function C(e,t){e.push({id:`${t.classification}:${t.path}:${t.kind}`,path:t.path,classification:t.classification,message:t.message,...t.classification==="weakening"||t.classification==="judgment-required"?{nextAction:`Restore the previous protection at ${t.path}, then run ArkGate again.`}:{},...t.before===void 0?{}:{before:t.before},...t.after===void 0?{}:{after:t.after}})}function w(e){return[...new Set(e??[])].sort()}function N(e,t,n,r,i){let a=w(n),s=w(r),c=new Set(a),p=new Set(s),y=s.filter(m=>!c.has(m)),u=a.filter(m=>!p.has(m));y.length===0&&u.length===0||(y.length>0&&C(e,{kind:"added",path:t,classification:i.added,message:i.addedMessage,before:a,after:s}),u.length>0&&C(e,{kind:"removed",path:t,classification:i.removed,message:i.removedMessage,before:a,after:s}))}function le(e,t,n,r,i,a,s){if(n===r)return;C(e,{kind:r?"enabled":"disabled",path:t,classification:r?i:i==="strengthening"?"weakening":"strengthening",message:r?a:s,before:n,after:r})}function pe(e,t){let n=new Map,r=new Set;for(let i of e){let a=t(i);n.has(a)?r.add(a):n.set(a,i)}return{values:n,duplicates:[...r].sort()}}function Gt(e,t,n){let r=pe(t,a=>a.name),i=pe(n,a=>a.name);(r.duplicates.length>0||i.duplicates.length>0)&&C(e,{kind:"duplicate-layer",path:"$.layers",classification:"judgment-required",message:"Duplicate layer names make policy ownership ambiguous.",before:r.duplicates,after:i.duplicates});for(let a of[...new Set([...r.values.keys(),...i.values.keys()])].sort()){let s=r.values.get(a),c=i.values.get(a),p=`$.layers[${a}]`;if(!s&&c){C(e,{kind:"layer-added",path:p,classification:"judgment-required",message:"A layer was added; verify overlap, ownership, and rule coverage.",after:c});continue}if(s&&!c){C(e,{kind:"layer-removed",path:p,classification:"weakening",message:"Removing a layer can leave its source paths ungoverned.",before:s});continue}if(!s||!c)continue;N(e,`${p}.patterns`,s.patterns,c.patterns,{added:"strengthening",removed:"weakening",addedMessage:"Additional paths are governed by this layer.",removedMessage:"Paths were removed from this layer and may become ungoverned."}),N(e,`${p}.exclude`,s.exclude,c.exclude,{added:"weakening",removed:"strengthening",addedMessage:"Additional paths are excluded from this layer.",removedMessage:"Fewer paths are excluded from this layer."});let y=te(s),u=te(c);N(e,`${p}.forbiddenGlobals`,y.rawGlobals,u.rawGlobals,{added:"strengthening",removed:"weakening",addedMessage:"Additional forbidden globals are enforced in this layer.",removedMessage:"A forbidden-global protection was removed from this layer."}),N(e,`${p}.capabilities`,y.atoms,u.atoms,{added:"strengthening",removed:"weakening",addedMessage:"Additional ambient/import protection is enforced in this layer (coverage atoms).",removedMessage:"An ambient/import protection was lost from this layer (coverage atoms)."}),w(s.intentPrefixes).join("\0")!==w(c.intentPrefixes).join("\0")&&C(e,{kind:"intent-prefixes-changed",path:`${p}.intentPrefixes`,classification:"judgment-required",message:"Intent ownership changed and must be reviewed against publishers and consumers.",before:w(s.intentPrefixes),after:w(c.intentPrefixes)}),le(e,`${p}.mayImportInfrastructure`,s.mayImportInfrastructure===!0,c.mayImportInfrastructure===!0,"weakening","The layer may now import infrastructure directly.","Direct infrastructure imports are no longer allowed for this layer."),le(e,`${p}.optional`,s.optional===!0,c.optional===!0,"weakening","The layer is now optional and can be absent without a strict warning.","The layer is now required when its contract is active.")}}function Ht(e,t,n){let r=s=>`${s.from}->${s.to}`,i=pe(t,r),a=pe(n,r);(i.duplicates.length>0||a.duplicates.length>0)&&C(e,{kind:"duplicate-rule",path:"$.rules",classification:"judgment-required",message:"Duplicate rule edges make the effective verdict order-dependent.",before:i.duplicates,after:a.duplicates});for(let s of[...new Set([...i.values.keys(),...a.values.keys()])].sort()){let c=i.values.get(s),p=a.values.get(s),y=`$.rules[${s}]`;if(!c&&p){p.allowed===!1&&C(e,{kind:"deny-added",path:y,classification:"strengthening",message:"A denied dependency edge was added.",after:p});continue}if(c&&!p){c.allowed===!1&&C(e,{kind:"deny-removed",path:y,classification:"weakening",message:"A denied dependency edge was removed.",before:c});continue}if(!c||!p)continue;c.allowed!==p.allowed&&C(e,{kind:p.allowed?"deny-disabled":"deny-enabled",path:`${y}.allowed`,classification:p.allowed?"weakening":"strengthening",message:p.allowed?"A previously denied dependency edge is now allowed.":"A dependency edge is now denied.",before:c.allowed,after:p.allowed});let u=c.peerIsolation===!0,m=p.peerIsolation===!0;if(u!==m){let l=c.from===c.to&&p.from===p.to;C(e,{kind:m?"peer-isolation-enabled":"peer-isolation-disabled",path:`${y}.peerIsolation`,classification:l?m?"strengthening":"weakening":"judgment-required",message:l?m?"Cross-slice dependencies inside this layer are now denied.":"Cross-slice dependencies inside this layer are no longer denied.":"Changing peer isolation on a cross-layer edge changes the denial scope.",before:u,after:m})}w(c.sliceFolders).join("\0")!==w(p.sliceFolders).join("\0")&&C(e,{kind:"slice-folders-changed",path:`${y}.sliceFolders`,classification:"judgment-required",message:"Slice ownership folders changed and can reclassify existing dependencies.",before:w(c.sliceFolders),after:w(p.sliceFolders)})}}function Ut(e,t,n){let r=t.safety??{},i=n.safety??{};for(let a of["maxTsSuppressions","maxAnyCasts"]){let s=r[a]??0,c=i[a]??0;s!==c&&C(e,{kind:c>s?"threshold-raised":"threshold-lowered",path:`$.safety.${a}`,classification:c>s?"weakening":"strengthening",message:c>s?"The safety threshold allows more violations.":"The safety threshold allows fewer violations.",before:s,after:c})}for(let a of["allowInMemory","allowDisabledPeerIsolation"])le(e,`$.safety.${a}`,r[a]===!0,i[a]===!0,"weakening","A safety exception was enabled.","A safety exception was disabled.")}function Bt(e){return e.some(t=>t.classification==="weakening")?"weakening":e.some(t=>t.classification==="judgment-required")?"judgment-required":e.some(t=>t.classification==="strengthening")?"strengthening":"neutral"}function _e(e,t){let n=[];N(n,"$.include",e.include,t.include,{added:"strengthening",removed:"weakening",addedMessage:"Additional project roots are governed.",removedMessage:"Project roots were removed from governance."}),N(n,"$.exclude",e.exclude,t.exclude,{added:"weakening",removed:"strengthening",addedMessage:"Additional project paths are excluded from governance.",removedMessage:"Fewer project paths are excluded from governance."}),N(n,"$.dynamicImportAllowlist",e.dynamicImportAllowlist,t.dynamicImportAllowlist,{added:"weakening",removed:"strengthening",addedMessage:"Additional files may use non-literal dynamic imports.",removedMessage:"Fewer files may use non-literal dynamic imports."}),le(n,"$.excludeGenerated",e.excludeGenerated!==!1,t.excludeGenerated!==!1,"weakening","Generated source is now excluded from governance.","Generated source is now governed.");let r={off:0,soft:1,"framework-soft":1,strict:2},i=e.cyclePolicy??"strict",a=t.cyclePolicy??"strict";if(i!==a){let s=r[a]===r[i]?"judgment-required":r[a]>r[i]?"strengthening":"weakening";C(n,{kind:"cycle-policy-changed",path:"$.cyclePolicy",classification:s,message:"The cycle enforcement level changed.",before:i,after:a})}return(e.frameworkOverlay??null)!==(t.frameworkOverlay??null)&&C(n,{kind:"framework-overlay-changed",path:"$.frameworkOverlay",classification:"judgment-required",message:"The framework overlay changed and may alter effective layer matching.",before:e.frameworkOverlay??null,after:t.frameworkOverlay??null}),Gt(n,e.layers,t.layers),Ht(n,e.rules,t.rules),Ut(n,e,t),n.sort((s,c)=>s.path.localeCompare(c.path)||s.id.localeCompare(c.id)),{schemaVersion:ve,classification:Bt(n),findings:n}}function Ne(e,t){if(!e||e.schemaVersion!==ve||typeof e.basePolicyHash!="string"||typeof e.candidatePolicyHash!="string"||typeof e.reason!="string"||!Array.isArray(e.findingIds)||e.findingIds.some(i=>typeof i!="string")||e.reason.trim().length===0||e.basePolicyHash!==t.basePolicyHash||e.candidatePolicyHash!==t.candidatePolicyHash)return!1;let n=w(e.findingIds),r=w(t.findingIds);return n.length===r.length&&n.every((i,a)=>i===r[a])}function M(e){let t=[];for(let n of e.replace(/\\/g,"/").split("/"))!n||n==="."||(n===".."&&t.length>0&&t.at(-1)!==".."?t.pop():t.push(n));return t.join("/")}function Qe(e){return e!==void 0&&/[A-Za-z0-9_$]/.test(e)}function $(e,t){for(;t<e.length&&/\s/.test(e[t]);)t+=1;return t}function J(e,t){let n=e[t];if(n!=="'"&&n!=='"')return;let r=t,i="";for(t+=1;t<e.length;t+=1){let a=e[t];if(a===n)return{value:i,offset:r,excerpt:e.slice(r,t+1)};a==="\\"&&t+1<e.length?(i+=e[t+1],t+=1):i+=a}}function P(e,t,n){return e.startsWith(t,n)&&!Qe(e[n-1])&&!Qe(e[n+t.length])}function zt(e,t){if(t=$(e,t+6),e[t]==="(")return J(e,$(e,t+1));let n=!1;if(P(e,"type",t)){let i=$(e,t+4);e[i]!==","&&!P(e,"from",i)&&(n=!0)}let r=Xe(e,t,!0);return r&&n?{...r,typeOnly:!0}:r}function Yt(e,t){t=t+6;let n=$(e,t),r=!1;if(P(e,"type",n)){let a=$(e,n+4);(e[a]==="{"||e[a]==="*")&&(r=!0)}let i=Xe(e,t,!1);return i&&r?{...i,typeOnly:!0}:i}function Xe(e,t,n){for(;t<e.length;t+=1){if(e[t]===";")return;if(P(e,"from",t))return J(e,$(e,t+4));if(n&&(e[t]==="'"||e[t]==='"'))return J(e,t);if(t>0&&(P(e,"import",t)||P(e,"export",t)))return}}function qt(e,t){for(t+=1;t<e.length;t+=1){let n=e[t];if(n==="\\")t+=1;else if(n==="`")return t}return e.length}function Kt(e,t){let n=t-1;for(;n>=0&&/\s/.test(e[n]);)n-=1;if(e[n]===".")return;let r=$(e,t+7);if(e[r]!=="(")return;r=$(e,r+1);let i=J(e,r);return i?{...i,requireCall:!0}:void 0}function Wt(e){let t=[];for(let n=0;n<e.length;n+=1){let r=e[n];if(r==="/"&&e[n+1]==="/"){if(n=e.indexOf(`
@@ -123,12 +123,18 @@ Smell **ids** (stable JSON) plus **outcome** lines (plain language, Q02) on each
123
123
  contract itself (never of the code): `contract-bidirectional-allow` (both directions explicitly
124
124
  allowed between two layers), `contract-peripheral-depends-core` (audit/observability layer allowed
125
125
  into orchestration/persistence), `contract-lateral-adapter-allow` (adapter layer allowed into a
126
- sibling adapter layer), `contract-dead-rule` (rule references an empty or unknown layer, or is a
126
+ sibling adapter layer; X03 an adapter into its OWN family's infra base, e.g.
127
+ `PaymentsAdapters -> PaymentsInfra`, is the sanctioned direction and does not fire),
128
+ `contract-dead-rule` (rule references an empty or unknown layer, or is a
127
129
  same-layer no-op; `optional: true` layers are exempt). Advisory only: it never changes the
128
130
  verdict, `designFitness`, or `patternBets` — layer roles come from name heuristics, so treat a
129
131
  miss as a warning to read, not a defect to silence. A deliberate edge is acknowledged in
130
- `.ark/contract-smell-acks.json` (`{ acks: [{ id, edge, reason }] }`); `acknowledged` counts
131
- applied acks only, and a malformed sidecar or edge string suppresses nothing.
132
+ `.ark/contract-smell-acks.json` (`{ acks: [{ id, edge, reason, reviewBy? }] }`); `acknowledged` counts
133
+ applied acks only, and a malformed sidecar or edge string suppresses nothing. X02 — acks have a
134
+ lifecycle: an optional `reviewBy` (`YYYY-MM-DD`) marks when the exception must be re-reviewed;
135
+ past that date the ack stops applying and the smell returns annotated (`(ack expired …)`).
136
+ Undated acks keep applying but are counted in `contractHealth.ackLifecycle.undated` — give
137
+ migration acks a date so they cannot fossilize.
132
138
 
133
139
  **Effect capabilities (U03, evidence-only):** the analysis IR reports typed capability uses for
134
140
  seven closed ids (`network`, `filesystem`, `clock`, `randomness`, `environment`, `process`,
@@ -23,12 +23,13 @@ hardening guide remains repository-hosted rather than duplicated in the gate tar
23
23
  | **Plan pattern B (P03+)** | `ark-check --plan --json` → `plan.patternBets[]`, `plan.goal.designWeak` | Additive. Each bet: `id`, `smellId`, `pilot`, `evidence`, `successSignal`, `killSwitch`, **`neverMechanicalSafe: true`**, `class: "judgment"`. **Never** auto-applied by loop/autoPatch; not a `remediationKind` mechanical-safe. `goal.met` remains edge honesty only. |
24
24
  | **Pilot loop (Q04)** | `plan.pilotLoop` / `doctor.pilotLoop` | Additive. When design-weak: `active`, `oneAtATime`, `neverMechanicalSafe`, **`nextPilot`** extraction-card fields (`pilotTarget`, `smellId`, `move`, `successSignal`, `killSwitch`, `doNot[]`). **One pilot → re-doctor**; never multi-pilot batch; never mechanical-safe. |
25
25
  | **AI-velocity eval (Q05)** | `npm run eval:ai-velocity` → `eval/ai-velocity-report.json` | Fixture-measured (no live LLM). Same feature scenario on design-weak vs golden-path arms; metric **`placementTurns`** (agent-equivalent). Golden must be strictly better. Method string lives next to the number. Does not weaken the gate. |
26
- | **Contract health (W01)** | `ark-check --doctor --json` → `doctor.contractHealth`; optional `.ark/contract-smell-acks.json` | Additive, **advisory only** — meta-lint of the contract itself (layer-name heuristics; imprecision costs a warning line, never a verdict); never changes the verdict, `designFitness`, or `patternBets`. Stable smell ids: `contract-bidirectional-allow`, `contract-peripheral-depends-core`, `contract-lateral-adapter-allow`, `contract-dead-rule`; each smell has `severity`, `evidence[]` (sorted, honest `…(+N more)` truncation), `fix`, `message`, plain-language `outcome`, and `acknowledgedEdges` (acks applied to that id). Acknowledgments live in the bounded sidecar (`{ acks: [{ id, edge, reason }] }`, ≤64 KB / ≤200 entries; bidirectional edges order-insensitive, exact two segments); `contractHealth.acknowledged` counts **applied** acks only (stale acks count 0). **Absent is normal**; malformed file or edge grammar → ignored + `ackFile.invalid` where applicable, never silent suppression. |
26
+ | **Contract health (W01)** | `ark-check --doctor --json` → `doctor.contractHealth`; optional `.ark/contract-smell-acks.json` | Additive, **advisory only** — meta-lint of the contract itself (layer-name heuristics; imprecision costs a warning line, never a verdict); never changes the verdict, `designFitness`, or `patternBets`. Stable smell ids: `contract-bidirectional-allow`, `contract-peripheral-depends-core`, `contract-lateral-adapter-allow`, `contract-dead-rule`; each smell has `severity`, `evidence[]` (sorted, honest `…(+N more)` truncation), `fix`, `message`, plain-language `outcome`, and `acknowledgedEdges` (acks applied to that id). **X03**: the lateral smell does not fire on an adapter reaching its **own family's infra base** (same leading name token and **every** remaining target token an infra word — `Infra(structure)`/`Base`/`Core`/`Shared`/`Common`/`Kernel`/`Platform`/`Foundation` — e.g. `PaymentsAdapters -> PaymentsInfra`; `PaymentsCoreAdapters` is still a sibling); cross-family edges, non-infra siblings, and the reverse (base → member) still fire. Acknowledgments live in the bounded sidecar (`{ acks: [{ id, edge, reason, reviewBy? }] }`, ≤64 KB / ≤200 entries; bidirectional edges order-insensitive, exact two segments); `contractHealth.acknowledged` counts **applied** acks only (stale acks count 0). **X02 ack lifecycle**: optional `reviewBy` (`YYYY-MM-DD`, strict round-trip validation — `2026-02-30` is malformed) — past the date the ack **stops applying** and the smell returns with `(ack expired …)` annotated evidence; among dated entries a fresh re-ack wins over a dead one, but once ANY dated ack exists for an edge the dated entries govern — a leftover undated duplicate cannot resurrect an expired exception. `detectContractSmells` defaults `today` to the real clock (pass `null` to disable expiry); `analyzeContractSmells` stays pure (clock injected). `contractHealth.ackLifecycle` reports `{ undated, malformed, expiredCount, expired[] (capped at 12) }`; undated acks apply (backward compatible) but surface in doctor, report, and the fossilization note even when every smell is suppressed. Malformed `reviewBy` never applies (fail-loud, like a sloppy edge); non-string `reviewBy` → whole file `invalid`. **Absent is normal**; malformed file or edge grammar → ignored + `ackFile.invalid` where applicable, never silent suppression. |
27
27
  | **Effect capabilities (U03)** | Analysis API `collectCapabilityUses(ts, sourceFile)` + Domain vocabulary (`CAPABILITY_IDS`, `capabilityForModuleSpecifier`, `capabilityForAmbientName`, `lowerForbiddenGlobal`); pure-engine `ir.capabilityUses` | Additive within IR `1.0`. Seven **closed** ids: `network`, `filesystem`, `clock`, `randomness`, `environment`, `process`, `persistence` (ADR 0009). Direct evidence only — transitive inference never detects. The symbol-aware collector covers ambient globals (shadowing/type-only/globalThis-alias precision from the S05/C04 machinery) plus imports; the compiler-free IR engine carries **import-based** uses only (exact module or subpath match, never substring; textual `import type`/`export type` erasure — ANY braced named-binding list stays a value import there; template-literal bodies are skipped entirely (specifiers inside `${…}` are the symbol path's job); `require(…)` counts as capability evidence but never creates a graph edge on the pure path). **U04 walls are opt-in:** per-layer `capabilities: { deny: [...] }` or the dual-depth sugar `pure: true` (denies all seven); absence changes no verdict. `CAPABILITY_VIOLATION` is judgment-class (never mechanical-safe) with a port-injection `nextAction`; D7 dedup — an ambient use covered by the layer's `forbiddenGlobals` reports only `FORBIDDEN_GLOBAL`. Atomic preflight blocks denied capabilities across a complete multi-file candidate (import-based on the pure path; ambient adds on the symbol-aware CLI/hook path). T01 policy-delta classifies the ambient surface on **coverage atoms** (`ambient:<entry>` prefix-expanded + `import:<capability>`): any lost atom is weakening (`fetch`→`XMLHttpRequest`, `Date`→`Date.now`, wall→fg all weaken; finding path `$.layers[name].capabilities`); fg → equivalent-or-stronger wall never needs an acknowledgment; unlowerable custom globals keep raw key comparison. |
28
28
  | **Ambient state (U05)** | `ark-check --doctor --json` → `doctor.ambientState`; optional `.ark/ambient-state-acks.json` | Additive, **advisory only and opt-in**: only layers declared `pure: true` are scanned; the MVP shape is module-scope `let`/`var`. Findings carry `file`/`line`/`name`/`kind` (sorted, capped with honest `truncated` count). Acknowledgments live in the bounded sidecar (`{ acks: [{ file, name, reason }] }`, ≤64 KB / ≤200 entries); `acknowledged` counts applied acks; malformed file suppresses nothing. When TypeScript is unavailable the sensor reports `available: false` instead of guessing. **No strict mode exists** — A5: strictness requires a completed corpus and an explicit later decision. |
29
29
  | **Capability walls, every adapter (U04+U06)** | CLI scan, pure IR engine, atomic preflight, `ark-mcp --hook` / MCP gate (`capabilityWalls`), ESLint `ark/no-denied-capabilities` | The same opt-in deny set enforces across every surface: hook/MCP and CLI cover ambient + import evidence (symbol-aware); the pure engine, preflight, and ESLint cover the import dimension (documented envelope). Dual depth everywhere: plain port hint (`FIX_HINTS`/`suggestion`) + stable JSON (`ruleId`, `capability`, `fixClass: inject-port`, deterministic `nextAction`). |
30
30
  | **Hook-path budgets (U06)** | `npm run bench:hook-path`; `eval/performance/hook-budgets.v1.json`; CI job "Hook-path end-to-end budgets" | Measures the COMPLETE pre-tool paths as fresh child processes (hook cold/warm, doctor cold) at 1k/10k. D5 method locked: ceilings are Linux-baseline p95 + fixed headroom, set once per cycle, never ratcheted; scenarios without a recorded baseline stay in RECORDING mode and cannot fail CI. |
31
31
  | **Governance weight (W02)** | `ark-check --doctor --json` → `doctor.contractHealth.governanceWeight` | Additive, **advisory only** — raw facts (`declaredLayers`, `populatedLayers`, `governedFiles`, `rules`, `deniedEdges`, `allowedEdges`, `filesPerLayer`, `rulesPerLayer`) plus a fixed comparative band `weight: heavy | typical | light | unknown` and its fixed `note`. Fixed deterministic thresholds: **heavy** = fewer than 25 governed files per declared layer AND (6+ layers OR 4+ well-formed rules per layer); **light** = at most 2 layers over 150+ governed files; **unknown** = no layers or no governed files; everything else is **typical** (banding uses raw ratios; the reported ratios are rounded for display). `notAScore: true` is explicit: never a composite score, ranking, or gate input; the heavy note asks to justify NEW layers/rules and never suggests deleting working ones. Human doctor prints a line only for `heavy`/`light`. |
32
+ | **Report parity (X01)** | `ark-check --report` → advisory sections (`data-advisory="contractHealth\|ambientState"`, nested `governanceWeight`) + layer wall badges | The report is a rendering of doctor truth. **Standing rule:** every doctor advisory ships with its report section — enforced by the `reportParity` guard, which enumerates the doctor's advisory keys and fails on any missing section. |
32
33
  | **MCP tools** | `arkgate-mcp` / `ark://…` resources | Tool names and primary argument shapes are stable within a major. |
33
34
  | **`ark.config.json`** | Layer globs, rules, include/exclude, forbiddenGlobals, intent prefixes, `peerIsolation`, `dynamicImportAllowlist`, `safety` thresholds | Versioned by `schemaVersion`; unknown fields fail closed and migrations preserve the previous supported major. |
34
35
  | **Policy transition analysis (T01, 3.1.0)** | `analyzePolicyDelta(...)`; MCP `ark_policy_delta`; CLI `--policy-base` / `--policy-base-ref` / `--policy-ack`; check JSON `policyDelta` | Additive schema `1.0`. Classifications and finding ids are deterministic. Weakening/judgment requires an acknowledgement bound to both policy hashes and the exact blocking finding set. |
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "arkgate",
3
- "version": "3.4.0",
4
- "description": "ArkGate \u2014 architecture co-pilot for AI TypeScript (write gate, CI gate, plan/loop)",
3
+ "version": "3.5.0",
4
+ "description": "ArkGate architecture co-pilot for AI TypeScript (write gate, CI gate, plan/loop)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
7
7
  "module": "./dist/index.js",
package/server.json CHANGED
@@ -6,12 +6,12 @@
6
6
  "url": "https://github.com/pedroknigge/arkgate",
7
7
  "source": "github"
8
8
  },
9
- "version": "3.4.0",
9
+ "version": "3.5.0",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "arkgate",
14
- "version": "3.4.0",
14
+ "version": "3.5.0",
15
15
  "runtimeHint": "npx",
16
16
  "transport": {
17
17
  "type": "stdio"