@unbrained/pm-cli 2026.8.21 → 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 +16 -0
- package/dist/cli/error-guidance.d.ts +9 -4
- package/dist/cli/error-guidance.js +16 -15
- package/dist/cli/main.d.ts +3 -0
- package/dist/cli/main.js +40 -7
- package/dist/cli-bundle/bundle-manifest.json +153 -153
- package/dist/cli-bundle/chunks/{chunk-BKYB45NQ.js → chunk-BWX4SK46.js} +48 -48
- 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-LARJJ3RK.js → chunk-MYYI3ZOA.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-BUNIEJNE.js → chunk-MZTZQTMS.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-LOVTQ52Z.js → chunk-QI2CT3IX.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-SMWXHMVB.js +35 -0
- package/dist/cli-bundle/chunks/{chunk-N3DSV4CH.js → chunk-U6GQ4VMV.js} +6 -6
- package/dist/cli-bundle/chunks/{chunk-3AFAUHUE.js → chunk-VMMIL3SI.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-WG4STC36.js +5 -0
- package/dist/cli-bundle/chunks/{chunk-JSLOFXGA.js → chunk-YBPJAOQQ.js} +2 -2
- package/dist/cli-bundle/chunks/{register-list-query-F57EWDEF.js → register-list-query-HZKBQNWW.js} +2 -2
- package/dist/cli-bundle/chunks/{register-mutation-7IY2D5KO.js → register-mutation-HTKEEGRD.js} +2 -2
- package/dist/cli-bundle/chunks/{register-operations-FALL3YTU.js → register-operations-6EMRWJJY.js} +2 -2
- package/dist/cli-bundle/chunks/{register-setup-HXU7IZZD.js → register-setup-QK4OTOXN.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-FJ22HNRP.js → chunk-2ARQGDDW.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-CKKQSJNN.js → chunk-2AZT5WXZ.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-F4YEW5I2.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-FH6DYED3.js → chunk-6S2MO3Q3.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-UZE7XUZK.js → chunk-A2BC5PQL.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-C4S4A4JJ.js +5 -0
- package/dist/cli-bundle/focused-chunks/{chunk-KDNG7TKQ.js → chunk-D6RBZEJJ.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-E2EWYPY6.js +16 -0
- package/dist/cli-bundle/focused-chunks/{chunk-4ERLO7QD.js → chunk-GMYPD4FX.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-L5LDSRO2.js +155 -0
- package/dist/cli-bundle/focused-chunks/{chunk-ZOSEELYJ.js → chunk-MB37AYN6.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-MCOR37QK.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-2NIY5NKG.js → chunk-PH2OXXVZ.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-YKBKIEAJ.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 +4 -0
- package/dist/core/shared/errors.js +2 -2
- 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 +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/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/generated/generated-error-code-catalog-part-1.js +38 -14
- package/dist/sdk/generated/generated-error-code-catalog-part-2.js +14 -2
- 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 +1 -7
- package/dist/sdk/governance/validate.js +47 -99
- 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 +2 -2
- package/dist/sdk/index.js +3 -3
- package/dist/sdk/item-children.js +21 -8
- package/dist/sdk/lifecycle/create.js +125 -96
- 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 +171 -83
- 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-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/relationships.js +17 -10
- package/dist/sdk/runtime.d.ts +3 -2
- package/dist/sdk/runtime.js +3 -2
- package/dist/sdk/test/batch.js +13 -5
- package/dist/sdk/test/execution.js +10 -2
- package/dist/types.d.ts +11 -0
- package/dist/types.js +2 -2
- package/docs/AGENT_UX_CONTRACTS.md +20 -4
- package/docs/COMMANDS.md +29 -14
- package/docs/DEPENDENCY_KIND_CONTRACT.md +31 -3
- 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/RELATIONSHIP_GRAPH.md +6 -3
- 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 +593 -35
- package/dist/cli-bundle/chunks/chunk-2MA6ZQVZ.js +0 -197
- package/dist/cli-bundle/chunks/chunk-7VHZS727.js +0 -5
- package/dist/cli-bundle/chunks/chunk-OCLPLFAY.js +0 -35
- package/dist/cli-bundle/chunks/chunk-S4IRYKBU.js +0 -13
- package/dist/cli-bundle/focused-chunks/chunk-7WZRLCYF.js +0 -50
- 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
|
@@ -7,6 +7,7 @@ import { type HistoryAuthorAttributionScan } from "../author-attribution.js";
|
|
|
7
7
|
import type { PmSettings } from "../../types/index.js";
|
|
8
8
|
import { buildCapabilityContractMetadata, collectUnknownCapabilityGuidance } from "../extension/doctor.js";
|
|
9
9
|
import { type StaleInProgressScan } from "./stale-work.js";
|
|
10
|
+
import { type HierarchyIntegrityAnalysis } from "../graph/hierarchy-integrity.js";
|
|
10
11
|
type HealthStatus = "ok" | "warn";
|
|
11
12
|
type MigrationRuntimeStatus = "pending" | "failed" | "applied";
|
|
12
13
|
/** Documents the health check payload exchanged by command, SDK, and package integrations. */
|
|
@@ -145,6 +146,7 @@ declare function normalizeExtensionNameForMatch(value: string): string;
|
|
|
145
146
|
declare function isExpectedUnmanagedExtension(name: string, directory: string): boolean;
|
|
146
147
|
declare function isDirectory(targetPath: string): Promise<boolean>;
|
|
147
148
|
declare function listItemDocumentPaths(pmRoot: string, typeToFolder: Record<string, string>): Promise<string[]>;
|
|
149
|
+
declare function buildHierarchyIntegrityWarnings(analysis: HierarchyIntegrityAnalysis): string[];
|
|
148
150
|
declare function buildExtensionHealthTriageSummary(warnings: string[], loadFailureCount: number, activationFailureCount: number, migrationStatus: MigrationStatusSummary, managedStateWarningCount: number, managedExtensionEntriesCount: number, unmanagedLoadedExtensions: string[], unmanagedExpectedExtensions: string[], unmanagedActionRequiredExtensions: string[]): ExtensionHealthTriageSummary;
|
|
149
151
|
declare function summarizeRecordList(value: unknown, limit: number): {
|
|
150
152
|
count: number;
|
|
@@ -218,6 +220,7 @@ declare function projectHealthResult(result: HealthResult, options: RunHealthOpt
|
|
|
218
220
|
export declare function runHealth(global: GlobalOptions, options?: RunHealthOptions): Promise<HealthResult>;
|
|
219
221
|
/** Public contract for test only health command, shared by SDK and presentation-layer consumers. */
|
|
220
222
|
export declare const _testOnlyHealthCommand: {
|
|
223
|
+
buildHierarchyIntegrityWarnings: typeof buildHierarchyIntegrityWarnings;
|
|
221
224
|
buildExtensionHealthTriageSummary: typeof buildExtensionHealthTriageSummary;
|
|
222
225
|
buildHealthFindings: typeof buildHealthFindings;
|
|
223
226
|
buildCapabilityContractMetadata: typeof buildCapabilityContractMetadata;
|
|
@@ -4,11 +4,13 @@
|
|
|
4
4
|
* Implements the pm health 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]="f7b3ff92-1fc7-55e9-aa76-af956714998f")}catch(e){}}();
|
|
8
8
|
import fs from "node:fs/promises";
|
|
9
9
|
import path from "node:path";
|
|
10
10
|
import { assertInitializedTracker } from "../environment/tracker-preflight.js";
|
|
11
|
+
import { isTerminalStatus } from "../../core/item/status.js";
|
|
11
12
|
import { resolveItemTypeRegistry } from "../../core/item/type-registry.js";
|
|
13
|
+
import { resolveRuntimeStatusRegistry } from "../../core/schema/runtime-schema.js";
|
|
12
14
|
import { isFileMissingError, readFileIfExists, } from "../../core/fs/fs-utils.js";
|
|
13
15
|
import { activateExtensions, getActiveExtensionRegistrations, loadExtensions, runActiveOnReadHooks, } from "../../core/extensions/index.js";
|
|
14
16
|
import { collectRegisteredItemFieldNames } from "../../core/extensions/item-fields.js";
|
|
@@ -44,6 +46,8 @@ import { scanStaleInProgressItems, } from "./stale-work.js";
|
|
|
44
46
|
import { scanTrackedRuntimeCache } from "./tracked-runtime-cache.js";
|
|
45
47
|
import { auditMergeDriverConfiguration, findGitWorkspaceRoot, } from "../merge/install.js";
|
|
46
48
|
import { listMergeReceipts, partitionMergeReceipts, } from "../merge/receipts.js";
|
|
49
|
+
import { resolveWorkspaceRelationshipKindRegistry } from "../graph/assembly.js";
|
|
50
|
+
import { analyzeHierarchyIntegrity, } from "../graph/hierarchy-integrity.js";
|
|
47
51
|
const PM_TELEMETRY_SOURCE_CONTEXT_SET = new Set(PM_TELEMETRY_SOURCE_CONTEXT_VALUES);
|
|
48
52
|
const STALE_VECTORIZATION_SUMMARY_LIMIT = 25;
|
|
49
53
|
const BRIEF_HEALTH_DETAIL_LIMIT = 8;
|
|
@@ -61,6 +65,7 @@ const TELEMETRY_SERVER_MAX_SCHEMA_VERSION_HEADERS = [
|
|
|
61
65
|
function isAdvisoryHealthWarning(warning, requireMergeDrivers = false) {
|
|
62
66
|
return (warning.startsWith("telemetry_") ||
|
|
63
67
|
warning.startsWith("history_stream_over_compact_threshold:") ||
|
|
68
|
+
warning.startsWith("integrity_legacy_hierarchy_") ||
|
|
64
69
|
warning.startsWith("stale_in_progress_items:") ||
|
|
65
70
|
warning.startsWith("provenance_resolver_zero_success:") ||
|
|
66
71
|
warning.startsWith("provenance_value_domain_invalid:") ||
|
|
@@ -315,10 +320,25 @@ async function scanHistoryIntegrity(pmRoot) {
|
|
|
315
320
|
}
|
|
316
321
|
return { files, unreadable, conflictMarkers, invalidJson };
|
|
317
322
|
}
|
|
318
|
-
|
|
323
|
+
function buildHierarchyIntegrityWarnings(analysis) {
|
|
324
|
+
return [
|
|
325
|
+
...analysis.cycles.map((cycle) => cycle.legacy_terminal
|
|
326
|
+
? `integrity_legacy_hierarchy_cycle:${cycle.item_ids.join(",")}`
|
|
327
|
+
: `integrity_hierarchy_cycle:${cycle.item_ids.join(",")}`),
|
|
328
|
+
...analysis.cardinality_violations.map((finding) => finding.legacy_terminal
|
|
329
|
+
? `integrity_legacy_hierarchy_cardinality:${finding.child_id}`
|
|
330
|
+
: `integrity_hierarchy_cardinality:${finding.child_id}`),
|
|
331
|
+
...analysis.divergences.map((finding) => finding.legacy_terminal
|
|
332
|
+
? `integrity_legacy_hierarchy_divergence:${finding.child_id}`
|
|
333
|
+
: `integrity_hierarchy_divergence:${finding.child_id}`),
|
|
334
|
+
];
|
|
335
|
+
}
|
|
336
|
+
async function buildIntegrityCheck(pmRoot, typeToFolder, schema, requireMergeDrivers, items) {
|
|
319
337
|
const itemScan = await scanItemIntegrity(pmRoot, typeToFolder, schema);
|
|
320
338
|
const historyScan = await scanHistoryIntegrity(pmRoot);
|
|
321
339
|
const formatVersionScan = scanItemFormatVersions(itemScan.formatVersions);
|
|
340
|
+
const statusRegistry = resolveRuntimeStatusRegistry(schema);
|
|
341
|
+
const hierarchyIntegrity = analyzeHierarchyIntegrity(items, (status) => isTerminalStatus(status, statusRegistry), resolveWorkspaceRelationshipKindRegistry());
|
|
322
342
|
const trackedRuntimeCache = await scanTrackedRuntimeCache(pmRoot);
|
|
323
343
|
const gitWorkspaceRoot = await findGitWorkspaceRoot(pmRoot);
|
|
324
344
|
const mergeDriverAudit = gitWorkspaceRoot === null
|
|
@@ -345,6 +365,7 @@ async function buildIntegrityCheck(pmRoot, typeToFolder, schema, requireMergeDri
|
|
|
345
365
|
...formatVersionScan.outdated.map((entry) => `integrity_item_outdated_format_version:${entry}`),
|
|
346
366
|
/* c8 ignore stop */
|
|
347
367
|
...formatVersionScan.ahead.map((entry) => `integrity_item_ahead_format_version:${entry}`),
|
|
368
|
+
...buildHierarchyIntegrityWarnings(hierarchyIntegrity),
|
|
348
369
|
...(trackedRuntimeCache.tracked_path_count > 0
|
|
349
370
|
? [
|
|
350
371
|
`tracked_runtime_cache_files:${trackedRuntimeCache.tracked_path_count}`,
|
|
@@ -360,11 +381,12 @@ async function buildIntegrityCheck(pmRoot, typeToFolder, schema, requireMergeDri
|
|
|
360
381
|
: []),
|
|
361
382
|
];
|
|
362
383
|
const normalizedWarnings = [...new Set(warnings)].sort((left, right) => left.localeCompare(right));
|
|
384
|
+
const actionableWarnings = normalizedWarnings.filter((warning) => !warning.startsWith("integrity_legacy_hierarchy_"));
|
|
363
385
|
return {
|
|
364
386
|
check: {
|
|
365
387
|
name: "integrity",
|
|
366
|
-
status:
|
|
367
|
-
ok:
|
|
388
|
+
status: actionableWarnings.length === 0 ? "ok" : "warn",
|
|
389
|
+
ok: actionableWarnings.length === 0,
|
|
368
390
|
details: {
|
|
369
391
|
checked_item_files: itemScan.paths.length,
|
|
370
392
|
checked_history_streams: historyScan.files.length,
|
|
@@ -377,6 +399,9 @@ async function buildIntegrityCheck(pmRoot, typeToFolder, schema, requireMergeDri
|
|
|
377
399
|
history_invalid_json: historyScan.invalidJson.length,
|
|
378
400
|
item_outdated_format_version: formatVersionScan.outdated.length,
|
|
379
401
|
item_ahead_format_version: formatVersionScan.ahead.length,
|
|
402
|
+
hierarchy_cycles: hierarchyIntegrity.cycles.length,
|
|
403
|
+
hierarchy_cardinality_violations: hierarchyIntegrity.cardinality_violations.length,
|
|
404
|
+
hierarchy_parent_divergences: hierarchyIntegrity.divergences.length,
|
|
380
405
|
tracked_runtime_cache_files: trackedRuntimeCache.tracked_path_count,
|
|
381
406
|
merge_driver_configuration: mergeDriverAudit === null || mergeDriverAudit.status === "ok"
|
|
382
407
|
? 0
|
|
@@ -390,6 +415,10 @@ async function buildIntegrityCheck(pmRoot, typeToFolder, schema, requireMergeDri
|
|
|
390
415
|
item_parse_failures: itemScan.parseFailures,
|
|
391
416
|
item_outdated_format_version: formatVersionScan.outdated,
|
|
392
417
|
item_ahead_format_version: formatVersionScan.ahead,
|
|
418
|
+
hierarchy_integrity: {
|
|
419
|
+
...hierarchyIntegrity,
|
|
420
|
+
relations: summarizeRecordList(hierarchyIntegrity.relations, BRIEF_HEALTH_DETAIL_LIMIT),
|
|
421
|
+
},
|
|
393
422
|
history_unreadable: historyScan.unreadable,
|
|
394
423
|
history_conflict_markers: historyScan.conflictMarkers,
|
|
395
424
|
history_invalid_json: historyScan.invalidJson,
|
|
@@ -1832,7 +1861,9 @@ function extractHistoryDriftedCount(historyDriftCheck) {
|
|
|
1832
1861
|
function buildHealthRemediationSources(params) {
|
|
1833
1862
|
return {
|
|
1834
1863
|
settings: params.normalizedSettingsReadWarnings,
|
|
1835
|
-
directories: params.directoryState.missingDirs
|
|
1864
|
+
directories: params.directoryState.missingDirs
|
|
1865
|
+
.map((dir) => `missing_directory:${dir}`)
|
|
1866
|
+
.concat(params.directoryState.hookWarnings),
|
|
1836
1867
|
settings_values: params.settingWarnings,
|
|
1837
1868
|
telemetry: params.telemetryCheck.warnings,
|
|
1838
1869
|
extensions: params.extensionCheck.warnings,
|
|
@@ -2000,7 +2031,7 @@ export async function runHealth(global, options = {}) {
|
|
|
2000
2031
|
const locksCheck = await buildLocksCheck(pmRoot);
|
|
2001
2032
|
const integrityCheck = skipPolicy.skipIntegrity
|
|
2002
2033
|
? buildSkippedHealthCheck("integrity")
|
|
2003
|
-
: await buildIntegrityCheck(pmRoot, typeRegistry.type_to_folder, settings.schema, options.requireMergeDrivers === true);
|
|
2034
|
+
: await buildIntegrityCheck(pmRoot, typeRegistry.type_to_folder, settings.schema, options.requireMergeDrivers === true, items);
|
|
2004
2035
|
const historyDriftCheck = skipPolicy.skipDrift
|
|
2005
2036
|
? buildSkippedHealthCheck("history_drift")
|
|
2006
2037
|
: await buildHistoryDriftCheck(pmRoot, itemsWithBody);
|
|
@@ -2082,6 +2113,7 @@ export async function runHealth(global, options = {}) {
|
|
|
2082
2113
|
}
|
|
2083
2114
|
/** Public contract for test only health command, shared by SDK and presentation-layer consumers. */
|
|
2084
2115
|
export const _testOnlyHealthCommand = {
|
|
2116
|
+
buildHierarchyIntegrityWarnings,
|
|
2085
2117
|
buildExtensionHealthTriageSummary,
|
|
2086
2118
|
buildHealthFindings,
|
|
2087
2119
|
buildCapabilityContractMetadata,
|
|
@@ -2110,4 +2142,4 @@ export const _testOnlyHealthCommand = {
|
|
|
2110
2142
|
warningCode,
|
|
2111
2143
|
};
|
|
2112
2144
|
//# sourceMappingURL=health.js.map
|
|
2113
|
-
//# debugId=
|
|
2145
|
+
//# debugId=f7b3ff92-1fc7-55e9-aa76-af956714998f
|
|
@@ -0,0 +1,29 @@
|
|
|
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
|
+
import { type RuntimeStatusRegistry } from "../../core/schema/runtime-schema.js";
|
|
8
|
+
import type { RelationshipKindRegistry } from "../relationships.js";
|
|
9
|
+
import type { ValidateItem } from "./validate-item-reader.js";
|
|
10
|
+
/** Resolve one deterministic closed walk through a known cycle component. */
|
|
11
|
+
export declare function resolveLifecycleDependencyCycleSamplePath(component: string[], graph: Map<string, string[]>): string[];
|
|
12
|
+
/** Build canonical child-to-parent adjacency across every hierarchy spelling. */
|
|
13
|
+
export declare function buildLifecycleParentGraph(items: ValidateItem[], relationshipRegistry?: RelationshipKindRegistry): Map<string, string[]>;
|
|
14
|
+
/** Project shared hierarchy analysis into the validation result contract. */
|
|
15
|
+
export declare function detectLifecycleParentCycles(items: ValidateItem[], statusRegistry?: RuntimeStatusRegistry, relationshipRegistry?: RelationshipKindRegistry): {
|
|
16
|
+
cycle_count: number;
|
|
17
|
+
active_cycle_count: number;
|
|
18
|
+
legacy_cycle_count: number;
|
|
19
|
+
cycle_item_ids: string[];
|
|
20
|
+
cycle_sample_paths: string[];
|
|
21
|
+
cardinality_violation_count: number;
|
|
22
|
+
active_cardinality_violation_count: number;
|
|
23
|
+
legacy_cardinality_violation_count: number;
|
|
24
|
+
cardinality_violation_rows: string[];
|
|
25
|
+
parent_divergence_count: number;
|
|
26
|
+
active_parent_divergence_count: number;
|
|
27
|
+
legacy_parent_divergence_count: number;
|
|
28
|
+
parent_divergence_rows: string[];
|
|
29
|
+
};
|
|
@@ -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;
|
|
@@ -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]="87e3932d-26f0-5d44-ac35-5d0d7d1f4467")}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";
|
|
@@ -44,6 +44,7 @@ import { runFiles } from "../files.js";
|
|
|
44
44
|
import { extractReferencedPmItemIdsFromCommand } from "../test/linked-command-detection.js";
|
|
45
45
|
import { readValidateItems, } from "./validate-item-reader.js";
|
|
46
46
|
import { buildValidateHistoryDriftCheck } from "./validate-history-drift.js";
|
|
47
|
+
import { buildLifecycleParentGraph, detectLifecycleParentCycles, resolveLifecycleDependencyCycleSamplePath, } from "./hierarchy-validation.js";
|
|
47
48
|
const FILE_SCAN_DIRECTORIES = ["src", "tests", "docs"];
|
|
48
49
|
const FILE_SCAN_ROOT_FILES = [
|
|
49
50
|
"README.md",
|
|
@@ -1107,41 +1108,6 @@ function findLifecycleDependencyCycleComponents(graph) {
|
|
|
1107
1108
|
left.join(",").localeCompare(right.join(",")));
|
|
1108
1109
|
}
|
|
1109
1110
|
/* 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
1111
|
function detectLifecycleDependencyCycles(activeItems, idPrefix = "pm") {
|
|
1146
1112
|
const graph = buildLifecycleDependencyGraph(activeItems, idPrefix);
|
|
1147
1113
|
const cycleComponents = findLifecycleDependencyCycleComponents(graph);
|
|
@@ -1153,62 +1119,6 @@ function detectLifecycleDependencyCycles(activeItems, idPrefix = "pm") {
|
|
|
1153
1119
|
cycle_sample_paths: cycleSamplePaths,
|
|
1154
1120
|
};
|
|
1155
1121
|
}
|
|
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
1122
|
function classifyOrphanedPath(pathValue) {
|
|
1213
1123
|
if (pathValue.startsWith("docs/")) {
|
|
1214
1124
|
return "docs_unowned";
|
|
@@ -1435,7 +1345,10 @@ function lifecycleCycleWarningToken(prefix, severity, count) {
|
|
|
1435
1345
|
}
|
|
1436
1346
|
return `${severity === "error" ? `${prefix}_error` : prefix}:${count}`;
|
|
1437
1347
|
}
|
|
1438
|
-
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) {
|
|
1439
1352
|
const warnings = [];
|
|
1440
1353
|
if (rows.closureLikeRows.length > 0) {
|
|
1441
1354
|
warnings.push(`validate_lifecycle_active_closure_like_metadata:${rows.closureLikeRows.length}`);
|
|
@@ -1446,12 +1359,22 @@ function buildLifecycleWarnings(rows, includeStaleBlockers, dependencyCycleSever
|
|
|
1446
1359
|
if (includeStaleBlockers && rows.staleBlockerRows.length > 0) {
|
|
1447
1360
|
warnings.push(`validate_lifecycle_stale_blockers:${rows.staleBlockerRows.length}`);
|
|
1448
1361
|
}
|
|
1449
|
-
const dependencyWarning = lifecycleCycleWarningToken("validate_lifecycle_dependency_cycles", dependencyCycleSeverity,
|
|
1450
|
-
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);
|
|
1451
1369
|
return [
|
|
1452
1370
|
...warnings,
|
|
1453
1371
|
...(dependencyWarning ? [dependencyWarning] : []),
|
|
1454
1372
|
...(parentWarning ? [parentWarning] : []),
|
|
1373
|
+
...(cardinalityWarning ? [cardinalityWarning] : []),
|
|
1374
|
+
...(divergenceWarning ? [divergenceWarning] : []),
|
|
1375
|
+
...(legacyCycleWarning ? [legacyCycleWarning] : []),
|
|
1376
|
+
...(legacyCardinalityWarning ? [legacyCardinalityWarning] : []),
|
|
1377
|
+
...(legacyDivergenceWarning ? [legacyDivergenceWarning] : []),
|
|
1455
1378
|
];
|
|
1456
1379
|
}
|
|
1457
1380
|
function linkedArtifactPathExceedsFilesystemLimits(artifactPath) {
|
|
@@ -1557,8 +1480,16 @@ function buildLifecycleCheck(items, includeStaleBlockers, dependencyCycleSeverit
|
|
|
1557
1480
|
const rows = collectLifecycleScanRows(items, includeStaleBlockers, statusRegistry, lifecyclePatternPolicy);
|
|
1558
1481
|
sortLifecycleScanRows(rows);
|
|
1559
1482
|
const dependencyCycleDiagnostics = detectLifecycleDependencyCycles(rows.activeItems, idPrefix);
|
|
1560
|
-
const parentCycleDiagnostics = detectLifecycleParentCycles(items);
|
|
1561
|
-
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
|
+
});
|
|
1562
1493
|
const diagnosticLimit = verboseDiagnostics
|
|
1563
1494
|
? Number.POSITIVE_INFINITY
|
|
1564
1495
|
: DIAGNOSTIC_LIST_SUMMARY_LIMIT;
|
|
@@ -1569,9 +1500,14 @@ function buildLifecycleCheck(items, includeStaleBlockers, dependencyCycleSeverit
|
|
|
1569
1500
|
const summarizedDependencyCycleSamplePaths = summarizeList(dependencyCycleDiagnostics.cycle_sample_paths, diagnosticLimit);
|
|
1570
1501
|
const summarizedParentCycleItemIds = summarizeList(parentCycleDiagnostics.cycle_item_ids, diagnosticLimit);
|
|
1571
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);
|
|
1572
1505
|
const hasErrorSeverityCycle = (dependencyCycleDiagnostics.cycle_count > 0 &&
|
|
1573
1506
|
dependencyCycleSeverity === "error") ||
|
|
1574
|
-
(parentCycleDiagnostics.
|
|
1507
|
+
((parentCycleDiagnostics.active_cycle_count > 0 ||
|
|
1508
|
+
parentCycleDiagnostics.active_cardinality_violation_count > 0 ||
|
|
1509
|
+
parentCycleDiagnostics.active_parent_divergence_count > 0) &&
|
|
1510
|
+
parentCycleSeverity === "error");
|
|
1575
1511
|
return {
|
|
1576
1512
|
check: {
|
|
1577
1513
|
name: "lifecycle",
|
|
@@ -1602,11 +1538,23 @@ function buildLifecycleCheck(items, includeStaleBlockers, dependencyCycleSeverit
|
|
|
1602
1538
|
dependency_cycle_sample_paths_truncated: summarizedDependencyCycleSamplePaths.truncated,
|
|
1603
1539
|
parent_cycle_severity_policy: parentCycleSeverity,
|
|
1604
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,
|
|
1605
1543
|
parent_cycle_item_count: parentCycleDiagnostics.cycle_item_ids.length,
|
|
1606
1544
|
parent_cycle_item_ids: summarizedParentCycleItemIds.values,
|
|
1607
1545
|
parent_cycle_item_ids_truncated: summarizedParentCycleItemIds.truncated,
|
|
1608
1546
|
parent_cycle_sample_paths: summarizedParentCycleSamplePaths.values,
|
|
1609
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,
|
|
1610
1558
|
stale_blocker_reason_patterns: [
|
|
1611
1559
|
...lifecyclePatternPolicy.stale_blocker_reason_patterns,
|
|
1612
1560
|
],
|
|
@@ -2307,4 +2255,4 @@ export async function runValidate(options, global, services = {}) {
|
|
|
2307
2255
|
}
|
|
2308
2256
|
/* c8 ignore stop */
|
|
2309
2257
|
//# sourceMappingURL=validate.js.map
|
|
2310
|
-
//# 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
|
/**
|