@unbrained/pm-cli 2026.8.21 → 2026.8.23
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/.claude-plugin/marketplace.json +2 -2
- package/CHANGELOG.md +44 -0
- package/dist/cli/error-guidance.d.ts +22 -4
- package/dist/cli/error-guidance.js +73 -16
- package/dist/cli/main.d.ts +3 -0
- package/dist/cli/main.js +51 -9
- package/dist/cli/register-mutation.js +6 -6
- package/dist/cli/register-operations.d.ts +73 -0
- package/dist/cli/register-operations.js +39 -14
- package/dist/cli/register-setup.js +3 -3
- package/dist/cli/schema-registration-helpers.d.ts +8 -1
- package/dist/cli/schema-registration-helpers.js +14 -5
- package/dist/cli-bundle/bundle-manifest.json +149 -149
- package/dist/cli-bundle/chunks/chunk-3ENVHX6C.js +36 -0
- package/dist/cli-bundle/chunks/{chunk-LOVTQ52Z.js → chunk-DW2CGEMU.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-EG6CLDZT.js +5 -0
- package/dist/cli-bundle/chunks/{chunk-LARJJ3RK.js → chunk-F3AI2JFL.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-N3DSV4CH.js → chunk-HWZ6CELB.js} +8 -8
- package/dist/cli-bundle/chunks/chunk-HZBMLSZI.js +201 -0
- package/dist/cli-bundle/chunks/chunk-JB4KZWBF.js +3 -0
- package/dist/cli-bundle/chunks/chunk-KPH73XXJ.js +2 -0
- package/dist/cli-bundle/chunks/{chunk-BKYB45NQ.js → chunk-KXPMB2YL.js} +50 -50
- package/dist/cli-bundle/chunks/{chunk-BUNIEJNE.js → chunk-QDVHNHAQ.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-TFI5V5N2.js +13 -0
- package/dist/cli-bundle/chunks/{register-list-query-F57EWDEF.js → register-list-query-UJB5FMV2.js} +2 -2
- package/dist/cli-bundle/chunks/register-mutation-NX5XE4VX.js +20 -0
- package/dist/cli-bundle/chunks/register-operations-RXIJIYXC.js +2 -0
- package/dist/cli-bundle/chunks/{register-setup-HXU7IZZD.js → register-setup-GFUZEKRE.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-F4YEW5I2.js → chunk-37Z4OEAP.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-3UPTYG44.js +8 -0
- package/dist/cli-bundle/focused-chunks/chunk-5S7E72FY.js +16 -0
- package/dist/cli-bundle/focused-chunks/{chunk-4ERLO7QD.js → chunk-AUFH5H35.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-E7X25GC3.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-FQCSTBWE.js +4 -0
- package/dist/cli-bundle/focused-chunks/{chunk-FH6DYED3.js → chunk-FZS4H32O.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-IFLWKY2R.js +156 -0
- package/dist/cli-bundle/focused-chunks/{chunk-YKBKIEAJ.js → chunk-JOWDB2MH.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-LYVSW5VM.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-MRCNF3U6.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-UZE7XUZK.js → chunk-OFVUGTLJ.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-FJ22HNRP.js → chunk-OGLQBL46.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-OY7QAV4Y.js +51 -0
- package/dist/cli-bundle/focused-chunks/chunk-QGFNI65N.js +26 -0
- package/dist/cli-bundle/focused-chunks/{chunk-KDNG7TKQ.js → chunk-R27IDGHD.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-UYRFYCDQ.js +5 -0
- package/dist/cli-bundle/focused-chunks/{chunk-2NIY5NKG.js → chunk-ZHVA42R5.js} +9 -9
- package/dist/cli-bundle/main.js +13 -13
- package/dist/cli-bundle/sdk-authoring.js +1 -1
- package/dist/cli-bundle/sdk-contracts.js +2 -2
- package/dist/cli-bundle/sdk-core.js +40 -40
- package/dist/cli-bundle/sdk-governance.js +1 -1
- package/dist/cli-bundle/sdk-graph.js +1 -1
- package/dist/cli-bundle/sdk-merge.js +33 -32
- package/dist/cli-bundle/sdk-query.js +1 -1
- package/dist/cli-bundle/sdk-runtime.js +1 -1
- package/dist/cli-bundle/sdk-testing.js +1 -1
- package/dist/cli-bundle/sdk.js +5 -5
- package/dist/core/config/nested-settings.js +8 -2
- package/dist/core/config/positional-value.d.ts +1 -1
- package/dist/core/config/positional-value.js +5 -2
- package/dist/core/extensions/extension-hook-runtime.js +22 -20
- package/dist/core/extensions/extension-types.d.ts +8 -1
- package/dist/core/extensions/extension-types.js +2 -2
- package/dist/core/extensions/loader.js +7 -3
- package/dist/core/extensions/service-ownership.d.ts +8 -0
- package/dist/core/extensions/service-ownership.js +20 -0
- package/dist/core/history/history.js +3 -3
- package/dist/core/item/id.js +20 -5
- package/dist/core/item/item-format.js +81 -2
- package/dist/core/reproducibility/context.d.ts +4 -0
- package/dist/core/reproducibility/context.js +14 -8
- package/dist/core/shared/author.d.ts +55 -1
- package/dist/core/shared/author.js +333 -16
- package/dist/core/shared/constants.js +3 -2
- package/dist/core/shared/errors.d.ts +4 -0
- package/dist/core/shared/errors.js +2 -2
- package/dist/core/store/settings-validator.d.ts +1 -0
- package/dist/core/store/settings-validator.js +6 -3
- package/dist/core/store/settings.js +5 -2
- package/dist/mcp/server.d.ts +1 -1
- package/dist/mcp/server.js +30 -9
- package/dist/sdk/agent/identity-config.d.ts +63 -0
- package/dist/sdk/agent/identity-config.js +107 -0
- package/dist/sdk/agent/refusal-corpus-contracts.d.ts +76 -0
- package/dist/sdk/agent/refusal-corpus-contracts.js +194 -0
- package/dist/sdk/agent/refusal-reachability.d.ts +1 -1
- package/dist/sdk/authoring.d.ts +1 -1
- package/dist/sdk/authoring.js +2 -2
- package/dist/sdk/cli-contracts/agent-output-contracts.d.ts +77 -0
- package/dist/sdk/cli-contracts/agent-output-contracts.js +326 -2
- package/dist/sdk/cli-contracts/commander-mutation-options.js +3 -3
- package/dist/sdk/cli-contracts/enum-contracts.d.ts +4 -0
- package/dist/sdk/cli-contracts/enum-contracts.js +9 -2
- package/dist/sdk/cli-contracts/flag-contracts.js +9 -2
- package/dist/sdk/cli-contracts/flag-lexicon-contracts.js +4 -4
- package/dist/sdk/cli-contracts/registration-helpers.js +5 -2
- package/dist/sdk/cli-contracts/runtime-contracts.d.ts +3 -1
- package/dist/sdk/cli-contracts/runtime-contracts.js +4 -3
- package/dist/sdk/cli-contracts/tool-parameter-tables.js +22 -2
- package/dist/sdk/cli-contracts/tool-schema.d.ts +1 -1
- package/dist/sdk/cli-contracts/tool-schema.js +14 -3
- package/dist/sdk/cli-contracts.d.ts +4 -4
- package/dist/sdk/cli-contracts.js +5 -5
- package/dist/sdk/config.d.ts +6 -1
- package/dist/sdk/config.js +119 -14
- package/dist/sdk/context-intent-contracts.js +4 -2
- package/dist/sdk/core-governance.d.ts +1 -1
- package/dist/sdk/core-governance.js +3 -3
- package/dist/sdk/extension/install-sources.d.ts +1 -1
- package/dist/sdk/extension/install-sources.js +6 -3
- package/dist/sdk/extension/output-ownership.d.ts +1 -0
- package/dist/sdk/extension/output-ownership.js +3 -2
- package/dist/sdk/extension/scaffold.js +11 -5
- package/dist/sdk/flag-invocation-contracts.js +4 -2
- package/dist/sdk/generated/generated-error-code-catalog-part-1.js +74 -2
- package/dist/sdk/generated/generated-error-code-catalog-part-2.js +87 -26
- package/dist/sdk/governance/assurance-action.js +30 -10
- package/dist/sdk/governance/health.d.ts +3 -0
- package/dist/sdk/governance/health.js +39 -7
- package/dist/sdk/governance/hierarchy-validation.d.ts +29 -0
- package/dist/sdk/governance/hierarchy-validation.js +109 -0
- package/dist/sdk/governance/validate.d.ts +3 -9
- package/dist/sdk/governance/validate.js +84 -111
- package/dist/sdk/graph/assembly.d.ts +6 -1
- package/dist/sdk/graph/assembly.js +16 -22
- package/dist/sdk/graph/governance-contracts.d.ts +1 -1
- package/dist/sdk/graph/governance-contracts.js +8 -2
- package/dist/sdk/graph/governance.js +46 -2
- package/dist/sdk/graph/hierarchy-integrity.d.ts +94 -0
- package/dist/sdk/graph/hierarchy-integrity.js +311 -0
- package/dist/sdk/graph/index.d.ts +2 -1
- package/dist/sdk/graph/index.js +3 -2
- package/dist/sdk/graph/remediation.js +32 -2
- package/dist/sdk/graph/run.d.ts +31 -2
- package/dist/sdk/graph/run.js +80 -31
- package/dist/sdk/graph/workspace-relationship-kind-registry.d.ts +8 -0
- package/dist/sdk/graph/workspace-relationship-kind-registry.js +28 -0
- package/dist/sdk/index.d.ts +8 -3
- package/dist/sdk/index.js +9 -4
- package/dist/sdk/item-children.js +21 -8
- package/dist/sdk/lifecycle/create.js +129 -97
- package/dist/sdk/lifecycle/hierarchy-mutation-lock.d.ts +15 -0
- package/dist/sdk/lifecycle/hierarchy-mutation-lock.js +25 -0
- package/dist/sdk/lifecycle/update.d.ts +2 -0
- package/dist/sdk/lifecycle/update.js +174 -84
- package/dist/sdk/merge/three-way.js +105 -26
- package/dist/sdk/query/complete-list.d.ts +3 -3
- package/dist/sdk/query/complete-list.js +46 -3
- package/dist/sdk/query/list.d.ts +4 -3
- package/dist/sdk/query/list.js +89 -49
- package/dist/sdk/read-output-budget.d.ts +21 -0
- package/dist/sdk/read-output-budget.js +39 -2
- package/dist/sdk/read-output-contracts.d.ts +10 -4
- package/dist/sdk/read-output-contracts.js +105 -32
- package/dist/sdk/relationship-context.js +3 -4
- package/dist/sdk/relationships.js +17 -10
- package/dist/sdk/reproducibility/process.d.ts +19 -0
- package/dist/sdk/reproducibility/process.js +88 -0
- package/dist/sdk/runtime-primitives.d.ts +1 -1
- package/dist/sdk/runtime-primitives.js +3 -3
- package/dist/sdk/runtime.d.ts +3 -2
- package/dist/sdk/runtime.js +7 -5
- package/dist/sdk/test/batch.d.ts +6 -0
- package/dist/sdk/test/batch.js +26 -6
- package/dist/sdk/test/entry.d.ts +1 -1
- package/dist/sdk/test/entry.js +3 -2
- package/dist/sdk/test/execution.d.ts +28 -3
- package/dist/sdk/test/execution.js +201 -34
- package/dist/sdk/test/parsers.d.ts +6 -0
- package/dist/sdk/test/parsers.js +21 -2
- package/dist/sdk/test/trust.d.ts +33 -0
- package/dist/sdk/test/trust.js +207 -0
- package/dist/types.d.ts +34 -0
- package/dist/types.js +2 -2
- package/docs/AGENT_PROVENANCE_ADR.md +41 -5
- package/docs/AGENT_UX_CONTRACTS.md +20 -4
- package/docs/COMMANDS.md +29 -14
- package/docs/CONFIGURATION.md +4 -0
- package/docs/DEPENDENCY_KIND_CONTRACT.md +31 -3
- package/docs/DIAGNOSTIC_OUTPUT_CONTRACTS.md +115 -0
- package/docs/EXTENSIONS.md +2 -1
- package/docs/MERGE_SAFETY.md +15 -1
- package/docs/README.md +1 -0
- package/docs/READ_OUTPUT_CONTRACTS.md +19 -2
- package/docs/RELATIONSHIP_GRAPH.md +6 -3
- package/docs/REPRODUCIBLE_WORKSPACES.md +42 -8
- package/docs/SDK.md +9 -0
- package/docs/SDK_AGENT_SESSION_CONTEXT.md +6 -4
- package/docs/SDK_EVIDENCE_TRACEABILITY.md +1 -1
- package/docs/TESTING.md +83 -1
- package/docs/agent-task-token-baseline.json +2 -2
- package/docs/generated/FLAG_LEXICON_BUDGETS.md +2 -2
- package/marketplace.json +2 -2
- package/package.json +2 -2
- package/packages/pm-beads/package.json +1 -1
- package/packages/pm-calendar/package.json +1 -1
- package/packages/pm-command-kit/package.json +1 -1
- package/packages/pm-digital-twin/package.json +1 -1
- package/packages/pm-governance-audit/package.json +1 -1
- package/packages/pm-guide-shell/package.json +1 -1
- package/packages/pm-kanban/package.json +1 -1
- package/packages/pm-lifecycle-hooks/package.json +1 -1
- package/packages/pm-linked-test-adapters/package.json +1 -1
- package/packages/pm-search-advanced/package.json +1 -1
- package/packages/pm-templates/package.json +1 -1
- package/packages/pm-todos/package.json +1 -1
- package/packages/pm-vcs/package.json +1 -1
- package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
- package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
- package/sdk/public-surface.json +1021 -53
- package/dist/cli-bundle/chunks/chunk-2MA6ZQVZ.js +0 -197
- package/dist/cli-bundle/chunks/chunk-3AFAUHUE.js +0 -2
- package/dist/cli-bundle/chunks/chunk-7VHZS727.js +0 -5
- package/dist/cli-bundle/chunks/chunk-JSLOFXGA.js +0 -3
- package/dist/cli-bundle/chunks/chunk-OCLPLFAY.js +0 -35
- package/dist/cli-bundle/chunks/chunk-S4IRYKBU.js +0 -13
- package/dist/cli-bundle/chunks/register-mutation-7IY2D5KO.js +0 -20
- package/dist/cli-bundle/chunks/register-operations-FALL3YTU.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-7WZRLCYF.js +0 -50
- package/dist/cli-bundle/focused-chunks/chunk-CKKQSJNN.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-EEFKYMDR.js +0 -16
- package/dist/cli-bundle/focused-chunks/chunk-F2A56DHV.js +0 -8
- package/dist/cli-bundle/focused-chunks/chunk-FV5ACOK7.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-JEAVFOWM.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-NJ63LOP6.js +0 -155
- package/dist/cli-bundle/focused-chunks/chunk-NXRTQ7HI.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-TJ7EMJ46.js +0 -4
- package/dist/cli-bundle/focused-chunks/chunk-ZOSEELYJ.js +0 -26
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module sdk/governance/hierarchy-validation
|
|
3
|
+
*
|
|
4
|
+
* Adapts the shared hierarchy analyzer to validation-specific diagnostics and
|
|
5
|
+
* deterministic cycle paths without coupling the analyzer to CLI policy.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="77a3c404-bbcd-5a5e-80bf-cb61c45af55d")}catch(e){}}();
|
|
9
|
+
import { statusIsTerminal, } from "../../core/schema/runtime-schema.js";
|
|
10
|
+
import { resolveWorkspaceRelationshipKindRegistry } from "../graph/assembly.js";
|
|
11
|
+
import { analyzeHierarchyIntegrity } from "../graph/hierarchy-integrity.js";
|
|
12
|
+
/** Resolve one deterministic closed walk through a known cycle component. */
|
|
13
|
+
/* c8 ignore start -- fallback branches are covered by lifecycle graph integration tests */
|
|
14
|
+
export function resolveLifecycleDependencyCycleSamplePath(component, graph) {
|
|
15
|
+
const start = component[0];
|
|
16
|
+
if (component.length === 1)
|
|
17
|
+
return [start, start];
|
|
18
|
+
const componentSet = new Set(component);
|
|
19
|
+
const path = [start];
|
|
20
|
+
const visited = new Set([start]);
|
|
21
|
+
const search = (current) => {
|
|
22
|
+
for (const next of (graph.get(current) ?? []).filter((candidate) => componentSet.has(candidate))) {
|
|
23
|
+
if (next === start && path.length > 1) {
|
|
24
|
+
path.push(start);
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
if (visited.has(next))
|
|
28
|
+
continue;
|
|
29
|
+
visited.add(next);
|
|
30
|
+
path.push(next);
|
|
31
|
+
if (search(next))
|
|
32
|
+
return true;
|
|
33
|
+
path.pop();
|
|
34
|
+
visited.delete(next);
|
|
35
|
+
}
|
|
36
|
+
return false;
|
|
37
|
+
};
|
|
38
|
+
return search(start) ? [...path] : [...component, start];
|
|
39
|
+
}
|
|
40
|
+
/* c8 ignore stop */
|
|
41
|
+
/** Resolve a scalar parent spelling to the workspace's canonical identifier. */
|
|
42
|
+
function resolveCanonicalScalarParent(parent, canonicalIdByLowercase) {
|
|
43
|
+
if (typeof parent !== "string")
|
|
44
|
+
return undefined;
|
|
45
|
+
const normalized = parent.trim().toLowerCase();
|
|
46
|
+
return normalized.length > 0
|
|
47
|
+
? canonicalIdByLowercase.get(normalized)
|
|
48
|
+
: undefined;
|
|
49
|
+
}
|
|
50
|
+
/** Append dependency-backed child-to-parent adjacency for one holder. */
|
|
51
|
+
function appendDependencyHierarchyEdges(item, graph, canonicalIdByLowercase, relationshipRegistry) {
|
|
52
|
+
for (const dependency of item.dependencies ?? []) {
|
|
53
|
+
if (typeof dependency !== "object" ||
|
|
54
|
+
dependency === null ||
|
|
55
|
+
typeof dependency.id !== "string")
|
|
56
|
+
continue;
|
|
57
|
+
const definition = relationshipRegistry.resolve(dependency.kind);
|
|
58
|
+
if (!definition?.hierarchy)
|
|
59
|
+
continue;
|
|
60
|
+
const target = canonicalIdByLowercase.get(dependency.id.toLowerCase());
|
|
61
|
+
if (!target)
|
|
62
|
+
continue;
|
|
63
|
+
if (definition.hierarchyDirection === "source_parent")
|
|
64
|
+
graph.get(target).push(item.id);
|
|
65
|
+
else
|
|
66
|
+
graph.get(item.id).push(target);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/** Build canonical child-to-parent adjacency across every hierarchy spelling. */
|
|
70
|
+
export function buildLifecycleParentGraph(items, relationshipRegistry = resolveWorkspaceRelationshipKindRegistry()) {
|
|
71
|
+
const canonicalIdByLowercase = new Map(items.map((item) => [item.id.toLowerCase(), item.id]));
|
|
72
|
+
const graph = new Map(items.map((item) => [item.id, []]));
|
|
73
|
+
for (const item of [...items].sort((left, right) => left.id.localeCompare(right.id))) {
|
|
74
|
+
const edges = graph.get(item.id);
|
|
75
|
+
const canonicalParentId = resolveCanonicalScalarParent(item.parent, canonicalIdByLowercase);
|
|
76
|
+
if (canonicalParentId)
|
|
77
|
+
edges.push(canonicalParentId);
|
|
78
|
+
appendDependencyHierarchyEdges(item, graph, canonicalIdByLowercase, relationshipRegistry);
|
|
79
|
+
}
|
|
80
|
+
for (const [id, edges] of graph) {
|
|
81
|
+
graph.set(id, [...new Set(edges)].sort());
|
|
82
|
+
}
|
|
83
|
+
return graph;
|
|
84
|
+
}
|
|
85
|
+
/** Project shared hierarchy analysis into the validation result contract. */
|
|
86
|
+
export function detectLifecycleParentCycles(items, statusRegistry, relationshipRegistry = resolveWorkspaceRelationshipKindRegistry()) {
|
|
87
|
+
const hierarchy = analyzeHierarchyIntegrity(items, (status) => statusRegistry
|
|
88
|
+
? statusIsTerminal(status, statusRegistry)
|
|
89
|
+
: status === "closed" || status === "canceled", relationshipRegistry);
|
|
90
|
+
const graph = buildLifecycleParentGraph(items, relationshipRegistry);
|
|
91
|
+
const cycleComponents = hierarchy.cycles.map((cycle) => cycle.item_ids);
|
|
92
|
+
return {
|
|
93
|
+
cycle_count: cycleComponents.length,
|
|
94
|
+
active_cycle_count: hierarchy.cycles.filter((cycle) => !cycle.legacy_terminal).length,
|
|
95
|
+
legacy_cycle_count: hierarchy.cycles.filter((cycle) => cycle.legacy_terminal).length,
|
|
96
|
+
cycle_item_ids: [...new Set(cycleComponents.flat())].sort(),
|
|
97
|
+
cycle_sample_paths: cycleComponents.map((component) => resolveLifecycleDependencyCycleSamplePath(component, graph).join("->")),
|
|
98
|
+
cardinality_violation_count: hierarchy.cardinality_violations.length,
|
|
99
|
+
active_cardinality_violation_count: hierarchy.cardinality_violations.filter((finding) => !finding.legacy_terminal).length,
|
|
100
|
+
legacy_cardinality_violation_count: hierarchy.cardinality_violations.filter((finding) => finding.legacy_terminal).length,
|
|
101
|
+
cardinality_violation_rows: hierarchy.cardinality_violations.map((finding) => `${finding.child_id}:${finding.parent_ids.join(",")}:${finding.legacy_terminal ? "legacy" : "active"}`),
|
|
102
|
+
parent_divergence_count: hierarchy.divergences.length,
|
|
103
|
+
active_parent_divergence_count: hierarchy.divergences.filter((finding) => !finding.legacy_terminal).length,
|
|
104
|
+
legacy_parent_divergence_count: hierarchy.divergences.filter((finding) => finding.legacy_terminal).length,
|
|
105
|
+
parent_divergence_rows: hierarchy.divergences.map((finding) => `${finding.child_id}:scalar=${finding.scalar_parent_id}:dependencies=${finding.dependency_parent_ids.join(",")}:${finding.legacy_terminal ? "legacy" : "active"}`),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=hierarchy-validation.js.map
|
|
109
|
+
//# debugId=77a3c404-bbcd-5a5e-80bf-cb61c45af55d
|
|
@@ -7,6 +7,7 @@ import type { ValidateMetadataProfile, ValidateMetadataRequiredField } from "../
|
|
|
7
7
|
import { type OutputProjectionDeclaration } from "../output-projection.js";
|
|
8
8
|
import { type RelationshipKindRegistry } from "../relationships.js";
|
|
9
9
|
import { type ValidateItem } from "./validate-item-reader.js";
|
|
10
|
+
import { buildLifecycleParentGraph, detectLifecycleParentCycles, resolveLifecycleDependencyCycleSamplePath } from "./hierarchy-validation.js";
|
|
10
11
|
type ValidateCheckName = "metadata" | "resolution" | "lifecycle" | "dependency_references" | "files" | "command_references" | "history_drift" | "format_version" | "storage_integrity";
|
|
11
12
|
type ValidateStatus = "ok" | "warn" | "error";
|
|
12
13
|
type ValidateDependencyCycleSeverity = "off" | "warn" | "error";
|
|
@@ -198,18 +199,11 @@ declare function buildDependencyReferencesCheck(items: ItemWithBody[], verboseDi
|
|
|
198
199
|
};
|
|
199
200
|
declare function extractItemIds(value: string, idPrefix?: string): string[];
|
|
200
201
|
declare function findLifecycleDependencyCycleComponents(graph: Map<string, string[]>): string[][];
|
|
201
|
-
declare function resolveLifecycleDependencyCycleSamplePath(component: string[], graph: Map<string, string[]>): string[];
|
|
202
202
|
declare function detectLifecycleDependencyCycles(activeItems: ItemWithBody[], idPrefix?: string): {
|
|
203
203
|
cycle_count: number;
|
|
204
204
|
cycle_item_ids: string[];
|
|
205
205
|
cycle_sample_paths: string[];
|
|
206
206
|
};
|
|
207
|
-
declare function buildLifecycleParentGraph(items: ItemWithBody[], relationshipRegistry?: RelationshipKindRegistry): Map<string, string[]>;
|
|
208
|
-
declare function detectLifecycleParentCycles(items: ItemWithBody[]): {
|
|
209
|
-
cycle_count: number;
|
|
210
|
-
cycle_item_ids: string[];
|
|
211
|
-
cycle_sample_paths: string[];
|
|
212
|
-
};
|
|
213
207
|
interface OrphanedPathRow {
|
|
214
208
|
path: string;
|
|
215
209
|
classification: OrphanedPathClassification;
|
|
@@ -237,10 +231,10 @@ declare function buildFilesCheck(items: ItemWithBody[], workspaceRoot: string, p
|
|
|
237
231
|
warnings: string[];
|
|
238
232
|
staleLinkPruneRows: StaleLinkPruneRow[];
|
|
239
233
|
}>;
|
|
240
|
-
declare function buildCommandReferencesCheck(items: ItemWithBody[], idPrefix: string, verboseDiagnostics: boolean): {
|
|
234
|
+
declare function buildCommandReferencesCheck(items: ItemWithBody[], idPrefix: string, verboseDiagnostics: boolean, pmRoot: string): Promise<{
|
|
241
235
|
check: ValidateCheck;
|
|
242
236
|
warnings: string[];
|
|
243
|
-
}
|
|
237
|
+
}>;
|
|
244
238
|
/** Apply one planned fix through the SAME audited command paths an operator would use by hand (`pm update` / `pm files --remove` / `pm docs --remove`), so every applied fix carries normal history, locking, and hook behavior. Command modules are imported lazily: plain validate runs stay read-only and never pay the mutation-stack import cost. */
|
|
245
239
|
declare function applyValidateFix(fix: ValidateFixRecord, global: GlobalOptions, services: ValidateMutationServices): Promise<void>;
|
|
246
240
|
declare function applyValidateFixes(applicable: ValidateFixRecord[], global: GlobalOptions, services: ValidateMutationServices): Promise<{
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Implements the pm validate command surface and its agent-facing runtime behavior.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
7
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="aca69fd7-c47a-5424-b58f-f305f690edd9")}catch(e){}}();
|
|
8
8
|
import { assertInitializedTracker } from "../environment/tracker-preflight.js";
|
|
9
9
|
import fs from "node:fs/promises";
|
|
10
10
|
import { realpathSync } from "node:fs";
|
|
@@ -42,8 +42,10 @@ import { createRelationshipKindRegistry, } from "../relationships.js";
|
|
|
42
42
|
import { runDocs } from "../docs.js";
|
|
43
43
|
import { runFiles } from "../files.js";
|
|
44
44
|
import { extractReferencedPmItemIdsFromCommand } from "../test/linked-command-detection.js";
|
|
45
|
+
import { resolveLinkedTestSourceRef, resolveLinkedTestSourceWorkspaceRoot, resolveLinkedTestTrustBatch, } from "../test/trust.js";
|
|
45
46
|
import { readValidateItems, } from "./validate-item-reader.js";
|
|
46
47
|
import { buildValidateHistoryDriftCheck } from "./validate-history-drift.js";
|
|
48
|
+
import { buildLifecycleParentGraph, detectLifecycleParentCycles, resolveLifecycleDependencyCycleSamplePath, } from "./hierarchy-validation.js";
|
|
47
49
|
const FILE_SCAN_DIRECTORIES = ["src", "tests", "docs"];
|
|
48
50
|
const FILE_SCAN_ROOT_FILES = [
|
|
49
51
|
"README.md",
|
|
@@ -1107,41 +1109,6 @@ function findLifecycleDependencyCycleComponents(graph) {
|
|
|
1107
1109
|
left.join(",").localeCompare(right.join(",")));
|
|
1108
1110
|
}
|
|
1109
1111
|
/* c8 ignore stop */
|
|
1110
|
-
/* c8 ignore start -- cycle sample-path fallback branches are covered by lifecycle graph integration tests */
|
|
1111
|
-
function resolveLifecycleDependencyCycleSamplePath(component, graph) {
|
|
1112
|
-
const start = component[0];
|
|
1113
|
-
if (component.length === 1) {
|
|
1114
|
-
return [start, start];
|
|
1115
|
-
}
|
|
1116
|
-
const componentSet = new Set(component);
|
|
1117
|
-
const path = [start];
|
|
1118
|
-
const visited = new Set([start]);
|
|
1119
|
-
const search = (current) => {
|
|
1120
|
-
const neighbors = (graph.get(current) ?? []).filter((candidate) => componentSet.has(candidate));
|
|
1121
|
-
for (const next of neighbors) {
|
|
1122
|
-
if (next === start && path.length > 1) {
|
|
1123
|
-
path.push(start);
|
|
1124
|
-
return true;
|
|
1125
|
-
}
|
|
1126
|
-
if (visited.has(next)) {
|
|
1127
|
-
continue;
|
|
1128
|
-
}
|
|
1129
|
-
visited.add(next);
|
|
1130
|
-
path.push(next);
|
|
1131
|
-
if (search(next)) {
|
|
1132
|
-
return true;
|
|
1133
|
-
}
|
|
1134
|
-
path.pop();
|
|
1135
|
-
visited.delete(next);
|
|
1136
|
-
}
|
|
1137
|
-
return false;
|
|
1138
|
-
};
|
|
1139
|
-
if (search(start)) {
|
|
1140
|
-
return [...path];
|
|
1141
|
-
}
|
|
1142
|
-
return [...component, start];
|
|
1143
|
-
}
|
|
1144
|
-
/* c8 ignore stop */
|
|
1145
1112
|
function detectLifecycleDependencyCycles(activeItems, idPrefix = "pm") {
|
|
1146
1113
|
const graph = buildLifecycleDependencyGraph(activeItems, idPrefix);
|
|
1147
1114
|
const cycleComponents = findLifecycleDependencyCycleComponents(graph);
|
|
@@ -1153,62 +1120,6 @@ function detectLifecycleDependencyCycles(activeItems, idPrefix = "pm") {
|
|
|
1153
1120
|
cycle_sample_paths: cycleSamplePaths,
|
|
1154
1121
|
};
|
|
1155
1122
|
}
|
|
1156
|
-
// Parent (composition) cycle detection (pm-8vul / GH-280). The dependency-cycle
|
|
1157
|
-
// path above only walks blocked_by/definition_of_ready edges across ACTIVE items;
|
|
1158
|
-
// it never traverses item.parent, so a parent cycle (A.parent=B, B.parent=A, or
|
|
1159
|
-
// any longer ring) goes undetected while it silently breaks `pm list --tree`,
|
|
1160
|
-
// orphan detection, and progress rollups. We reuse the generic Tarjan SCC helper
|
|
1161
|
-
// on a child->[parent] adjacency map. Unlike dependency cycles we scan ALL items
|
|
1162
|
-
// (not just active ones) because a parent cycle among closed items is still
|
|
1163
|
-
// structural corruption of the hierarchy.
|
|
1164
|
-
function buildLifecycleParentGraph(items, relationshipRegistry = createRelationshipKindRegistry()) {
|
|
1165
|
-
// PR #279 made parent matching case-insensitive (e.g. `parent: PM-FK49`
|
|
1166
|
-
// resolves to `id: pm-fk49`). Resolve parent references to their canonical
|
|
1167
|
-
// item id the same way so a casing mismatch can never silently drop a cycle
|
|
1168
|
-
// edge and hide a parent cycle (false negative).
|
|
1169
|
-
const canonicalIdByLowercase = new Map(items.map((item) => [item.id.toLowerCase(), item.id]));
|
|
1170
|
-
const graph = new Map(items.map((item) => [item.id, []]));
|
|
1171
|
-
const sortedItems = [...items].sort((left, right) => left.id.localeCompare(right.id));
|
|
1172
|
-
sortedItems.forEach((item) => {
|
|
1173
|
-
const edges = graph.get(item.id);
|
|
1174
|
-
const parentId = toMeaningfulString(item.parent);
|
|
1175
|
-
const canonicalParentId = parentId
|
|
1176
|
-
? canonicalIdByLowercase.get(parentId.toLowerCase())
|
|
1177
|
-
: undefined;
|
|
1178
|
-
if (canonicalParentId) {
|
|
1179
|
-
edges.push(canonicalParentId);
|
|
1180
|
-
}
|
|
1181
|
-
(item.dependencies ?? []).forEach((dependency) => {
|
|
1182
|
-
const definition = relationshipRegistry.resolve(dependency.kind);
|
|
1183
|
-
if (!definition?.hierarchy)
|
|
1184
|
-
return;
|
|
1185
|
-
const target = canonicalIdByLowercase.get(dependency.id.toLowerCase());
|
|
1186
|
-
if (!target)
|
|
1187
|
-
return;
|
|
1188
|
-
if (definition.hierarchyDirection === "source_parent") {
|
|
1189
|
-
graph.get(target).push(item.id);
|
|
1190
|
-
}
|
|
1191
|
-
else {
|
|
1192
|
-
edges.push(target);
|
|
1193
|
-
}
|
|
1194
|
-
});
|
|
1195
|
-
});
|
|
1196
|
-
graph.forEach((edges, id) => {
|
|
1197
|
-
graph.set(id, [...new Set(edges)].sort((left, right) => left.localeCompare(right)));
|
|
1198
|
-
});
|
|
1199
|
-
return graph;
|
|
1200
|
-
}
|
|
1201
|
-
function detectLifecycleParentCycles(items) {
|
|
1202
|
-
const graph = buildLifecycleParentGraph(items);
|
|
1203
|
-
const cycleComponents = findLifecycleDependencyCycleComponents(graph);
|
|
1204
|
-
const cycleItemIds = [...new Set(cycleComponents.flat())].sort((left, right) => left.localeCompare(right));
|
|
1205
|
-
const cycleSamplePaths = cycleComponents.map((component) => resolveLifecycleDependencyCycleSamplePath(component, graph).join("->"));
|
|
1206
|
-
return {
|
|
1207
|
-
cycle_count: cycleComponents.length,
|
|
1208
|
-
cycle_item_ids: cycleItemIds,
|
|
1209
|
-
cycle_sample_paths: cycleSamplePaths,
|
|
1210
|
-
};
|
|
1211
|
-
}
|
|
1212
1123
|
function classifyOrphanedPath(pathValue) {
|
|
1213
1124
|
if (pathValue.startsWith("docs/")) {
|
|
1214
1125
|
return "docs_unowned";
|
|
@@ -1435,7 +1346,10 @@ function lifecycleCycleWarningToken(prefix, severity, count) {
|
|
|
1435
1346
|
}
|
|
1436
1347
|
return `${severity === "error" ? `${prefix}_error` : prefix}:${count}`;
|
|
1437
1348
|
}
|
|
1438
|
-
function
|
|
1349
|
+
function lifecycleAdvisoryWarningToken(prefix, severity, count) {
|
|
1350
|
+
return count > 0 && severity !== "off" ? `${prefix}:${count}` : null;
|
|
1351
|
+
}
|
|
1352
|
+
function buildLifecycleWarnings(rows, includeStaleBlockers, dependencyCycleSeverity, parentCycleSeverity, counts) {
|
|
1439
1353
|
const warnings = [];
|
|
1440
1354
|
if (rows.closureLikeRows.length > 0) {
|
|
1441
1355
|
warnings.push(`validate_lifecycle_active_closure_like_metadata:${rows.closureLikeRows.length}`);
|
|
@@ -1446,12 +1360,22 @@ function buildLifecycleWarnings(rows, includeStaleBlockers, dependencyCycleSever
|
|
|
1446
1360
|
if (includeStaleBlockers && rows.staleBlockerRows.length > 0) {
|
|
1447
1361
|
warnings.push(`validate_lifecycle_stale_blockers:${rows.staleBlockerRows.length}`);
|
|
1448
1362
|
}
|
|
1449
|
-
const dependencyWarning = lifecycleCycleWarningToken("validate_lifecycle_dependency_cycles", dependencyCycleSeverity,
|
|
1450
|
-
const parentWarning = lifecycleCycleWarningToken("validate_hierarchy_parent_cycle", parentCycleSeverity,
|
|
1363
|
+
const dependencyWarning = lifecycleCycleWarningToken("validate_lifecycle_dependency_cycles", dependencyCycleSeverity, counts.dependencyCycles);
|
|
1364
|
+
const parentWarning = lifecycleCycleWarningToken("validate_hierarchy_parent_cycle", parentCycleSeverity, counts.activeHierarchyCycles);
|
|
1365
|
+
const cardinalityWarning = lifecycleCycleWarningToken("validate_hierarchy_cardinality_violation", parentCycleSeverity, counts.activeHierarchyCardinality);
|
|
1366
|
+
const divergenceWarning = lifecycleCycleWarningToken("validate_hierarchy_parent_divergence", parentCycleSeverity, counts.activeHierarchyDivergence);
|
|
1367
|
+
const legacyCycleWarning = lifecycleAdvisoryWarningToken("validate_legacy_hierarchy_parent_cycle", parentCycleSeverity, counts.legacyHierarchyCycles);
|
|
1368
|
+
const legacyCardinalityWarning = lifecycleAdvisoryWarningToken("validate_legacy_hierarchy_cardinality_violation", parentCycleSeverity, counts.legacyHierarchyCardinality);
|
|
1369
|
+
const legacyDivergenceWarning = lifecycleAdvisoryWarningToken("validate_legacy_hierarchy_parent_divergence", parentCycleSeverity, counts.legacyHierarchyDivergence);
|
|
1451
1370
|
return [
|
|
1452
1371
|
...warnings,
|
|
1453
1372
|
...(dependencyWarning ? [dependencyWarning] : []),
|
|
1454
1373
|
...(parentWarning ? [parentWarning] : []),
|
|
1374
|
+
...(cardinalityWarning ? [cardinalityWarning] : []),
|
|
1375
|
+
...(divergenceWarning ? [divergenceWarning] : []),
|
|
1376
|
+
...(legacyCycleWarning ? [legacyCycleWarning] : []),
|
|
1377
|
+
...(legacyCardinalityWarning ? [legacyCardinalityWarning] : []),
|
|
1378
|
+
...(legacyDivergenceWarning ? [legacyDivergenceWarning] : []),
|
|
1455
1379
|
];
|
|
1456
1380
|
}
|
|
1457
1381
|
function linkedArtifactPathExceedsFilesystemLimits(artifactPath) {
|
|
@@ -1557,8 +1481,16 @@ function buildLifecycleCheck(items, includeStaleBlockers, dependencyCycleSeverit
|
|
|
1557
1481
|
const rows = collectLifecycleScanRows(items, includeStaleBlockers, statusRegistry, lifecyclePatternPolicy);
|
|
1558
1482
|
sortLifecycleScanRows(rows);
|
|
1559
1483
|
const dependencyCycleDiagnostics = detectLifecycleDependencyCycles(rows.activeItems, idPrefix);
|
|
1560
|
-
const parentCycleDiagnostics = detectLifecycleParentCycles(items);
|
|
1561
|
-
const warnings = buildLifecycleWarnings(rows, includeStaleBlockers, dependencyCycleSeverity, parentCycleSeverity,
|
|
1484
|
+
const parentCycleDiagnostics = detectLifecycleParentCycles(items, statusRegistry);
|
|
1485
|
+
const warnings = buildLifecycleWarnings(rows, includeStaleBlockers, dependencyCycleSeverity, parentCycleSeverity, {
|
|
1486
|
+
dependencyCycles: dependencyCycleDiagnostics.cycle_count,
|
|
1487
|
+
activeHierarchyCycles: parentCycleDiagnostics.active_cycle_count,
|
|
1488
|
+
activeHierarchyCardinality: parentCycleDiagnostics.active_cardinality_violation_count,
|
|
1489
|
+
activeHierarchyDivergence: parentCycleDiagnostics.active_parent_divergence_count,
|
|
1490
|
+
legacyHierarchyCycles: parentCycleDiagnostics.legacy_cycle_count,
|
|
1491
|
+
legacyHierarchyCardinality: parentCycleDiagnostics.legacy_cardinality_violation_count,
|
|
1492
|
+
legacyHierarchyDivergence: parentCycleDiagnostics.legacy_parent_divergence_count,
|
|
1493
|
+
});
|
|
1562
1494
|
const diagnosticLimit = verboseDiagnostics
|
|
1563
1495
|
? Number.POSITIVE_INFINITY
|
|
1564
1496
|
: DIAGNOSTIC_LIST_SUMMARY_LIMIT;
|
|
@@ -1569,9 +1501,14 @@ function buildLifecycleCheck(items, includeStaleBlockers, dependencyCycleSeverit
|
|
|
1569
1501
|
const summarizedDependencyCycleSamplePaths = summarizeList(dependencyCycleDiagnostics.cycle_sample_paths, diagnosticLimit);
|
|
1570
1502
|
const summarizedParentCycleItemIds = summarizeList(parentCycleDiagnostics.cycle_item_ids, diagnosticLimit);
|
|
1571
1503
|
const summarizedParentCycleSamplePaths = summarizeList(parentCycleDiagnostics.cycle_sample_paths, diagnosticLimit);
|
|
1504
|
+
const summarizedHierarchyCardinalityRows = summarizeList(parentCycleDiagnostics.cardinality_violation_rows, diagnosticLimit);
|
|
1505
|
+
const summarizedHierarchyDivergenceRows = summarizeList(parentCycleDiagnostics.parent_divergence_rows, diagnosticLimit);
|
|
1572
1506
|
const hasErrorSeverityCycle = (dependencyCycleDiagnostics.cycle_count > 0 &&
|
|
1573
1507
|
dependencyCycleSeverity === "error") ||
|
|
1574
|
-
(parentCycleDiagnostics.
|
|
1508
|
+
((parentCycleDiagnostics.active_cycle_count > 0 ||
|
|
1509
|
+
parentCycleDiagnostics.active_cardinality_violation_count > 0 ||
|
|
1510
|
+
parentCycleDiagnostics.active_parent_divergence_count > 0) &&
|
|
1511
|
+
parentCycleSeverity === "error");
|
|
1575
1512
|
return {
|
|
1576
1513
|
check: {
|
|
1577
1514
|
name: "lifecycle",
|
|
@@ -1602,11 +1539,23 @@ function buildLifecycleCheck(items, includeStaleBlockers, dependencyCycleSeverit
|
|
|
1602
1539
|
dependency_cycle_sample_paths_truncated: summarizedDependencyCycleSamplePaths.truncated,
|
|
1603
1540
|
parent_cycle_severity_policy: parentCycleSeverity,
|
|
1604
1541
|
parent_cycle_count: parentCycleDiagnostics.cycle_count,
|
|
1542
|
+
active_parent_cycle_count: parentCycleDiagnostics.active_cycle_count,
|
|
1543
|
+
legacy_parent_cycle_count: parentCycleDiagnostics.legacy_cycle_count,
|
|
1605
1544
|
parent_cycle_item_count: parentCycleDiagnostics.cycle_item_ids.length,
|
|
1606
1545
|
parent_cycle_item_ids: summarizedParentCycleItemIds.values,
|
|
1607
1546
|
parent_cycle_item_ids_truncated: summarizedParentCycleItemIds.truncated,
|
|
1608
1547
|
parent_cycle_sample_paths: summarizedParentCycleSamplePaths.values,
|
|
1609
1548
|
parent_cycle_sample_paths_truncated: summarizedParentCycleSamplePaths.truncated,
|
|
1549
|
+
hierarchy_cardinality_violation_count: parentCycleDiagnostics.cardinality_violation_count,
|
|
1550
|
+
active_hierarchy_cardinality_violation_count: parentCycleDiagnostics.active_cardinality_violation_count,
|
|
1551
|
+
legacy_hierarchy_cardinality_violation_count: parentCycleDiagnostics.legacy_cardinality_violation_count,
|
|
1552
|
+
hierarchy_cardinality_violation_rows: summarizedHierarchyCardinalityRows.values,
|
|
1553
|
+
hierarchy_cardinality_violation_rows_truncated: summarizedHierarchyCardinalityRows.truncated,
|
|
1554
|
+
hierarchy_parent_divergence_count: parentCycleDiagnostics.parent_divergence_count,
|
|
1555
|
+
active_hierarchy_parent_divergence_count: parentCycleDiagnostics.active_parent_divergence_count,
|
|
1556
|
+
legacy_hierarchy_parent_divergence_count: parentCycleDiagnostics.legacy_parent_divergence_count,
|
|
1557
|
+
hierarchy_parent_divergence_rows: summarizedHierarchyDivergenceRows.values,
|
|
1558
|
+
hierarchy_parent_divergence_rows_truncated: summarizedHierarchyDivergenceRows.truncated,
|
|
1610
1559
|
stale_blocker_reason_patterns: [
|
|
1611
1560
|
...lifecyclePatternPolicy.stale_blocker_reason_patterns,
|
|
1612
1561
|
],
|
|
@@ -1762,23 +1711,40 @@ async function buildFilesCheck(items, workspaceRoot, pmRoot, fileScanMode, inclu
|
|
|
1762
1711
|
};
|
|
1763
1712
|
}
|
|
1764
1713
|
/* c8 ignore stop */
|
|
1765
|
-
|
|
1766
|
-
function
|
|
1714
|
+
/** Collapse a linked command to the bounded preview used in validation rows. */
|
|
1715
|
+
function summarizeCommandPreview(command) {
|
|
1767
1716
|
const normalizedCommand = command.trim().replaceAll(/\s+/g, " ");
|
|
1768
|
-
|
|
1717
|
+
return normalizedCommand.length > 120
|
|
1769
1718
|
? `${normalizedCommand.slice(0, 117)}...`
|
|
1770
1719
|
: normalizedCommand;
|
|
1771
|
-
return `${ownerId}:${referencedId}:${commandPreview}`;
|
|
1772
1720
|
}
|
|
1773
|
-
|
|
1721
|
+
/** Join command-reference identity and its bounded preview for diagnostics. */
|
|
1722
|
+
function summarizeCommandReferenceRow(ownerId, referencedId, command) {
|
|
1723
|
+
return `${ownerId}:${referencedId}:${summarizeCommandPreview(command)}`;
|
|
1724
|
+
}
|
|
1725
|
+
/** Render portable diagnostics for linked tests that this clone has not trusted. */
|
|
1726
|
+
function buildUntrustedLinkedTestRows(linkedTests, trustDecisions) {
|
|
1727
|
+
const rows = [];
|
|
1728
|
+
linkedTests.forEach(({ item, linkedTest }, index) => {
|
|
1729
|
+
const trust = trustDecisions[index];
|
|
1730
|
+
if (trust.trusted)
|
|
1731
|
+
return;
|
|
1732
|
+
const command = summarizeCommandPreview(linkedTest.command ?? "");
|
|
1733
|
+
rows.push(`${item.id}:${trust.source_ref ?? "unknown"}->${trust.current_source_ref ?? "unknown"}:${command}`);
|
|
1734
|
+
});
|
|
1735
|
+
return rows;
|
|
1736
|
+
}
|
|
1774
1737
|
/* c8 ignore start -- command-reference discovery/stale-id permutations are covered by linked-test integration suites */
|
|
1775
|
-
function buildCommandReferencesCheck(items, idPrefix, verboseDiagnostics) {
|
|
1738
|
+
async function buildCommandReferencesCheck(items, idPrefix, verboseDiagnostics, pmRoot) {
|
|
1776
1739
|
const knownIds = new Set(items.map((item) => item.id.toLowerCase()));
|
|
1777
1740
|
let linkedCommandsScanned = 0;
|
|
1778
1741
|
let referencedPmIdCount = 0;
|
|
1779
1742
|
const referencedPmIds = new Set();
|
|
1780
1743
|
const staleReferenceRows = [];
|
|
1781
1744
|
const linkedTests = items.flatMap((item) => (item.tests ?? []).map((linkedTest) => ({ item, linkedTest })));
|
|
1745
|
+
const currentSourceRef = await resolveLinkedTestSourceRef(resolveLinkedTestSourceWorkspaceRoot());
|
|
1746
|
+
const trustDecisions = await resolveLinkedTestTrustBatch(pmRoot, linkedTests.map(({ linkedTest }) => linkedTest), currentSourceRef);
|
|
1747
|
+
const untrustedRows = buildUntrustedLinkedTestRows(linkedTests, trustDecisions);
|
|
1782
1748
|
for (const { item, linkedTest } of linkedTests) {
|
|
1783
1749
|
if (typeof linkedTest.command !== "string" ||
|
|
1784
1750
|
linkedTest.command.trim().length === 0) {
|
|
@@ -1803,17 +1769,20 @@ function buildCommandReferencesCheck(items, idPrefix, verboseDiagnostics) {
|
|
|
1803
1769
|
]
|
|
1804
1770
|
.filter((value) => value.length > 0)
|
|
1805
1771
|
.sort((left, right) => left.localeCompare(right));
|
|
1806
|
-
const warnings =
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1772
|
+
const warnings = [];
|
|
1773
|
+
if (uniqueStaleReferenceRows.length > 0) {
|
|
1774
|
+
warnings.push(`validate_command_references_stale_pm_ids:${uniqueStaleReferenceRows.length}`);
|
|
1775
|
+
}
|
|
1776
|
+
if (untrustedRows.length > 0) {
|
|
1777
|
+
warnings.push(`validate_linked_test_trust_unacknowledged:${untrustedRows.length}`);
|
|
1778
|
+
}
|
|
1811
1779
|
const diagnosticLimit = verboseDiagnostics
|
|
1812
1780
|
? Number.POSITIVE_INFINITY
|
|
1813
1781
|
: DIAGNOSTIC_LIST_SUMMARY_LIMIT;
|
|
1814
1782
|
const summarizedRows = summarizeList(uniqueStaleReferenceRows, diagnosticLimit);
|
|
1815
1783
|
const summarizedStalePmIds = summarizeList(stalePmIds, diagnosticLimit);
|
|
1816
1784
|
const summarizedReferencedPmIds = summarizeList([...referencedPmIds].sort((left, right) => left.localeCompare(right)), diagnosticLimit);
|
|
1785
|
+
const summarizedUntrustedRows = summarizeList(untrustedRows, diagnosticLimit);
|
|
1817
1786
|
return {
|
|
1818
1787
|
check: {
|
|
1819
1788
|
name: "command_references",
|
|
@@ -1826,6 +1795,10 @@ function buildCommandReferencesCheck(items, idPrefix, verboseDiagnostics) {
|
|
|
1826
1795
|
unique_referenced_pm_ids_count: referencedPmIds.size,
|
|
1827
1796
|
unique_referenced_pm_ids: summarizedReferencedPmIds.values,
|
|
1828
1797
|
unique_referenced_pm_ids_truncated: summarizedReferencedPmIds.truncated,
|
|
1798
|
+
linked_test_trust_current_source_ref: currentSourceRef ?? null,
|
|
1799
|
+
untrusted_linked_tests_count: untrustedRows.length,
|
|
1800
|
+
untrusted_linked_tests: summarizedUntrustedRows.values,
|
|
1801
|
+
untrusted_linked_tests_truncated: summarizedUntrustedRows.truncated,
|
|
1829
1802
|
stale_pm_id_references_count: uniqueStaleReferenceRows.length,
|
|
1830
1803
|
stale_pm_ids_count: stalePmIds.length,
|
|
1831
1804
|
stale_pm_ids: summarizedStalePmIds.values,
|
|
@@ -2145,7 +2118,7 @@ async function executeRequestedValidateChecks(params) {
|
|
|
2145
2118
|
recordValidateCheck(state, built, fixHintsEnabled);
|
|
2146
2119
|
}
|
|
2147
2120
|
if (params.requestedChecks.has("command_references")) {
|
|
2148
|
-
recordValidateCheck(state, buildCommandReferencesCheck(params.items, params.settings.id_prefix, fullDiagnostics), fixHintsEnabled);
|
|
2121
|
+
recordValidateCheck(state, await buildCommandReferencesCheck(params.items, params.settings.id_prefix, fullDiagnostics, params.pmRoot), fixHintsEnabled);
|
|
2149
2122
|
}
|
|
2150
2123
|
if (params.requestedChecks.has("history_drift")) {
|
|
2151
2124
|
recordValidateCheck(state, await buildValidateHistoryDriftCheck(params.pmRoot, params.items, fullDiagnostics), fixHintsEnabled);
|
|
@@ -2307,4 +2280,4 @@ export async function runValidate(options, global, services = {}) {
|
|
|
2307
2280
|
}
|
|
2308
2281
|
/* c8 ignore stop */
|
|
2309
2282
|
//# sourceMappingURL=validate.js.map
|
|
2310
|
-
//# debugId=
|
|
2283
|
+
//# debugId=aca69fd7-c47a-5424-b58f-f305f690edd9
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import type { Dependency, ItemStatus } from "../../types/index.js";
|
|
13
13
|
import { RelationshipGraph, type RelationshipKindRegistry } from "../relationships.js";
|
|
14
|
+
import { type HierarchyIntegrityAnalysis } from "./hierarchy-integrity.js";
|
|
15
|
+
export { resolveWorkspaceRelationshipKindRegistry } from "./workspace-relationship-kind-registry.js";
|
|
14
16
|
/** Minimal item shape inspected by dependency-reference governance. */
|
|
15
17
|
export interface DependencyReferenceHolder {
|
|
16
18
|
/** Stable item identifier. */
|
|
@@ -61,7 +63,6 @@ export declare function normalizeDependencyGraphTarget(target: unknown): string
|
|
|
61
63
|
* share this resolution so custom ordering semantics apply identically on
|
|
62
64
|
* every surface.
|
|
63
65
|
*/
|
|
64
|
-
export declare function resolveWorkspaceRelationshipKindRegistry(): RelationshipKindRegistry;
|
|
65
66
|
/** One raw stored dependency row duplicated verbatim on a single holder. */
|
|
66
67
|
export interface DuplicateDependencyRow {
|
|
67
68
|
/** Item that stores the duplicated row. */
|
|
@@ -152,7 +153,11 @@ export interface WorkspaceRelationshipAssembly {
|
|
|
152
153
|
orderingContradictions?: OrderingStorageContradiction[];
|
|
153
154
|
/** Raw legacy alias rows grouped by accepted spelling before graph canonicalization. */
|
|
154
155
|
legacyAliasCounts: Record<string, number>;
|
|
156
|
+
/** Shared hierarchy analysis retained across ordinary object spread copies. */
|
|
157
|
+
hierarchyIntegrity?: HierarchyIntegrityAnalysis;
|
|
155
158
|
}
|
|
159
|
+
/** Read the hierarchy analysis paired with an assembly created by this module. */
|
|
160
|
+
export declare function getWorkspaceHierarchyIntegrity(assembly: WorkspaceRelationshipAssembly): HierarchyIntegrityAnalysis | undefined;
|
|
156
161
|
/** Count raw dependency rows that use a registered compatibility alias instead of its canonical kind. */
|
|
157
162
|
export declare function collectLegacyDependencyAliasCounts(items: readonly DependencyReferenceHolder[], registry?: RelationshipKindRegistry): Record<string, number>;
|
|
158
163
|
/**
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
3
|
-
import {
|
|
4
|
-
import { RelationshipGraph, createRelationshipKindRegistry, } from "../relationships.js";
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="9a6974b3-63d7-5dc7-8329-0f15f0dc83ec")}catch(e){}}();
|
|
3
|
+
import { RelationshipGraph, } from "../relationships.js";
|
|
5
4
|
import { isExternalDependencySourceKind } from "../dependency-provenance.js";
|
|
5
|
+
import { analyzeHierarchyIntegrity, } from "./hierarchy-integrity.js";
|
|
6
|
+
import { resolveWorkspaceRelationshipKindRegistry } from "./workspace-relationship-kind-registry.js";
|
|
7
|
+
export { resolveWorkspaceRelationshipKindRegistry } from "./workspace-relationship-kind-registry.js";
|
|
6
8
|
/** Normalize a decoded reference target and reject empty legacy placeholders. */
|
|
7
9
|
function normalizeDependencyReferenceTarget(target) {
|
|
8
10
|
if (typeof target !== "string")
|
|
@@ -20,23 +22,6 @@ export function normalizeDependencyGraphTarget(target) {
|
|
|
20
22
|
? undefined
|
|
21
23
|
: normalized;
|
|
22
24
|
}
|
|
23
|
-
/**
|
|
24
|
-
* Resolve the default workspace relationship-kind registry: the built-in kinds
|
|
25
|
-
* merged with every active extension-contributed relationship-kind
|
|
26
|
-
* registration. Assembly, mutation advisories, and standalone graph consumers
|
|
27
|
-
* share this resolution so custom ordering semantics apply identically on
|
|
28
|
-
* every surface.
|
|
29
|
-
*/
|
|
30
|
-
export function resolveWorkspaceRelationshipKindRegistry() {
|
|
31
|
-
const registry = createRelationshipKindRegistry();
|
|
32
|
-
for (const registration of getActiveExtensionRegistrations()
|
|
33
|
-
?.relationship_kinds ?? []) {
|
|
34
|
-
for (const definition of registration.definitions) {
|
|
35
|
-
registry.register(definition);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
return registry;
|
|
39
|
-
}
|
|
40
25
|
/** Compare text case-insensitively with an exact-spelling deterministic tie-breaker. */
|
|
41
26
|
function compareCaseFoldedText(left, right) {
|
|
42
27
|
const normalizedLeft = left.toLowerCase();
|
|
@@ -242,6 +227,11 @@ export function collectExternalDependencyTargetIds(items) {
|
|
|
242
227
|
}
|
|
243
228
|
return [...targets.values()].sort((left, right) => left.localeCompare(right));
|
|
244
229
|
}
|
|
230
|
+
const hierarchyIntegrityByAssembly = new WeakMap();
|
|
231
|
+
/** Read the hierarchy analysis paired with an assembly created by this module. */
|
|
232
|
+
export function getWorkspaceHierarchyIntegrity(assembly) {
|
|
233
|
+
return assembly.hierarchyIntegrity ?? hierarchyIntegrityByAssembly.get(assembly);
|
|
234
|
+
}
|
|
245
235
|
/** Count raw dependency rows that use a registered compatibility alias instead of its canonical kind. */
|
|
246
236
|
export function collectLegacyDependencyAliasCounts(items, registry = resolveWorkspaceRelationshipKindRegistry()) {
|
|
247
237
|
const counts = {};
|
|
@@ -316,7 +306,8 @@ export function assembleWorkspaceRelationshipGraph(items, isTerminal, registry)
|
|
|
316
306
|
dependencies,
|
|
317
307
|
};
|
|
318
308
|
});
|
|
319
|
-
|
|
309
|
+
const hierarchyIntegrity = analyzeHierarchyIntegrity(safeItems, isTerminal, relationshipRegistry);
|
|
310
|
+
const assembly = {
|
|
320
311
|
graph: RelationshipGraph.fromItems([
|
|
321
312
|
...graphItems,
|
|
322
313
|
...missingIds.map((id) => ({ id })),
|
|
@@ -349,7 +340,10 @@ export function assembleWorkspaceRelationshipGraph(items, isTerminal, registry)
|
|
|
349
340
|
duplicateRows: collectDuplicateDependencyRows(safeItems, isTerminal),
|
|
350
341
|
orderingContradictions: collectOrderingStorageContradictions(safeItems, isTerminal, relationshipRegistry),
|
|
351
342
|
legacyAliasCounts: collectLegacyDependencyAliasCounts(safeItems, relationshipRegistry),
|
|
343
|
+
hierarchyIntegrity,
|
|
352
344
|
};
|
|
345
|
+
hierarchyIntegrityByAssembly.set(assembly, hierarchyIntegrity);
|
|
346
|
+
return assembly;
|
|
353
347
|
}
|
|
354
348
|
//# sourceMappingURL=assembly.js.map
|
|
355
|
-
//# debugId=
|
|
349
|
+
//# debugId=9a6974b3-63d7-5dc7-8329-0f15f0dc83ec
|
|
@@ -7,6 +7,6 @@
|
|
|
7
7
|
* drifting when a finding family is added.
|
|
8
8
|
*/
|
|
9
9
|
/** Stable machine-readable relationship audit finding families. */
|
|
10
|
-
export declare const RELATIONSHIP_AUDIT_FINDING_CODES: readonly ["missing_reference_active", "missing_reference_terminal", "legacy_no_blocker_sentinel", "ordering_cycle", "legacy_ordering_cycle", "ordering_storage_contradiction", "legacy_ordering_storage_contradiction", "duplicate_edge", "legacy_duplicate_edge", "duplicate_dependency_row", "legacy_duplicate_dependency_row", "stale_lifecycle_block", "isolated_active_node", "sparse_active_node"];
|
|
10
|
+
export declare const RELATIONSHIP_AUDIT_FINDING_CODES: readonly ["missing_reference_active", "missing_reference_terminal", "legacy_no_blocker_sentinel", "ordering_cycle", "legacy_ordering_cycle", "ordering_storage_contradiction", "legacy_ordering_storage_contradiction", "hierarchy_cycle", "legacy_hierarchy_cycle", "hierarchy_cardinality_violation", "legacy_hierarchy_cardinality_violation", "hierarchy_direction_violation", "legacy_hierarchy_direction_violation", "duplicate_edge", "legacy_duplicate_edge", "duplicate_dependency_row", "legacy_duplicate_dependency_row", "stale_lifecycle_block", "isolated_active_node", "sparse_active_node"];
|
|
11
11
|
/** Machine-readable identifier for one relationship audit finding family. */
|
|
12
12
|
export type RelationshipAuditFindingCode = (typeof RELATIONSHIP_AUDIT_FINDING_CODES)[number];
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
/** Stable machine-readable relationship audit finding families. */
|
|
10
10
|
|
|
11
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
11
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="a8bc0ebd-e5e4-57f3-8e6c-417f9809eb72")}catch(e){}}();
|
|
12
12
|
export const RELATIONSHIP_AUDIT_FINDING_CODES = [
|
|
13
13
|
"missing_reference_active",
|
|
14
14
|
"missing_reference_terminal",
|
|
@@ -17,6 +17,12 @@ export const RELATIONSHIP_AUDIT_FINDING_CODES = [
|
|
|
17
17
|
"legacy_ordering_cycle",
|
|
18
18
|
"ordering_storage_contradiction",
|
|
19
19
|
"legacy_ordering_storage_contradiction",
|
|
20
|
+
"hierarchy_cycle",
|
|
21
|
+
"legacy_hierarchy_cycle",
|
|
22
|
+
"hierarchy_cardinality_violation",
|
|
23
|
+
"legacy_hierarchy_cardinality_violation",
|
|
24
|
+
"hierarchy_direction_violation",
|
|
25
|
+
"legacy_hierarchy_direction_violation",
|
|
20
26
|
"duplicate_edge",
|
|
21
27
|
"legacy_duplicate_edge",
|
|
22
28
|
"duplicate_dependency_row",
|
|
@@ -26,4 +32,4 @@ export const RELATIONSHIP_AUDIT_FINDING_CODES = [
|
|
|
26
32
|
"sparse_active_node",
|
|
27
33
|
];
|
|
28
34
|
//# sourceMappingURL=governance-contracts.js.map
|
|
29
|
-
//# debugId=
|
|
35
|
+
//# debugId=a8bc0ebd-e5e4-57f3-8e6c-417f9809eb72
|