@unbrained/pm-cli 2026.8.20 → 2026.8.22
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 +38 -0
- package/dist/cli/error-guidance.d.ts +29 -4
- package/dist/cli/error-guidance.js +43 -43
- package/dist/cli/main.d.ts +3 -0
- package/dist/cli/main.js +40 -7
- package/dist/cli-bundle/bundle-manifest.json +150 -150
- package/dist/cli-bundle/chunks/{chunk-L4LGPBJU.js → chunk-BWX4SK46.js} +58 -58
- package/dist/cli-bundle/chunks/chunk-EMWDM5FI.js +13 -0
- package/dist/cli-bundle/chunks/chunk-HYG6LRL4.js +200 -0
- package/dist/cli-bundle/chunks/{chunk-TSMWLCHS.js → chunk-MYYI3ZOA.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-4GB2LTOQ.js → chunk-MZTZQTMS.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-7BRF2BX2.js → chunk-QI2CT3IX.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-SMWXHMVB.js +35 -0
- package/dist/cli-bundle/chunks/{chunk-US2ZQW2M.js → chunk-U6GQ4VMV.js} +47 -47
- package/dist/cli-bundle/chunks/{chunk-K3EN3LK2.js → chunk-VMMIL3SI.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-WG4STC36.js +5 -0
- package/dist/cli-bundle/chunks/{chunk-XYDDWBZN.js → chunk-YBPJAOQQ.js} +3 -3
- package/dist/cli-bundle/chunks/{register-list-query-JSAQCVOC.js → register-list-query-HZKBQNWW.js} +2 -2
- package/dist/cli-bundle/chunks/{register-mutation-XJPAW63D.js → register-mutation-HTKEEGRD.js} +2 -2
- package/dist/cli-bundle/chunks/{register-operations-DSOSB2XQ.js → register-operations-6EMRWJJY.js} +2 -2
- package/dist/cli-bundle/chunks/{register-setup-QUR6QL2C.js → register-setup-QK4OTOXN.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-TDR5OGG3.js → chunk-2ARQGDDW.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-AZBACT5T.js → chunk-2AZT5WXZ.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-M32HFIQB.js → chunk-2POVTNLY.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-3LKU6PTJ.js +4 -0
- package/dist/cli-bundle/focused-chunks/chunk-5SHSBMX2.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-UGSS4F5X.js → chunk-6S2MO3Q3.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-5L7M4AI7.js → chunk-A2BC5PQL.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-C4S4A4JJ.js +5 -0
- package/dist/cli-bundle/focused-chunks/{chunk-OM65KHGF.js → chunk-D6RBZEJJ.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-E2EWYPY6.js +16 -0
- package/dist/cli-bundle/focused-chunks/{chunk-G3C5ERXG.js → chunk-GMYPD4FX.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-L5LDSRO2.js +155 -0
- package/dist/cli-bundle/focused-chunks/{chunk-BLJCGGID.js → chunk-MB37AYN6.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-MCOR37QK.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-FUREQLQN.js → chunk-PH2OXXVZ.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-325UXIO7.js → chunk-PMGJUFNQ.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-R6SR45XU.js +50 -0
- package/dist/cli-bundle/focused-chunks/chunk-WZE4LIPW.js +8 -0
- 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 +32 -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/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/item/id.js +20 -5
- package/dist/core/item/item-format.js +27 -2
- package/dist/core/shared/errors.d.ts +20 -0
- package/dist/core/shared/errors.js +2 -2
- package/dist/core/store/item-store.js +6 -91
- package/dist/core/store/tracker-preflight.d.ts +7 -0
- package/dist/core/store/tracker-preflight.js +142 -0
- package/dist/sdk/agent/refusal-reachability.d.ts +1 -1
- package/dist/sdk/agent/tracker-preflight-contracts.d.ts +63 -0
- package/dist/sdk/agent/tracker-preflight-contracts.js +127 -0
- package/dist/sdk/annotations.js +6 -7
- 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 +324 -2
- package/dist/sdk/cli-contracts/commander-mutation-options.js +3 -3
- 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.d.ts +1 -1
- package/dist/sdk/cli-contracts.js +3 -3
- package/dist/sdk/config.d.ts +5 -0
- package/dist/sdk/config.js +5 -5
- package/dist/sdk/context-intent-runtime.js +7 -4
- package/dist/sdk/contracts.d.ts +1 -0
- package/dist/sdk/contracts.js +3 -2
- package/dist/sdk/dependencies.js +5 -7
- package/dist/sdk/dependency-flag-validation.d.ts +1 -1
- package/dist/sdk/dependency-flag-validation.js +10 -4
- package/dist/sdk/environment/tracker-preflight.d.ts +6 -0
- package/dist/sdk/environment/tracker-preflight.js +10 -0
- package/dist/sdk/eval.js +5 -7
- 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/files.js +6 -10
- package/dist/sdk/generated/generated-error-code-catalog-part-1.js +75 -15
- package/dist/sdk/generated/generated-error-code-catalog-part-2.js +81 -8
- package/dist/sdk/governance/assurance.d.ts +36 -2
- package/dist/sdk/governance/assurance.js +184 -10
- package/dist/sdk/governance/gc.js +5 -6
- package/dist/sdk/governance/health.d.ts +3 -0
- package/dist/sdk/governance/health.js +42 -11
- package/dist/sdk/governance/hierarchy-validation.d.ts +29 -0
- package/dist/sdk/governance/hierarchy-validation.js +109 -0
- package/dist/sdk/governance/reindex.js +5 -6
- package/dist/sdk/governance/validate.d.ts +2 -9
- package/dist/sdk/governance/validate.js +51 -109
- 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.d.ts +7 -5
- package/dist/sdk/graph/governance.js +76 -16
- 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 +83 -36
- package/dist/sdk/graph/traversal.js +14 -5
- 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/history-compact.js +6 -9
- package/dist/sdk/history-read.js +5 -7
- package/dist/sdk/history-redact.js +5 -6
- package/dist/sdk/history-repair.js +6 -9
- package/dist/sdk/improvement-ledger.js +7 -10
- package/dist/sdk/index.d.ts +4 -2
- package/dist/sdk/index.js +5 -3
- package/dist/sdk/item-children.js +21 -8
- package/dist/sdk/lifecycle/append.d.ts +0 -5
- package/dist/sdk/lifecycle/append.js +5 -6
- package/dist/sdk/lifecycle/claim.d.ts +0 -5
- package/dist/sdk/lifecycle/claim.js +6 -9
- package/dist/sdk/lifecycle/close-many.d.ts +0 -5
- package/dist/sdk/lifecycle/close-many.js +5 -6
- package/dist/sdk/lifecycle/close.d.ts +0 -5
- package/dist/sdk/lifecycle/close.js +5 -6
- package/dist/sdk/lifecycle/copy.d.ts +0 -5
- package/dist/sdk/lifecycle/copy.js +5 -6
- package/dist/sdk/lifecycle/create.d.ts +0 -5
- package/dist/sdk/lifecycle/create.js +127 -102
- package/dist/sdk/lifecycle/delete.js +5 -6
- package/dist/sdk/lifecycle/focus.d.ts +0 -5
- package/dist/sdk/lifecycle/focus.js +5 -6
- 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/plan.d.ts +0 -5
- package/dist/sdk/lifecycle/plan.js +5 -6
- package/dist/sdk/lifecycle/restore.js +5 -6
- package/dist/sdk/lifecycle/update-many.d.ts +0 -5
- package/dist/sdk/lifecycle/update-many.js +5 -6
- package/dist/sdk/lifecycle/update.d.ts +2 -5
- package/dist/sdk/lifecycle/update.js +211 -84
- package/dist/sdk/linked-artifacts.js +6 -7
- package/dist/sdk/merge/install.js +5 -6
- package/dist/sdk/mutation-events.js +5 -7
- package/dist/sdk/package-import-adapters.js +6 -8
- package/dist/sdk/profile.js +6 -7
- package/dist/sdk/query/activity.js +5 -6
- package/dist/sdk/query/calendar.js +5 -6
- package/dist/sdk/query/complete-list.d.ts +3 -3
- package/dist/sdk/query/complete-list.js +46 -3
- package/dist/sdk/query/get.d.ts +0 -5
- package/dist/sdk/query/get.js +5 -6
- package/dist/sdk/query/history.d.ts +0 -5
- package/dist/sdk/query/history.js +5 -6
- package/dist/sdk/query/list.d.ts +4 -3
- package/dist/sdk/query/list.js +92 -54
- package/dist/sdk/query/search.js +5 -7
- package/dist/sdk/read-output-contracts.d.ts +5 -1
- package/dist/sdk/read-output-contracts.js +87 -22
- package/dist/sdk/relationship-context.js +3 -4
- package/dist/sdk/relationship-kinds/contract.d.ts +8 -0
- package/dist/sdk/relationship-kinds/contract.js +13 -13
- package/dist/sdk/relationships.d.ts +1 -1
- package/dist/sdk/relationships.js +27 -2
- package/dist/sdk/runtime.d.ts +3 -2
- package/dist/sdk/runtime.js +3 -2
- package/dist/sdk/schema-migration.js +6 -7
- package/dist/sdk/schema.js +5 -6
- package/dist/sdk/stats.js +5 -8
- package/dist/sdk/templates.js +5 -6
- package/dist/sdk/test/batch.js +15 -9
- package/dist/sdk/test/execution.js +12 -5
- package/dist/sdk/test/runs.js +5 -7
- package/dist/types.d.ts +11 -0
- package/dist/types.js +2 -2
- package/docs/AGENT_UX_CONTRACTS.md +24 -1
- package/docs/COMMANDS.md +29 -14
- package/docs/DEPENDENCY_KIND_CONTRACT.md +40 -1
- package/docs/DIAGNOSTIC_OUTPUT_CONTRACTS.md +98 -0
- package/docs/EXTENSIONS.md +2 -1
- package/docs/README.md +1 -0
- package/docs/READ_OUTPUT_CONTRACTS.md +9 -2
- package/docs/RECURRENCE_RECOVERY_CONTRACTS.md +37 -0
- package/docs/RELATIONSHIP_GRAPH.md +97 -12
- package/docs/SDK_PRIMITIVE_INVENTORY.md +15 -1
- package/docs/TESTING.md +15 -0
- package/marketplace.json +2 -2
- package/package.json +1 -1
- 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 +848 -56
- package/dist/cli-bundle/chunks/chunk-2RDVGYWE.js +0 -13
- package/dist/cli-bundle/chunks/chunk-BLVQHFSY.js +0 -35
- package/dist/cli-bundle/chunks/chunk-CU25EQVV.js +0 -5
- package/dist/cli-bundle/chunks/chunk-RS3IV2R6.js +0 -197
- package/dist/cli-bundle/focused-chunks/chunk-6AHG2YW7.js +0 -50
- package/dist/cli-bundle/focused-chunks/chunk-GXWQBE3T.js +0 -8
- package/dist/cli-bundle/focused-chunks/chunk-JWKEDZJI.js +0 -4
- package/dist/cli-bundle/focused-chunks/chunk-KYZ6JGPL.js +0 -155
- package/dist/cli-bundle/focused-chunks/chunk-Q47EX4DI.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-RIHDMCUM.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-T4ZFY7ZJ.js +0 -16
- package/dist/cli-bundle/focused-chunks/chunk-Z3PLWSGM.js +0 -2
|
@@ -7,14 +7,14 @@ 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";
|
|
13
14
|
type ValidateFileScanMode = "default" | "tracked-all" | "tracked-all-strict";
|
|
14
15
|
type ItemWithBody = ValidateItem;
|
|
15
16
|
type OrphanedPathClassification = "docs_unowned" | "tests_unowned" | "source_unowned" | "unlinked_existing";
|
|
16
|
-
|
|
17
|
-
type LifecyclePatternFieldKey = (typeof LIFECYCLE_PATTERN_FIELD_KEYS)[number];
|
|
17
|
+
type LifecyclePatternFieldKey = "blocked_reason" | "resolution" | "actual_result";
|
|
18
18
|
/** Documents the validate command options payload exchanged by command, SDK, and package integrations. */
|
|
19
19
|
export interface ValidateCommandOptions {
|
|
20
20
|
/** Value that configures or reports check metadata for this contract. */
|
|
@@ -199,18 +199,11 @@ declare function buildDependencyReferencesCheck(items: ItemWithBody[], verboseDi
|
|
|
199
199
|
};
|
|
200
200
|
declare function extractItemIds(value: string, idPrefix?: string): string[];
|
|
201
201
|
declare function findLifecycleDependencyCycleComponents(graph: Map<string, string[]>): string[][];
|
|
202
|
-
declare function resolveLifecycleDependencyCycleSamplePath(component: string[], graph: Map<string, string[]>): string[];
|
|
203
202
|
declare function detectLifecycleDependencyCycles(activeItems: ItemWithBody[], idPrefix?: string): {
|
|
204
203
|
cycle_count: number;
|
|
205
204
|
cycle_item_ids: string[];
|
|
206
205
|
cycle_sample_paths: string[];
|
|
207
206
|
};
|
|
208
|
-
declare function buildLifecycleParentGraph(items: ItemWithBody[], relationshipRegistry?: RelationshipKindRegistry): Map<string, string[]>;
|
|
209
|
-
declare function detectLifecycleParentCycles(items: ItemWithBody[]): {
|
|
210
|
-
cycle_count: number;
|
|
211
|
-
cycle_item_ids: string[];
|
|
212
|
-
cycle_sample_paths: string[];
|
|
213
|
-
};
|
|
214
207
|
interface OrphanedPathRow {
|
|
215
208
|
path: string;
|
|
216
209
|
classification: OrphanedPathClassification;
|
|
@@ -4,7 +4,8 @@
|
|
|
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]="87e3932d-26f0-5d44-ac35-5d0d7d1f4467")}catch(e){}}();
|
|
8
|
+
import { assertInitializedTracker } from "../environment/tracker-preflight.js";
|
|
8
9
|
import fs from "node:fs/promises";
|
|
9
10
|
import { realpathSync } from "node:fs";
|
|
10
11
|
import { execFile } from "node:child_process";
|
|
@@ -13,7 +14,7 @@ import { promisify } from "node:util";
|
|
|
13
14
|
import { buildRemediationCommands } from "../../core/diagnostics/remediation.js";
|
|
14
15
|
import { getActiveExtensionRegistrations } from "../../core/extensions/index.js";
|
|
15
16
|
import { collectRegisteredItemFieldNames } from "../../core/extensions/item-fields.js";
|
|
16
|
-
import { isFileMissingError
|
|
17
|
+
import { isFileMissingError } from "../../core/fs/fs-utils.js";
|
|
17
18
|
import { normalizeStatusInput } from "../../core/item/status.js";
|
|
18
19
|
import { resolveItemTypeRegistry } from "../../core/item/type-registry.js";
|
|
19
20
|
import { resolveRuntimeStatusRegistry, } from "../../core/schema/runtime-schema.js";
|
|
@@ -22,7 +23,7 @@ import { PmCliError } from "../../core/shared/errors.js";
|
|
|
22
23
|
import { toNonEmptyStringOrUndefined } from "../../core/shared/primitives.js";
|
|
23
24
|
import { nowIso } from "../../core/shared/time.js";
|
|
24
25
|
import { CURRENT_ITEM_FORMAT_VERSION, effectiveItemFormatVersion, scanItemFormatVersions, } from "../../core/item/item-format-version.js";
|
|
25
|
-
import {
|
|
26
|
+
import { resolvePmRoot } from "../../core/store/paths.js";
|
|
26
27
|
import { readSettings } from "../../core/store/settings.js";
|
|
27
28
|
import { partitionFixesByGrant, planCloseReasonBackfillFixes, planEstimateBackfillFixes, planResolutionBackfillFixes, planStaleLinkPruneFixes, planTerminalParentFixes, resolveGrantedFixScopes, toFixOutputRow, } from "../../core/validate/fix-planning.js";
|
|
28
29
|
import { findDuplicateIssueCodes, } from "../../core/governance/issue-codes.js";
|
|
@@ -43,6 +44,7 @@ import { runFiles } from "../files.js";
|
|
|
43
44
|
import { extractReferencedPmItemIdsFromCommand } from "../test/linked-command-detection.js";
|
|
44
45
|
import { readValidateItems, } from "./validate-item-reader.js";
|
|
45
46
|
import { buildValidateHistoryDriftCheck } from "./validate-history-drift.js";
|
|
47
|
+
import { buildLifecycleParentGraph, detectLifecycleParentCycles, resolveLifecycleDependencyCycleSamplePath, } from "./hierarchy-validation.js";
|
|
46
48
|
const FILE_SCAN_DIRECTORIES = ["src", "tests", "docs"];
|
|
47
49
|
const FILE_SCAN_ROOT_FILES = [
|
|
48
50
|
"README.md",
|
|
@@ -78,11 +80,6 @@ const VALIDATE_DEPENDENCY_CYCLE_SEVERITY_VALUES = [
|
|
|
78
80
|
"warn",
|
|
79
81
|
"error",
|
|
80
82
|
];
|
|
81
|
-
const LIFECYCLE_PATTERN_FIELD_KEYS = [
|
|
82
|
-
"blocked_reason",
|
|
83
|
-
"resolution",
|
|
84
|
-
"actual_result",
|
|
85
|
-
];
|
|
86
83
|
const CORE_METADATA_REQUIRED_FIELDS = [
|
|
87
84
|
"author",
|
|
88
85
|
"acceptance_criteria",
|
|
@@ -1111,41 +1108,6 @@ function findLifecycleDependencyCycleComponents(graph) {
|
|
|
1111
1108
|
left.join(",").localeCompare(right.join(",")));
|
|
1112
1109
|
}
|
|
1113
1110
|
/* c8 ignore stop */
|
|
1114
|
-
/* c8 ignore start -- cycle sample-path fallback branches are covered by lifecycle graph integration tests */
|
|
1115
|
-
function resolveLifecycleDependencyCycleSamplePath(component, graph) {
|
|
1116
|
-
const start = component[0];
|
|
1117
|
-
if (component.length === 1) {
|
|
1118
|
-
return [start, start];
|
|
1119
|
-
}
|
|
1120
|
-
const componentSet = new Set(component);
|
|
1121
|
-
const path = [start];
|
|
1122
|
-
const visited = new Set([start]);
|
|
1123
|
-
const search = (current) => {
|
|
1124
|
-
const neighbors = (graph.get(current) ?? []).filter((candidate) => componentSet.has(candidate));
|
|
1125
|
-
for (const next of neighbors) {
|
|
1126
|
-
if (next === start && path.length > 1) {
|
|
1127
|
-
path.push(start);
|
|
1128
|
-
return true;
|
|
1129
|
-
}
|
|
1130
|
-
if (visited.has(next)) {
|
|
1131
|
-
continue;
|
|
1132
|
-
}
|
|
1133
|
-
visited.add(next);
|
|
1134
|
-
path.push(next);
|
|
1135
|
-
if (search(next)) {
|
|
1136
|
-
return true;
|
|
1137
|
-
}
|
|
1138
|
-
path.pop();
|
|
1139
|
-
visited.delete(next);
|
|
1140
|
-
}
|
|
1141
|
-
return false;
|
|
1142
|
-
};
|
|
1143
|
-
if (search(start)) {
|
|
1144
|
-
return [...path];
|
|
1145
|
-
}
|
|
1146
|
-
return [...component, start];
|
|
1147
|
-
}
|
|
1148
|
-
/* c8 ignore stop */
|
|
1149
1111
|
function detectLifecycleDependencyCycles(activeItems, idPrefix = "pm") {
|
|
1150
1112
|
const graph = buildLifecycleDependencyGraph(activeItems, idPrefix);
|
|
1151
1113
|
const cycleComponents = findLifecycleDependencyCycleComponents(graph);
|
|
@@ -1157,62 +1119,6 @@ function detectLifecycleDependencyCycles(activeItems, idPrefix = "pm") {
|
|
|
1157
1119
|
cycle_sample_paths: cycleSamplePaths,
|
|
1158
1120
|
};
|
|
1159
1121
|
}
|
|
1160
|
-
// Parent (composition) cycle detection (pm-8vul / GH-280). The dependency-cycle
|
|
1161
|
-
// path above only walks blocked_by/definition_of_ready edges across ACTIVE items;
|
|
1162
|
-
// it never traverses item.parent, so a parent cycle (A.parent=B, B.parent=A, or
|
|
1163
|
-
// any longer ring) goes undetected while it silently breaks `pm list --tree`,
|
|
1164
|
-
// orphan detection, and progress rollups. We reuse the generic Tarjan SCC helper
|
|
1165
|
-
// on a child->[parent] adjacency map. Unlike dependency cycles we scan ALL items
|
|
1166
|
-
// (not just active ones) because a parent cycle among closed items is still
|
|
1167
|
-
// structural corruption of the hierarchy.
|
|
1168
|
-
function buildLifecycleParentGraph(items, relationshipRegistry = createRelationshipKindRegistry()) {
|
|
1169
|
-
// PR #279 made parent matching case-insensitive (e.g. `parent: PM-FK49`
|
|
1170
|
-
// resolves to `id: pm-fk49`). Resolve parent references to their canonical
|
|
1171
|
-
// item id the same way so a casing mismatch can never silently drop a cycle
|
|
1172
|
-
// edge and hide a parent cycle (false negative).
|
|
1173
|
-
const canonicalIdByLowercase = new Map(items.map((item) => [item.id.toLowerCase(), item.id]));
|
|
1174
|
-
const graph = new Map(items.map((item) => [item.id, []]));
|
|
1175
|
-
const sortedItems = [...items].sort((left, right) => left.id.localeCompare(right.id));
|
|
1176
|
-
sortedItems.forEach((item) => {
|
|
1177
|
-
const edges = graph.get(item.id);
|
|
1178
|
-
const parentId = toMeaningfulString(item.parent);
|
|
1179
|
-
const canonicalParentId = parentId
|
|
1180
|
-
? canonicalIdByLowercase.get(parentId.toLowerCase())
|
|
1181
|
-
: undefined;
|
|
1182
|
-
if (canonicalParentId) {
|
|
1183
|
-
edges.push(canonicalParentId);
|
|
1184
|
-
}
|
|
1185
|
-
(item.dependencies ?? []).forEach((dependency) => {
|
|
1186
|
-
const definition = relationshipRegistry.resolve(dependency.kind);
|
|
1187
|
-
if (!definition?.hierarchy)
|
|
1188
|
-
return;
|
|
1189
|
-
const target = canonicalIdByLowercase.get(dependency.id.toLowerCase());
|
|
1190
|
-
if (!target)
|
|
1191
|
-
return;
|
|
1192
|
-
if (definition.hierarchyDirection === "source_parent") {
|
|
1193
|
-
graph.get(target).push(item.id);
|
|
1194
|
-
}
|
|
1195
|
-
else {
|
|
1196
|
-
edges.push(target);
|
|
1197
|
-
}
|
|
1198
|
-
});
|
|
1199
|
-
});
|
|
1200
|
-
graph.forEach((edges, id) => {
|
|
1201
|
-
graph.set(id, [...new Set(edges)].sort((left, right) => left.localeCompare(right)));
|
|
1202
|
-
});
|
|
1203
|
-
return graph;
|
|
1204
|
-
}
|
|
1205
|
-
function detectLifecycleParentCycles(items) {
|
|
1206
|
-
const graph = buildLifecycleParentGraph(items);
|
|
1207
|
-
const cycleComponents = findLifecycleDependencyCycleComponents(graph);
|
|
1208
|
-
const cycleItemIds = [...new Set(cycleComponents.flat())].sort((left, right) => left.localeCompare(right));
|
|
1209
|
-
const cycleSamplePaths = cycleComponents.map((component) => resolveLifecycleDependencyCycleSamplePath(component, graph).join("->"));
|
|
1210
|
-
return {
|
|
1211
|
-
cycle_count: cycleComponents.length,
|
|
1212
|
-
cycle_item_ids: cycleItemIds,
|
|
1213
|
-
cycle_sample_paths: cycleSamplePaths,
|
|
1214
|
-
};
|
|
1215
|
-
}
|
|
1216
1122
|
function classifyOrphanedPath(pathValue) {
|
|
1217
1123
|
if (pathValue.startsWith("docs/")) {
|
|
1218
1124
|
return "docs_unowned";
|
|
@@ -1439,7 +1345,10 @@ function lifecycleCycleWarningToken(prefix, severity, count) {
|
|
|
1439
1345
|
}
|
|
1440
1346
|
return `${severity === "error" ? `${prefix}_error` : prefix}:${count}`;
|
|
1441
1347
|
}
|
|
1442
|
-
function
|
|
1348
|
+
function lifecycleAdvisoryWarningToken(prefix, severity, count) {
|
|
1349
|
+
return count > 0 && severity !== "off" ? `${prefix}:${count}` : null;
|
|
1350
|
+
}
|
|
1351
|
+
function buildLifecycleWarnings(rows, includeStaleBlockers, dependencyCycleSeverity, parentCycleSeverity, counts) {
|
|
1443
1352
|
const warnings = [];
|
|
1444
1353
|
if (rows.closureLikeRows.length > 0) {
|
|
1445
1354
|
warnings.push(`validate_lifecycle_active_closure_like_metadata:${rows.closureLikeRows.length}`);
|
|
@@ -1450,12 +1359,22 @@ function buildLifecycleWarnings(rows, includeStaleBlockers, dependencyCycleSever
|
|
|
1450
1359
|
if (includeStaleBlockers && rows.staleBlockerRows.length > 0) {
|
|
1451
1360
|
warnings.push(`validate_lifecycle_stale_blockers:${rows.staleBlockerRows.length}`);
|
|
1452
1361
|
}
|
|
1453
|
-
const dependencyWarning = lifecycleCycleWarningToken("validate_lifecycle_dependency_cycles", dependencyCycleSeverity,
|
|
1454
|
-
const parentWarning = lifecycleCycleWarningToken("validate_hierarchy_parent_cycle", parentCycleSeverity,
|
|
1362
|
+
const dependencyWarning = lifecycleCycleWarningToken("validate_lifecycle_dependency_cycles", dependencyCycleSeverity, counts.dependencyCycles);
|
|
1363
|
+
const parentWarning = lifecycleCycleWarningToken("validate_hierarchy_parent_cycle", parentCycleSeverity, counts.activeHierarchyCycles);
|
|
1364
|
+
const cardinalityWarning = lifecycleCycleWarningToken("validate_hierarchy_cardinality_violation", parentCycleSeverity, counts.activeHierarchyCardinality);
|
|
1365
|
+
const divergenceWarning = lifecycleCycleWarningToken("validate_hierarchy_parent_divergence", parentCycleSeverity, counts.activeHierarchyDivergence);
|
|
1366
|
+
const legacyCycleWarning = lifecycleAdvisoryWarningToken("validate_legacy_hierarchy_parent_cycle", parentCycleSeverity, counts.legacyHierarchyCycles);
|
|
1367
|
+
const legacyCardinalityWarning = lifecycleAdvisoryWarningToken("validate_legacy_hierarchy_cardinality_violation", parentCycleSeverity, counts.legacyHierarchyCardinality);
|
|
1368
|
+
const legacyDivergenceWarning = lifecycleAdvisoryWarningToken("validate_legacy_hierarchy_parent_divergence", parentCycleSeverity, counts.legacyHierarchyDivergence);
|
|
1455
1369
|
return [
|
|
1456
1370
|
...warnings,
|
|
1457
1371
|
...(dependencyWarning ? [dependencyWarning] : []),
|
|
1458
1372
|
...(parentWarning ? [parentWarning] : []),
|
|
1373
|
+
...(cardinalityWarning ? [cardinalityWarning] : []),
|
|
1374
|
+
...(divergenceWarning ? [divergenceWarning] : []),
|
|
1375
|
+
...(legacyCycleWarning ? [legacyCycleWarning] : []),
|
|
1376
|
+
...(legacyCardinalityWarning ? [legacyCardinalityWarning] : []),
|
|
1377
|
+
...(legacyDivergenceWarning ? [legacyDivergenceWarning] : []),
|
|
1459
1378
|
];
|
|
1460
1379
|
}
|
|
1461
1380
|
function linkedArtifactPathExceedsFilesystemLimits(artifactPath) {
|
|
@@ -1561,8 +1480,16 @@ function buildLifecycleCheck(items, includeStaleBlockers, dependencyCycleSeverit
|
|
|
1561
1480
|
const rows = collectLifecycleScanRows(items, includeStaleBlockers, statusRegistry, lifecyclePatternPolicy);
|
|
1562
1481
|
sortLifecycleScanRows(rows);
|
|
1563
1482
|
const dependencyCycleDiagnostics = detectLifecycleDependencyCycles(rows.activeItems, idPrefix);
|
|
1564
|
-
const parentCycleDiagnostics = detectLifecycleParentCycles(items);
|
|
1565
|
-
const warnings = buildLifecycleWarnings(rows, includeStaleBlockers, dependencyCycleSeverity, parentCycleSeverity,
|
|
1483
|
+
const parentCycleDiagnostics = detectLifecycleParentCycles(items, statusRegistry);
|
|
1484
|
+
const warnings = buildLifecycleWarnings(rows, includeStaleBlockers, dependencyCycleSeverity, parentCycleSeverity, {
|
|
1485
|
+
dependencyCycles: dependencyCycleDiagnostics.cycle_count,
|
|
1486
|
+
activeHierarchyCycles: parentCycleDiagnostics.active_cycle_count,
|
|
1487
|
+
activeHierarchyCardinality: parentCycleDiagnostics.active_cardinality_violation_count,
|
|
1488
|
+
activeHierarchyDivergence: parentCycleDiagnostics.active_parent_divergence_count,
|
|
1489
|
+
legacyHierarchyCycles: parentCycleDiagnostics.legacy_cycle_count,
|
|
1490
|
+
legacyHierarchyCardinality: parentCycleDiagnostics.legacy_cardinality_violation_count,
|
|
1491
|
+
legacyHierarchyDivergence: parentCycleDiagnostics.legacy_parent_divergence_count,
|
|
1492
|
+
});
|
|
1566
1493
|
const diagnosticLimit = verboseDiagnostics
|
|
1567
1494
|
? Number.POSITIVE_INFINITY
|
|
1568
1495
|
: DIAGNOSTIC_LIST_SUMMARY_LIMIT;
|
|
@@ -1573,9 +1500,14 @@ function buildLifecycleCheck(items, includeStaleBlockers, dependencyCycleSeverit
|
|
|
1573
1500
|
const summarizedDependencyCycleSamplePaths = summarizeList(dependencyCycleDiagnostics.cycle_sample_paths, diagnosticLimit);
|
|
1574
1501
|
const summarizedParentCycleItemIds = summarizeList(parentCycleDiagnostics.cycle_item_ids, diagnosticLimit);
|
|
1575
1502
|
const summarizedParentCycleSamplePaths = summarizeList(parentCycleDiagnostics.cycle_sample_paths, diagnosticLimit);
|
|
1503
|
+
const summarizedHierarchyCardinalityRows = summarizeList(parentCycleDiagnostics.cardinality_violation_rows, diagnosticLimit);
|
|
1504
|
+
const summarizedHierarchyDivergenceRows = summarizeList(parentCycleDiagnostics.parent_divergence_rows, diagnosticLimit);
|
|
1576
1505
|
const hasErrorSeverityCycle = (dependencyCycleDiagnostics.cycle_count > 0 &&
|
|
1577
1506
|
dependencyCycleSeverity === "error") ||
|
|
1578
|
-
(parentCycleDiagnostics.
|
|
1507
|
+
((parentCycleDiagnostics.active_cycle_count > 0 ||
|
|
1508
|
+
parentCycleDiagnostics.active_cardinality_violation_count > 0 ||
|
|
1509
|
+
parentCycleDiagnostics.active_parent_divergence_count > 0) &&
|
|
1510
|
+
parentCycleSeverity === "error");
|
|
1579
1511
|
return {
|
|
1580
1512
|
check: {
|
|
1581
1513
|
name: "lifecycle",
|
|
@@ -1606,11 +1538,23 @@ function buildLifecycleCheck(items, includeStaleBlockers, dependencyCycleSeverit
|
|
|
1606
1538
|
dependency_cycle_sample_paths_truncated: summarizedDependencyCycleSamplePaths.truncated,
|
|
1607
1539
|
parent_cycle_severity_policy: parentCycleSeverity,
|
|
1608
1540
|
parent_cycle_count: parentCycleDiagnostics.cycle_count,
|
|
1541
|
+
active_parent_cycle_count: parentCycleDiagnostics.active_cycle_count,
|
|
1542
|
+
legacy_parent_cycle_count: parentCycleDiagnostics.legacy_cycle_count,
|
|
1609
1543
|
parent_cycle_item_count: parentCycleDiagnostics.cycle_item_ids.length,
|
|
1610
1544
|
parent_cycle_item_ids: summarizedParentCycleItemIds.values,
|
|
1611
1545
|
parent_cycle_item_ids_truncated: summarizedParentCycleItemIds.truncated,
|
|
1612
1546
|
parent_cycle_sample_paths: summarizedParentCycleSamplePaths.values,
|
|
1613
1547
|
parent_cycle_sample_paths_truncated: summarizedParentCycleSamplePaths.truncated,
|
|
1548
|
+
hierarchy_cardinality_violation_count: parentCycleDiagnostics.cardinality_violation_count,
|
|
1549
|
+
active_hierarchy_cardinality_violation_count: parentCycleDiagnostics.active_cardinality_violation_count,
|
|
1550
|
+
legacy_hierarchy_cardinality_violation_count: parentCycleDiagnostics.legacy_cardinality_violation_count,
|
|
1551
|
+
hierarchy_cardinality_violation_rows: summarizedHierarchyCardinalityRows.values,
|
|
1552
|
+
hierarchy_cardinality_violation_rows_truncated: summarizedHierarchyCardinalityRows.truncated,
|
|
1553
|
+
hierarchy_parent_divergence_count: parentCycleDiagnostics.parent_divergence_count,
|
|
1554
|
+
active_hierarchy_parent_divergence_count: parentCycleDiagnostics.active_parent_divergence_count,
|
|
1555
|
+
legacy_hierarchy_parent_divergence_count: parentCycleDiagnostics.legacy_parent_divergence_count,
|
|
1556
|
+
hierarchy_parent_divergence_rows: summarizedHierarchyDivergenceRows.values,
|
|
1557
|
+
hierarchy_parent_divergence_rows_truncated: summarizedHierarchyDivergenceRows.truncated,
|
|
1614
1558
|
stale_blocker_reason_patterns: [
|
|
1615
1559
|
...lifecyclePatternPolicy.stale_blocker_reason_patterns,
|
|
1616
1560
|
],
|
|
@@ -2226,9 +2170,7 @@ export async function runValidate(options, global, services = {}) {
|
|
|
2226
2170
|
// Resolved up-front so unknown --fix-scope values fail fast before any scan.
|
|
2227
2171
|
const grantedFixScopes = resolveGrantedFixScopes(options.fixScope);
|
|
2228
2172
|
const pmRoot = resolvePmRoot(process.cwd(), global.path);
|
|
2229
|
-
|
|
2230
|
-
throw new PmCliError(`Tracker is not initialized at ${pmRoot}. Run pm init first.`, EXIT_CODE.NOT_FOUND);
|
|
2231
|
-
}
|
|
2173
|
+
await assertInitializedTracker(pmRoot);
|
|
2232
2174
|
const settings = await readSettings(pmRoot);
|
|
2233
2175
|
const statusRegistry = resolveRuntimeStatusRegistry(settings.schema);
|
|
2234
2176
|
const typeRegistry = resolveItemTypeRegistry(settings, getActiveExtensionRegistrations());
|
|
@@ -2313,4 +2255,4 @@ export async function runValidate(options, global, services = {}) {
|
|
|
2313
2255
|
}
|
|
2314
2256
|
/* c8 ignore stop */
|
|
2315
2257
|
//# sourceMappingURL=validate.js.map
|
|
2316
|
-
//# debugId=
|
|
2258
|
+
//# debugId=87e3932d-26f0-5d44-ac35-5d0d7d1f4467
|
|
@@ -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
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* carries a deterministic bounded sample, and proposes a safe next action so
|
|
13
13
|
* agents can plan remediation without re-deriving context.
|
|
14
14
|
*/
|
|
15
|
-
import type { RelationshipEdge } from "../relationships.js";
|
|
15
|
+
import type { RelationshipEdge, RelationshipOutcomeTraversal } from "../relationships.js";
|
|
16
16
|
import type { OrderingStorageContradiction, WorkspaceRelationshipAssembly } from "./assembly.js";
|
|
17
17
|
import { type RelationshipAuditFindingCode } from "./governance-contracts.js";
|
|
18
18
|
export type { RelationshipAuditFindingCode } from "./governance-contracts.js";
|
|
@@ -88,17 +88,19 @@ export interface RelationshipCoverageProfile {
|
|
|
88
88
|
bridge_edges: number;
|
|
89
89
|
/** Milestone nodes whose title explicitly declares an outcome. */
|
|
90
90
|
outcome_nodes: number;
|
|
91
|
-
/**
|
|
91
|
+
/** Declared edge kinds and directions admitted by typed outcome reachability. */
|
|
92
|
+
outcome_reachability_basis: Record<RelationshipOutcomeTraversal, string>;
|
|
93
|
+
/** Active non-outcome nodes with a path admitted by outcome_reachability_basis. */
|
|
92
94
|
active_outcome_reachable_nodes: number;
|
|
93
|
-
/** Active non-outcome nodes without a
|
|
95
|
+
/** Active non-outcome nodes without a path admitted by outcome_reachability_basis. */
|
|
94
96
|
active_outcome_unreachable_nodes: number;
|
|
95
97
|
/** Active typed-outcome reachability expressed as integer basis points. */
|
|
96
98
|
active_outcome_reachability_basis_points: number;
|
|
97
99
|
/** Terminal non-placeholder, non-outcome nodes included in the all-status census. */
|
|
98
100
|
terminal_nodes: number;
|
|
99
|
-
/** Terminal non-outcome nodes with a
|
|
101
|
+
/** Terminal non-outcome nodes with a path admitted by outcome_reachability_basis. */
|
|
100
102
|
terminal_outcome_reachable_nodes: number;
|
|
101
|
-
/** Terminal non-outcome nodes without a
|
|
103
|
+
/** Terminal non-outcome nodes without a path admitted by outcome_reachability_basis. */
|
|
102
104
|
terminal_outcome_unreachable_nodes: number;
|
|
103
105
|
/** Terminal typed-outcome reachability expressed as integer basis points. */
|
|
104
106
|
terminal_outcome_reachability_basis_points: number;
|
|
@@ -1,7 +1,8 @@
|
|
|
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]="
|
|
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]="27de9c62-434c-566d-a000-54eddb9a16a9")}catch(e){}}();
|
|
3
3
|
import { findRedundantRelationshipEdges, isTransitiveKind, orientTransitiveEdge, transitiveFamilyKey, } from "./analytics.js";
|
|
4
4
|
import { findRelationshipCutStructure } from "./centrality.js";
|
|
5
|
+
import { getWorkspaceHierarchyIntegrity } from "./assembly.js";
|
|
5
6
|
import { RELATIONSHIP_AUDIT_FINDING_CODES, } from "./governance-contracts.js";
|
|
6
7
|
/** Default bounded sample size applied to every finding family. */
|
|
7
8
|
const DEFAULT_MAX_SAMPLE_SIZE = 25;
|
|
@@ -53,6 +54,48 @@ function buildFinding(code, severity, policy, describe, subjects, maxSampleSize,
|
|
|
53
54
|
}),
|
|
54
55
|
};
|
|
55
56
|
}
|
|
57
|
+
function collectHierarchyCycleFindings(analysis, maxSampleSize) {
|
|
58
|
+
return analysis.cycles.map((cycle) => buildFinding(cycle.legacy_terminal ? "legacy_hierarchy_cycle" : "hierarchy_cycle", cycle.legacy_terminal ? "info" : "error", "Structural hierarchy must be acyclic across scalar and registered dependency relationships.", (count) => `${count} items participate in one hierarchy cycle.`, cycle.item_ids, maxSampleSize, cycle.legacy_terminal
|
|
59
|
+
? "Preserve historical rows unless an explicit legacy repair is approved."
|
|
60
|
+
: "Remove or retype one exact hierarchy relation before continuing mutation."));
|
|
61
|
+
}
|
|
62
|
+
function collectHierarchyCardinalityFindings(analysis, maxSampleSize, legacyTerminal) {
|
|
63
|
+
const rows = analysis.cardinality_violations.filter((finding) => finding.legacy_terminal === legacyTerminal);
|
|
64
|
+
if (rows.length === 0)
|
|
65
|
+
return [];
|
|
66
|
+
return [
|
|
67
|
+
buildFinding(legacyTerminal
|
|
68
|
+
? "legacy_hierarchy_cardinality_violation"
|
|
69
|
+
: "hierarchy_cardinality_violation", legacyTerminal ? "info" : "error", "Every structural child must resolve to at most one logical parent across all hierarchy spellings.", (count) => `${count} child item${count === 1 ? "" : "s"} resolve to multiple hierarchy parents.`, rows.map((finding) => finding.child_id), maxSampleSize, legacyTerminal
|
|
70
|
+
? "Preserve historical rows unless an explicit legacy repair is approved."
|
|
71
|
+
: "Remove the exact surplus hierarchy rows so each child has one parent.", rows.map((finding) => `${finding.child_id} <- ${finding.parent_ids.join(", ")}`)),
|
|
72
|
+
];
|
|
73
|
+
}
|
|
74
|
+
function collectHierarchyDirectionFindings(analysis, maxSampleSize, legacyTerminal) {
|
|
75
|
+
const rows = analysis.divergences.filter((finding) => finding.legacy_terminal === legacyTerminal);
|
|
76
|
+
if (rows.length === 0)
|
|
77
|
+
return [];
|
|
78
|
+
return [
|
|
79
|
+
buildFinding(legacyTerminal
|
|
80
|
+
? "legacy_hierarchy_direction_violation"
|
|
81
|
+
: "hierarchy_direction_violation", legacyTerminal ? "info" : "error", "Scalar parent metadata and dependency-backed hierarchy rows must resolve to the same logical parent.", (count) => `${count} child item${count === 1 ? "" : "s"} have divergent hierarchy storage surfaces.`, rows.map((finding) => finding.child_id), maxSampleSize, legacyTerminal
|
|
82
|
+
? "Preserve historical rows unless an explicit legacy repair is approved."
|
|
83
|
+
: "Align the scalar parent and exact dependency rows to one canonical parent.", rows.map((finding) => `${finding.child_id}: scalar=${finding.scalar_parent_id} dependencies=${finding.dependency_parent_ids.join(",")}`)),
|
|
84
|
+
];
|
|
85
|
+
}
|
|
86
|
+
/** Convert unified hierarchy analysis into lifecycle-aware governance findings. */
|
|
87
|
+
function collectHierarchyIntegrityFindings(assembly, maxSampleSize) {
|
|
88
|
+
const analysis = getWorkspaceHierarchyIntegrity(assembly);
|
|
89
|
+
if (!analysis)
|
|
90
|
+
return [];
|
|
91
|
+
return [
|
|
92
|
+
...collectHierarchyCycleFindings(analysis, maxSampleSize),
|
|
93
|
+
...collectHierarchyCardinalityFindings(analysis, maxSampleSize, false),
|
|
94
|
+
...collectHierarchyCardinalityFindings(analysis, maxSampleSize, true),
|
|
95
|
+
...collectHierarchyDirectionFindings(analysis, maxSampleSize, false),
|
|
96
|
+
...collectHierarchyDirectionFindings(analysis, maxSampleSize, true),
|
|
97
|
+
];
|
|
98
|
+
}
|
|
56
99
|
/** Resolve the ordering-only successor adjacency for cycle analysis. */
|
|
57
100
|
function buildOrderingAdjacency(assembly) {
|
|
58
101
|
const registry = assembly.graph.registry();
|
|
@@ -336,23 +379,37 @@ function collectDuplicateRowFindings(assembly, maxSampleSize) {
|
|
|
336
379
|
/** Build the reverse typed paths that can carry context toward an outcome. */
|
|
337
380
|
function buildOutcomeReverseIndex(assembly, signal) {
|
|
338
381
|
const reverse = new Map();
|
|
382
|
+
const basisKinds = {
|
|
383
|
+
source_to_target: [],
|
|
384
|
+
target_to_source: [],
|
|
385
|
+
both: [],
|
|
386
|
+
};
|
|
387
|
+
for (const definition of assembly.graph.registry().list()) {
|
|
388
|
+
if (definition.outcomeTraversal !== undefined) {
|
|
389
|
+
basisKinds[definition.outcomeTraversal].push(definition.kind);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
const basis = {
|
|
393
|
+
source_to_target: basisKinds.source_to_target.join(","),
|
|
394
|
+
target_to_source: basisKinds.target_to_source.join(","),
|
|
395
|
+
both: basisKinds.both.join(","),
|
|
396
|
+
};
|
|
397
|
+
const append = (from, to) => {
|
|
398
|
+
const sources = reverse.get(to) ?? new Set();
|
|
399
|
+
sources.add(from);
|
|
400
|
+
reverse.set(to, sources);
|
|
401
|
+
};
|
|
339
402
|
for (const edge of assembly.graph.edges()) {
|
|
340
403
|
signal?.throwIfAborted();
|
|
341
404
|
const definition = assembly.graph.registry().require(edge.kind);
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
}
|
|
349
|
-
if (oriented === undefined)
|
|
350
|
-
continue;
|
|
351
|
-
const sources = reverse.get(oriented.to) ?? new Set();
|
|
352
|
-
sources.add(oriented.from);
|
|
353
|
-
reverse.set(oriented.to, sources);
|
|
405
|
+
if (definition.outcomeTraversal === "source_to_target" ||
|
|
406
|
+
definition.outcomeTraversal === "both")
|
|
407
|
+
append(edge.source, edge.target);
|
|
408
|
+
if (definition.outcomeTraversal === "target_to_source" ||
|
|
409
|
+
definition.outcomeTraversal === "both")
|
|
410
|
+
append(edge.target, edge.source);
|
|
354
411
|
}
|
|
355
|
-
return reverse;
|
|
412
|
+
return { basis, reverse };
|
|
356
413
|
}
|
|
357
414
|
/** Expand outcome nodes through every deterministic reverse typed path. */
|
|
358
415
|
function expandOutcomeReachability(outcomes, reverse, signal) {
|
|
@@ -425,9 +482,11 @@ function profileOutcomeReachability(assembly, nodeStates, signal) {
|
|
|
425
482
|
.toLowerCase()
|
|
426
483
|
.startsWith(OUTCOME_MILESTONE_TITLE_PREFIX))
|
|
427
484
|
.map((detail) => detail.id));
|
|
428
|
-
const
|
|
485
|
+
const { basis, reverse } = buildOutcomeReverseIndex(assembly, signal);
|
|
486
|
+
const reachable = expandOutcomeReachability(outcomes, reverse, signal);
|
|
429
487
|
return {
|
|
430
488
|
outcome_nodes: outcomes.size,
|
|
489
|
+
outcome_reachability_basis: basis,
|
|
431
490
|
...tallyOutcomeReachability(nodeStates, outcomes, reachable, signal),
|
|
432
491
|
};
|
|
433
492
|
}
|
|
@@ -561,6 +620,7 @@ export function auditWorkspaceRelationshipGraph(assembly, options = {}) {
|
|
|
561
620
|
for (const edge of assembly.graph.edges())
|
|
562
621
|
edgesByKind[edge.kind] = (edgesByKind[edge.kind] ?? 0) + 1;
|
|
563
622
|
const findings = collectIntegrityFindings(assembly, isTerminal, maxSampleSize);
|
|
623
|
+
findings.push(...collectHierarchyIntegrityFindings(assembly, maxSampleSize));
|
|
564
624
|
options.signal?.throwIfAborted();
|
|
565
625
|
findings.push(...collectOrderingStorageContradictionFindings(assembly, maxSampleSize));
|
|
566
626
|
options.signal?.throwIfAborted();
|
|
@@ -673,4 +733,4 @@ export function diffRelationshipAuditSnapshots(baseline, current) {
|
|
|
673
733
|
};
|
|
674
734
|
}
|
|
675
735
|
//# sourceMappingURL=governance.js.map
|
|
676
|
-
//# debugId=
|
|
736
|
+
//# debugId=27de9c62-434c-566d-a000-54eddb9a16a9
|