arkgate 4.8.24 → 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.
- package/CHANGELOG.md +85 -0
- package/README.md +2 -2
- package/bin/ark-check-runtime.mjs +17 -14
- package/bin/ark-layer-match.mjs +115 -13
- package/bin/ark-mcp-runtime.mjs +36 -31
- package/bin/ark-shared.mjs +4 -23
- package/bin/lib/adoption-stance.mjs +2 -2
- package/bin/lib/adr-path.mjs +3 -3
- package/bin/lib/agent-gates.mjs +0 -61
- package/bin/lib/agent-homes.mjs +12 -21
- package/bin/lib/agent-projection-command.mjs +4 -4
- package/bin/lib/ambient-state.mjs +9 -30
- package/bin/lib/analysis-engine.mjs +14 -14
- package/bin/lib/architecture-scan.mjs +11 -9
- package/bin/lib/ark-order-doctor.mjs +3 -28
- package/bin/lib/ark-order-facts.mjs +6 -99
- package/bin/lib/ark-order-types.mjs +1 -2
- package/bin/lib/ark-run-doctor.mjs +3 -28
- package/bin/lib/ark-run-facts.mjs +30 -40
- package/bin/lib/ark-run-sensors.mjs +1 -1
- package/bin/lib/arkrule-file-hints.mjs +2 -47
- package/bin/lib/arkrules-contract.mjs +124 -133
- package/bin/lib/arkrules-sensors.mjs +7 -5
- package/bin/lib/check-args.mjs +63 -1
- package/bin/lib/ci-and-commands.mjs +8 -72
- package/bin/lib/ci-merge-boundary.mjs +1 -1
- package/bin/lib/class-source-scan.mjs +5 -5
- package/bin/lib/clone-detection.mjs +474 -0
- package/bin/lib/codex-home.mjs +1 -1
- package/bin/lib/config-contract-slices.mjs +44 -6
- package/bin/lib/config-contract.mjs +40 -117
- package/bin/lib/config-extras.mjs +16 -22
- package/bin/lib/contract-smells.mjs +33 -15
- package/bin/lib/core-ratchet.mjs +0 -2
- package/bin/lib/dashboard-cli.mjs +2 -2
- package/bin/lib/deep-module-coach.mjs +2 -4
- package/bin/lib/deploy-path.mjs +13 -38
- package/bin/lib/design-smells.mjs +2 -2
- package/bin/lib/diagnostic-catalog.mjs +8 -1
- package/bin/lib/doctor-advisories.mjs +55 -9
- package/bin/lib/doctor-green-cite.mjs +1 -1
- package/bin/lib/doctor-human.mjs +1 -1
- package/bin/lib/doctor-next-actions.mjs +2 -2
- package/bin/lib/doctor-plan.mjs +4 -6
- package/bin/lib/duplication-io.mjs +572 -0
- package/bin/lib/effective-contract-load.mjs +181 -44
- package/bin/lib/entry-points-io.mjs +424 -0
- package/bin/lib/extra-merge-teeth.mjs +36 -0
- package/bin/lib/field-install.mjs +38 -14
- package/bin/lib/first-run-help.mjs +7 -3
- package/bin/lib/flat-parent-importers.mjs +5 -3
- package/bin/lib/gate-files.mjs +30 -89
- package/bin/lib/github-enforcement.mjs +165 -28
- package/bin/lib/golden-pattern.mjs +1 -1
- package/bin/lib/hook-templates.mjs +16 -16
- package/bin/lib/html-report-advisories.mjs +4 -0
- package/bin/lib/html-report-depth.mjs +2 -2
- package/bin/lib/html-report.mjs +2 -2
- package/bin/lib/import-graph-projection.mjs +112 -0
- package/bin/lib/improvement-compass-doctor.mjs +1 -24
- package/bin/lib/invariant-coverage-io.mjs +4 -3
- package/bin/lib/invariant-coverage.mjs +26 -0
- package/bin/lib/invariant-probe-cli.mjs +347 -0
- package/bin/lib/invariant-probe-io.mjs +165 -0
- package/bin/lib/invariant-probe-runner.mjs +268 -0
- package/bin/lib/invariant-probe-sites.mjs +174 -0
- package/bin/lib/invariant-probe-workspace.mjs +183 -0
- package/bin/lib/invariant-probe.mjs +461 -0
- package/bin/lib/invariant-tests-path.mjs +16 -22
- package/bin/lib/literal-path-drift.mjs +4 -2
- package/bin/lib/managed-upgrade.mjs +2 -8
- package/bin/lib/mcp-adoption.mjs +4 -5
- package/bin/lib/mcp-hook-payload.mjs +4 -4
- package/bin/lib/orphan-modules-io.mjs +458 -0
- package/bin/lib/orphan-modules.mjs +251 -0
- package/bin/lib/outside-importers.mjs +197 -0
- package/bin/lib/package-manager.mjs +3 -3
- package/bin/lib/parse-health.mjs +1 -1
- package/bin/lib/persistence-write-hint.mjs +33 -8
- package/bin/lib/pin-dependent-gates.mjs +2 -2
- package/bin/lib/policy-delta-io.mjs +55 -8
- package/bin/lib/post-green-path.mjs +1 -1
- package/bin/lib/prepare-change.mjs +3 -9
- package/bin/lib/prepare-write.mjs +1 -2
- package/bin/lib/presets.mjs +56 -68
- package/bin/lib/product-copy.mjs +1 -6
- package/bin/lib/project-root.mjs +31 -62
- package/bin/lib/resolved-candidate-facts.mjs +90 -62
- package/bin/lib/rules-under-contract.mjs +72 -8
- package/bin/lib/scan-files.mjs +4 -4
- package/bin/lib/schema-validation.mjs +130 -0
- package/bin/lib/sensor-promote-cli.mjs +2 -11
- package/bin/lib/sensor-promote-io.mjs +33 -22
- package/bin/lib/sensor-promotion.mjs +1 -1
- package/bin/lib/skill-catalog-apply.mjs +1 -1
- package/bin/lib/skill-install.mjs +42 -38
- package/bin/lib/snippet-analysis.mjs +1 -1
- package/bin/lib/start-install-recovery.mjs +1 -1
- package/bin/lib/start-preview.mjs +1 -1
- package/bin/lib/states-transitions-presence.mjs +3 -3
- package/bin/lib/status-command.mjs +2 -18
- package/bin/lib/status-manifest.mjs +18 -26
- package/bin/lib/status-transition-catalog.mjs +0 -6
- package/bin/lib/suggestions.mjs +3 -3
- package/bin/lib/team-parliament-io.mjs +3 -17
- package/bin/lib/team-parliament.mjs +15 -9
- package/bin/lib/upgrade-behavior-changes.mjs +2 -2
- package/bin/lib/upgrade-command.mjs +2 -24
- package/bin/lib/upgrade-outcome.mjs +1 -1
- package/bin/lib/upstream-report.mjs +21 -10
- package/bin/lib/violations.mjs +10 -9
- package/bin/lib/weakest-link.mjs +13 -0
- package/bin/lib/write-path-capabilities.mjs +2 -2
- package/dist/chunk-AHACEKSV.js +16 -0
- package/dist/chunk-UN5EH4QV.cjs +16 -0
- package/dist/{configTypes-BJbaeoj4.d.cts → configTypes-GPvvdleM.d.cts} +12 -1
- package/dist/{configTypes-BJbaeoj4.d.ts → configTypes-GPvvdleM.d.ts} +12 -1
- package/dist/eslint/index.cjs +11 -11
- package/dist/eslint/index.d.cts +10 -1
- package/dist/eslint/index.d.ts +10 -1
- package/dist/eslint/index.js +11 -11
- package/dist/index.cjs +34 -34
- package/dist/index.d.cts +587 -515
- package/dist/index.d.ts +587 -515
- package/dist/index.js +34 -34
- package/dist/nestjs/index.cjs +1 -1
- package/dist/nestjs/index.d.cts +1 -1
- package/dist/nestjs/index.d.ts +1 -1
- package/dist/nestjs/index.js +1 -1
- package/dist/order/index.cjs +1 -1
- package/dist/order/index.js +1 -1
- package/dist/runtime/index.cjs +12 -12
- package/dist/runtime/index.d.cts +174 -136
- package/dist/runtime/index.d.ts +174 -136
- package/dist/runtime/index.js +12 -12
- package/dist/{types-DRinG-vT.d.cts → types-CnauMQQL.d.cts} +33 -25
- package/dist/{types-DRinG-vT.d.ts → types-CnauMQQL.d.ts} +33 -25
- package/docs/README.md +2 -1
- package/docs/agent-guide.md +134 -2
- package/docs/ai-gates.md +15 -1
- package/docs/configuration.md +40 -11
- package/docs/diagnostics.md +118 -2
- package/docs/package-surface.md +4 -1
- package/docs/product-voice.md +7 -1
- package/package.json +11 -2
- package/schemas/ark.config.schema.json +31 -3
- package/schemas/ark.invariant-probe.schema.json +368 -0
- package/server.json +2 -2
- package/templates/agent-skills/README.md +1 -1
- package/templates/agent-skills/ark-adopt/SKILL.md +1 -0
- package/templates/agent-skills/ark-contract/SKILL.md +17 -0
- package/templates/agent-skills/ark-coverage/SKILL.md +2 -0
- package/templates/agent-skills/ark-explore/SKILL.md +7 -0
- package/templates/agent-skills/ark-fix/SKILL.md +20 -0
- package/templates/agent-skills/ark-place/SKILL.md +8 -0
- package/templates/skills/ark-adopt.md +1 -0
- package/templates/skills/ark-contract.md +17 -0
- package/templates/skills/ark-coverage.md +2 -0
- package/templates/skills/ark-explore.md +7 -0
- package/templates/skills/ark-fix.md +20 -0
- package/templates/skills/ark-place.md +8 -0
- package/bin/lib/ark-order-error.mjs +0 -18
- package/bin/lib/ark-order-invariants.mjs +0 -388
- package/bin/lib/stable-hash.mjs +0 -90
- package/dist/chunk-MBXCFLO4.js +0 -16
- package/dist/chunk-RNQHRDCQ.cjs +0 -16
|
@@ -16,6 +16,7 @@ import { effectiveAnalysisConfig } from './analysis-policy.mjs';
|
|
|
16
16
|
import { resolveCandidateFacts } from './resolved-candidate-facts.mjs';
|
|
17
17
|
import {
|
|
18
18
|
arkRulesDriftWarnings,
|
|
19
|
+
arkRulesLoadFailed,
|
|
19
20
|
loadEffectiveArkRulesFromDisk,
|
|
20
21
|
} from './effective-contract-load.mjs';
|
|
21
22
|
import {
|
|
@@ -31,6 +32,7 @@ import {
|
|
|
31
32
|
} from './invariant-tests-path.mjs';
|
|
32
33
|
import { loadArkRuleFileHints } from './arkrule-file-hints.mjs';
|
|
33
34
|
import { collectGovernedFiles } from './scan-files.mjs';
|
|
35
|
+
import { projectImporterIndex } from './import-graph-projection.mjs';
|
|
34
36
|
|
|
35
37
|
const HINT_CACHE_CAP = 16;
|
|
36
38
|
/** Process-local hint map keyed by scoped path + content hash. Not a second engine. */
|
|
@@ -206,13 +208,7 @@ export function resolveArchitectureSnapshot({
|
|
|
206
208
|
observeInput,
|
|
207
209
|
});
|
|
208
210
|
if (arkRulesLoad.errors.length > 0) {
|
|
209
|
-
|
|
210
|
-
.map((issue) => `- ${issue.path}: ${issue.message}`)
|
|
211
|
-
.join('\n');
|
|
212
|
-
const err = new Error(`Invalid Effective Contract (${configPath}):\n${message}`);
|
|
213
|
-
err.code = 'ARKRULES_LOAD_FAILED';
|
|
214
|
-
err.issues = arkRulesLoad.errors;
|
|
215
|
-
throw err;
|
|
211
|
+
throw arkRulesLoadFailed(configPath, arkRulesLoad.errors);
|
|
216
212
|
}
|
|
217
213
|
const loadedContract = loadContract(effectiveConfig, configPath, {
|
|
218
214
|
arkRules: arkRulesLoad.arkRules,
|
|
@@ -296,8 +292,14 @@ export function resolveArchitectureSnapshot({
|
|
|
296
292
|
|
|
297
293
|
/**
|
|
298
294
|
* Full architecture scan for governed files.
|
|
299
|
-
*
|
|
295
|
+
*
|
|
296
|
+
* `graphProjection: true` (doctor / report) also returns `importGraph`, a compact
|
|
297
|
+
* importer index projected from the resolved facts before they are released
|
|
298
|
+
* (ADR 0037 D1). The verdict fields are the same object either way.
|
|
299
|
+
* @returns {{ violations: object[], warnings: object[], importGraph?: object }}
|
|
300
300
|
*/
|
|
301
301
|
export function runArchitectureScan(options) {
|
|
302
|
-
|
|
302
|
+
const snapshot = resolveArchitectureSnapshot({ ...options, captureInputs: false });
|
|
303
|
+
if (options?.graphProjection !== true) return snapshot.result;
|
|
304
|
+
return { ...snapshot.result, importGraph: projectImporterIndex(snapshot.facts) };
|
|
303
305
|
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Pure CLI helper (bin/lib/ark-order-doctor.mjs). Zero Node I/O.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { composeMergePlanesHonesty, extraMergeTeethAllowed, isArkOrderRuleId, } from './extra-merge-teeth.mjs';
|
|
11
|
+
import { composeMergePlanesHonesty, extraMergeTeethAllowed, isArkOrderRuleId, mergePlanesArkRulesInput, projectExtraPlaneStatus, } from './extra-merge-teeth.mjs';
|
|
12
12
|
export const ARK_ORDER_DOCTOR_SCHEMA_VERSION = '1.0';
|
|
13
13
|
const RESIDUAL_RULE_CAP = 12;
|
|
14
14
|
export const ARKORDER_ONE_BREATH = 'Layers stop a bad import. ArkOrder stops rewriting a big product choice — like the billing plan — as if it were a seat count. Change those choices through a valve, not a generic update.';
|
|
@@ -54,17 +54,7 @@ export function summarizeArkOrderSection(input = {}) {
|
|
|
54
54
|
const residualCount = uniqueIds.length;
|
|
55
55
|
const mergePlanes = composeMergePlanesHonesty({
|
|
56
56
|
classification: input.classification,
|
|
57
|
-
arkRules:
|
|
58
|
-
active: input.arkRules?.active === true,
|
|
59
|
-
structureEnforced: input.arkRules?.structureEnforced,
|
|
60
|
-
structureTotal: input.arkRules?.structureTotal,
|
|
61
|
-
structureAdvisory: input.arkRules?.structureAdvisory,
|
|
62
|
-
invariantEnforced: input.arkRules?.invariantEnforced,
|
|
63
|
-
invariantTotal: input.arkRules?.invariantTotal,
|
|
64
|
-
invariantAdvisory: input.arkRules?.invariantAdvisory,
|
|
65
|
-
covered: input.arkRules?.covered,
|
|
66
|
-
uncovered: input.arkRules?.uncovered,
|
|
67
|
-
},
|
|
57
|
+
arkRules: mergePlanesArkRulesInput(input.arkRules),
|
|
68
58
|
arkRun: {
|
|
69
59
|
present: input.arkRun?.present === true,
|
|
70
60
|
mode: input.arkRun?.mode ?? null,
|
|
@@ -110,22 +100,7 @@ export function summarizeArkOrderSection(input = {}) {
|
|
|
110
100
|
}
|
|
111
101
|
/** Thin status slice — counts only; residual null means unknown, not green. */
|
|
112
102
|
export function projectStatusArkOrder(input = {}) {
|
|
113
|
-
|
|
114
|
-
const mode = closedMode(input.mode);
|
|
115
|
-
const residualRaw = input.residual;
|
|
116
|
-
let residual = null;
|
|
117
|
-
if (typeof residualRaw === 'number' && Number.isFinite(residualRaw) && residualRaw >= 0) {
|
|
118
|
-
residual = Math.floor(residualRaw);
|
|
119
|
-
}
|
|
120
|
-
if (!present)
|
|
121
|
-
residual = residual == null ? 0 : residual;
|
|
122
|
-
return {
|
|
123
|
-
notAScore: true,
|
|
124
|
-
present,
|
|
125
|
-
mode: present ? mode : null,
|
|
126
|
-
extraMergeTeeth: present && mode === 'enforced' && input.extraMergeTeeth === true,
|
|
127
|
-
residual,
|
|
128
|
-
};
|
|
103
|
+
return projectExtraPlaneStatus(input);
|
|
129
104
|
}
|
|
130
105
|
export function formatArkOrderDoctorLines(section) {
|
|
131
106
|
if (!section || section.notAScore !== true)
|
|
@@ -9,105 +9,12 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { XI_TTL_KEY_RE } from './ark-order-types.mjs';
|
|
12
|
+
import { globToRegExp } from '../ark-layer-match.mjs';
|
|
12
13
|
import { sourceHasPersistenceWrite, sourceImportsPersistenceDriverText } from './persistence-write-hint.mjs';
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* XIWRITE-001: same engine as `globToRegExp` in src/domain/layerMatch.ts.
|
|
17
|
-
* Inlined so generate:cli-pure emits self-contained bin/lib/ark-order-facts.mjs /
|
|
18
|
-
* ark-order-sensors.mjs
|
|
19
|
-
* (layerMatch is derived to bin/ark-layer-match.mjs, not a bin/lib sibling).
|
|
20
|
-
*/
|
|
21
|
-
const appliesToRegexpCache = new Map();
|
|
22
|
-
function escapeAppliesToLiteral(ch) {
|
|
23
|
-
return /[.*+?^${}()|[\]\\]/.test(ch) ? `\\${ch}` : ch;
|
|
24
|
-
}
|
|
25
|
-
function normalizeAppliesToGlob(pattern) {
|
|
26
|
-
let out = '';
|
|
27
|
-
for (let i = 0; i < pattern.length; i += 1) {
|
|
28
|
-
const c = pattern[i];
|
|
29
|
-
if (c === '\\' && i + 1 < pattern.length) {
|
|
30
|
-
const next = pattern[i + 1];
|
|
31
|
-
if ('*?{}[],'.includes(next) || next === '\\') {
|
|
32
|
-
out += '\\' + next;
|
|
33
|
-
i += 1;
|
|
34
|
-
continue;
|
|
35
|
-
}
|
|
36
|
-
out += '/';
|
|
37
|
-
continue;
|
|
38
|
-
}
|
|
39
|
-
out += c;
|
|
40
|
-
}
|
|
41
|
-
return out;
|
|
42
|
-
}
|
|
43
|
-
function appliesToBracesBalanced(glob) {
|
|
44
|
-
let depth = 0;
|
|
45
|
-
for (let i = 0; i < glob.length; i += 1) {
|
|
46
|
-
const c = glob[i];
|
|
47
|
-
if (c === '\\') {
|
|
48
|
-
i += 1;
|
|
49
|
-
continue;
|
|
50
|
-
}
|
|
51
|
-
if (c === '{')
|
|
52
|
-
depth += 1;
|
|
53
|
-
else if (c === '}') {
|
|
54
|
-
depth -= 1;
|
|
55
|
-
if (depth < 0)
|
|
56
|
-
return false;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
return depth === 0;
|
|
60
|
-
}
|
|
14
|
+
const ARKORDER_PLANE_FACTORY = 'createOrderPlane';
|
|
15
|
+
/** XIWRITE-001 `appliesTo` globs use the layer-glob engine (`globToRegExp`). */
|
|
61
16
|
export function arkOrderGlobToRegExp(pattern) {
|
|
62
|
-
|
|
63
|
-
if (cached)
|
|
64
|
-
return cached;
|
|
65
|
-
const glob = normalizeAppliesToGlob(pattern);
|
|
66
|
-
const useBraces = appliesToBracesBalanced(glob);
|
|
67
|
-
let out = '';
|
|
68
|
-
let braceDepth = 0;
|
|
69
|
-
for (let i = 0; i < glob.length; i += 1) {
|
|
70
|
-
const c = glob[i];
|
|
71
|
-
if (c === '\\' && i + 1 < glob.length) {
|
|
72
|
-
out += escapeAppliesToLiteral(glob[i + 1]);
|
|
73
|
-
i += 1;
|
|
74
|
-
}
|
|
75
|
-
else if (c === '*') {
|
|
76
|
-
if (glob[i + 1] === '*') {
|
|
77
|
-
if (glob[i + 2] === '/') {
|
|
78
|
-
out += '(?:.*/)?';
|
|
79
|
-
i += 2;
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
82
|
-
out += '.*';
|
|
83
|
-
i += 1;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
else {
|
|
87
|
-
out += '[^/]*';
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
else if (c === '?') {
|
|
91
|
-
out += '[^/]';
|
|
92
|
-
}
|
|
93
|
-
else if (c === '{' && useBraces) {
|
|
94
|
-
out += '(?:';
|
|
95
|
-
braceDepth += 1;
|
|
96
|
-
}
|
|
97
|
-
else if (c === '}' && useBraces && braceDepth > 0) {
|
|
98
|
-
out += ')';
|
|
99
|
-
braceDepth -= 1;
|
|
100
|
-
}
|
|
101
|
-
else if (c === ',' && useBraces && braceDepth > 0) {
|
|
102
|
-
out += '|';
|
|
103
|
-
}
|
|
104
|
-
else {
|
|
105
|
-
out += escapeAppliesToLiteral(c);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
const re = new RegExp(`^${out}$`);
|
|
109
|
-
appliesToRegexpCache.set(pattern, re);
|
|
110
|
-
return re;
|
|
17
|
+
return globToRegExp(pattern);
|
|
111
18
|
}
|
|
112
19
|
function escapeRegExp(value) {
|
|
113
20
|
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
@@ -195,7 +102,7 @@ const TRAILING_IDENTIFIER_RE = /([A-Za-z_$][\w$]*)\s*!?\s*\??\s*$/;
|
|
|
195
102
|
/** Leading identifier inside a parenthesized receiver: `x as T`, `<T>x`, `x!`, `x`. */
|
|
196
103
|
const WRAPPED_IDENTIFIER_RE = /^\s*(?:<[^<>]*>\s*)?([A-Za-z_$][\w$]*)\s*!?\s*(?:\b(?:as|satisfies)\b[\s\S]*)?$/;
|
|
197
104
|
/** Identifiers bound to `createOrderPlane(...)` in this file. */
|
|
198
|
-
|
|
105
|
+
function arkOrderPlaneBindings(content) {
|
|
199
106
|
const source = stripCommentsPreservingLines(content);
|
|
200
107
|
const names = new Set();
|
|
201
108
|
const re = new RegExp(PLANE_BINDING_RE.source, 'g');
|
|
@@ -260,7 +167,7 @@ function specifierResolvesToPlaneRoot(file, specifier, planeRoots) {
|
|
|
260
167
|
* named import from a declared plane root. `isArkOrderFile` reports an `arkgate/order`
|
|
261
168
|
* import, which admits the conventional names file-wide.
|
|
262
169
|
*/
|
|
263
|
-
|
|
170
|
+
function arkOrderPlaneReceivers(file, content, options = {}) {
|
|
264
171
|
const source = stripCommentsPreservingLines(content);
|
|
265
172
|
const names = new Set(arkOrderPlaneBindings(content));
|
|
266
173
|
const annotated = new RegExp(ORDER_PLANE_ANNOTATION_RE.source, 'g');
|
|
@@ -11,6 +11,5 @@
|
|
|
11
11
|
export const DEFAULT_MAX_XI_KEYS = 7;
|
|
12
12
|
/** ξ keys that name freshness. Freshness is σ, never ξ (runtime and static sensor share this). */
|
|
13
13
|
export const XI_TTL_KEY_RE = /^(ttl|freshUntil|fresh_until|maxAge|max_age)$/i;
|
|
14
|
-
|
|
15
|
-
export const INGEST_REASON_CODES = ['not-in-pattern', 'stale-sigma', 'pack', 'capacity'];
|
|
14
|
+
const INGEST_REASON_CODES = ['not-in-pattern', 'stale-sigma', 'pack', 'capacity'];
|
|
16
15
|
export const CAPACITY_OPS = ['lte', 'lt', 'gte', 'gt'];
|
|
@@ -8,7 +8,7 @@
|
|
|
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
12
|
import { unresolvableLayerFlowLayers } from './source-policy.mjs';
|
|
13
13
|
export const ARK_RUN_DOCTOR_SCHEMA_VERSION = '1.0';
|
|
14
14
|
const RESIDUAL_RULE_CAP = 12;
|
|
@@ -59,17 +59,7 @@ export function summarizeArkRunSection(input = {}) {
|
|
|
59
59
|
const residualCount = uniqueIds.length;
|
|
60
60
|
const mergePlanes = composeMergePlanesHonesty({
|
|
61
61
|
classification: input.classification,
|
|
62
|
-
arkRules:
|
|
63
|
-
active: input.arkRules?.active === true,
|
|
64
|
-
structureEnforced: input.arkRules?.structureEnforced,
|
|
65
|
-
structureTotal: input.arkRules?.structureTotal,
|
|
66
|
-
structureAdvisory: input.arkRules?.structureAdvisory,
|
|
67
|
-
invariantEnforced: input.arkRules?.invariantEnforced,
|
|
68
|
-
invariantTotal: input.arkRules?.invariantTotal,
|
|
69
|
-
invariantAdvisory: input.arkRules?.invariantAdvisory,
|
|
70
|
-
covered: input.arkRules?.covered,
|
|
71
|
-
uncovered: input.arkRules?.uncovered,
|
|
72
|
-
},
|
|
62
|
+
arkRules: mergePlanesArkRulesInput(input.arkRules),
|
|
73
63
|
arkRun: {
|
|
74
64
|
present: extra.present,
|
|
75
65
|
mode: extra.mode,
|
|
@@ -112,22 +102,7 @@ export function summarizeArkRunSection(input = {}) {
|
|
|
112
102
|
}
|
|
113
103
|
/** Thin status slice — counts only; residual null means unknown, not green. */
|
|
114
104
|
export function projectStatusArkRun(input = {}) {
|
|
115
|
-
|
|
116
|
-
const mode = closedMode(input.mode);
|
|
117
|
-
const residualRaw = input.residual;
|
|
118
|
-
let residual = null;
|
|
119
|
-
if (typeof residualRaw === 'number' && Number.isFinite(residualRaw) && residualRaw >= 0) {
|
|
120
|
-
residual = Math.floor(residualRaw);
|
|
121
|
-
}
|
|
122
|
-
if (!present)
|
|
123
|
-
residual = residual == null ? 0 : residual;
|
|
124
|
-
return {
|
|
125
|
-
notAScore: true,
|
|
126
|
-
present,
|
|
127
|
-
mode: present ? mode : null,
|
|
128
|
-
extraMergeTeeth: present && mode === 'enforced' && input.extraMergeTeeth === true,
|
|
129
|
-
residual,
|
|
130
|
-
};
|
|
105
|
+
return projectExtraPlaneStatus(input);
|
|
131
106
|
}
|
|
132
107
|
export function formatArkRunDoctorLines(section) {
|
|
133
108
|
if (!section || section.notAScore !== true)
|
|
@@ -23,14 +23,14 @@ export const ARKRUN_KERNEL_FACTORY_CALLEES = [
|
|
|
23
23
|
* count as a kernel factory only when `ArkModule` is imported from a kernel module
|
|
24
24
|
* (`arkgate/nestjs`). A bare `.forRoot(` on any other receiver never counts.
|
|
25
25
|
*/
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
const ARKRUN_NEST_KERNEL_MODULE = 'ArkModule';
|
|
27
|
+
const ARKRUN_NEST_FACTORY_METHODS = ['forRoot', 'forRootAsync'];
|
|
28
28
|
/**
|
|
29
29
|
* Kernel types whose annotated bindings (`ark: ArkKernel`, constructor-injected
|
|
30
30
|
* `private readonly ark: ArkKernel`) are traced as kernel receivers when the type
|
|
31
31
|
* is imported from a kernel module.
|
|
32
32
|
*/
|
|
33
|
-
|
|
33
|
+
const ARKRUN_KERNEL_RECEIVER_TYPES = [
|
|
34
34
|
'ArkKernel',
|
|
35
35
|
'EventBus',
|
|
36
36
|
'EventPublisher',
|
|
@@ -310,21 +310,33 @@ export function extractArkRunValueImportDependenciesFromSource(file, content) {
|
|
|
310
310
|
}
|
|
311
311
|
return out;
|
|
312
312
|
}
|
|
313
|
+
/**
|
|
314
|
+
* Value bindings of a braced import list (`{ A, B as C }`): local name plus the
|
|
315
|
+
* imported (original) name. Inline `type` specifiers are skipped.
|
|
316
|
+
*/
|
|
317
|
+
function bracedValueImportBindings(clause) {
|
|
318
|
+
const braced = /\{([^}]*)\}/.exec(clause);
|
|
319
|
+
if (!braced?.[1])
|
|
320
|
+
return [];
|
|
321
|
+
const out = [];
|
|
322
|
+
for (const part of braced[1].split(',')) {
|
|
323
|
+
const piece = part.trim();
|
|
324
|
+
if (!piece || piece.startsWith('type '))
|
|
325
|
+
continue;
|
|
326
|
+
const alias = /^([A-Za-z_][A-Za-z0-9_]*)\s+as\s+([A-Za-z_][A-Za-z0-9_]*)$/.exec(piece);
|
|
327
|
+
const local = alias?.[2] ?? /^([A-Za-z_][A-Za-z0-9_]*)$/.exec(piece)?.[1];
|
|
328
|
+
const original = alias?.[1] ?? local;
|
|
329
|
+
if (local && original)
|
|
330
|
+
out.push({ local, original });
|
|
331
|
+
}
|
|
332
|
+
return out;
|
|
333
|
+
}
|
|
313
334
|
/** PascalCase named bindings from value import clauses (snippet admitted constructors). */
|
|
314
335
|
export function extractArkRunImportedConstructorNamesFromSource(content) {
|
|
315
336
|
const names = [];
|
|
316
337
|
forEachArkRunValueImportClause(content, (clause) => {
|
|
317
|
-
const
|
|
318
|
-
|
|
319
|
-
return;
|
|
320
|
-
for (const part of braced[1].split(',')) {
|
|
321
|
-
const piece = part.trim();
|
|
322
|
-
if (!piece || piece.startsWith('type '))
|
|
323
|
-
continue;
|
|
324
|
-
const alias = /^([A-Za-z_][A-Za-z0-9_]*)\s+as\s+([A-Za-z_][A-Za-z0-9_]*)$/.exec(piece);
|
|
325
|
-
const local = alias?.[2] ?? /^([A-Za-z_][A-Za-z0-9_]*)$/.exec(piece)?.[1];
|
|
326
|
-
const original = alias?.[1] ?? local;
|
|
327
|
-
if (local && original && /^[A-Z]/.test(original))
|
|
338
|
+
for (const { local, original } of bracedValueImportBindings(clause)) {
|
|
339
|
+
if (/^[A-Z]/.test(original))
|
|
328
340
|
names.push(local, original);
|
|
329
341
|
}
|
|
330
342
|
});
|
|
@@ -342,21 +354,8 @@ function collectKernelImportBindings(content) {
|
|
|
342
354
|
const defaultIdent = /^([A-Za-z_][A-Za-z0-9_]*)\s*(?:,|$)/.exec(clause.trim());
|
|
343
355
|
if (defaultIdent?.[1])
|
|
344
356
|
named.set(defaultIdent[1], defaultIdent[1]);
|
|
345
|
-
const
|
|
346
|
-
|
|
347
|
-
return;
|
|
348
|
-
for (const part of braced[1].split(',')) {
|
|
349
|
-
const piece = part.trim();
|
|
350
|
-
if (!piece || piece.startsWith('type '))
|
|
351
|
-
continue;
|
|
352
|
-
const alias = /^([A-Za-z_][A-Za-z0-9_]*)\s+as\s+([A-Za-z_][A-Za-z0-9_]*)$/.exec(piece);
|
|
353
|
-
if (alias) {
|
|
354
|
-
named.set(alias[2], alias[1]);
|
|
355
|
-
continue;
|
|
356
|
-
}
|
|
357
|
-
const ident = /^([A-Za-z_][A-Za-z0-9_]*)$/.exec(piece);
|
|
358
|
-
if (ident?.[1])
|
|
359
|
-
named.set(ident[1], ident[1]);
|
|
357
|
+
for (const { local, original } of bracedValueImportBindings(clause)) {
|
|
358
|
+
named.set(local, original);
|
|
360
359
|
}
|
|
361
360
|
});
|
|
362
361
|
return { named, namespaces };
|
|
@@ -364,17 +363,8 @@ function collectKernelImportBindings(content) {
|
|
|
364
363
|
function collectImportedConstructors(content, admitted) {
|
|
365
364
|
const out = new Set(admitted);
|
|
366
365
|
parseValueImportClause(content, (clause, specifier) => {
|
|
367
|
-
const
|
|
368
|
-
|
|
369
|
-
return;
|
|
370
|
-
for (const part of braced[1].split(',')) {
|
|
371
|
-
const piece = part.trim();
|
|
372
|
-
if (!piece || piece.startsWith('type '))
|
|
373
|
-
continue;
|
|
374
|
-
const alias = /^([A-Za-z_][A-Za-z0-9_]*)\s+as\s+([A-Za-z_][A-Za-z0-9_]*)$/.exec(piece);
|
|
375
|
-
const local = alias?.[2] ?? /^([A-Za-z_][A-Za-z0-9_]*)$/.exec(piece)?.[1];
|
|
376
|
-
const original = alias?.[1] ?? local;
|
|
377
|
-
if (!local || !original || !/^[A-Z]/.test(original))
|
|
366
|
+
for (const { local, original } of bracedValueImportBindings(clause)) {
|
|
367
|
+
if (!/^[A-Z]/.test(original))
|
|
378
368
|
continue;
|
|
379
369
|
if (isArkRunKernelModuleSpecifier(specifier) || admitted.has(original) || admitted.has(local)) {
|
|
380
370
|
out.add(local);
|
|
@@ -30,7 +30,7 @@ export const ARKRUN_EDITOR_SENSOR_IDS = [
|
|
|
30
30
|
'arkrun-transport-bypass',
|
|
31
31
|
];
|
|
32
32
|
const EDITOR_SENSOR_SET = new Set(ARKRUN_EDITOR_SENSOR_IDS);
|
|
33
|
-
|
|
33
|
+
function isArkRunEditorSensor(sensor) {
|
|
34
34
|
return EDITOR_SENSOR_SET.has(sensor);
|
|
35
35
|
}
|
|
36
36
|
export const ARKRUN_RULE_IDS = {
|
|
@@ -11,11 +11,12 @@
|
|
|
11
11
|
import fs from 'node:fs';
|
|
12
12
|
import path from 'node:path';
|
|
13
13
|
import { buildArkRuleFileHints } from './arkrules-sensors.mjs';
|
|
14
|
+
import { matchSimpleGlob } from './invariant-coverage-io.mjs';
|
|
14
15
|
|
|
15
16
|
/** Default hint-file budget. Same lever as `coverage.maxFiles`. */
|
|
16
17
|
export const DEFAULT_MAX_HINT_FILES = 400;
|
|
17
18
|
/** Hard ceiling — same clamp as coverage.maxFiles. */
|
|
18
|
-
|
|
19
|
+
const MAX_HINT_FILES_CAP = 20_000;
|
|
19
20
|
const MAX_FILE_BYTES = 256 * 1024;
|
|
20
21
|
|
|
21
22
|
const HINT_SENSORS = new Set([
|
|
@@ -37,17 +38,6 @@ export function needsArkRuleFileHints(arkRules) {
|
|
|
37
38
|
return (arkRules?.structure ?? []).some((rule) => HINT_SENSORS.has(rule?.sensor));
|
|
38
39
|
}
|
|
39
40
|
|
|
40
|
-
/**
|
|
41
|
-
* @param {unknown} maxFiles
|
|
42
|
-
* @returns {number}
|
|
43
|
-
*/
|
|
44
|
-
export function resolveHintBudget(maxFiles) {
|
|
45
|
-
if (Number.isInteger(maxFiles) && maxFiles > 0) {
|
|
46
|
-
return Math.min(maxFiles, MAX_HINT_FILES_CAP);
|
|
47
|
-
}
|
|
48
|
-
return DEFAULT_MAX_HINT_FILES;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
41
|
/**
|
|
52
42
|
* @param {unknown} hints
|
|
53
43
|
* @returns {null | {
|
|
@@ -74,41 +64,6 @@ export function formatHintBudgetDoctorLine(budget) {
|
|
|
74
64
|
return `${HINT_BUDGET_DOCTOR_LINE} Hinted ${budget.hinted} of ${budget.governed} eligible governed files (budget ${budget.budget}).`;
|
|
75
65
|
}
|
|
76
66
|
|
|
77
|
-
/**
|
|
78
|
-
* Minimal glob match (double-star slash = zero path segments).
|
|
79
|
-
* @param {string} glob
|
|
80
|
-
* @param {string} file
|
|
81
|
-
*/
|
|
82
|
-
function matchSimpleGlob(glob, file) {
|
|
83
|
-
const pattern = String(glob || '').replace(/\\/g, '/');
|
|
84
|
-
const target = String(file || '').replace(/\\/g, '/');
|
|
85
|
-
if (!pattern) return false;
|
|
86
|
-
let out = '';
|
|
87
|
-
for (let i = 0; i < pattern.length; i += 1) {
|
|
88
|
-
const c = pattern[i];
|
|
89
|
-
if (c === '*') {
|
|
90
|
-
if (pattern[i + 1] === '*') {
|
|
91
|
-
if (pattern[i + 2] === '/') {
|
|
92
|
-
out += '(?:.*/)?';
|
|
93
|
-
i += 2;
|
|
94
|
-
} else {
|
|
95
|
-
out += '.*';
|
|
96
|
-
i += 1;
|
|
97
|
-
}
|
|
98
|
-
} else {
|
|
99
|
-
out += '[^/]*';
|
|
100
|
-
}
|
|
101
|
-
} else if (c === '?') {
|
|
102
|
-
out += '[^/]';
|
|
103
|
-
} else if (/[.+^${}()|[\]\\]/.test(c)) {
|
|
104
|
-
out += `\\${c}`;
|
|
105
|
-
} else {
|
|
106
|
-
out += c;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
return new RegExp(`^${out}$`).test(target);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
67
|
/**
|
|
113
68
|
* @param {string} file
|
|
114
69
|
* @param {unknown} appliesTo
|