artifact-graph 0.4.1 → 0.5.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 +44 -0
- package/INSTALL.md +110 -16
- package/README.md +22 -6
- package/README.zh-CN.md +21 -6
- package/dist/cli.js +356 -77
- package/dist/index.cjs +315 -63
- package/dist/index.d.cts +41 -3
- package/dist/index.d.ts +41 -3
- package/dist/index.js +316 -64
- package/package.json +2 -1
- package/schemas/review-result.schema.json +74 -1
- package/templates/git-hooks/pre-commit.sh +18 -1
package/dist/cli.js
CHANGED
|
@@ -4,15 +4,16 @@
|
|
|
4
4
|
import yaml2 from "js-yaml";
|
|
5
5
|
import { realpathSync } from "fs";
|
|
6
6
|
import { access as access2, mkdir as mkdir6, readFile as readFile3, writeFile as writeFile5 } from "fs/promises";
|
|
7
|
-
import { isAbsolute as
|
|
7
|
+
import { isAbsolute as isAbsolute4, join as join7 } from "path";
|
|
8
8
|
import { fileURLToPath } from "url";
|
|
9
9
|
|
|
10
10
|
// src/index.ts
|
|
11
11
|
import Database from "better-sqlite3";
|
|
12
12
|
import matter from "gray-matter";
|
|
13
13
|
import yaml from "js-yaml";
|
|
14
|
+
import { accessSync, constants as fsConstants, statSync } from "fs";
|
|
14
15
|
import { mkdir as mkdir4, readFile as readFile2, readdir, writeFile as writeFile3 } from "fs/promises";
|
|
15
|
-
import { basename as basename2, dirname as dirname3, extname, join as join5, relative as relative2 } from "path";
|
|
16
|
+
import { basename as basename2, dirname as dirname3, extname, isAbsolute as isAbsolute3, join as join5, relative as relative2, resolve as resolve3 } from "path";
|
|
16
17
|
|
|
17
18
|
// src/packet-constants.ts
|
|
18
19
|
var ALWAYS_PRESENT_ITEMS = [
|
|
@@ -91,13 +92,76 @@ function validatePacket(packet, schema) {
|
|
|
91
92
|
path: "target.id"
|
|
92
93
|
});
|
|
93
94
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
95
|
+
const isBaselineExplicitlyDisabled = packet.baselinePolicy === false;
|
|
96
|
+
const expectedPaths = new Set(ALWAYS_PRESENT_ITEMS.map((item) => item.path));
|
|
97
|
+
if (isBaselineExplicitlyDisabled) {
|
|
98
|
+
if (packet.requiredBaseline.total !== 0) {
|
|
99
|
+
issues.push({
|
|
100
|
+
severity: "error",
|
|
101
|
+
code: "PKT-004",
|
|
102
|
+
message: `baselinePolicy=false requires requiredBaseline.total=0, got ${packet.requiredBaseline.total}`,
|
|
103
|
+
path: "requiredBaseline.total"
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
if (packet.requiredBaseline.items.length !== 0) {
|
|
107
|
+
issues.push({
|
|
108
|
+
severity: "error",
|
|
109
|
+
code: "PKT-004",
|
|
110
|
+
message: `baselinePolicy=false requires requiredBaseline.items=[], got ${packet.requiredBaseline.items.length} item(s)`,
|
|
111
|
+
path: "requiredBaseline.items"
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
} else {
|
|
115
|
+
if (packet.requiredBaseline.total !== BASELINE_ITEMS_COUNT) {
|
|
116
|
+
issues.push({
|
|
117
|
+
severity: "error",
|
|
118
|
+
code: "PKT-004",
|
|
119
|
+
message: `requiredBaseline.total must be ${BASELINE_ITEMS_COUNT}, got ${packet.requiredBaseline.total}`,
|
|
120
|
+
path: "requiredBaseline.total"
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
if (packet.requiredBaseline.items.length !== BASELINE_ITEMS_COUNT) {
|
|
124
|
+
issues.push({
|
|
125
|
+
severity: "error",
|
|
126
|
+
code: "PKT-004",
|
|
127
|
+
message: `requiredBaseline.items.length must be ${BASELINE_ITEMS_COUNT}, got ${packet.requiredBaseline.items.length}`,
|
|
128
|
+
path: "requiredBaseline.items"
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
const actualPaths = packet.requiredBaseline.items.map((item) => item.path);
|
|
132
|
+
const actualPathSet = new Set(actualPaths);
|
|
133
|
+
if (actualPathSet.size !== actualPaths.length) {
|
|
134
|
+
issues.push({
|
|
135
|
+
severity: "error",
|
|
136
|
+
code: "PKT-004",
|
|
137
|
+
message: `requiredBaseline.items contains duplicate paths (${actualPaths.length} items, ${actualPathSet.size} unique)`,
|
|
138
|
+
path: "requiredBaseline.items"
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
const missingPaths = [];
|
|
142
|
+
for (const ep of expectedPaths) {
|
|
143
|
+
if (!actualPathSet.has(ep)) missingPaths.push(ep);
|
|
144
|
+
}
|
|
145
|
+
if (missingPaths.length > 0) {
|
|
146
|
+
issues.push({
|
|
147
|
+
severity: "error",
|
|
148
|
+
code: "PKT-004",
|
|
149
|
+
message: `requiredBaseline.items missing expected path(s): ${missingPaths.join(", ")}`,
|
|
150
|
+
path: "requiredBaseline.items"
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
const extraPaths = [];
|
|
154
|
+
for (const ap of actualPathSet) {
|
|
155
|
+
if (!expectedPaths.has(ap)) extraPaths.push(ap);
|
|
156
|
+
}
|
|
157
|
+
if (extraPaths.length > 0) {
|
|
158
|
+
issues.push({
|
|
159
|
+
severity: "error",
|
|
160
|
+
code: "PKT-004",
|
|
161
|
+
message: `requiredBaseline.items contains unexpected path(s): ${extraPaths.join(", ")}`,
|
|
162
|
+
path: "requiredBaseline.items"
|
|
163
|
+
});
|
|
164
|
+
}
|
|
101
165
|
}
|
|
102
166
|
const constraints = packet.implementationBlueprintDraft.constraints;
|
|
103
167
|
if (constraints.length !== BASELINE_CONSTRAINTS_COUNT) {
|
|
@@ -499,7 +563,10 @@ function assemblePacket(manifest, options) {
|
|
|
499
563
|
missing: [...manifest.missing],
|
|
500
564
|
missingDetails: manifest.missingDetails ? [...manifest.missingDetails] : void 0,
|
|
501
565
|
implementationBlueprintDraft: blueprintDraft,
|
|
502
|
-
validationCommands
|
|
566
|
+
validationCommands,
|
|
567
|
+
// @feature ACA17
|
|
568
|
+
// @decision D-ACA-17
|
|
569
|
+
baselinePolicy: manifest.baselinePolicy
|
|
503
570
|
};
|
|
504
571
|
return packet;
|
|
505
572
|
}
|
|
@@ -720,7 +787,9 @@ async function auditSingleTarget(target, graph, options) {
|
|
|
720
787
|
const manifest = resolveArtifactContext(graph, {
|
|
721
788
|
target: { type: target.type, id: target.id },
|
|
722
789
|
mode: options.mode,
|
|
723
|
-
maxPerCategory: options.maxPerCategory
|
|
790
|
+
maxPerCategory: options.maxPerCategory,
|
|
791
|
+
universalBaseline: options.universalBaseline,
|
|
792
|
+
root: options.root
|
|
724
793
|
});
|
|
725
794
|
const packet = assemblePacket(manifest, {
|
|
726
795
|
mode: options.mode,
|
|
@@ -857,6 +926,7 @@ async function discoverAndAuditPackets(root, options) {
|
|
|
857
926
|
type: d.type,
|
|
858
927
|
id: d.id
|
|
859
928
|
}));
|
|
929
|
+
const effectiveBaseline = options.universalBaseline ?? config.context?.universal_baseline;
|
|
860
930
|
return auditPackets(root, targets, {
|
|
861
931
|
root,
|
|
862
932
|
outDir: options.outDir,
|
|
@@ -866,7 +936,8 @@ async function discoverAndAuditPackets(root, options) {
|
|
|
866
936
|
summaryOnly: options.summaryOnly,
|
|
867
937
|
sampleTargets: options.sampleTargets,
|
|
868
938
|
summaryDetail: options.summaryDetail,
|
|
869
|
-
schema: config
|
|
939
|
+
schema: config,
|
|
940
|
+
universalBaseline: effectiveBaseline
|
|
870
941
|
}, graph);
|
|
871
942
|
}
|
|
872
943
|
|
|
@@ -2577,12 +2648,35 @@ var VALID_DECISIONS = /* @__PURE__ */ new Set([
|
|
|
2577
2648
|
var VALID_SEVERITIES = /* @__PURE__ */ new Set(["block", "warn", "info"]);
|
|
2578
2649
|
var VALID_FINDING_STATUSES = /* @__PURE__ */ new Set(["open", "resolved", "accepted", "superseded"]);
|
|
2579
2650
|
var VALID_EXECUTORS = /* @__PURE__ */ new Set(["script", "worker", "agent", "manual", "cli"]);
|
|
2651
|
+
var TOP_LEVEL_FIELDS = /* @__PURE__ */ new Set([
|
|
2652
|
+
"schema_version",
|
|
2653
|
+
"run_id",
|
|
2654
|
+
"stage_id",
|
|
2655
|
+
"attempt",
|
|
2656
|
+
"status",
|
|
2657
|
+
"decision",
|
|
2658
|
+
"summary",
|
|
2659
|
+
"outputs",
|
|
2660
|
+
"warnings",
|
|
2661
|
+
"blocking_reason",
|
|
2662
|
+
"degradation",
|
|
2663
|
+
"producer",
|
|
2664
|
+
"acceptance",
|
|
2665
|
+
"evidence",
|
|
2666
|
+
"review",
|
|
2667
|
+
"repair"
|
|
2668
|
+
]);
|
|
2580
2669
|
function validateReviewResult(input) {
|
|
2581
2670
|
const errors = [];
|
|
2582
2671
|
if (!input || typeof input !== "object" || Array.isArray(input)) {
|
|
2583
2672
|
return [{ path: "$", message: "Root must be a non-null object" }];
|
|
2584
2673
|
}
|
|
2585
2674
|
const obj = input;
|
|
2675
|
+
for (const key of Object.keys(obj)) {
|
|
2676
|
+
if (!TOP_LEVEL_FIELDS.has(key)) {
|
|
2677
|
+
errors.push({ path: `$.${key}`, message: "Unknown top-level property" });
|
|
2678
|
+
}
|
|
2679
|
+
}
|
|
2586
2680
|
if (obj.schema_version !== "1.0") {
|
|
2587
2681
|
errors.push({ path: "$.schema_version", message: `Must be "1.0", got ${JSON.stringify(obj.schema_version)}` });
|
|
2588
2682
|
}
|
|
@@ -2601,8 +2695,8 @@ function validateReviewResult(input) {
|
|
|
2601
2695
|
if (obj.stage_id !== void 0 && typeof obj.stage_id !== "string") {
|
|
2602
2696
|
errors.push({ path: "$.stage_id", message: "Must be a string if present" });
|
|
2603
2697
|
}
|
|
2604
|
-
if (obj.attempt !== void 0 && (!Number.isInteger(obj.attempt) || obj.attempt < 1)) {
|
|
2605
|
-
errors.push({ path: "$.attempt", message: "Must be
|
|
2698
|
+
if (obj.attempt !== void 0 && (!Number.isInteger(obj.attempt) || obj.attempt < 1 || obj.attempt > 3)) {
|
|
2699
|
+
errors.push({ path: "$.attempt", message: "Must be an integer from 1 through 3 if present" });
|
|
2606
2700
|
}
|
|
2607
2701
|
if (obj.outputs !== void 0) {
|
|
2608
2702
|
checkStringArray(obj.outputs, "$.outputs", errors);
|
|
@@ -2613,20 +2707,14 @@ function validateReviewResult(input) {
|
|
|
2613
2707
|
checkOptionalNullableString(obj.blocking_reason, "$.blocking_reason", errors);
|
|
2614
2708
|
checkOptionalNullableString(obj.degradation, "$.degradation", errors);
|
|
2615
2709
|
if (obj.producer !== void 0) {
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
}
|
|
2625
|
-
if (typeof p.name !== "string") {
|
|
2626
|
-
errors.push({ path: "$.producer.name", message: "Must be a string" });
|
|
2627
|
-
}
|
|
2628
|
-
checkOptionalString(p.skill, "$.producer.skill", errors);
|
|
2629
|
-
}
|
|
2710
|
+
validateProducer(obj.producer, "$.producer", errors);
|
|
2711
|
+
}
|
|
2712
|
+
const successfulAcceptance = obj.status === "SUCCEEDED" && (obj.decision === "PASS" || obj.decision === "PASS_WITH_RESIDUAL_MINOR");
|
|
2713
|
+
if (successfulAcceptance && obj.producer === void 0) {
|
|
2714
|
+
errors.push({ path: "$.producer", message: "Successful acceptance requires producer identity" });
|
|
2715
|
+
}
|
|
2716
|
+
if (obj.acceptance !== void 0) {
|
|
2717
|
+
validateAcceptance(obj.acceptance, obj.producer, "$.acceptance", errors);
|
|
2630
2718
|
}
|
|
2631
2719
|
if (obj.evidence !== void 0) {
|
|
2632
2720
|
if (!Array.isArray(obj.evidence)) {
|
|
@@ -2652,6 +2740,17 @@ function validateReviewResult(input) {
|
|
|
2652
2740
|
}
|
|
2653
2741
|
if (obj.review !== void 0) {
|
|
2654
2742
|
validateReviewData(obj.review, "$.review", errors);
|
|
2743
|
+
if (obj.decision === "PASS" || obj.decision === "PASS_WITH_RESIDUAL_MINOR") {
|
|
2744
|
+
const findings = isPlainObject(obj.review) && Array.isArray(obj.review.findings) ? obj.review.findings : [];
|
|
2745
|
+
findings.forEach((finding, index) => {
|
|
2746
|
+
if (isPlainObject(finding) && finding.severity === "block" && (finding.status === void 0 || finding.status === "open")) {
|
|
2747
|
+
errors.push({
|
|
2748
|
+
path: `$.review.findings[${index}]`,
|
|
2749
|
+
message: `${obj.decision} cannot contain an open block finding`
|
|
2750
|
+
});
|
|
2751
|
+
}
|
|
2752
|
+
});
|
|
2753
|
+
}
|
|
2655
2754
|
}
|
|
2656
2755
|
if (obj.repair !== void 0) {
|
|
2657
2756
|
if (!isPlainObject(obj.repair)) {
|
|
@@ -2687,6 +2786,47 @@ function checkStringArray(val, path, errors) {
|
|
|
2687
2786
|
function isPlainObject(val) {
|
|
2688
2787
|
return typeof val === "object" && val !== null && !Array.isArray(val);
|
|
2689
2788
|
}
|
|
2789
|
+
function validateProducer(val, path, errors) {
|
|
2790
|
+
if (!isPlainObject(val)) {
|
|
2791
|
+
errors.push({ path, message: "Must be an object" });
|
|
2792
|
+
return;
|
|
2793
|
+
}
|
|
2794
|
+
if (typeof val.executor !== "string") {
|
|
2795
|
+
errors.push({ path: `${path}.executor`, message: "Must be a string" });
|
|
2796
|
+
} else if (!VALID_EXECUTORS.has(val.executor)) {
|
|
2797
|
+
errors.push({ path: `${path}.executor`, message: `Must be one of ${[...VALID_EXECUTORS].join(", ")}; got ${JSON.stringify(val.executor)}` });
|
|
2798
|
+
}
|
|
2799
|
+
if (typeof val.name !== "string" || val.name.length === 0) {
|
|
2800
|
+
errors.push({ path: `${path}.name`, message: "Must be a non-empty string" });
|
|
2801
|
+
}
|
|
2802
|
+
checkOptionalString(val.skill, `${path}.skill`, errors);
|
|
2803
|
+
}
|
|
2804
|
+
function producerIdentity(val) {
|
|
2805
|
+
return JSON.stringify([val.executor, val.name]);
|
|
2806
|
+
}
|
|
2807
|
+
function validateAcceptance(val, resultProducer, path, errors) {
|
|
2808
|
+
if (!isPlainObject(val)) {
|
|
2809
|
+
errors.push({ path, message: "Must be an object" });
|
|
2810
|
+
return;
|
|
2811
|
+
}
|
|
2812
|
+
validateProducer(val.reviewer, `${path}.reviewer`, errors);
|
|
2813
|
+
if (!isPlainObject(val.source_result)) {
|
|
2814
|
+
errors.push({ path: `${path}.source_result`, message: "Must be an object" });
|
|
2815
|
+
return;
|
|
2816
|
+
}
|
|
2817
|
+
const source = val.source_result;
|
|
2818
|
+
if (typeof source.run_id !== "string" || source.run_id.length === 0) {
|
|
2819
|
+
errors.push({ path: `${path}.source_result.run_id`, message: "Must be a non-empty string" });
|
|
2820
|
+
}
|
|
2821
|
+
checkOptionalString(source.stage_id, `${path}.source_result.stage_id`, errors);
|
|
2822
|
+
validateProducer(source.producer, `${path}.source_result.producer`, errors);
|
|
2823
|
+
if (isPlainObject(val.reviewer) && isPlainObject(resultProducer) && producerIdentity(val.reviewer) !== producerIdentity(resultProducer)) {
|
|
2824
|
+
errors.push({ path: `${path}.reviewer`, message: "Acceptance reviewer must match the result producer" });
|
|
2825
|
+
}
|
|
2826
|
+
if (isPlainObject(val.reviewer) && isPlainObject(source.producer) && producerIdentity(val.reviewer) === producerIdentity(source.producer)) {
|
|
2827
|
+
errors.push({ path: `${path}.reviewer`, message: "Repair producer cannot accept its own result" });
|
|
2828
|
+
}
|
|
2829
|
+
}
|
|
2690
2830
|
function checkOptionalString(val, path, errors) {
|
|
2691
2831
|
if (val !== void 0 && typeof val !== "string") {
|
|
2692
2832
|
errors.push({ path, message: "Must be a string if present" });
|
|
@@ -2855,7 +2995,7 @@ var DEFAULT_SCHEMA = {
|
|
|
2855
2995
|
scenario: { paths: ["artifacts/scenarios/**/*.md"], displayName: "\u573A\u666F\u5267\u672C", role: "scenario", layer: "scenario", aliases: ["scenarios", "scenario-script"] },
|
|
2856
2996
|
design: { paths: ["artifacts/design/**/*.md"], displayName: "\u8BBE\u8BA1\u89C4\u683C", role: "design", layer: "design", aliases: ["design-spec", "design_docs"] },
|
|
2857
2997
|
test: { paths: ["heimdall/packages/**/*.test.ts"], displayName: "\u4EE3\u7801\u6CE8\u91CA\u8FFD\u6EAF", role: "context", layer: "implementation", aliases: ["code-test", "code-trace", "unit-test"] },
|
|
2858
|
-
e2e_test: { paths: ["artifacts/tests/e2e/*.md"], displayName: "E2E \u6D4B\u8BD5\u89C4\u683C", role: "e2e_test", layer: "verification", aliases: ["e2e-test", "e2e_tests"] },
|
|
2998
|
+
e2e_test: { paths: ["artifacts/tests/e2e/*.md"], displayName: "E2E \u6D4B\u8BD5\u89C4\u683C", role: "e2e_test", layer: "verification", aliases: ["e2e-test", "e2e_tests", "tc"] },
|
|
2859
2999
|
e2e_registry: { paths: ["artifacts/tests/e2e/e2e-test-registry.json"], displayName: "E2E \u6D4B\u8BD5\u6CE8\u518C\u8868", role: "context", layer: "verification", aliases: ["e2e-registry"] },
|
|
2860
3000
|
"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"] },
|
|
2861
3001
|
"test-strategy": { paths: ["artifacts/design/test-strategy.md"], displayName: "\u6D4B\u8BD5\u7B56\u7565", role: "context", layer: "verification", aliases: ["test_strategy"] },
|
|
@@ -2898,6 +3038,12 @@ async function loadConfig(root) {
|
|
|
2898
3038
|
throw error;
|
|
2899
3039
|
}
|
|
2900
3040
|
}
|
|
3041
|
+
const ub = parsed.context?.universal_baseline;
|
|
3042
|
+
if (ub !== void 0 && typeof ub !== "boolean") {
|
|
3043
|
+
throw new Error(
|
|
3044
|
+
`Invalid context.universal_baseline: ${JSON.stringify(ub)}. Must be boolean (true or false).`
|
|
3045
|
+
);
|
|
3046
|
+
}
|
|
2901
3047
|
return {
|
|
2902
3048
|
...DEFAULT_SCHEMA,
|
|
2903
3049
|
...parsed,
|
|
@@ -2910,7 +3056,7 @@ async function loadConfig(root) {
|
|
|
2910
3056
|
idRanges: mergeRecord(DEFAULT_SCHEMA.idRanges, parsed.idRanges)
|
|
2911
3057
|
};
|
|
2912
3058
|
}
|
|
2913
|
-
function buildGraph(nodes, edges, diagnostics = []) {
|
|
3059
|
+
function buildGraph(nodes, edges, diagnostics = [], root) {
|
|
2914
3060
|
const graphNodes = nodes.map((node) => ({ ...node, uid: toUid(node.type, node.code) }));
|
|
2915
3061
|
graphNodes.sort(compareNode);
|
|
2916
3062
|
edges.sort(compareEdge);
|
|
@@ -2927,6 +3073,7 @@ function buildGraph(nodes, edges, diagnostics = []) {
|
|
|
2927
3073
|
nodes: graphNodes,
|
|
2928
3074
|
edges: dedupedEdges,
|
|
2929
3075
|
generatedAt: (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
3076
|
+
...root ? { root } : {},
|
|
2930
3077
|
diagnostics: diagnostics.sort((left, right) => left.code.localeCompare(right.code) || left.path.localeCompare(right.path) || left.line - right.line)
|
|
2931
3078
|
};
|
|
2932
3079
|
}
|
|
@@ -2958,7 +3105,8 @@ async function scanArtifacts(root, schema) {
|
|
|
2958
3105
|
scanDiagnostics.push(...parsed.diagnostics);
|
|
2959
3106
|
}
|
|
2960
3107
|
}
|
|
2961
|
-
const
|
|
3108
|
+
const absoluteRoot = isAbsolute3(root) ? root : resolve3(root);
|
|
3109
|
+
const graph = buildGraph(nodes, edges, scanDiagnostics, absoluteRoot);
|
|
2962
3110
|
return resolveMatrixEdges(graph);
|
|
2963
3111
|
}
|
|
2964
3112
|
function artifactTypeEntriesBySpecificity(schema) {
|
|
@@ -3298,23 +3446,34 @@ async function validateScenarioPrdLinkIndex(root, graph) {
|
|
|
3298
3446
|
function validateCodeCommentTraceabilityFormat(graph) {
|
|
3299
3447
|
const issues = [];
|
|
3300
3448
|
for (const node of graph.nodes) {
|
|
3301
|
-
if (node.type !== "test") {
|
|
3449
|
+
if (node.type !== "test" && node.type !== "implementation") {
|
|
3302
3450
|
continue;
|
|
3303
3451
|
}
|
|
3304
3452
|
const invalidComments = node.attrs?.invalidTraceabilityComments;
|
|
3305
|
-
if (
|
|
3306
|
-
|
|
3453
|
+
if (Array.isArray(invalidComments)) {
|
|
3454
|
+
for (const invalid of invalidComments) {
|
|
3455
|
+
const line = typeof invalid?.line === "number" ? invalid.line : node.line;
|
|
3456
|
+
const reason = typeof invalid?.reason === "string" ? invalid.reason : "traceability tags must use standalone // comments";
|
|
3457
|
+
issues.push(issue(
|
|
3458
|
+
"CODE_COMMENT_TRACEABILITY_FORMAT",
|
|
3459
|
+
`${reason}: ${typeof invalid?.text === "string" ? invalid.text : ""}`.trim(),
|
|
3460
|
+
node.path,
|
|
3461
|
+
line,
|
|
3462
|
+
{ node: node.uid }
|
|
3463
|
+
));
|
|
3464
|
+
}
|
|
3307
3465
|
}
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
const
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3466
|
+
const deprecatedComments = node.attrs?.deprecatedTraceabilityComments;
|
|
3467
|
+
if (Array.isArray(deprecatedComments)) {
|
|
3468
|
+
for (const deprecated of deprecatedComments) {
|
|
3469
|
+
issues.push(issue(
|
|
3470
|
+
"E2E-TRACE-007",
|
|
3471
|
+
"@tc is deprecated; use @e2e_test instead",
|
|
3472
|
+
node.path,
|
|
3473
|
+
typeof deprecated?.line === "number" ? deprecated.line : node.line,
|
|
3474
|
+
{ node: node.uid, severity: "warning" }
|
|
3475
|
+
));
|
|
3476
|
+
}
|
|
3318
3477
|
}
|
|
3319
3478
|
}
|
|
3320
3479
|
return issues;
|
|
@@ -3796,13 +3955,16 @@ function parseTest(path, raw, schema = DEFAULT_SCHEMA) {
|
|
|
3796
3955
|
const isTest = isTestFile(path);
|
|
3797
3956
|
const nodeType = isTest ? "test" : "implementation";
|
|
3798
3957
|
const edgeKind = isTest ? "verifies" : "implements";
|
|
3958
|
+
const attrs = {};
|
|
3959
|
+
if (traceabilityComments.invalid.length > 0) attrs.invalidTraceabilityComments = traceabilityComments.invalid;
|
|
3960
|
+
if (traceabilityComments.deprecated.length > 0) attrs.deprecatedTraceabilityComments = traceabilityComments.deprecated;
|
|
3799
3961
|
const node = {
|
|
3800
3962
|
type: nodeType,
|
|
3801
3963
|
code: path,
|
|
3802
3964
|
title: path.split("/").at(-1) ?? path,
|
|
3803
3965
|
path,
|
|
3804
3966
|
line: 1,
|
|
3805
|
-
attrs
|
|
3967
|
+
attrs
|
|
3806
3968
|
};
|
|
3807
3969
|
let hasTags = false;
|
|
3808
3970
|
for (const { tags, lineNumber } of traceabilityComments.canonical) {
|
|
@@ -3813,7 +3975,7 @@ function parseTest(path, raw, schema = DEFAULT_SCHEMA) {
|
|
|
3813
3975
|
}
|
|
3814
3976
|
}
|
|
3815
3977
|
}
|
|
3816
|
-
if (hasTags || traceabilityComments.invalid.length > 0) {
|
|
3978
|
+
if (hasTags || traceabilityComments.invalid.length > 0 || traceabilityComments.deprecated.length > 0) {
|
|
3817
3979
|
nodes.push(node);
|
|
3818
3980
|
}
|
|
3819
3981
|
return { nodes, edges };
|
|
@@ -3821,6 +3983,7 @@ function parseTest(path, raw, schema = DEFAULT_SCHEMA) {
|
|
|
3821
3983
|
function scanTraceabilityComments(raw, schema = DEFAULT_SCHEMA) {
|
|
3822
3984
|
const canonical = [];
|
|
3823
3985
|
const invalid = [];
|
|
3986
|
+
const deprecated = [];
|
|
3824
3987
|
for (const comment of scanCodeComments(raw)) {
|
|
3825
3988
|
if (!containsTraceabilityTag(comment.text, schema)) {
|
|
3826
3989
|
continue;
|
|
@@ -3836,6 +3999,9 @@ function scanTraceabilityComments(raw, schema = DEFAULT_SCHEMA) {
|
|
|
3836
3999
|
const parsed = parseTraceabilityTagLine(comment.text.trim(), schema);
|
|
3837
4000
|
if (parsed.valid) {
|
|
3838
4001
|
canonical.push({ tags: parsed.tags, lineNumber: comment.lineNumber });
|
|
4002
|
+
if (/(?:^|\s)@tc(?=\s|$)/.test(comment.text.trim())) {
|
|
4003
|
+
deprecated.push({ line: comment.lineNumber, text: comment.text.trim() });
|
|
4004
|
+
}
|
|
3839
4005
|
} else {
|
|
3840
4006
|
invalid.push({ line: comment.lineNumber, text: comment.text.trim(), reason: parsed.reason });
|
|
3841
4007
|
}
|
|
@@ -3851,11 +4017,14 @@ function scanTraceabilityComments(raw, schema = DEFAULT_SCHEMA) {
|
|
|
3851
4017
|
const parsed = parseTraceabilityTagLine(comment.text.trim(), schema);
|
|
3852
4018
|
if (parsed.valid) {
|
|
3853
4019
|
canonical.push({ tags: parsed.tags, lineNumber: comment.lineNumber });
|
|
4020
|
+
if (/(?:^|\s)@tc(?=\s|$)/.test(comment.text.trim())) {
|
|
4021
|
+
deprecated.push({ line: comment.lineNumber, text: comment.text.trim() });
|
|
4022
|
+
}
|
|
3854
4023
|
} else {
|
|
3855
4024
|
invalid.push({ line: comment.lineNumber, text: comment.text.trim(), reason: parsed.reason });
|
|
3856
4025
|
}
|
|
3857
4026
|
}
|
|
3858
|
-
return { canonical, invalid };
|
|
4027
|
+
return { canonical, invalid, deprecated };
|
|
3859
4028
|
}
|
|
3860
4029
|
function scanCodeComments(raw) {
|
|
3861
4030
|
const comments = [];
|
|
@@ -4022,7 +4191,14 @@ function expandCodeRange(value) {
|
|
|
4022
4191
|
return Array.from({ length: end - start + 1 }, (_, index) => `${prefix}${String(start + index).padStart(width, "0")}`);
|
|
4023
4192
|
}
|
|
4024
4193
|
function containsTraceabilityTag(value, schema = DEFAULT_SCHEMA) {
|
|
4025
|
-
|
|
4194
|
+
const tokens = /* @__PURE__ */ new Set();
|
|
4195
|
+
for (const [type, definition] of Object.entries(schema.types)) {
|
|
4196
|
+
tokens.add(type);
|
|
4197
|
+
for (const alias of definition.aliases ?? []) tokens.add(alias);
|
|
4198
|
+
}
|
|
4199
|
+
if (tokens.size === 0) return false;
|
|
4200
|
+
const alternatives = [...tokens].sort((a, b) => b.length - a.length).map((token) => token.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&")).join("|");
|
|
4201
|
+
return new RegExp(`(?:^|\\s)@(?:${alternatives})(?=\\s|$)`).test(value);
|
|
4026
4202
|
}
|
|
4027
4203
|
function parseDesign(path, raw) {
|
|
4028
4204
|
const parsed = matter(raw);
|
|
@@ -5139,8 +5315,8 @@ async function validateExecutableTraceability(root) {
|
|
|
5139
5315
|
}
|
|
5140
5316
|
}
|
|
5141
5317
|
const refToSource = /* @__PURE__ */ new Map();
|
|
5142
|
-
const tcAnnotationRegex = /\/\/!?\s*@tc\s+(\S+?)\s+\[(\w+)\]/;
|
|
5143
|
-
const tcAnnotationNoLevelRegex = /\/\/!?\s*@tc\s+(\S+)/;
|
|
5318
|
+
const tcAnnotationRegex = /\/\/!?\s*@(?:e2e_test|tc)\s+(\S+?)\s+\[(\w+)\]/;
|
|
5319
|
+
const tcAnnotationNoLevelRegex = /\/\/!?\s*@(?:e2e_test|tc)\s+(\S+)/;
|
|
5144
5320
|
for (const specFile of specFiles) {
|
|
5145
5321
|
const fullSpecPath = join5(root, specFile);
|
|
5146
5322
|
let content;
|
|
@@ -5229,7 +5405,7 @@ async function validateExecutableTraceability(root) {
|
|
|
5229
5405
|
return normalizedAnnFile === normalizedRefFile;
|
|
5230
5406
|
}) ?? false;
|
|
5231
5407
|
if (!hasAnnotationInFile) {
|
|
5232
|
-
issues.push(issue("E2E-TRACE-003", `executable_ref target ${entry.file} has no
|
|
5408
|
+
issues.push(issue("E2E-TRACE-003", `executable_ref target ${entry.file} has no E2E trace annotation ${tcKey}`, path, line, { node: tcKey, severity: "warning" }));
|
|
5233
5409
|
continue;
|
|
5234
5410
|
}
|
|
5235
5411
|
validFiles.push(normalizedRefFile);
|
|
@@ -5240,13 +5416,13 @@ async function validateExecutableTraceability(root) {
|
|
|
5240
5416
|
const batch = tcKey.split(":")[0];
|
|
5241
5417
|
if (!mdBatches.has(batch)) {
|
|
5242
5418
|
for (const ann of annotations) {
|
|
5243
|
-
issues.push(issue("E2E-TRACE-002",
|
|
5419
|
+
issues.push(issue("E2E-TRACE-002", `E2E trace annotation ${tcKey} references non-existent E2E batch "${batch}"`, ann.file, ann.line, { node: tcKey, severity: "warning" }));
|
|
5244
5420
|
}
|
|
5245
5421
|
continue;
|
|
5246
5422
|
}
|
|
5247
5423
|
if (!mdToRef.has(tcKey) && !await hasMarkdownTc(tcKey, e2eDir)) {
|
|
5248
5424
|
for (const ann of annotations) {
|
|
5249
|
-
issues.push(issue("E2E-TRACE-002",
|
|
5425
|
+
issues.push(issue("E2E-TRACE-002", `E2E trace annotation ${tcKey} references non-existent Markdown TC`, ann.file, ann.line, { node: tcKey, severity: "warning" }));
|
|
5250
5426
|
}
|
|
5251
5427
|
}
|
|
5252
5428
|
}
|
|
@@ -5268,7 +5444,7 @@ async function validateExecutableTraceability(root) {
|
|
|
5268
5444
|
const primaryRef = refEntries[0];
|
|
5269
5445
|
const normalizedPrimary = primaryRef?.file.startsWith("heimdall/") ? primaryRef?.file : `heimdall/${primaryRef?.file ?? ""}`;
|
|
5270
5446
|
const detail = `file: MD refs=[${refEntries.map((e) => e.file).join(", ")}] vs source=${ann.file}`;
|
|
5271
|
-
issues.push(issue("E2E-TRACE-003", `executable_ref \u2194
|
|
5447
|
+
issues.push(issue("E2E-TRACE-003", `executable_ref \u2194 E2E trace annotation mismatch for ${tcKey}: ${detail}`, path, line, { node: tcKey, severity: "warning" }));
|
|
5272
5448
|
}
|
|
5273
5449
|
}
|
|
5274
5450
|
for (const [tcKey, { chainType, path, line }] of mdToRef) {
|
|
@@ -5434,14 +5610,14 @@ async function validatePartialRustEvidence(tcFields, tcKey, root) {
|
|
|
5434
5610
|
return { hasValidPartialRust: false, detail: `partial_rust file not found: ${ref.file}` };
|
|
5435
5611
|
}
|
|
5436
5612
|
const tcAnnotationPattern = new RegExp(
|
|
5437
|
-
`//[/!]?\\s*@tc\\s+${escapeRegExp(tcKey)}\\s+\\[partial_rust\\]`
|
|
5613
|
+
`//[/!]?\\s*@(?:e2e_test|tc)\\s+${escapeRegExp(tcKey)}\\s+\\[partial_rust\\]`
|
|
5438
5614
|
);
|
|
5439
5615
|
if (!tcAnnotationPattern.test(content)) {
|
|
5440
|
-
const noLevelPattern = new RegExp(`//[/!]?\\s*@tc\\s+${escapeRegExp(tcKey)}\\b`);
|
|
5616
|
+
const noLevelPattern = new RegExp(`//[/!]?\\s*@(?:e2e_test|tc)\\s+${escapeRegExp(tcKey)}\\b`);
|
|
5441
5617
|
if (noLevelPattern.test(content)) {
|
|
5442
|
-
return { hasValidPartialRust: false, detail: `partial_rust file ${ref.file} has
|
|
5618
|
+
return { hasValidPartialRust: false, detail: `partial_rust file ${ref.file} has E2E trace annotation ${tcKey} but not tagged [partial_rust]` };
|
|
5443
5619
|
}
|
|
5444
|
-
return { hasValidPartialRust: false, detail: `partial_rust file ${ref.file} has no
|
|
5620
|
+
return { hasValidPartialRust: false, detail: `partial_rust file ${ref.file} has no E2E trace annotation ${tcKey}` };
|
|
5445
5621
|
}
|
|
5446
5622
|
}
|
|
5447
5623
|
return { hasValidPartialRust: true, detail: "ok" };
|
|
@@ -5768,9 +5944,14 @@ function mergeRecord(base, override) {
|
|
|
5768
5944
|
function mergeArtifactTypes(base, override) {
|
|
5769
5945
|
const result = { ...base };
|
|
5770
5946
|
for (const [type, definition] of Object.entries(override ?? {})) {
|
|
5947
|
+
const aliases = [
|
|
5948
|
+
...base[type]?.aliases ?? [],
|
|
5949
|
+
...definition.aliases ?? []
|
|
5950
|
+
].filter((alias, index, all) => all.indexOf(alias) === index);
|
|
5771
5951
|
result[type] = {
|
|
5772
5952
|
...base[type] ?? {},
|
|
5773
|
-
...definition
|
|
5953
|
+
...definition,
|
|
5954
|
+
...aliases.length > 0 ? { aliases } : {}
|
|
5774
5955
|
};
|
|
5775
5956
|
}
|
|
5776
5957
|
return result;
|
|
@@ -5864,6 +6045,8 @@ var TIER_ORDER = ["baseline", "target", "direct", "matrix", "transitive"];
|
|
|
5864
6045
|
function resolveArtifactContext(graph, opts) {
|
|
5865
6046
|
const mode = opts.mode ?? "full";
|
|
5866
6047
|
const maxPerCategory = opts.maxPerCategory ?? 20;
|
|
6048
|
+
const universalBaseline = opts.universalBaseline ?? true;
|
|
6049
|
+
const root = opts.root ?? graph.root;
|
|
5867
6050
|
const legacyCount = [opts.feature, opts.scenario, opts.decision, opts.design, opts.e2e_test].filter(Boolean).length;
|
|
5868
6051
|
if (opts.target && legacyCount > 0) {
|
|
5869
6052
|
return {
|
|
@@ -6016,12 +6199,80 @@ function resolveArtifactContext(graph, opts) {
|
|
|
6016
6199
|
return "direct";
|
|
6017
6200
|
}
|
|
6018
6201
|
const pathMap = /* @__PURE__ */ new Map();
|
|
6019
|
-
|
|
6020
|
-
const
|
|
6021
|
-
|
|
6022
|
-
if (
|
|
6202
|
+
if (universalBaseline) {
|
|
6203
|
+
for (const ap of ALWAYS_PRESENT_ITEMS) {
|
|
6204
|
+
const existing = pathMap.get(ap.path);
|
|
6205
|
+
if (existing) {
|
|
6206
|
+
if (!existing.reasons.includes(ap.reason)) existing.reasons.push(ap.reason);
|
|
6207
|
+
} else {
|
|
6208
|
+
pathMap.set(ap.path, { path: ap.path, reasons: [ap.reason], category: "baseline", required: true, tier: "baseline" });
|
|
6209
|
+
}
|
|
6210
|
+
}
|
|
6211
|
+
if (root) {
|
|
6212
|
+
for (const ap of ALWAYS_PRESENT_ITEMS) {
|
|
6213
|
+
const fullPath = join5(root, ap.path);
|
|
6214
|
+
let stat;
|
|
6215
|
+
try {
|
|
6216
|
+
stat = statSync(fullPath);
|
|
6217
|
+
} catch {
|
|
6218
|
+
stat = null;
|
|
6219
|
+
}
|
|
6220
|
+
if (!stat) {
|
|
6221
|
+
const msg = `Required baseline artifact not found: ${ap.path}`;
|
|
6222
|
+
if (!missing.includes(msg)) {
|
|
6223
|
+
missing.push(msg);
|
|
6224
|
+
missingDetails.push({
|
|
6225
|
+
ref: ap.path,
|
|
6226
|
+
from: "baseline",
|
|
6227
|
+
kind: "missing-baseline",
|
|
6228
|
+
message: msg,
|
|
6229
|
+
suggestedAction: `\u521B\u5EFA\u6587\u4EF6 ${ap.path} \u6216\u914D\u7F6E\u8DF3\u8FC7 universal baseline`
|
|
6230
|
+
});
|
|
6231
|
+
}
|
|
6232
|
+
} else if (!stat.isFile()) {
|
|
6233
|
+
const msg = `Required baseline artifact is not a regular file: ${ap.path}`;
|
|
6234
|
+
if (!missing.includes(msg)) {
|
|
6235
|
+
missing.push(msg);
|
|
6236
|
+
missingDetails.push({
|
|
6237
|
+
ref: ap.path,
|
|
6238
|
+
from: "baseline",
|
|
6239
|
+
kind: "missing-baseline",
|
|
6240
|
+
message: msg,
|
|
6241
|
+
suggestedAction: `\u5C06 ${ap.path} \u4ECE\u76EE\u5F55\u6539\u4E3A\u6587\u4EF6\uFF0C\u6216\u914D\u7F6E\u8DF3\u8FC7 universal baseline`
|
|
6242
|
+
});
|
|
6243
|
+
}
|
|
6244
|
+
} else {
|
|
6245
|
+
try {
|
|
6246
|
+
accessSync(fullPath, fsConstants.R_OK);
|
|
6247
|
+
} catch {
|
|
6248
|
+
const msg = `Required baseline artifact is not readable: ${ap.path}`;
|
|
6249
|
+
if (!missing.includes(msg)) {
|
|
6250
|
+
missing.push(msg);
|
|
6251
|
+
missingDetails.push({
|
|
6252
|
+
ref: ap.path,
|
|
6253
|
+
from: "baseline",
|
|
6254
|
+
kind: "missing-baseline",
|
|
6255
|
+
message: msg,
|
|
6256
|
+
suggestedAction: `\u4FEE\u590D ${ap.path} \u7684\u6587\u4EF6\u6743\u9650\uFF0C\u6216\u914D\u7F6E\u8DF3\u8FC7 universal baseline`
|
|
6257
|
+
});
|
|
6258
|
+
}
|
|
6259
|
+
}
|
|
6260
|
+
}
|
|
6261
|
+
}
|
|
6023
6262
|
} else {
|
|
6024
|
-
|
|
6263
|
+
for (const ap of ALWAYS_PRESENT_ITEMS) {
|
|
6264
|
+
const msg = `Cannot verify baseline without root: ${ap.path}`;
|
|
6265
|
+
if (!missing.includes(msg)) {
|
|
6266
|
+
missing.push(msg);
|
|
6267
|
+
missingDetails.push({
|
|
6268
|
+
ref: ap.path,
|
|
6269
|
+
from: "baseline",
|
|
6270
|
+
kind: "missing-baseline",
|
|
6271
|
+
message: msg,
|
|
6272
|
+
suggestedAction: `\u4F20\u9012 root \u53C2\u6570\u6216\u914D\u7F6E\u8DF3\u8FC7 universal baseline`
|
|
6273
|
+
});
|
|
6274
|
+
}
|
|
6275
|
+
}
|
|
6025
6276
|
}
|
|
6026
6277
|
}
|
|
6027
6278
|
pathMap.set(targetNode.path, {
|
|
@@ -6122,7 +6373,8 @@ function resolveArtifactContext(graph, opts) {
|
|
|
6122
6373
|
context,
|
|
6123
6374
|
missing,
|
|
6124
6375
|
missingDetails,
|
|
6125
|
-
omitted
|
|
6376
|
+
omitted,
|
|
6377
|
+
baselinePolicy: universalBaseline
|
|
6126
6378
|
};
|
|
6127
6379
|
}
|
|
6128
6380
|
function formatContextMarkdown(manifest) {
|
|
@@ -6206,7 +6458,9 @@ async function auditSinglePromptTarget(target, graph, options) {
|
|
|
6206
6458
|
try {
|
|
6207
6459
|
const manifest = resolveArtifactContext(graph, {
|
|
6208
6460
|
target: { type: target.type, id: target.id },
|
|
6209
|
-
mode: "implementation"
|
|
6461
|
+
mode: "implementation",
|
|
6462
|
+
root: options.root,
|
|
6463
|
+
universalBaseline: options.universalBaseline
|
|
6210
6464
|
});
|
|
6211
6465
|
if (manifest.missing.length > 0) {
|
|
6212
6466
|
entry.ok = false;
|
|
@@ -6362,13 +6616,15 @@ async function discoverAndAuditPromptBatch(root, options) {
|
|
|
6362
6616
|
type: d.type,
|
|
6363
6617
|
id: d.id
|
|
6364
6618
|
}));
|
|
6619
|
+
const effectiveBaseline = options.universalBaseline ?? config.context?.universal_baseline;
|
|
6365
6620
|
return auditPromptBatch(root, targets, {
|
|
6366
6621
|
root,
|
|
6367
6622
|
outDir: options.outDir,
|
|
6368
6623
|
format: options.format,
|
|
6369
6624
|
maxChars: options.maxChars,
|
|
6370
6625
|
summaryOnly: options.summaryOnly,
|
|
6371
|
-
summaryDetail: options.summaryDetail
|
|
6626
|
+
summaryDetail: options.summaryDetail,
|
|
6627
|
+
universalBaseline: effectiveBaseline
|
|
6372
6628
|
}, graph);
|
|
6373
6629
|
}
|
|
6374
6630
|
|
|
@@ -6380,7 +6636,8 @@ async function runCli(argv, io = {}) {
|
|
|
6380
6636
|
const err = io.stderr ?? ((chunk) => process.stderr.write(chunk));
|
|
6381
6637
|
const root = String(parsed.flags.root ?? cwd);
|
|
6382
6638
|
try {
|
|
6383
|
-
|
|
6639
|
+
const hasHelpFlag = parsed.flags.help === true || parsed.positional.some((p) => p === "--help" || p === "-h");
|
|
6640
|
+
if (parsed.command === "--help" || parsed.command === "-h" || parsed.command === "help" || hasHelpFlag) {
|
|
6384
6641
|
out(helpText());
|
|
6385
6642
|
return 0;
|
|
6386
6643
|
}
|
|
@@ -6487,7 +6744,9 @@ async function runCli(argv, io = {}) {
|
|
|
6487
6744
|
const manifest = resolveArtifactContext(graph, {
|
|
6488
6745
|
target: resolvedTarget,
|
|
6489
6746
|
mode: contextMode,
|
|
6490
|
-
maxPerCategory
|
|
6747
|
+
maxPerCategory,
|
|
6748
|
+
universalBaseline: config.context?.universal_baseline,
|
|
6749
|
+
root
|
|
6491
6750
|
});
|
|
6492
6751
|
if (parsed.flags.format === "json") {
|
|
6493
6752
|
out(`${JSON.stringify(manifest, null, 2)}
|
|
@@ -6528,7 +6787,9 @@ async function runCli(argv, io = {}) {
|
|
|
6528
6787
|
const manifest = resolveArtifactContext(graph, {
|
|
6529
6788
|
target: resolvedTarget,
|
|
6530
6789
|
mode: packetMode,
|
|
6531
|
-
maxPerCategory: packetMaxPerCategory
|
|
6790
|
+
maxPerCategory: packetMaxPerCategory,
|
|
6791
|
+
universalBaseline: config.context?.universal_baseline,
|
|
6792
|
+
root
|
|
6532
6793
|
});
|
|
6533
6794
|
if (manifest.missing.length > 0) {
|
|
6534
6795
|
err("Missing artifacts detected \u2014 cannot generate packet:\n");
|
|
@@ -6657,7 +6918,9 @@ async function runCli(argv, io = {}) {
|
|
|
6657
6918
|
const graph = await scanArtifacts(root);
|
|
6658
6919
|
const promptManifest = resolveArtifactContext(graph, {
|
|
6659
6920
|
target: resolvedTarget,
|
|
6660
|
-
mode: "implementation"
|
|
6921
|
+
mode: "implementation",
|
|
6922
|
+
universalBaseline: config.context?.universal_baseline,
|
|
6923
|
+
root
|
|
6661
6924
|
});
|
|
6662
6925
|
if (promptManifest.missing.length > 0) {
|
|
6663
6926
|
err("Missing artifacts detected \u2014 cannot generate prompt:\n");
|
|
@@ -6797,6 +7060,7 @@ async function runCli(argv, io = {}) {
|
|
|
6797
7060
|
}
|
|
6798
7061
|
let summary;
|
|
6799
7062
|
if (discover) {
|
|
7063
|
+
const discoverConfig = await loadConfig(root);
|
|
6800
7064
|
summary = await discoverAndAuditPackets(root, {
|
|
6801
7065
|
root,
|
|
6802
7066
|
outDir: auditOutDir,
|
|
@@ -6806,7 +7070,8 @@ async function runCli(argv, io = {}) {
|
|
|
6806
7070
|
limit: limit === 0 ? Infinity : limit,
|
|
6807
7071
|
summaryOnly,
|
|
6808
7072
|
sampleTargets,
|
|
6809
|
-
summaryDetail
|
|
7073
|
+
summaryDetail,
|
|
7074
|
+
universalBaseline: discoverConfig.context?.universal_baseline
|
|
6810
7075
|
});
|
|
6811
7076
|
} else {
|
|
6812
7077
|
const targetsContent = await readFile3(targetsFile, "utf-8");
|
|
@@ -6834,7 +7099,8 @@ async function runCli(argv, io = {}) {
|
|
|
6834
7099
|
summaryOnly,
|
|
6835
7100
|
sampleTargets,
|
|
6836
7101
|
summaryDetail,
|
|
6837
|
-
schema: auditConfig2
|
|
7102
|
+
schema: auditConfig2,
|
|
7103
|
+
universalBaseline: auditConfig2.context?.universal_baseline
|
|
6838
7104
|
});
|
|
6839
7105
|
}
|
|
6840
7106
|
if (parsed.flags.format === "json") {
|
|
@@ -6914,6 +7180,7 @@ async function runCli(argv, io = {}) {
|
|
|
6914
7180
|
}
|
|
6915
7181
|
let ppaSummary;
|
|
6916
7182
|
if (ppaDiscover) {
|
|
7183
|
+
const ppaDiscoverConfig = await loadConfig(root);
|
|
6917
7184
|
ppaSummary = await discoverAndAuditPromptBatch(root, {
|
|
6918
7185
|
root,
|
|
6919
7186
|
outDir: ppaOutDir,
|
|
@@ -6921,7 +7188,8 @@ async function runCli(argv, io = {}) {
|
|
|
6921
7188
|
maxChars: ppaMaxChars,
|
|
6922
7189
|
limit: ppaLimit === 0 ? Infinity : ppaLimit,
|
|
6923
7190
|
summaryOnly: ppaSummaryOnly,
|
|
6924
|
-
summaryDetail: ppaSummaryDetail
|
|
7191
|
+
summaryDetail: ppaSummaryDetail,
|
|
7192
|
+
universalBaseline: ppaDiscoverConfig.context?.universal_baseline
|
|
6925
7193
|
});
|
|
6926
7194
|
if (ppaSummary.total === 0) {
|
|
6927
7195
|
err(`\u9519\u8BEF\uFF1Adiscover \u6A21\u5F0F\u5728 ${root} \u4E2D\u672A\u627E\u5230\u4EFB\u4F55 target\u3002\u8BF7\u786E\u8BA4\u8FD9\u662F\u4E00\u4E2A\u6709\u6548\u7684 artifact root\u3002
|
|
@@ -6963,7 +7231,8 @@ async function runCli(argv, io = {}) {
|
|
|
6963
7231
|
maxChars: ppaMaxChars,
|
|
6964
7232
|
sourceTargetsPath: ppaTargetsFile,
|
|
6965
7233
|
summaryOnly: ppaSummaryOnly,
|
|
6966
|
-
summaryDetail: ppaSummaryDetail
|
|
7234
|
+
summaryDetail: ppaSummaryDetail,
|
|
7235
|
+
universalBaseline: ppaConfig.context?.universal_baseline
|
|
6967
7236
|
});
|
|
6968
7237
|
}
|
|
6969
7238
|
if (ppaFormat === "json") {
|
|
@@ -7076,6 +7345,10 @@ async function runCli(argv, io = {}) {
|
|
|
7076
7345
|
err("Usage: artifact-graph version-lock refresh (--all | --changed-only (--staged | --worktree | --base <ref>)) [--remove-orphans] [--format json|markdown] [--lock-path <path>]\n");
|
|
7077
7346
|
return 1;
|
|
7078
7347
|
}
|
|
7348
|
+
if (parsed.flags.help === true) {
|
|
7349
|
+
out("Usage: artifact-graph version-lock refresh (--all | --changed-only (--staged | --worktree | --base <ref>)) [--remove-orphans] [--format json|markdown] [--lock-path <path>]\n");
|
|
7350
|
+
return 0;
|
|
7351
|
+
}
|
|
7079
7352
|
if (refreshAll && refreshChangedOnly) {
|
|
7080
7353
|
err("Error: --all and --changed-only are mutually exclusive\n");
|
|
7081
7354
|
return 1;
|
|
@@ -7213,7 +7486,7 @@ async function runCli(argv, io = {}) {
|
|
|
7213
7486
|
err("Usage: artifact-graph validate-review-result --file <path> [--format json]\n");
|
|
7214
7487
|
return 1;
|
|
7215
7488
|
}
|
|
7216
|
-
const resolvedPath =
|
|
7489
|
+
const resolvedPath = isAbsolute4(filePath) ? filePath : join7(root, filePath);
|
|
7217
7490
|
let content;
|
|
7218
7491
|
try {
|
|
7219
7492
|
content = await readFile3(resolvedPath, "utf-8");
|
|
@@ -7284,12 +7557,18 @@ function parseArgs(argv) {
|
|
|
7284
7557
|
if (token.startsWith("--")) {
|
|
7285
7558
|
const key = token.slice(2);
|
|
7286
7559
|
const next = rest[index + 1];
|
|
7287
|
-
|
|
7560
|
+
const nextLooksLikeFlag = next && next.startsWith("-") && next.length > 1 && !/\d/.test(next[1]);
|
|
7561
|
+
if (next && !nextLooksLikeFlag) {
|
|
7288
7562
|
flags[key] = next;
|
|
7289
7563
|
index += 1;
|
|
7290
7564
|
} else {
|
|
7291
7565
|
flags[key] = true;
|
|
7292
7566
|
}
|
|
7567
|
+
} else if (token === "-h") {
|
|
7568
|
+
flags.help = true;
|
|
7569
|
+
} else if (token.startsWith("-") && token.length > 1) {
|
|
7570
|
+
const key = token.slice(1);
|
|
7571
|
+
flags[key] = true;
|
|
7293
7572
|
} else {
|
|
7294
7573
|
positional.push(token);
|
|
7295
7574
|
}
|