arkgate 4.8.23 → 4.8.25

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 (201) hide show
  1. package/CHANGELOG.md +153 -0
  2. package/README.md +7 -5
  3. package/bin/ark-check-runtime.mjs +41 -91
  4. package/bin/ark-dashboard.mjs +56 -53
  5. package/bin/ark-layer-match.mjs +707 -121
  6. package/bin/ark-mcp-runtime.mjs +448 -169
  7. package/bin/ark-mcp.mjs +68 -4
  8. package/bin/ark-shared.mjs +42 -22
  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/adoption-stance.mjs +2 -2
  13. package/bin/lib/adr-path.mjs +3 -3
  14. package/bin/lib/agent-gates.mjs +0 -61
  15. package/bin/lib/agent-homes.mjs +12 -21
  16. package/bin/lib/agent-projection-command.mjs +9 -5
  17. package/bin/lib/ambient-state.mjs +9 -30
  18. package/bin/lib/analysis-engine.mjs +15 -11
  19. package/bin/lib/architecture-scan.mjs +61 -15
  20. package/bin/lib/ark-order-doctor.mjs +4 -29
  21. package/bin/lib/ark-order-facts.mjs +319 -17
  22. package/bin/lib/ark-order-sensors.mjs +28 -101
  23. package/bin/lib/ark-order-types.mjs +3 -2
  24. package/bin/lib/ark-run-doctor.mjs +17 -28
  25. package/bin/lib/ark-run-facts.mjs +461 -48
  26. package/bin/lib/ark-run-sensors.mjs +6 -4
  27. package/bin/lib/arkgate-pins.mjs +91 -0
  28. package/bin/lib/arkrule-file-hints.mjs +2 -47
  29. package/bin/lib/arkrules-contract.mjs +124 -133
  30. package/bin/lib/arkrules-sensors.mjs +55 -252
  31. package/bin/lib/check-args.mjs +85 -1
  32. package/bin/lib/ci-and-commands.mjs +24 -78
  33. package/bin/lib/ci-merge-boundary.mjs +1 -1
  34. package/bin/lib/class-source-scan.mjs +479 -0
  35. package/bin/lib/clone-detection.mjs +474 -0
  36. package/bin/lib/codex-home.mjs +5 -3
  37. package/bin/lib/config-contract-slices.mjs +739 -0
  38. package/bin/lib/config-contract.mjs +42 -607
  39. package/bin/lib/config-extras.mjs +16 -22
  40. package/bin/lib/config-warnings.mjs +32 -0
  41. package/bin/lib/contract-smells.mjs +33 -15
  42. package/bin/lib/core-ratchet.mjs +0 -2
  43. package/bin/lib/dashboard-cli.mjs +62 -0
  44. package/bin/lib/deep-module-coach.mjs +2 -4
  45. package/bin/lib/deploy-path.mjs +13 -38
  46. package/bin/lib/design-delta.mjs +1 -1
  47. package/bin/lib/design-smells.mjs +2 -2
  48. package/bin/lib/diagnostic-catalog.mjs +23 -9
  49. package/bin/lib/doctor-advisories.mjs +78 -8
  50. package/bin/lib/doctor-green-cite.mjs +1 -1
  51. package/bin/lib/doctor-human.mjs +1 -1
  52. package/bin/lib/doctor-next-actions.mjs +19 -4
  53. package/bin/lib/doctor-plan.mjs +9 -13
  54. package/bin/lib/duplication-io.mjs +572 -0
  55. package/bin/lib/effective-contract-load.mjs +282 -83
  56. package/bin/lib/enforcement-profiles.mjs +40 -4
  57. package/bin/lib/entry-points-io.mjs +424 -0
  58. package/bin/lib/extra-merge-teeth.mjs +36 -0
  59. package/bin/lib/field-install.mjs +38 -14
  60. package/bin/lib/first-run-help.mjs +10 -5
  61. package/bin/lib/flat-parent-importers.mjs +139 -0
  62. package/bin/lib/flat-parent-pilot.mjs +40 -100
  63. package/bin/lib/gate-files.mjs +58 -90
  64. package/bin/lib/git-change-scope.mjs +109 -0
  65. package/bin/lib/github-enforcement.mjs +166 -29
  66. package/bin/lib/golden-pattern.mjs +1 -1
  67. package/bin/lib/hook-templates.mjs +17 -15
  68. package/bin/lib/host-support-matrix.mjs +3 -1
  69. package/bin/lib/html-report-advisories.mjs +4 -0
  70. package/bin/lib/html-report-depth.mjs +2 -2
  71. package/bin/lib/html-report.mjs +2 -2
  72. package/bin/lib/import-graph-projection.mjs +112 -0
  73. package/bin/lib/import-resolve.mjs +13 -1
  74. package/bin/lib/improvement-compass-doctor.mjs +1 -24
  75. package/bin/lib/install-migrate.mjs +50 -73
  76. package/bin/lib/invariant-coverage-io.mjs +4 -3
  77. package/bin/lib/invariant-coverage.mjs +85 -11
  78. package/bin/lib/invariant-probe-cli.mjs +347 -0
  79. package/bin/lib/invariant-probe-io.mjs +165 -0
  80. package/bin/lib/invariant-probe-runner.mjs +268 -0
  81. package/bin/lib/invariant-probe-sites.mjs +174 -0
  82. package/bin/lib/invariant-probe-workspace.mjs +183 -0
  83. package/bin/lib/invariant-probe.mjs +461 -0
  84. package/bin/lib/invariant-tests-path.mjs +16 -22
  85. package/bin/lib/literal-path-drift.mjs +4 -2
  86. package/bin/lib/managed-upgrade.mjs +2 -8
  87. package/bin/lib/mcp-adoption.mjs +13 -6
  88. package/bin/lib/mcp-hook-payload.mjs +403 -63
  89. package/bin/lib/orphan-modules-io.mjs +458 -0
  90. package/bin/lib/orphan-modules.mjs +251 -0
  91. package/bin/lib/outside-importers.mjs +197 -0
  92. package/bin/lib/package-manager.mjs +60 -2
  93. package/bin/lib/parse-health.mjs +1 -1
  94. package/bin/lib/passthrough-commands.mjs +83 -0
  95. package/bin/lib/persistence-write-hint.mjs +106 -0
  96. package/bin/lib/pin-dependent-gates.mjs +117 -0
  97. package/bin/lib/policy-delta-io.mjs +219 -14
  98. package/bin/lib/post-green-path.mjs +1 -1
  99. package/bin/lib/prepare-change.mjs +115 -7
  100. package/bin/lib/prepare-write.mjs +2 -2
  101. package/bin/lib/presets.mjs +59 -73
  102. package/bin/lib/product-copy.mjs +1 -6
  103. package/bin/lib/project-root.mjs +31 -62
  104. package/bin/lib/remediation.mjs +23 -4
  105. package/bin/lib/resolved-candidate-facts.mjs +405 -117
  106. package/bin/lib/rules-inventory.mjs +22 -3
  107. package/bin/lib/rules-under-contract.mjs +247 -9
  108. package/bin/lib/scan-files.mjs +36 -4
  109. package/bin/lib/schema-validation.mjs +130 -0
  110. package/bin/lib/sensor-promote-cli.mjs +2 -11
  111. package/bin/lib/sensor-promote-io.mjs +33 -22
  112. package/bin/lib/sensor-promotion.mjs +1 -1
  113. package/bin/lib/skill-catalog-apply.mjs +1 -1
  114. package/bin/lib/skill-install.mjs +42 -38
  115. package/bin/lib/snippet-analysis.mjs +140 -6
  116. package/bin/lib/source-policy.mjs +34 -0
  117. package/bin/lib/start-install-recovery.mjs +72 -2
  118. package/bin/lib/start-preview.mjs +31 -1
  119. package/bin/lib/states-transitions-presence.mjs +3 -3
  120. package/bin/lib/status-command.mjs +35 -22
  121. package/bin/lib/status-manifest.mjs +50 -30
  122. package/bin/lib/status-transition-catalog.mjs +0 -6
  123. package/bin/lib/suggestions.mjs +3 -3
  124. package/bin/lib/team-parliament-io.mjs +76 -88
  125. package/bin/lib/team-parliament.mjs +21 -10
  126. package/bin/lib/upgrade-behavior-changes.mjs +2 -2
  127. package/bin/lib/upgrade-command.mjs +2 -24
  128. package/bin/lib/upgrade-outcome.mjs +1 -1
  129. package/bin/lib/upstream-report.mjs +21 -10
  130. package/bin/lib/violations.mjs +10 -9
  131. package/bin/lib/weakest-link.mjs +13 -0
  132. package/bin/lib/write-path-capabilities.mjs +6 -4
  133. package/dist/chunk-A5EIY3HI.cjs +5 -0
  134. package/dist/chunk-AHACEKSV.js +16 -0
  135. package/dist/chunk-SVSQVGFJ.js +5 -0
  136. package/dist/chunk-UN5EH4QV.cjs +16 -0
  137. package/dist/{configTypes-Bbf_MFGe.d.ts → configTypes-GPvvdleM.d.cts} +30 -4
  138. package/dist/configTypes-GPvvdleM.d.ts +257 -0
  139. package/dist/eslint/index.cjs +18 -5
  140. package/dist/eslint/index.d.cts +368 -0
  141. package/dist/eslint/index.d.ts +139 -156
  142. package/dist/eslint/index.js +11 -5
  143. package/dist/index.cjs +36 -35
  144. package/dist/index.d.cts +5547 -0
  145. package/dist/index.d.ts +3283 -198
  146. package/dist/index.js +36 -35
  147. package/dist/inspectorListen-6DQVJ4DC.js +1 -0
  148. package/dist/inspectorListen-KGMIQ2TF.cjs +1 -0
  149. package/dist/nestjs/index.cjs +1 -18
  150. package/dist/nestjs/index.d.cts +22 -0
  151. package/dist/nestjs/index.d.ts +1 -3
  152. package/dist/nestjs/index.js +1 -18
  153. package/dist/order/index.cjs +1 -1
  154. package/dist/order/index.d.cts +155 -0
  155. package/dist/order/index.d.ts +15 -3
  156. package/dist/order/index.js +1 -1
  157. package/dist/runtime/index.cjs +13 -30
  158. package/dist/{diagnosticCatalog-35mrwAHe.d.ts → runtime/index.d.cts} +828 -276
  159. package/dist/runtime/index.d.ts +2741 -133
  160. package/dist/runtime/index.js +13 -30
  161. package/dist/{types-Bdj32QwA.d.ts → types-CnauMQQL.d.cts} +607 -163
  162. package/dist/types-CnauMQQL.d.ts +1800 -0
  163. package/docs/README.md +3 -1
  164. package/docs/agent-guide.md +217 -16
  165. package/docs/ai-gates.md +174 -26
  166. package/docs/arkorder.md +32 -15
  167. package/docs/brownfield-adoption.md +5 -1
  168. package/docs/configuration.md +230 -31
  169. package/docs/develop.md +2 -1
  170. package/docs/diagnostics.md +212 -19
  171. package/docs/package-surface.md +35 -9
  172. package/docs/product-voice.md +7 -1
  173. package/package.json +69 -19
  174. package/schemas/ark.analysis-result.schema.json +15 -0
  175. package/schemas/ark.config.schema.json +77 -12
  176. package/schemas/ark.invariant-probe.schema.json +368 -0
  177. package/schemas/ark.resolved-candidate-facts.schema.json +1 -1
  178. package/schemas/ark.status-manifest.schema.json +22 -0
  179. package/server.json +3 -9
  180. package/templates/agent-skills/README.md +1 -1
  181. package/templates/agent-skills/ark-adopt/SKILL.md +5 -1
  182. package/templates/agent-skills/ark-contract/SKILL.md +17 -0
  183. package/templates/agent-skills/ark-coverage/SKILL.md +2 -0
  184. package/templates/agent-skills/ark-explore/SKILL.md +7 -0
  185. package/templates/agent-skills/ark-fix/SKILL.md +20 -0
  186. package/templates/agent-skills/ark-order/SKILL.md +4 -2
  187. package/templates/agent-skills/ark-place/SKILL.md +8 -0
  188. package/templates/agent-skills/ark-runtime/SKILL.md +6 -1
  189. package/templates/arkrules/PersistenceAdapters.json +1 -1
  190. package/templates/arkrules/PresentationAdapters.json +1 -1
  191. package/templates/skills/ark-adopt.md +5 -1
  192. package/templates/skills/ark-contract.md +17 -0
  193. package/templates/skills/ark-coverage.md +2 -0
  194. package/templates/skills/ark-explore.md +7 -0
  195. package/templates/skills/ark-fix.md +20 -0
  196. package/templates/skills/ark-order.md +4 -2
  197. package/templates/skills/ark-place.md +8 -0
  198. package/templates/skills/ark-runtime.md +6 -1
  199. package/bin/lib/ark-order-error.mjs +0 -18
  200. package/bin/lib/ark-order-invariants.mjs +0 -346
  201. package/dist/types-Ik_OakcI.d.ts +0 -183
@@ -8,105 +8,9 @@
8
8
  * Pure CLI helper (bin/lib/ark-order-sensors.mjs). Zero Node I/O.
9
9
  */
10
10
 
11
- import { extractArkOrderGenericUpdatesFromSource, extractArkOrderIngestWritesXiFromSource, extractArkOrderPlaneCallsFromSource, extractArkOrderReleaseKeyCountsFromSource, extractArkOrderXiFieldWritesFromSource, isArkOrderModuleSpecifier, } from './ark-order-facts.mjs';
11
+ import { extractArkOrderBudgetLeaksFromSource, extractArkOrderGenericUpdatesFromSource, extractArkOrderIngestWritesXiFromSource, extractArkOrderModuleImportsFromSource, extractArkOrderPlaneCallsFromSource, extractArkOrderReleaseKeyCountsFromSource, extractArkOrderXiFieldWritesFromSource, extractArkOrderXiTtlKeysFromSource, arkOrderGlobToRegExp as globToRegExp, isArkOrderModuleSpecifier, } from './ark-order-facts.mjs';
12
12
  import { extraMergeTeethAllowed, } from './extra-merge-teeth.mjs';
13
13
  import { deterministicNextAction } from './remediation.mjs';
14
- /**
15
- * XIWRITE-001: same engine as `globToRegExp` in src/domain/layerMatch.ts.
16
- * Inlined so generate:cli-pure emits a self-contained bin/lib/ark-order-sensors.mjs
17
- * (layerMatch is derived to bin/ark-layer-match.mjs, not a bin/lib sibling).
18
- */
19
- const appliesToRegexpCache = new Map();
20
- function escapeAppliesToLiteral(ch) {
21
- return /[.*+?^${}()|[\]\\]/.test(ch) ? `\\${ch}` : ch;
22
- }
23
- function normalizeAppliesToGlob(pattern) {
24
- let out = '';
25
- for (let i = 0; i < pattern.length; i += 1) {
26
- const c = pattern[i];
27
- if (c === '\\' && i + 1 < pattern.length) {
28
- const next = pattern[i + 1];
29
- if ('*?{}[],'.includes(next) || next === '\\') {
30
- out += '\\' + next;
31
- i += 1;
32
- continue;
33
- }
34
- out += '/';
35
- continue;
36
- }
37
- out += c;
38
- }
39
- return out;
40
- }
41
- function appliesToBracesBalanced(glob) {
42
- let depth = 0;
43
- for (let i = 0; i < glob.length; i += 1) {
44
- const c = glob[i];
45
- if (c === '\\') {
46
- i += 1;
47
- continue;
48
- }
49
- if (c === '{')
50
- depth += 1;
51
- else if (c === '}') {
52
- depth -= 1;
53
- if (depth < 0)
54
- return false;
55
- }
56
- }
57
- return depth === 0;
58
- }
59
- function globToRegExp(pattern) {
60
- const cached = appliesToRegexpCache.get(pattern);
61
- if (cached)
62
- return cached;
63
- const glob = normalizeAppliesToGlob(pattern);
64
- const useBraces = appliesToBracesBalanced(glob);
65
- let out = '';
66
- let braceDepth = 0;
67
- for (let i = 0; i < glob.length; i += 1) {
68
- const c = glob[i];
69
- if (c === '\\' && i + 1 < glob.length) {
70
- out += escapeAppliesToLiteral(glob[i + 1]);
71
- i += 1;
72
- }
73
- else if (c === '*') {
74
- if (glob[i + 1] === '*') {
75
- if (glob[i + 2] === '/') {
76
- out += '(?:.*/)?';
77
- i += 2;
78
- }
79
- else {
80
- out += '.*';
81
- i += 1;
82
- }
83
- }
84
- else {
85
- out += '[^/]*';
86
- }
87
- }
88
- else if (c === '?') {
89
- out += '[^/]';
90
- }
91
- else if (c === '{' && useBraces) {
92
- out += '(?:';
93
- braceDepth += 1;
94
- }
95
- else if (c === '}' && useBraces && braceDepth > 0) {
96
- out += ')';
97
- braceDepth -= 1;
98
- }
99
- else if (c === ',' && useBraces && braceDepth > 0) {
100
- out += '|';
101
- }
102
- else {
103
- out += escapeAppliesToLiteral(c);
104
- }
105
- }
106
- const re = new RegExp(`^${out}$`);
107
- appliesToRegexpCache.set(pattern, re);
108
- return re;
109
- }
110
14
  export const ARKORDER_TIER1_SENSOR_IDS = [
111
15
  'arkorder-missing-plane',
112
16
  'arkorder-kernel-in-domain',
@@ -212,6 +116,12 @@ export function evaluateArkOrderSensors(input) {
212
116
  for (const ingest of input.ingestWritesXi ?? []) {
213
117
  findings.push(finding(extra, 'arkorder-ingest-writes-xi', ingest.file, ingest.line, 'ingest() result is assigned into a Release or ξ store; ingest may absorb or escalate, never mint a pattern.', undefined, teethAllowed));
214
118
  }
119
+ for (const ttl of input.xiTtlKeys ?? []) {
120
+ findings.push(finding(extra, 'arkorder-xi-ttl', ttl.file, ttl.line, `ξ key ${JSON.stringify(ttl.key)} is a freshness field; freshness belongs on σ (freshUntil), never on ξ.`, { target: ttl.key }, teethAllowed));
121
+ }
122
+ for (const leak of input.budgetLeaks ?? []) {
123
+ findings.push(finding(extra, 'arkorder-information-budget', leak.file, leak.line, `Projection allowedKinds lists ${JSON.stringify(leak.kind)}, which informationBudget.cannotObserve denies.`, { target: leak.kind }, teethAllowed));
124
+ }
215
125
  const managed = new Set(extra.managedLayers);
216
126
  for (const write of xiKeys.length === 0 ? [] : input.xiFieldWrites ?? []) {
217
127
  const fromLayer = input.layerForFile(write.file);
@@ -230,22 +140,39 @@ export function evaluateArkOrderEditorSensors(input) {
230
140
  if (!input.arkOrder)
231
141
  return [];
232
142
  const planeCalls = extractArkOrderPlaneCallsFromSource(input.file, input.source);
233
- const genericUpdates = extractArkOrderGenericUpdatesFromSource(input.file, input.source);
143
+ const receivers = { planeRoots: input.arkOrder.planeRoots ?? [] };
144
+ const genericUpdates = extractArkOrderGenericUpdatesFromSource(input.file, input.source, receivers);
234
145
  const xiKeys = input.arkOrder.xiKeys ?? [];
146
+ // kernel-in-domain needs the file's own layer (name + intent prefixes) and the
147
+ // arkgate/order specifiers of this one source — the write-path twin of CI facts.
148
+ const layers = input.fromLayer
149
+ ? [
150
+ {
151
+ name: input.fromLayer,
152
+ patterns: [],
153
+ intentPrefixes: [...(input.intentPrefixes ?? [])],
154
+ },
155
+ ]
156
+ : [];
235
157
  return evaluateArkOrderSensors({
236
158
  arkOrder: input.arkOrder,
237
- layers: [],
159
+ layers,
238
160
  planeCalls,
239
161
  genericUpdates,
240
162
  planeRootHits: [],
241
163
  xiFieldWrites: extractArkOrderXiFieldWritesFromSource(input.file, input.source, xiKeys),
242
164
  ingestWritesXi: extractArkOrderIngestWritesXiFromSource(input.file, input.source),
243
165
  releaseKeyCounts: extractArkOrderReleaseKeyCountsFromSource(input.file, input.source),
244
- dependencies: [],
166
+ xiTtlKeys: extractArkOrderXiTtlKeysFromSource(input.file, input.source, receivers),
167
+ budgetLeaks: extractArkOrderBudgetLeaksFromSource(input.file, input.source),
168
+ dependencies: extractArkOrderModuleImportsFromSource(input.file, input.source),
245
169
  layerForFile: () => input.fromLayer,
170
+ ...(input.classification ? { classification: input.classification } : {}),
246
171
  }).findings.filter((item) => item.sensor === 'arkorder-generic-update' ||
247
172
  item.sensor === 'arkorder-kernel-in-domain' ||
248
173
  item.sensor === 'arkorder-xi-field-write' ||
249
174
  item.sensor === 'arkorder-ingest-writes-xi' ||
250
- item.sensor === 'arkorder-too-many-params');
175
+ item.sensor === 'arkorder-too-many-params' ||
176
+ item.sensor === 'arkorder-xi-ttl' ||
177
+ item.sensor === 'arkorder-information-budget');
251
178
  }
@@ -9,6 +9,7 @@
9
9
  */
10
10
 
11
11
  export const DEFAULT_MAX_XI_KEYS = 7;
12
- export const INGEST_RESIDUAL_KINDS = ['absorb', 'escalate_up', 'hold'];
13
- export const INGEST_REASON_CODES = ['not-in-pattern', 'stale-sigma', 'pack', 'capacity'];
12
+ /** ξ keys that name freshness. Freshness is σ, never ξ (runtime and static sensor share this). */
13
+ export const XI_TTL_KEY_RE = /^(ttl|freshUntil|fresh_until|maxAge|max_age)$/i;
14
+ const INGEST_REASON_CODES = ['not-in-pattern', 'stale-sigma', 'pack', 'capacity'];
14
15
  export const CAPACITY_OPS = ['lte', 'lt', 'gte', 'gt'];
@@ -8,7 +8,8 @@
8
8
  * Pure CLI helper (bin/lib/ark-run-doctor.mjs). Zero Node I/O.
9
9
  */
10
10
 
11
- import { composeMergePlanesHonesty, extraMergeTeethAllowed, isArkRunRuleId, } from './extra-merge-teeth.mjs';
11
+ import { composeMergePlanesHonesty, extraMergeTeethAllowed, isArkRunRuleId, mergePlanesArkRulesInput, projectExtraPlaneStatus, } from './extra-merge-teeth.mjs';
12
+ import { unresolvableLayerFlowLayers } from './source-policy.mjs';
12
13
  export const ARK_RUN_DOCTOR_SCHEMA_VERSION = '1.0';
13
14
  const RESIDUAL_RULE_CAP = 12;
14
15
  /** Compact / details — only when `arkRun` is on. Absence stays silent. */
@@ -47,22 +48,18 @@ function extraFromConfig(arkRun) {
47
48
  */
48
49
  export function summarizeArkRunSection(input = {}) {
49
50
  const extra = extraFromConfig(input.arkRun);
51
+ const layerFlowUnresolvable = extra.present && Array.isArray(input.layerFlow?.layers)
52
+ ? unresolvableLayerFlowLayers({
53
+ layers: input.layerFlow.layers,
54
+ rules: Array.isArray(input.layerFlow.rules) ? input.layerFlow.rules : [],
55
+ })
56
+ : [];
50
57
  const uniqueIds = extra.present ? uniqueArkRunRuleIds(input.findings) : [];
51
58
  const ruleIds = uniqueIds.slice(0, RESIDUAL_RULE_CAP);
52
59
  const residualCount = uniqueIds.length;
53
60
  const mergePlanes = composeMergePlanesHonesty({
54
61
  classification: input.classification,
55
- arkRules: {
56
- active: input.arkRules?.active === true,
57
- structureEnforced: input.arkRules?.structureEnforced,
58
- structureTotal: input.arkRules?.structureTotal,
59
- structureAdvisory: input.arkRules?.structureAdvisory,
60
- invariantEnforced: input.arkRules?.invariantEnforced,
61
- invariantTotal: input.arkRules?.invariantTotal,
62
- invariantAdvisory: input.arkRules?.invariantAdvisory,
63
- covered: input.arkRules?.covered,
64
- uncovered: input.arkRules?.uncovered,
65
- },
62
+ arkRules: mergePlanesArkRulesInput(input.arkRules),
66
63
  arkRun: {
67
64
  present: extra.present,
68
65
  mode: extra.mode,
@@ -100,26 +97,12 @@ export function summarizeArkRunSection(input = {}) {
100
97
  failMergeWhen: mergePlanes.failMergeWhen,
101
98
  note,
102
99
  mergePlanes,
100
+ layerFlowUnresolvable,
103
101
  };
104
102
  }
105
103
  /** Thin status slice — counts only; residual null means unknown, not green. */
106
104
  export function projectStatusArkRun(input = {}) {
107
- const present = input.present === true;
108
- const mode = closedMode(input.mode);
109
- const residualRaw = input.residual;
110
- let residual = null;
111
- if (typeof residualRaw === 'number' && Number.isFinite(residualRaw) && residualRaw >= 0) {
112
- residual = Math.floor(residualRaw);
113
- }
114
- if (!present)
115
- residual = residual == null ? 0 : residual;
116
- return {
117
- notAScore: true,
118
- present,
119
- mode: present ? mode : null,
120
- extraMergeTeeth: present && mode === 'enforced' && input.extraMergeTeeth === true,
121
- residual,
122
- };
105
+ return projectExtraPlaneStatus(input);
123
106
  }
124
107
  export function formatArkRunDoctorLines(section) {
125
108
  if (!section || section.notAScore !== true)
@@ -144,6 +127,12 @@ export function formatArkRunDoctorLines(section) {
144
127
  else {
145
128
  lines.push('Residual: none on this scan (not a score — green extras ≠ finished kernel wiring).');
146
129
  }
130
+ const unresolvable = Array.isArray(section.layerFlowUnresolvable)
131
+ ? section.layerFlowUnresolvable
132
+ : [];
133
+ if (unresolvable.length > 0) {
134
+ lines.push(`Runtime layer flow: ${unresolvable.join(', ')} name(s) in a deny rule but map to no intent (no intentPrefixes, not a canonical layer). A kernel from this config cannot enforce those rules — add intentPrefixes (ARKRUN_LAYER_FLOW_UNRESOLVABLE).`);
135
+ }
147
136
  if (section.failMergeWhen)
148
137
  lines.push(section.failMergeWhen);
149
138
  return lines;