artifact-graph 0.6.0 → 0.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 +38 -4
- package/INSTALL.md +3 -3
- package/LICENSE +1 -1
- package/NOTICE +17 -3
- package/README.md +2 -2
- package/README.zh-CN.md +15 -15
- package/contracts/e2e-test/schema.json +315 -0
- package/dist/cli.js +1757 -404
- package/dist/index.cjs +1418 -485
- package/dist/index.d.cts +332 -1
- package/dist/index.d.ts +332 -1
- package/dist/index.js +1306 -387
- package/package.json +12 -6
package/dist/index.cjs
CHANGED
|
@@ -5,6 +5,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __esm = (fn, res) => function __init() {
|
|
9
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
+
};
|
|
8
11
|
var __export = (target, all) => {
|
|
9
12
|
for (var name in all)
|
|
10
13
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -27,121 +30,47 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
27
30
|
));
|
|
28
31
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
32
|
|
|
30
|
-
// src/index.ts
|
|
31
|
-
var index_exports = {};
|
|
32
|
-
__export(index_exports, {
|
|
33
|
-
ALWAYS_PRESENT: () => ALWAYS_PRESENT_ITEMS,
|
|
34
|
-
BASELINE_CONSTRAINTS: () => BASELINE_CONSTRAINTS,
|
|
35
|
-
BASELINE_CONSTRAINTS_COUNT: () => BASELINE_CONSTRAINTS_COUNT,
|
|
36
|
-
BASELINE_ITEMS_COUNT: () => BASELINE_ITEMS_COUNT,
|
|
37
|
-
DEFAULT_MAX_CHARS: () => DEFAULT_MAX_CHARS,
|
|
38
|
-
DEFAULT_SCHEMA: () => DEFAULT_SCHEMA,
|
|
39
|
-
MIN_PROMPT_CHARS: () => MIN_PROMPT_CHARS,
|
|
40
|
-
TARGET_ARTIFACT_TYPES: () => TARGET_ARTIFACT_TYPES,
|
|
41
|
-
VALID_PACKET_TARGET_TYPES: () => VALID_PACKET_TARGET_TYPES,
|
|
42
|
-
VERSION_INDEX_SCHEMA_VERSION: () => VERSION_INDEX_SCHEMA_VERSION,
|
|
43
|
-
VERSION_LOCK_PATH: () => VERSION_LOCK_PATH,
|
|
44
|
-
VERSION_LOCK_SCHEMA_VERSION: () => VERSION_LOCK_SCHEMA_VERSION,
|
|
45
|
-
applyPreparedManagedHookBlocks: () => applyPreparedManagedHookBlocks,
|
|
46
|
-
assemblePacket: () => assemblePacket,
|
|
47
|
-
auditPackets: () => auditPackets,
|
|
48
|
-
auditVersionLock: () => auditVersionLock,
|
|
49
|
-
bootstrapVersionLock: () => bootstrapVersionLock,
|
|
50
|
-
buildGraph: () => buildGraph,
|
|
51
|
-
buildVersionIndex: () => buildVersionIndex,
|
|
52
|
-
collectChangedPaths: () => collectChangedPaths,
|
|
53
|
-
computeE2eCoverageStats: () => computeE2eCoverageStats,
|
|
54
|
-
dirname: () => import_node_path6.dirname,
|
|
55
|
-
discoverAndAuditPackets: () => discoverAndAuditPackets,
|
|
56
|
-
discoverTargets: () => discoverTargets,
|
|
57
|
-
doctorArtifactChain: () => doctorArtifactChain,
|
|
58
|
-
extname: () => import_node_path6.extname,
|
|
59
|
-
formatContextMarkdown: () => formatContextMarkdown,
|
|
60
|
-
generateE2eRegistry: () => generateE2eRegistry,
|
|
61
|
-
getArtifactTypeMetadata: () => getArtifactTypeMetadata,
|
|
62
|
-
getTargetArtifactTypes: () => getTargetArtifactTypes,
|
|
63
|
-
installManagedHookBlock: () => installManagedHookBlock,
|
|
64
|
-
isPacketTargetType: () => isPacketTargetType,
|
|
65
|
-
isPacketTargetTypeDynamic: () => isPacketTargetTypeDynamic,
|
|
66
|
-
isTargetArtifactType: () => isTargetArtifactType,
|
|
67
|
-
loadConfig: () => loadConfig,
|
|
68
|
-
nextId: () => nextId,
|
|
69
|
-
parseTargetSelector: () => parseTargetSelector,
|
|
70
|
-
parseTargetsFile: () => parseTargetsFile,
|
|
71
|
-
prepareManagedHookBlock: () => prepareManagedHookBlock,
|
|
72
|
-
queryGraph: () => queryGraph,
|
|
73
|
-
refreshVersionLock: () => refreshVersionLock,
|
|
74
|
-
renderDoctorMarkdown: () => renderDoctorMarkdown,
|
|
75
|
-
renderMermaid: () => renderMermaid,
|
|
76
|
-
renderPacketMarkdown: () => renderPacketMarkdown,
|
|
77
|
-
renderPacketPrompt: () => renderPacketPrompt,
|
|
78
|
-
renderTraceVersionMarkdown: () => renderTraceVersionMarkdown,
|
|
79
|
-
renderVersionLockAuditMarkdown: () => renderVersionLockAuditMarkdown,
|
|
80
|
-
renderVersionLockRefreshMarkdown: () => renderVersionLockRefreshMarkdown,
|
|
81
|
-
resolveArtifactContext: () => resolveArtifactContext,
|
|
82
|
-
resolveArtifactGraphCli: () => resolveArtifactGraphCli,
|
|
83
|
-
resolveArtifactTypeName: () => resolveArtifactTypeName,
|
|
84
|
-
resolveCliTarget: () => resolveCliTarget,
|
|
85
|
-
resolveGitHookPath: () => resolveGitHookPath,
|
|
86
|
-
resolveMatrixEdges: () => resolveMatrixEdges,
|
|
87
|
-
scanArtifacts: () => scanArtifacts,
|
|
88
|
-
traceVersion: () => traceVersion,
|
|
89
|
-
updateVersionLock: () => updateVersionLock,
|
|
90
|
-
validateExecutableTraceability: () => validateExecutableTraceability,
|
|
91
|
-
validateGraph: () => validateGraph,
|
|
92
|
-
validatePacket: () => validatePacket,
|
|
93
|
-
validatePacketMarkdown: () => validatePacketMarkdown,
|
|
94
|
-
validatePacketPrompt: () => validatePacketPrompt,
|
|
95
|
-
validateReviewResult: () => validateReviewResult,
|
|
96
|
-
validateScenarioPrdLinkIndex: () => validateScenarioPrdLinkIndex,
|
|
97
|
-
validateScenarioPrdLinks: () => validateScenarioPrdLinks,
|
|
98
|
-
writeGraphCache: () => writeGraphCache
|
|
99
|
-
});
|
|
100
|
-
module.exports = __toCommonJS(index_exports);
|
|
101
|
-
var import_better_sqlite3 = __toESM(require("better-sqlite3"), 1);
|
|
102
|
-
var import_gray_matter = __toESM(require("gray-matter"), 1);
|
|
103
|
-
var import_js_yaml = __toESM(require("js-yaml"), 1);
|
|
104
|
-
var import_node_fs4 = require("fs");
|
|
105
|
-
var import_promises5 = require("fs/promises");
|
|
106
|
-
var import_node_path6 = require("path");
|
|
107
|
-
|
|
108
33
|
// src/packet-constants.ts
|
|
109
|
-
var ALWAYS_PRESENT_ITEMS
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
34
|
+
var ALWAYS_PRESENT_ITEMS, BASELINE_ITEMS_COUNT, BASELINE_CONSTRAINTS, BASELINE_CONSTRAINTS_COUNT;
|
|
35
|
+
var init_packet_constants = __esm({
|
|
36
|
+
"src/packet-constants.ts"() {
|
|
37
|
+
"use strict";
|
|
38
|
+
ALWAYS_PRESENT_ITEMS = [
|
|
39
|
+
{ path: "AGENTS.md", reason: "project agent instructions" },
|
|
40
|
+
{ path: "CLAUDE.md", reason: "project instructions" },
|
|
41
|
+
{ path: "artifacts/artifact-chain-spec.md", reason: "artifact chain specification" },
|
|
42
|
+
{ path: "artifacts/blueprints/generation-packet-spec.md", reason: "generation packet spec" },
|
|
43
|
+
{ path: "artifacts/blueprints/implementation-blueprint.md", reason: "implementation blueprint" },
|
|
44
|
+
{ path: "artifacts/contracts/interface-contracts.md", reason: "interface contracts" },
|
|
45
|
+
{ path: "artifacts/contracts/data-contracts.md", reason: "data contracts" },
|
|
46
|
+
{ path: "artifacts/contracts/application-state-machines.md", reason: "application state machines" },
|
|
47
|
+
{ path: "artifacts/contracts/error-model.md", reason: "error model" },
|
|
48
|
+
{ path: "artifacts/contracts/report-contracts.md", reason: "report contracts" },
|
|
49
|
+
{ path: "artifacts/contracts/ui-flow-contracts.md", reason: "UI flow contracts" },
|
|
50
|
+
{ path: "artifacts/contracts/non-functional-budgets.md", reason: "non-functional budgets" },
|
|
51
|
+
{ path: "artifacts/domain/domain-glossary.md", reason: "domain glossary" },
|
|
52
|
+
{ path: "artifacts/domain/bounded-context-map.md", reason: "bounded context map" },
|
|
53
|
+
{ path: "artifacts/domain/domain-invariants.md", reason: "domain invariants" },
|
|
54
|
+
{ path: "artifacts/tests/rule-golden-cases.md", reason: "rule golden cases" },
|
|
55
|
+
{ path: "artifacts/tests/verification-fixtures.md", reason: "verification fixtures" },
|
|
56
|
+
{ path: "artifacts/design/test-strategy.md", reason: "test strategy" },
|
|
57
|
+
{ path: "artifacts/traceability-matrix-v2.md", reason: "traceability matrix" }
|
|
58
|
+
];
|
|
59
|
+
BASELINE_ITEMS_COUNT = ALWAYS_PRESENT_ITEMS.length;
|
|
60
|
+
BASELINE_CONSTRAINTS = [
|
|
61
|
+
{ id: "C-ARCH-01", description: "\u6280\u672F\u6808\u5FC5\u987B\u4E3A TypeScript \u5168\u6808", source: "artifacts/decisions/D-ARCH-01.md" },
|
|
62
|
+
{ id: "C-ARCH-03", description: "\u4E0D\u53EF\u5728 Quick Check \u8DEF\u5F84\u5F15\u5165 LLM", source: "artifacts/decisions/D-ARCH-03.md" },
|
|
63
|
+
{ id: "C-ARCH-05", description: "\u4EA7\u54C1\u5F62\u6001\u4E3A Tauri v2 \u684C\u9762\u5E94\u7528\uFF0C\u4E0D\u4F7F\u7528\u5DF2\u5E9F\u5F03\u7684 Web Dashboard", source: "artifacts/decisions/D-ARCH-05.md" },
|
|
64
|
+
{ id: "C-RULE-01", description: "SEC severity \u4E0D\u5141\u8BB8\u964D\u7EA7", source: "artifacts/decisions/D-RULE-01.md" },
|
|
65
|
+
{ id: "C-GEN-01", description: "\u5B9E\u73B0\u84DD\u56FE\u4E0D\u5F97\u5F15\u5165 PRD \u4E2D\u672A\u51FA\u73B0\u7684\u65B0\u529F\u80FD", source: "artifacts/blueprints/implementation-blueprint.md" },
|
|
66
|
+
{ id: "C-GEN-02", description: "\u6587\u4EF6\u53D8\u66F4\u5FC5\u987B\u5148\u68C0\u67E5 interface-contracts\u3001data-contracts\u3001error-model \u662F\u5426\u9700\u8981\u540C\u6B65", source: "artifacts/blueprints/implementation-blueprint.md" },
|
|
67
|
+
{ id: "C-GEN-03", description: "\u4E0D\u5F97\u5C06\u5E9F\u5F03 Web Dashboard \u8BBE\u8BA1\u4F5C\u4E3A\u5B9E\u73B0\u4F9D\u636E", source: "artifacts/blueprints/implementation-blueprint.md" }
|
|
68
|
+
];
|
|
69
|
+
BASELINE_CONSTRAINTS_COUNT = BASELINE_CONSTRAINTS.length;
|
|
70
|
+
}
|
|
71
|
+
});
|
|
141
72
|
|
|
142
73
|
// src/packet-validator.ts
|
|
143
|
-
var MIN_VALIDATION_COMMANDS = 4;
|
|
144
|
-
var VALID_PACKET_TARGET_TYPES = ["feature", "scenario", "decision", "design", "e2e_test"];
|
|
145
74
|
function isPacketTargetType(type) {
|
|
146
75
|
return VALID_PACKET_TARGET_TYPES.includes(type);
|
|
147
76
|
}
|
|
@@ -154,7 +83,6 @@ function isPacketTargetTypeDynamic(type, schema) {
|
|
|
154
83
|
}
|
|
155
84
|
return isPacketTargetType(type);
|
|
156
85
|
}
|
|
157
|
-
var VALID_TARGET_TYPES = [...VALID_PACKET_TARGET_TYPES];
|
|
158
86
|
function validatePacket(packet, schema) {
|
|
159
87
|
const issues = [];
|
|
160
88
|
if (packet.schemaVersion !== "1.0") {
|
|
@@ -332,16 +260,6 @@ function validatePacket(packet, schema) {
|
|
|
332
260
|
const hasError = issues.some((i) => i.severity === "error");
|
|
333
261
|
return { ok: !hasError, issues };
|
|
334
262
|
}
|
|
335
|
-
var REQUIRED_SECTIONS = [
|
|
336
|
-
"## 1. \u5B9E\u73B0\u76EE\u6807",
|
|
337
|
-
"## 2. \u4E0A\u4E0B\u6587\u6E05\u5355\u6458\u8981",
|
|
338
|
-
"## 3. Required Baseline\uFF08\u5FC5\u8BFB\u5236\u54C1\uFF09",
|
|
339
|
-
"## 8. Implementation Blueprint Draft\uFF08\u5B9E\u73B0\u84DD\u56FE\u8349\u6848\uFF09",
|
|
340
|
-
"### 8.3 \u7EA6\u675F\u4E0E\u8FB9\u754C",
|
|
341
|
-
"### 8.4 \u9A8C\u8BC1\u547D\u4EE4",
|
|
342
|
-
"### 8.5 \u63A8\u8350\u5BA1\u9605\u987A\u5E8F",
|
|
343
|
-
"### 8.6 \u98CE\u9669\u68C0\u67E5\u6E05\u5355"
|
|
344
|
-
];
|
|
345
263
|
function validatePacketMarkdown(markdown) {
|
|
346
264
|
const issues = [];
|
|
347
265
|
for (const section of REQUIRED_SECTIONS) {
|
|
@@ -357,6 +275,26 @@ function validatePacketMarkdown(markdown) {
|
|
|
357
275
|
const hasError = issues.some((i) => i.severity === "error");
|
|
358
276
|
return { ok: !hasError, issues };
|
|
359
277
|
}
|
|
278
|
+
var MIN_VALIDATION_COMMANDS, VALID_PACKET_TARGET_TYPES, VALID_TARGET_TYPES, REQUIRED_SECTIONS;
|
|
279
|
+
var init_packet_validator = __esm({
|
|
280
|
+
"src/packet-validator.ts"() {
|
|
281
|
+
"use strict";
|
|
282
|
+
init_packet_constants();
|
|
283
|
+
MIN_VALIDATION_COMMANDS = 4;
|
|
284
|
+
VALID_PACKET_TARGET_TYPES = ["feature", "scenario", "decision", "design", "e2e_test"];
|
|
285
|
+
VALID_TARGET_TYPES = [...VALID_PACKET_TARGET_TYPES];
|
|
286
|
+
REQUIRED_SECTIONS = [
|
|
287
|
+
"## 1. \u5B9E\u73B0\u76EE\u6807",
|
|
288
|
+
"## 2. \u4E0A\u4E0B\u6587\u6E05\u5355\u6458\u8981",
|
|
289
|
+
"## 3. Required Baseline\uFF08\u5FC5\u8BFB\u5236\u54C1\uFF09",
|
|
290
|
+
"## 8. Implementation Blueprint Draft\uFF08\u5B9E\u73B0\u84DD\u56FE\u8349\u6848\uFF09",
|
|
291
|
+
"### 8.3 \u7EA6\u675F\u4E0E\u8FB9\u754C",
|
|
292
|
+
"### 8.4 \u9A8C\u8BC1\u547D\u4EE4",
|
|
293
|
+
"### 8.5 \u63A8\u8350\u5BA1\u9605\u987A\u5E8F",
|
|
294
|
+
"### 8.6 \u98CE\u9669\u68C0\u67E5\u6E05\u5355"
|
|
295
|
+
];
|
|
296
|
+
}
|
|
297
|
+
});
|
|
360
298
|
|
|
361
299
|
// src/glob-matcher.ts
|
|
362
300
|
function matchesRunnerGlob(filePath, pattern) {
|
|
@@ -396,6 +334,11 @@ function normalizeGlobValue(value) {
|
|
|
396
334
|
function escapeRegexCharacter(character) {
|
|
397
335
|
return "\\^$+?.()|{}[]".includes(character) ? String.fromCharCode(92) + character : character;
|
|
398
336
|
}
|
|
337
|
+
var init_glob_matcher = __esm({
|
|
338
|
+
"src/glob-matcher.ts"() {
|
|
339
|
+
"use strict";
|
|
340
|
+
}
|
|
341
|
+
});
|
|
399
342
|
|
|
400
343
|
// src/target-selector.ts
|
|
401
344
|
function parseTargetSelector(value) {
|
|
@@ -405,14 +348,6 @@ function parseTargetSelector(value) {
|
|
|
405
348
|
}
|
|
406
349
|
return { type: value.slice(0, separator), id: value.slice(separator + 1) };
|
|
407
350
|
}
|
|
408
|
-
var LEGACY_FLAGS = ["feature", "scenario", "decision", "design", "e2e-test"];
|
|
409
|
-
var LEGACY_FLAG_TO_TYPE = {
|
|
410
|
-
feature: "feature",
|
|
411
|
-
scenario: "scenario",
|
|
412
|
-
decision: "decision",
|
|
413
|
-
design: "design",
|
|
414
|
-
"e2e-test": "e2e_test"
|
|
415
|
-
};
|
|
416
351
|
function resolveCliTarget(flags, schema) {
|
|
417
352
|
const hasTarget = typeof flags.target === "string";
|
|
418
353
|
const legacyEntries = LEGACY_FLAGS.filter((flag) => typeof flags[flag] === "string").map((flag) => flag);
|
|
@@ -445,14 +380,23 @@ function resolveCliTarget(flags, schema) {
|
|
|
445
380
|
"\u672A\u6307\u5B9A target\uFF1A\u8BF7\u4F7F\u7528 --target <type>:<id> \u6216 --feature/--scenario/--decision/--design/--e2e-test <id>"
|
|
446
381
|
);
|
|
447
382
|
}
|
|
383
|
+
var LEGACY_FLAGS, LEGACY_FLAG_TO_TYPE;
|
|
384
|
+
var init_target_selector = __esm({
|
|
385
|
+
"src/target-selector.ts"() {
|
|
386
|
+
"use strict";
|
|
387
|
+
init_index();
|
|
388
|
+
LEGACY_FLAGS = ["feature", "scenario", "decision", "design", "e2e-test"];
|
|
389
|
+
LEGACY_FLAG_TO_TYPE = {
|
|
390
|
+
feature: "feature",
|
|
391
|
+
scenario: "scenario",
|
|
392
|
+
decision: "decision",
|
|
393
|
+
design: "design",
|
|
394
|
+
"e2e-test": "e2e_test"
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
});
|
|
448
398
|
|
|
449
399
|
// src/packet-assembler.ts
|
|
450
|
-
var DEFAULT_VALIDATION_COMMANDS = [
|
|
451
|
-
"pnpm build",
|
|
452
|
-
"pnpm test",
|
|
453
|
-
"artifact-graph validate --root . --warning-only",
|
|
454
|
-
"artifact-graph version-lock audit --root . --strict-missing-lock"
|
|
455
|
-
];
|
|
456
400
|
function toPacketItem(item) {
|
|
457
401
|
const pi = {
|
|
458
402
|
path: item.path,
|
|
@@ -589,12 +533,6 @@ function renderContextCategory(cat, opts) {
|
|
|
589
533
|
}
|
|
590
534
|
return lines;
|
|
591
535
|
}
|
|
592
|
-
var COMMAND_COMMENTS = {
|
|
593
|
-
"pnpm build": "\u6784\u5EFA\u9879\u76EE",
|
|
594
|
-
"pnpm test": "\u8FD0\u884C\u9879\u76EE\u6D4B\u8BD5",
|
|
595
|
-
"artifact-graph validate --root . --warning-only": "\u5236\u54C1\u94FE\u4E00\u81F4\u6027\u6821\u9A8C\uFF08warning-only\uFF09",
|
|
596
|
-
"artifact-graph version-lock audit --root . --strict-missing-lock": "\u5236\u54C1\u94FE\u7248\u672C\u9501\u4E25\u683C\u5BA1\u8BA1"
|
|
597
|
-
};
|
|
598
536
|
function classifyCategories(manifest) {
|
|
599
537
|
let baseline = { category: "baseline", total: 0, items: [] };
|
|
600
538
|
const direct = [];
|
|
@@ -891,12 +829,27 @@ function renderPacketMarkdown(packet) {
|
|
|
891
829
|
lines.push("> \u63D0\u793A\uFF1A\u4F7F\u7528 Ctrl+F \u641C\u7D22 `[\u5FC5\u8BFB]` \u5FEB\u901F\u5B9A\u4F4D\u5FC5\u8BFB\u5236\u54C1\u3002");
|
|
892
830
|
return lines.join("\n");
|
|
893
831
|
}
|
|
832
|
+
var DEFAULT_VALIDATION_COMMANDS, COMMAND_COMMENTS;
|
|
833
|
+
var init_packet_assembler = __esm({
|
|
834
|
+
"src/packet-assembler.ts"() {
|
|
835
|
+
"use strict";
|
|
836
|
+
init_packet_constants();
|
|
837
|
+
DEFAULT_VALIDATION_COMMANDS = [
|
|
838
|
+
"pnpm build",
|
|
839
|
+
"pnpm test",
|
|
840
|
+
"artifact-graph validate --root . --warning-only",
|
|
841
|
+
"artifact-graph version-lock audit --root . --strict-missing-lock"
|
|
842
|
+
];
|
|
843
|
+
COMMAND_COMMENTS = {
|
|
844
|
+
"pnpm build": "\u6784\u5EFA\u9879\u76EE",
|
|
845
|
+
"pnpm test": "\u8FD0\u884C\u9879\u76EE\u6D4B\u8BD5",
|
|
846
|
+
"artifact-graph validate --root . --warning-only": "\u5236\u54C1\u94FE\u4E00\u81F4\u6027\u6821\u9A8C\uFF08warning-only\uFF09",
|
|
847
|
+
"artifact-graph version-lock audit --root . --strict-missing-lock": "\u5236\u54C1\u94FE\u7248\u672C\u9501\u4E25\u683C\u5BA1\u8BA1"
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
});
|
|
894
851
|
|
|
895
852
|
// src/packet-audit.ts
|
|
896
|
-
var import_promises = require("fs/promises");
|
|
897
|
-
var import_node_path = require("path");
|
|
898
|
-
var VALID_TYPES = new Set(VALID_PACKET_TARGET_TYPES);
|
|
899
|
-
var VALID_TYPES_LABEL = VALID_PACKET_TARGET_TYPES.join(", ");
|
|
900
853
|
function parseTargetsFile(content, schema) {
|
|
901
854
|
const validTypes = schema ? new Set(getTargetArtifactTypes(schema)) : VALID_TYPES;
|
|
902
855
|
const validTypesLabel = [...validTypes].join(", ");
|
|
@@ -1094,20 +1047,23 @@ async function discoverAndAuditPackets(root, options) {
|
|
|
1094
1047
|
universalBaseline: effectiveBaseline
|
|
1095
1048
|
}, graph);
|
|
1096
1049
|
}
|
|
1050
|
+
var import_promises, import_node_path, VALID_TYPES, VALID_TYPES_LABEL;
|
|
1051
|
+
var init_packet_audit = __esm({
|
|
1052
|
+
"src/packet-audit.ts"() {
|
|
1053
|
+
"use strict";
|
|
1054
|
+
init_index();
|
|
1055
|
+
init_packet_validator();
|
|
1056
|
+
import_promises = require("fs/promises");
|
|
1057
|
+
import_node_path = require("path");
|
|
1058
|
+
VALID_TYPES = new Set(VALID_PACKET_TARGET_TYPES);
|
|
1059
|
+
VALID_TYPES_LABEL = VALID_PACKET_TARGET_TYPES.join(", ");
|
|
1060
|
+
}
|
|
1061
|
+
});
|
|
1097
1062
|
|
|
1098
1063
|
// src/packet-prompt.ts
|
|
1099
|
-
var TYPE_CLI_FLAG = {
|
|
1100
|
-
feature: "feature",
|
|
1101
|
-
scenario: "scenario",
|
|
1102
|
-
decision: "decision",
|
|
1103
|
-
design: "design",
|
|
1104
|
-
e2e_test: "e2e-test"
|
|
1105
|
-
};
|
|
1106
1064
|
function cliFlag(type) {
|
|
1107
1065
|
return TYPE_CLI_FLAG[type] ?? type;
|
|
1108
1066
|
}
|
|
1109
|
-
var DEFAULT_MAX_CHARS = 4e3;
|
|
1110
|
-
var MIN_PROMPT_CHARS = 320;
|
|
1111
1067
|
function renderPromptHeader(packet) {
|
|
1112
1068
|
const t = packet.target;
|
|
1113
1069
|
const typeLabelMap = { feature: "\u529F\u80FD", scenario: "\u573A\u666F", decision: "\u51B3\u7B56", design: "\u8BBE\u8BA1\u89C4\u683C", e2e_test: "E2E \u6D4B\u8BD5" };
|
|
@@ -1331,52 +1287,24 @@ function renderPacketPrompt(packet, options) {
|
|
|
1331
1287
|
minRequired: absMinText.length
|
|
1332
1288
|
};
|
|
1333
1289
|
}
|
|
1290
|
+
var TYPE_CLI_FLAG, DEFAULT_MAX_CHARS, MIN_PROMPT_CHARS;
|
|
1291
|
+
var init_packet_prompt = __esm({
|
|
1292
|
+
"src/packet-prompt.ts"() {
|
|
1293
|
+
"use strict";
|
|
1294
|
+
init_packet_constants();
|
|
1295
|
+
TYPE_CLI_FLAG = {
|
|
1296
|
+
feature: "feature",
|
|
1297
|
+
scenario: "scenario",
|
|
1298
|
+
decision: "decision",
|
|
1299
|
+
design: "design",
|
|
1300
|
+
e2e_test: "e2e-test"
|
|
1301
|
+
};
|
|
1302
|
+
DEFAULT_MAX_CHARS = 4e3;
|
|
1303
|
+
MIN_PROMPT_CHARS = 320;
|
|
1304
|
+
}
|
|
1305
|
+
});
|
|
1334
1306
|
|
|
1335
1307
|
// src/packet-prompt-validator.ts
|
|
1336
|
-
var REQUIRED_CHECKS = [
|
|
1337
|
-
{
|
|
1338
|
-
code: "PP-001",
|
|
1339
|
-
description: "\u76EE\u6807\u4FE1\u606F",
|
|
1340
|
-
test: (t) => /实现任务/.test(t),
|
|
1341
|
-
severity: "error"
|
|
1342
|
-
},
|
|
1343
|
-
{
|
|
1344
|
-
code: "PP-002",
|
|
1345
|
-
description: "packet \u547D\u4EE4\u6216\u6765\u6E90\u5F15\u7528",
|
|
1346
|
-
test: (t) => /packet\s+--root/.test(t) || /packet\.json/.test(t) || /artifact-graph\s+packet/.test(t),
|
|
1347
|
-
severity: "error"
|
|
1348
|
-
},
|
|
1349
|
-
{
|
|
1350
|
-
code: "PP-003",
|
|
1351
|
-
description: "\u9A8C\u8BC1\u547D\u4EE4",
|
|
1352
|
-
test: (t) => /验证命令/.test(t) || /artifact-graph\s+(build|test)/.test(t),
|
|
1353
|
-
severity: "error"
|
|
1354
|
-
},
|
|
1355
|
-
{
|
|
1356
|
-
code: "PP-004",
|
|
1357
|
-
description: "\u63D0\u4EA4\u8981\u6C42",
|
|
1358
|
-
test: (t) => /提交要求/.test(t) || /git\s+diff\s+--check/.test(t),
|
|
1359
|
-
severity: "error"
|
|
1360
|
-
},
|
|
1361
|
-
{
|
|
1362
|
-
code: "PP-005",
|
|
1363
|
-
description: "\u7981\u6B62\u4E8B\u9879",
|
|
1364
|
-
test: (t) => /禁止事项/.test(t),
|
|
1365
|
-
severity: "error"
|
|
1366
|
-
},
|
|
1367
|
-
{
|
|
1368
|
-
code: "PP-006",
|
|
1369
|
-
description: "\u4E0D\u5F97\u56DE\u9000\u89C4\u5219",
|
|
1370
|
-
test: (t) => /不得回退/.test(t) || /不要回退/.test(t) || /no[- ]?revert/i.test(t),
|
|
1371
|
-
severity: "error"
|
|
1372
|
-
},
|
|
1373
|
-
{
|
|
1374
|
-
code: "PP-007",
|
|
1375
|
-
description: "SEC severity \u89C4\u5219",
|
|
1376
|
-
test: (t) => /SEC\s+severity/.test(t) || /severity/.test(t) && /降级/.test(t),
|
|
1377
|
-
severity: "error"
|
|
1378
|
-
}
|
|
1379
|
-
];
|
|
1380
1308
|
function checkChineseDominance(text) {
|
|
1381
1309
|
const textOutsideCode = text.replace(/```[\s\S]*?```/g, "");
|
|
1382
1310
|
const cleanText = textOutsideCode.replace(/^[#>]+ .*$/gm, "").replace(/\[.*?\]\(.*?\)/g, "");
|
|
@@ -1411,15 +1339,58 @@ function validatePacketPrompt(prompt) {
|
|
|
1411
1339
|
const hasErrors = issues.some((i) => i.severity === "error");
|
|
1412
1340
|
return { ok: !hasErrors, issues };
|
|
1413
1341
|
}
|
|
1342
|
+
var REQUIRED_CHECKS;
|
|
1343
|
+
var init_packet_prompt_validator = __esm({
|
|
1344
|
+
"src/packet-prompt-validator.ts"() {
|
|
1345
|
+
"use strict";
|
|
1346
|
+
REQUIRED_CHECKS = [
|
|
1347
|
+
{
|
|
1348
|
+
code: "PP-001",
|
|
1349
|
+
description: "\u76EE\u6807\u4FE1\u606F",
|
|
1350
|
+
test: (t) => /实现任务/.test(t),
|
|
1351
|
+
severity: "error"
|
|
1352
|
+
},
|
|
1353
|
+
{
|
|
1354
|
+
code: "PP-002",
|
|
1355
|
+
description: "packet \u547D\u4EE4\u6216\u6765\u6E90\u5F15\u7528",
|
|
1356
|
+
test: (t) => /packet\s+--root/.test(t) || /packet\.json/.test(t) || /artifact-graph\s+packet/.test(t),
|
|
1357
|
+
severity: "error"
|
|
1358
|
+
},
|
|
1359
|
+
{
|
|
1360
|
+
code: "PP-003",
|
|
1361
|
+
description: "\u9A8C\u8BC1\u547D\u4EE4",
|
|
1362
|
+
test: (t) => /验证命令/.test(t) || /artifact-graph\s+(build|test)/.test(t),
|
|
1363
|
+
severity: "error"
|
|
1364
|
+
},
|
|
1365
|
+
{
|
|
1366
|
+
code: "PP-004",
|
|
1367
|
+
description: "\u63D0\u4EA4\u8981\u6C42",
|
|
1368
|
+
test: (t) => /提交要求/.test(t) || /git\s+diff\s+--check/.test(t),
|
|
1369
|
+
severity: "error"
|
|
1370
|
+
},
|
|
1371
|
+
{
|
|
1372
|
+
code: "PP-005",
|
|
1373
|
+
description: "\u7981\u6B62\u4E8B\u9879",
|
|
1374
|
+
test: (t) => /禁止事项/.test(t),
|
|
1375
|
+
severity: "error"
|
|
1376
|
+
},
|
|
1377
|
+
{
|
|
1378
|
+
code: "PP-006",
|
|
1379
|
+
description: "\u4E0D\u5F97\u56DE\u9000\u89C4\u5219",
|
|
1380
|
+
test: (t) => /不得回退/.test(t) || /不要回退/.test(t) || /no[- ]?revert/i.test(t),
|
|
1381
|
+
severity: "error"
|
|
1382
|
+
},
|
|
1383
|
+
{
|
|
1384
|
+
code: "PP-007",
|
|
1385
|
+
description: "SEC severity \u89C4\u5219",
|
|
1386
|
+
test: (t) => /SEC\s+severity/.test(t) || /severity/.test(t) && /降级/.test(t),
|
|
1387
|
+
severity: "error"
|
|
1388
|
+
}
|
|
1389
|
+
];
|
|
1390
|
+
}
|
|
1391
|
+
});
|
|
1414
1392
|
|
|
1415
1393
|
// src/versioned-traceability.ts
|
|
1416
|
-
var import_node_crypto = require("crypto");
|
|
1417
|
-
var import_node_fs = require("fs");
|
|
1418
|
-
var import_promises2 = require("fs/promises");
|
|
1419
|
-
var import_node_path2 = require("path");
|
|
1420
|
-
var VERSION_LOCK_PATH = "artifacts/traceability-version-lock.json";
|
|
1421
|
-
var VERSION_INDEX_SCHEMA_VERSION = "1.0";
|
|
1422
|
-
var VERSION_LOCK_SCHEMA_VERSION = "1.0";
|
|
1423
1394
|
async function buildVersionIndex(root, graph) {
|
|
1424
1395
|
const scannedGraph = graph ?? await scanArtifacts(root);
|
|
1425
1396
|
const hashCache = /* @__PURE__ */ new Map();
|
|
@@ -2219,34 +2190,23 @@ async function isFileActiveInRunner(root, filePath, runner) {
|
|
|
2219
2190
|
function sortUnique(items) {
|
|
2220
2191
|
return [...new Set(items)].sort((left, right) => left.localeCompare(right));
|
|
2221
2192
|
}
|
|
2193
|
+
var import_node_crypto, import_node_fs, import_promises2, import_node_path2, VERSION_LOCK_PATH, VERSION_INDEX_SCHEMA_VERSION, VERSION_LOCK_SCHEMA_VERSION;
|
|
2194
|
+
var init_versioned_traceability = __esm({
|
|
2195
|
+
"src/versioned-traceability.ts"() {
|
|
2196
|
+
"use strict";
|
|
2197
|
+
import_node_crypto = require("crypto");
|
|
2198
|
+
import_node_fs = require("fs");
|
|
2199
|
+
import_promises2 = require("fs/promises");
|
|
2200
|
+
import_node_path2 = require("path");
|
|
2201
|
+
init_index();
|
|
2202
|
+
init_glob_matcher();
|
|
2203
|
+
VERSION_LOCK_PATH = "artifacts/traceability-version-lock.json";
|
|
2204
|
+
VERSION_INDEX_SCHEMA_VERSION = "1.0";
|
|
2205
|
+
VERSION_LOCK_SCHEMA_VERSION = "1.0";
|
|
2206
|
+
}
|
|
2207
|
+
});
|
|
2222
2208
|
|
|
2223
2209
|
// src/cli-resolver.ts
|
|
2224
|
-
var import_node_child_process = require("child_process");
|
|
2225
|
-
var import_node_fs2 = require("fs");
|
|
2226
|
-
var import_promises3 = require("fs/promises");
|
|
2227
|
-
var import_node_path3 = require("path");
|
|
2228
|
-
var import_node_util = require("util");
|
|
2229
|
-
var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
|
|
2230
|
-
var KNOWN_COMMANDS = [
|
|
2231
|
-
"init",
|
|
2232
|
-
"scan",
|
|
2233
|
-
"validate",
|
|
2234
|
-
"query",
|
|
2235
|
-
"context",
|
|
2236
|
-
"packet",
|
|
2237
|
-
"packet-prompt",
|
|
2238
|
-
"packet-audit",
|
|
2239
|
-
"packet-prompt-audit",
|
|
2240
|
-
"version-index",
|
|
2241
|
-
"version-lock audit",
|
|
2242
|
-
"version-lock update",
|
|
2243
|
-
"version-lock bootstrap",
|
|
2244
|
-
"version-lock refresh",
|
|
2245
|
-
"trace-version",
|
|
2246
|
-
"next-id",
|
|
2247
|
-
"render",
|
|
2248
|
-
"doctor"
|
|
2249
|
-
];
|
|
2250
2210
|
async function resolveArtifactGraphCli(root, options = {}) {
|
|
2251
2211
|
const pathCli = await findCommandOnPath("artifact-graph");
|
|
2252
2212
|
const legacyCliPath = options.projectCliPath ?? process.env.ARTIFACT_GRAPH_LEGACY_CLI;
|
|
@@ -2382,11 +2342,41 @@ function isNodeCompatible(version) {
|
|
|
2382
2342
|
const major = Number(version.split(".")[0]);
|
|
2383
2343
|
return Number.isFinite(major) && major >= 22;
|
|
2384
2344
|
}
|
|
2345
|
+
var import_node_child_process, import_node_fs2, import_promises3, import_node_path3, import_node_util, execFileAsync, KNOWN_COMMANDS;
|
|
2346
|
+
var init_cli_resolver = __esm({
|
|
2347
|
+
"src/cli-resolver.ts"() {
|
|
2348
|
+
"use strict";
|
|
2349
|
+
import_node_child_process = require("child_process");
|
|
2350
|
+
import_node_fs2 = require("fs");
|
|
2351
|
+
import_promises3 = require("fs/promises");
|
|
2352
|
+
import_node_path3 = require("path");
|
|
2353
|
+
import_node_util = require("util");
|
|
2354
|
+
init_versioned_traceability();
|
|
2355
|
+
execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
|
|
2356
|
+
KNOWN_COMMANDS = [
|
|
2357
|
+
"init",
|
|
2358
|
+
"scan",
|
|
2359
|
+
"validate",
|
|
2360
|
+
"query",
|
|
2361
|
+
"context",
|
|
2362
|
+
"packet",
|
|
2363
|
+
"packet-prompt",
|
|
2364
|
+
"packet-audit",
|
|
2365
|
+
"packet-prompt-audit",
|
|
2366
|
+
"version-index",
|
|
2367
|
+
"version-lock audit",
|
|
2368
|
+
"version-lock update",
|
|
2369
|
+
"version-lock bootstrap",
|
|
2370
|
+
"version-lock refresh",
|
|
2371
|
+
"trace-version",
|
|
2372
|
+
"next-id",
|
|
2373
|
+
"render",
|
|
2374
|
+
"doctor"
|
|
2375
|
+
];
|
|
2376
|
+
}
|
|
2377
|
+
});
|
|
2385
2378
|
|
|
2386
2379
|
// src/git-changes.ts
|
|
2387
|
-
var import_node_child_process2 = require("child_process");
|
|
2388
|
-
var import_node_util2 = require("util");
|
|
2389
|
-
var execFileAsync2 = (0, import_node_util2.promisify)(import_node_child_process2.execFile);
|
|
2390
2380
|
async function collectChangedPaths(root, options) {
|
|
2391
2381
|
const args = gitDiffArgs(options);
|
|
2392
2382
|
let stdout;
|
|
@@ -2396,8 +2386,10 @@ async function collectChangedPaths(root, options) {
|
|
|
2396
2386
|
const message = error instanceof Error ? error.message : String(error);
|
|
2397
2387
|
throw new Error(`Unable to collect git changed paths (${options.mode}): ${message}`);
|
|
2398
2388
|
}
|
|
2399
|
-
const
|
|
2400
|
-
const
|
|
2389
|
+
const trackedChangedPaths = normalizeGitPathList(stdout);
|
|
2390
|
+
const untrackedPaths = options.mode === "worktree" || options.mode === "staged" ? await collectUntrackedPaths(root) : [];
|
|
2391
|
+
const changedPaths = options.mode === "worktree" ? sortUnique2([...trackedChangedPaths, ...untrackedPaths]) : trackedChangedPaths;
|
|
2392
|
+
const unstagedPaths = options.mode === "staged" ? sortUnique2([...await collectUnstagedPaths(root), ...untrackedPaths]) : [];
|
|
2401
2393
|
const stagedUnstagedConflictPaths = options.mode === "staged" ? intersect(changedPaths, unstagedPaths) : [];
|
|
2402
2394
|
return {
|
|
2403
2395
|
root,
|
|
@@ -2429,6 +2421,14 @@ async function collectUnstagedPaths(root) {
|
|
|
2429
2421
|
return [];
|
|
2430
2422
|
}
|
|
2431
2423
|
}
|
|
2424
|
+
async function collectUntrackedPaths(root) {
|
|
2425
|
+
try {
|
|
2426
|
+
const { stdout } = await execFileAsync2("git", ["ls-files", "--others", "--exclude-standard"], { cwd: root });
|
|
2427
|
+
return normalizeGitPathList(stdout);
|
|
2428
|
+
} catch {
|
|
2429
|
+
return [];
|
|
2430
|
+
}
|
|
2431
|
+
}
|
|
2432
2432
|
function normalizeGitPathList(stdout) {
|
|
2433
2433
|
return sortUnique2(stdout.split(/\r?\n/).map((line) => line.trim().replace(/\\/g, "/").replace(/^\.\//, "")).filter((line) => line.length > 0 && !line.startsWith("../") && !line.startsWith("/")));
|
|
2434
2434
|
}
|
|
@@ -2439,12 +2439,17 @@ function intersect(left, right) {
|
|
|
2439
2439
|
function sortUnique2(items) {
|
|
2440
2440
|
return [...new Set(items)].sort((left, right) => left.localeCompare(right));
|
|
2441
2441
|
}
|
|
2442
|
+
var import_node_child_process2, import_node_util2, execFileAsync2;
|
|
2443
|
+
var init_git_changes = __esm({
|
|
2444
|
+
"src/git-changes.ts"() {
|
|
2445
|
+
"use strict";
|
|
2446
|
+
import_node_child_process2 = require("child_process");
|
|
2447
|
+
import_node_util2 = require("util");
|
|
2448
|
+
execFileAsync2 = (0, import_node_util2.promisify)(import_node_child_process2.execFile);
|
|
2449
|
+
}
|
|
2450
|
+
});
|
|
2442
2451
|
|
|
2443
2452
|
// src/git-hook-path.ts
|
|
2444
|
-
var import_node_child_process3 = require("child_process");
|
|
2445
|
-
var import_node_path4 = require("path");
|
|
2446
|
-
var import_node_util3 = require("util");
|
|
2447
|
-
var execFileAsync3 = (0, import_node_util3.promisify)(import_node_child_process3.execFile);
|
|
2448
2453
|
async function resolveGitHookPath(root, hookName) {
|
|
2449
2454
|
const { stdout } = await execFileAsync3("git", [
|
|
2450
2455
|
"-C",
|
|
@@ -2457,74 +2462,18 @@ async function resolveGitHookPath(root, hookName) {
|
|
|
2457
2462
|
if (!value) throw new Error(`Git returned an empty hook path for ${hookName}`);
|
|
2458
2463
|
return (0, import_node_path4.isAbsolute)(value) ? (0, import_node_path4.resolve)(value) : (0, import_node_path4.resolve)(root, value);
|
|
2459
2464
|
}
|
|
2465
|
+
var import_node_child_process3, import_node_path4, import_node_util3, execFileAsync3;
|
|
2466
|
+
var init_git_hook_path = __esm({
|
|
2467
|
+
"src/git-hook-path.ts"() {
|
|
2468
|
+
"use strict";
|
|
2469
|
+
import_node_child_process3 = require("child_process");
|
|
2470
|
+
import_node_path4 = require("path");
|
|
2471
|
+
import_node_util3 = require("util");
|
|
2472
|
+
execFileAsync3 = (0, import_node_util3.promisify)(import_node_child_process3.execFile);
|
|
2473
|
+
}
|
|
2474
|
+
});
|
|
2460
2475
|
|
|
2461
2476
|
// src/hook-installer.ts
|
|
2462
|
-
var import_node_fs3 = require("fs");
|
|
2463
|
-
var import_node_crypto2 = require("crypto");
|
|
2464
|
-
var import_promises4 = require("fs/promises");
|
|
2465
|
-
var import_node_path5 = require("path");
|
|
2466
|
-
var DEFAULT_MARKER_ID = "artifact-chain-assistant";
|
|
2467
|
-
var MANAGED_STATE_PREFIX = "# artifact-chain-assistant managed state v1:";
|
|
2468
|
-
var UnsupportedHookInterpreterError = class extends Error {
|
|
2469
|
-
constructor(hookPath, shebang) {
|
|
2470
|
-
super(
|
|
2471
|
-
`Refusing to modify non-shell Git hook ${hookPath} (${shebang || "missing shebang"}). Call the artifact-chain hook from the existing hook explicitly.`
|
|
2472
|
-
);
|
|
2473
|
-
this.hookPath = hookPath;
|
|
2474
|
-
this.shebang = shebang;
|
|
2475
|
-
}
|
|
2476
|
-
hookPath;
|
|
2477
|
-
shebang;
|
|
2478
|
-
code = "UNSUPPORTED_HOOK_INTERPRETER";
|
|
2479
|
-
};
|
|
2480
|
-
var SymlinkHookUnsupportedError = class extends Error {
|
|
2481
|
-
constructor(hookPath, linkTarget) {
|
|
2482
|
-
super(
|
|
2483
|
-
`SYMLINK_HOOK_UNSUPPORTED: Refusing to modify Git hook symlink ${hookPath} -> ${linkTarget}. Integrate the artifact-graph commands manually in the symlink target or replace the link yourself.`
|
|
2484
|
-
);
|
|
2485
|
-
this.hookPath = hookPath;
|
|
2486
|
-
this.linkTarget = linkTarget;
|
|
2487
|
-
}
|
|
2488
|
-
hookPath;
|
|
2489
|
-
linkTarget;
|
|
2490
|
-
code = "SYMLINK_HOOK_UNSUPPORTED";
|
|
2491
|
-
};
|
|
2492
|
-
var UnsupportedHookTypeError = class extends Error {
|
|
2493
|
-
constructor(hookPath) {
|
|
2494
|
-
super(`UNSUPPORTED_HOOK_TYPE: Git hook path ${hookPath} is not a regular file. Resolve it manually before retrying.`);
|
|
2495
|
-
this.hookPath = hookPath;
|
|
2496
|
-
}
|
|
2497
|
-
hookPath;
|
|
2498
|
-
code = "UNSUPPORTED_HOOK_TYPE";
|
|
2499
|
-
};
|
|
2500
|
-
var InvalidManagedHookStateError = class extends Error {
|
|
2501
|
-
constructor(hookPath, detail) {
|
|
2502
|
-
super(`INVALID_MANAGED_HOOK_STATE: Cannot safely update ${hookPath}: ${detail}`);
|
|
2503
|
-
this.hookPath = hookPath;
|
|
2504
|
-
}
|
|
2505
|
-
hookPath;
|
|
2506
|
-
code = "INVALID_MANAGED_HOOK_STATE";
|
|
2507
|
-
};
|
|
2508
|
-
var ConcurrentHookModificationError = class extends Error {
|
|
2509
|
-
constructor(hookPath) {
|
|
2510
|
-
super(`Refusing to overwrite concurrently modified Git hook ${hookPath}. Retry the operation after reviewing the hook.`);
|
|
2511
|
-
this.hookPath = hookPath;
|
|
2512
|
-
}
|
|
2513
|
-
hookPath;
|
|
2514
|
-
code = "CONCURRENT_HOOK_MODIFICATION";
|
|
2515
|
-
};
|
|
2516
|
-
var HookTransactionRollbackError = class extends Error {
|
|
2517
|
-
constructor(operationError, rollbackErrors) {
|
|
2518
|
-
super(
|
|
2519
|
-
`HOOK_TRANSACTION_ROLLBACK_FAILED: Hook update failed and ${rollbackErrors.length} rollback operation(s) also failed: ` + rollbackErrors.map(({ hookPath, error }) => `${hookPath}: ${error.message}`).join("; ")
|
|
2520
|
-
);
|
|
2521
|
-
this.operationError = operationError;
|
|
2522
|
-
this.rollbackErrors = rollbackErrors;
|
|
2523
|
-
}
|
|
2524
|
-
operationError;
|
|
2525
|
-
rollbackErrors;
|
|
2526
|
-
code = "HOOK_TRANSACTION_ROLLBACK_FAILED";
|
|
2527
|
-
};
|
|
2528
2477
|
function detectHookInterpreter(content) {
|
|
2529
2478
|
if (content.trim().length === 0) {
|
|
2530
2479
|
return "empty";
|
|
@@ -2882,44 +2831,80 @@ function findManagedRange(content, begin, end) {
|
|
|
2882
2831
|
end: endLine < 0 ? content.length : endLine + 1
|
|
2883
2832
|
};
|
|
2884
2833
|
}
|
|
2834
|
+
var import_node_fs3, import_node_crypto2, import_promises4, import_node_path5, DEFAULT_MARKER_ID, MANAGED_STATE_PREFIX, UnsupportedHookInterpreterError, SymlinkHookUnsupportedError, UnsupportedHookTypeError, InvalidManagedHookStateError, ConcurrentHookModificationError, HookTransactionRollbackError;
|
|
2835
|
+
var init_hook_installer = __esm({
|
|
2836
|
+
"src/hook-installer.ts"() {
|
|
2837
|
+
"use strict";
|
|
2838
|
+
import_node_fs3 = require("fs");
|
|
2839
|
+
import_node_crypto2 = require("crypto");
|
|
2840
|
+
import_promises4 = require("fs/promises");
|
|
2841
|
+
import_node_path5 = require("path");
|
|
2842
|
+
DEFAULT_MARKER_ID = "artifact-chain-assistant";
|
|
2843
|
+
MANAGED_STATE_PREFIX = "# artifact-chain-assistant managed state v1:";
|
|
2844
|
+
UnsupportedHookInterpreterError = class extends Error {
|
|
2845
|
+
constructor(hookPath, shebang) {
|
|
2846
|
+
super(
|
|
2847
|
+
`Refusing to modify non-shell Git hook ${hookPath} (${shebang || "missing shebang"}). Call the artifact-chain hook from the existing hook explicitly.`
|
|
2848
|
+
);
|
|
2849
|
+
this.hookPath = hookPath;
|
|
2850
|
+
this.shebang = shebang;
|
|
2851
|
+
}
|
|
2852
|
+
hookPath;
|
|
2853
|
+
shebang;
|
|
2854
|
+
code = "UNSUPPORTED_HOOK_INTERPRETER";
|
|
2855
|
+
};
|
|
2856
|
+
SymlinkHookUnsupportedError = class extends Error {
|
|
2857
|
+
constructor(hookPath, linkTarget) {
|
|
2858
|
+
super(
|
|
2859
|
+
`SYMLINK_HOOK_UNSUPPORTED: Refusing to modify Git hook symlink ${hookPath} -> ${linkTarget}. Integrate the artifact-graph commands manually in the symlink target or replace the link yourself.`
|
|
2860
|
+
);
|
|
2861
|
+
this.hookPath = hookPath;
|
|
2862
|
+
this.linkTarget = linkTarget;
|
|
2863
|
+
}
|
|
2864
|
+
hookPath;
|
|
2865
|
+
linkTarget;
|
|
2866
|
+
code = "SYMLINK_HOOK_UNSUPPORTED";
|
|
2867
|
+
};
|
|
2868
|
+
UnsupportedHookTypeError = class extends Error {
|
|
2869
|
+
constructor(hookPath) {
|
|
2870
|
+
super(`UNSUPPORTED_HOOK_TYPE: Git hook path ${hookPath} is not a regular file. Resolve it manually before retrying.`);
|
|
2871
|
+
this.hookPath = hookPath;
|
|
2872
|
+
}
|
|
2873
|
+
hookPath;
|
|
2874
|
+
code = "UNSUPPORTED_HOOK_TYPE";
|
|
2875
|
+
};
|
|
2876
|
+
InvalidManagedHookStateError = class extends Error {
|
|
2877
|
+
constructor(hookPath, detail) {
|
|
2878
|
+
super(`INVALID_MANAGED_HOOK_STATE: Cannot safely update ${hookPath}: ${detail}`);
|
|
2879
|
+
this.hookPath = hookPath;
|
|
2880
|
+
}
|
|
2881
|
+
hookPath;
|
|
2882
|
+
code = "INVALID_MANAGED_HOOK_STATE";
|
|
2883
|
+
};
|
|
2884
|
+
ConcurrentHookModificationError = class extends Error {
|
|
2885
|
+
constructor(hookPath) {
|
|
2886
|
+
super(`Refusing to overwrite concurrently modified Git hook ${hookPath}. Retry the operation after reviewing the hook.`);
|
|
2887
|
+
this.hookPath = hookPath;
|
|
2888
|
+
}
|
|
2889
|
+
hookPath;
|
|
2890
|
+
code = "CONCURRENT_HOOK_MODIFICATION";
|
|
2891
|
+
};
|
|
2892
|
+
HookTransactionRollbackError = class extends Error {
|
|
2893
|
+
constructor(operationError, rollbackErrors) {
|
|
2894
|
+
super(
|
|
2895
|
+
`HOOK_TRANSACTION_ROLLBACK_FAILED: Hook update failed and ${rollbackErrors.length} rollback operation(s) also failed: ` + rollbackErrors.map(({ hookPath, error }) => `${hookPath}: ${error.message}`).join("; ")
|
|
2896
|
+
);
|
|
2897
|
+
this.operationError = operationError;
|
|
2898
|
+
this.rollbackErrors = rollbackErrors;
|
|
2899
|
+
}
|
|
2900
|
+
operationError;
|
|
2901
|
+
rollbackErrors;
|
|
2902
|
+
code = "HOOK_TRANSACTION_ROLLBACK_FAILED";
|
|
2903
|
+
};
|
|
2904
|
+
}
|
|
2905
|
+
});
|
|
2885
2906
|
|
|
2886
2907
|
// src/review-result-validator.ts
|
|
2887
|
-
var VALID_STATUSES = /* @__PURE__ */ new Set([
|
|
2888
|
-
"SUCCEEDED",
|
|
2889
|
-
"FAILED",
|
|
2890
|
-
"BLOCKED",
|
|
2891
|
-
"NEEDS_INPUT",
|
|
2892
|
-
"SKIPPED"
|
|
2893
|
-
]);
|
|
2894
|
-
var VALID_DECISIONS = /* @__PURE__ */ new Set([
|
|
2895
|
-
"PASS",
|
|
2896
|
-
"FAIL",
|
|
2897
|
-
"PASS_WITH_RESIDUAL_MINOR",
|
|
2898
|
-
"BLOCKED",
|
|
2899
|
-
"NEEDS_INPUT",
|
|
2900
|
-
"NOT_APPLICABLE"
|
|
2901
|
-
]);
|
|
2902
|
-
var VALID_SEVERITIES = /* @__PURE__ */ new Set(["block", "warn", "info"]);
|
|
2903
|
-
var VALID_FINDING_STATUSES = /* @__PURE__ */ new Set(["open", "resolved", "accepted", "superseded"]);
|
|
2904
|
-
var VALID_EXECUTORS = /* @__PURE__ */ new Set(["script", "worker", "agent", "manual", "cli"]);
|
|
2905
|
-
var TOP_LEVEL_FIELDS = /* @__PURE__ */ new Set([
|
|
2906
|
-
"schema_version",
|
|
2907
|
-
"run_id",
|
|
2908
|
-
"stage_id",
|
|
2909
|
-
"attempt",
|
|
2910
|
-
"status",
|
|
2911
|
-
"decision",
|
|
2912
|
-
"summary",
|
|
2913
|
-
"outputs",
|
|
2914
|
-
"warnings",
|
|
2915
|
-
"blocking_reason",
|
|
2916
|
-
"degradation",
|
|
2917
|
-
"producer",
|
|
2918
|
-
"acceptance",
|
|
2919
|
-
"evidence",
|
|
2920
|
-
"review",
|
|
2921
|
-
"repair"
|
|
2922
|
-
]);
|
|
2923
2908
|
function validateReviewResult(input) {
|
|
2924
2909
|
const errors = [];
|
|
2925
2910
|
if (!input || typeof input !== "object" || Array.isArray(input)) {
|
|
@@ -3168,19 +3153,6 @@ function validateBatches(val, path, errors) {
|
|
|
3168
3153
|
checkOptionalInteger(b.chars, `${itemPath}.chars`, errors);
|
|
3169
3154
|
}
|
|
3170
3155
|
}
|
|
3171
|
-
var METRIC_FIELDS = [
|
|
3172
|
-
"files_reviewed",
|
|
3173
|
-
"files_scanned",
|
|
3174
|
-
"findings_count",
|
|
3175
|
-
"deterministic_findings_count",
|
|
3176
|
-
"semantic_findings_count",
|
|
3177
|
-
"block_count",
|
|
3178
|
-
"warn_count",
|
|
3179
|
-
"info_count",
|
|
3180
|
-
"resolved_count",
|
|
3181
|
-
"batch_count",
|
|
3182
|
-
"scenario_count"
|
|
3183
|
-
];
|
|
3184
3156
|
function validateMetrics(val, path, errors) {
|
|
3185
3157
|
if (!isPlainObject(val)) {
|
|
3186
3158
|
errors.push({ path, message: "Must be an object" });
|
|
@@ -3197,10 +3169,924 @@ function validateRepairValidation(val, path, errors) {
|
|
|
3197
3169
|
checkOptionalInteger(val.exit_code, `${path}.exit_code`, errors);
|
|
3198
3170
|
checkOptionalInteger(val.findings_remaining, `${path}.findings_remaining`, errors, 0);
|
|
3199
3171
|
}
|
|
3172
|
+
var VALID_STATUSES, VALID_DECISIONS, VALID_SEVERITIES, VALID_FINDING_STATUSES, VALID_EXECUTORS, TOP_LEVEL_FIELDS, METRIC_FIELDS;
|
|
3173
|
+
var init_review_result_validator = __esm({
|
|
3174
|
+
"src/review-result-validator.ts"() {
|
|
3175
|
+
"use strict";
|
|
3176
|
+
VALID_STATUSES = /* @__PURE__ */ new Set([
|
|
3177
|
+
"SUCCEEDED",
|
|
3178
|
+
"FAILED",
|
|
3179
|
+
"BLOCKED",
|
|
3180
|
+
"NEEDS_INPUT",
|
|
3181
|
+
"SKIPPED"
|
|
3182
|
+
]);
|
|
3183
|
+
VALID_DECISIONS = /* @__PURE__ */ new Set([
|
|
3184
|
+
"PASS",
|
|
3185
|
+
"FAIL",
|
|
3186
|
+
"PASS_WITH_RESIDUAL_MINOR",
|
|
3187
|
+
"BLOCKED",
|
|
3188
|
+
"NEEDS_INPUT",
|
|
3189
|
+
"NOT_APPLICABLE"
|
|
3190
|
+
]);
|
|
3191
|
+
VALID_SEVERITIES = /* @__PURE__ */ new Set(["block", "warn", "info"]);
|
|
3192
|
+
VALID_FINDING_STATUSES = /* @__PURE__ */ new Set(["open", "resolved", "accepted", "superseded"]);
|
|
3193
|
+
VALID_EXECUTORS = /* @__PURE__ */ new Set(["script", "worker", "agent", "manual", "cli"]);
|
|
3194
|
+
TOP_LEVEL_FIELDS = /* @__PURE__ */ new Set([
|
|
3195
|
+
"schema_version",
|
|
3196
|
+
"run_id",
|
|
3197
|
+
"stage_id",
|
|
3198
|
+
"attempt",
|
|
3199
|
+
"status",
|
|
3200
|
+
"decision",
|
|
3201
|
+
"summary",
|
|
3202
|
+
"outputs",
|
|
3203
|
+
"warnings",
|
|
3204
|
+
"blocking_reason",
|
|
3205
|
+
"degradation",
|
|
3206
|
+
"producer",
|
|
3207
|
+
"acceptance",
|
|
3208
|
+
"evidence",
|
|
3209
|
+
"review",
|
|
3210
|
+
"repair"
|
|
3211
|
+
]);
|
|
3212
|
+
METRIC_FIELDS = [
|
|
3213
|
+
"files_reviewed",
|
|
3214
|
+
"files_scanned",
|
|
3215
|
+
"findings_count",
|
|
3216
|
+
"deterministic_findings_count",
|
|
3217
|
+
"semantic_findings_count",
|
|
3218
|
+
"block_count",
|
|
3219
|
+
"warn_count",
|
|
3220
|
+
"info_count",
|
|
3221
|
+
"resolved_count",
|
|
3222
|
+
"batch_count",
|
|
3223
|
+
"scenario_count"
|
|
3224
|
+
];
|
|
3225
|
+
}
|
|
3226
|
+
});
|
|
3227
|
+
|
|
3228
|
+
// src/contract-kernel.ts
|
|
3229
|
+
function isOfficialNamespace(namespace) {
|
|
3230
|
+
return OFFICIAL_NAMESPACE_PATTERN.test(namespace);
|
|
3231
|
+
}
|
|
3232
|
+
function validateNamespaceAuthority(identity, expectedAuthority) {
|
|
3233
|
+
if (isOfficialNamespace(identity.namespace) && identity.authority !== OFFICIAL_AUTHORITY) {
|
|
3234
|
+
throw new ContractError(
|
|
3235
|
+
CONTRACT_ERROR_CODES.AUTHORITY_VIOLATION,
|
|
3236
|
+
`Namespace ${identity.namespace} requires authority "${OFFICIAL_AUTHORITY}", got "${identity.authority}"`,
|
|
3237
|
+
{ identity, expectedAuthority: OFFICIAL_AUTHORITY }
|
|
3238
|
+
);
|
|
3239
|
+
}
|
|
3240
|
+
if (expectedAuthority && identity.authority !== expectedAuthority) {
|
|
3241
|
+
throw new ContractError(
|
|
3242
|
+
CONTRACT_ERROR_CODES.AUTHORITY_VIOLATION,
|
|
3243
|
+
`Expected authority "${expectedAuthority}", got "${identity.authority}"`,
|
|
3244
|
+
{ identity, expectedAuthority }
|
|
3245
|
+
);
|
|
3246
|
+
}
|
|
3247
|
+
}
|
|
3248
|
+
function canonicalizeForDigest(rawContent) {
|
|
3249
|
+
try {
|
|
3250
|
+
const parsed = JSON.parse(rawContent);
|
|
3251
|
+
if (parsed.contractIdentity && typeof parsed.contractIdentity === "object") {
|
|
3252
|
+
const ci = { ...parsed.contractIdentity };
|
|
3253
|
+
delete ci.revisionDigest;
|
|
3254
|
+
parsed.contractIdentity = ci;
|
|
3255
|
+
}
|
|
3256
|
+
return JSON.stringify(parsed);
|
|
3257
|
+
} catch {
|
|
3258
|
+
return rawContent;
|
|
3259
|
+
}
|
|
3260
|
+
}
|
|
3261
|
+
function computeRevisionDigest(content) {
|
|
3262
|
+
const canonical = canonicalizeForDigest(content);
|
|
3263
|
+
const hash = (0, import_node_crypto3.createHash)("sha256").update(canonical, "utf-8").digest("hex");
|
|
3264
|
+
return `sha256:${hash}`;
|
|
3265
|
+
}
|
|
3266
|
+
function verifyDigest(content, expectedDigest) {
|
|
3267
|
+
const actualDigest = computeRevisionDigest(content);
|
|
3268
|
+
return actualDigest === expectedDigest;
|
|
3269
|
+
}
|
|
3270
|
+
function setNestedValue(obj, path, value) {
|
|
3271
|
+
const parts = path.split(".");
|
|
3272
|
+
let current = obj;
|
|
3273
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
3274
|
+
const part = parts[i];
|
|
3275
|
+
if (current[part] === void 0 || typeof current[part] !== "object" || current[part] === null) {
|
|
3276
|
+
current[part] = {};
|
|
3277
|
+
}
|
|
3278
|
+
current = current[part];
|
|
3279
|
+
}
|
|
3280
|
+
current[parts[parts.length - 1]] = value;
|
|
3281
|
+
}
|
|
3282
|
+
function matchesSchemaType(value, definition) {
|
|
3283
|
+
const type = definition?.type;
|
|
3284
|
+
if (type === "array") return Array.isArray(value);
|
|
3285
|
+
if (type === "object") return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3286
|
+
if (type === "string") return typeof value === "string";
|
|
3287
|
+
if (type === "number" || type === "integer") return typeof value === "number";
|
|
3288
|
+
if (type === "boolean") return typeof value === "boolean";
|
|
3289
|
+
return value !== void 0;
|
|
3290
|
+
}
|
|
3291
|
+
function getNestedValue(source, path) {
|
|
3292
|
+
let current = source;
|
|
3293
|
+
for (const part of path.split(".")) {
|
|
3294
|
+
if (typeof current !== "object" || current === null || Array.isArray(current)) return void 0;
|
|
3295
|
+
current = current[part];
|
|
3296
|
+
}
|
|
3297
|
+
return current;
|
|
3298
|
+
}
|
|
3299
|
+
function valuesEqual(left, right) {
|
|
3300
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
3301
|
+
}
|
|
3302
|
+
function normalizeToCanonical(legacyData, config, contract) {
|
|
3303
|
+
const errors = [];
|
|
3304
|
+
const warnings = [];
|
|
3305
|
+
const legacyFieldMap = /* @__PURE__ */ new Map();
|
|
3306
|
+
for (const mapping of config.fieldMappings) {
|
|
3307
|
+
legacyFieldMap.set(mapping.legacy, mapping.canonical);
|
|
3308
|
+
}
|
|
3309
|
+
const canonical = {};
|
|
3310
|
+
const canonicalRoots = new Set(Object.keys(contract.schema.properties));
|
|
3311
|
+
const presentCanonicalRoots = /* @__PURE__ */ new Set();
|
|
3312
|
+
for (const root of canonicalRoots) {
|
|
3313
|
+
const value = legacyData[root];
|
|
3314
|
+
if (value !== void 0 && matchesSchemaType(value, contract.schema.properties[root])) {
|
|
3315
|
+
canonical[root] = structuredClone(value);
|
|
3316
|
+
presentCanonicalRoots.add(root);
|
|
3317
|
+
}
|
|
3318
|
+
}
|
|
3319
|
+
const unmappedLegacyFields = new Set(
|
|
3320
|
+
Object.keys(legacyData).filter((field) => !presentCanonicalRoots.has(field) && !legacyFieldMap.has(field))
|
|
3321
|
+
);
|
|
3322
|
+
let usedLegacyExpression = false;
|
|
3323
|
+
for (const mapping of config.fieldMappings) {
|
|
3324
|
+
const legacyValue = legacyData[mapping.legacy];
|
|
3325
|
+
const existingCanonicalValue = getNestedValue(canonical, mapping.canonical);
|
|
3326
|
+
if (legacyValue !== void 0 && !presentCanonicalRoots.has(mapping.legacy)) {
|
|
3327
|
+
usedLegacyExpression = true;
|
|
3328
|
+
const canonicalValue = mapping.transform ? mapping.transform(legacyValue) : legacyValue;
|
|
3329
|
+
if (existingCanonicalValue !== void 0) {
|
|
3330
|
+
if (!valuesEqual(existingCanonicalValue, canonicalValue)) {
|
|
3331
|
+
errors.push({
|
|
3332
|
+
code: CONTRACT_ERROR_CODES.CANONICAL_LEGACY_CONFLICT,
|
|
3333
|
+
path: `/${mapping.canonical.replaceAll(".", "/")}`,
|
|
3334
|
+
message: `Canonical field "${mapping.canonical}" conflicts with legacy field "${mapping.legacy}"`
|
|
3335
|
+
});
|
|
3336
|
+
}
|
|
3337
|
+
} else {
|
|
3338
|
+
setNestedValue(canonical, mapping.canonical, canonicalValue);
|
|
3339
|
+
}
|
|
3340
|
+
}
|
|
3341
|
+
if (mapping.required && getNestedValue(canonical, mapping.canonical) === void 0) {
|
|
3342
|
+
errors.push({
|
|
3343
|
+
code: "MISSING_REQUIRED_FIELD",
|
|
3344
|
+
path: `/${mapping.canonical.replaceAll(".", "/")}`,
|
|
3345
|
+
message: `Required canonical field "${mapping.canonical}" missing (legacy: "${mapping.legacy}")`
|
|
3346
|
+
});
|
|
3347
|
+
}
|
|
3348
|
+
}
|
|
3349
|
+
for (const field of unmappedLegacyFields) {
|
|
3350
|
+
errors.push({
|
|
3351
|
+
code: "LOSSY_NORMALIZATION",
|
|
3352
|
+
path: field,
|
|
3353
|
+
message: `Legacy field "${field}" not mapped to canonical form \u2014 would be lost`
|
|
3354
|
+
});
|
|
3355
|
+
}
|
|
3356
|
+
if (config.validate) {
|
|
3357
|
+
const validationErrors = config.validate(canonical);
|
|
3358
|
+
for (const error of validationErrors) {
|
|
3359
|
+
errors.push({
|
|
3360
|
+
code: "VALIDATION_FAILED",
|
|
3361
|
+
path: "",
|
|
3362
|
+
message: error
|
|
3363
|
+
});
|
|
3364
|
+
}
|
|
3365
|
+
}
|
|
3366
|
+
if (errors.length > 0) {
|
|
3367
|
+
return { success: false, errors, warnings };
|
|
3368
|
+
}
|
|
3369
|
+
const schemaResult = validateContractAgainstSchema(canonical, contract);
|
|
3370
|
+
if (!schemaResult.valid) {
|
|
3371
|
+
return {
|
|
3372
|
+
success: false,
|
|
3373
|
+
errors: schemaResult.errors.map((e) => ({
|
|
3374
|
+
code: "SCHEMA_VALIDATION_FAILED",
|
|
3375
|
+
path: e.split(":")[0]?.trim() ?? "",
|
|
3376
|
+
message: e
|
|
3377
|
+
})),
|
|
3378
|
+
warnings
|
|
3379
|
+
};
|
|
3380
|
+
}
|
|
3381
|
+
if (Array.isArray(canonical.relations)) {
|
|
3382
|
+
const relationResult = validateRelations(canonical.relations, contract);
|
|
3383
|
+
if (!relationResult.valid) {
|
|
3384
|
+
return {
|
|
3385
|
+
success: false,
|
|
3386
|
+
errors: relationResult.issues.map((i) => ({
|
|
3387
|
+
code: i.code,
|
|
3388
|
+
path: i.path,
|
|
3389
|
+
message: i.message
|
|
3390
|
+
})),
|
|
3391
|
+
warnings
|
|
3392
|
+
};
|
|
3393
|
+
}
|
|
3394
|
+
}
|
|
3395
|
+
const sourceRevision = usedLegacyExpression ? "legacy" : "canonical";
|
|
3396
|
+
return {
|
|
3397
|
+
success: true,
|
|
3398
|
+
ir: {
|
|
3399
|
+
type: contract.identity.major.split(".")[1]?.split("@")[0] ?? "unknown",
|
|
3400
|
+
id: String(canonical.metadata?.id ?? canonical["id"] ?? ""),
|
|
3401
|
+
contractMajor: contract.identity.major,
|
|
3402
|
+
contractDigest: contract.identity.revisionDigest,
|
|
3403
|
+
canonical,
|
|
3404
|
+
sourceRevision,
|
|
3405
|
+
warnings
|
|
3406
|
+
},
|
|
3407
|
+
errors: [],
|
|
3408
|
+
warnings
|
|
3409
|
+
};
|
|
3410
|
+
}
|
|
3411
|
+
function resolvePropertyPath(schema, fieldPath) {
|
|
3412
|
+
const parts = fieldPath.split(".");
|
|
3413
|
+
let current = schema.properties;
|
|
3414
|
+
for (const part of parts) {
|
|
3415
|
+
if (!current || typeof current !== "object") return void 0;
|
|
3416
|
+
const propDef = current[part];
|
|
3417
|
+
if (!propDef) return void 0;
|
|
3418
|
+
if (parts.indexOf(part) < parts.length - 1) {
|
|
3419
|
+
current = propDef.properties;
|
|
3420
|
+
} else {
|
|
3421
|
+
return propDef;
|
|
3422
|
+
}
|
|
3423
|
+
}
|
|
3424
|
+
return void 0;
|
|
3425
|
+
}
|
|
3426
|
+
function validatePolicyCompatibility(policy, baseContract) {
|
|
3427
|
+
const errors = [];
|
|
3428
|
+
const warnings = [];
|
|
3429
|
+
if (policy.baseContractMajor !== baseContract.identity.major) {
|
|
3430
|
+
errors.push(
|
|
3431
|
+
`POLICY_INCOMPATIBLE: Policy references ${policy.baseContractMajor}, but base contract is ${baseContract.identity.major}`
|
|
3432
|
+
);
|
|
3433
|
+
return { compatible: false, errors, warnings };
|
|
3434
|
+
}
|
|
3435
|
+
const schema = baseContract.schema;
|
|
3436
|
+
if (policy.additionalRequired) {
|
|
3437
|
+
for (const field of policy.additionalRequired) {
|
|
3438
|
+
const propDef = resolvePropertyPath(schema, field);
|
|
3439
|
+
if (!propDef) {
|
|
3440
|
+
errors.push(`POLICY_INCOMPATIBLE: Policy requires field "${field}" which does not exist in base contract`);
|
|
3441
|
+
}
|
|
3442
|
+
}
|
|
3443
|
+
}
|
|
3444
|
+
if (policy.restrictedEnums) {
|
|
3445
|
+
for (const [field, values] of Object.entries(policy.restrictedEnums)) {
|
|
3446
|
+
const baseProperty = resolvePropertyPath(schema, field);
|
|
3447
|
+
if (!baseProperty) {
|
|
3448
|
+
errors.push(`POLICY_INCOMPATIBLE: Policy restricts enum for field "${field}" which does not exist in base contract`);
|
|
3449
|
+
continue;
|
|
3450
|
+
}
|
|
3451
|
+
if (!Array.isArray(baseProperty.enum)) {
|
|
3452
|
+
errors.push(`POLICY_INCOMPATIBLE: Field "${field}" is not an enum in base contract \u2014 cannot restrict`);
|
|
3453
|
+
continue;
|
|
3454
|
+
}
|
|
3455
|
+
for (const value of values) {
|
|
3456
|
+
if (!baseProperty.enum.includes(value)) {
|
|
3457
|
+
errors.push(`POLICY_INCOMPATIBLE: Policy enum value "${String(value)}" for field "${field}" not in base contract enum [${baseProperty.enum.join(", ")}]`);
|
|
3458
|
+
}
|
|
3459
|
+
}
|
|
3460
|
+
}
|
|
3461
|
+
}
|
|
3462
|
+
if (policy.minCardinality) {
|
|
3463
|
+
for (const [field, min] of Object.entries(policy.minCardinality)) {
|
|
3464
|
+
if (!Number.isFinite(min) || min < 0 || !Number.isInteger(min)) {
|
|
3465
|
+
errors.push(`POLICY_INCOMPATIBLE: Policy min cardinality for "${field}" must be a non-negative integer, got ${min}`);
|
|
3466
|
+
continue;
|
|
3467
|
+
}
|
|
3468
|
+
const baseProperty = resolvePropertyPath(schema, field);
|
|
3469
|
+
if (!baseProperty) {
|
|
3470
|
+
errors.push(`POLICY_INCOMPATIBLE: Policy sets min cardinality for field "${field}" which does not exist in base contract`);
|
|
3471
|
+
continue;
|
|
3472
|
+
}
|
|
3473
|
+
const baseMin = baseProperty.type === "array" ? baseProperty.minItems : baseProperty.minimum;
|
|
3474
|
+
if (baseMin !== void 0 && min < baseMin) {
|
|
3475
|
+
errors.push(`POLICY_INCOMPATIBLE: Policy min cardinality ${min} for field "${field}" loosens base ${baseProperty.type === "array" ? "minItems" : "minimum"}=${baseMin}`);
|
|
3476
|
+
}
|
|
3477
|
+
}
|
|
3478
|
+
}
|
|
3479
|
+
if (policy.maxCardinality) {
|
|
3480
|
+
for (const [field, max] of Object.entries(policy.maxCardinality)) {
|
|
3481
|
+
if (!Number.isFinite(max) || max < 0 || !Number.isInteger(max)) {
|
|
3482
|
+
errors.push(`POLICY_INCOMPATIBLE: Policy max cardinality for "${field}" must be a non-negative integer, got ${max}`);
|
|
3483
|
+
continue;
|
|
3484
|
+
}
|
|
3485
|
+
const baseProperty = resolvePropertyPath(schema, field);
|
|
3486
|
+
if (!baseProperty) {
|
|
3487
|
+
errors.push(`POLICY_INCOMPATIBLE: Policy sets max cardinality for field "${field}" which does not exist in base contract`);
|
|
3488
|
+
continue;
|
|
3489
|
+
}
|
|
3490
|
+
const baseMax = baseProperty.type === "array" ? baseProperty.maxItems : baseProperty.maximum;
|
|
3491
|
+
if (baseMax !== void 0 && max > baseMax) {
|
|
3492
|
+
errors.push(`POLICY_INCOMPATIBLE: Policy max cardinality ${max} for field "${field}" loosens base ${baseProperty.type === "array" ? "maxItems" : "maximum"}=${baseMax}`);
|
|
3493
|
+
}
|
|
3494
|
+
}
|
|
3495
|
+
}
|
|
3496
|
+
if (policy.constraints && Object.keys(policy.constraints).length > 0) {
|
|
3497
|
+
for (const key of Object.keys(policy.constraints)) {
|
|
3498
|
+
errors.push(`POLICY_INCOMPATIBLE: Constraint "${key}" is not implemented \u2014 rejected as unvalidated escape hatch`);
|
|
3499
|
+
}
|
|
3500
|
+
}
|
|
3501
|
+
return {
|
|
3502
|
+
compatible: errors.length === 0,
|
|
3503
|
+
errors,
|
|
3504
|
+
warnings
|
|
3505
|
+
};
|
|
3506
|
+
}
|
|
3507
|
+
async function loadContract(contractPath, options) {
|
|
3508
|
+
const rawContent = await (0, import_promises5.readFile)(contractPath, "utf-8");
|
|
3509
|
+
let schema;
|
|
3510
|
+
try {
|
|
3511
|
+
schema = JSON.parse(rawContent);
|
|
3512
|
+
} catch (error) {
|
|
3513
|
+
throw new ContractError(
|
|
3514
|
+
CONTRACT_ERROR_CODES.INVALID_IDENTITY,
|
|
3515
|
+
`Failed to parse contract JSON: ${error.message}`,
|
|
3516
|
+
{ path: contractPath }
|
|
3517
|
+
);
|
|
3518
|
+
}
|
|
3519
|
+
if (!schema.contractIdentity) {
|
|
3520
|
+
throw new ContractError(
|
|
3521
|
+
CONTRACT_ERROR_CODES.INVALID_IDENTITY,
|
|
3522
|
+
"Contract missing contractIdentity",
|
|
3523
|
+
{ path: contractPath }
|
|
3524
|
+
);
|
|
3525
|
+
}
|
|
3526
|
+
const identity = schema.contractIdentity;
|
|
3527
|
+
if (!identity.major || !identity.authority || !identity.namespace) {
|
|
3528
|
+
throw new ContractError(
|
|
3529
|
+
CONTRACT_ERROR_CODES.INVALID_IDENTITY,
|
|
3530
|
+
"Contract identity missing required fields (major, authority, namespace)",
|
|
3531
|
+
{ identity }
|
|
3532
|
+
);
|
|
3533
|
+
}
|
|
3534
|
+
if (options?.expectedAuthority) {
|
|
3535
|
+
validateNamespaceAuthority(identity, options.expectedAuthority);
|
|
3536
|
+
} else {
|
|
3537
|
+
validateNamespaceAuthority(identity);
|
|
3538
|
+
}
|
|
3539
|
+
const computedDigest = computeRevisionDigest(rawContent);
|
|
3540
|
+
if (identity.revisionDigest && identity.revisionDigest !== computedDigest) {
|
|
3541
|
+
throw new ContractError(
|
|
3542
|
+
CONTRACT_ERROR_CODES.DIGEST_MISMATCH,
|
|
3543
|
+
`Contract digest mismatch: declared ${identity.revisionDigest}, computed ${computedDigest}`,
|
|
3544
|
+
{ identity, computedDigest }
|
|
3545
|
+
);
|
|
3546
|
+
}
|
|
3547
|
+
identity.revisionDigest = computedDigest;
|
|
3548
|
+
return {
|
|
3549
|
+
identity,
|
|
3550
|
+
schema,
|
|
3551
|
+
rawContent
|
|
3552
|
+
};
|
|
3553
|
+
}
|
|
3554
|
+
async function loadContractsFromDirectory(contractsDir, options) {
|
|
3555
|
+
const contracts = [];
|
|
3556
|
+
const entries = await (0, import_promises5.readdir)(contractsDir, { withFileTypes: true });
|
|
3557
|
+
for (const entry of entries) {
|
|
3558
|
+
if (entry.isDirectory()) {
|
|
3559
|
+
const schemaPath = (0, import_node_path6.join)(contractsDir, entry.name, "schema.json");
|
|
3560
|
+
const contract = await loadContract(schemaPath, options);
|
|
3561
|
+
contracts.push(contract);
|
|
3562
|
+
}
|
|
3563
|
+
}
|
|
3564
|
+
return contracts;
|
|
3565
|
+
}
|
|
3566
|
+
function getValidator(schema) {
|
|
3567
|
+
const key = `${schema.$id || "default"}#${computeRevisionDigest(JSON.stringify(schema))}`;
|
|
3568
|
+
if (!_validatorCache.has(key)) {
|
|
3569
|
+
const AjvCtor = import_ajv.default;
|
|
3570
|
+
const ajv = new AjvCtor({ allErrors: true, strict: false });
|
|
3571
|
+
_validatorCache.set(key, ajv.compile(schema));
|
|
3572
|
+
}
|
|
3573
|
+
return _validatorCache.get(key);
|
|
3574
|
+
}
|
|
3575
|
+
function validateContractAgainstSchema(data, contract) {
|
|
3576
|
+
const validate = getValidator(contract.schema);
|
|
3577
|
+
const valid = validate(data);
|
|
3578
|
+
if (valid) {
|
|
3579
|
+
return { valid: true, errors: [] };
|
|
3580
|
+
}
|
|
3581
|
+
const errors = (validate.errors ?? []).map(
|
|
3582
|
+
(err) => `${err.instancePath || "/"}: ${err.message ?? "validation error"}`
|
|
3583
|
+
);
|
|
3584
|
+
return { valid: false, errors };
|
|
3585
|
+
}
|
|
3586
|
+
function validateRelations(relations, contract) {
|
|
3587
|
+
const issues = [];
|
|
3588
|
+
const relationRules = contract.identity.relationRules;
|
|
3589
|
+
if (!relationRules) {
|
|
3590
|
+
issues.push({
|
|
3591
|
+
code: "RELATION_RULES_MISSING",
|
|
3592
|
+
path: "/contractIdentity/relationRules",
|
|
3593
|
+
message: "Contract does not define relation rules \u2014 cannot validate relations"
|
|
3594
|
+
});
|
|
3595
|
+
return { valid: false, issues };
|
|
3596
|
+
}
|
|
3597
|
+
const relationsByKind = /* @__PURE__ */ new Map();
|
|
3598
|
+
for (const relation of relations) {
|
|
3599
|
+
const kind = relation.kind;
|
|
3600
|
+
if (!relationsByKind.has(kind)) {
|
|
3601
|
+
relationsByKind.set(kind, []);
|
|
3602
|
+
}
|
|
3603
|
+
relationsByKind.get(kind).push(relation);
|
|
3604
|
+
}
|
|
3605
|
+
for (let i = 0; i < relations.length; i++) {
|
|
3606
|
+
const relation = relations[i];
|
|
3607
|
+
const kind = relation.kind;
|
|
3608
|
+
const targetType = relation.target_type;
|
|
3609
|
+
const path = `/relations/${i}`;
|
|
3610
|
+
if (!relationRules[kind]) {
|
|
3611
|
+
issues.push({
|
|
3612
|
+
code: "RELATION_INVALID_KIND",
|
|
3613
|
+
path: `${path}/kind`,
|
|
3614
|
+
message: `Relation kind "${kind}" is not allowed by contract rules`
|
|
3615
|
+
});
|
|
3616
|
+
continue;
|
|
3617
|
+
}
|
|
3618
|
+
const rule = relationRules[kind];
|
|
3619
|
+
if (!rule.allowedTargetTypes.includes(targetType)) {
|
|
3620
|
+
issues.push({
|
|
3621
|
+
code: "RELATION_INVALID_TARGET_TYPE",
|
|
3622
|
+
path: `${path}/target_type`,
|
|
3623
|
+
message: `Target type "${targetType}" is not allowed for relation kind "${kind}". Allowed: ${rule.allowedTargetTypes.join(", ")}`
|
|
3624
|
+
});
|
|
3625
|
+
}
|
|
3626
|
+
const hasAnchor = relation.anchor !== void 0 && relation.anchor !== "";
|
|
3627
|
+
if (rule.anchorPolicy === "required" && !hasAnchor) {
|
|
3628
|
+
issues.push({
|
|
3629
|
+
code: "RELATION_MISSING_ANCHOR",
|
|
3630
|
+
path: `${path}/anchor`,
|
|
3631
|
+
message: `Relation kind "${kind}" requires an anchor`
|
|
3632
|
+
});
|
|
3633
|
+
} else if (rule.anchorPolicy === "forbidden" && hasAnchor) {
|
|
3634
|
+
issues.push({
|
|
3635
|
+
code: "RELATION_FORBIDDEN_ANCHOR",
|
|
3636
|
+
path: `${path}/anchor`,
|
|
3637
|
+
message: `Relation kind "${kind}" forbids anchors`
|
|
3638
|
+
});
|
|
3639
|
+
}
|
|
3640
|
+
}
|
|
3641
|
+
for (const [kind, rule] of Object.entries(relationRules)) {
|
|
3642
|
+
const count = relationsByKind.get(kind)?.length ?? 0;
|
|
3643
|
+
if (count < rule.min) {
|
|
3644
|
+
issues.push({
|
|
3645
|
+
code: "RELATION_BELOW_MIN",
|
|
3646
|
+
path: "/relations",
|
|
3647
|
+
message: `Relation kind "${kind}" has ${count} instances, minimum is ${rule.min}`
|
|
3648
|
+
});
|
|
3649
|
+
}
|
|
3650
|
+
if (count > rule.max) {
|
|
3651
|
+
issues.push({
|
|
3652
|
+
code: "RELATION_ABOVE_MAX",
|
|
3653
|
+
path: "/relations",
|
|
3654
|
+
message: `Relation kind "${kind}" has ${count} instances, maximum is ${rule.max}`
|
|
3655
|
+
});
|
|
3656
|
+
}
|
|
3657
|
+
}
|
|
3658
|
+
return {
|
|
3659
|
+
valid: issues.length === 0,
|
|
3660
|
+
issues
|
|
3661
|
+
};
|
|
3662
|
+
}
|
|
3663
|
+
function normalizeE2eLegacyArtifact(legacyData, contract) {
|
|
3664
|
+
return normalizeToCanonical(legacyData, E2E_NORMALIZER_CONFIG, contract);
|
|
3665
|
+
}
|
|
3666
|
+
async function loadContractCatalog(contractsDir, options) {
|
|
3667
|
+
const catalog = new ContractCatalog();
|
|
3668
|
+
const contracts = await loadContractsFromDirectory(contractsDir, options);
|
|
3669
|
+
for (const contract of contracts) {
|
|
3670
|
+
catalog.add(contract);
|
|
3671
|
+
}
|
|
3672
|
+
return catalog;
|
|
3673
|
+
}
|
|
3674
|
+
var import_node_crypto3, import_promises5, import_node_path6, import_ajv, CONTRACT_ERROR_CODES, ContractError, OFFICIAL_AUTHORITY, OFFICIAL_NAMESPACE_PATTERN, ContractRegistry, _validatorCache, E2E_NORMALIZER_CONFIG, ContractCatalog;
|
|
3675
|
+
var init_contract_kernel = __esm({
|
|
3676
|
+
"src/contract-kernel.ts"() {
|
|
3677
|
+
"use strict";
|
|
3678
|
+
import_node_crypto3 = require("crypto");
|
|
3679
|
+
import_promises5 = require("fs/promises");
|
|
3680
|
+
import_node_path6 = require("path");
|
|
3681
|
+
import_ajv = __toESM(require("ajv"), 1);
|
|
3682
|
+
CONTRACT_ERROR_CODES = {
|
|
3683
|
+
/** Contract identity not found */
|
|
3684
|
+
CONTRACT_NOT_FOUND: "CONTRACT_NOT_FOUND",
|
|
3685
|
+
/** Invalid contract identity format */
|
|
3686
|
+
INVALID_IDENTITY: "INVALID_IDENTITY",
|
|
3687
|
+
/** Revision digest mismatch */
|
|
3688
|
+
DIGEST_MISMATCH: "DIGEST_MISMATCH",
|
|
3689
|
+
/** Duplicate contract identity */
|
|
3690
|
+
DUPLICATE_IDENTITY: "DUPLICATE_IDENTITY",
|
|
3691
|
+
/** Multiple active write contracts for same type */
|
|
3692
|
+
MULTIPLE_ACTIVE_WRITE: "MULTIPLE_ACTIVE_WRITE",
|
|
3693
|
+
/** Unknown authority namespace */
|
|
3694
|
+
UNKNOWN_AUTHORITY: "UNKNOWN_AUTHORITY",
|
|
3695
|
+
/** Namespace authority violation */
|
|
3696
|
+
AUTHORITY_VIOLATION: "AUTHORITY_VIOLATION",
|
|
3697
|
+
/** Schema validation failed */
|
|
3698
|
+
SCHEMA_VALIDATION_FAILED: "SCHEMA_VALIDATION_FAILED",
|
|
3699
|
+
/** Canonical IR normalization failed */
|
|
3700
|
+
NORMALIZATION_FAILED: "NORMALIZATION_FAILED",
|
|
3701
|
+
/** Policy compatibility check failed */
|
|
3702
|
+
POLICY_INCOMPATIBLE: "POLICY_INCOMPATIBLE",
|
|
3703
|
+
/** Legacy revision cannot be normalized */
|
|
3704
|
+
LEGACY_NORMALIZATION_FAILED: "LEGACY_NORMALIZATION_FAILED",
|
|
3705
|
+
/** Canonical and legacy conflict */
|
|
3706
|
+
CANONICAL_LEGACY_CONFLICT: "CANONICAL_LEGACY_CONFLICT",
|
|
3707
|
+
/** Relation rule violation */
|
|
3708
|
+
RELATION_RULE_VIOLATION: "RELATION_RULE_VIOLATION",
|
|
3709
|
+
/** Relation rules missing in contract (fail closed) */
|
|
3710
|
+
RELATION_RULES_MISSING: "RELATION_RULES_MISSING",
|
|
3711
|
+
/** Ambiguous revision — multiple revisions found, no unique active write */
|
|
3712
|
+
AMBIGUOUS_REVISION: "AMBIGUOUS_REVISION",
|
|
3713
|
+
/** Invalid relation kind */
|
|
3714
|
+
RELATION_INVALID_KIND: "RELATION_INVALID_KIND",
|
|
3715
|
+
/** Invalid relation target type */
|
|
3716
|
+
RELATION_INVALID_TARGET_TYPE: "RELATION_INVALID_TARGET_TYPE",
|
|
3717
|
+
/** Relation below minimum cardinality */
|
|
3718
|
+
RELATION_BELOW_MIN: "RELATION_BELOW_MIN",
|
|
3719
|
+
/** Relation above maximum cardinality */
|
|
3720
|
+
RELATION_ABOVE_MAX: "RELATION_ABOVE_MAX",
|
|
3721
|
+
/** Missing required anchor */
|
|
3722
|
+
RELATION_MISSING_ANCHOR: "RELATION_MISSING_ANCHOR",
|
|
3723
|
+
/** Forbidden anchor present */
|
|
3724
|
+
RELATION_FORBIDDEN_ANCHOR: "RELATION_FORBIDDEN_ANCHOR",
|
|
3725
|
+
/** Missing required semantic marker */
|
|
3726
|
+
MARKER_MISSING: "MARKER_MISSING",
|
|
3727
|
+
/** Duplicate semantic marker */
|
|
3728
|
+
MARKER_DUPLICATE: "MARKER_DUPLICATE",
|
|
3729
|
+
/** Unknown semantic marker */
|
|
3730
|
+
MARKER_UNKNOWN: "MARKER_UNKNOWN"
|
|
3731
|
+
};
|
|
3732
|
+
ContractError = class extends Error {
|
|
3733
|
+
constructor(code, message, details) {
|
|
3734
|
+
super(message);
|
|
3735
|
+
this.code = code;
|
|
3736
|
+
this.details = details;
|
|
3737
|
+
this.name = "ContractError";
|
|
3738
|
+
}
|
|
3739
|
+
code;
|
|
3740
|
+
details;
|
|
3741
|
+
};
|
|
3742
|
+
OFFICIAL_AUTHORITY = "artifact";
|
|
3743
|
+
OFFICIAL_NAMESPACE_PATTERN = /^artifact(?:\.|$)/;
|
|
3744
|
+
ContractRegistry = class {
|
|
3745
|
+
contracts = /* @__PURE__ */ new Map();
|
|
3746
|
+
typeToActiveWrite = /* @__PURE__ */ new Map();
|
|
3747
|
+
majorToContracts = /* @__PURE__ */ new Map();
|
|
3748
|
+
/**
|
|
3749
|
+
* Register a contract
|
|
3750
|
+
* @throws ContractError if duplicate identity or multiple active write contracts
|
|
3751
|
+
*/
|
|
3752
|
+
register(contract, options) {
|
|
3753
|
+
const major = contract.identity.major;
|
|
3754
|
+
const digest = contract.identity.revisionDigest;
|
|
3755
|
+
const key = `${major}@${digest}`;
|
|
3756
|
+
const isActive = options?.isActive ?? true;
|
|
3757
|
+
const isWriteTarget = options?.isWriteTarget ?? true;
|
|
3758
|
+
if (this.contracts.has(key)) {
|
|
3759
|
+
throw new ContractError(
|
|
3760
|
+
CONTRACT_ERROR_CODES.DUPLICATE_IDENTITY,
|
|
3761
|
+
`Contract ${key} already registered`,
|
|
3762
|
+
{ identity: contract.identity }
|
|
3763
|
+
);
|
|
3764
|
+
}
|
|
3765
|
+
if (isActive && isWriteTarget) {
|
|
3766
|
+
const type = major.split("@")[0];
|
|
3767
|
+
if (this.typeToActiveWrite.has(type)) {
|
|
3768
|
+
throw new ContractError(
|
|
3769
|
+
CONTRACT_ERROR_CODES.MULTIPLE_ACTIVE_WRITE,
|
|
3770
|
+
`Type ${type} already has active write contract: ${this.typeToActiveWrite.get(type)}`,
|
|
3771
|
+
{ identity: contract.identity, existing: this.typeToActiveWrite.get(type) }
|
|
3772
|
+
);
|
|
3773
|
+
}
|
|
3774
|
+
this.typeToActiveWrite.set(type, key);
|
|
3775
|
+
}
|
|
3776
|
+
this.contracts.set(key, {
|
|
3777
|
+
contract,
|
|
3778
|
+
isActive,
|
|
3779
|
+
isWriteTarget,
|
|
3780
|
+
loadedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3781
|
+
});
|
|
3782
|
+
if (!this.majorToContracts.has(major)) {
|
|
3783
|
+
this.majorToContracts.set(major, /* @__PURE__ */ new Set());
|
|
3784
|
+
}
|
|
3785
|
+
this.majorToContracts.get(major).add(key);
|
|
3786
|
+
}
|
|
3787
|
+
/**
|
|
3788
|
+
* Resolve by major identity. Multiple revisions require a unique active write
|
|
3789
|
+
* revision; insertion order is never a resolution policy.
|
|
3790
|
+
*/
|
|
3791
|
+
get(major) {
|
|
3792
|
+
const keys = this.majorToContracts.get(major);
|
|
3793
|
+
if (!keys || keys.size === 0) return void 0;
|
|
3794
|
+
if (keys.size === 1) {
|
|
3795
|
+
const onlyKey = keys.values().next().value;
|
|
3796
|
+
return onlyKey ? this.contracts.get(onlyKey)?.contract : void 0;
|
|
3797
|
+
}
|
|
3798
|
+
const type = major.split("@")[0];
|
|
3799
|
+
const activeWriteKey = this.typeToActiveWrite.get(type);
|
|
3800
|
+
if (activeWriteKey && keys.has(activeWriteKey)) {
|
|
3801
|
+
return this.contracts.get(activeWriteKey)?.contract;
|
|
3802
|
+
}
|
|
3803
|
+
throw new ContractError(
|
|
3804
|
+
CONTRACT_ERROR_CODES.AMBIGUOUS_REVISION,
|
|
3805
|
+
`Multiple revisions found for ${major} with no unique active write \u2014 specify digest`,
|
|
3806
|
+
{
|
|
3807
|
+
major,
|
|
3808
|
+
availableDigests: Array.from(keys).map((key) => this.contracts.get(key)?.contract.identity.revisionDigest).filter((digest) => Boolean(digest))
|
|
3809
|
+
}
|
|
3810
|
+
);
|
|
3811
|
+
}
|
|
3812
|
+
/**
|
|
3813
|
+
* Get contract by major identity and digest
|
|
3814
|
+
*/
|
|
3815
|
+
getByMajorAndDigest(major, digest) {
|
|
3816
|
+
const key = `${major}@${digest}`;
|
|
3817
|
+
return this.contracts.get(key)?.contract;
|
|
3818
|
+
}
|
|
3819
|
+
/**
|
|
3820
|
+
* Get all contracts for a major identity
|
|
3821
|
+
*/
|
|
3822
|
+
getByMajor(major) {
|
|
3823
|
+
const keys = this.majorToContracts.get(major);
|
|
3824
|
+
if (!keys) return [];
|
|
3825
|
+
return Array.from(keys).map((key) => this.contracts.get(key)?.contract).filter((c) => c !== void 0);
|
|
3826
|
+
}
|
|
3827
|
+
/**
|
|
3828
|
+
* Get active write contract for a type
|
|
3829
|
+
*/
|
|
3830
|
+
getActiveWriteContract(typePrefix) {
|
|
3831
|
+
const key = this.typeToActiveWrite.get(typePrefix);
|
|
3832
|
+
return key ? this.contracts.get(key)?.contract : void 0;
|
|
3833
|
+
}
|
|
3834
|
+
/**
|
|
3835
|
+
* List all registered contracts
|
|
3836
|
+
*/
|
|
3837
|
+
list() {
|
|
3838
|
+
return Array.from(this.contracts.values());
|
|
3839
|
+
}
|
|
3840
|
+
/**
|
|
3841
|
+
* Check if a contract is registered by major identity
|
|
3842
|
+
*/
|
|
3843
|
+
has(major) {
|
|
3844
|
+
const keys = this.majorToContracts.get(major);
|
|
3845
|
+
return keys !== void 0 && keys.size > 0;
|
|
3846
|
+
}
|
|
3847
|
+
/**
|
|
3848
|
+
* Check if a contract is registered by major identity and digest
|
|
3849
|
+
*/
|
|
3850
|
+
hasByMajorAndDigest(major, digest) {
|
|
3851
|
+
const key = `${major}@${digest}`;
|
|
3852
|
+
return this.contracts.has(key);
|
|
3853
|
+
}
|
|
3854
|
+
};
|
|
3855
|
+
_validatorCache = /* @__PURE__ */ new Map();
|
|
3856
|
+
E2E_NORMALIZER_CONFIG = {
|
|
3857
|
+
contractMajor: "artifact.e2e-test@1",
|
|
3858
|
+
fieldMappings: [
|
|
3859
|
+
{ legacy: "id", canonical: "metadata.id", required: true },
|
|
3860
|
+
{ legacy: "title", canonical: "metadata.title", required: true },
|
|
3861
|
+
{ legacy: "status", canonical: "metadata.status", required: true },
|
|
3862
|
+
{ legacy: "test_batch", canonical: "metadata.test_batch" },
|
|
3863
|
+
{ legacy: "scope", canonical: "scope.business_goal", transform: (v) => String(v) },
|
|
3864
|
+
{ legacy: "actors", canonical: "scope.actors", transform: (v) => Array.isArray(v) ? v : [v] },
|
|
3865
|
+
{ legacy: "system_boundaries", canonical: "system_boundary.components", transform: (v) => Array.isArray(v) ? v : [v] },
|
|
3866
|
+
{ legacy: "test_cases", canonical: "test_cases", required: true },
|
|
3867
|
+
{ legacy: "relations", canonical: "relations" },
|
|
3868
|
+
// Map legacy coverage fields if present
|
|
3869
|
+
{ legacy: "ac_coverage", canonical: "coverage.ac_coverage" },
|
|
3870
|
+
{ legacy: "related_scenarios", canonical: "coverage.related_scenarios" },
|
|
3871
|
+
// Map legacy environment fields if present
|
|
3872
|
+
{ legacy: "topology", canonical: "environment_data.topology" },
|
|
3873
|
+
{ legacy: "fixtures", canonical: "environment_data.fixtures" },
|
|
3874
|
+
{ legacy: "isolation_strategy", canonical: "environment_data.isolation_strategy" },
|
|
3875
|
+
// Map legacy evidence fields if present
|
|
3876
|
+
{ legacy: "required_artifacts", canonical: "evidence_contract.required_artifacts" },
|
|
3877
|
+
{ legacy: "runner_binding", canonical: "evidence_contract.runner_binding" }
|
|
3878
|
+
],
|
|
3879
|
+
validate: (canonical) => {
|
|
3880
|
+
const errors = [];
|
|
3881
|
+
if (!canonical.metadata || typeof canonical.metadata !== "object") {
|
|
3882
|
+
errors.push("metadata must be an object");
|
|
3883
|
+
}
|
|
3884
|
+
if (!canonical.scope || typeof canonical.scope !== "object") {
|
|
3885
|
+
errors.push("scope must be an object");
|
|
3886
|
+
}
|
|
3887
|
+
if (!canonical.system_boundary || typeof canonical.system_boundary !== "object") {
|
|
3888
|
+
errors.push("system_boundary must be an object");
|
|
3889
|
+
}
|
|
3890
|
+
if (!Array.isArray(canonical.test_cases) || canonical.test_cases.length === 0) {
|
|
3891
|
+
errors.push("test_cases must be a non-empty array");
|
|
3892
|
+
}
|
|
3893
|
+
if (Array.isArray(canonical.test_cases)) {
|
|
3894
|
+
for (let i = 0; i < canonical.test_cases.length; i++) {
|
|
3895
|
+
const tc = canonical.test_cases[i];
|
|
3896
|
+
if (!Array.isArray(tc.trace_targets) || tc.trace_targets.length === 0) {
|
|
3897
|
+
errors.push(`test_cases[${i}].trace_targets must be a non-empty array`);
|
|
3898
|
+
}
|
|
3899
|
+
}
|
|
3900
|
+
}
|
|
3901
|
+
return errors;
|
|
3902
|
+
}
|
|
3903
|
+
};
|
|
3904
|
+
ContractCatalog = class {
|
|
3905
|
+
contracts = /* @__PURE__ */ new Map();
|
|
3906
|
+
majorToContracts = /* @__PURE__ */ new Map();
|
|
3907
|
+
majorToActiveWrite = /* @__PURE__ */ new Map();
|
|
3908
|
+
/**
|
|
3909
|
+
* Add a contract to the catalog
|
|
3910
|
+
* @throws ContractError if duplicate identity
|
|
3911
|
+
*/
|
|
3912
|
+
add(contract, options) {
|
|
3913
|
+
const major = contract.identity.major;
|
|
3914
|
+
const digest = contract.identity.revisionDigest;
|
|
3915
|
+
const key = `${major}@${digest}`;
|
|
3916
|
+
const isActive = options?.isActive ?? true;
|
|
3917
|
+
const isWriteTarget = options?.isWriteTarget ?? true;
|
|
3918
|
+
if (this.contracts.has(key)) {
|
|
3919
|
+
throw new ContractError(
|
|
3920
|
+
CONTRACT_ERROR_CODES.DUPLICATE_IDENTITY,
|
|
3921
|
+
`Contract ${key} already in catalog`,
|
|
3922
|
+
{ identity: contract.identity }
|
|
3923
|
+
);
|
|
3924
|
+
}
|
|
3925
|
+
this.contracts.set(key, contract);
|
|
3926
|
+
if (!this.majorToContracts.has(major)) {
|
|
3927
|
+
this.majorToContracts.set(major, /* @__PURE__ */ new Set());
|
|
3928
|
+
}
|
|
3929
|
+
this.majorToContracts.get(major).add(key);
|
|
3930
|
+
if (isActive && isWriteTarget) {
|
|
3931
|
+
const existingActive = this.majorToActiveWrite.get(major);
|
|
3932
|
+
if (existingActive && existingActive !== key) {
|
|
3933
|
+
this.contracts.delete(key);
|
|
3934
|
+
this.majorToContracts.get(major)?.delete(key);
|
|
3935
|
+
throw new ContractError(
|
|
3936
|
+
CONTRACT_ERROR_CODES.MULTIPLE_ACTIVE_WRITE,
|
|
3937
|
+
`Major ${major} already has active write revision`,
|
|
3938
|
+
{ major, existing: existingActive, attempted: key }
|
|
3939
|
+
);
|
|
3940
|
+
}
|
|
3941
|
+
this.majorToActiveWrite.set(major, key);
|
|
3942
|
+
}
|
|
3943
|
+
}
|
|
3944
|
+
/**
|
|
3945
|
+
* Resolve a contract by major identity.
|
|
3946
|
+
* - 0 entries: returns undefined
|
|
3947
|
+
* - 1 entry: returns it
|
|
3948
|
+
* - multiple: if there's a unique active write, returns it; otherwise throws AMBIGUOUS_REVISION
|
|
3949
|
+
*/
|
|
3950
|
+
resolve(major) {
|
|
3951
|
+
const keys = this.majorToContracts.get(major);
|
|
3952
|
+
if (!keys || keys.size === 0) return void 0;
|
|
3953
|
+
if (keys.size === 1) {
|
|
3954
|
+
const firstKey = keys.values().next().value;
|
|
3955
|
+
return firstKey ? this.contracts.get(firstKey) : void 0;
|
|
3956
|
+
}
|
|
3957
|
+
const activeWriteKey = this.majorToActiveWrite.get(major);
|
|
3958
|
+
if (activeWriteKey) {
|
|
3959
|
+
return this.contracts.get(activeWriteKey);
|
|
3960
|
+
}
|
|
3961
|
+
throw new ContractError(
|
|
3962
|
+
CONTRACT_ERROR_CODES.AMBIGUOUS_REVISION,
|
|
3963
|
+
`Multiple revisions found for ${major} with no unique active write \u2014 specify digest`,
|
|
3964
|
+
{
|
|
3965
|
+
major,
|
|
3966
|
+
availableDigests: Array.from(keys).map((key) => this.contracts.get(key)?.identity.revisionDigest).filter((digest) => Boolean(digest))
|
|
3967
|
+
}
|
|
3968
|
+
);
|
|
3969
|
+
}
|
|
3970
|
+
/**
|
|
3971
|
+
* Resolve a contract by major identity and exact digest
|
|
3972
|
+
*/
|
|
3973
|
+
resolveByDigest(major, digest) {
|
|
3974
|
+
const key = `${major}@${digest}`;
|
|
3975
|
+
return this.contracts.get(key);
|
|
3976
|
+
}
|
|
3977
|
+
/**
|
|
3978
|
+
* List all catalog entries (all revisions)
|
|
3979
|
+
*/
|
|
3980
|
+
list() {
|
|
3981
|
+
return Array.from(this.contracts.values()).map((contract) => ({
|
|
3982
|
+
identity: contract.identity,
|
|
3983
|
+
contract
|
|
3984
|
+
}));
|
|
3985
|
+
}
|
|
3986
|
+
/**
|
|
3987
|
+
* Get catalog as JSON-serializable object (all revisions)
|
|
3988
|
+
*/
|
|
3989
|
+
toJSON() {
|
|
3990
|
+
return {
|
|
3991
|
+
contracts: this.list().map((entry) => ({
|
|
3992
|
+
major: entry.identity.major,
|
|
3993
|
+
authority: entry.identity.authority,
|
|
3994
|
+
namespace: entry.identity.namespace,
|
|
3995
|
+
revisionDigest: entry.identity.revisionDigest
|
|
3996
|
+
}))
|
|
3997
|
+
};
|
|
3998
|
+
}
|
|
3999
|
+
};
|
|
4000
|
+
}
|
|
4001
|
+
});
|
|
3200
4002
|
|
|
3201
4003
|
// src/index.ts
|
|
3202
|
-
var
|
|
3203
|
-
|
|
4004
|
+
var index_exports = {};
|
|
4005
|
+
__export(index_exports, {
|
|
4006
|
+
ALWAYS_PRESENT: () => ALWAYS_PRESENT_ITEMS,
|
|
4007
|
+
BASELINE_CONSTRAINTS: () => BASELINE_CONSTRAINTS,
|
|
4008
|
+
BASELINE_CONSTRAINTS_COUNT: () => BASELINE_CONSTRAINTS_COUNT,
|
|
4009
|
+
BASELINE_ITEMS_COUNT: () => BASELINE_ITEMS_COUNT,
|
|
4010
|
+
CONTRACT_ERROR_CODES: () => CONTRACT_ERROR_CODES,
|
|
4011
|
+
ContractCatalog: () => ContractCatalog,
|
|
4012
|
+
ContractError: () => ContractError,
|
|
4013
|
+
ContractRegistry: () => ContractRegistry,
|
|
4014
|
+
DEFAULT_MAX_CHARS: () => DEFAULT_MAX_CHARS,
|
|
4015
|
+
DEFAULT_SCHEMA: () => DEFAULT_SCHEMA,
|
|
4016
|
+
E2E_NORMALIZER_CONFIG: () => E2E_NORMALIZER_CONFIG,
|
|
4017
|
+
MIN_PROMPT_CHARS: () => MIN_PROMPT_CHARS,
|
|
4018
|
+
TARGET_ARTIFACT_TYPES: () => TARGET_ARTIFACT_TYPES,
|
|
4019
|
+
VALID_PACKET_TARGET_TYPES: () => VALID_PACKET_TARGET_TYPES,
|
|
4020
|
+
VERSION_INDEX_SCHEMA_VERSION: () => VERSION_INDEX_SCHEMA_VERSION,
|
|
4021
|
+
VERSION_LOCK_PATH: () => VERSION_LOCK_PATH,
|
|
4022
|
+
VERSION_LOCK_SCHEMA_VERSION: () => VERSION_LOCK_SCHEMA_VERSION,
|
|
4023
|
+
applyPreparedManagedHookBlocks: () => applyPreparedManagedHookBlocks,
|
|
4024
|
+
assemblePacket: () => assemblePacket,
|
|
4025
|
+
auditPackets: () => auditPackets,
|
|
4026
|
+
auditVersionLock: () => auditVersionLock,
|
|
4027
|
+
bootstrapVersionLock: () => bootstrapVersionLock,
|
|
4028
|
+
buildGraph: () => buildGraph,
|
|
4029
|
+
buildVersionIndex: () => buildVersionIndex,
|
|
4030
|
+
collectChangedPaths: () => collectChangedPaths,
|
|
4031
|
+
computeE2eCoverageStats: () => computeE2eCoverageStats,
|
|
4032
|
+
computeRevisionDigest: () => computeRevisionDigest,
|
|
4033
|
+
dirname: () => import_node_path7.dirname,
|
|
4034
|
+
discoverAndAuditPackets: () => discoverAndAuditPackets,
|
|
4035
|
+
discoverTargets: () => discoverTargets,
|
|
4036
|
+
doctorArtifactChain: () => doctorArtifactChain,
|
|
4037
|
+
extname: () => import_node_path7.extname,
|
|
4038
|
+
formatContextMarkdown: () => formatContextMarkdown,
|
|
4039
|
+
generateE2eRegistry: () => generateE2eRegistry,
|
|
4040
|
+
getArtifactTypeMetadata: () => getArtifactTypeMetadata,
|
|
4041
|
+
getTargetArtifactTypes: () => getTargetArtifactTypes,
|
|
4042
|
+
installManagedHookBlock: () => installManagedHookBlock,
|
|
4043
|
+
isOfficialNamespace: () => isOfficialNamespace,
|
|
4044
|
+
isPacketTargetType: () => isPacketTargetType,
|
|
4045
|
+
isPacketTargetTypeDynamic: () => isPacketTargetTypeDynamic,
|
|
4046
|
+
isTargetArtifactType: () => isTargetArtifactType,
|
|
4047
|
+
loadConfig: () => loadConfig,
|
|
4048
|
+
loadContract: () => loadContract,
|
|
4049
|
+
loadContractCatalog: () => loadContractCatalog,
|
|
4050
|
+
loadContractsFromDirectory: () => loadContractsFromDirectory,
|
|
4051
|
+
nextId: () => nextId,
|
|
4052
|
+
normalizeE2eLegacyArtifact: () => normalizeE2eLegacyArtifact,
|
|
4053
|
+
normalizeToCanonical: () => normalizeToCanonical,
|
|
4054
|
+
parseTargetSelector: () => parseTargetSelector,
|
|
4055
|
+
parseTargetsFile: () => parseTargetsFile,
|
|
4056
|
+
prepareManagedHookBlock: () => prepareManagedHookBlock,
|
|
4057
|
+
queryGraph: () => queryGraph,
|
|
4058
|
+
refreshVersionLock: () => refreshVersionLock,
|
|
4059
|
+
renderDoctorMarkdown: () => renderDoctorMarkdown,
|
|
4060
|
+
renderMermaid: () => renderMermaid,
|
|
4061
|
+
renderPacketMarkdown: () => renderPacketMarkdown,
|
|
4062
|
+
renderPacketPrompt: () => renderPacketPrompt,
|
|
4063
|
+
renderTraceVersionMarkdown: () => renderTraceVersionMarkdown,
|
|
4064
|
+
renderVersionLockAuditMarkdown: () => renderVersionLockAuditMarkdown,
|
|
4065
|
+
renderVersionLockRefreshMarkdown: () => renderVersionLockRefreshMarkdown,
|
|
4066
|
+
resolveArtifactContext: () => resolveArtifactContext,
|
|
4067
|
+
resolveArtifactGraphCli: () => resolveArtifactGraphCli,
|
|
4068
|
+
resolveArtifactTypeName: () => resolveArtifactTypeName,
|
|
4069
|
+
resolveCliTarget: () => resolveCliTarget,
|
|
4070
|
+
resolveGitHookPath: () => resolveGitHookPath,
|
|
4071
|
+
resolveMatrixEdges: () => resolveMatrixEdges,
|
|
4072
|
+
scanArtifacts: () => scanArtifacts,
|
|
4073
|
+
traceVersion: () => traceVersion,
|
|
4074
|
+
updateVersionLock: () => updateVersionLock,
|
|
4075
|
+
validateContractAgainstSchema: () => validateContractAgainstSchema,
|
|
4076
|
+
validateExecutableTraceability: () => validateExecutableTraceability,
|
|
4077
|
+
validateGraph: () => validateGraph,
|
|
4078
|
+
validateNamespaceAuthority: () => validateNamespaceAuthority,
|
|
4079
|
+
validatePacket: () => validatePacket,
|
|
4080
|
+
validatePacketMarkdown: () => validatePacketMarkdown,
|
|
4081
|
+
validatePacketPrompt: () => validatePacketPrompt,
|
|
4082
|
+
validatePolicyCompatibility: () => validatePolicyCompatibility,
|
|
4083
|
+
validateReviewResult: () => validateReviewResult,
|
|
4084
|
+
validateScenarioPrdLinkIndex: () => validateScenarioPrdLinkIndex,
|
|
4085
|
+
validateScenarioPrdLinks: () => validateScenarioPrdLinks,
|
|
4086
|
+
verifyDigest: () => verifyDigest,
|
|
4087
|
+
writeGraphCache: () => writeGraphCache
|
|
4088
|
+
});
|
|
4089
|
+
module.exports = __toCommonJS(index_exports);
|
|
3204
4090
|
function isTargetArtifactType(type) {
|
|
3205
4091
|
return isPacketTargetType(type);
|
|
3206
4092
|
}
|
|
@@ -3241,56 +4127,11 @@ function resolveArtifactTypeName(schema, token) {
|
|
|
3241
4127
|
}
|
|
3242
4128
|
return void 0;
|
|
3243
4129
|
}
|
|
3244
|
-
var DEFAULT_SCHEMA = {
|
|
3245
|
-
types: {
|
|
3246
|
-
decision: { paths: ["artifacts/decisions/**/*.md"], displayName: "\u51B3\u7B56\u6587\u6863", role: "decision", layer: "decision", aliases: ["decisions", "adr"] },
|
|
3247
|
-
entity: { paths: ["artifacts/entities/entity-registry.md"], displayName: "\u5B9E\u4F53\u6CE8\u518C\u8868", role: "context", layer: "domain", aliases: ["entity-registry"] },
|
|
3248
|
-
feature: { paths: ["artifacts/prd/features/**/*.md"], displayName: "\u529F\u80FD\u7279\u6027", role: "feature", layer: "prd", aliases: ["prd-feature", "prd_features"] },
|
|
3249
|
-
scenario: { paths: ["artifacts/scenarios/**/*.md"], displayName: "\u573A\u666F\u5267\u672C", role: "scenario", layer: "scenario", aliases: ["scenarios", "scenario-script"] },
|
|
3250
|
-
design: { paths: ["artifacts/design/**/*.md"], displayName: "\u8BBE\u8BA1\u89C4\u683C", role: "design", layer: "design", aliases: ["design-spec", "design_docs"] },
|
|
3251
|
-
test: { paths: ["heimdall/packages/**/*.test.ts"], displayName: "\u4EE3\u7801\u6CE8\u91CA\u8FFD\u6EAF", role: "context", layer: "implementation", aliases: ["code-test", "code-trace", "unit-test"] },
|
|
3252
|
-
e2e_test: { paths: ["artifacts/tests/e2e/*.md"], displayName: "E2E \u6D4B\u8BD5\u89C4\u683C", role: "e2e_test", layer: "verification", aliases: ["e2e-test", "e2e_tests", "tc"] },
|
|
3253
|
-
e2e_registry: { paths: ["artifacts/tests/e2e/e2e-test-registry.json"], displayName: "E2E \u6D4B\u8BD5\u6CE8\u518C\u8868", role: "context", layer: "verification", aliases: ["e2e-registry"] },
|
|
3254
|
-
"rule-golden-cases": { paths: ["artifacts/tests/rule-golden-cases.md"], displayName: "\u89C4\u5219\u9EC4\u91D1\u6D4B\u8BD5\u7528\u4F8B", role: "context", layer: "verification", aliases: ["rule_golden_cases"] },
|
|
3255
|
-
"test-strategy": { paths: ["artifacts/design/test-strategy.md"], displayName: "\u6D4B\u8BD5\u7B56\u7565", role: "context", layer: "verification", aliases: ["test_strategy"] },
|
|
3256
|
-
"traceability-matrix-v2": { paths: ["artifacts/traceability-matrix-v2.md"], displayName: "\u8FFD\u6EAF\u77E9\u9635 v2", role: "context", layer: "traceability", aliases: ["traceability_matrix_v2"] }
|
|
3257
|
-
},
|
|
3258
|
-
idPatterns: {
|
|
3259
|
-
decision: "^D-[A-Z]+-\\d+$",
|
|
3260
|
-
entity: "^E-\\d{3,}$",
|
|
3261
|
-
feature: "^[A-Z]{1,4}\\d+$",
|
|
3262
|
-
scenario: "^S-\\d+[a-z]?$",
|
|
3263
|
-
design: "^[A-Za-z0-9._-]+$",
|
|
3264
|
-
test: "^.+\\.test\\.ts$",
|
|
3265
|
-
e2e_test: "^.+:(TC-\\d+[a-z]?|FILE)$",
|
|
3266
|
-
e2e_registry: "^e2e-test-registry$",
|
|
3267
|
-
"rule-golden-cases": "^[A-Z]+-\\d{3}:(pass|fail|edge)$",
|
|
3268
|
-
"test-strategy": "^(unit|integration|e2e|desktop_chain|rule|contract)$",
|
|
3269
|
-
"traceability-matrix-v2": "^.+$"
|
|
3270
|
-
},
|
|
3271
|
-
relationFields: {
|
|
3272
|
-
feature: ["scenarios", "decisions", "depends_on", "design_docs"],
|
|
3273
|
-
scenario: ["\u5173\u8054\u529F\u80FD", "\u5173\u8054\u51B3\u7B56"],
|
|
3274
|
-
design: ["related_features", "related_scenarios"],
|
|
3275
|
-
test: ["@scenario", "@feature", "@entity", "@decision"],
|
|
3276
|
-
e2e_test: ["test_batch", "scope", "ac_coverage", "related_scenarios", "related_decisions", "related_entities", "\u8986\u76D6\u573A\u666F", "\u8986\u76D6\u529F\u80FD"],
|
|
3277
|
-
e2e_registry: ["batches"]
|
|
3278
|
-
},
|
|
3279
|
-
allowedEdges: [],
|
|
3280
|
-
forbiddenEdges: [{ from: "scenario", to: "entity", kind: "references" }],
|
|
3281
|
-
statuses: ["planned", "active", "done", "deprecated"],
|
|
3282
|
-
idRanges: {},
|
|
3283
|
-
e2e: {
|
|
3284
|
-
report_uncovered_scenarios: true,
|
|
3285
|
-
report_uncovered_features: true,
|
|
3286
|
-
runners: []
|
|
3287
|
-
}
|
|
3288
|
-
};
|
|
3289
4130
|
async function loadConfig(root) {
|
|
3290
|
-
const configPath = (0,
|
|
4131
|
+
const configPath = (0, import_node_path7.join)(root, "artifact-graph.config.yaml");
|
|
3291
4132
|
let parsed = {};
|
|
3292
4133
|
try {
|
|
3293
|
-
const raw = await (0,
|
|
4134
|
+
const raw = await (0, import_promises6.readFile)(configPath, "utf-8");
|
|
3294
4135
|
parsed = import_js_yaml.default.load(raw) ?? {};
|
|
3295
4136
|
} catch (error) {
|
|
3296
4137
|
if (error.code !== "ENOENT") {
|
|
@@ -3380,7 +4221,7 @@ function validateE2eConfig(e2e) {
|
|
|
3380
4221
|
if (typeof runner.root !== "string" || !runner.root.trim()) {
|
|
3381
4222
|
throw new Error(`Invalid e2e.runners[${runner.name}].root: must be a non-empty string.`);
|
|
3382
4223
|
}
|
|
3383
|
-
if ((0,
|
|
4224
|
+
if ((0, import_node_path7.isAbsolute)(runner.root)) {
|
|
3384
4225
|
throw new Error(`Invalid e2e.runners[${runner.name}].root: "${runner.root}" must not be an absolute path.`);
|
|
3385
4226
|
}
|
|
3386
4227
|
if (runner.root.replace(/\\/g, "/").split("/").includes("..")) {
|
|
@@ -3464,14 +4305,14 @@ async function scanArtifacts(root, schema) {
|
|
|
3464
4305
|
continue;
|
|
3465
4306
|
}
|
|
3466
4307
|
scannedFiles.set(file, type);
|
|
3467
|
-
const raw = await (0,
|
|
4308
|
+
const raw = await (0, import_promises6.readFile)((0, import_node_path7.join)(root, file), "utf-8");
|
|
3468
4309
|
const parsed = parseFile(type, file, raw, config);
|
|
3469
4310
|
nodes.push(...parsed.nodes);
|
|
3470
4311
|
edges.push(...parsed.edges);
|
|
3471
4312
|
scanDiagnostics.push(...parsed.diagnostics);
|
|
3472
4313
|
}
|
|
3473
4314
|
}
|
|
3474
|
-
const absoluteRoot = (0,
|
|
4315
|
+
const absoluteRoot = (0, import_node_path7.isAbsolute)(root) ? root : (0, import_node_path7.resolve)(root);
|
|
3475
4316
|
const graph = buildGraph(nodes, edges, scanDiagnostics, absoluteRoot);
|
|
3476
4317
|
return resolveMatrixEdges(graph);
|
|
3477
4318
|
}
|
|
@@ -3776,7 +4617,7 @@ async function validateScenarioPrdLinkIndex(root, graph) {
|
|
|
3776
4617
|
const indexPath = "artifacts/prd/feature-index.md";
|
|
3777
4618
|
let raw = "";
|
|
3778
4619
|
try {
|
|
3779
|
-
raw = await (0,
|
|
4620
|
+
raw = await (0, import_promises6.readFile)((0, import_node_path7.join)(root, indexPath), "utf-8");
|
|
3780
4621
|
} catch (error) {
|
|
3781
4622
|
if (error.code === "ENOENT") {
|
|
3782
4623
|
return [];
|
|
@@ -3966,11 +4807,11 @@ function nextId(graph, schema, type, rangeName) {
|
|
|
3966
4807
|
throw new Error(`ID range ${type}.${rangeName} is exhausted`);
|
|
3967
4808
|
}
|
|
3968
4809
|
async function writeGraphCache(root, graph) {
|
|
3969
|
-
const cacheDir = (0,
|
|
3970
|
-
await (0,
|
|
3971
|
-
await (0,
|
|
4810
|
+
const cacheDir = (0, import_node_path7.join)(root, ".artifact-graph");
|
|
4811
|
+
await (0, import_promises6.mkdir)(cacheDir, { recursive: true });
|
|
4812
|
+
await (0, import_promises6.writeFile)((0, import_node_path7.join)(cacheDir, "index.json"), `${JSON.stringify(graph, null, 2)}
|
|
3972
4813
|
`);
|
|
3973
|
-
const db = new import_better_sqlite3.default((0,
|
|
4814
|
+
const db = new import_better_sqlite3.default((0, import_node_path7.join)(cacheDir, "graph.sqlite"));
|
|
3974
4815
|
try {
|
|
3975
4816
|
db.exec(`
|
|
3976
4817
|
DROP TABLE IF EXISTS nodes;
|
|
@@ -4081,7 +4922,7 @@ function parseFile(type, path, raw, schema = DEFAULT_SCHEMA) {
|
|
|
4081
4922
|
}
|
|
4082
4923
|
function parseGenericMarkdown(type, path, raw, schema) {
|
|
4083
4924
|
const diagnostics = [];
|
|
4084
|
-
const ext = (0,
|
|
4925
|
+
const ext = (0, import_node_path7.extname)(path).toLowerCase();
|
|
4085
4926
|
if (ext !== ".md" && ext !== ".markdown") {
|
|
4086
4927
|
diagnostics.push(issue(
|
|
4087
4928
|
"UNSUPPORTED_FORMAT",
|
|
@@ -4308,7 +5149,7 @@ function parseDecisions(path, raw) {
|
|
|
4308
5149
|
}
|
|
4309
5150
|
function isTestFile(filePath) {
|
|
4310
5151
|
const normalized = filePath.replace(/\\/g, "/");
|
|
4311
|
-
const name = (0,
|
|
5152
|
+
const name = (0, import_node_path7.basename)(normalized);
|
|
4312
5153
|
if (/\.(test|spec)\.[^.]+$/.test(name)) return true;
|
|
4313
5154
|
if (/(^|\/)(tests|test|__tests__)\//.test(normalized)) return true;
|
|
4314
5155
|
if (/\w+Tests?\.java$/.test(name)) return true;
|
|
@@ -4599,7 +5440,7 @@ function parseE2eTest(path, raw) {
|
|
|
4599
5440
|
});
|
|
4600
5441
|
const nodes = [];
|
|
4601
5442
|
const edges = [];
|
|
4602
|
-
const batch = String(data.test_batch ?? (0,
|
|
5443
|
+
const batch = String(data.test_batch ?? (0, import_node_path7.basename)(path, (0, import_node_path7.extname)(path))).trim();
|
|
4603
5444
|
const frontmatterScenarios = toArray(data.related_scenarios).map((value) => String(value).trim()).filter(Boolean);
|
|
4604
5445
|
const scopeFeatures = extractCodes(String(data.scope ?? ""), "feature");
|
|
4605
5446
|
const frontmatterFeatures = [.../* @__PURE__ */ new Set([...Object.keys(asRecord(data.ac_coverage)), ...scopeFeatures])];
|
|
@@ -4672,7 +5513,7 @@ function parseE2eRegistry(path, raw) {
|
|
|
4672
5513
|
data = { parseError: error.message };
|
|
4673
5514
|
}
|
|
4674
5515
|
return {
|
|
4675
|
-
nodes: [{ type: "e2e_registry", code: (0,
|
|
5516
|
+
nodes: [{ type: "e2e_registry", code: (0, import_node_path7.basename)(path, (0, import_node_path7.extname)(path)), title: "E2E Test Registry", path, line: 1, attrs: data }],
|
|
4676
5517
|
edges: []
|
|
4677
5518
|
};
|
|
4678
5519
|
}
|
|
@@ -5653,10 +6494,10 @@ function validateE2eRegistry(graph) {
|
|
|
5653
6494
|
async function validateExecutableTraceability(root, config) {
|
|
5654
6495
|
const issues = [];
|
|
5655
6496
|
const schema = config ?? await loadConfig(root);
|
|
5656
|
-
const e2eDir = (0,
|
|
6497
|
+
const e2eDir = (0, import_node_path7.join)(root, "artifacts", "tests", "e2e");
|
|
5657
6498
|
let e2eFiles;
|
|
5658
6499
|
try {
|
|
5659
|
-
e2eFiles = (await (0,
|
|
6500
|
+
e2eFiles = (await (0, import_promises6.readdir)(e2eDir)).filter((name) => /^test-.*\.md$/.test(name)).map((name) => (0, import_node_path7.join)(e2eDir, name));
|
|
5660
6501
|
} catch {
|
|
5661
6502
|
return [];
|
|
5662
6503
|
}
|
|
@@ -5665,11 +6506,11 @@ async function validateExecutableTraceability(root, config) {
|
|
|
5665
6506
|
const tcKeyToFields = /* @__PURE__ */ new Map();
|
|
5666
6507
|
const mdBatches = /* @__PURE__ */ new Set();
|
|
5667
6508
|
for (const filePath of e2eFiles) {
|
|
5668
|
-
const raw = await (0,
|
|
5669
|
-
const relPath = (0,
|
|
6509
|
+
const raw = await (0, import_promises6.readFile)(filePath, "utf-8");
|
|
6510
|
+
const relPath = (0, import_node_path7.relative)(root, filePath).split("\\").join("/");
|
|
5670
6511
|
const parsed = (0, import_gray_matter.default)(raw);
|
|
5671
6512
|
const data = parsed.data;
|
|
5672
|
-
const batch = String(data.test_batch ?? (0,
|
|
6513
|
+
const batch = String(data.test_batch ?? (0, import_node_path7.basename)(filePath, (0, import_node_path7.extname)(filePath))).trim();
|
|
5673
6514
|
const lines = raw.split(/\r?\n/);
|
|
5674
6515
|
const tcStarts = [];
|
|
5675
6516
|
lines.forEach((line, index) => {
|
|
@@ -5714,10 +6555,10 @@ async function validateExecutableTraceability(root, config) {
|
|
|
5714
6555
|
const tcAnnotationRegex = /\/\/!?\s*@(?:e2e_test|tc)\s+(\S+?)\s+\[(\w+)\]/;
|
|
5715
6556
|
const tcAnnotationNoLevelRegex = /\/\/!?\s*@(?:e2e_test|tc)\s+(\S+)/;
|
|
5716
6557
|
for (const specFile of specFiles) {
|
|
5717
|
-
const fullSpecPath = (0,
|
|
6558
|
+
const fullSpecPath = (0, import_node_path7.join)(root, specFile);
|
|
5718
6559
|
let content;
|
|
5719
6560
|
try {
|
|
5720
|
-
content = await (0,
|
|
6561
|
+
content = await (0, import_promises6.readFile)(fullSpecPath, "utf-8");
|
|
5721
6562
|
} catch {
|
|
5722
6563
|
continue;
|
|
5723
6564
|
}
|
|
@@ -5794,7 +6635,7 @@ async function validateExecutableTraceability(root, config) {
|
|
|
5794
6635
|
if (entry.testId) {
|
|
5795
6636
|
let content;
|
|
5796
6637
|
try {
|
|
5797
|
-
content = await (0,
|
|
6638
|
+
content = await (0, import_promises6.readFile)((0, import_node_path7.join)(root, normalizedRefFile), "utf-8");
|
|
5798
6639
|
} catch {
|
|
5799
6640
|
continue;
|
|
5800
6641
|
}
|
|
@@ -5971,16 +6812,16 @@ async function computeE2eCoverageStats(graph, root, thresholds = {}) {
|
|
|
5971
6812
|
let withExecutableRef = 0;
|
|
5972
6813
|
const statusBreakdown = {};
|
|
5973
6814
|
const chainTypeBreakdown = {};
|
|
5974
|
-
const e2eDir = (0,
|
|
6815
|
+
const e2eDir = (0, import_node_path7.join)(root, "artifacts", "tests", "e2e");
|
|
5975
6816
|
const tcFieldsMap = /* @__PURE__ */ new Map();
|
|
5976
6817
|
let e2eFiles;
|
|
5977
6818
|
try {
|
|
5978
|
-
e2eFiles = (await (0,
|
|
6819
|
+
e2eFiles = (await (0, import_promises6.readdir)(e2eDir)).filter((name) => /^test-.*\.md$/.test(name)).map((name) => (0, import_node_path7.join)(e2eDir, name));
|
|
5979
6820
|
} catch {
|
|
5980
6821
|
e2eFiles = [];
|
|
5981
6822
|
}
|
|
5982
6823
|
for (const filePath of e2eFiles) {
|
|
5983
|
-
const raw = await (0,
|
|
6824
|
+
const raw = await (0, import_promises6.readFile)(filePath, "utf-8");
|
|
5984
6825
|
const lines = raw.split(/\r?\n/);
|
|
5985
6826
|
const tcStarts = [];
|
|
5986
6827
|
lines.forEach((line, index) => {
|
|
@@ -5990,7 +6831,7 @@ async function computeE2eCoverageStats(graph, root, thresholds = {}) {
|
|
|
5990
6831
|
}
|
|
5991
6832
|
});
|
|
5992
6833
|
const parsed = (0, import_gray_matter.default)(raw);
|
|
5993
|
-
const batch = String(parsed.data.test_batch ?? (0,
|
|
6834
|
+
const batch = String(parsed.data.test_batch ?? (0, import_node_path7.basename)(filePath, (0, import_node_path7.extname)(filePath))).trim();
|
|
5994
6835
|
for (let i = 0; i < tcStarts.length; i++) {
|
|
5995
6836
|
const start = tcStarts[i];
|
|
5996
6837
|
const end = tcStarts[i + 1]?.index ?? lines.length;
|
|
@@ -6053,7 +6894,7 @@ async function computeE2eCoverageStats(graph, root, thresholds = {}) {
|
|
|
6053
6894
|
let hasActiveE2eRef = false;
|
|
6054
6895
|
for (const entry of parseExecutableRefLines(execRef)) {
|
|
6055
6896
|
const normalized = resolveExecutableRefFile(entry.file, allProjectFiles);
|
|
6056
|
-
if (!normalized || !(0, import_node_fs4.existsSync)((0,
|
|
6897
|
+
if (!normalized || !(0, import_node_fs4.existsSync)((0, import_node_path7.join)(root, normalized))) continue;
|
|
6057
6898
|
const accepting = await getAcceptingRunners(root, normalized, runners);
|
|
6058
6899
|
if (accepting.some((runner) => runner.kind === "e2e")) {
|
|
6059
6900
|
hasActiveE2eRef = true;
|
|
@@ -6112,7 +6953,7 @@ async function computeE2eCoverageStats(graph, root, thresholds = {}) {
|
|
|
6112
6953
|
const acCoverageRateByFeature = {};
|
|
6113
6954
|
const featureAcMap = /* @__PURE__ */ new Map();
|
|
6114
6955
|
for (const node of featureNodes) {
|
|
6115
|
-
const acs = parseAcceptanceCriteria(await (0,
|
|
6956
|
+
const acs = parseAcceptanceCriteria(await (0, import_promises6.readFile)((0, import_node_path7.join)(root, node.path), "utf-8"));
|
|
6116
6957
|
featureAcMap.set(node.code, new Set(acs));
|
|
6117
6958
|
}
|
|
6118
6959
|
const coveredAcByFeature = /* @__PURE__ */ new Map();
|
|
@@ -6154,10 +6995,10 @@ async function computeE2eCoverageStats(graph, root, thresholds = {}) {
|
|
|
6154
6995
|
};
|
|
6155
6996
|
}
|
|
6156
6997
|
async function generateE2eRegistry(root, opts) {
|
|
6157
|
-
const e2eDir = (0,
|
|
6998
|
+
const e2eDir = (0, import_node_path7.join)(root, "artifacts", "tests", "e2e");
|
|
6158
6999
|
let files;
|
|
6159
7000
|
try {
|
|
6160
|
-
files = (await (0,
|
|
7001
|
+
files = (await (0, import_promises6.readdir)(e2eDir)).filter((name) => /^test-.*\.md$/.test(name)).sort();
|
|
6161
7002
|
} catch {
|
|
6162
7003
|
return {
|
|
6163
7004
|
registry_version: "1.0",
|
|
@@ -6170,11 +7011,11 @@ async function generateE2eRegistry(root, opts) {
|
|
|
6170
7011
|
const batches = [];
|
|
6171
7012
|
let totalTestCases = 0;
|
|
6172
7013
|
for (const file of files) {
|
|
6173
|
-
const filePath = (0,
|
|
6174
|
-
const raw = await (0,
|
|
7014
|
+
const filePath = (0, import_node_path7.join)(e2eDir, file);
|
|
7015
|
+
const raw = await (0, import_promises6.readFile)(filePath, "utf-8");
|
|
6175
7016
|
const parsed = (0, import_gray_matter.default)(raw);
|
|
6176
7017
|
const data = parsed.data;
|
|
6177
|
-
const batch = String(data.test_batch ?? (0,
|
|
7018
|
+
const batch = String(data.test_batch ?? (0, import_node_path7.basename)(file, (0, import_node_path7.extname)(file))).trim();
|
|
6178
7019
|
const relPath = `artifacts/tests/e2e/${file}`;
|
|
6179
7020
|
const scope = String(data.scope ?? "").trim();
|
|
6180
7021
|
const acCoverage = normalizeAcCoverageForRegistry(data.ac_coverage);
|
|
@@ -6263,20 +7104,6 @@ function parseExecutableRefLines(ref) {
|
|
|
6263
7104
|
}
|
|
6264
7105
|
return results;
|
|
6265
7106
|
}
|
|
6266
|
-
var VALID_TC_STATUSES = /* @__PURE__ */ new Set(["created", "automated", "verified", "waived"]);
|
|
6267
|
-
var VALID_CHAIN_TYPES = /* @__PURE__ */ new Set([
|
|
6268
|
-
"desktop_chain",
|
|
6269
|
-
"mock_playwright",
|
|
6270
|
-
"core_e2e",
|
|
6271
|
-
"cli_e2e",
|
|
6272
|
-
"ui_sidecar_bridge",
|
|
6273
|
-
"partial_sidecar",
|
|
6274
|
-
"partial_rust"
|
|
6275
|
-
]);
|
|
6276
|
-
var DEPRECATED_CHAIN_TYPE_ALIASES = {
|
|
6277
|
-
core_only: "core_e2e",
|
|
6278
|
-
frontend_only: "mock_playwright"
|
|
6279
|
-
};
|
|
6280
7107
|
function isDesktopChainType(chainType) {
|
|
6281
7108
|
const normalizedChainType = chainType.trim().toLowerCase();
|
|
6282
7109
|
return normalizedChainType === "desktop_chain" || normalizedChainType === "";
|
|
@@ -6312,10 +7139,10 @@ async function validatePartialRustEvidence(tcFields, tcKey, root, allFiles) {
|
|
|
6312
7139
|
if (!normalizedPath) {
|
|
6313
7140
|
return { hasValidPartialRust: false, detail: `partial_rust file not found: ${ref.file}` };
|
|
6314
7141
|
}
|
|
6315
|
-
const fullPath = (0,
|
|
7142
|
+
const fullPath = (0, import_node_path7.join)(root, normalizedPath);
|
|
6316
7143
|
let content;
|
|
6317
7144
|
try {
|
|
6318
|
-
content = await (0,
|
|
7145
|
+
content = await (0, import_promises6.readFile)(fullPath, "utf-8");
|
|
6319
7146
|
} catch {
|
|
6320
7147
|
return { hasValidPartialRust: false, detail: `partial_rust file not found: ${ref.file}` };
|
|
6321
7148
|
}
|
|
@@ -6349,7 +7176,7 @@ function detectTestLevel(specFile, content) {
|
|
|
6349
7176
|
}
|
|
6350
7177
|
function resolveExecutableRefFile(refFile, allFiles) {
|
|
6351
7178
|
const normalized = refFile.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
6352
|
-
if (!normalized || (0,
|
|
7179
|
+
if (!normalized || (0, import_node_path7.isAbsolute)(refFile) || normalized.split("/").includes("..")) {
|
|
6353
7180
|
return void 0;
|
|
6354
7181
|
}
|
|
6355
7182
|
if (allFiles.includes(normalized)) {
|
|
@@ -6424,9 +7251,9 @@ function escapeRegExp(value) {
|
|
|
6424
7251
|
}
|
|
6425
7252
|
async function hasMarkdownTc(tcKey, e2eDir) {
|
|
6426
7253
|
const [batch, tcId] = tcKey.split(":");
|
|
6427
|
-
const filePath = (0,
|
|
7254
|
+
const filePath = (0, import_node_path7.join)(e2eDir, `${batch}.md`);
|
|
6428
7255
|
try {
|
|
6429
|
-
const raw = await (0,
|
|
7256
|
+
const raw = await (0, import_promises6.readFile)(filePath, "utf-8");
|
|
6430
7257
|
const tcRegex = new RegExp(`^#{2,3}\\s+${escapeRegExp(tcId)}\\s*[:\uFF1A]?`, "m");
|
|
6431
7258
|
return tcRegex.test(raw);
|
|
6432
7259
|
} catch {
|
|
@@ -6446,17 +7273,17 @@ async function findFiles(root, patterns) {
|
|
|
6446
7273
|
return [...matched].sort();
|
|
6447
7274
|
}
|
|
6448
7275
|
async function walk(root, current = root) {
|
|
6449
|
-
const entries = await (0,
|
|
7276
|
+
const entries = await (0, import_promises6.readdir)(current, { withFileTypes: true });
|
|
6450
7277
|
const files = [];
|
|
6451
7278
|
for (const entry of entries) {
|
|
6452
7279
|
if (entry.name === "node_modules" || entry.name === "dist" || entry.name === ".git" || entry.name === ".artifact-graph") {
|
|
6453
7280
|
continue;
|
|
6454
7281
|
}
|
|
6455
|
-
const fullPath = (0,
|
|
7282
|
+
const fullPath = (0, import_node_path7.join)(current, entry.name);
|
|
6456
7283
|
if (entry.isDirectory()) {
|
|
6457
7284
|
files.push(...await walk(root, fullPath));
|
|
6458
7285
|
} else {
|
|
6459
|
-
files.push((0,
|
|
7286
|
+
files.push((0, import_node_path7.relative)(root, fullPath).split("\\").join("/"));
|
|
6460
7287
|
}
|
|
6461
7288
|
}
|
|
6462
7289
|
return files;
|
|
@@ -6671,7 +7498,7 @@ function normalizeDesignCode(value) {
|
|
|
6671
7498
|
if (!raw) {
|
|
6672
7499
|
return "";
|
|
6673
7500
|
}
|
|
6674
|
-
return (0,
|
|
7501
|
+
return (0, import_node_path7.basename)(raw, (0, import_node_path7.extname)(raw));
|
|
6675
7502
|
}
|
|
6676
7503
|
function toUid(type, code) {
|
|
6677
7504
|
return `${type}:${code}`;
|
|
@@ -6768,30 +7595,6 @@ function discoverTargets(graph, options) {
|
|
|
6768
7595
|
}
|
|
6769
7596
|
return result;
|
|
6770
7597
|
}
|
|
6771
|
-
var CONTEXT_CATEGORIES = {
|
|
6772
|
-
feature: "prd",
|
|
6773
|
-
scenario: "scenario",
|
|
6774
|
-
decision: "decision",
|
|
6775
|
-
design: "design",
|
|
6776
|
-
entity: "entity",
|
|
6777
|
-
"interface_contracts": "contract",
|
|
6778
|
-
"data_contracts": "contract",
|
|
6779
|
-
"application_state_machines": "contract",
|
|
6780
|
-
"error_model": "contract",
|
|
6781
|
-
"report-contracts": "contract",
|
|
6782
|
-
"ui-flow-contracts": "contract",
|
|
6783
|
-
"non-functional-budgets": "contract",
|
|
6784
|
-
"domain-glossary": "domain",
|
|
6785
|
-
"bounded-context-map": "domain",
|
|
6786
|
-
"domain-invariants": "domain",
|
|
6787
|
-
"generation-packet-spec": "domain",
|
|
6788
|
-
"rule-golden-cases": "verification",
|
|
6789
|
-
"test-strategy": "verification",
|
|
6790
|
-
"verification-fixtures": "verification",
|
|
6791
|
-
"implementation-blueprint": "blueprint",
|
|
6792
|
-
"traceability-matrix-v2": "matrix"
|
|
6793
|
-
};
|
|
6794
|
-
var TIER_ORDER = ["baseline", "target", "direct", "matrix", "transitive"];
|
|
6795
7598
|
function resolveArtifactContext(graph, opts) {
|
|
6796
7599
|
const mode = opts.mode ?? "full";
|
|
6797
7600
|
const maxPerCategory = opts.maxPerCategory ?? 20;
|
|
@@ -6960,7 +7763,7 @@ function resolveArtifactContext(graph, opts) {
|
|
|
6960
7763
|
}
|
|
6961
7764
|
if (root) {
|
|
6962
7765
|
for (const ap of ALWAYS_PRESENT_ITEMS) {
|
|
6963
|
-
const fullPath = (0,
|
|
7766
|
+
const fullPath = (0, import_node_path7.join)(root, ap.path);
|
|
6964
7767
|
let stat;
|
|
6965
7768
|
try {
|
|
6966
7769
|
stat = (0, import_node_fs4.statSync)(fullPath);
|
|
@@ -7190,14 +7993,133 @@ function formatContextMarkdown(manifest) {
|
|
|
7190
7993
|
}
|
|
7191
7994
|
return lines.join("\n");
|
|
7192
7995
|
}
|
|
7996
|
+
var import_better_sqlite3, import_gray_matter, import_js_yaml, import_node_fs4, import_promises6, import_node_path7, TARGET_ARTIFACT_TYPES, NON_TARGET_ROLES, DEFAULT_SCHEMA, VALID_TC_STATUSES, VALID_CHAIN_TYPES, DEPRECATED_CHAIN_TYPE_ALIASES, CONTEXT_CATEGORIES, TIER_ORDER;
|
|
7997
|
+
var init_index = __esm({
|
|
7998
|
+
"src/index.ts"() {
|
|
7999
|
+
import_better_sqlite3 = __toESM(require("better-sqlite3"), 1);
|
|
8000
|
+
import_gray_matter = __toESM(require("gray-matter"), 1);
|
|
8001
|
+
import_js_yaml = __toESM(require("js-yaml"), 1);
|
|
8002
|
+
import_node_fs4 = require("fs");
|
|
8003
|
+
import_promises6 = require("fs/promises");
|
|
8004
|
+
import_node_path7 = require("path");
|
|
8005
|
+
init_packet_constants();
|
|
8006
|
+
init_packet_validator();
|
|
8007
|
+
init_glob_matcher();
|
|
8008
|
+
init_packet_constants();
|
|
8009
|
+
init_target_selector();
|
|
8010
|
+
init_packet_assembler();
|
|
8011
|
+
init_packet_audit();
|
|
8012
|
+
init_packet_validator();
|
|
8013
|
+
init_packet_prompt();
|
|
8014
|
+
init_packet_prompt_validator();
|
|
8015
|
+
init_versioned_traceability();
|
|
8016
|
+
init_cli_resolver();
|
|
8017
|
+
init_git_changes();
|
|
8018
|
+
init_git_hook_path();
|
|
8019
|
+
init_hook_installer();
|
|
8020
|
+
init_review_result_validator();
|
|
8021
|
+
init_contract_kernel();
|
|
8022
|
+
TARGET_ARTIFACT_TYPES = VALID_PACKET_TARGET_TYPES;
|
|
8023
|
+
NON_TARGET_ROLES = ["context", "candidate", "not-recommended"];
|
|
8024
|
+
DEFAULT_SCHEMA = {
|
|
8025
|
+
types: {
|
|
8026
|
+
decision: { paths: ["artifacts/decisions/**/*.md"], displayName: "\u51B3\u7B56\u6587\u6863", role: "decision", layer: "decision", aliases: ["decisions", "adr"] },
|
|
8027
|
+
entity: { paths: ["artifacts/entities/entity-registry.md"], displayName: "\u5B9E\u4F53\u6CE8\u518C\u8868", role: "context", layer: "domain", aliases: ["entity-registry"] },
|
|
8028
|
+
feature: { paths: ["artifacts/prd/features/**/*.md"], displayName: "\u529F\u80FD\u7279\u6027", role: "feature", layer: "prd", aliases: ["prd-feature", "prd_features"] },
|
|
8029
|
+
scenario: { paths: ["artifacts/scenarios/**/*.md"], displayName: "\u573A\u666F\u5267\u672C", role: "scenario", layer: "scenario", aliases: ["scenarios", "scenario-script"] },
|
|
8030
|
+
design: { paths: ["artifacts/design/**/*.md"], displayName: "\u8BBE\u8BA1\u89C4\u683C", role: "design", layer: "design", aliases: ["design-spec", "design_docs"] },
|
|
8031
|
+
test: { paths: ["heimdall/packages/**/*.test.ts"], displayName: "\u4EE3\u7801\u6CE8\u91CA\u8FFD\u6EAF", role: "context", layer: "implementation", aliases: ["code-test", "code-trace", "unit-test"] },
|
|
8032
|
+
e2e_test: { paths: ["artifacts/tests/e2e/*.md"], displayName: "E2E \u6D4B\u8BD5\u89C4\u683C", role: "e2e_test", layer: "verification", aliases: ["e2e-test", "e2e_tests", "tc"] },
|
|
8033
|
+
e2e_registry: { paths: ["artifacts/tests/e2e/e2e-test-registry.json"], displayName: "E2E \u6D4B\u8BD5\u6CE8\u518C\u8868", role: "context", layer: "verification", aliases: ["e2e-registry"] },
|
|
8034
|
+
"rule-golden-cases": { paths: ["artifacts/tests/rule-golden-cases.md"], displayName: "\u89C4\u5219\u9EC4\u91D1\u6D4B\u8BD5\u7528\u4F8B", role: "context", layer: "verification", aliases: ["rule_golden_cases"] },
|
|
8035
|
+
"test-strategy": { paths: ["artifacts/design/test-strategy.md"], displayName: "\u6D4B\u8BD5\u7B56\u7565", role: "context", layer: "verification", aliases: ["test_strategy"] },
|
|
8036
|
+
"traceability-matrix-v2": { paths: ["artifacts/traceability-matrix-v2.md"], displayName: "\u8FFD\u6EAF\u77E9\u9635 v2", role: "context", layer: "traceability", aliases: ["traceability_matrix_v2"] }
|
|
8037
|
+
},
|
|
8038
|
+
idPatterns: {
|
|
8039
|
+
decision: "^D-[A-Z]+-\\d+$",
|
|
8040
|
+
entity: "^E-\\d{3,}$",
|
|
8041
|
+
feature: "^[A-Z]{1,4}\\d+$",
|
|
8042
|
+
scenario: "^S-\\d+[a-z]?$",
|
|
8043
|
+
design: "^[A-Za-z0-9._-]+$",
|
|
8044
|
+
test: "^.+\\.test\\.ts$",
|
|
8045
|
+
e2e_test: "^.+:(TC-\\d+[a-z]?|FILE)$",
|
|
8046
|
+
e2e_registry: "^e2e-test-registry$",
|
|
8047
|
+
"rule-golden-cases": "^[A-Z]+-\\d{3}:(pass|fail|edge)$",
|
|
8048
|
+
"test-strategy": "^(unit|integration|e2e|desktop_chain|rule|contract)$",
|
|
8049
|
+
"traceability-matrix-v2": "^.+$"
|
|
8050
|
+
},
|
|
8051
|
+
relationFields: {
|
|
8052
|
+
feature: ["scenarios", "decisions", "depends_on", "design_docs"],
|
|
8053
|
+
scenario: ["\u5173\u8054\u529F\u80FD", "\u5173\u8054\u51B3\u7B56"],
|
|
8054
|
+
design: ["related_features", "related_scenarios"],
|
|
8055
|
+
test: ["@scenario", "@feature", "@entity", "@decision"],
|
|
8056
|
+
e2e_test: ["test_batch", "scope", "ac_coverage", "related_scenarios", "related_decisions", "related_entities", "\u8986\u76D6\u573A\u666F", "\u8986\u76D6\u529F\u80FD"],
|
|
8057
|
+
e2e_registry: ["batches"]
|
|
8058
|
+
},
|
|
8059
|
+
allowedEdges: [],
|
|
8060
|
+
forbiddenEdges: [{ from: "scenario", to: "entity", kind: "references" }],
|
|
8061
|
+
statuses: ["planned", "active", "done", "deprecated"],
|
|
8062
|
+
idRanges: {},
|
|
8063
|
+
e2e: {
|
|
8064
|
+
report_uncovered_scenarios: true,
|
|
8065
|
+
report_uncovered_features: true,
|
|
8066
|
+
runners: []
|
|
8067
|
+
}
|
|
8068
|
+
};
|
|
8069
|
+
VALID_TC_STATUSES = /* @__PURE__ */ new Set(["created", "automated", "verified", "waived"]);
|
|
8070
|
+
VALID_CHAIN_TYPES = /* @__PURE__ */ new Set([
|
|
8071
|
+
"desktop_chain",
|
|
8072
|
+
"mock_playwright",
|
|
8073
|
+
"core_e2e",
|
|
8074
|
+
"cli_e2e",
|
|
8075
|
+
"ui_sidecar_bridge",
|
|
8076
|
+
"partial_sidecar",
|
|
8077
|
+
"partial_rust"
|
|
8078
|
+
]);
|
|
8079
|
+
DEPRECATED_CHAIN_TYPE_ALIASES = {
|
|
8080
|
+
core_only: "core_e2e",
|
|
8081
|
+
frontend_only: "mock_playwright"
|
|
8082
|
+
};
|
|
8083
|
+
CONTEXT_CATEGORIES = {
|
|
8084
|
+
feature: "prd",
|
|
8085
|
+
scenario: "scenario",
|
|
8086
|
+
decision: "decision",
|
|
8087
|
+
design: "design",
|
|
8088
|
+
entity: "entity",
|
|
8089
|
+
"interface_contracts": "contract",
|
|
8090
|
+
"data_contracts": "contract",
|
|
8091
|
+
"application_state_machines": "contract",
|
|
8092
|
+
"error_model": "contract",
|
|
8093
|
+
"report-contracts": "contract",
|
|
8094
|
+
"ui-flow-contracts": "contract",
|
|
8095
|
+
"non-functional-budgets": "contract",
|
|
8096
|
+
"domain-glossary": "domain",
|
|
8097
|
+
"bounded-context-map": "domain",
|
|
8098
|
+
"domain-invariants": "domain",
|
|
8099
|
+
"generation-packet-spec": "domain",
|
|
8100
|
+
"rule-golden-cases": "verification",
|
|
8101
|
+
"test-strategy": "verification",
|
|
8102
|
+
"verification-fixtures": "verification",
|
|
8103
|
+
"implementation-blueprint": "blueprint",
|
|
8104
|
+
"traceability-matrix-v2": "matrix"
|
|
8105
|
+
};
|
|
8106
|
+
TIER_ORDER = ["baseline", "target", "direct", "matrix", "transitive"];
|
|
8107
|
+
}
|
|
8108
|
+
});
|
|
8109
|
+
init_index();
|
|
7193
8110
|
// Annotate the CommonJS export names for ESM import in node:
|
|
7194
8111
|
0 && (module.exports = {
|
|
7195
8112
|
ALWAYS_PRESENT,
|
|
7196
8113
|
BASELINE_CONSTRAINTS,
|
|
7197
8114
|
BASELINE_CONSTRAINTS_COUNT,
|
|
7198
8115
|
BASELINE_ITEMS_COUNT,
|
|
8116
|
+
CONTRACT_ERROR_CODES,
|
|
8117
|
+
ContractCatalog,
|
|
8118
|
+
ContractError,
|
|
8119
|
+
ContractRegistry,
|
|
7199
8120
|
DEFAULT_MAX_CHARS,
|
|
7200
8121
|
DEFAULT_SCHEMA,
|
|
8122
|
+
E2E_NORMALIZER_CONFIG,
|
|
7201
8123
|
MIN_PROMPT_CHARS,
|
|
7202
8124
|
TARGET_ARTIFACT_TYPES,
|
|
7203
8125
|
VALID_PACKET_TARGET_TYPES,
|
|
@@ -7213,6 +8135,7 @@ function formatContextMarkdown(manifest) {
|
|
|
7213
8135
|
buildVersionIndex,
|
|
7214
8136
|
collectChangedPaths,
|
|
7215
8137
|
computeE2eCoverageStats,
|
|
8138
|
+
computeRevisionDigest,
|
|
7216
8139
|
dirname,
|
|
7217
8140
|
discoverAndAuditPackets,
|
|
7218
8141
|
discoverTargets,
|
|
@@ -7223,11 +8146,17 @@ function formatContextMarkdown(manifest) {
|
|
|
7223
8146
|
getArtifactTypeMetadata,
|
|
7224
8147
|
getTargetArtifactTypes,
|
|
7225
8148
|
installManagedHookBlock,
|
|
8149
|
+
isOfficialNamespace,
|
|
7226
8150
|
isPacketTargetType,
|
|
7227
8151
|
isPacketTargetTypeDynamic,
|
|
7228
8152
|
isTargetArtifactType,
|
|
7229
8153
|
loadConfig,
|
|
8154
|
+
loadContract,
|
|
8155
|
+
loadContractCatalog,
|
|
8156
|
+
loadContractsFromDirectory,
|
|
7230
8157
|
nextId,
|
|
8158
|
+
normalizeE2eLegacyArtifact,
|
|
8159
|
+
normalizeToCanonical,
|
|
7231
8160
|
parseTargetSelector,
|
|
7232
8161
|
parseTargetsFile,
|
|
7233
8162
|
prepareManagedHookBlock,
|
|
@@ -7249,13 +8178,17 @@ function formatContextMarkdown(manifest) {
|
|
|
7249
8178
|
scanArtifacts,
|
|
7250
8179
|
traceVersion,
|
|
7251
8180
|
updateVersionLock,
|
|
8181
|
+
validateContractAgainstSchema,
|
|
7252
8182
|
validateExecutableTraceability,
|
|
7253
8183
|
validateGraph,
|
|
8184
|
+
validateNamespaceAuthority,
|
|
7254
8185
|
validatePacket,
|
|
7255
8186
|
validatePacketMarkdown,
|
|
7256
8187
|
validatePacketPrompt,
|
|
8188
|
+
validatePolicyCompatibility,
|
|
7257
8189
|
validateReviewResult,
|
|
7258
8190
|
validateScenarioPrdLinkIndex,
|
|
7259
8191
|
validateScenarioPrdLinks,
|
|
8192
|
+
verifyDigest,
|
|
7260
8193
|
writeGraphCache
|
|
7261
8194
|
});
|