arkgate 4.8.11 → 4.8.14
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 +125 -2
- package/README.md +39 -46
- package/SECURITY.md +5 -3
- package/bin/ark-check-runtime.mjs +11 -9
- package/bin/lib/agent-projection-formatters.mjs +2 -0
- package/bin/lib/agent-skills-package.mjs +63 -8
- package/bin/lib/analysis-engine.mjs +4 -4
- package/bin/lib/architecture-scan.mjs +8 -2
- package/bin/lib/ark-order-doctor.mjs +160 -0
- package/bin/lib/ark-order-report.mjs +65 -0
- package/bin/lib/ark-order-sensors.mjs +1 -1
- package/bin/lib/ci-and-commands.mjs +7 -2
- package/bin/lib/design-smells.mjs +21 -1
- package/bin/lib/diagnostic-catalog.mjs +4 -4
- package/bin/lib/doctor-advisories.mjs +99 -18
- package/bin/lib/doctor-human.mjs +4 -11
- package/bin/lib/doctor-plan.mjs +4 -3
- package/bin/lib/extra-merge-teeth.mjs +32 -4
- package/bin/lib/first-run-help.mjs +11 -2
- package/bin/lib/gate-files.mjs +40 -3
- package/bin/lib/html-report-advisories.mjs +2 -0
- package/bin/lib/html-report-depth.mjs +8 -18
- package/bin/lib/html-report.mjs +16 -0
- package/bin/lib/install-migrate.mjs +23 -0
- package/bin/lib/mcp-hook-payload.mjs +1 -1
- package/bin/lib/product-copy.mjs +4 -0
- package/bin/lib/remediation.mjs +7 -7
- package/bin/lib/resolved-candidate-facts.mjs +144 -36
- package/bin/lib/rules-under-contract.mjs +14 -0
- package/bin/lib/scan-files.mjs +39 -0
- package/bin/lib/start-preview.mjs +4 -0
- package/bin/lib/status-command.mjs +28 -0
- package/bin/lib/status-manifest.mjs +23 -0
- package/bin/lib/upgrade-whats-new.mjs +3 -3
- package/bin/lib/violations.mjs +40 -1
- package/dist/{diagnosticCatalog-DiflIock.d.ts → diagnosticCatalog-DVx_2RmF.d.ts} +1 -1
- package/dist/eslint/index.cjs +4 -4
- package/dist/eslint/index.js +4 -4
- package/dist/index.cjs +31 -31
- package/dist/index.d.ts +129 -15
- package/dist/index.js +31 -31
- package/dist/nestjs/index.cjs +1 -1
- package/dist/nestjs/index.js +1 -1
- package/dist/runtime/index.cjs +15 -15
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/index.js +15 -15
- package/docs/README.md +11 -8
- package/docs/agent-guide.md +30 -13
- package/docs/ai-gates.md +3 -1
- package/docs/arkorder.md +35 -10
- package/docs/configuration.md +7 -6
- package/docs/develop.md +3 -1
- package/docs/diagnostics.md +7 -7
- package/docs/enthusiast/README.md +6 -1
- package/docs/enthusiast/how-to-gallery-starter.md +2 -1
- package/docs/package-surface.md +8 -5
- package/docs/product-voice.md +40 -13
- package/docs/threat-model.md +2 -2
- package/docs/typescript-support.md +3 -3
- package/docs/use.md +18 -11
- package/package.json +1 -1
- package/schemas/ark.status-manifest.schema.json +47 -0
- package/server.json +2 -2
- package/templates/agent-skills/README.md +7 -4
- package/templates/agent-skills/ark-adopt/SKILL.md +9 -5
- package/templates/agent-skills/ark-architect/SKILL.md +5 -18
- package/templates/agent-skills/ark-autopilot/SKILL.md +8 -4
- package/templates/agent-skills/ark-contract/SKILL.md +9 -20
- package/templates/agent-skills/ark-coverage/SKILL.md +12 -8
- package/templates/agent-skills/ark-explain/SKILL.md +7 -3
- package/templates/agent-skills/ark-explore/SKILL.md +25 -4
- package/templates/agent-skills/ark-fix/SKILL.md +15 -20
- package/templates/agent-skills/ark-loop/SKILL.md +14 -20
- package/templates/agent-skills/ark-order/SKILL.md +200 -0
- package/templates/agent-skills/ark-place/SKILL.md +11 -8
- package/templates/agent-skills/ark-runtime/SKILL.md +17 -4
- package/templates/agent-skills/ark-think/SKILL.md +24 -126
- package/templates/agent-skills/ark-upgrade/SKILL.md +13 -2
- package/templates/skills/ark-adopt.md +9 -5
- package/templates/skills/ark-architect.md +5 -18
- package/templates/skills/ark-autopilot.md +8 -4
- package/templates/skills/ark-contract.md +9 -20
- package/templates/skills/ark-coverage.md +12 -8
- package/templates/skills/ark-explain.md +7 -3
- package/templates/skills/ark-explore.md +25 -4
- package/templates/skills/ark-fix.md +15 -20
- package/templates/skills/ark-loop.md +14 -20
- package/templates/skills/ark-order.md +200 -0
- package/templates/skills/ark-place.md +11 -8
- package/templates/skills/ark-runtime.md +17 -4
- package/templates/skills/ark-think.md +24 -126
- package/templates/skills/ark-upgrade.md +13 -2
- package/templates/tests/ark-adoption-gaps.test.ts +5 -4
|
@@ -211,32 +211,67 @@ function canonicalOverlayPath(
|
|
|
211
211
|
return [...candidates].sort()[0] ?? requested;
|
|
212
212
|
}
|
|
213
213
|
|
|
214
|
-
function
|
|
214
|
+
function discoverScopedFiles(root, config, scopeRelatives, observeInput) {
|
|
215
|
+
const discovered = [];
|
|
216
|
+
const seen = new Set();
|
|
217
|
+
for (const raw of scopeRelatives ?? []) {
|
|
218
|
+
let relative;
|
|
219
|
+
try {
|
|
220
|
+
relative = canonicalProjectPath(
|
|
221
|
+
String(raw || '')
|
|
222
|
+
.replace(/\\/g, '/')
|
|
223
|
+
.replace(/^\.\//, '')
|
|
224
|
+
);
|
|
225
|
+
} catch {
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
if (seen.has(relative) || !isGovernableSourceFile(path.basename(relative))) continue;
|
|
229
|
+
if (!isIncluded(relative, config.include) || isScanExcludedRelative(relative, config)) {
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
const absolute = path.join(root, ...relative.split('/'));
|
|
233
|
+
if (!fs.existsSync(absolute)) continue;
|
|
234
|
+
seen.add(relative);
|
|
235
|
+
observeResolvedInput(observeInput, absolute, 'realpath');
|
|
236
|
+
discovered.push({
|
|
237
|
+
absolute,
|
|
238
|
+
real: fs.realpathSync(absolute),
|
|
239
|
+
relative,
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
return discovered.sort((left, right) =>
|
|
243
|
+
left.relative < right.relative ? -1 : left.relative > right.relative ? 1 : 0
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function collectCandidateFiles(root, config, changes, observeInput, scopeRelatives) {
|
|
215
248
|
const files = new Map();
|
|
216
249
|
const directoryAliases = new Map();
|
|
217
250
|
const expandedAliasDirectories = new Set();
|
|
218
|
-
const discovered = (
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
251
|
+
const discovered = Array.isArray(scopeRelatives)
|
|
252
|
+
? discoverScopedFiles(root, config, scopeRelatives, observeInput)
|
|
253
|
+
: (config.include ?? [])
|
|
254
|
+
.flatMap((entry) =>
|
|
255
|
+
collectGovernedFiles(root, { ...config, include: [entry] }, {
|
|
256
|
+
observeInput,
|
|
257
|
+
onDirectory(absolute, real) {
|
|
258
|
+
let relative = normalize(path.relative(root, absolute));
|
|
259
|
+
if (relative === '.') relative = '';
|
|
260
|
+
rememberDirectoryAlias(directoryAliases, real, relative, absolute);
|
|
261
|
+
},
|
|
262
|
+
})
|
|
263
|
+
)
|
|
264
|
+
.map((absolute) => {
|
|
265
|
+
observeResolvedInput(observeInput, absolute, 'realpath');
|
|
266
|
+
return {
|
|
267
|
+
absolute,
|
|
268
|
+
real: fs.realpathSync(absolute),
|
|
269
|
+
relative: canonicalProjectPath(normalize(path.relative(root, absolute))),
|
|
270
|
+
};
|
|
271
|
+
})
|
|
272
|
+
.sort((left, right) =>
|
|
273
|
+
left.relative < right.relative ? -1 : left.relative > right.relative ? 1 : 0
|
|
274
|
+
);
|
|
240
275
|
const canonicalByRealpath = new Map();
|
|
241
276
|
for (const candidate of discovered) {
|
|
242
277
|
if (!canonicalByRealpath.has(candidate.real)) canonicalByRealpath.set(candidate.real, candidate);
|
|
@@ -582,7 +617,8 @@ function compilerContext(ts, root, tsconfig, candidateFiles, observeInput) {
|
|
|
582
617
|
const optionsByPath = new Map();
|
|
583
618
|
const configInputsByPath = new Map();
|
|
584
619
|
const reasons = [];
|
|
585
|
-
|
|
620
|
+
const loadConfigOptions = (configPath) => {
|
|
621
|
+
if (optionsByPath.has(configPath)) return optionsByPath.get(configPath);
|
|
586
622
|
const configContents = new Map();
|
|
587
623
|
const readConfig = (fileName) => {
|
|
588
624
|
observeResolvedInput(observeInput, fileName, 'tsconfig');
|
|
@@ -605,15 +641,16 @@ function compilerContext(ts, root, tsconfig, candidateFiles, observeInput) {
|
|
|
605
641
|
});
|
|
606
642
|
optionsByPath.set(configPath, {});
|
|
607
643
|
configInputsByPath.set(configPath, configContents);
|
|
608
|
-
|
|
644
|
+
return {};
|
|
609
645
|
}
|
|
610
646
|
const parsed = ts.parseJsonConfigFileContent(
|
|
611
647
|
read.config,
|
|
612
648
|
{
|
|
613
649
|
useCaseSensitiveFileNames: ts.sys?.useCaseSensitiveFileNames ?? true,
|
|
614
|
-
readDirectory(
|
|
615
|
-
observeResolvedInput(observeInput,
|
|
616
|
-
|
|
650
|
+
readDirectory(dir) {
|
|
651
|
+
observeResolvedInput(observeInput, dir, 'tsconfig-directory');
|
|
652
|
+
// Options-only parse: `fileNames` are unused. Skip the include walk (#205).
|
|
653
|
+
return [];
|
|
617
654
|
},
|
|
618
655
|
fileExists(fileName) {
|
|
619
656
|
observeResolvedInput(observeInput, fileName, 'tsconfig-exists');
|
|
@@ -637,11 +674,19 @@ function compilerContext(ts, root, tsconfig, candidateFiles, observeInput) {
|
|
|
637
674
|
}
|
|
638
675
|
optionsByPath.set(configPath, parsed.options ?? {});
|
|
639
676
|
configInputsByPath.set(configPath, configContents);
|
|
640
|
-
|
|
677
|
+
return parsed.options ?? {};
|
|
678
|
+
};
|
|
679
|
+
for (const configPath of configPaths) loadConfigOptions(configPath);
|
|
641
680
|
|
|
642
681
|
const optionsFor = (fileName) => {
|
|
643
|
-
const
|
|
644
|
-
|
|
682
|
+
const resolved = path.resolve(fileName);
|
|
683
|
+
let configPath = configByFile.get(resolved);
|
|
684
|
+
if (!configPath) {
|
|
685
|
+
configPath = explicitPath
|
|
686
|
+
?? nearestTsconfig(root, resolved, nearestConfigByDirectory, observeInput);
|
|
687
|
+
if (configPath) configByFile.set(resolved, configPath);
|
|
688
|
+
}
|
|
689
|
+
return configPath ? loadConfigOptions(configPath) ?? {} : {};
|
|
645
690
|
};
|
|
646
691
|
const configs = configPaths.map((configPath) => ({
|
|
647
692
|
path: configLabel(root, configPath, externalAnchor),
|
|
@@ -1036,6 +1081,19 @@ function unavailableFacts(config, ts, reason) {
|
|
|
1036
1081
|
});
|
|
1037
1082
|
}
|
|
1038
1083
|
|
|
1084
|
+
function loadOneCandidateFile(root, config, relative, observeInput) {
|
|
1085
|
+
const discovered = discoverScopedFiles(root, config, [relative], observeInput);
|
|
1086
|
+
const found = discovered[0];
|
|
1087
|
+
if (!found) return null;
|
|
1088
|
+
observeResolvedInput(observeInput, found.absolute, 'source');
|
|
1089
|
+
return {
|
|
1090
|
+
path: found.relative,
|
|
1091
|
+
absolute: path.resolve(found.absolute),
|
|
1092
|
+
real: path.resolve(found.real),
|
|
1093
|
+
content: fs.readFileSync(found.absolute, 'utf8'),
|
|
1094
|
+
};
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1039
1097
|
/** Resolve one complete candidate tree (base or virtual overlay) into versioned neutral facts. */
|
|
1040
1098
|
export function resolveCandidateFacts({
|
|
1041
1099
|
root,
|
|
@@ -1044,6 +1102,7 @@ export function resolveCandidateFacts({
|
|
|
1044
1102
|
tsconfig,
|
|
1045
1103
|
changes = [],
|
|
1046
1104
|
observeInput,
|
|
1105
|
+
scopeFiles,
|
|
1047
1106
|
}) {
|
|
1048
1107
|
if (!ts?.createSourceFile || !ts?.resolveModuleName) {
|
|
1049
1108
|
return unavailableFacts(config, ts, 'No API-compatible TypeScript resolver is available.');
|
|
@@ -1053,10 +1112,17 @@ export function resolveCandidateFacts({
|
|
|
1053
1112
|
let candidateFiles;
|
|
1054
1113
|
let canonicalChanges;
|
|
1055
1114
|
let compiler;
|
|
1115
|
+
const scoped = Array.isArray(scopeFiles);
|
|
1056
1116
|
try {
|
|
1057
1117
|
observeResolvedInput(observeInput, root, 'realpath');
|
|
1058
1118
|
canonicalRoot = fs.realpathSync(root);
|
|
1059
|
-
const candidate = collectCandidateFiles(
|
|
1119
|
+
const candidate = collectCandidateFiles(
|
|
1120
|
+
canonicalRoot,
|
|
1121
|
+
config,
|
|
1122
|
+
changes,
|
|
1123
|
+
observeInput,
|
|
1124
|
+
scoped ? scopeFiles : undefined
|
|
1125
|
+
);
|
|
1060
1126
|
candidateFiles = candidate.files;
|
|
1061
1127
|
canonicalChanges = candidate.changes;
|
|
1062
1128
|
compiler = compilerContext(ts, canonicalRoot, tsconfig, candidateFiles, observeInput);
|
|
@@ -1106,7 +1172,8 @@ export function resolveCandidateFacts({
|
|
|
1106
1172
|
const compositionRootPatterns = [...(config.arkRun?.compositionRoots ?? [])];
|
|
1107
1173
|
const planeRootPatterns = [...(config.arkOrder?.planeRoots ?? [])];
|
|
1108
1174
|
|
|
1109
|
-
|
|
1175
|
+
const seedPathSet = new Set(candidateFiles.map((file) => file.path));
|
|
1176
|
+
const ingest = (candidate, fullExtract) => {
|
|
1110
1177
|
const sourceFile = ts.createSourceFile(
|
|
1111
1178
|
candidate.absolute,
|
|
1112
1179
|
candidate.content,
|
|
@@ -1158,6 +1225,7 @@ export function resolveCandidateFacts({
|
|
|
1158
1225
|
message: `${candidate.path} has ${parseDiagnosticCount} TypeScript parse diagnostic(s).`,
|
|
1159
1226
|
});
|
|
1160
1227
|
}
|
|
1228
|
+
if (!fullExtract) return;
|
|
1161
1229
|
const forbiddenUses = mayContainForbiddenCapability(ts, sourceFile, forbiddenGlobals)
|
|
1162
1230
|
? collectForbiddenCapabilityUses(ts, sourceFile, forbiddenGlobals)
|
|
1163
1231
|
: [];
|
|
@@ -1244,10 +1312,50 @@ export function resolveCandidateFacts({
|
|
|
1244
1312
|
// Never fail the resolver for ArkOrder release key-count extraction.
|
|
1245
1313
|
}
|
|
1246
1314
|
}
|
|
1315
|
+
};
|
|
1316
|
+
|
|
1317
|
+
for (const candidate of candidateFiles) ingest(candidate, true);
|
|
1318
|
+
|
|
1319
|
+
if (scoped) {
|
|
1320
|
+
const loaded = new Set(candidateFiles.map((file) => file.path));
|
|
1321
|
+
let pending = [...candidateFiles];
|
|
1322
|
+
while (pending.length > 0) {
|
|
1323
|
+
const discovered = [];
|
|
1324
|
+
for (const source of pending) {
|
|
1325
|
+
const parsedFile = parsed.get(source.path);
|
|
1326
|
+
if (!parsedFile) continue;
|
|
1327
|
+
for (const dependency of parsedFile.dependencies) {
|
|
1328
|
+
const resolved = resolveDependency(
|
|
1329
|
+
ts,
|
|
1330
|
+
dependency,
|
|
1331
|
+
parsedFile.candidate.absolute,
|
|
1332
|
+
compiler.optionsFor(parsedFile.candidate.absolute),
|
|
1333
|
+
host
|
|
1334
|
+
);
|
|
1335
|
+
if (resolved.resolution !== 'resolved-project' || !resolved.target) continue;
|
|
1336
|
+
if (loaded.has(resolved.target)) continue;
|
|
1337
|
+
const extra = loadOneCandidateFile(
|
|
1338
|
+
canonicalRoot,
|
|
1339
|
+
config,
|
|
1340
|
+
resolved.target,
|
|
1341
|
+
observeInput
|
|
1342
|
+
);
|
|
1343
|
+
if (!extra) continue;
|
|
1344
|
+
loaded.add(extra.path);
|
|
1345
|
+
candidateFiles.push(extra);
|
|
1346
|
+
ingest(extra, false);
|
|
1347
|
+
discovered.push(extra);
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
pending = discovered;
|
|
1351
|
+
}
|
|
1247
1352
|
}
|
|
1248
1353
|
|
|
1354
|
+
const extractCandidates = scoped
|
|
1355
|
+
? candidateFiles.filter((file) => seedPathSet.has(file.path))
|
|
1356
|
+
: candidateFiles;
|
|
1249
1357
|
const admittedTypeNames = new Set(classShapes.map((shape) => shape.className));
|
|
1250
|
-
for (const candidate of
|
|
1358
|
+
for (const candidate of extractCandidates) {
|
|
1251
1359
|
if (!/\.(tsx?|mts|cts)$/i.test(candidate.path)) continue;
|
|
1252
1360
|
try {
|
|
1253
1361
|
arkRunManagedNews.push(
|
|
@@ -1264,7 +1372,7 @@ export function resolveCandidateFacts({
|
|
|
1264
1372
|
|
|
1265
1373
|
if (planeRootPatterns.length > 0) {
|
|
1266
1374
|
const factoryFiles = new Set(arkOrderPlaneCalls.map((call) => call.file));
|
|
1267
|
-
for (const candidate of
|
|
1375
|
+
for (const candidate of extractCandidates) {
|
|
1268
1376
|
for (const pattern of planeRootPatterns) {
|
|
1269
1377
|
try {
|
|
1270
1378
|
if (!globToRegExp(pattern).test(candidate.path)) continue;
|
|
@@ -1284,7 +1392,7 @@ export function resolveCandidateFacts({
|
|
|
1284
1392
|
const factoryFiles = new Set(
|
|
1285
1393
|
arkRunKernelCalls.filter((call) => call.kind === 'factory').map((call) => call.file)
|
|
1286
1394
|
);
|
|
1287
|
-
for (const candidate of
|
|
1395
|
+
for (const candidate of extractCandidates) {
|
|
1288
1396
|
for (const pattern of compositionRootPatterns) {
|
|
1289
1397
|
try {
|
|
1290
1398
|
if (!globToRegExp(pattern).test(candidate.path)) continue;
|
|
@@ -62,6 +62,18 @@ function arkRunMergeInput(config, residualCount = 0) {
|
|
|
62
62
|
};
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
function arkOrderMergeInput(config, residualCount = 0) {
|
|
66
|
+
const extra = config?.arkOrder;
|
|
67
|
+
if (!extra || typeof extra !== 'object') {
|
|
68
|
+
return { present: false, mode: null, residualCount: 0 };
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
present: true,
|
|
72
|
+
mode: extra.mode === 'enforced' || extra.mode === 'advisory' ? extra.mode : null,
|
|
73
|
+
residualCount: Number(residualCount) || 0,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
65
77
|
export function summarizeRulesUnderContract(root, config, facts, classification) {
|
|
66
78
|
if (!config?.arkRules || Object.keys(config.arkRules).length === 0) {
|
|
67
79
|
return {
|
|
@@ -74,6 +86,7 @@ export function summarizeRulesUnderContract(root, config, facts, classification)
|
|
|
74
86
|
classification,
|
|
75
87
|
arkRules: { active: false },
|
|
76
88
|
arkRun: arkRunMergeInput(config),
|
|
89
|
+
arkOrder: arkOrderMergeInput(config),
|
|
77
90
|
}),
|
|
78
91
|
notAScore: true,
|
|
79
92
|
note: 'No arkRules map — intra-layer ArkRules are opt-in.',
|
|
@@ -187,6 +200,7 @@ export function summarizeRulesUnderContract(root, config, facts, classification)
|
|
|
187
200
|
uncovered: uncoveredInvariants,
|
|
188
201
|
},
|
|
189
202
|
arkRun: arkRunMergeInput(config),
|
|
203
|
+
arkOrder: arkOrderMergeInput(config),
|
|
190
204
|
});
|
|
191
205
|
|
|
192
206
|
return {
|
package/bin/lib/scan-files.mjs
CHANGED
|
@@ -115,6 +115,45 @@ export function walk(dir, files = [], options = {}) {
|
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
/** Walk include roots then drop codegen / config.exclude (universal scan filter). */
|
|
118
|
+
export function isIncludeMatch(relativePath, include) {
|
|
119
|
+
return (include ?? []).some((entry) => {
|
|
120
|
+
const includeRoot = String(entry)
|
|
121
|
+
.replace(/\\/g, '/')
|
|
122
|
+
.replace(/^\.\//, '')
|
|
123
|
+
.replace(/\/$/, '');
|
|
124
|
+
return (
|
|
125
|
+
includeRoot === '.' ||
|
|
126
|
+
relativePath === includeRoot ||
|
|
127
|
+
relativePath.startsWith(`${includeRoot}/`)
|
|
128
|
+
);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Path-only governed check — no tree walk. Matches `collectGovernedFiles` membership. */
|
|
133
|
+
export function isGovernedSourcePath(root, relativePath, config) {
|
|
134
|
+
const rel = normalize(String(relativePath || ''))
|
|
135
|
+
.replace(/^\.\//, '')
|
|
136
|
+
.replace(/\\/g, '/');
|
|
137
|
+
if (!rel || !isGovernableSourceFile(path.basename(rel))) return false;
|
|
138
|
+
if (!isIncludeMatch(rel, config?.include) || isScanExcludedRelative(rel, config)) return false;
|
|
139
|
+
return fs.existsSync(path.join(root, ...rel.split('/')));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Map diff paths to absolute governed files without walking the include tree. */
|
|
143
|
+
export function governedFilesFromRelativePaths(root, relativePaths, config) {
|
|
144
|
+
const out = [];
|
|
145
|
+
const seen = new Set();
|
|
146
|
+
for (const raw of relativePaths ?? []) {
|
|
147
|
+
const rel = normalize(String(raw || ''))
|
|
148
|
+
.replace(/^\.\//, '')
|
|
149
|
+
.replace(/\\/g, '/');
|
|
150
|
+
if (!rel || seen.has(rel) || !isGovernedSourcePath(root, rel, config)) continue;
|
|
151
|
+
seen.add(rel);
|
|
152
|
+
out.push(path.resolve(root, ...rel.split('/')));
|
|
153
|
+
}
|
|
154
|
+
return out;
|
|
155
|
+
}
|
|
156
|
+
|
|
118
157
|
export function collectGovernedFiles(root, config, options = {}) {
|
|
119
158
|
options.observeInput?.(path.resolve(root), 'realpath');
|
|
120
159
|
const state = {
|
|
@@ -4,6 +4,7 @@ import fs from 'node:fs';
|
|
|
4
4
|
import os from 'node:os';
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import { arkCommand, buildArchitectureRecommendation } from '../ark-shared.mjs';
|
|
7
|
+
import { ARKORDER_FIRST_CONTACT_NEXT, ARKORDER_ONE_BREATH } from './ark-order-doctor.mjs';
|
|
7
8
|
import { compactAgentInstructions, instructionRule, mcpJson } from './ci-and-commands.mjs';
|
|
8
9
|
import {
|
|
9
10
|
antigravityHooks,
|
|
@@ -162,6 +163,9 @@ export function renderStartPreview(preview, options = {}) {
|
|
|
162
163
|
if (!applying) {
|
|
163
164
|
console.log('Setup: install package + host gates (see --json).');
|
|
164
165
|
console.log('Preview does not write. Apply installs CI.');
|
|
166
|
+
console.log('Optional extras stay off. This start is layers only — they stop bad imports.');
|
|
167
|
+
console.log(ARKORDER_ONE_BREATH);
|
|
168
|
+
console.log(ARKORDER_FIRST_CONTACT_NEXT);
|
|
165
169
|
}
|
|
166
170
|
if (preview.runtimeActivation) {
|
|
167
171
|
console.log('Host: Codex is configured but not verified yet. Restart the host, then confirm this project.');
|
|
@@ -26,6 +26,7 @@ import { readBaseline } from './violations.mjs';
|
|
|
26
26
|
import { reportsDir, readJsonSafe } from './html-report.mjs';
|
|
27
27
|
import { summarizeRulesUnderContract } from './rules-under-contract.mjs';
|
|
28
28
|
import { projectStatusArkRun } from './ark-run-doctor.mjs';
|
|
29
|
+
import { projectStatusArkOrder } from './ark-order-doctor.mjs';
|
|
29
30
|
import { collectVsBaseFacts, discoverTeamBaseRef } from './team-parliament-io.mjs';
|
|
30
31
|
import { classifyAdopted, readAdoptionStance } from './adoption-stance.mjs';
|
|
31
32
|
|
|
@@ -354,6 +355,19 @@ export function collectStatusFacts(options = {}) {
|
|
|
354
355
|
return projectStatusArkRun({ present: true, mode, extraMergeTeeth, residual });
|
|
355
356
|
})();
|
|
356
357
|
|
|
358
|
+
const arkOrder = (() => {
|
|
359
|
+
const extra = config?.arkOrder;
|
|
360
|
+
if (!extra || typeof extra !== 'object') {
|
|
361
|
+
return projectStatusArkOrder({ present: false, residual: 0 });
|
|
362
|
+
}
|
|
363
|
+
const snap = latest?.arkOrder && typeof latest.arkOrder === 'object' ? latest.arkOrder : null;
|
|
364
|
+
const mode = extra.mode === 'enforced' || extra.mode === 'advisory' ? extra.mode : null;
|
|
365
|
+
const extraMergeTeeth =
|
|
366
|
+
snap && typeof snap.extraMergeTeeth === 'boolean' ? snap.extraMergeTeeth === true : false;
|
|
367
|
+
const residual = typeof snap?.residual === 'number' ? snap.residual : null;
|
|
368
|
+
return projectStatusArkOrder({ present: true, mode, extraMergeTeeth, residual });
|
|
369
|
+
})();
|
|
370
|
+
|
|
357
371
|
// DF02 — always project compass with honesty mode (never invent green residual).
|
|
358
372
|
// Prefer explicit override (tests/MCP inject doctor-facts); else report snapshot.
|
|
359
373
|
let improvementCompass = null;
|
|
@@ -403,6 +417,7 @@ export function collectStatusFacts(options = {}) {
|
|
|
403
417
|
latest?.designFitness?.designWeak === true ||
|
|
404
418
|
latest?.doctor?.designFitness?.designWeak === true,
|
|
405
419
|
arkRun: options.arkRun ?? arkRun,
|
|
420
|
+
arkOrder: options.arkOrder ?? arkOrder,
|
|
406
421
|
adopted:
|
|
407
422
|
options.adopted ??
|
|
408
423
|
classifyAdopted({
|
|
@@ -534,6 +549,19 @@ export function runStatusCommand(args = {}) {
|
|
|
534
549
|
' · not a score'
|
|
535
550
|
);
|
|
536
551
|
}
|
|
552
|
+
const arkOrderLine = manifest.arkOrder;
|
|
553
|
+
if (arkOrderLine && arkOrderLine.notAScore === true) {
|
|
554
|
+
const residual =
|
|
555
|
+
arkOrderLine.residual == null ? 'unknown' : String(arkOrderLine.residual);
|
|
556
|
+
write(
|
|
557
|
+
` arkOrder: ${arkOrderLine.present ? arkOrderLine.mode || 'on' : 'absent'}` +
|
|
558
|
+
` · residual=${residual}` +
|
|
559
|
+
(arkOrderLine.present
|
|
560
|
+
? ` · extraMergeTeeth=${arkOrderLine.extraMergeTeeth === true}`
|
|
561
|
+
: '') +
|
|
562
|
+
' · not a score'
|
|
563
|
+
);
|
|
564
|
+
}
|
|
537
565
|
write(` next: [${manifest.nextAction.id}] ${manifest.nextAction.summary}`);
|
|
538
566
|
}
|
|
539
567
|
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { projectStatusArkRun } from './ark-run-doctor.mjs';
|
|
12
|
+
import { projectStatusArkOrder } from './ark-order-doctor.mjs';
|
|
12
13
|
export const ARK_STATUS_MANIFEST_SCHEMA_VERSION = '1.0';
|
|
13
14
|
export const ARK_STATUS_MANIFEST_SCHEMA_URL = 'https://unpkg.com/arkgate@4/schemas/ark.status-manifest.schema.json';
|
|
14
15
|
/**
|
|
@@ -249,6 +250,12 @@ export function resolveStatusNextAction(facts, binding, activation, lastCheck, r
|
|
|
249
250
|
summary: 'ArkRun residual remains — wire kernel usage or declarations through arkgate/runtime. Not a score.',
|
|
250
251
|
};
|
|
251
252
|
}
|
|
253
|
+
if (facts.arkOrder?.present === true && (facts.arkOrder.residual ?? 0) > 0) {
|
|
254
|
+
return {
|
|
255
|
+
id: 'review-arkorder-residual',
|
|
256
|
+
summary: 'ArkOrder leftover remains — change that product choice through the valve (proposeRelease then apply), not a generic update. Not a score.',
|
|
257
|
+
};
|
|
258
|
+
}
|
|
252
259
|
if (facts.adopted === 'required-merge' || facts.adopted === 'advisory-only-acked') {
|
|
253
260
|
return {
|
|
254
261
|
id: 'stay-enforced',
|
|
@@ -336,6 +343,9 @@ export function buildStatusManifest(facts) {
|
|
|
336
343
|
if (facts.arkRun && typeof facts.arkRun === 'object') {
|
|
337
344
|
status.arkRun = projectStatusArkRun(facts.arkRun);
|
|
338
345
|
}
|
|
346
|
+
if (facts.arkOrder && typeof facts.arkOrder === 'object') {
|
|
347
|
+
status.arkOrder = projectStatusArkOrder(facts.arkOrder);
|
|
348
|
+
}
|
|
339
349
|
return status;
|
|
340
350
|
}
|
|
341
351
|
const STATUS_COMPASS_MODE_SET = new Set(STATUS_COMPASS_MODES);
|
|
@@ -628,5 +638,18 @@ export const ARK_STATUS_MANIFEST_SCHEMA = {
|
|
|
628
638
|
residual: { anyOf: [{ type: 'integer', minimum: 0 }, { type: 'null' }] },
|
|
629
639
|
},
|
|
630
640
|
},
|
|
641
|
+
arkOrder: {
|
|
642
|
+
type: 'object',
|
|
643
|
+
description: 'ArkOrder extra residual (notAScore). present/mode from config; residual is a finding-id count (null = unknown, not green). extraMergeTeeth is honesty, never a score.',
|
|
644
|
+
additionalProperties: false,
|
|
645
|
+
required: ['notAScore', 'present', 'mode', 'extraMergeTeeth', 'residual'],
|
|
646
|
+
properties: {
|
|
647
|
+
notAScore: { const: true },
|
|
648
|
+
present: { type: 'boolean' },
|
|
649
|
+
mode: { anyOf: [{ enum: ['advisory', 'enforced'] }, { type: 'null' }] },
|
|
650
|
+
extraMergeTeeth: { type: 'boolean' },
|
|
651
|
+
residual: { anyOf: [{ type: 'integer', minimum: 0 }, { type: 'null' }] },
|
|
652
|
+
},
|
|
653
|
+
},
|
|
631
654
|
},
|
|
632
655
|
};
|
|
@@ -36,10 +36,10 @@ export function buildUpgradeWhatsNewSuggestions() {
|
|
|
36
36
|
{
|
|
37
37
|
id: 'five-door-autonomy',
|
|
38
38
|
title: 'Five doors (invoke = write or map)',
|
|
39
|
-
try: '/ark-adopt · /ark-place · /ark-autopilot · /ark-explore · /ark-upgrade',
|
|
40
|
-
inspect: 'Skill bodies + doctor next action (other /ark-* names are shortcuts)',
|
|
39
|
+
try: '/ark-adopt · /ark-place · /ark-autopilot · /ark-explore · /ark-upgrade · /ark-runtime · /ark-order',
|
|
40
|
+
inspect: 'Skill bodies + doctor next action (other /ark-* names are one-release shortcuts)',
|
|
41
41
|
why:
|
|
42
|
-
'Invoking a door is the approval. The CLI is sensor + gate — it does not apply the change. Explore maps only.
|
|
42
|
+
'Invoking a door is the approval. The CLI is sensor + gate — it does not apply the change. Explore maps only. Closed catalog: first-class doors plus stubs. Contener · Guiar · Ordenar.',
|
|
43
43
|
},
|
|
44
44
|
{
|
|
45
45
|
id: 'team-parliament',
|
package/bin/lib/violations.mjs
CHANGED
|
@@ -59,9 +59,48 @@ export const FIX_HINTS = {
|
|
|
59
59
|
'Break the cycle: extract the shared code into a module both sides import, invert one edge behind a port/interface, or merge the files if they are really one unit.',
|
|
60
60
|
};
|
|
61
61
|
|
|
62
|
+
/** Extra-plane label so a slow-key deny reads as clearly as a bad import. */
|
|
63
|
+
export function violationPlaneLabel(ruleId) {
|
|
64
|
+
if (typeof ruleId !== 'string') return '';
|
|
65
|
+
if (ruleId.startsWith('ARKORDER_')) return '[ArkOrder] ';
|
|
66
|
+
if (ruleId.startsWith('ARKRUN_')) return '[ArkRun] ';
|
|
67
|
+
return '';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Where a warning came from, in the same `path:line` shape as a deny.
|
|
72
|
+
* Sensors already carry `file` / `line`; the human printer used to drop them.
|
|
73
|
+
* No file means the finding is about the project rules, not a line you edited.
|
|
74
|
+
*/
|
|
75
|
+
export const WARNING_UNATTRIBUTED =
|
|
76
|
+
'not a file — this is about the project rules, not a line you just edited';
|
|
77
|
+
|
|
78
|
+
export function formatWarningAttribution(warning) {
|
|
79
|
+
const file = typeof warning?.file === 'string' ? warning.file.trim() : '';
|
|
80
|
+
if (!file) return WARNING_UNATTRIBUTED;
|
|
81
|
+
const line =
|
|
82
|
+
Number.isInteger(warning.line) && warning.line > 0 ? warning.line : 1;
|
|
83
|
+
return `${file}:${line}`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function formatWarningLine(warning) {
|
|
87
|
+
const ruleId = typeof warning?.ruleId === 'string' ? warning.ruleId : 'WARNING';
|
|
88
|
+
const message = typeof warning?.message === 'string' ? warning.message : '';
|
|
89
|
+
return `warning ${ruleId} ${formatWarningAttribution(warning)} ${message}`.trimEnd();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function printWarning(warning) {
|
|
93
|
+
const ruleId = typeof warning?.ruleId === 'string' ? warning.ruleId : 'WARNING';
|
|
94
|
+
const message = typeof warning?.message === 'string' ? warning.message : '';
|
|
95
|
+
console.error(
|
|
96
|
+
`${color.yellow('warning')} ${ruleId} ${formatWarningAttribution(warning)} ${message}`.trimEnd()
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
62
100
|
export function printViolation(violation) {
|
|
63
101
|
const location = `${violation.file}:${violation.line}`;
|
|
64
|
-
|
|
102
|
+
const plane = violationPlaneLabel(violation.ruleId);
|
|
103
|
+
console.error(`${color.red('✖')} ${color.bold(`${plane}${violation.ruleId}`)} ${location}`);
|
|
65
104
|
if (violation.fromLayer && violation.toLayer) {
|
|
66
105
|
const target = violation.target ? ` ${color.dim(`(${violation.target})`)}` : '';
|
|
67
106
|
console.error(` ${violation.fromLayer} → ${violation.toLayer}${target}`);
|
|
@@ -392,7 +392,7 @@ declare function createAdapterResult(input: {
|
|
|
392
392
|
}): CurrentAdapterResult;
|
|
393
393
|
|
|
394
394
|
/** ArkGate library version — single source of truth. */
|
|
395
|
-
declare const version = "4.8.
|
|
395
|
+
declare const version = "4.8.14";
|
|
396
396
|
|
|
397
397
|
/**
|
|
398
398
|
* AI Code Gate (basic).
|