arkgate 4.6.7 → 4.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +109 -0
- package/README.md +20 -9
- package/SECURITY.md +1 -1
- package/bin/ark-check-runtime.mjs +13 -1
- package/bin/ark-mcp-runtime.mjs +65 -3
- package/bin/lib/adapter-contract.mjs +17 -36
- package/bin/lib/analysis-engine.mjs +6 -6
- package/bin/lib/ark-run-doctor.mjs +144 -0
- package/bin/lib/ark-run-facts.mjs +472 -0
- package/bin/lib/ark-run-report.mjs +57 -0
- package/bin/lib/ark-run-sensors.mjs +309 -0
- package/bin/lib/config-contract.mjs +86 -11
- package/bin/lib/diagnostic-catalog.mjs +8 -0
- package/bin/lib/doctor-advisories.mjs +45 -8
- package/bin/lib/doctor-human.mjs +10 -0
- package/bin/lib/doctor-plan.mjs +20 -16
- package/bin/lib/extra-merge-teeth.mjs +187 -0
- package/bin/lib/html-report-advisories.mjs +2 -0
- package/bin/lib/html-report-depth.mjs +22 -2
- package/bin/lib/html-report.mjs +16 -0
- package/bin/lib/remediation.mjs +132 -0
- package/bin/lib/resolved-candidate-facts.mjs +67 -2
- package/bin/lib/rules-under-contract.mjs +37 -89
- package/bin/lib/snippet-analysis.mjs +43 -2
- package/bin/lib/status-command.mjs +28 -0
- package/bin/lib/status-manifest.mjs +23 -0
- package/dist/{configTypes-l6XiwiC1.d.ts → configTypes-CgJimx9o.d.ts} +17 -3
- package/dist/eslint/index.cjs +6 -2
- package/dist/eslint/index.d.ts +70 -2
- package/dist/eslint/index.js +6 -2
- package/dist/index.cjs +35 -35
- package/dist/index.d.ts +787 -272
- package/dist/index.js +35 -35
- package/docs/README.md +2 -1
- package/docs/agent-guide.md +21 -15
- package/docs/ai-gates.md +13 -0
- package/docs/configuration.md +24 -11
- package/docs/develop.md +12 -3
- package/docs/diagnostics.md +75 -0
- package/docs/enthusiast/README.md +4 -3
- package/docs/package-surface.md +16 -13
- package/docs/product-voice.md +6 -3
- package/docs/threat-model.md +1 -1
- package/docs/use.md +5 -4
- package/package.json +1 -1
- package/schemas/ark.config.schema.json +41 -2
- package/schemas/ark.resolved-candidate-facts.schema.json +1 -1
- package/schemas/ark.status-manifest.schema.json +47 -0
- package/server.json +2 -2
- package/templates/agent-skills/README.md +1 -1
- package/templates/agent-skills/ark-adopt/SKILL.md +23 -2
- package/templates/agent-skills/ark-place/SKILL.md +26 -2
- package/templates/agent-skills/ark-runtime/SKILL.md +66 -24
- package/templates/skills/ark-adopt.md +23 -2
- package/templates/skills/ark-place.md +26 -2
- package/templates/skills/ark-runtime.md +66 -24
|
@@ -7,6 +7,11 @@
|
|
|
7
7
|
import { loadEffectiveArkRulesFromDisk } from './effective-contract-load.mjs';
|
|
8
8
|
import { evaluateInvariantCoverage } from './invariant-coverage.mjs';
|
|
9
9
|
import { loadInvariantCoverageInputs } from './invariant-coverage-io.mjs';
|
|
10
|
+
import {
|
|
11
|
+
EXTRA_MERGE_TEETH_GOVERNED_FLOOR,
|
|
12
|
+
composeMergePlanesHonesty,
|
|
13
|
+
demoteExtraPlaneTeethUnderClassificationFloor,
|
|
14
|
+
} from './extra-merge-teeth.mjs';
|
|
10
15
|
|
|
11
16
|
/**
|
|
12
17
|
* Cap long catalogs in doctor JSON (and HTML, which consumes the same summary).
|
|
@@ -16,12 +21,11 @@ const COVERED_SAMPLE_MAX = 24;
|
|
|
16
21
|
const STRUCTURE_CATALOG_MAX = 40;
|
|
17
22
|
const UNCOVERED_CATALOG_MAX = 30;
|
|
18
23
|
|
|
19
|
-
|
|
20
|
-
export const EXTRA_MERGE_TEETH_GOVERNED_FLOOR = 50;
|
|
24
|
+
export { EXTRA_MERGE_TEETH_GOVERNED_FLOOR };
|
|
21
25
|
|
|
22
26
|
/**
|
|
23
27
|
* P1M / extraMergeTeeth: under the classification floor, demote enforced ArkRules
|
|
24
|
-
*
|
|
28
|
+
* and ArkRun findings so merge matches doctor stamp (layer graph only).
|
|
25
29
|
* Unknown classification (null/null) → do not demote (contract-only callers).
|
|
26
30
|
*
|
|
27
31
|
* @param {object[]} violations
|
|
@@ -29,28 +33,7 @@ export const EXTRA_MERGE_TEETH_GOVERNED_FLOOR = 50;
|
|
|
29
33
|
* @returns {object[]}
|
|
30
34
|
*/
|
|
31
35
|
export function demoteArkRuleTeethUnderClassificationFloor(violations, classification = {}) {
|
|
32
|
-
|
|
33
|
-
const governed =
|
|
34
|
-
typeof classification.governedPercent === 'number' ? classification.governedPercent : null;
|
|
35
|
-
const populated =
|
|
36
|
-
typeof classification.populatedLayerCount === 'number'
|
|
37
|
-
? classification.populatedLayerCount
|
|
38
|
-
: null;
|
|
39
|
-
if (governed == null && populated == null) return violations;
|
|
40
|
-
const allowsTeeth =
|
|
41
|
-
(governed ?? 0) >= EXTRA_MERGE_TEETH_GOVERNED_FLOOR && (populated ?? 0) >= 1;
|
|
42
|
-
if (allowsTeeth) return violations;
|
|
43
|
-
for (const v of violations) {
|
|
44
|
-
const isArkRule =
|
|
45
|
-
v?.arkruleId != null ||
|
|
46
|
-
(typeof v?.ruleId === 'string' &&
|
|
47
|
-
(v.ruleId.startsWith('ARKRULE') || v.ruleId.startsWith('arkrule')));
|
|
48
|
-
if (isArkRule && v.failsStrict !== false) {
|
|
49
|
-
v.failsStrict = false;
|
|
50
|
-
if (v.severity === 'error') v.severity = 'warning';
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
return violations;
|
|
36
|
+
return demoteExtraPlaneTeethUnderClassificationFloor(violations, classification);
|
|
54
37
|
}
|
|
55
38
|
|
|
56
39
|
/**
|
|
@@ -63,6 +46,18 @@ export function demoteArkRuleTeethUnderClassificationFloor(violations, classific
|
|
|
63
46
|
* classifiedFiles?: number | null,
|
|
64
47
|
* }} [classification] layer-plane coverage (when known)
|
|
65
48
|
*/
|
|
49
|
+
function arkRunMergeInput(config, residualCount = 0) {
|
|
50
|
+
const extra = config?.arkRun;
|
|
51
|
+
if (!extra || typeof extra !== 'object') {
|
|
52
|
+
return { present: false, mode: null, residualCount: 0 };
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
present: true,
|
|
56
|
+
mode: extra.mode === 'enforced' || extra.mode === 'advisory' ? extra.mode : null,
|
|
57
|
+
residualCount: Number(residualCount) || 0,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
66
61
|
export function summarizeRulesUnderContract(root, config, facts, classification) {
|
|
67
62
|
if (!config?.arkRules || Object.keys(config.arkRules).length === 0) {
|
|
68
63
|
return {
|
|
@@ -71,6 +66,11 @@ export function summarizeRulesUnderContract(root, config, facts, classification)
|
|
|
71
66
|
invariants: 0,
|
|
72
67
|
coveredInvariants: 0,
|
|
73
68
|
uncoveredInvariants: 0,
|
|
69
|
+
mergePlanes: composeMergePlanesHonesty({
|
|
70
|
+
classification,
|
|
71
|
+
arkRules: { active: false },
|
|
72
|
+
arkRun: arkRunMergeInput(config),
|
|
73
|
+
}),
|
|
74
74
|
notAScore: true,
|
|
75
75
|
note: 'No arkRules map — intra-layer ArkRules are opt-in.',
|
|
76
76
|
};
|
|
@@ -162,73 +162,21 @@ export function summarizeRulesUnderContract(root, config, facts, classification)
|
|
|
162
162
|
const invariantAdvisory = invariants - invariantEnforced;
|
|
163
163
|
const coveredInvariants = coverage.coverage.filter((c) => c.covered).length;
|
|
164
164
|
const uncoveredInvariants = coverage.coverage.filter((c) => !c.covered).length;
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
:
|
|
174
|
-
|
|
175
|
-
classification && typeof classification.populatedLayerCount === 'number'
|
|
176
|
-
? classification.populatedLayerCount
|
|
177
|
-
: classification && typeof classification.classifiedFiles === 'number'
|
|
178
|
-
? classification.classifiedFiles > 0
|
|
179
|
-
? 1
|
|
180
|
-
: 0
|
|
181
|
-
: null;
|
|
182
|
-
const classificationKnown = governedPercent != null || populatedLayerCount != null;
|
|
183
|
-
const classificationAllowsTeeth = !classificationKnown
|
|
184
|
-
? true
|
|
185
|
-
: (governedPercent ?? 0) >= EXTRA_MERGE_TEETH_GOVERNED_FLOOR &&
|
|
186
|
-
(populatedLayerCount ?? 0) >= 1;
|
|
187
|
-
const extraMergeTeeth = hasEnforcedTeeth && classificationAllowsTeeth;
|
|
188
|
-
const teethDeferredForClassification =
|
|
189
|
-
hasEnforcedTeeth && classificationKnown && !classificationAllowsTeeth;
|
|
190
|
-
// P1-M — which plane can fail merge (layers vs enforced structure vs invariants).
|
|
191
|
-
const mergePlanes = {
|
|
192
|
-
layers: {
|
|
193
|
-
role: 'inter-layer-edges',
|
|
194
|
-
alwaysOnGate: true,
|
|
195
|
-
note: 'Import/export layer graph — the default merge plane. Absent arkRules changes nothing here.',
|
|
196
|
-
},
|
|
197
|
-
structureSensors: {
|
|
198
|
-
role: 'intra-layer-heuristics',
|
|
199
|
-
total: structureRules,
|
|
200
|
-
enforced: structureEnforced,
|
|
201
|
-
advisory: structureAdvisory,
|
|
202
|
-
note: 'Structure sensors are heuristics (prefer false negatives). Only mode:enforced fails merge; noisy sensors stay advisory by default. Advisory-only packs never add merge teeth (FG-ARKRULES-ADVISORY-ONLY).',
|
|
203
|
-
},
|
|
204
|
-
invariants: {
|
|
205
|
-
role: 'catalog-plus-coverage',
|
|
206
|
-
total: invariants,
|
|
207
|
-
enforced: invariantEnforced,
|
|
208
|
-
advisory: invariantAdvisory,
|
|
165
|
+
const mergePlanes = composeMergePlanesHonesty({
|
|
166
|
+
classification,
|
|
167
|
+
arkRules: {
|
|
168
|
+
active: true,
|
|
169
|
+
structureEnforced,
|
|
170
|
+
structureTotal: structureRules,
|
|
171
|
+
structureAdvisory,
|
|
172
|
+
invariantEnforced,
|
|
173
|
+
invariantTotal: invariants,
|
|
174
|
+
invariantAdvisory,
|
|
209
175
|
covered: coveredInvariants,
|
|
210
176
|
uncovered: uncoveredInvariants,
|
|
211
|
-
note: 'Invariants are catalog + coverage evidence, not a business runtime. Enforced + proven-uncovered fails merge; absence of enforced rules adds no extra teeth.',
|
|
212
177
|
},
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
extraMergeTeeth,
|
|
216
|
-
...(classificationKnown
|
|
217
|
-
? {
|
|
218
|
-
classificationGate: {
|
|
219
|
-
governedPercent: governedPercent ?? null,
|
|
220
|
-
populatedLayerCount: populatedLayerCount ?? null,
|
|
221
|
-
floorPercent: EXTRA_MERGE_TEETH_GOVERNED_FLOOR,
|
|
222
|
-
allowsTeeth: classificationAllowsTeeth,
|
|
223
|
-
},
|
|
224
|
-
}
|
|
225
|
-
: {}),
|
|
226
|
-
failMergeWhen: extraMergeTeeth
|
|
227
|
-
? 'Layer graph failures plus enforced structure/invariant findings (advisory sensors never fail merge alone).'
|
|
228
|
-
: teethDeferredForClassification
|
|
229
|
-
? `Layer graph only — enforced ArkRules structure/invariant findings are demoted under the teeth floor (need ≥${EXTRA_MERGE_TEETH_GOVERNED_FLOOR}% governed and ≥1 populated layer); they do not merge-block until classification is honest.`
|
|
230
|
-
: 'Layer graph only — no enforced ArkRules structure/invariant teeth on this tree. Advisory packs do not arm merge teeth.',
|
|
231
|
-
};
|
|
178
|
+
arkRun: arkRunMergeInput(config),
|
|
179
|
+
});
|
|
232
180
|
|
|
233
181
|
return {
|
|
234
182
|
active: true,
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/** Fail-closed completeness evidence for one proposed source snippet. */
|
|
2
|
+
import { layerForRelativePath } from '../ark-layer-match.mjs';
|
|
2
3
|
import { ANALYSIS_COMPLETENESS } from './analysis-completeness.mjs';
|
|
4
|
+
import { evaluateArkRunEditorSensorsFromSource } from './ark-run-sensors.mjs';
|
|
3
5
|
|
|
4
6
|
export function flattenTsParseDiagnostics(ts, diagnostics, sourceFile) {
|
|
5
7
|
if (!Array.isArray(diagnostics) || !ts) return [];
|
|
@@ -31,11 +33,50 @@ function finding(ruleId, message, file, nextAction) {
|
|
|
31
33
|
};
|
|
32
34
|
}
|
|
33
35
|
|
|
36
|
+
function arkRunSnippetViolations(source, context = {}) {
|
|
37
|
+
const extra = context.arkRun;
|
|
38
|
+
const layers = context.layers;
|
|
39
|
+
const file = context.relFile || context.filePath;
|
|
40
|
+
if (!extra || !Array.isArray(layers) || typeof file !== 'string' || file.length === 0) {
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
const layerForFile = (pathValue) => {
|
|
44
|
+
if (pathValue === file && typeof context.layer === 'string') return context.layer;
|
|
45
|
+
return layerForRelativePath(pathValue, layers);
|
|
46
|
+
};
|
|
47
|
+
const { findings } = evaluateArkRunEditorSensorsFromSource({
|
|
48
|
+
arkRun: extra,
|
|
49
|
+
layers,
|
|
50
|
+
file,
|
|
51
|
+
source,
|
|
52
|
+
layerForFile,
|
|
53
|
+
classification: context.classification,
|
|
54
|
+
});
|
|
55
|
+
return findings
|
|
56
|
+
.filter((finding) => finding.failsStrict)
|
|
57
|
+
.map((finding) => ({
|
|
58
|
+
ruleId: finding.ruleId,
|
|
59
|
+
code: finding.ruleId,
|
|
60
|
+
message: finding.message,
|
|
61
|
+
file: finding.file,
|
|
62
|
+
line: finding.line,
|
|
63
|
+
fromLayer: finding.fromLayer,
|
|
64
|
+
target: finding.target,
|
|
65
|
+
nextAction: finding.nextAction,
|
|
66
|
+
failsStrict: true,
|
|
67
|
+
severity: 'error',
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
70
|
+
|
|
34
71
|
export function validateSnippetAnalysis({ gate, ts, source, context = {} }) {
|
|
35
72
|
const observed = gate.validate(source, context);
|
|
73
|
+
const arkRunViolations = arkRunSnippetViolations(source, context);
|
|
36
74
|
const base = {
|
|
37
|
-
valid: Boolean(observed.lexicalValid ?? observed.valid),
|
|
38
|
-
violations:
|
|
75
|
+
valid: Boolean(observed.lexicalValid ?? observed.valid) && arkRunViolations.length === 0,
|
|
76
|
+
violations: [
|
|
77
|
+
...(Array.isArray(observed.violations) ? observed.violations : []),
|
|
78
|
+
...arkRunViolations,
|
|
79
|
+
],
|
|
39
80
|
};
|
|
40
81
|
const file = context.filePath;
|
|
41
82
|
|
|
@@ -25,6 +25,7 @@ import { detectActiveAgentHost } from './skill-install.mjs';
|
|
|
25
25
|
import { readBaseline } from './violations.mjs';
|
|
26
26
|
import { reportsDir, readJsonSafe } from './html-report.mjs';
|
|
27
27
|
import { summarizeRulesUnderContract } from './rules-under-contract.mjs';
|
|
28
|
+
import { projectStatusArkRun } from './ark-run-doctor.mjs';
|
|
28
29
|
import { collectVsBaseFacts, discoverTeamBaseRef } from './team-parliament-io.mjs';
|
|
29
30
|
import { classifyAdopted, readAdoptionStance } from './adoption-stance.mjs';
|
|
30
31
|
|
|
@@ -340,6 +341,19 @@ export function collectStatusFacts(options = {}) {
|
|
|
340
341
|
|
|
341
342
|
const arkruleFrozenFallback = countArkruleFrozenKeys(baseline);
|
|
342
343
|
|
|
344
|
+
const arkRun = (() => {
|
|
345
|
+
const extra = config?.arkRun;
|
|
346
|
+
if (!extra || typeof extra !== 'object') {
|
|
347
|
+
return projectStatusArkRun({ present: false, residual: 0 });
|
|
348
|
+
}
|
|
349
|
+
const snap = latest?.arkRun && typeof latest.arkRun === 'object' ? latest.arkRun : null;
|
|
350
|
+
const mode = extra.mode === 'enforced' || extra.mode === 'advisory' ? extra.mode : null;
|
|
351
|
+
const extraMergeTeeth =
|
|
352
|
+
snap && typeof snap.extraMergeTeeth === 'boolean' ? snap.extraMergeTeeth === true : false;
|
|
353
|
+
const residual = typeof snap?.residual === 'number' ? snap.residual : null;
|
|
354
|
+
return projectStatusArkRun({ present: true, mode, extraMergeTeeth, residual });
|
|
355
|
+
})();
|
|
356
|
+
|
|
343
357
|
// DF02 — always project compass with honesty mode (never invent green residual).
|
|
344
358
|
// Prefer explicit override (tests/MCP inject doctor-facts); else report snapshot.
|
|
345
359
|
let improvementCompass = null;
|
|
@@ -388,6 +402,7 @@ export function collectStatusFacts(options = {}) {
|
|
|
388
402
|
latest?.leftoverDesignWork === true ||
|
|
389
403
|
latest?.designFitness?.designWeak === true ||
|
|
390
404
|
latest?.doctor?.designFitness?.designWeak === true,
|
|
405
|
+
arkRun: options.arkRun ?? arkRun,
|
|
391
406
|
adopted:
|
|
392
407
|
options.adopted ??
|
|
393
408
|
classifyAdopted({
|
|
@@ -506,6 +521,19 @@ export function runStatusCommand(args = {}) {
|
|
|
506
521
|
);
|
|
507
522
|
}
|
|
508
523
|
if (manifest.vsBase?.line) write(` ${manifest.vsBase.line}`);
|
|
524
|
+
const arkRunLine = manifest.arkRun;
|
|
525
|
+
if (arkRunLine && arkRunLine.notAScore === true) {
|
|
526
|
+
const residual =
|
|
527
|
+
arkRunLine.residual == null ? 'unknown' : String(arkRunLine.residual);
|
|
528
|
+
write(
|
|
529
|
+
` arkRun: ${arkRunLine.present ? arkRunLine.mode || 'on' : 'absent'}` +
|
|
530
|
+
` · residual=${residual}` +
|
|
531
|
+
(arkRunLine.present
|
|
532
|
+
? ` · extraMergeTeeth=${arkRunLine.extraMergeTeeth === true}`
|
|
533
|
+
: '') +
|
|
534
|
+
' · not a score'
|
|
535
|
+
);
|
|
536
|
+
}
|
|
509
537
|
write(` next: [${manifest.nextAction.id}] ${manifest.nextAction.summary}`);
|
|
510
538
|
}
|
|
511
539
|
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* Pure CLI helper (bin/lib/status-manifest.mjs). Zero Node I/O.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
import { projectStatusArkRun } from './ark-run-doctor.mjs';
|
|
11
12
|
export const ARK_STATUS_MANIFEST_SCHEMA_VERSION = '1.0';
|
|
12
13
|
export const ARK_STATUS_MANIFEST_SCHEMA_URL = 'https://unpkg.com/arkgate@4/schemas/ark.status-manifest.schema.json';
|
|
13
14
|
/**
|
|
@@ -242,6 +243,12 @@ export function resolveStatusNextAction(facts, binding, activation, lastCheck, r
|
|
|
242
243
|
summary: 'ArkRules residual remains frozen — review inventory debt without claiming a score.',
|
|
243
244
|
};
|
|
244
245
|
}
|
|
246
|
+
if (facts.arkRun?.present === true && (facts.arkRun.residual ?? 0) > 0) {
|
|
247
|
+
return {
|
|
248
|
+
id: 'review-arkrun-residual',
|
|
249
|
+
summary: 'ArkRun residual remains — wire kernel usage or declarations through @arkgate/runtime. Not a score.',
|
|
250
|
+
};
|
|
251
|
+
}
|
|
245
252
|
if (facts.adopted === 'required-merge' || facts.adopted === 'advisory-only-acked') {
|
|
246
253
|
return {
|
|
247
254
|
id: 'stay-enforced',
|
|
@@ -326,6 +333,9 @@ export function buildStatusManifest(facts) {
|
|
|
326
333
|
if (facts.vsBase && typeof facts.vsBase.baseRef === 'string' && facts.vsBase.baseRef.length > 0) {
|
|
327
334
|
status.vsBase = facts.vsBase;
|
|
328
335
|
}
|
|
336
|
+
if (facts.arkRun && typeof facts.arkRun === 'object') {
|
|
337
|
+
status.arkRun = projectStatusArkRun(facts.arkRun);
|
|
338
|
+
}
|
|
329
339
|
return status;
|
|
330
340
|
}
|
|
331
341
|
const STATUS_COMPASS_MODE_SET = new Set(STATUS_COMPASS_MODES);
|
|
@@ -605,5 +615,18 @@ export const ARK_STATUS_MANIFEST_SCHEMA = {
|
|
|
605
615
|
baselineGrew: { type: 'boolean' },
|
|
606
616
|
},
|
|
607
617
|
},
|
|
618
|
+
arkRun: {
|
|
619
|
+
type: 'object',
|
|
620
|
+
description: 'ArkRun extra residual (notAScore). present/mode from config; residual is a finding-id count (null = unknown, not green). extraMergeTeeth is honesty, never a score.',
|
|
621
|
+
additionalProperties: false,
|
|
622
|
+
required: ['notAScore', 'present', 'mode', 'extraMergeTeeth', 'residual'],
|
|
623
|
+
properties: {
|
|
624
|
+
notAScore: { const: true },
|
|
625
|
+
present: { type: 'boolean' },
|
|
626
|
+
mode: { anyOf: [{ enum: ['advisory', 'enforced'] }, { type: 'null' }] },
|
|
627
|
+
extraMergeTeeth: { type: 'boolean' },
|
|
628
|
+
residual: { anyOf: [{ type: 'integer', minimum: 0 }, { type: 'null' }] },
|
|
629
|
+
},
|
|
630
|
+
},
|
|
608
631
|
},
|
|
609
632
|
};
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* must stay self-contained: type-only imports/exports are erased on transpile, so
|
|
7
7
|
* this split never reaches bin/lib/config-contract.mjs.
|
|
8
8
|
*/
|
|
9
|
-
type ArkConfigSchemaVersion = '1.0' | '1.1';
|
|
9
|
+
type ArkConfigSchemaVersion = '1.0' | '1.1' | '1.2';
|
|
10
10
|
type ArkConfigCyclePolicy = 'strict' | 'soft' | 'framework-soft' | 'off';
|
|
11
11
|
type ArkConfigLayerCapabilities = {
|
|
12
12
|
deny?: string[];
|
|
@@ -51,6 +51,18 @@ type ArkConfigSafety = {
|
|
|
51
51
|
* Absence changes no inter-layer verdict.
|
|
52
52
|
*/
|
|
53
53
|
type ArkConfigArkRulesRefs = Record<string, string>;
|
|
54
|
+
/** ADR 0020 — advisory never adds merge teeth; enforced is the extra's merge plane. */
|
|
55
|
+
type ArkConfigArkRunMode = 'advisory' | 'enforced';
|
|
56
|
+
/**
|
|
57
|
+
* ADR 0020 — optional inline ArkRun extra (schema 1.2+). Absence is silent.
|
|
58
|
+
* Present objects are fully defaulted by the loader.
|
|
59
|
+
*/
|
|
60
|
+
type ArkConfigArkRun = {
|
|
61
|
+
mode: ArkConfigArkRunMode;
|
|
62
|
+
compositionRoots: string[];
|
|
63
|
+
managedLayers: string[];
|
|
64
|
+
requireDeclarations: boolean;
|
|
65
|
+
};
|
|
54
66
|
type ArkConfig = {
|
|
55
67
|
$schema: string;
|
|
56
68
|
schemaVersion: ArkConfigSchemaVersion;
|
|
@@ -66,6 +78,8 @@ type ArkConfig = {
|
|
|
66
78
|
safety?: ArkConfigSafety;
|
|
67
79
|
/** ADR 0012 — modular ArkRules references (schema 1.1+). */
|
|
68
80
|
arkRules?: ArkConfigArkRulesRefs;
|
|
81
|
+
/** ADR 0020 — optional ArkRun extra (schema 1.2+). Absence changes no Layers/ArkRules verdict. */
|
|
82
|
+
arkRun?: ArkConfigArkRun;
|
|
69
83
|
/**
|
|
70
84
|
* Optional GitHub handles or emails who may loosen the contract or grow the baseline.
|
|
71
85
|
* Metadata — excluded from policy hash. Absence means no steward lock (policy-ack still applies).
|
|
@@ -77,10 +91,10 @@ type ArkConfigIssue = {
|
|
|
77
91
|
message: string;
|
|
78
92
|
};
|
|
79
93
|
/** Original input version when the loader rewrote schemaVersion toward current. */
|
|
80
|
-
type ArkConfigMigratedFrom = 'unversioned' | '1.0' | null;
|
|
94
|
+
type ArkConfigMigratedFrom = 'unversioned' | '1.0' | '1.1' | null;
|
|
81
95
|
type ArkConfigLoadResult = {
|
|
82
96
|
config: ArkConfig;
|
|
83
97
|
migratedFrom: ArkConfigMigratedFrom;
|
|
84
98
|
};
|
|
85
99
|
|
|
86
|
-
export type { ArkConfig as A, ArkConfigRule as a, ArkConfigSchemaVersion as b, ArkConfigLoadResult as c, ArkConfigLayer as d, ArkConfigIssue as e };
|
|
100
|
+
export type { ArkConfig as A, ArkConfigRule as a, ArkConfigSchemaVersion as b, ArkConfigLoadResult as c, ArkConfigLayer as d, ArkConfigIssue as e, ArkConfigArkRun as f };
|