arkgate 4.8.22 → 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.
- package/CHANGELOG.md +91 -0
- package/README.md +7 -5
- package/bin/ark-check-runtime.mjs +27 -78
- package/bin/ark-dashboard.mjs +56 -53
- package/bin/ark-layer-match.mjs +1117 -38
- package/bin/ark-mcp-runtime.mjs +412 -138
- package/bin/ark-mcp.mjs +68 -4
- package/bin/ark-shared.mjs +39 -0
- package/bin/ark.mjs +65 -97
- package/bin/lib/adapter-contract-types.mjs +5 -0
- package/bin/lib/adapter-contract.mjs +15 -0
- package/bin/lib/agent-projection-command.mjs +5 -1
- package/bin/lib/analysis-engine.mjs +15 -11
- package/bin/lib/architecture-scan.mjs +50 -6
- package/bin/lib/ark-order-doctor.mjs +1 -1
- package/bin/lib/ark-order-facts.mjs +410 -15
- package/bin/lib/ark-order-invariants.mjs +51 -9
- package/bin/lib/ark-order-sensors.mjs +28 -101
- package/bin/lib/ark-order-types.mjs +2 -0
- package/bin/lib/ark-run-doctor.mjs +14 -0
- package/bin/lib/ark-run-facts.mjs +434 -11
- package/bin/lib/ark-run-sensors.mjs +5 -3
- package/bin/lib/arkgate-pins.mjs +91 -0
- package/bin/lib/arkrules-sensors.mjs +50 -249
- package/bin/lib/check-args.mjs +23 -1
- package/bin/lib/ci-and-commands.mjs +16 -6
- package/bin/lib/class-source-scan.mjs +479 -0
- package/bin/lib/codex-home.mjs +4 -2
- package/bin/lib/config-contract-slices.mjs +701 -0
- package/bin/lib/config-contract.mjs +5 -2
- package/bin/lib/config-warnings.mjs +32 -0
- package/bin/lib/dashboard-cli.mjs +62 -0
- package/bin/lib/design-delta.mjs +1 -1
- package/bin/lib/diagnostic-catalog.mjs +45 -6
- package/bin/lib/doctor-advisories.mjs +84 -1
- package/bin/lib/doctor-next-actions.mjs +17 -2
- package/bin/lib/doctor-plan.mjs +7 -9
- package/bin/lib/effective-contract-load.mjs +121 -59
- package/bin/lib/enforcement-honesty.mjs +22 -0
- package/bin/lib/enforcement-profiles.mjs +40 -4
- package/bin/lib/first-run-help.mjs +3 -2
- package/bin/lib/flat-parent-importers.mjs +137 -0
- package/bin/lib/flat-parent-pilot.mjs +192 -0
- package/bin/lib/gate-files.mjs +29 -2
- package/bin/lib/git-change-scope.mjs +109 -0
- package/bin/lib/github-enforcement.mjs +1 -1
- package/bin/lib/hook-templates.mjs +18 -16
- package/bin/lib/host-support-matrix.mjs +3 -1
- package/bin/lib/html-report-advisories.mjs +2 -0
- package/bin/lib/html-report-depth.mjs +3 -1
- package/bin/lib/import-resolve.mjs +13 -1
- package/bin/lib/install-migrate.mjs +50 -73
- package/bin/lib/invariant-coverage.mjs +59 -11
- package/bin/lib/mcp-adoption.mjs +9 -1
- package/bin/lib/mcp-hook-payload.mjs +401 -61
- package/bin/lib/package-manager.mjs +59 -1
- package/bin/lib/passthrough-commands.mjs +83 -0
- package/bin/lib/persistence-write-hint.mjs +81 -0
- package/bin/lib/physical-cohesion.mjs +16 -18
- package/bin/lib/pilot-loop.mjs +21 -8
- package/bin/lib/pin-dependent-gates.mjs +117 -0
- package/bin/lib/policy-delta-io.mjs +168 -10
- package/bin/lib/prepare-change.mjs +120 -6
- package/bin/lib/prepare-write.mjs +1 -0
- package/bin/lib/presets.mjs +3 -5
- package/bin/lib/remediation.mjs +23 -4
- package/bin/lib/resolved-candidate-facts.mjs +315 -55
- package/bin/lib/rules-inventory.mjs +22 -3
- package/bin/lib/rules-under-contract.mjs +179 -5
- package/bin/lib/scan-files.mjs +32 -0
- package/bin/lib/snippet-analysis.mjs +140 -6
- package/bin/lib/source-policy.mjs +34 -0
- package/bin/lib/stable-hash.mjs +90 -0
- package/bin/lib/start-install-recovery.mjs +71 -1
- package/bin/lib/start-preview.mjs +30 -0
- package/bin/lib/status-command.mjs +33 -4
- package/bin/lib/status-manifest.mjs +32 -4
- package/bin/lib/team-parliament-io.mjs +79 -75
- package/bin/lib/team-parliament.mjs +6 -1
- package/bin/lib/write-path-capabilities.mjs +4 -2
- package/dist/chunk-A5EIY3HI.cjs +5 -0
- package/dist/chunk-MBXCFLO4.js +16 -0
- package/dist/chunk-RNQHRDCQ.cjs +16 -0
- package/dist/chunk-SVSQVGFJ.js +5 -0
- package/dist/{configTypes-CTELo87-.d.ts → configTypes-BJbaeoj4.d.cts} +53 -4
- package/dist/configTypes-BJbaeoj4.d.ts +246 -0
- package/dist/eslint/index.cjs +17 -4
- package/dist/eslint/index.d.cts +359 -0
- package/dist/eslint/index.d.ts +178 -158
- package/dist/eslint/index.js +10 -4
- package/dist/index.cjs +38 -37
- package/dist/index.d.cts +5475 -0
- package/dist/index.d.ts +3408 -296
- package/dist/index.js +38 -37
- package/dist/inspectorListen-6DQVJ4DC.js +1 -0
- package/dist/inspectorListen-KGMIQ2TF.cjs +1 -0
- package/dist/nestjs/index.cjs +1 -18
- package/dist/nestjs/index.d.cts +22 -0
- package/dist/nestjs/index.d.ts +1 -3
- package/dist/nestjs/index.js +1 -18
- package/dist/order/index.cjs +1 -1
- package/dist/order/index.d.cts +155 -0
- package/dist/order/index.d.ts +15 -3
- package/dist/order/index.js +1 -1
- package/dist/runtime/index.cjs +13 -30
- package/dist/{diagnosticCatalog-WD2TPSwZ.d.ts → runtime/index.d.cts} +814 -290
- package/dist/runtime/index.d.ts +2678 -108
- package/dist/runtime/index.js +13 -30
- package/dist/{types-yDNEQ603.d.ts → types-DRinG-vT.d.cts} +576 -140
- package/dist/types-DRinG-vT.d.ts +1792 -0
- package/docs/README.md +3 -1
- package/docs/agent-guide.md +92 -15
- package/docs/ai-gates.md +160 -26
- package/docs/arkorder.md +32 -15
- package/docs/brownfield-adoption.md +5 -1
- package/docs/configuration.md +273 -12
- package/docs/develop.md +2 -1
- package/docs/diagnostics.md +122 -13
- package/docs/package-surface.md +31 -8
- package/docs/product-voice.md +1 -1
- package/package.json +59 -18
- package/schemas/ark.analysis-result.schema.json +15 -0
- package/schemas/ark.config.schema.json +163 -4
- package/schemas/ark.resolved-candidate-facts.schema.json +1 -1
- package/schemas/ark.status-manifest.schema.json +22 -0
- package/server.json +3 -9
- package/templates/agent-skills/README.md +1 -1
- package/templates/agent-skills/ark-adopt/SKILL.md +4 -1
- package/templates/agent-skills/ark-order/SKILL.md +4 -2
- package/templates/agent-skills/ark-runtime/SKILL.md +6 -1
- package/templates/arkrules/PersistenceAdapters.json +1 -1
- package/templates/arkrules/PresentationAdapters.json +1 -1
- package/templates/skills/ark-adopt.md +4 -1
- package/templates/skills/ark-order.md +4 -2
- package/templates/skills/ark-runtime.md +6 -1
- package/dist/types-fQ9-KrBj.d.ts +0 -183
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { ArkOrderError } from './ark-order-error.mjs';
|
|
12
|
-
import { CAPACITY_OPS, DEFAULT_MAX_XI_KEYS } from './ark-order-types.mjs';
|
|
13
|
-
import { deterministicHash, stableSerialize } from './
|
|
12
|
+
import { CAPACITY_OPS, DEFAULT_MAX_XI_KEYS, XI_TTL_KEY_RE } from './ark-order-types.mjs';
|
|
13
|
+
import { deterministicHash, stableSerialize } from './stable-hash.mjs';
|
|
14
14
|
export { DEFAULT_MAX_XI_KEYS };
|
|
15
15
|
/** D7: consumer still owns handlers — this only names the travel verb. */
|
|
16
16
|
export function ingestTravelAction(residual) {
|
|
@@ -27,7 +27,7 @@ export function isForbiddenPlaneMethod(name) {
|
|
|
27
27
|
function isPrimitive(value) {
|
|
28
28
|
return (value === null ||
|
|
29
29
|
typeof value === 'string' ||
|
|
30
|
-
typeof value === 'number' ||
|
|
30
|
+
(typeof value === 'number' && Number.isFinite(value)) ||
|
|
31
31
|
typeof value === 'boolean');
|
|
32
32
|
}
|
|
33
33
|
export function freezeRecord(input, label) {
|
|
@@ -38,10 +38,14 @@ export function freezeRecord(input, label) {
|
|
|
38
38
|
const out = {};
|
|
39
39
|
for (const key of keys.sort()) {
|
|
40
40
|
const value = input[key];
|
|
41
|
+
if (typeof value === 'number' && !Number.isFinite(value)) {
|
|
42
|
+
// JSON (and so the hash) turns NaN/±Infinity into null: no stable identity.
|
|
43
|
+
throw new ArkOrderError('ARKORDER_NESTED_XI', `${label} key ${JSON.stringify(key)} is a non-finite number (NaN/±Infinity has no stable identity)`);
|
|
44
|
+
}
|
|
41
45
|
if (!isPrimitive(value)) {
|
|
42
46
|
throw new ArkOrderError('ARKORDER_NESTED_XI', `${label} key ${JSON.stringify(key)} is not a slow primitive (nested values are microstate)`);
|
|
43
47
|
}
|
|
44
|
-
out[key] = value;
|
|
48
|
+
out[key] = Object.is(value, -0) ? 0 : value;
|
|
45
49
|
}
|
|
46
50
|
return Object.freeze(out);
|
|
47
51
|
}
|
|
@@ -141,7 +145,6 @@ export function refreshSigmaRecord(input) {
|
|
|
141
145
|
releasedAt: input.now,
|
|
142
146
|
});
|
|
143
147
|
}
|
|
144
|
-
const XI_TTL_KEY_RE = /^(ttl|freshUntil|fresh_until|maxAge|max_age)$/i;
|
|
145
148
|
export function assertXiHasNoTtl(xi) {
|
|
146
149
|
for (const key of Object.keys(xi)) {
|
|
147
150
|
if (XI_TTL_KEY_RE.test(key)) {
|
|
@@ -159,9 +162,12 @@ export function assertInformationBudget(projection, budget) {
|
|
|
159
162
|
}
|
|
160
163
|
}
|
|
161
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* σ freshness. A numeric σ.freshUntil is honored on its own and wins over the age
|
|
167
|
+
* check; otherwise `maxAgeMs` (sigmaMaxAgeMs) bounds the age from σ.releasedAt or
|
|
168
|
+
* the Release freeze time. Neither set means σ never goes stale.
|
|
169
|
+
*/
|
|
162
170
|
export function assertSigmaFresh(input) {
|
|
163
|
-
if (input.maxAgeMs === undefined)
|
|
164
|
-
return;
|
|
165
171
|
const until = input.sigma.freshUntil;
|
|
166
172
|
if (typeof until === 'number') {
|
|
167
173
|
if (input.now > until) {
|
|
@@ -169,6 +175,8 @@ export function assertSigmaFresh(input) {
|
|
|
169
175
|
}
|
|
170
176
|
return;
|
|
171
177
|
}
|
|
178
|
+
if (input.maxAgeMs === undefined)
|
|
179
|
+
return;
|
|
172
180
|
const origin = typeof input.sigma.releasedAt === 'number' ? input.sigma.releasedAt : input.releasedAt;
|
|
173
181
|
if (typeof origin === 'number' && input.now - origin > input.maxAgeMs) {
|
|
174
182
|
throw new ArkOrderError('ARKORDER_STALE_SIGMA', 'σ is older than sigmaMaxAgeMs; ξ does not TTL');
|
|
@@ -312,6 +320,8 @@ export function proposePatternChange(input) {
|
|
|
312
320
|
}
|
|
313
321
|
const previous = input.projector(input.current, input.current.sigma);
|
|
314
322
|
const next = input.projector(candidate, candidate.sigma);
|
|
323
|
+
// A pattern the budget denies is never offered for review.
|
|
324
|
+
assertInformationBudget(next, input.informationBudget);
|
|
315
325
|
const { blastRadius, invalidations } = blastRadiusOf(previous, next);
|
|
316
326
|
if (blastRadius.length === 0) {
|
|
317
327
|
throw new ArkOrderError('ARKORDER_EMPTY_BLAST', 'pattern change has empty blast radius; that key is not an order parameter');
|
|
@@ -320,10 +330,37 @@ export function proposePatternChange(input) {
|
|
|
320
330
|
nextXi: candidate.xi,
|
|
321
331
|
blastRadius,
|
|
322
332
|
invalidations,
|
|
333
|
+
baseXiHash: input.current.xiHash,
|
|
334
|
+
baseVersion: input.current.version,
|
|
323
335
|
};
|
|
324
336
|
}
|
|
325
|
-
|
|
337
|
+
function sameSortedList(left, right) {
|
|
338
|
+
if (!Array.isArray(left) || left.length !== right.length)
|
|
339
|
+
return false;
|
|
340
|
+
const sorted = [...left].map(String).sort();
|
|
341
|
+
return sorted.every((item, index) => item === right[index]);
|
|
342
|
+
}
|
|
343
|
+
function staleProposal(detail) {
|
|
344
|
+
return new ArkOrderError('ARKORDER_STALE_PROPOSAL', `${detail}; re-run proposeRelease against the current Release and review the new blast radius`);
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* D1 valve: freeze ProposeResult.nextXi. The proposal must be bound to the current
|
|
348
|
+
* Release (baseXiHash + baseVersion) and its reviewed blastRadius / invalidations must
|
|
349
|
+
* equal the transition actually committed — otherwise ARKORDER_STALE_PROPOSAL.
|
|
350
|
+
* Empty blast still fails; a pattern the information budget denies never persists.
|
|
351
|
+
*/
|
|
326
352
|
export function applyProposedRelease(input) {
|
|
353
|
+
const proposal = input.proposal;
|
|
354
|
+
if (!proposal ||
|
|
355
|
+
typeof proposal !== 'object' ||
|
|
356
|
+
!proposal.nextXi ||
|
|
357
|
+
typeof proposal.nextXi !== 'object') {
|
|
358
|
+
throw staleProposal('apply() requires a ProposeResult from proposeRelease');
|
|
359
|
+
}
|
|
360
|
+
if (proposal.baseXiHash !== input.current.xiHash ||
|
|
361
|
+
proposal.baseVersion !== input.current.version) {
|
|
362
|
+
throw staleProposal(`proposal was computed against a different Release (base v${String(proposal.baseVersion)}, current v${input.current.version})`);
|
|
363
|
+
}
|
|
327
364
|
const candidate = createFrozenRelease({
|
|
328
365
|
xi: { ...input.proposal.nextXi },
|
|
329
366
|
sigma: { ...input.current.sigma },
|
|
@@ -338,9 +375,14 @@ export function applyProposedRelease(input) {
|
|
|
338
375
|
}
|
|
339
376
|
const previous = input.projector(input.current, input.current.sigma);
|
|
340
377
|
const next = input.projector(candidate, candidate.sigma);
|
|
341
|
-
|
|
378
|
+
assertInformationBudget(next, input.informationBudget);
|
|
379
|
+
const { blastRadius, invalidations } = blastRadiusOf(previous, next);
|
|
342
380
|
if (blastRadius.length === 0) {
|
|
343
381
|
throw new ArkOrderError('ARKORDER_EMPTY_BLAST', 'pattern change has empty blast radius; that key is not an order parameter');
|
|
344
382
|
}
|
|
383
|
+
if (!sameSortedList(proposal.blastRadius, blastRadius) ||
|
|
384
|
+
!sameSortedList(proposal.invalidations, invalidations)) {
|
|
385
|
+
throw staleProposal('the reviewed blast radius is not the transition apply() would commit');
|
|
386
|
+
}
|
|
345
387
|
return candidate;
|
|
346
388
|
}
|
|
@@ -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
|
|
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
|
-
|
|
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,8 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
export const DEFAULT_MAX_XI_KEYS = 7;
|
|
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;
|
|
12
14
|
export const INGEST_RESIDUAL_KINDS = ['absorb', 'escalate_up', 'hold'];
|
|
13
15
|
export const INGEST_REASON_CODES = ['not-in-pattern', 'stale-sigma', 'pack', 'capacity'];
|
|
14
16
|
export const CAPACITY_OPS = ['lte', 'lt', 'gte', 'gt'];
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { composeMergePlanesHonesty, extraMergeTeethAllowed, isArkRunRuleId, } 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,6 +48,12 @@ 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;
|
|
@@ -100,6 +107,7 @@ export function summarizeArkRunSection(input = {}) {
|
|
|
100
107
|
failMergeWhen: mergePlanes.failMergeWhen,
|
|
101
108
|
note,
|
|
102
109
|
mergePlanes,
|
|
110
|
+
layerFlowUnresolvable,
|
|
103
111
|
};
|
|
104
112
|
}
|
|
105
113
|
/** Thin status slice — counts only; residual null means unknown, not green. */
|
|
@@ -144,6 +152,12 @@ export function formatArkRunDoctorLines(section) {
|
|
|
144
152
|
else {
|
|
145
153
|
lines.push('Residual: none on this scan (not a score — green extras ≠ finished kernel wiring).');
|
|
146
154
|
}
|
|
155
|
+
const unresolvable = Array.isArray(section.layerFlowUnresolvable)
|
|
156
|
+
? section.layerFlowUnresolvable
|
|
157
|
+
: [];
|
|
158
|
+
if (unresolvable.length > 0) {
|
|
159
|
+
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).`);
|
|
160
|
+
}
|
|
147
161
|
if (section.failMergeWhen)
|
|
148
162
|
lines.push(section.failMergeWhen);
|
|
149
163
|
return lines;
|