arkgate 4.8.23 → 4.8.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/README.md +7 -5
  3. package/bin/ark-check-runtime.mjs +24 -77
  4. package/bin/ark-dashboard.mjs +56 -53
  5. package/bin/ark-layer-match.mjs +596 -112
  6. package/bin/ark-mcp-runtime.mjs +412 -138
  7. package/bin/ark-mcp.mjs +68 -4
  8. package/bin/ark-shared.mjs +39 -0
  9. package/bin/ark.mjs +65 -97
  10. package/bin/lib/adapter-contract-types.mjs +5 -0
  11. package/bin/lib/adapter-contract.mjs +15 -0
  12. package/bin/lib/agent-projection-command.mjs +5 -1
  13. package/bin/lib/analysis-engine.mjs +15 -11
  14. package/bin/lib/architecture-scan.mjs +50 -6
  15. package/bin/lib/ark-order-doctor.mjs +1 -1
  16. package/bin/lib/ark-order-facts.mjs +410 -15
  17. package/bin/lib/ark-order-invariants.mjs +51 -9
  18. package/bin/lib/ark-order-sensors.mjs +28 -101
  19. package/bin/lib/ark-order-types.mjs +2 -0
  20. package/bin/lib/ark-run-doctor.mjs +14 -0
  21. package/bin/lib/ark-run-facts.mjs +434 -11
  22. package/bin/lib/ark-run-sensors.mjs +5 -3
  23. package/bin/lib/arkgate-pins.mjs +91 -0
  24. package/bin/lib/arkrules-sensors.mjs +50 -249
  25. package/bin/lib/check-args.mjs +23 -1
  26. package/bin/lib/ci-and-commands.mjs +16 -6
  27. package/bin/lib/class-source-scan.mjs +479 -0
  28. package/bin/lib/codex-home.mjs +4 -2
  29. package/bin/lib/config-contract-slices.mjs +701 -0
  30. package/bin/lib/config-contract.mjs +5 -493
  31. package/bin/lib/config-warnings.mjs +32 -0
  32. package/bin/lib/dashboard-cli.mjs +62 -0
  33. package/bin/lib/design-delta.mjs +1 -1
  34. package/bin/lib/diagnostic-catalog.mjs +16 -9
  35. package/bin/lib/doctor-advisories.mjs +25 -1
  36. package/bin/lib/doctor-next-actions.mjs +17 -2
  37. package/bin/lib/doctor-plan.mjs +6 -8
  38. package/bin/lib/effective-contract-load.mjs +121 -59
  39. package/bin/lib/enforcement-profiles.mjs +40 -4
  40. package/bin/lib/first-run-help.mjs +3 -2
  41. package/bin/lib/flat-parent-importers.mjs +137 -0
  42. package/bin/lib/flat-parent-pilot.mjs +40 -100
  43. package/bin/lib/gate-files.mjs +29 -2
  44. package/bin/lib/git-change-scope.mjs +109 -0
  45. package/bin/lib/github-enforcement.mjs +1 -1
  46. package/bin/lib/hook-templates.mjs +18 -16
  47. package/bin/lib/host-support-matrix.mjs +3 -1
  48. package/bin/lib/import-resolve.mjs +13 -1
  49. package/bin/lib/install-migrate.mjs +50 -73
  50. package/bin/lib/invariant-coverage.mjs +59 -11
  51. package/bin/lib/mcp-adoption.mjs +9 -1
  52. package/bin/lib/mcp-hook-payload.mjs +401 -61
  53. package/bin/lib/package-manager.mjs +59 -1
  54. package/bin/lib/passthrough-commands.mjs +83 -0
  55. package/bin/lib/persistence-write-hint.mjs +81 -0
  56. package/bin/lib/pin-dependent-gates.mjs +117 -0
  57. package/bin/lib/policy-delta-io.mjs +168 -10
  58. package/bin/lib/prepare-change.mjs +120 -6
  59. package/bin/lib/prepare-write.mjs +1 -0
  60. package/bin/lib/presets.mjs +3 -5
  61. package/bin/lib/remediation.mjs +23 -4
  62. package/bin/lib/resolved-candidate-facts.mjs +315 -55
  63. package/bin/lib/rules-inventory.mjs +22 -3
  64. package/bin/lib/rules-under-contract.mjs +179 -5
  65. package/bin/lib/scan-files.mjs +32 -0
  66. package/bin/lib/snippet-analysis.mjs +140 -6
  67. package/bin/lib/source-policy.mjs +34 -0
  68. package/bin/lib/stable-hash.mjs +90 -0
  69. package/bin/lib/start-install-recovery.mjs +71 -1
  70. package/bin/lib/start-preview.mjs +30 -0
  71. package/bin/lib/status-command.mjs +33 -4
  72. package/bin/lib/status-manifest.mjs +32 -4
  73. package/bin/lib/team-parliament-io.mjs +77 -75
  74. package/bin/lib/team-parliament.mjs +6 -1
  75. package/bin/lib/write-path-capabilities.mjs +4 -2
  76. package/dist/chunk-A5EIY3HI.cjs +5 -0
  77. package/dist/chunk-MBXCFLO4.js +16 -0
  78. package/dist/chunk-RNQHRDCQ.cjs +16 -0
  79. package/dist/chunk-SVSQVGFJ.js +5 -0
  80. package/dist/{configTypes-Bbf_MFGe.d.ts → configTypes-BJbaeoj4.d.cts} +18 -3
  81. package/dist/configTypes-BJbaeoj4.d.ts +246 -0
  82. package/dist/eslint/index.cjs +17 -4
  83. package/dist/eslint/index.d.cts +359 -0
  84. package/dist/eslint/index.d.ts +130 -156
  85. package/dist/eslint/index.js +10 -4
  86. package/dist/index.cjs +35 -34
  87. package/dist/index.d.cts +5475 -0
  88. package/dist/index.d.ts +3312 -299
  89. package/dist/index.js +35 -34
  90. package/dist/inspectorListen-6DQVJ4DC.js +1 -0
  91. package/dist/inspectorListen-KGMIQ2TF.cjs +1 -0
  92. package/dist/nestjs/index.cjs +1 -18
  93. package/dist/nestjs/index.d.cts +22 -0
  94. package/dist/nestjs/index.d.ts +1 -3
  95. package/dist/nestjs/index.js +1 -18
  96. package/dist/order/index.cjs +1 -1
  97. package/dist/order/index.d.cts +155 -0
  98. package/dist/order/index.d.ts +15 -3
  99. package/dist/order/index.js +1 -1
  100. package/dist/runtime/index.cjs +13 -30
  101. package/dist/{diagnosticCatalog-35mrwAHe.d.ts → runtime/index.d.cts} +804 -290
  102. package/dist/runtime/index.d.ts +2678 -108
  103. package/dist/runtime/index.js +13 -30
  104. package/dist/{types-Bdj32QwA.d.ts → types-DRinG-vT.d.cts} +576 -140
  105. package/dist/types-DRinG-vT.d.ts +1792 -0
  106. package/docs/README.md +2 -1
  107. package/docs/agent-guide.md +84 -15
  108. package/docs/ai-gates.md +160 -26
  109. package/docs/arkorder.md +32 -15
  110. package/docs/brownfield-adoption.md +5 -1
  111. package/docs/configuration.md +196 -26
  112. package/docs/develop.md +2 -1
  113. package/docs/diagnostics.md +96 -19
  114. package/docs/package-surface.md +31 -8
  115. package/docs/product-voice.md +1 -1
  116. package/package.json +59 -18
  117. package/schemas/ark.analysis-result.schema.json +15 -0
  118. package/schemas/ark.config.schema.json +47 -10
  119. package/schemas/ark.resolved-candidate-facts.schema.json +1 -1
  120. package/schemas/ark.status-manifest.schema.json +22 -0
  121. package/server.json +3 -9
  122. package/templates/agent-skills/README.md +1 -1
  123. package/templates/agent-skills/ark-adopt/SKILL.md +4 -1
  124. package/templates/agent-skills/ark-order/SKILL.md +4 -2
  125. package/templates/agent-skills/ark-runtime/SKILL.md +6 -1
  126. package/templates/arkrules/PersistenceAdapters.json +1 -1
  127. package/templates/arkrules/PresentationAdapters.json +1 -1
  128. package/templates/skills/ark-adopt.md +4 -1
  129. package/templates/skills/ark-order.md +4 -2
  130. package/templates/skills/ark-runtime.md +6 -1
  131. package/dist/types-Ik_OakcI.d.ts +0 -183
@@ -5,7 +5,10 @@
5
5
  * pin-aware npx, and AgeExclude-must-be-in-config stay behind this module.
6
6
  */
7
7
  import { spawnSync } from 'node:child_process';
8
- import { arkPackageRecoveryCommand } from './package-manager.mjs';
8
+ import fs from 'node:fs';
9
+ import path from 'node:path';
10
+ import { pinArkgateDevDependency } from './field-install.mjs';
11
+ import { arkPackageRecoveryCommand, packageInstallArgv } from './package-manager.mjs';
9
12
 
10
13
  const AGE_MARK = /ERR_PNPM_NO_MATURE_MATCHING_VERSION|minimumReleaseAge/i;
11
14
  const AGE_EXCLUDE_LINE =
@@ -154,3 +157,70 @@ export function runStartPackageInstall(command, commandArgs, cwd) {
154
157
  stderr: result.stderr ?? '',
155
158
  };
156
159
  }
160
+
161
+ // Setup-time arkgate pin shared by `ark start` and `ark init` (kept out of bin/ark.mjs).
162
+ /**
163
+ * Pin arkgate in package.json (and optionally run the package manager).
164
+ * start calls this so CI/`npx` is not forced to rely on a stale global install.
165
+ *
166
+ * @param {string} root
167
+ * @param {{ install?: boolean, runPackageManager?: boolean }} [opts]
168
+ */
169
+ export function ensureProjectArkgateDependency(root, opts = {}) {
170
+ const install = opts.install !== false;
171
+ const runPm = opts.runPackageManager === true;
172
+ if (!install) {
173
+ return { pinned: { changed: false, reason: 'skipped-no-install' }, installStatus: null };
174
+ }
175
+ const pinned = pinArkgateDevDependency(root);
176
+ let installStatus = null;
177
+ let hostOutput = '';
178
+ // Only run the package manager after a successful pin change — avoid surprise
179
+ // network on every start when arkgate is already listed.
180
+ if (runPm && pinned.changed) {
181
+ const [command, commandArgs] = packageInstallArgv(root, pinned.version);
182
+ const install = runStartPackageInstall(command, commandArgs, root);
183
+ installStatus = install.status;
184
+ hostOutput = `${install.stdout}\n${install.stderr}`;
185
+ }
186
+ return { pinned, installStatus, hostOutput };
187
+ }
188
+
189
+ /**
190
+ * Setup-time pin shared by `ark start` and `ark init`. With the pin, generated CI and host
191
+ * hooks call the local `ark-check` / `arkgate-mcp` bins through the project runner. Without
192
+ * it they run `npx -y -p arkgate@<this exact version> <bin>`, because a bare `npx ark-check`
193
+ * asks npm for a package named `ark-check` (404) or runs a stale global.
194
+ */
195
+ export function pinArkgateForSetup(root, args, cliVersion) {
196
+ if (!args.install) {
197
+ console.log(' Skipping arkgate package pin (--no-install).');
198
+ return;
199
+ }
200
+ if (!fs.existsSync(path.join(root, 'package.json'))) {
201
+ console.log(
202
+ ` Warning: no package.json — the generated CI workflow and host hooks run npx -y -p arkgate@${cliVersion()} (downloaded on each run). To run a local install instead, create package.json, add arkgate@^${cliVersion()} to devDependencies, then re-run setup.`
203
+ );
204
+ return;
205
+ }
206
+ const { pinned, installStatus, hostOutput } = ensureProjectArkgateDependency(root, {
207
+ install: true,
208
+ runPackageManager: !args.skipPackageManager,
209
+ });
210
+ if (pinned.changed) {
211
+ console.log(` Pinned arkgate@${pinned.version} in package.json devDependencies.`);
212
+ if (installStatus !== null && installStatus !== 0) {
213
+ const [command, commandArgs] = packageInstallArgv(root, pinned.version);
214
+ console.error(
215
+ formatStartPackageInstallFailure({
216
+ exitStatus: installStatus,
217
+ installCommand: `${command} ${commandArgs.join(' ')}`,
218
+ hostOutput,
219
+ packageVersion: cliVersion(),
220
+ })
221
+ );
222
+ }
223
+ } else if (pinned.reason === 'already-present') {
224
+ console.log(` arkgate already in package.json (${pinned.version}).`);
225
+ }
226
+ }
@@ -20,6 +20,9 @@ import {
20
20
  grokHooks,
21
21
  grokProjectConfig,
22
22
  opencodeProjectConfig,
23
+ removeAntigravityArkHook,
24
+ removeClaudeStyleArkHooks,
25
+ removeCursorArkHook,
23
26
  } from './hook-templates.mjs';
24
27
  import { codexPrimaryTable, codexProjectMcpIsValid } from './codex-home.mjs';
25
28
  import { codexRuntimeActivation } from './enforcement-state.mjs';
@@ -56,6 +59,17 @@ const COMPACT_HOST_TEMPLATES = {
56
59
  gemini: (root) => [['GEMINI.md', instructionRule(root)]],
57
60
  };
58
61
 
62
+ /**
63
+ * Host hook files the installer merges into (keeping user keys). Removal drops only the
64
+ * Ark-owned entries, with the same identity as the merge helper.
65
+ */
66
+ const MERGED_HOOK_FILE_REMOVERS = {
67
+ '.claude/settings.json': removeClaudeStyleArkHooks,
68
+ '.codex/hooks.json': removeClaudeStyleArkHooks,
69
+ '.cursor/hooks.json': removeCursorArkHook,
70
+ '.agents/hooks.json': removeAntigravityArkHook,
71
+ };
72
+
59
73
  function treeFiles(root) {
60
74
  const files = new Map();
61
75
  const visit = (dir) => {
@@ -334,6 +348,22 @@ function planHostRemoval(args, helpers) {
334
348
  changes.push(change(relativePath, before, after));
335
349
  continue;
336
350
  }
351
+ const remover = MERGED_HOOK_FILE_REMOVERS[relativePath];
352
+ if (remover && before.toString('utf8') !== expected) {
353
+ const removal = remover(before.toString('utf8'), expected);
354
+ if (removal.status === 'removed') {
355
+ changes.push(
356
+ change(relativePath, before, removal.text == null ? null : Buffer.from(removal.text))
357
+ );
358
+ } else if (removal.status === 'customized') {
359
+ unresolvedDecisions.push(
360
+ `${relativePath}: the Ark hook entry was customized and was left untouched.`
361
+ );
362
+ } else if (removal.status === 'unreadable') {
363
+ unresolvedDecisions.push(`${relativePath} is not readable JSON and was left untouched.`);
364
+ }
365
+ continue;
366
+ }
337
367
  if (before.toString('utf8') !== expected) {
338
368
  unresolvedDecisions.push(`${relativePath} was customized and was left untouched.`);
339
369
  continue;
@@ -17,6 +17,7 @@ import {
17
17
  unavailableStatusImprovementCompass,
18
18
  } from './status-manifest.mjs';
19
19
  import { createProjectId } from './project-identity.mjs';
20
+ import { parseArkConfigJson } from './config-contract.mjs';
20
21
  import { resolveEffectiveProjectRoot } from './project-root.mjs';
21
22
  import { detectWritePathCapabilities } from './write-path-detect.mjs';
22
23
  import { buildWritePathHonesty } from './enforcement-honesty.mjs';
@@ -158,7 +159,7 @@ export function statusCompassFromSnapshot(latest, opts = {}) {
158
159
  return unavailableStatusImprovementCompass({
159
160
  reasonCode: 'NO_SESSION_SNAPSHOT',
160
161
  reason:
161
- 'No session report snapshot yet — run ark-check --doctor or --report for residual lenses. Status never invents green.',
162
+ 'No session report snapshot yet — run ark-check --report to write the session snapshot (--doctor shows full lenses but does not write it). Status never invents green.',
162
163
  contractHash,
163
164
  });
164
165
  }
@@ -189,7 +190,7 @@ export function statusCompassFromSnapshot(latest, opts = {}) {
189
190
  reasonCode: complete ? undefined : 'FACTS_PARTIAL',
190
191
  reason: complete
191
192
  ? undefined
192
- : 'Session snapshot residual is partial — re-run doctor/report for full compass.',
193
+ : 'Session snapshot residual is partial — re-run ark-check --report for full compass.',
193
194
  factsSource: 'report-snapshot',
194
195
  contractHash,
195
196
  });
@@ -198,7 +199,7 @@ export function statusCompassFromSnapshot(latest, opts = {}) {
198
199
  return unavailableStatusImprovementCompass({
199
200
  reasonCode: 'NO_SESSION_SNAPSHOT',
200
201
  reason:
201
- 'Session snapshot has no improvement compass facts — run ark-check --doctor or --report. Status never invents green.',
202
+ 'Session snapshot has no improvement compass facts — run ark-check --report to refresh the session snapshot. Status never invents green.',
202
203
  contractHash,
203
204
  });
204
205
  }
@@ -242,6 +243,24 @@ export function thinStatusCompassFromDoctor(doctorCompass, opts = {}) {
242
243
  * contractHash?: string | null,
243
244
  * }} [options]
244
245
  */
246
+ /**
247
+ * Run the shared contract validator (same rules as ark-check / MCP / ESLint).
248
+ * @param {string} text
249
+ * @param {string} source
250
+ * @returns {string[]} `<path>: <message>` lines; empty when valid
251
+ */
252
+ function validateConfigText(text, source) {
253
+ try {
254
+ parseArkConfigJson(text, source);
255
+ return [];
256
+ } catch (error) {
257
+ if (Array.isArray(error?.issues) && error.issues.length > 0) {
258
+ return error.issues.map((issue) => `${issue.path}: ${issue.message}`);
259
+ }
260
+ return [error instanceof Error ? error.message : String(error)];
261
+ }
262
+ }
263
+
245
264
  export function collectStatusFacts(options = {}) {
246
265
  const startRoot = path.resolve(options.root || process.cwd());
247
266
  const configName = options.config || 'ark.config.json';
@@ -256,12 +275,17 @@ export function collectStatusFacts(options = {}) {
256
275
  const configExists = fs.existsSync(configPath);
257
276
 
258
277
  let config = null;
278
+ /** Shared contract validator messages — status must not look authoritative-green on a config every gate refuses. */
279
+ let configErrors = [];
259
280
  if (configExists) {
281
+ let text = '';
260
282
  try {
261
- config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
283
+ text = fs.readFileSync(configPath, 'utf8');
284
+ config = JSON.parse(text);
262
285
  } catch {
263
286
  config = null;
264
287
  }
288
+ configErrors = validateConfigText(text, path.basename(configPath));
265
289
  }
266
290
 
267
291
  const projectId = configExists
@@ -390,6 +414,7 @@ export function collectStatusFacts(options = {}) {
390
414
  arkgateVersion: options.arkgateVersion || packageVersion(),
391
415
  resolvedRoot: realpathOrResolve(resolvedRoot),
392
416
  resolvedConfigPath: configExists ? realpathOrResolve(configPath) : null,
417
+ configErrors,
393
418
  projectId,
394
419
  expectation,
395
420
  expectedRootRelation,
@@ -564,6 +589,10 @@ export function runStatusCommand(args = {}) {
564
589
  ' · not a score'
565
590
  );
566
591
  }
592
+ if (manifest.contract?.valid === false) {
593
+ write(` contract: invalid (${manifest.contract.errors.length} issue(s)) — every enforcing surface refuses this config`);
594
+ for (const issue of manifest.contract.errors.slice(0, 5)) write(` - ${issue}`);
595
+ }
567
596
  write(` next: [${manifest.nextAction.id}] ${manifest.nextAction.summary}`);
568
597
  }
569
598
 
@@ -175,6 +175,11 @@ export function defaultHonestLabel(writePath, host) {
175
175
  * Deterministic next action from residual facts (no LLM).
176
176
  * Prefer explicit override from productHonesty when provided.
177
177
  */
178
+ function normalizedConfigErrors(errors) {
179
+ if (!Array.isArray(errors))
180
+ return [];
181
+ return errors.filter((entry) => typeof entry === 'string' && entry.length > 0);
182
+ }
178
183
  export function resolveStatusNextAction(facts, binding, activation, lastCheck, rules) {
179
184
  if (facts.nextActionOverride?.id && facts.nextActionOverride.summary) {
180
185
  return {
@@ -202,22 +207,31 @@ export function resolveStatusNextAction(facts, binding, activation, lastCheck, r
202
207
  summary: 'No ark.config.json found — run ark start (preview) then ark start --apply.',
203
208
  };
204
209
  }
210
+ const configErrors = normalizedConfigErrors(facts.configErrors);
211
+ if (configErrors.length > 0) {
212
+ return {
213
+ id: 'fix-config',
214
+ summary: `The Ark config is invalid, so ark-check, MCP and ESLint refuse it — fix: ${configErrors
215
+ .slice(0, 3)
216
+ .join('; ')}${configErrors.length > 3 ? ` (+${configErrors.length - 3} more)` : ''}`,
217
+ };
218
+ }
205
219
  if (lastCheck.verdict === 'fail' || (lastCheck.activeViolations ?? 0) > 0) {
206
220
  return {
207
221
  id: 'fix-active-violations',
208
- summary: `Clear ${lastCheck.activeViolations ?? 'active'} blocking architecture finding(s), then re-run ark-check (or ark-check --doctor).`,
222
+ summary: `Clear ${lastCheck.activeViolations ?? 'active'} blocking architecture finding(s), then re-run ark-check --report to refresh the last-check snapshot (a plain check or --doctor does not update .ark/reports/latest.json).`,
209
223
  };
210
224
  }
211
225
  if (lastCheck.verdict === 'incomplete') {
212
226
  return {
213
227
  id: 'restore-complete-analysis',
214
- summary: 'Last check was incomplete — restore TypeScript/analysis inputs and re-run ark-check.',
228
+ summary: 'Last check was incomplete — restore TypeScript/analysis inputs and re-run ark-check --report to refresh the snapshot.',
215
229
  };
216
230
  }
217
231
  if (lastCheck.verdict == null && lastCheck.at == null) {
218
232
  return {
219
233
  id: 'run-ark-check',
220
- summary: 'No last-check snapshot yet — run ark-check --report (or --doctor) to freeze session evidence.',
234
+ summary: 'No last-check snapshot yet — run ark-check --report to freeze session evidence (--doctor does not write the snapshot).',
221
235
  };
222
236
  }
223
237
  if (activation.writePath === 'unavailable') {
@@ -334,6 +348,10 @@ export function buildStatusManifest(facts) {
334
348
  rules,
335
349
  nextAction: resolveStatusNextAction(facts, binding, activation, lastCheck, rules),
336
350
  };
351
+ const configErrors = normalizedConfigErrors(facts.configErrors);
352
+ if (projectIdentity.resolvedConfigPath && configErrors.length > 0) {
353
+ status.contract = { valid: false, errors: configErrors };
354
+ }
337
355
  const compass = normalizeStatusImprovementCompass(facts.improvementCompass);
338
356
  if (compass)
339
357
  status.improvementCompass = compass;
@@ -433,7 +451,7 @@ export function unavailableStatusImprovementCompass(input = {}) {
433
451
  topResidual: [],
434
452
  reasonCode: input.reasonCode ?? STATUS_COMPASS_REASON_CODES.NO_SESSION_SNAPSHOT,
435
453
  reason: input.reason ??
436
- 'No session compass facts yet — run ark-check --doctor or --report for residual lenses. Status never invents green.',
454
+ 'No session compass facts yet — run ark-check --report to write the session snapshot (--doctor shows full lenses but does not write it). Status never invents green.',
437
455
  factsSource: 'none',
438
456
  contractHash: input.contractHash,
439
457
  });
@@ -591,6 +609,16 @@ export const ARK_STATUS_MANIFEST_SCHEMA = {
591
609
  summary: { type: 'string', minLength: 1 },
592
610
  },
593
611
  },
612
+ contract: {
613
+ type: 'object',
614
+ description: 'Present only when the resolved config fails the shared contract validator (the same fail-closed rules ark-check, MCP and ESLint apply).',
615
+ additionalProperties: false,
616
+ required: ['valid', 'errors'],
617
+ properties: {
618
+ valid: { const: false },
619
+ errors: { type: 'array', minItems: 1, items: { type: 'string', minLength: 1 } },
620
+ },
621
+ },
594
622
  improvementCompass: {
595
623
  type: 'object',
596
624
  description: 'Thin improvement-compass residual ids with honesty mode (notAScore). full | subset | unavailable. Never a gate input; full lenses on doctor JSON. When full, residual ids ⊆ doctor residual for the same facts. unavailable never invents green residual.',
@@ -2,11 +2,10 @@
2
2
  * Team parliament I/O — git base refs, changed paths, pin/contract/baseline compare.
3
3
  * Pure classification lives in team-parliament.mjs (Domain).
4
4
  */
5
- import { spawnSync } from 'node:child_process';
6
5
  import fs from 'node:fs';
7
6
  import path from 'node:path';
8
7
  import { createHash } from 'node:crypto';
9
- import { applyAdvisorySiblingRatchet } from '../ark-layer-match.mjs';
8
+ import { applyAdvisorySiblingRatchet, globToRegExp } from '../ark-layer-match.mjs';
10
9
  import {
11
10
  baselineKeysFromDocument,
12
11
  classifyBaselineKeyDelta,
@@ -20,6 +19,15 @@ import {
20
19
  resolveStewardHandle,
21
20
  suggestStewards,
22
21
  } from './team-parliament.mjs';
22
+ import {
23
+ SPAWN_TIMEOUT_MS,
24
+ TEAM_BASE_CANDIDATES,
25
+ discoverTeamBaseRef,
26
+ gitShowText,
27
+ listChangedPaths,
28
+ runGit,
29
+ safeGitRef,
30
+ } from './git-change-scope.mjs';
23
31
 
24
32
  export {
25
33
  baselineKeysFromDocument,
@@ -34,25 +42,8 @@ export {
34
42
  resolveStewardHandle,
35
43
  suggestStewards,
36
44
  };
37
-
38
- /** Kill hung git instead of stalling CI. */
39
- export const SPAWN_TIMEOUT_MS = 8000;
40
-
41
- function runGit(cwd, args) {
42
- return spawnSync('git', ['-C', cwd, ...args], {
43
- encoding: 'utf8',
44
- stdio: ['ignore', 'pipe', 'pipe'],
45
- timeout: SPAWN_TIMEOUT_MS,
46
- });
47
- }
48
-
49
- export function safeGitRef(value) {
50
- return (
51
- typeof value === 'string' &&
52
- /^[A-Za-z0-9][A-Za-z0-9._/-]{0,200}$/.test(value) &&
53
- !value.includes('..')
54
- );
55
- }
45
+ // prettier-ignore
46
+ export { SPAWN_TIMEOUT_MS, TEAM_BASE_CANDIDATES, discoverTeamBaseRef, gitShowText, listChangedPaths, safeGitRef };
56
47
 
57
48
  export function contractSessionFrom(args, env = process.env) {
58
49
  if (args?.contractSession === true) return true;
@@ -70,49 +61,6 @@ export function resolveTeamAuthor(args, env = process.env) {
70
61
  });
71
62
  }
72
63
 
73
- export function discoverTeamBaseRef(root, preferred) {
74
- if (safeGitRef(preferred)) return preferred;
75
- const candidates = ['origin/dev', 'origin/main', 'origin/master', 'dev', 'main'];
76
- for (const candidate of candidates) {
77
- const exists = runGit(root, ['rev-parse', '--verify', `${candidate}^{commit}`]);
78
- if (exists.status === 0) return candidate;
79
- }
80
- return null;
81
- }
82
-
83
- export function listChangedPaths(root, baseRef) {
84
- if (!safeGitRef(baseRef)) {
85
- return { ok: false, paths: [], error: 'Invalid or missing git base ref.' };
86
- }
87
- const verify = runGit(root, ['rev-parse', '--verify', `${baseRef}^{commit}`]);
88
- if (verify.status !== 0) {
89
- return { ok: false, paths: [], error: `Cannot resolve git ref ${baseRef}.` };
90
- }
91
- const diff = runGit(root, ['diff', '--name-only', `${baseRef}...HEAD`]);
92
- const unstaged = runGit(root, ['diff', '--name-only']);
93
- const staged = runGit(root, ['diff', '--name-only', '--cached']);
94
- const untracked = runGit(root, ['ls-files', '--others', '--exclude-standard']);
95
- if (diff.status !== 0) {
96
- return { ok: false, paths: [], error: diff.stderr.trim() || `git diff ${baseRef} failed.` };
97
- }
98
- const paths = [
99
- ...diff.stdout.split('\n'),
100
- ...unstaged.stdout.split('\n'),
101
- ...staged.stdout.split('\n'),
102
- ...untracked.stdout.split('\n'),
103
- ]
104
- .map((line) => line.trim().replace(/\\/g, '/'))
105
- .filter(Boolean);
106
- return { ok: true, paths: [...new Set(paths)].sort(), error: null };
107
- }
108
-
109
- export function gitShowText(root, baseRef, relPath) {
110
- if (!safeGitRef(baseRef) || !relPath) return null;
111
- const shown = runGit(root, ['show', `${baseRef}:${relPath}`]);
112
- if (shown.status !== 0) return null;
113
- return shown.stdout;
114
- }
115
-
116
64
  export function readJsonMaybe(text) {
117
65
  if (typeof text !== 'string' || !text.trim()) return null;
118
66
  try {
@@ -193,8 +141,11 @@ export function bindTeamBaseRefs(args, root) {
193
141
  if (!next.against && (next.changed || next.contractDiff || next.persona)) {
194
142
  next.against = teamBase;
195
143
  }
196
- if (!next.policyBaseRef && (next.contractDiff || next.changed || next.against)) {
144
+ // A contract session must classify the law change, or loosening skips steward/ack checks.
145
+ // policyBaseFromTeam: a base without the contract yet is adoption (no predecessor), not an error.
146
+ if (!next.policyBaseRef && (next.contractDiff || next.changed || next.against || next.contractSession)) {
197
147
  next.policyBaseRef = teamBase;
148
+ next.policyBaseFromTeam = true;
198
149
  }
199
150
  if (!next.baseRef && next.failOnNewSmells) next.baseRef = teamBase;
200
151
  }
@@ -236,17 +187,28 @@ export function runTeamPreflight({ root, args, config, policyDelta, teamBase })
236
187
  return { halt: null, teamParliament: null, changedPaths: [] };
237
188
  }
238
189
  const againstRef = args.against || teamBase;
239
- if (args.local && !againstRef) {
240
- const message =
241
- '--local needs a git merge base so it can reuse --changed. Pass --base <ref> (for example --base HEAD or --base origin/main). The merge gate stays --strict-merge.';
242
- const teamParliament = { deny: false, reasonId: 'local-needs-base', message };
190
+ if ((args.local || args.changed || args.contractDiff) && !againstRef) {
191
+ const reasonId = args.local
192
+ ? 'local-needs-base'
193
+ : args.changed
194
+ ? 'changed-needs-base'
195
+ : 'contract-diff-needs-base';
196
+ const message = args.local
197
+ ? '--local needs a git merge base so it can reuse --changed. Pass --base <ref> (for example --base HEAD or --base origin/main). The merge gate stays --strict-merge.'
198
+ : args.changed
199
+ ? changedNeedsBaseMessage(args)
200
+ : contractDiffNeedsBaseMessage(args);
201
+ const teamParliament = { deny: false, reasonId, message };
243
202
  return {
244
203
  halt: { exitCode: 2, message, teamParliament },
245
204
  teamParliament,
246
205
  changedPaths: [],
247
206
  };
248
207
  }
249
- const listed = againstRef ? listChangedPaths(root, againstRef) : { ok: true, paths: [], error: null };
208
+ // No base ref means the change set was never computed: never report it as empty.
209
+ const listed = againstRef
210
+ ? listChangedPaths(root, againstRef)
211
+ : { ok: false, paths: [], error: 'No git base ref resolved.' };
250
212
  const changedPaths = listed.ok ? listed.paths : [];
251
213
  const changeSet = classifyChangeSet(changedPaths);
252
214
  const baseBaselineRaw = againstRef
@@ -279,8 +241,12 @@ export function runTeamPreflight({ root, args, config, policyDelta, teamBase })
279
241
  changedPathError: listed.ok ? null : listed.error,
280
242
  };
281
243
  if (!listed.ok && (args.changed || args.against || args.contractDiff)) {
244
+ // The verdict above ran over an uncomputed diff: never label it `ok`.
245
+ teamParliament.deny = false;
246
+ teamParliament.reasonId = 'changed-paths-unavailable';
247
+ teamParliament.message = `${listed.error || 'Cannot list changed paths.'} Pass --base <ref> or fix the git error, or run a full check without diff flags.`;
282
248
  return {
283
- halt: { exitCode: 2, message: listed.error || 'Cannot resolve team base ref.', teamParliament },
249
+ halt: { exitCode: 2, message: teamParliament.message, teamParliament },
284
250
  teamParliament,
285
251
  changedPaths,
286
252
  };
@@ -302,6 +268,17 @@ export function runTeamPreflight({ root, args, config, policyDelta, teamBase })
302
268
  return { halt: null, teamParliament, changedPaths };
303
269
  }
304
270
 
271
+ /** `--changed` / `--persona touch|contributor|agent` without a resolvable merge base. */
272
+ export function changedNeedsBaseMessage(args) {
273
+ const flag = args?.persona ? `--persona ${args.persona}` : '--changed';
274
+ return `${flag} needs a git merge base to know which files changed, and none was found (tried ${TEAM_BASE_CANDIDATES.join(', ')}; or this is not a git repository). Pass --base <ref> (for example --base HEAD or --base origin/main), or run without ${flag} for a full-tree check.`;
275
+ }
276
+
277
+ export function contractDiffNeedsBaseMessage(args) {
278
+ const flag = args?.persona ? `--persona ${args.persona}` : '--contract-diff';
279
+ return `${flag} compares the contract and .ark-baseline.json against a git base, and none was found (tried ${TEAM_BASE_CANDIDATES.join(', ')}; or this is not a git repository). Pass --base <ref> (for example --base origin/main), or run a plain full-tree check without ${flag}.`;
280
+ }
281
+
305
282
  export function ungovernedDumpMessage(dumped) {
306
283
  return `New ungoverned source in this diff: ${dumped.slice(0, 8).join(', ')}${dumped.length > 8 ? '…' : ''}. Classify via /ark-adopt (contract session) or move into a governed layer.`;
307
284
  }
@@ -312,6 +289,29 @@ export function filterChangedGovernedFiles(allGovernedFiles, root, changedPaths,
312
289
  return allGovernedFiles.filter((abs) => changedSet.has(normalizeRel(path.relative(root, abs))));
313
290
  }
314
291
 
292
+ /**
293
+ * `--changed` scans a subset, so a layer whose files were not touched looks empty to the
294
+ * kernel. Keep CONFIG_LAYER_PATTERN_NO_MATCHES only when the FULL governed tree has no
295
+ * match either (a real typo). `loadFullFiles` is lazy: the full walk runs only when needed.
296
+ */
297
+ export function pruneScopedPatternWarnings(warnings, root, loadFullFiles) {
298
+ const isNoMatch = (w) =>
299
+ (w?.ruleId ?? w?.code) === 'CONFIG_LAYER_PATTERN_NO_MATCHES' && typeof w.pattern === 'string';
300
+ if (!Array.isArray(warnings) || !warnings.some(isNoMatch)) return warnings;
301
+ const rels = loadFullFiles().map((abs) => path.relative(root, abs).split(path.sep).join('/'));
302
+ for (let i = warnings.length - 1; i >= 0; i -= 1) {
303
+ if (!isNoMatch(warnings[i])) continue;
304
+ let expression;
305
+ try {
306
+ expression = globToRegExp(warnings[i].pattern);
307
+ } catch {
308
+ continue;
309
+ }
310
+ if (rels.some((rel) => expression.test(rel))) warnings.splice(i, 1);
311
+ }
312
+ return warnings;
313
+ }
314
+
315
315
  export function applyAgainstRatchet({
316
316
  violations,
317
317
  againstRef,
@@ -319,16 +319,18 @@ export function applyAgainstRatchet({
319
319
  changed,
320
320
  changedPaths,
321
321
  occurrenceKeys,
322
+ rules,
323
+ layers,
322
324
  }) {
323
325
  const baseRaw = readJsonMaybe(gitShowText(root, againstRef, '.ark-baseline.json'));
324
326
  const baseKeys = new Set(baselineKeysFromDocument(baseRaw));
325
- const judged = baseRaw ? applyAdvisorySiblingRatchet(violations, occurrenceKeys, baseKeys) : violations;
327
+ const judged = baseRaw ? applyAdvisorySiblingRatchet(violations, occurrenceKeys, baseKeys, { rules, layers }) : violations;
326
328
  const vsBaseActive = judged.filter((_, index) => !baseKeys.has(occurrenceKeys[index]));
327
329
  const changedSet = new Set(changedPaths ?? []);
330
+ // A CROSS_PARENT_VIA_SHARED path belongs to the change when any file on it changed.
331
+ const touched = (v) => [v.file, ...(Array.isArray(v.via) ? v.via : []), ...(v.reasonId === 'CROSS_PARENT_VIA_SHARED' ? [v.target] : [])];
328
332
  const activeViolations = changed
329
- ? vsBaseActive.filter((violation) =>
330
- changedSet.has(String(violation.file || '').replace(/\\/g, '/'))
331
- )
333
+ ? vsBaseActive.filter((v) => touched(v).some((f) => changedSet.has(String(f || '').replace(/\\/g, '/'))))
332
334
  : vsBaseActive;
333
335
  return {
334
336
  activeViolations,
@@ -119,7 +119,12 @@ export function isGitHubEmail(value) {
119
119
  const email = value.trim().toLowerCase();
120
120
  if (!email.includes('@') || email.includes(' ') || isAutomationAuthor(email))
121
121
  return false;
122
- return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
122
+ // local@domain.tld without a backtracking regex (CodeQL js/polynomial-redos).
123
+ const parts = email.split('@');
124
+ if (parts.length !== 2 || !parts[0] || /\s/.test(email))
125
+ return false;
126
+ const dot = parts[1].lastIndexOf('.');
127
+ return dot > 0 && dot < parts[1].length - 1;
123
128
  }
124
129
  /** `123+login@users.noreply.github.com` or `login@users.noreply.github.com` → login. */
125
130
  export function githubHandleFromEmail(email) {
@@ -20,6 +20,7 @@ import {
20
20
  import { detectActiveAgentHost } from './skill-install.mjs';
21
21
  import { detectCiEnforcement } from './weakest-link.mjs';
22
22
  import { buildEnforcementState, packageInstallation } from './enforcement-state.mjs';
23
+ import { npxArkgatePrefixLength } from './package-manager.mjs';
23
24
 
24
25
  export const WRITE_CAPABILITY_NAMES = [
25
26
  'hard-write',
@@ -161,8 +162,9 @@ function arkMcpInvocation(server) {
161
162
  if (argv.filter(isArkMcpToken).length !== 1) return false;
162
163
  if (isArkMcpToken(server.command)) return { argv, binIndex: 0 };
163
164
  const runner = path.basename(server.command.trim().replace(/\\/g, '/'));
164
- if (['npx', 'yarn'].includes(runner) && isArkMcpToken(args[0])) {
165
- return { argv, binIndex: 1 };
165
+ const npxPrefix = runner === 'npx' ? npxArkgatePrefixLength(args) : 0;
166
+ if (['npx', 'yarn'].includes(runner) && isArkMcpToken(args[npxPrefix])) {
167
+ return { argv, binIndex: npxPrefix + 1 };
166
168
  }
167
169
  if (runner === 'node') {
168
170
  const script = args[0]?.replace(/\\/g, '/');
@@ -0,0 +1,5 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }var G=Object.defineProperty;var v=Object.getOwnPropertyDescriptor;var i=(n,e)=>G(n,"name",{value:e,configurable:!0});var Jn=(n,e,r,t)=>{for(var s=t>1?void 0:t?v(e,r):e,u=n.length-1,a;u>=0;u--)(a=n[u])&&(s=(t?a(e,r,s):a(s))||s);return t&&s&&G(e,r,s),s};var D="1.0",nn= exports.d =["singleton","transient"],en=["absorb","escalate_up","hold"],rn=new Set(nn);function E(n,e){return n<e?-1:n>e?1:0}i(E,"compare");function tn(n){return[...new Set(n)].sort(E)}i(tn,"uniqueSorted");function k(n){if(!Array.isArray(n))return[];let e=[];for(let r of n)typeof r=="string"&&r.length>0&&e.push(r);return tn(e)}i(k,"nameList");function on(n){if(n===null)return null;if(typeof n=="string"||typeof n=="boolean"||typeof n=="number"&&Number.isFinite(n))return n}i(on,"jsonPrimitive");function sn(n){return typeof n=="string"&&rn.has(n)?n:"singleton"}i(sn,"closedLifetime");function L(n){if(n===null||typeof n!="object"||Array.isArray(n))return;let e={};for(let r of Object.keys(n).sort(E)){if(r.length===0)continue;let t=on(n[r]);t!==void 0&&(e[r]=t)}return Object.keys(e).length>0?e:void 0}i(L,"sanitizeMetadata");function un(n){if(n===null||typeof n!="object"||Array.isArray(n))return;let e=n,r={};typeof e.label=="string"&&e.label.length>0&&(r.label=e.label),typeof e.architectureKind=="string"&&e.architectureKind.length>0&&(r.architectureKind=e.architectureKind);let t=k(e.tags);t.length>0&&(r.tags=t),typeof e.group=="string"&&e.group.length>0&&(r.group=e.group);let s=L(e.metadata);return s&&(r.metadata=s),Object.keys(r).length>0?r:void 0}i(un,"sanitizeArkRunExtendedInfo");function an(n){if(n===null||typeof n!="object"||Array.isArray(n))return;let e=n,r=typeof e.id=="string"?e.id.trim():"";if(!r)return;let t={id:r,lifetime:sn(e.lifetime),uses:k(e.uses),reactsTo:k(e.reactsTo),raises:k(e.raises),sends:k(e.sends)},s=un(e.extendedInfo);return s&&(t.extendedInfo=s),t}i(an,"sanitizeArkRunComponent");var cn=new Set(en);function dn(n){return L(n)}i(dn,"sanitizeTapePayload");function U(n){if(n===null||typeof n!="object"||Array.isArray(n))return;let e=n,r=typeof e.xiHash=="string"?e.xiHash.trim():"";if(!r)return;let t=e.event;if(t===null||typeof t!="object"||Array.isArray(t))return;let s=t,u=typeof s.kind=="string"?s.kind.trim():"";if(!u)return;let a=e.residual;if(a===null||typeof a!="object"||Array.isArray(a))return;let o=a,c=typeof o.kind=="string"&&cn.has(o.kind)?o.kind:void 0;if(!c)return;let d={kind:u},p=dn(s.payload);p&&(d.payload=p);let w={kind:c};return typeof o.reasonCode=="string"&&o.reasonCode.length>0&&(w.reasonCode=o.reasonCode),typeof o.eventId=="string"&&o.eventId.length>0&&(w.eventId=o.eventId),typeof o.target=="string"&&o.target.length>0&&(w.target=o.target),{xiHash:r,event:d,residual:w}}i(U,"sanitizeDecisionTapeRecord");function pn(n){if(!Array.isArray(n))return[];let e=[];for(let r of n){let t=U(r);t&&e.push(t)}return e}i(pn,"sanitizeDecisionTape");function R(n){let e=typeof n.kernelInstanceId=="string"?n.kernelInstanceId:"",r=Array.isArray(n.components)?n.components:[],t=new Set,s=[];for(let a of r){let o=an(a);!o||t.has(o.id)||(t.add(o.id),s.push(o))}s.sort((a,o)=>E(a.id,o.id));let u=pn(n.decisionTape);return{schemaVersion:"1.0",kernelInstanceId:e,components:s,...u.length>0?{decisionTape:u}:{}}}i(R,"buildDependencyInformationPackage");function Yn(n,e){let r=U(e),t=_nullishCoalesce(n.decisionTape, () => ([])),s=r?[...t,r]:[...t];return{schemaVersion:n.schemaVersion,kernelInstanceId:n.kernelInstanceId,components:n.components,...s.length>0?{decisionTape:s}:{}}}i(Yn,"appendDecisionTape");var fn="1.0",ln= exports.i ="process",gn= exports.j =["process","technical"],ee= exports.k =["component","interaction"],re= exports.l =["raises","reactsTo","sends"],te= exports.m =["uses"],l= exports.n =class extends Error{static{i(this,"InvalidArkRunGraphQueryError")}constructor(e,r){super(_nullishCoalesce(r, () => ((e==="slice"?'ArkRun graph slice must be "process" or "technical".':e==="degreesOfSeparation"?"ArkRun graph degreesOfSeparation must be a non-negative integer.":`ArkRun graph ${e} is not a closed query.`)))),this.name="InvalidArkRunGraphQueryError",this.option=e}},kn=new Set(gn),Rn=new Set(["slice","nodeIds","degreesOfSeparation","include","exclude"]),An=new Set(["ids","labels","tags","groups","architectureKinds"]);function y(n,e){return n<e?-1:n>e?1:0}i(y,"compare");function C(n){return[...new Set(n)].sort(y)}i(C,"uniqueSorted");function H(){return{tokens:[],ids:[],labels:[],tags:[],groups:[],architectureKinds:[]}}i(H,"emptyMatch");function T(n){return n.tokens.length+n.ids.length+n.labels.length+n.tags.length+n.groups.length+n.architectureKinds.length>0}i(T,"matchHasCriteria");function f(n,e){if(n==null||n==="")return[];if(typeof n=="string")return C(n.split(",").map(t=>t.trim()).filter(t=>t.length>0));if(!Array.isArray(n))throw new l(e);let r=[];for(let t of n){if(typeof t!="string")throw new l(e);let s=t.trim();s.length>0&&r.push(s)}return C(r)}i(f,"nameList");function V(n,e){if(e==="*")return n.length>0;if(!e.includes("*"))return n===e;let r="^";for(let t of e)t==="*"?r+=".*":/[.*+?^${}()|[\]\\]/.test(t)?r+=`\\${t}`:r+=t;return r+="$",new RegExp(r).test(n)}i(V,"globMatch");function A(n,e){return n?e.some(r=>V(n,r)):!1}i(A,"anyGlob");function j(n){if(n==null||n==="")return ln;if(typeof n=="string"&&kn.has(n))return n;throw new l("slice")}i(j,"closedSlice");function W(n){if(n==null||n==="")return null;if(typeof n=="string"&&/^(0|[1-9]\d*)$/.test(n))return Number(n);if(typeof n=="number"&&Number.isInteger(n)&&n>=0)return n;throw new l("degreesOfSeparation")}i(W,"closedDegrees");function F(n,e){if(n==null||n==="")return H();if(typeof n=="string"||Array.isArray(n))return{...H(),tokens:f(n,e)};if(typeof n!="object")throw new l(e);let r=n;for(let t of Object.keys(r))if(!An.has(t))throw new l(e);return{tokens:[],ids:f(r.ids,e),labels:f(r.labels,e),tags:f(r.tags,e),groups:f(r.groups,e),architectureKinds:f(r.architectureKinds,e)}}i(F,"closedMatch");function Q(n={}){if(n==null||n==="")return Q({});if(typeof n!="object"||Array.isArray(n))throw new l("slice","ArkRun graph query must be an object.");let e=n;for(let r of Object.keys(e))if(!Rn.has(r))throw new l("slice",`ArkRun graph query key "${r}" is not closed.`);return{slice:j(e.slice),nodeIds:f(e.nodeIds,"nodeIds"),degreesOfSeparation:W(e.degreesOfSeparation),include:F(e.include,"include"),exclude:F(e.exclude,"exclude")}}i(Q,"closeArkRunGraphQuery");function oe(n){let e={};n.slice&&(e.slice=j(n.slice));let r=f(_nullishCoalesce(n.nodeIds, () => (void 0)),"nodeIds");r.length>0&&(e.nodeIds=r);let t=W(_nullishCoalesce(n.degreesOfSeparation, () => (void 0)));t!==null&&(e.degreesOfSeparation=t);let s=f(_nullishCoalesce(n.include, () => (void 0)),"include");s.length>0&&(e.include=s);let u=f(_nullishCoalesce(n.exclude, () => (void 0)),"exclude");return u.length>0&&(e.exclude=u),e}i(oe,"arkRunGraphQueryFromSearchParams");function mn(n){let e=[n.id];return n.label&&e.push(n.label),n.group&&e.push(n.group),n.architectureKind&&e.push(n.architectureKind),n.tags&&e.push(...n.tags),e}i(mn,"nodeFields");function $(n,e){if(!T(e))return!0;let r=mn(n);return e.tokens.some(t=>r.some(s=>V(s,t)))||A(n.id,e.ids)||A(n.label,e.labels)||_optionalChain([n, 'access', _2 => _2.tags, 'optionalAccess', _3 => _3.some, 'call', _4 => _4(t=>A(t,e.tags))])||A(n.group,e.groups)?!0:A(n.architectureKind,e.architectureKinds)}i($,"nodeMatches");function yn(n){if(n&&typeof n=="object"&&!Array.isArray(n)){let e=n;return R({kernelInstanceId:e.kernelInstanceId,components:e.components})}return R({})}i(yn,"asPackage");function hn(n){let e={id:n.id,kind:"component",lifetime:n.lifetime},r=n.extendedInfo;return _optionalChain([r, 'optionalAccess', _5 => _5.label])&&(e.label=r.label),_optionalChain([r, 'optionalAccess', _6 => _6.group])&&(e.group=r.group),_optionalChain([r, 'optionalAccess', _7 => _7.architectureKind])&&(e.architectureKind=r.architectureKind),_optionalChain([r, 'optionalAccess', _8 => _8.tags])&&r.tags.length>0&&(e.tags=[...r.tags]),e}i(hn,"componentNode");function S(n){return{id:n,kind:"interaction"}}i(S,"interactionNode");function m(n,e){let r=n.get(e.id);(!r||r.kind==="interaction"&&e.kind==="component")&&n.set(e.id,e)}i(m,"addNode");function _(n,e,r,t,s){let u=`${r}\0${s}\0${t}`;e.has(u)||(e.add(u),n.push({from:r,to:t,kind:s}))}i(_,"addEdge");function bn(n,e){let r=new Map,t=[],s=new Set;for(let u of n){if(m(r,hn(u)),e==="technical"){for(let a of u.uses)m(r,_nullishCoalesce(r.get(a), () => (S(a)))),_(t,s,u.id,a,"uses");continue}for(let a of u.raises)m(r,_nullishCoalesce(r.get(a), () => (S(a)))),_(t,s,u.id,a,"raises");for(let a of u.reactsTo)m(r,_nullishCoalesce(r.get(a), () => (S(a)))),_(t,s,a,u.id,"reactsTo");for(let a of u.sends)m(r,_nullishCoalesce(r.get(a), () => (S(a)))),_(t,s,u.id,a,"sends")}return{nodes:r,edges:t}}i(bn,"buildSlice");function In(n){let e=new Map,r=i((t,s)=>{let u=e.get(t);u?u.push(s):e.set(t,[s])},"push");for(let t of n)r(t.from,t.to),r(t.to,t.from);return e}i(In,"adjacency");function wn(n,e,r,t){let s=e.filter(o=>n.has(o));if(s.length===0)return new Set;let u=new Set,a=s.map(o=>({id:o,depth:0}));for(;a.length>0;){let o=a.shift();if(!(!o||u.has(o.id))&&(u.add(o.id),!(r!==null&&o.depth>=r)))for(let c of _nullishCoalesce(t.get(o.id), () => ([])))!u.has(c)&&n.has(c)&&a.push({id:c,depth:o.depth+1})}return u}i(wn,"neighborhood");function h(n){let e=n.replace(/[^A-Za-z0-9_]/g,"_");return/^[A-Za-z_]/.test(e)?e:`n_${e}`}i(h,"mermaidSafeId");function B(n){return n.replace(/\\/g,"\\\\").replace(/"/g,"#quot;").replace(/]/g,"#93;")}i(B,"mermaidLabel");function Sn(n){let e=new Set,r=new Map;for(let t of n){let s=h(t),u=s,a=2;for(;e.has(u);)u=`${s}_${a}`,a+=1;e.add(u),r.set(t,u)}return r}i(Sn,"uniqueMermaidIds");function _n(n){let e=n.slice==="technical"?"TD":"LR",r=n.nodes.map(o=>o.id),t=Sn(r),s=C(n.nodes.map(o=>o.group).filter(o=>!!o)),u=[`flowchart ${e}`],a=i((o,c)=>{let d=_nullishCoalesce(t.get(o.id), () => (h(o.id))),p=B(_nullishCoalesce(o.label, () => (o.id)));u.push(`${c}${d}["${p}"]`)},"emitNode");for(let o of s){let c=h(`group_${o}`);u.push(` subgraph ${c}["${B(o)}"]`);for(let d of n.nodes)d.group===o&&a(d," ");u.push(" end")}for(let o of n.nodes)o.group||a(o," ");for(let o of n.edges){let c=_nullishCoalesce(t.get(o.from), () => (h(o.from))),d=_nullishCoalesce(t.get(o.to), () => (h(o.to)));u.push(` ${c} -->|${o.kind}| ${d}`)}return u.join(`
2
+ `)}i(_n,"formatArkRunGraphMermaid");function xn(n,e,r){let t=new Set(n.keys());if(T(r.include)&&(t=new Set([...t].filter(o=>{let c=n.get(o);return c!==void 0&&$(c,r.include)}))),T(r.exclude))for(let o of[...t]){let c=n.get(o);c&&$(c,r.exclude)&&t.delete(o)}let s=e.filter(o=>t.has(o.from)&&t.has(o.to));r.nodeIds.length>0&&(t=wn(t,r.nodeIds,r.degreesOfSeparation,In(s)));let u=[...t].sort(y).map(o=>n.get(o)).filter(o=>!!o),a=s.filter(o=>t.has(o.from)&&t.has(o.to)).sort((o,c)=>y(o.from,c.from)||y(o.kind,c.kind)||y(o.to,c.to));return{nodes:u,edges:a}}i(xn,"filterGraph");function se(n={},e={}){let r=yn(n),t=Q(e),s=bn(r.components,t.slice),u=xn(s.nodes,s.edges,t),a={schemaVersion:fn,kernelInstanceId:r.kernelInstanceId,slice:t.slice,query:t,nodes:u.nodes,edges:u.edges,mermaid:""};return a.mermaid=_n(a),a}i(se,"requestArkRunGraph");var O=["local","localBlocking","broker"],z= exports.t =!0,Nn=new Set(O),x= exports.u =class extends Error{static{i(this,"InvalidArkRunSendOptionError")}constructor(e){super(e==="transport"?'ArkRun transport must be "local", "localBlocking", or "broker".':"ArkRun ephemeral must be a boolean."),this.name="InvalidArkRunSendOptionError",this.option=e}};function En(n){if(n===void 0)return"local";if(typeof n=="string"&&Nn.has(n))return n;throw new x("transport")}i(En,"closedArkRunTransportKind");function Cn(n){if(n===void 0)return!0;if(n===!0||n===!1)return n;throw new x("ephemeral")}i(Cn,"closedArkRunEphemeral");function ae(n){let e=En(n.transport),r=Cn(n.ephemeral),t=e==="broker"&&n.brokerBound!==!0,s=e==="broker"&&!t?"broker":"local",u=e==="localBlocking";return{transport:e,ephemeral:r,deliveredVia:s,fallbackToLocal:t,notifySubscribers:s==="local",awaitHandlers:u,awaitHandoff:r||u}}i(ae,"resolveArkRunSendPlan");var Tn="1.0",q= exports.z ="127.0.0.1",On= exports.A =0,le= exports.B ="/snapshot",ge= exports.C ="/events",ke= exports.D ="/graph",Re= exports.E ="/outbox",Ae= exports.F ="/workflows",Kn= exports.G ="snapshot",Mn= exports.H ="in-process-local",b= exports.I =32;var P=class extends Error{static{i(this,"ArkRunInspectorProductionError")}constructor(){super("ArkRun inspector refuses to start when NODE_ENV=production."),this.name="ArkRunInspectorProductionError"}},I= exports.K =class extends Error{static{i(this,"ArkRunInspectorBindError")}constructor(e){super(`ArkRun inspector binds loopback only (default 127.0.0.1). Public host "${e}" is rejected.`),this.name="ArkRunInspectorBindError"}},Pn=new Set(["","*","0","0.0.0.0","::","::0","[::]","[::0]","::ffff:0.0.0.0","[::ffff:0.0.0.0]"]);function J(n){return n==="production"}i(J,"isArkRunInspectorProductionEnv");function Gn(n){let e=n.split(".");if(e.length!==4)return;let r=[];for(let t of e){if(!/^\d{1,3}$/.test(t))return;let s=Number(t);if(s>255)return;r.push(s)}return r}i(Gn,"ipv4Octets");function X(n){if(n.startsWith("[::ffff:")&&n.endsWith("]"))return n.slice(8,-1);if(n.startsWith("::ffff:"))return n.slice(7)}i(X,"unwrapIpv4Mapped");function Y(n){if(typeof n!="string")return!1;let e=n.trim().toLowerCase();if(e==="localhost"||e==="::1"||e==="[::1]")return!0;let r=X(e);if(r)return Y(r);let t=Gn(e);return t!==void 0&&t[0]===127}i(Y,"isArkRunInspectorLoopbackHost");function Z(n){if(n===void 0)return q;if(typeof n!="string")throw new I(String(n));let e=n.trim();if(Pn.has(e.toLowerCase())||!Y(e))throw new I(e);let r=e.toLowerCase();if(r==="localhost")return q;if(r==="[::1]")return"::1";let t=X(r);return t?Z(t):r}i(Z,"closedHost");function Dn(n){if(n===void 0)return On;if(typeof n!="number"||!Number.isInteger(n)||n<0||n>65535)throw new I(`port:${String(n)}`);return n}i(Dn,"closedPort");function Ln(n={}){if(J(n.nodeEnv)||J(n.processNodeEnv))throw new P;return{host:Z(n.host),port:Dn(n.port)}}i(Ln,"resolveArkRunInspectorBind");function me(n,e,r){return`http://${n.includes(":")?`[${n}]`:n}:${e}${r}`}i(me,"arkRunInspectorUrl");function Un(n){if(n!==void 0)try{return JSON.parse(JSON.stringify(n))}catch (e2){return}}i(Un,"jsonClone");function Hn(n){return{kinds:[...O],ephemeralDefault:n.ephemeralDefault===!1?!1:!0,brokerBound:n.brokerBound===!0,cloudSdksShipped:!1,fallback:Mn}}i(Hn,"closedTransportFacts");function g(n){if(!(!n||typeof n!="object"||Array.isArray(n)))return n}i(g,"asRecord");function Fn(n){let e=g(n);if(!e||typeof e.id!="string")return;let r=e.status;if(r!=="pending"&&r!=="failed")return;let t=g(e.event),s=t&&typeof t.intent=="string"?t.intent:typeof e.intent=="string"?e.intent:void 0,u={id:e.id,status:r,attempts:typeof e.attempts=="number"&&Number.isFinite(e.attempts)?e.attempts:0};return s&&(u.intent=s),typeof e.error=="string"&&e.error.length>0&&(u.error=e.error),typeof e.updatedAt=="string"&&(u.updatedAt=e.updatedAt),u}i(Fn,"closedOutboxSummary");function N(n){if(typeof n!="number"||!Number.isFinite(n))return b;let e=Math.trunc(n);return e<0?0:e>b?b:e}i(N,"closedSampleLimit");function $n(n,e){let r=typeof n=="string"&&n.trim().length>0?n.trim():n&&typeof n=="object"&&"constructor"in n?String(_nullishCoalesce(_optionalChain([n, 'access', _9 => _9.constructor, 'optionalAccess', _10 => _10.name]), () => (e))):e,t=/^InMemory/i.test(r)?"memory":"durable";return{role:e,id:r,kind:t}}i($n,"classifyArkRunInspectorStoreDurability");function Bn(n={}){let e=g(n),r=_nullishCoalesce(g(_optionalChain([e, 'optionalAccess', _11 => _11.durability])), () => (e)),t=Array.isArray(_optionalChain([r, 'optionalAccess', _12 => _12.stores]))?r.stores:Array.isArray(_optionalChain([e, 'optionalAccess', _13 => _13.stores]))?e.stores:[],s=[];for(let u of t){let a=g(u);if(!a)continue;let o=a.role;if(o!=="outbox"&&o!=="audit"&&o!=="workflow")continue;let c=typeof a.id=="string"?a.id:typeof a.name=="string"?a.name:void 0;if(!c)continue;let d=a.kind==="memory"||a.kind==="durable"?a.kind:$n(c,o).kind;s.push({role:o,id:c,kind:d})}return{durability:{stores:s}}}i(Bn,"buildArkRunInspectorHardening");function Vn(n=[],e={}){let r=N(e.sampleLimit),t=Array.isArray(n)?n:[],s=[],u=[];for(let a of t){let o=Fn(a);o&&(o.status==="pending"?s.push(o):u.push(o))}return{available:!0,pendingCount:s.length,failedCount:u.length,pending:s.slice(0,r),failed:u.slice(0,r),sampleLimit:r}}i(Vn,"buildArkRunInspectorOutboxMonitor");function K(){return{available:!1,pendingCount:0,failedCount:0,pending:[],failed:[],sampleLimit:b}}i(K,"unavailableArkRunInspectorOutboxMonitor");function jn(n){let e=g(n);if(!e||typeof e.id!="string")return;let r=typeof e.workflowName=="string"?e.workflowName:typeof e.name=="string"?e.name:void 0;if(!r)return;let t={id:e.id,name:r,status:typeof e.status=="string"?e.status:"unknown"};return typeof e.currentStep=="string"&&(t.currentStep=e.currentStep),typeof e.error=="string"&&e.error.length>0&&(t.error=e.error),t}i(jn,"closedWorkflowSummary");function Wn(n){return n==="idle"||n==="waiting"}i(Wn,"isPendingWorkflowStatus");function Qn(n=[],e={}){let r=N(e.sampleLimit),t=Array.isArray(n)?n:[],s=[],u=0,a=0,o=0,c=0;for(let d of t){let p=jn(d);p&&(s.push(p),p.status==="running"?u+=1:p.status==="compensating"?a+=1:p.status==="failed"?o+=1:Wn(p.status)&&(c+=1))}return{available:!0,total:s.length,runningCount:u,compensatingCount:a,failedCount:o,pendingCount:c,workflows:s.slice(0,r),sampleLimit:r}}i(Qn,"buildArkRunInspectorWorkflowsMonitor");function M(){return{available:!1,total:0,runningCount:0,compensatingCount:0,failedCount:0,pendingCount:0,workflows:[],sampleLimit:b}}i(M,"unavailableArkRunInspectorWorkflowsMonitor");function zn(n){if(n===void 0)return;let e=g(n);if(!e||e.available===!1)return K();let r=N(e.sampleLimit);if(Array.isArray(e.pending)||Array.isArray(e.failed)||Array.isArray(e.records)){let t=[...Array.isArray(e.pending)?e.pending:[],...Array.isArray(e.failed)?e.failed:[],...Array.isArray(e.records)?e.records:[]],s=Vn(t,{sampleLimit:r}),u=typeof e.pendingCount=="number"&&Number.isFinite(e.pendingCount)?Math.max(0,Math.trunc(e.pendingCount)):s.pendingCount,a=typeof e.failedCount=="number"&&Number.isFinite(e.failedCount)?Math.max(0,Math.trunc(e.failedCount)):s.failedCount;return{...s,pendingCount:u,failedCount:a,sampleLimit:r}}return K()}i(zn,"closedOutboxMonitor");function qn(n){if(n===void 0)return;let e=g(n);if(!e||e.available===!1)return M();let r=N(e.sampleLimit);if(Array.isArray(e.workflows)){let t=Qn(e.workflows,{sampleLimit:r}),s=typeof e.total=="number"&&Number.isFinite(e.total)?Math.max(0,Math.trunc(e.total)):t.total,u=typeof e.runningCount=="number"&&Number.isFinite(e.runningCount)?Math.max(0,Math.trunc(e.runningCount)):t.runningCount,a=typeof e.compensatingCount=="number"&&Number.isFinite(e.compensatingCount)?Math.max(0,Math.trunc(e.compensatingCount)):t.compensatingCount,o=typeof e.failedCount=="number"&&Number.isFinite(e.failedCount)?Math.max(0,Math.trunc(e.failedCount)):t.failedCount,c=typeof e.pendingCount=="number"&&Number.isFinite(e.pendingCount)?Math.max(0,Math.trunc(e.pendingCount)):t.pendingCount;return{...t,total:s,runningCount:u,compensatingCount:a,failedCount:o,pendingCount:c,sampleLimit:r}}return M()}i(qn,"closedWorkflowsMonitor");function ye(n={}){let e=Ln({host:n.host,port:n.port}),r=R({kernelInstanceId:n.kernelInstanceId,components:n.package&&typeof n.package=="object"&&!Array.isArray(n.package)&&Array.isArray(n.package.components)?n.package.components:[]}),t=zn(n.outbox),s=qn(n.workflows),u={schemaVersion:Tn,kernelInstanceId:typeof n.kernelInstanceId=="string"?n.kernelInstanceId:r.kernelInstanceId,bind:{host:e.host,port:e.port,loopback:!0},package:{...r,schemaVersion:"1.0"},transport:Hn(n),observability:_nullishCoalesce(Un(n.observability), () => ({})),hardening:Bn(n.hardening)};return t&&(u.outbox=t),s&&(u.workflows=s),u}i(ye,"buildArkRunInspectorSnapshot");function he(n){return`event: ${Kn}
3
+ data: ${JSON.stringify(n)}
4
+
5
+ `}i(he,"formatArkRunInspectorSseEvent");exports.a = i; exports.b = Jn; exports.c = D; exports.d = nn; exports.e = an; exports.f = R; exports.g = Yn; exports.h = fn; exports.i = ln; exports.j = gn; exports.k = ee; exports.l = re; exports.m = te; exports.n = l; exports.o = Q; exports.p = oe; exports.q = _n; exports.r = se; exports.s = O; exports.t = z; exports.u = x; exports.v = En; exports.w = Cn; exports.x = ae; exports.y = Tn; exports.z = q; exports.A = On; exports.B = le; exports.C = ge; exports.D = ke; exports.E = Re; exports.F = Ae; exports.G = Kn; exports.H = Mn; exports.I = b; exports.J = P; exports.K = I; exports.L = J; exports.M = Y; exports.N = Ln; exports.O = me; exports.P = $n; exports.Q = Bn; exports.R = Vn; exports.S = K; exports.T = Qn; exports.U = M; exports.V = ye; exports.W = he;