@unbrained/pm-cli 2026.9.7 → 2026.9.9
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/.agents/skills/pm-developer/SKILL.md +12 -4
- package/.agents/skills/pm-developer/references/MULTI_AGENT_MERGE.md +19 -10
- package/.agents/skills/pm-extensions/SKILL.md +6 -0
- package/.agents/skills/pm-sdk/SKILL.md +11 -2
- package/.agents/skills/pm-user/SKILL.md +7 -2
- package/.agents/skills/pm-user/references/ECOSYSTEM_PLANNING.md +95 -0
- package/.claude-plugin/marketplace.json +2 -2
- package/CHANGELOG.md +231 -182
- package/dist/cli/after-command-context-usage.js +1 -48
- package/dist/cli/argv-utils.js +1 -40
- package/dist/cli/bootstrap-args.js +1 -9
- package/dist/cli/bundle-integrity.js +2 -110
- package/dist/cli/commander-usage.js +11 -771
- package/dist/cli/commands/activity.js +1 -9
- package/dist/cli/commands/aggregate.js +1 -5
- package/dist/cli/commands/append.js +1 -5
- package/dist/cli/commands/claim.js +1 -5
- package/dist/cli/commands/close-many.js +1 -5
- package/dist/cli/commands/close.js +1 -9
- package/dist/cli/commands/comments.js +1 -5
- package/dist/cli/commands/config.js +1 -9
- package/dist/cli/commands/context.js +1 -9
- package/dist/cli/commands/contracts.js +1 -5
- package/dist/cli/commands/copy.js +1 -9
- package/dist/cli/commands/create.js +1 -9
- package/dist/cli/commands/delete.js +1 -9
- package/dist/cli/commands/deps.js +1 -9
- package/dist/cli/commands/docs.js +1 -9
- package/dist/cli/commands/duplicates.js +1 -9
- package/dist/cli/commands/eval.js +1 -9
- package/dist/cli/commands/extension/describe.js +1 -5
- package/dist/cli/commands/extension/scaffold.js +1 -5
- package/dist/cli/commands/extension/shared.js +1 -5
- package/dist/cli/commands/extension.js +1 -9
- package/dist/cli/commands/files.js +1 -9
- package/dist/cli/commands/focus.js +1 -9
- package/dist/cli/commands/gc.js +1 -9
- package/dist/cli/commands/get.js +1 -9
- package/dist/cli/commands/graph.js +1 -10
- package/dist/cli/commands/health.js +1 -9
- package/dist/cli/commands/history-compact.js +1 -10
- package/dist/cli/commands/history-redact.js +1 -11
- package/dist/cli/commands/history-repair.js +1 -10
- package/dist/cli/commands/history.js +1 -5
- package/dist/cli/commands/index.js +1 -58
- package/dist/cli/commands/init-agent-guidance.js +1 -9
- package/dist/cli/commands/init.js +1 -9
- package/dist/cli/commands/learnings.js +1 -5
- package/dist/cli/commands/lifecycle-transitions.js +1 -5
- package/dist/cli/commands/list.js +1 -9
- package/dist/cli/commands/merge.js +1 -11
- package/dist/cli/commands/next.js +1 -9
- package/dist/cli/commands/notes.js +1 -5
- package/dist/cli/commands/plan.js +1 -9
- package/dist/cli/commands/profile.js +1 -9
- package/dist/cli/commands/restore.js +1 -9
- package/dist/cli/commands/scheduling-shortcuts.js +1 -9
- package/dist/cli/commands/schema.js +1 -10
- package/dist/cli/commands/search.js +1 -9
- package/dist/cli/commands/stats.js +1 -9
- package/dist/cli/commands/telemetry.js +1 -9
- package/dist/cli/commands/test-all.js +1 -9
- package/dist/cli/commands/test-runs.js +1 -9
- package/dist/cli/commands/test.js +1 -9
- package/dist/cli/commands/update-many.js +1 -5
- package/dist/cli/commands/update.js +1 -9
- package/dist/cli/commands/upgrade.js +1 -5
- package/dist/cli/commands/validate.js +1 -15
- package/dist/cli/context-intent-invocation.js +1 -20
- package/dist/cli/description-stdin.js +1 -17
- package/dist/cli/error-guidance.js +1 -1353
- package/dist/cli/extension-command-help.js +5 -636
- package/dist/cli/extension-command-options.js +1 -427
- package/dist/cli/extension-recovery.js +1 -126
- package/dist/cli/help-content.js +2 -1001
- package/dist/cli/help-json-payload.js +4 -570
- package/dist/cli/main.d.ts +4 -0
- package/dist/cli/main.js +5 -2573
- package/dist/cli/migration-gates.js +1 -289
- package/dist/cli/public.js +1 -20
- package/dist/cli/register-annotations.js +1 -270
- package/dist/cli/register-assurance.js +1 -62
- package/dist/cli/register-files-lookup.js +1 -56
- package/dist/cli/register-history-activity.d.ts +8 -0
- package/dist/cli/register-history-activity.js +2 -0
- package/dist/cli/register-history-author.js +1 -38
- package/dist/cli/register-history-maintenance.d.ts +9 -0
- package/dist/cli/register-history-maintenance.js +2 -0
- package/dist/cli/register-list-query.js +10 -943
- package/dist/cli/register-mutation-options.js +1 -9
- package/dist/cli/register-mutation.d.ts +0 -5
- package/dist/cli/register-mutation.js +19 -1893
- package/dist/cli/register-operations.js +1 -920
- package/dist/cli/register-setup.js +1 -716
- package/dist/cli/register-structured-mutation.js +1 -196
- package/dist/cli/registration-helpers.js +1 -5
- package/dist/cli/runtime-compatibility-boundary.js +4 -42
- package/dist/cli/schema-registration-helpers.js +1 -73
- package/dist/cli/search-refresh.js +1 -13
- package/dist/cli/stats-analytics-json.js +1 -68
- package/dist/cli/telemetry-flush.js +1 -10
- package/dist/cli-bundle/bundle-manifest.json +164 -156
- package/dist/cli-bundle/chunks/{chunk-NPN5I67J.js → chunk-3IPRM75M.js} +3 -3
- package/dist/cli-bundle/chunks/chunk-4VL5P22L.js +2 -0
- package/dist/cli-bundle/chunks/chunk-AAFBV3U3.js +29 -0
- package/dist/cli-bundle/chunks/chunk-D3YVILJJ.js +20 -0
- package/dist/cli-bundle/chunks/{chunk-WYRZZ5ZE.js → chunk-DEZPZOQ7.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-ECBLPN7B.js +3 -0
- package/dist/cli-bundle/chunks/chunk-J6ERHBV2.js +235 -0
- package/dist/cli-bundle/chunks/chunk-JUNJ5EKI.js +2 -0
- package/dist/cli-bundle/chunks/chunk-L5RCDKQ2.js +2 -0
- package/dist/cli-bundle/chunks/chunk-NSAPNEYH.js +2 -0
- package/dist/cli-bundle/chunks/chunk-OHNLSGGV.js +79 -0
- package/dist/cli-bundle/chunks/chunk-WQKMSFBV.js +5 -0
- package/dist/cli-bundle/chunks/{chunk-BAX2BYUW.js → chunk-XZ6T7YPP.js} +256 -150
- package/dist/cli-bundle/chunks/{chunk-JT6XF6GE.js → chunk-Z3XVXUVF.js} +4 -4
- package/dist/cli-bundle/chunks/register-list-query-UIHRCS6D.js +11 -0
- package/dist/cli-bundle/chunks/register-mutation-WHWMGRVX.js +20 -0
- package/dist/cli-bundle/chunks/register-operations-64YZYTC5.js +2 -0
- package/dist/cli-bundle/chunks/register-setup-PERI45EM.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-2W4XIYOB.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-2WSBXIGF.js +12 -0
- package/dist/cli-bundle/focused-chunks/chunk-5FHTCOHS.js +3 -0
- package/dist/cli-bundle/focused-chunks/chunk-7N6DOREF.js +26 -0
- package/dist/cli-bundle/focused-chunks/{chunk-3BZEOXLT.js → chunk-7PC4R5QB.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-BUCJUZVG.js +6 -0
- package/dist/cli-bundle/focused-chunks/chunk-FE3BABLJ.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-IWITF53W.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-OQUGRHKV.js +10 -0
- package/dist/cli-bundle/focused-chunks/{chunk-M6PBRFSH.js → chunk-PNKIANFZ.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-LIJIA4TY.js → chunk-QQQ4SAUO.js} +3 -3
- package/dist/cli-bundle/focused-chunks/chunk-TTYXF5YD.js +177 -0
- package/dist/cli-bundle/focused-chunks/chunk-XNR3DM6J.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-XSS3APUF.js +9 -0
- package/dist/cli-bundle/focused-chunks/chunk-YCDQ2N23.js +8 -0
- package/dist/cli-bundle/focused-chunks/chunk-YJUXGS57.js +20 -0
- package/dist/cli-bundle/focused-chunks/chunk-ZTCE4UO6.js +58 -0
- package/dist/cli-bundle/main.js +20 -20
- 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 +37 -37
- 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 +42 -42
- 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 +11 -11
- package/dist/cli.js +1 -104
- package/dist/core/checkpoint/checkpoint-gc.js +1 -169
- package/dist/core/checkpoint/mutation-checkpoint.js +2 -133
- package/dist/core/config/nested-settings.js +1 -534
- package/dist/core/config/positional-value.js +1 -109
- package/dist/core/diagnostics/remediation.js +1 -506
- package/dist/core/extensions/activation-summary-markdown.js +2 -145
- package/dist/core/extensions/activation-summary.js +1 -92
- package/dist/core/extensions/activation-transaction.js +1 -46
- package/dist/core/extensions/capability-usage.js +1 -173
- package/dist/core/extensions/command-hook-context.js +1 -13
- package/dist/core/extensions/command-metadata-contract.js +1 -22
- package/dist/core/extensions/command-visibility-tier.js +1 -29
- package/dist/core/extensions/contribution-inventory.js +1 -173
- package/dist/core/extensions/exporter-output-contract.js +1 -66
- package/dist/core/extensions/extension-candidate.js +1 -18
- package/dist/core/extensions/extension-capability-aliases.js +1 -158
- package/dist/core/extensions/extension-hook-runtime.js +1 -699
- package/dist/core/extensions/extension-policy.js +1 -599
- package/dist/core/extensions/extension-registries.js +1 -62
- package/dist/core/extensions/extension-runtime-helpers.js +1 -33
- package/dist/core/extensions/extension-types.js +1 -102
- package/dist/core/extensions/flag-definition-validation.js +1 -52
- package/dist/core/extensions/flag-value-types.js +1 -64
- package/dist/core/extensions/index.js +1 -526
- package/dist/core/extensions/item-field-types.js +1 -39
- package/dist/core/extensions/item-fields.js +1 -261
- package/dist/core/extensions/loader.js +1 -2223
- package/dist/core/extensions/manifest-schema.js +1 -69
- package/dist/core/extensions/module-graph-snapshot.js +1 -33
- package/dist/core/extensions/preflight-ownership.js +1 -41
- package/dist/core/extensions/registration-validation.js +1 -82
- package/dist/core/extensions/registration-values.js +1 -209
- package/dist/core/extensions/reserved-host-flags.js +1 -36
- package/dist/core/extensions/runtime-registrations.js +1 -67
- package/dist/core/extensions/service-ownership.js +1 -19
- package/dist/core/extensions/version-compat.js +1 -216
- package/dist/core/fs/fs-utils.js +2 -158
- package/dist/core/fs/host-environment-errors.js +1 -141
- package/dist/core/fs/index.js +1 -9
- package/dist/core/fs/path-utils.js +1 -17
- package/dist/core/governance/content-fields.js +1 -160
- package/dist/core/governance/issue-codes.js +1 -151
- package/dist/core/governance/metadata-coverage.js +1 -358
- package/dist/core/history/drift-cache.js +3 -72
- package/dist/core/history/drift-scan.js +1 -342
- package/dist/core/history/event-classification.js +1 -109
- package/dist/core/history/event-index.js +8 -784
- package/dist/core/history/history-compact-bulk.js +1 -104
- package/dist/core/history/history-diff.js +1 -158
- package/dist/core/history/history-rewrite.d.ts +6 -1
- package/dist/core/history/history-rewrite.js +1 -107
- package/dist/core/history/history-storage-stats.js +1 -102
- package/dist/core/history/history-stream-policy.js +1 -62
- package/dist/core/history/history.js +2 -617
- package/dist/core/history/identity.js +1 -30
- package/dist/core/history/index.js +1 -9
- package/dist/core/history/projection.js +1 -196
- package/dist/core/history/read.d.ts +9 -0
- package/dist/core/history/read.js +1 -57
- package/dist/core/history/replay.js +2 -439
- package/dist/core/history/version-address.js +1 -32
- package/dist/core/history/workspace-history.js +2 -433
- package/dist/core/io/body-file.js +1 -62
- package/dist/core/io/bulk-ids-input.js +1 -112
- package/dist/core/item/actionability.js +1 -327
- package/dist/core/item/dependency-reference.js +1 -20
- package/dist/core/item/id.js +1 -112
- package/dist/core/item/index.js +1 -12
- package/dist/core/item/item-format-version.js +1 -78
- package/dist/core/item/item-format.js +5 -1473
- package/dist/core/item/item-record.js +1 -23
- package/dist/core/item/item-type-definition.js +1 -133
- package/dist/core/item/parent-reference-policy.js +1 -57
- package/dist/core/item/parse.js +2 -603
- package/dist/core/item/plan-lifecycle.js +1 -19
- package/dist/core/item/priority.js +1 -61
- package/dist/core/item/sprint-release-format.js +1 -38
- package/dist/core/item/status-filter.js +1 -115
- package/dist/core/item/status.js +1 -43
- package/dist/core/item/toon-decode.js +1 -29
- package/dist/core/item/type-registry.js +1 -715
- package/dist/core/item/type-synonyms.js +1 -38
- package/dist/core/lock/index.js +1 -9
- package/dist/core/lock/lock-gc.js +1 -240
- package/dist/core/lock/lock.js +3 -590
- package/dist/core/output/mutation-projection.js +1 -217
- package/dist/core/output/output-control.js +1 -25
- package/dist/core/output/output.js +11 -494
- package/dist/core/output/query-summary.js +1 -58
- package/dist/core/output/tabular.js +1 -77
- package/dist/core/packages/manifest.js +1 -244
- package/dist/core/packages/root.js +1 -84
- package/dist/core/policy/workflow-policy-schema.d.ts +312 -0
- package/dist/core/policy/workflow-policy-schema.js +2 -0
- package/dist/core/policy/workflow-policy-store.d.ts +24 -0
- package/dist/core/policy/workflow-policy-store.js +2 -0
- package/dist/core/policy/workflow-policy.d.ts +139 -0
- package/dist/core/policy/workflow-policy.js +2 -0
- package/dist/core/profile/profile-describe.js +1 -83
- package/dist/core/profile/profile-lint.js +1 -415
- package/dist/core/profile/profile-plan.js +0 -0
- package/dist/core/profile/profile-presets.js +1 -370
- package/dist/core/reproducibility/context.js +1 -82
- package/dist/core/schema/fields-file.js +2 -288
- package/dist/core/schema/item-types-file.js +2 -296
- package/dist/core/schema/runtime-field-filters.js +1 -49
- package/dist/core/schema/runtime-field-values.js +1 -313
- package/dist/core/schema/runtime-schema.js +2 -826
- package/dist/core/schema/status-defs-file.js +2 -227
- package/dist/core/schema/status-token.js +1 -31
- package/dist/core/schema/type-inference.js +1 -95
- package/dist/core/schema/type-presets.js +1 -87
- package/dist/core/schema/type-workflows.js +1 -104
- package/dist/core/search/background-refresh.js +2 -286
- package/dist/core/search/bm25.js +1 -146
- package/dist/core/search/cache.js +2 -534
- package/dist/core/search/corpus.js +1 -311
- package/dist/core/search/embedding-batches.js +1 -163
- package/dist/core/search/eval.js +1 -225
- package/dist/core/search/http-client.js +1 -75
- package/dist/core/search/providers.js +1 -242
- package/dist/core/search/relevance.js +1 -244
- package/dist/core/search/semantic-defaults.js +1 -199
- package/dist/core/search/staleness.js +1 -30
- package/dist/core/search/vector-stores.js +2 -664
- package/dist/core/search/vectorization-metadata.js +1 -81
- package/dist/core/sentry/helpers.js +1 -272
- package/dist/core/sentry/instrument.js +1 -387
- package/dist/core/session/session-state.js +1 -260
- package/dist/core/shared/agent-session-context.js +1 -133
- package/dist/core/shared/author.js +1 -1172
- package/dist/core/shared/command-types.js +1 -4
- package/dist/core/shared/conflict-markers.js +1 -31
- package/dist/core/shared/constants.js +1 -405
- package/dist/core/shared/errors.js +1 -20
- package/dist/core/shared/html-entity-decode.js +1 -131
- package/dist/core/shared/index.js +1 -12
- package/dist/core/shared/item-metadata-contract.js +1 -89
- package/dist/core/shared/lazy-module.js +1 -16
- package/dist/core/shared/levenshtein.js +1 -61
- package/dist/core/shared/memo.js +1 -19
- package/dist/core/shared/numeric-parsers.js +1 -20
- package/dist/core/shared/option-alias-visibility.js +1 -67
- package/dist/core/shared/primitives.js +1 -127
- package/dist/core/shared/serial-queue.js +1 -79
- package/dist/core/shared/serialization.js +1 -210
- package/dist/core/shared/split-comma-list.js +1 -30
- package/dist/core/shared/text-normalization.js +1 -38
- package/dist/core/shared/time.js +1 -306
- package/dist/core/store/index.js +1 -12
- package/dist/core/store/item-format-migration.js +1 -125
- package/dist/core/store/item-metadata-cache.js +1 -1116
- package/dist/core/store/item-metadata-query-index.js +9 -394
- package/dist/core/store/item-metadata-read-work.js +1 -43
- package/dist/core/store/item-store.js +1 -693
- package/dist/core/store/paths.d.ts +1 -1
- package/dist/core/store/paths.js +1 -214
- package/dist/core/store/settings-read-cache.js +1 -85
- package/dist/core/store/settings-validator.d.ts +1 -0
- package/dist/core/store/settings-validator.js +1 -421
- package/dist/core/store/settings.js +3 -1309
- package/dist/core/store/tracker-preflight.js +1 -141
- package/dist/core/telemetry/consent.js +5 -108
- package/dist/core/telemetry/observability.js +1 -246
- package/dist/core/telemetry/runtime.js +4 -1988
- package/dist/core/test/background-runs.js +4 -945
- package/dist/core/test/item-test-run-tracking.js +1 -63
- package/dist/core/test/run-selectors.js +1 -120
- package/dist/core/validate/estimate-defaults.js +1 -89
- package/dist/core/validate/fix-planning.js +1 -189
- package/dist/core/validate/linked-artifact-reference.js +1 -36
- package/dist/core/validate/missing-by-type.js +1 -34
- package/dist/core/validate/missing-link-owners.js +1 -95
- package/dist/core/validate/stale-file-classification.js +1 -82
- package/dist/mcp/http-server.js +5 -448
- package/dist/mcp/legacy-adapter.js +1 -77
- package/dist/mcp/runtime-capabilities.js +1 -200
- package/dist/mcp/server.js +4 -1233
- package/dist/mcp/tool-definitions.js +1 -804
- package/dist/sdk/actionability.js +1 -13
- package/dist/sdk/agent/closed-domain-contracts.js +1 -161
- package/dist/sdk/agent/command-recovery.js +1 -59
- package/dist/sdk/agent/command-suggestions.js +1 -66
- package/dist/sdk/agent/create-option-policy.js +1 -31
- package/dist/sdk/agent/identity-config.js +1 -106
- package/dist/sdk/agent/item-addressing.js +1 -207
- package/dist/sdk/agent/refusal-closure-census.js +1 -151
- package/dist/sdk/agent/refusal-closure.js +1 -133
- package/dist/sdk/agent/refusal-corpus-contracts.js +1 -193
- package/dist/sdk/agent/refusal-reachability.js +1 -593
- package/dist/sdk/agent/subcommand-recovery.js +1 -83
- package/dist/sdk/agent/task-transcript-contracts.js +1 -197
- package/dist/sdk/agent/tracker-preflight-contracts.js +1 -126
- package/dist/sdk/agent-capability-contracts.js +1 -428
- package/dist/sdk/agent-session-context.js +1 -9
- package/dist/sdk/annotations.js +1 -448
- package/dist/sdk/author-attribution.js +1 -468
- package/dist/sdk/authoring.js +1 -20
- package/dist/sdk/cli-bootstrap.js +1 -838
- package/dist/sdk/cli-contracts/agent-output-contracts.d.ts +1 -1
- package/dist/sdk/cli-contracts/agent-output-contracts.js +4 -476
- package/dist/sdk/cli-contracts/bootstrap-command-scanner.js +1 -79
- package/dist/sdk/cli-contracts/command-aliases.d.ts +4 -0
- package/dist/sdk/cli-contracts/command-aliases.js +1 -107
- package/dist/sdk/cli-contracts/command-exit-contracts.js +1 -151
- package/dist/sdk/cli-contracts/commander-mutation-options.js +1 -1081
- package/dist/sdk/cli-contracts/commander-types.js +1 -159
- package/dist/sdk/cli-contracts/completeness.js +1 -393
- package/dist/sdk/cli-contracts/enum-contracts.d.ts +1 -1
- package/dist/sdk/cli-contracts/enum-contracts.js +1 -222
- package/dist/sdk/cli-contracts/flag-contracts.js +1 -1947
- package/dist/sdk/cli-contracts/flag-lexicon-contracts.js +1 -347
- package/dist/sdk/cli-contracts/grammar-contracts.js +1 -986
- package/dist/sdk/cli-contracts/registration-helpers.js +7 -990
- package/dist/sdk/cli-contracts/runtime-contracts.d.ts +5 -0
- package/dist/sdk/cli-contracts/runtime-contracts.js +1 -2090
- package/dist/sdk/cli-contracts/schemas/assurance-definition.js +1 -324
- package/dist/sdk/cli-contracts/string-lists.js +1 -16
- package/dist/sdk/cli-contracts/tool-option-contracts.js +1 -461
- package/dist/sdk/cli-contracts/tool-parameter-tables.js +1 -2167
- package/dist/sdk/cli-contracts/tool-schema.d.ts +3 -1
- package/dist/sdk/cli-contracts/tool-schema.js +1 -1664
- package/dist/sdk/cli-contracts/typed-action-inputs.js +1 -4
- package/dist/sdk/cli-contracts.d.ts +1 -1
- package/dist/sdk/cli-contracts.js +1 -17
- package/dist/sdk/cli-program.js +1 -49
- package/dist/sdk/command-line.d.ts +2 -5
- package/dist/sdk/command-line.js +1 -78
- package/dist/sdk/comments.js +1 -133
- package/dist/sdk/compile-cache.js +1 -73
- package/dist/sdk/completion.js +174 -839
- package/dist/sdk/compose.js +1 -1090
- package/dist/sdk/config.js +1 -1204
- package/dist/sdk/context/recency.js +1 -33
- package/dist/sdk/context/semantic-session-attribution.js +1 -82
- package/dist/sdk/context/usage-egress.js +1 -29
- package/dist/sdk/context/usage-ledger.js +3 -185
- package/dist/sdk/context-intent-contracts.js +1 -709
- package/dist/sdk/context-intent-runtime.js +1 -115
- package/dist/sdk/context-packing.js +1 -218
- package/dist/sdk/context-relevance.js +1 -503
- package/dist/sdk/context-signal-store.js +2 -426
- package/dist/sdk/context-usage.js +1 -367
- package/dist/sdk/contracts.js +1 -23
- package/dist/sdk/core-governance.js +1 -14
- package/dist/sdk/core.js +1 -14
- package/dist/sdk/corpus-shape.js +1 -401
- package/dist/sdk/define.js +1 -220
- package/dist/sdk/dependencies.js +1 -718
- package/dist/sdk/dependency-flag-validation.js +1 -156
- package/dist/sdk/dependency-provenance.js +1 -135
- package/dist/sdk/docs.js +1 -14
- package/dist/sdk/duplicates.js +1 -40
- package/dist/sdk/environment/agent-environment.js +1 -466
- package/dist/sdk/environment/host-environment-errors.js +1 -8
- package/dist/sdk/environment/project-runtime-compatibility.js +1 -481
- package/dist/sdk/environment/source-context.js +1 -28
- package/dist/sdk/environment/tracker-preflight.js +1 -9
- package/dist/sdk/error-code-catalog.js +1 -153
- package/dist/sdk/error-runtime.js +1 -58
- package/dist/sdk/errors.js +1 -58
- package/dist/sdk/eval.js +1 -190
- package/dist/sdk/extension/author-manifest.js +1 -92
- package/dist/sdk/extension/bundled-catalog.js +1 -332
- package/dist/sdk/extension/concurrency.js +1 -25
- package/dist/sdk/extension/copy-scope.d.ts +2 -0
- package/dist/sdk/extension/copy-scope.js +2 -0
- package/dist/sdk/extension/custom-field-collisions.js +1 -30
- package/dist/sdk/extension/describe.js +3 -189
- package/dist/sdk/extension/doctor.js +1 -606
- package/dist/sdk/extension/install-plan.d.ts +69 -0
- package/dist/sdk/extension/install-plan.js +2 -0
- package/dist/sdk/extension/install-receipts.d.ts +7 -0
- package/dist/sdk/extension/install-receipts.js +2 -0
- package/dist/sdk/extension/install-runtime.js +3 -313
- package/dist/sdk/extension/install-snapshot.js +1 -66
- package/dist/sdk/extension/install-sources.js +5 -1057
- package/dist/sdk/extension/managed-state.js +2 -252
- package/dist/sdk/extension/migrations.js +2 -174
- package/dist/sdk/extension/output-ownership.js +1 -25
- package/dist/sdk/extension/runtime-summary.js +1 -18
- package/dist/sdk/extension/scaffold.js +4 -2308
- package/dist/sdk/extension/shared.js +1 -147
- package/dist/sdk/extension/source-resolution.js +1 -65
- package/dist/sdk/extension/update-check.js +1 -67
- package/dist/sdk/extension-command-context.js +1 -44
- package/dist/sdk/extension-contracts.js +1 -73
- package/dist/sdk/extension.d.ts +7 -2
- package/dist/sdk/extension.js +1 -2196
- package/dist/sdk/files.js +1 -535
- package/dist/sdk/flag-invocation-contracts.d.ts +0 -5
- package/dist/sdk/flag-invocation-contracts.js +1 -344
- package/dist/sdk/generated/generated-error-code-catalog-part-1.js +1 -2224
- package/dist/sdk/generated/generated-error-code-catalog-part-2.js +1 -2236
- package/dist/sdk/generated-error-code-catalog.js +1 -16
- package/dist/sdk/governance/assurance-action-contracts.js +1 -4
- package/dist/sdk/governance/assurance-action.js +1 -411
- package/dist/sdk/governance/assurance-limits.js +1 -10
- package/dist/sdk/governance/assurance-mutation-error.js +1 -81
- package/dist/sdk/governance/assurance-presets.js +1 -135
- package/dist/sdk/governance/assurance-runtime.js +1 -238
- package/dist/sdk/governance/assurance.js +4 -1182
- package/dist/sdk/governance/boundary-fixtures.js +1 -200
- package/dist/sdk/governance/coverage-assurance.js +1 -60
- package/dist/sdk/governance/defect-recurrence-coverage.js +1 -188
- package/dist/sdk/governance/defect-recurrence-graph.js +1 -120
- package/dist/sdk/governance/defect-recurrence-signals.js +1 -24
- package/dist/sdk/governance/defect-recurrence.js +1 -623
- package/dist/sdk/governance/extension-host-version.js +1 -115
- package/dist/sdk/governance/gc.js +1 -368
- package/dist/sdk/governance/health.js +1 -2367
- package/dist/sdk/governance/hierarchy-validation.js +1 -108
- package/dist/sdk/governance/linked-file-report.js +1 -71
- package/dist/sdk/governance/merge-receipt-history.js +1 -206
- package/dist/sdk/governance/provenance-health.js +1 -282
- package/dist/sdk/governance/reindex.js +4 -761
- package/dist/sdk/governance/stale-work.js +1 -82
- package/dist/sdk/governance/status-role-diagnostics.js +1 -35
- package/dist/sdk/governance/storage-integrity.js +1 -280
- package/dist/sdk/governance/tracked-runtime-cache.js +1 -106
- package/dist/sdk/governance/upgrade.js +2 -327
- package/dist/sdk/governance/validate-history-drift.js +1 -81
- package/dist/sdk/governance/validate-item-reader.js +1 -17
- package/dist/sdk/governance/validate-normalization.js +1 -36
- package/dist/sdk/governance/validate.d.ts +3 -1
- package/dist/sdk/governance/validate.js +1 -2261
- package/dist/sdk/governance/workflow-completeness.d.ts +32 -0
- package/dist/sdk/governance/workflow-completeness.js +2 -0
- package/dist/sdk/governance/workflow-policy.d.ts +47 -0
- package/dist/sdk/governance/workflow-policy.js +4 -0
- package/dist/sdk/governance/workspace-position.js +1 -163
- package/dist/sdk/governance.d.ts +2 -0
- package/dist/sdk/governance.js +1 -31
- package/dist/sdk/graph/adapter.js +1 -181
- package/dist/sdk/graph/analytics.js +1 -474
- package/dist/sdk/graph/assembly.js +1 -377
- package/dist/sdk/graph/cache.js +1 -160
- package/dist/sdk/graph/centrality.js +1 -258
- package/dist/sdk/graph/conformance.js +1 -130
- package/dist/sdk/graph/deadline-scheduling.d.ts +90 -0
- package/dist/sdk/graph/deadline-scheduling.js +2 -0
- package/dist/sdk/graph/durable-cache.d.ts +1 -1
- package/dist/sdk/graph/durable-cache.js +1 -361
- package/dist/sdk/graph/governance-contracts.js +1 -34
- package/dist/sdk/graph/governance.d.ts +13 -1
- package/dist/sdk/graph/governance.js +1 -735
- package/dist/sdk/graph/hierarchy-integrity.js +1 -310
- package/dist/sdk/graph/index.d.ts +3 -2
- package/dist/sdk/graph/index.js +1 -26
- package/dist/sdk/graph/lifecycle-coverage.d.ts +32 -0
- package/dist/sdk/graph/lifecycle-coverage.js +2 -0
- package/dist/sdk/graph/mutation-advisory.js +1 -176
- package/dist/sdk/graph/remediation.js +1 -197
- package/dist/sdk/graph/run.d.ts +15 -1
- package/dist/sdk/graph/run.js +1 -768
- package/dist/sdk/graph/scheduling.js +1 -124
- package/dist/sdk/graph/traversal.js +1 -284
- package/dist/sdk/graph/workspace-relationship-kind-registry.js +1 -27
- package/dist/sdk/graph.js +1 -15
- package/dist/sdk/guide-topics.js +1 -707
- package/dist/sdk/guide.js +1 -279
- package/dist/sdk/history/maintenance.d.ts +71 -0
- package/dist/sdk/history/maintenance.js +2 -0
- package/dist/sdk/history/salvage.js +2 -247
- package/dist/sdk/history/subject.d.ts +19 -0
- package/dist/sdk/history/subject.js +2 -0
- package/dist/sdk/history-analytics.js +1 -282
- package/dist/sdk/history-compact.js +1 -527
- package/dist/sdk/history-mcp.js +1 -68
- package/dist/sdk/history-provenance.js +1 -182
- package/dist/sdk/history-read.js +1 -96
- package/dist/sdk/history-redact.d.ts +1 -14
- package/dist/sdk/history-redact.js +1 -643
- package/dist/sdk/history-repair.d.ts +5 -0
- package/dist/sdk/history-repair.js +1 -657
- package/dist/sdk/improvement-ledger-validation.js +1 -62
- package/dist/sdk/improvement-ledger.js +2 -257
- package/dist/sdk/index.d.ts +4 -1
- package/dist/sdk/index.js +1 -183
- package/dist/sdk/init-agent-guidance.js +2 -496
- package/dist/sdk/init.js +2 -820
- package/dist/sdk/invocation-author.js +1 -39
- package/dist/sdk/item-children.js +1 -90
- package/dist/sdk/item-metadata-index.js +1 -15
- package/dist/sdk/item-schedule.js +1 -27
- package/dist/sdk/item-transaction.js +1 -396
- package/dist/sdk/learnings.js +1 -29
- package/dist/sdk/lifecycle/append.js +2 -53
- package/dist/sdk/lifecycle/claim.d.ts +2 -0
- package/dist/sdk/lifecycle/claim.js +1 -242
- package/dist/sdk/lifecycle/close-many.js +1 -438
- package/dist/sdk/lifecycle/close.js +1 -468
- package/dist/sdk/lifecycle/copy.js +1 -212
- package/dist/sdk/lifecycle/create.js +1 -1949
- package/dist/sdk/lifecycle/delete.js +1 -45
- package/dist/sdk/lifecycle/event-validation-messages.js +1 -91
- package/dist/sdk/lifecycle/focus.js +1 -91
- package/dist/sdk/lifecycle/hierarchy-mutation-lock.js +1 -24
- package/dist/sdk/lifecycle/legacy-none-tokens.js +1 -149
- package/dist/sdk/lifecycle/lifecycle-transitions.js +1 -40
- package/dist/sdk/lifecycle/mcp-actions.d.ts +2 -0
- package/dist/sdk/lifecycle/mcp-actions.js +1 -51
- package/dist/sdk/lifecycle/metadata-normalizers.js +1 -46
- package/dist/sdk/lifecycle/mutation-command-options.js +1 -9
- package/dist/sdk/lifecycle/plan.js +1 -1606
- package/dist/sdk/lifecycle/recurrence-parsers.d.ts +1 -1
- package/dist/sdk/lifecycle/recurrence-parsers.js +1 -147
- package/dist/sdk/lifecycle/reopen.js +1 -68
- package/dist/sdk/lifecycle/repeatable-metadata-parsers.js +1 -143
- package/dist/sdk/lifecycle/restore.d.ts +1 -0
- package/dist/sdk/lifecycle/restore.js +1 -330
- package/dist/sdk/lifecycle/shared-unset-fields.js +1 -295
- package/dist/sdk/lifecycle/update-many.js +1 -861
- package/dist/sdk/lifecycle/update.js +1 -2136
- package/dist/sdk/lifecycle-completion.js +1 -92
- package/dist/sdk/lifecycle-policy.js +1 -120
- package/dist/sdk/linked-artifacts.js +1 -530
- package/dist/sdk/mcp/apps.js +4 -133
- package/dist/sdk/mcp/authorization.js +1 -404
- package/dist/sdk/mcp/discovery.js +1 -494
- package/dist/sdk/mcp/interactions.js +1 -336
- package/dist/sdk/mcp/protocol.d.ts +7 -0
- package/dist/sdk/mcp/protocol.js +1 -184
- package/dist/sdk/mcp/skills.js +1 -389
- package/dist/sdk/mcp/subscriptions.js +1 -211
- package/dist/sdk/mcp/tasks.js +2 -430
- package/dist/sdk/mcp/transport.js +1 -260
- package/dist/sdk/merge/abandoned-receipts.js +1 -46
- package/dist/sdk/merge/driver.js +1 -212
- package/dist/sdk/merge/index.js +1 -16
- package/dist/sdk/merge/install.js +3 -561
- package/dist/sdk/merge/receipt-file-boundary.js +1 -46
- package/dist/sdk/merge/receipt-operation.js +1 -96
- package/dist/sdk/merge/receipt-schema.js +1 -397
- package/dist/sdk/merge/receipts.js +4 -525
- package/dist/sdk/merge/reconcile.js +1 -352
- package/dist/sdk/merge/three-way.js +3 -775
- package/dist/sdk/merge.js +1 -10
- package/dist/sdk/mutation-events.js +1 -292
- package/dist/sdk/mutation-guard.js +1 -216
- package/dist/sdk/notes.js +1 -139
- package/dist/sdk/output-contracts.js +1 -172
- package/dist/sdk/output-projection.js +1 -430
- package/dist/sdk/output-token-accounting.js +1 -140
- package/dist/sdk/output.js +1 -64
- package/dist/sdk/ownership-source.js +1 -21
- package/dist/sdk/package-import-adapters.js +1 -442
- package/dist/sdk/package-migrations.js +1 -20
- package/dist/sdk/package-runtime-options.js +1 -80
- package/dist/sdk/pagination.js +1 -162
- package/dist/sdk/profile.js +1 -454
- package/dist/sdk/provenance.js +1 -258
- package/dist/sdk/public-surface.json +1 -1
- package/dist/sdk/query/activity.js +1 -401
- package/dist/sdk/query/aggregate.d.ts +16 -0
- package/dist/sdk/query/aggregate.js +1 -390
- package/dist/sdk/query/calendar.js +1 -964
- package/dist/sdk/query/complete-list.js +1 -215
- package/dist/sdk/query/context.js +1 -1702
- package/dist/sdk/query/duplicate-candidates.js +1 -70
- package/dist/sdk/query/get.js +1 -450
- package/dist/sdk/query/history.js +1 -186
- package/dist/sdk/query/item-filter-options.js +1 -9
- package/dist/sdk/query/light-metadata.js +1 -18
- package/dist/sdk/query/list-filter-shared.js +1 -81
- package/dist/sdk/query/list.js +1 -1304
- package/dist/sdk/query/multi-value-filters.js +1 -78
- package/dist/sdk/query/next.js +1 -513
- package/dist/sdk/query/parsers.js +1 -44
- package/dist/sdk/query/projection-contracts.js +1 -184
- package/dist/sdk/query/search-contracts.d.ts +27 -0
- package/dist/sdk/query/search-contracts.js +1 -230
- package/dist/sdk/query/search-pagination.js +1 -35
- package/dist/sdk/query/search-rendering.js +1 -105
- package/dist/sdk/query/search.d.ts +6 -4
- package/dist/sdk/query/search.js +2 -1987
- package/dist/sdk/query.js +1 -35
- package/dist/sdk/read-output/continuation.js +1 -194
- package/dist/sdk/read-output-budget.js +1 -135
- package/dist/sdk/read-output-contracts.d.ts +1 -1
- package/dist/sdk/read-output-contracts.js +1 -1225
- package/dist/sdk/read-output-rows.js +1 -186
- package/dist/sdk/read-output-session.js +1 -208
- package/dist/sdk/relationship-analytics.js +1 -410
- package/dist/sdk/relationship-context.js +1 -284
- package/dist/sdk/relationship-history.js +4 -519
- package/dist/sdk/relationship-kinds/contract.js +1 -22
- package/dist/sdk/relationship-migration.js +1 -77
- package/dist/sdk/relationships.js +1 -579
- package/dist/sdk/reproducibility/process.js +1 -87
- package/dist/sdk/runtime-action-aliases.js +1 -59
- package/dist/sdk/runtime-extended-actions.js +1 -177
- package/dist/sdk/runtime-input.js +1 -614
- package/dist/sdk/runtime-primitives.js +1 -84
- package/dist/sdk/runtime-public-contracts.d.ts +2 -1
- package/dist/sdk/runtime-public-contracts.js +1 -4
- package/dist/sdk/runtime-stats-options.js +1 -38
- package/dist/sdk/runtime.d.ts +4 -0
- package/dist/sdk/runtime.js +1 -1928
- package/dist/sdk/scheduling-shortcuts.js +1 -63
- package/dist/sdk/schema-migration.js +3 -856
- package/dist/sdk/schema.d.ts +2 -1
- package/dist/sdk/schema.js +1 -1376
- package/dist/sdk/search-refresh-worker.js +1 -18
- package/dist/sdk/similarity-scoring.js +1 -53
- package/dist/sdk/similarity.js +1 -326
- package/dist/sdk/start-task-status.js +1 -14
- package/dist/sdk/stats.js +1 -270
- package/dist/sdk/structured-mutations.js +1 -580
- package/dist/sdk/telemetry-flush.js +1 -13
- package/dist/sdk/telemetry.js +1 -405
- package/dist/sdk/templates.js +2 -292
- package/dist/sdk/test/batch.js +2 -509
- package/dist/sdk/test/entry.js +1 -70
- package/dist/sdk/test/execution.js +6 -2211
- package/dist/sdk/test/linked-command-detection.js +1 -546
- package/dist/sdk/test/measurements.js +1 -91
- package/dist/sdk/test/parsers.js +1 -545
- package/dist/sdk/test/runs.js +1 -193
- package/dist/sdk/test/trust.js +2 -206
- package/dist/sdk/testing.js +4 -1391
- package/dist/sdk/traceability/runtime-files-lookup.js +1 -32
- package/dist/sdk/traceability/source-traceability.js +1 -292
- package/dist/sdk/tracker-root-discovery.js +1 -9
- package/dist/sdk/workspace-contracts-cache.js +1 -28
- package/dist/sdk/workspace-contracts.js +1 -33
- package/dist/sdk/workspace-memory.js +2 -318
- package/dist/sdk/workspace-recipe.js +1 -71
- package/dist/sdk/workspace-snapshot.js +4 -613
- package/dist/sdk/workspace-transaction-gc.js +1 -149
- package/dist/sdk/workspace-transaction.js +2 -363
- package/dist/sdk/workspace.js +3 -127
- package/dist/types/index.js +1 -9
- package/dist/types.d.ts +4 -0
- package/dist/types.js +1 -245
- package/docs/AGENT_GUIDE.md +1 -1
- package/docs/COMMANDS.md +10 -0
- package/docs/CONFIGURATION.md +12 -1
- package/docs/CONTEXT_ALGORITHM_PORTFOLIO.md +167 -0
- package/docs/DIAGNOSTIC_OUTPUT_CONTRACTS.md +9 -3
- package/docs/EXTENSIONS.md +1 -0
- package/docs/HISTORY_MAINTENANCE.md +125 -0
- package/docs/MCP_2026_07_28_CONFORMANCE.md +20 -10
- package/docs/PACKAGE_EVIDENCE.md +92 -0
- package/docs/PLANNING_MEASUREMENTS.md +183 -0
- package/docs/README.md +4 -0
- package/docs/READ_OUTPUT_CONTRACTS.md +5 -0
- package/docs/SCRIPTING.md +21 -0
- package/docs/TESTING.md +33 -0
- package/docs/WORKFLOW_POLICIES.md +129 -0
- package/docs/generated/FLAG_LEXICON_BUDGETS.md +7 -7
- package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +11 -5
- package/marketplace.json +2 -2
- package/package.json +5 -5
- 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/scripts/finalize-build.mjs +45 -3
- package/dist/cli-bundle/chunks/chunk-AZDFJGH6.js +0 -9
- package/dist/cli-bundle/chunks/chunk-CKOL5U7J.js +0 -77
- package/dist/cli-bundle/chunks/chunk-JX4DH4WC.js +0 -3
- package/dist/cli-bundle/chunks/chunk-OF76PSDQ.js +0 -5
- package/dist/cli-bundle/chunks/chunk-QUWFABDM.js +0 -232
- package/dist/cli-bundle/chunks/chunk-QWRRFYK3.js +0 -2
- package/dist/cli-bundle/chunks/chunk-RWWBOWJ5.js +0 -2
- package/dist/cli-bundle/chunks/chunk-WGAOYSYH.js +0 -40
- package/dist/cli-bundle/chunks/chunk-WI2AYPA7.js +0 -2
- package/dist/cli-bundle/chunks/register-list-query-5JL52D33.js +0 -11
- package/dist/cli-bundle/chunks/register-mutation-ZPG7KMVP.js +0 -20
- package/dist/cli-bundle/chunks/register-operations-I5W3KMGB.js +0 -2
- package/dist/cli-bundle/chunks/register-setup-H4SVAVX7.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-AL5ON6K6.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-CHC2IDXA.js +0 -6
- package/dist/cli-bundle/focused-chunks/chunk-CK6QFAPZ.js +0 -20
- package/dist/cli-bundle/focused-chunks/chunk-EZR3GHFE.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-H2WJWK7L.js +0 -56
- package/dist/cli-bundle/focused-chunks/chunk-NTPY5GSO.js +0 -3
- package/dist/cli-bundle/focused-chunks/chunk-P5O7MP5Y.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-PDMW2E36.js +0 -177
- package/dist/cli-bundle/focused-chunks/chunk-PEFABF7E.js +0 -26
- package/dist/cli-bundle/focused-chunks/chunk-QTVCLMSZ.js +0 -6
- package/dist/cli-bundle/focused-chunks/chunk-QTZRBUYA.js +0 -8
- package/dist/cli-bundle/focused-chunks/chunk-SGQST3PQ.js +0 -12
- package/dist/cli-bundle/focused-chunks/chunk-UKHASMMP.js +0 -10
- package/dist/cli-bundle/focused-chunks/chunk-ZPEKQHOC.js +0 -2
|
@@ -1,736 +1,2 @@
|
|
|
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]="27de9c62-434c-566d-a000-54eddb9a16a9")}catch(e){}}();
|
|
3
|
-
import { findRedundantRelationshipEdges, isTransitiveKind, orientTransitiveEdge, transitiveFamilyKey, } from "./analytics.js";
|
|
4
|
-
import { findRelationshipCutStructure } from "./centrality.js";
|
|
5
|
-
import { getWorkspaceHierarchyIntegrity } from "./assembly.js";
|
|
6
|
-
import { RELATIONSHIP_AUDIT_FINDING_CODES, } from "./governance-contracts.js";
|
|
7
|
-
/** Default bounded sample size applied to every finding family. */
|
|
8
|
-
const DEFAULT_MAX_SAMPLE_SIZE = 25;
|
|
9
|
-
/** Canonical item type and title marker that identify explicit outcomes. */
|
|
10
|
-
const OUTCOME_MILESTONE_TYPE = "milestone";
|
|
11
|
-
const OUTCOME_MILESTONE_TITLE_PREFIX = "outcome milestone:";
|
|
12
|
-
/** Relationship kinds that preserve why work exists or how evidence supports it. */
|
|
13
|
-
const SEMANTIC_CONTEXT_KINDS = new Set([
|
|
14
|
-
"discovered_from",
|
|
15
|
-
"incident_from",
|
|
16
|
-
"recurs_from",
|
|
17
|
-
"supersedes",
|
|
18
|
-
"verifies",
|
|
19
|
-
]);
|
|
20
|
-
/** Return whether a relationship kind carries provenance or verification context. */
|
|
21
|
-
export function isSemanticContextKind(kind) {
|
|
22
|
-
return SEMANTIC_CONTEXT_KINDS.has(kind);
|
|
23
|
-
}
|
|
24
|
-
/** Rank used to order findings by decreasing severity. */
|
|
25
|
-
const SEVERITY_RANK = {
|
|
26
|
-
error: 0,
|
|
27
|
-
warning: 1,
|
|
28
|
-
info: 2,
|
|
29
|
-
};
|
|
30
|
-
/** Normalize optional exemption identifiers into a case-insensitive lookup set. */
|
|
31
|
-
function normalizeExemptionSet(values) {
|
|
32
|
-
return new Set((values ?? [])
|
|
33
|
-
.filter((value) => typeof value === "string")
|
|
34
|
-
.map((value) => value.trim().toLowerCase()));
|
|
35
|
-
}
|
|
36
|
-
/** Build one finding with deterministic bounded sampling over sorted subjects. */
|
|
37
|
-
function buildFinding(code, severity, policy, describe, subjects, maxSampleSize, remediation, evidence) {
|
|
38
|
-
const sorted = [...subjects].sort((left, right) => left.localeCompare(right));
|
|
39
|
-
return {
|
|
40
|
-
code,
|
|
41
|
-
severity,
|
|
42
|
-
policy,
|
|
43
|
-
message: describe(sorted.length),
|
|
44
|
-
count: sorted.length,
|
|
45
|
-
sample: sorted.slice(0, maxSampleSize),
|
|
46
|
-
sample_truncated: sorted.length > maxSampleSize,
|
|
47
|
-
remediation,
|
|
48
|
-
...(evidence === undefined
|
|
49
|
-
? {}
|
|
50
|
-
: {
|
|
51
|
-
evidence: [...evidence]
|
|
52
|
-
.sort((left, right) => left.localeCompare(right))
|
|
53
|
-
.slice(0, maxSampleSize),
|
|
54
|
-
}),
|
|
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
|
-
}
|
|
99
|
-
/** Resolve the ordering-only successor adjacency for cycle analysis. */
|
|
100
|
-
function buildOrderingAdjacency(assembly) {
|
|
101
|
-
const registry = assembly.graph.registry();
|
|
102
|
-
const successors = new Map();
|
|
103
|
-
for (const edge of assembly.graph.edges()) {
|
|
104
|
-
const definition = registry.require(edge.kind);
|
|
105
|
-
if (!definition.ordering)
|
|
106
|
-
continue;
|
|
107
|
-
// Orient every ordering edge predecessor -> successor so inverse spellings
|
|
108
|
-
// (blocked_by/blocks) land in one canonical execution-order digraph.
|
|
109
|
-
const [predecessor, successor] = (definition.precedence ?? "source_before_target") ===
|
|
110
|
-
"target_before_source"
|
|
111
|
-
? [edge.target, edge.source]
|
|
112
|
-
: [edge.source, edge.target];
|
|
113
|
-
const adjacent = successors.get(predecessor);
|
|
114
|
-
if (adjacent)
|
|
115
|
-
adjacent.push(successor);
|
|
116
|
-
else
|
|
117
|
-
successors.set(predecessor, [successor]);
|
|
118
|
-
}
|
|
119
|
-
for (const adjacent of successors.values())
|
|
120
|
-
adjacent.sort((left, right) => left.localeCompare(right));
|
|
121
|
-
return successors;
|
|
122
|
-
}
|
|
123
|
-
/** Register one newly discovered Tarjan node. */
|
|
124
|
-
function registerTarjanNode(node, state) {
|
|
125
|
-
state.indexes.set(node, state.nextIndex);
|
|
126
|
-
state.lowLinks.set(node, state.nextIndex);
|
|
127
|
-
state.nextIndex += 1;
|
|
128
|
-
state.stack.push(node);
|
|
129
|
-
state.onStack.add(node);
|
|
130
|
-
}
|
|
131
|
-
/** Visit one pending successor, returning whether the frame had work left. */
|
|
132
|
-
function visitNextTarjanNeighbor(frame, frames, successors, state) {
|
|
133
|
-
const neighbors = successors.get(frame.node) ?? [];
|
|
134
|
-
if (frame.neighborIndex >= neighbors.length)
|
|
135
|
-
return false;
|
|
136
|
-
const neighbor = neighbors[frame.neighborIndex];
|
|
137
|
-
frame.neighborIndex += 1;
|
|
138
|
-
if (!state.indexes.has(neighbor)) {
|
|
139
|
-
registerTarjanNode(neighbor, state);
|
|
140
|
-
frames.push({ node: neighbor, neighborIndex: 0 });
|
|
141
|
-
}
|
|
142
|
-
else if (state.onStack.has(neighbor)) {
|
|
143
|
-
state.lowLinks.set(frame.node, Math.min(state.lowLinks.get(frame.node), state.indexes.get(neighbor)));
|
|
144
|
-
}
|
|
145
|
-
return true;
|
|
146
|
-
}
|
|
147
|
-
/** Complete one Tarjan frame and emit its component when it is a root. */
|
|
148
|
-
function completeTarjanFrame(frame, frames, successors, state) {
|
|
149
|
-
frames.pop();
|
|
150
|
-
const parent = frames.at(-1);
|
|
151
|
-
if (parent)
|
|
152
|
-
state.lowLinks.set(parent.node, Math.min(state.lowLinks.get(parent.node), state.lowLinks.get(frame.node)));
|
|
153
|
-
if (state.lowLinks.get(frame.node) !== state.indexes.get(frame.node))
|
|
154
|
-
return;
|
|
155
|
-
const component = [];
|
|
156
|
-
while (true) {
|
|
157
|
-
const member = state.stack.pop();
|
|
158
|
-
state.onStack.delete(member);
|
|
159
|
-
component.push(member);
|
|
160
|
-
if (member === frame.node)
|
|
161
|
-
break;
|
|
162
|
-
}
|
|
163
|
-
if (component.length > 1 ||
|
|
164
|
-
(component.length === 1 &&
|
|
165
|
-
successors.get(component[0])?.includes(component[0])))
|
|
166
|
-
state.components.push(component.sort((left, right) => left.localeCompare(right)));
|
|
167
|
-
}
|
|
168
|
-
/**
|
|
169
|
-
* Enumerate strongly connected components with more than one member (or a
|
|
170
|
-
* self-loop) over an ordering-only successor digraph using an iterative Tarjan
|
|
171
|
-
* traversal. Only order-bearing kinds should participate: associative kinds
|
|
172
|
-
* such as `related` cannot create execution-order contradictions and stay out
|
|
173
|
-
* of cycle analysis by policy. Exported so incremental consumers (mutation
|
|
174
|
-
* advisories) share one cycle semantics with the workspace audit.
|
|
175
|
-
*/
|
|
176
|
-
export function collectOrderingCycles(successors) {
|
|
177
|
-
const state = {
|
|
178
|
-
indexes: new Map(),
|
|
179
|
-
lowLinks: new Map(),
|
|
180
|
-
onStack: new Set(),
|
|
181
|
-
stack: [],
|
|
182
|
-
components: [],
|
|
183
|
-
nextIndex: 0,
|
|
184
|
-
};
|
|
185
|
-
const roots = [...successors.keys()].sort((left, right) => left.localeCompare(right));
|
|
186
|
-
for (const root of roots) {
|
|
187
|
-
if (state.indexes.has(root))
|
|
188
|
-
continue;
|
|
189
|
-
// Explicit frame stack keeps the traversal safe on deep dependency chains.
|
|
190
|
-
const frames = [{ node: root, neighborIndex: 0 }];
|
|
191
|
-
registerTarjanNode(root, state);
|
|
192
|
-
while (frames.length > 0) {
|
|
193
|
-
const frame = frames.at(-1);
|
|
194
|
-
if (visitNextTarjanNeighbor(frame, frames, successors, state))
|
|
195
|
-
continue;
|
|
196
|
-
completeTarjanFrame(frame, frames, successors, state);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
return state.components.sort((left, right) => left[0].localeCompare(right[0]));
|
|
200
|
-
}
|
|
201
|
-
/** Format one dangling reference as a compact deterministic evidence string. */
|
|
202
|
-
function formatDanglingReference(reference) {
|
|
203
|
-
return `${reference.holder_id} -> ${reference.target_id} (${reference.kind})`;
|
|
204
|
-
}
|
|
205
|
-
/** Append one finding only when its subject collection is non-empty. */
|
|
206
|
-
function appendFinding(findings, subjects, maxSampleSize, code, severity, policy, describe, remediation) {
|
|
207
|
-
if (subjects.length === 0)
|
|
208
|
-
return;
|
|
209
|
-
findings.push(buildFinding(code, severity, policy, describe, subjects, maxSampleSize, remediation));
|
|
210
|
-
}
|
|
211
|
-
/** Collect dangling-reference and retired-sentinel governance findings. */
|
|
212
|
-
function collectIntegrityFindings(assembly, isTerminal, maxSampleSize) {
|
|
213
|
-
const findings = [];
|
|
214
|
-
const references = [
|
|
215
|
-
...assembly.dangling.active,
|
|
216
|
-
...assembly.dangling.legacy_terminal,
|
|
217
|
-
].filter((reference) => !reference.no_active_blocker_sentinel);
|
|
218
|
-
appendFinding(findings, references
|
|
219
|
-
.filter((reference) => !isTerminal(reference.holder_status))
|
|
220
|
-
.map(formatDanglingReference), maxSampleSize, "missing_reference_active", "error", "Active items must not reference absent targets; missing prerequisites distort scheduling.", (count) => `${count} active dangling reference(s) to absent items`, "Restore or re-create the referenced item, or remove/retype the reference on the holder.");
|
|
221
|
-
appendFinding(findings, references
|
|
222
|
-
.filter((reference) => isTerminal(reference.holder_status))
|
|
223
|
-
.map(formatDanglingReference), maxSampleSize, "missing_reference_terminal", "info", "Terminal items may retain historical references, but absent targets remain unexplainable context.", (count) => `${count} terminal dangling reference(s) to absent items`, "Leave as history debt or clean up in a dedicated changelog-safe closed-item batch.");
|
|
224
|
-
appendFinding(findings, assembly.dangling.no_active_blocker_sentinels.map(formatDanglingReference), maxSampleSize, "legacy_no_blocker_sentinel", "info", "The pre-structured-dependency no-active-blocker sentinel is retired; real edges carry blocker state.", (count) => `${count} legacy no-active-blocker sentinel reference(s)`, "Delete the sentinel reference; blocker state is derived from blocked_by edges.");
|
|
225
|
-
return findings;
|
|
226
|
-
}
|
|
227
|
-
/** Decide whether one audited node still participates in active scheduling. */
|
|
228
|
-
function isActiveAuditMember(member, nodeStates) {
|
|
229
|
-
const state = nodeStates.get(member);
|
|
230
|
-
// A trimmed or custom assembly may omit a member's details. Treat that
|
|
231
|
-
// unknown lifecycle conservatively as active so contradictions are never
|
|
232
|
-
// silently downgraded to legacy history debt.
|
|
233
|
-
if (!state)
|
|
234
|
-
return true;
|
|
235
|
-
return !state.terminal;
|
|
236
|
-
}
|
|
237
|
-
/** Format the exact contradictory storage pair that manufactures an ordering cycle. */
|
|
238
|
-
export function formatOrderingStorageContradiction(contradiction) {
|
|
239
|
-
return `${contradiction.holder_id} -> ${contradiction.target_id} (blocked_by scalar + ${contradiction.dependency_kind} dependency)`;
|
|
240
|
-
}
|
|
241
|
-
/** Report active and historical scalar-versus-structured ordering contradictions. */
|
|
242
|
-
function collectOrderingStorageContradictionFindings(assembly, maxSampleSize) {
|
|
243
|
-
const active = [];
|
|
244
|
-
const legacy = [];
|
|
245
|
-
for (const contradiction of assembly.orderingContradictions ?? [])
|
|
246
|
-
(contradiction.legacy_terminal ? legacy : active).push(formatOrderingStorageContradiction(contradiction));
|
|
247
|
-
const findings = [];
|
|
248
|
-
appendFinding(findings, active, maxSampleSize, "ordering_storage_contradiction", "error", "A scalar blocker and same-target structured ordering row must not assert opposite precedence.", (count) => `${count} active scalar-versus-structured ordering contradiction(s)`, "Remove the structured dependency row that points the holder before its scalar blocker, or replace both representations with one unambiguous ordering edge.");
|
|
249
|
-
appendFinding(findings, legacy, maxSampleSize, "legacy_ordering_storage_contradiction", "info", "Terminal scalar-versus-structured ordering contradictions are exact historical storage defects with no live scheduling effect.", (count) => `${count} terminal scalar-versus-structured ordering contradiction(s)`, "Remove the contradictory blocks dependency row in a dedicated changelog-safe closed-item batch; preserve the scalar blocker as the historical precedence record.");
|
|
250
|
-
return findings;
|
|
251
|
-
}
|
|
252
|
-
/** Collect ordering-cycle and lifecycle-block governance findings. */
|
|
253
|
-
function collectOrderingFindings(assembly, nodeStates, isBlocked, maxSampleSize) {
|
|
254
|
-
const findings = [];
|
|
255
|
-
const orderingAdjacency = buildOrderingAdjacency(assembly);
|
|
256
|
-
for (const cycle of collectOrderingCycles(orderingAdjacency)) {
|
|
257
|
-
// A cycle whose members are all terminal is a historical contradiction:
|
|
258
|
-
// it cannot make live work unschedulable, and "repairing" it would touch
|
|
259
|
-
// closed items (changelog drag), so it downgrades to informational debt.
|
|
260
|
-
const active = cycle.some((member) => isActiveAuditMember(member, nodeStates));
|
|
261
|
-
const cycleMembers = new Set(cycle.map((member) => member.toLowerCase()));
|
|
262
|
-
const contradictionEvidence = (assembly.orderingContradictions ?? [])
|
|
263
|
-
.filter((row) => cycleMembers.has(row.holder_id.toLowerCase()) &&
|
|
264
|
-
cycleMembers.has(row.target_id.toLowerCase()))
|
|
265
|
-
.map(formatOrderingStorageContradiction);
|
|
266
|
-
findings.push(active
|
|
267
|
-
? buildFinding("ordering_cycle", "error", "Order-bearing relationship kinds must stay acyclic; a cycle makes every member unschedulable.", (count) => `execution-order cycle across ${count} item(s)`, cycle, maxSampleSize, "Remove or retype one ordering edge inside the cycle, or split the work so precedence is linear.", contradictionEvidence.length === 0
|
|
268
|
-
? undefined
|
|
269
|
-
: contradictionEvidence)
|
|
270
|
-
: buildFinding("legacy_ordering_cycle", "info", "Ordering cycles between exclusively terminal items are historical contradictions with no scheduling effect.", (count) => `terminal-only execution-order cycle across ${count} item(s)`, cycle, maxSampleSize, "Leave as history debt, or clean up in a dedicated changelog-safe closed-item batch; never repair closed items ad hoc.", contradictionEvidence.length === 0
|
|
271
|
-
? undefined
|
|
272
|
-
: contradictionEvidence));
|
|
273
|
-
}
|
|
274
|
-
const predecessorsByNode = new Map();
|
|
275
|
-
for (const [predecessor, adjacent] of orderingAdjacency)
|
|
276
|
-
for (const successor of adjacent) {
|
|
277
|
-
const predecessors = predecessorsByNode.get(successor);
|
|
278
|
-
if (predecessors)
|
|
279
|
-
predecessors.push(predecessor);
|
|
280
|
-
else
|
|
281
|
-
predecessorsByNode.set(successor, [predecessor]);
|
|
282
|
-
}
|
|
283
|
-
const staleLifecycleBlocks = [...nodeStates.values()]
|
|
284
|
-
.filter((state) => !state.missing &&
|
|
285
|
-
!state.terminal &&
|
|
286
|
-
isBlocked(state.status) &&
|
|
287
|
-
!(predecessorsByNode.get(state.id) ?? []).some((predecessor) => {
|
|
288
|
-
const predecessorState = nodeStates.get(predecessor);
|
|
289
|
-
// A trimmed/custom assembly may omit predecessor details. Treat that
|
|
290
|
-
// unknown state conservatively as potentially open instead of
|
|
291
|
-
// falsely declaring the lifecycle block stale.
|
|
292
|
-
return predecessorState === undefined || !predecessorState.terminal;
|
|
293
|
-
}))
|
|
294
|
-
.map((state) => state.id);
|
|
295
|
-
appendFinding(findings, staleLifecycleBlocks, maxSampleSize, "stale_lifecycle_block", "warning", "A blocked lifecycle status must be backed by at least one open ordering predecessor.", (count) => `${count} status-blocked item(s) with no open blocker edge`, 'Add a local blocker edge, declare an external blocker with --dep "id=<external-ref>,kind=blocked_by,source_kind=external", or move the item back to an active status.');
|
|
296
|
-
return findings;
|
|
297
|
-
}
|
|
298
|
-
/** Format one duplicate-edge group as a compact deterministic evidence string. */
|
|
299
|
-
export function formatDuplicateEdgeGroup(group) {
|
|
300
|
-
const kinds = group.edges
|
|
301
|
-
.map((edge) => edge.kind)
|
|
302
|
-
.sort((left, right) => left.localeCompare(right));
|
|
303
|
-
return `${group.from} -> ${group.to} (${kinds.join(" + ")})`;
|
|
304
|
-
}
|
|
305
|
-
/**
|
|
306
|
-
* Collect the groups of parallel same-family stored edges over the directed
|
|
307
|
-
* ordering and hierarchy families. Each family joins a kind with its inverse
|
|
308
|
-
* spelling in semantic orientation, so a reciprocal pair such as
|
|
309
|
-
* `A blocked_by B` plus `B blocks A` collapses onto one oriented endpoint
|
|
310
|
-
* pair. Only groups holding at least two stored spellings are returned, in
|
|
311
|
-
* deterministic orientation order with per-group edges sorted by kind, source,
|
|
312
|
-
* then target — the exact stored identities remediation planning targets.
|
|
313
|
-
*/
|
|
314
|
-
export function collectDuplicateRelationshipEdgeGroups(assembly) {
|
|
315
|
-
const registry = assembly.graph.registry();
|
|
316
|
-
const groups = new Map();
|
|
317
|
-
for (const edge of assembly.graph.edges()) {
|
|
318
|
-
const definition = registry.require(edge.kind);
|
|
319
|
-
if (!isTransitiveKind(definition))
|
|
320
|
-
continue;
|
|
321
|
-
const oriented = orientTransitiveEdge(edge, definition);
|
|
322
|
-
if (oriented.from === oriented.to)
|
|
323
|
-
continue;
|
|
324
|
-
const key = `${transitiveFamilyKey(definition)}::${oriented.from}::${oriented.to}`;
|
|
325
|
-
const group = groups.get(key);
|
|
326
|
-
if (group)
|
|
327
|
-
group.edges.push(edge);
|
|
328
|
-
else
|
|
329
|
-
groups.set(key, { ...oriented, edges: [edge] });
|
|
330
|
-
}
|
|
331
|
-
const duplicated = [...groups.values()].filter((group) => group.edges.length >= 2);
|
|
332
|
-
// Within one oriented group every stored spelling has a distinct kind: a
|
|
333
|
-
// same-kind restatement of the same oriented pair would carry identical
|
|
334
|
-
// endpoints and is deduplicated by edge identity during graph construction.
|
|
335
|
-
for (const group of duplicated)
|
|
336
|
-
group.edges.sort((left, right) => left.kind.localeCompare(right.kind));
|
|
337
|
-
return duplicated.sort((left, right) => left.from.localeCompare(right.from) || left.to.localeCompare(right.to));
|
|
338
|
-
}
|
|
339
|
-
/**
|
|
340
|
-
* Collect duplicate stored-edge findings from the oriented duplicate groups.
|
|
341
|
-
* Transitive-reduction redundancy analysis deliberately skips the direct edge
|
|
342
|
-
* under test, so these exact parallels are invisible there and this is the
|
|
343
|
-
* only surface that reports them.
|
|
344
|
-
*/
|
|
345
|
-
function collectDuplicateEdgeFindings(assembly, nodeStates, maxSampleSize) {
|
|
346
|
-
const active = [];
|
|
347
|
-
const legacy = [];
|
|
348
|
-
for (const group of collectDuplicateRelationshipEdgeGroups(assembly)) {
|
|
349
|
-
const subjects = isActiveAuditMember(group.from, nodeStates) ||
|
|
350
|
-
isActiveAuditMember(group.to, nodeStates)
|
|
351
|
-
? active
|
|
352
|
-
: legacy;
|
|
353
|
-
subjects.push(formatDuplicateEdgeGroup(group));
|
|
354
|
-
}
|
|
355
|
-
const findings = [];
|
|
356
|
-
appendFinding(findings, active, maxSampleSize, "duplicate_edge", "info", "One semantic relationship should be stored once; parallel same-family spellings (reciprocal inverse pairs included) add no information and skew degree metrics.", (count) => `${count} duplicated relationship(s) between active endpoints`, "Remove all but one stored spelling of the relationship; keep the canonical direction the holder declared first.");
|
|
357
|
-
appendFinding(findings, legacy, maxSampleSize, "legacy_duplicate_edge", "info", "Duplicated spellings between exclusively terminal items are historical noise with no scheduling effect.", (count) => `${count} duplicated relationship(s) between terminal endpoints`, "Leave as history debt or clean up in a dedicated changelog-safe closed-item batch.");
|
|
358
|
-
return findings;
|
|
359
|
-
}
|
|
360
|
-
/**
|
|
361
|
-
* Collect storage-integrity findings for raw dependency rows whose exact
|
|
362
|
-
* identity is stored more than once on one holder. Graph edge-identity dedup
|
|
363
|
-
* hides these from every assembled-graph surface (duplicate_edge only covers
|
|
364
|
-
* cross-spelling parallels), so the audit reports them from the pre-assembly
|
|
365
|
-
* scan carried on the assembly.
|
|
366
|
-
*/
|
|
367
|
-
function collectDuplicateRowFindings(assembly, maxSampleSize) {
|
|
368
|
-
const active = [];
|
|
369
|
-
const legacy = [];
|
|
370
|
-
// Public/JSON-parsed assemblies produced before duplicate-row governance may omit this field.
|
|
371
|
-
for (const row of assembly.duplicateRows ?? []) {
|
|
372
|
-
(row.legacy_terminal ? legacy : active).push(`${row.holder_id} -> ${row.target_id} (${row.kind} x${row.occurrences})`);
|
|
373
|
-
}
|
|
374
|
-
const findings = [];
|
|
375
|
-
appendFinding(findings, active, maxSampleSize, "duplicate_dependency_row", "warning", "One dependency row must be stored once per holder; identical repeated rows are a storage-layer defect invisible to the assembled graph.", (count) => `${count} exactly duplicated stored dependency row(s) on active holder(s)`, "Remove the repeated rows so the dependency is stored exactly once; graph semantics are unchanged.");
|
|
376
|
-
appendFinding(findings, legacy, maxSampleSize, "legacy_duplicate_dependency_row", "info", "Identical repeated dependency rows on terminal holders are historical storage noise with no scheduling effect.", (count) => `${count} exactly duplicated stored dependency row(s) on terminal holder(s)`, "Leave as history debt or clean up in a dedicated changelog-safe closed-item batch.");
|
|
377
|
-
return findings;
|
|
378
|
-
}
|
|
379
|
-
/** Build the reverse typed paths that can carry context toward an outcome. */
|
|
380
|
-
function buildOutcomeReverseIndex(assembly, signal) {
|
|
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
|
-
};
|
|
402
|
-
for (const edge of assembly.graph.edges()) {
|
|
403
|
-
signal?.throwIfAborted();
|
|
404
|
-
const definition = assembly.graph.registry().require(edge.kind);
|
|
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);
|
|
411
|
-
}
|
|
412
|
-
return { basis, reverse };
|
|
413
|
-
}
|
|
414
|
-
/** Expand outcome nodes through every deterministic reverse typed path. */
|
|
415
|
-
function expandOutcomeReachability(outcomes, reverse, signal) {
|
|
416
|
-
const reachable = new Set(outcomes);
|
|
417
|
-
const queue = [...outcomes].sort();
|
|
418
|
-
for (let index = 0; index < queue.length; index += 1) {
|
|
419
|
-
signal?.throwIfAborted();
|
|
420
|
-
for (const source of [...(reverse.get(queue[index]) ?? [])].sort()) {
|
|
421
|
-
if (reachable.has(source))
|
|
422
|
-
continue;
|
|
423
|
-
reachable.add(source);
|
|
424
|
-
queue.push(source);
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
return reachable;
|
|
428
|
-
}
|
|
429
|
-
/** Count active and terminal nodes against the resolved outcome-reachable set. */
|
|
430
|
-
function tallyOutcomeReachability(nodeStates, outcomes, reachable, signal) {
|
|
431
|
-
let activeReachable = 0;
|
|
432
|
-
let activeUnreachable = 0;
|
|
433
|
-
let terminal = 0;
|
|
434
|
-
let terminalReachable = 0;
|
|
435
|
-
for (const state of nodeStates.values()) {
|
|
436
|
-
signal?.throwIfAborted();
|
|
437
|
-
if (outcomes.has(state.id))
|
|
438
|
-
continue;
|
|
439
|
-
if (state.missing || state.status === "external")
|
|
440
|
-
continue;
|
|
441
|
-
if (state.terminal) {
|
|
442
|
-
terminal += 1;
|
|
443
|
-
if (reachable.has(state.id))
|
|
444
|
-
terminalReachable += 1;
|
|
445
|
-
continue;
|
|
446
|
-
}
|
|
447
|
-
if (reachable.has(state.id))
|
|
448
|
-
activeReachable += 1;
|
|
449
|
-
else
|
|
450
|
-
activeUnreachable += 1;
|
|
451
|
-
}
|
|
452
|
-
const active = activeReachable + activeUnreachable;
|
|
453
|
-
const terminalUnreachable = terminal - terminalReachable;
|
|
454
|
-
const outcomeReachable = activeReachable + terminalReachable;
|
|
455
|
-
const outcomeUnreachable = activeUnreachable + terminalUnreachable;
|
|
456
|
-
const outcomePopulation = outcomeReachable + outcomeUnreachable;
|
|
457
|
-
return {
|
|
458
|
-
active_outcome_reachable_nodes: activeReachable,
|
|
459
|
-
active_outcome_unreachable_nodes: activeUnreachable,
|
|
460
|
-
active_outcome_reachability_basis_points: active === 0 ? 0 : Math.floor((activeReachable * 10_000) / active),
|
|
461
|
-
terminal_nodes: terminal,
|
|
462
|
-
terminal_outcome_reachable_nodes: terminalReachable,
|
|
463
|
-
terminal_outcome_unreachable_nodes: terminalUnreachable,
|
|
464
|
-
terminal_outcome_reachability_basis_points: terminal === 0 ? 0 : Math.floor((terminalReachable * 10_000) / terminal),
|
|
465
|
-
outcome_reachable_nodes: outcomeReachable,
|
|
466
|
-
outcome_unreachable_nodes: outcomeUnreachable,
|
|
467
|
-
outcome_reachability_basis_points: outcomePopulation === 0
|
|
468
|
-
? 0
|
|
469
|
-
: Math.floor((outcomeReachable * 10_000) / outcomePopulation),
|
|
470
|
-
};
|
|
471
|
-
}
|
|
472
|
-
/**
|
|
473
|
-
* Count nodes with an explicit typed path to an outcome milestone. Hierarchy
|
|
474
|
-
* edges flow child to parent and `implements` flows implementation to outcome;
|
|
475
|
-
* unrelated association edges deliberately contribute no reachability.
|
|
476
|
-
*/
|
|
477
|
-
function profileOutcomeReachability(assembly, nodeStates, signal) {
|
|
478
|
-
const outcomes = new Set(assembly.details
|
|
479
|
-
.filter((detail) => detail.type?.toLowerCase() === OUTCOME_MILESTONE_TYPE &&
|
|
480
|
-
detail.title
|
|
481
|
-
.trim()
|
|
482
|
-
.toLowerCase()
|
|
483
|
-
.startsWith(OUTCOME_MILESTONE_TITLE_PREFIX))
|
|
484
|
-
.map((detail) => detail.id));
|
|
485
|
-
const { basis, reverse } = buildOutcomeReverseIndex(assembly, signal);
|
|
486
|
-
const reachable = expandOutcomeReachability(outcomes, reverse, signal);
|
|
487
|
-
return {
|
|
488
|
-
outcome_nodes: outcomes.size,
|
|
489
|
-
outcome_reachability_basis: basis,
|
|
490
|
-
...tallyOutcomeReachability(nodeStates, outcomes, reachable, signal),
|
|
491
|
-
};
|
|
492
|
-
}
|
|
493
|
-
/** Fold one active node's degree into the aggregate and per-type coverage tallies. */
|
|
494
|
-
function tallyActiveCoverageNode(tallies, state, degree, exemptIsolates, isolateExemptTypes) {
|
|
495
|
-
tallies.active += 1;
|
|
496
|
-
const typeKey = state.type ?? "(untyped)";
|
|
497
|
-
let typeProfile = tallies.byType.get(typeKey);
|
|
498
|
-
if (!typeProfile) {
|
|
499
|
-
typeProfile = { active: 0, isolated: 0, degree_leq_one: 0 };
|
|
500
|
-
tallies.byType.set(typeKey, typeProfile);
|
|
501
|
-
}
|
|
502
|
-
typeProfile.active += 1;
|
|
503
|
-
if (degree > 1)
|
|
504
|
-
return;
|
|
505
|
-
tallies.degreeLeqOne += 1;
|
|
506
|
-
typeProfile.degree_leq_one += 1;
|
|
507
|
-
if (degree === 0) {
|
|
508
|
-
tallies.isolated += 1;
|
|
509
|
-
typeProfile.isolated += 1;
|
|
510
|
-
}
|
|
511
|
-
if (exemptIsolates.has(state.id.toLowerCase()) ||
|
|
512
|
-
isolateExemptTypes.has(typeKey.toLowerCase()))
|
|
513
|
-
return;
|
|
514
|
-
if (degree === 0)
|
|
515
|
-
tallies.isolates.push(state.id);
|
|
516
|
-
else
|
|
517
|
-
tallies.sparse.push(state.id);
|
|
518
|
-
}
|
|
519
|
-
function collectCoverageReport(assembly, nodeStates, exemptIsolates, isolateExemptTypes, maxSampleSize, edgesByKind, signal) {
|
|
520
|
-
const tallies = {
|
|
521
|
-
active: 0,
|
|
522
|
-
missing: 0,
|
|
523
|
-
isolated: 0,
|
|
524
|
-
degreeLeqOne: 0,
|
|
525
|
-
isolates: [],
|
|
526
|
-
sparse: [],
|
|
527
|
-
byType: new Map(),
|
|
528
|
-
};
|
|
529
|
-
for (const id of assembly.graph.nodes()) {
|
|
530
|
-
signal?.throwIfAborted();
|
|
531
|
-
const state = nodeStates.get(id);
|
|
532
|
-
/* c8 ignore next -- every graph node originates from assembly details by construction */
|
|
533
|
-
if (!state)
|
|
534
|
-
continue;
|
|
535
|
-
if (state.missing) {
|
|
536
|
-
tallies.missing += 1;
|
|
537
|
-
continue;
|
|
538
|
-
}
|
|
539
|
-
if (state.terminal)
|
|
540
|
-
continue;
|
|
541
|
-
tallyActiveCoverageNode(tallies, state, assembly.graph.incidentEdges(id).length, exemptIsolates, isolateExemptTypes);
|
|
542
|
-
}
|
|
543
|
-
const { isolates, sparse } = tallies;
|
|
544
|
-
const findings = [];
|
|
545
|
-
appendFinding(findings, isolates, maxSampleSize, "isolated_active_node", "warning", "Active work should be reachable from the graph unless explicitly declared a valid isolate.", (count) => `${count} active item(s) with no relationships`, "Link the item to its parent, prerequisite, or origin — or register it as an exempt isolate.");
|
|
546
|
-
appendFinding(findings, sparse, maxSampleSize, "sparse_active_node", "info", "Active items with a single edge often miss an honest semantic partner; sparse is not automatically defective.", (count) => `${count} active item(s) with exactly one relationship`, "Investigate for an honest second edge (story->task, defect family, cross-epic complement); do not fabricate one.");
|
|
547
|
-
const edgeCount = assembly.graph.edges().length;
|
|
548
|
-
const redundantEdges = findRedundantRelationshipEdges(assembly.graph, {
|
|
549
|
-
signal,
|
|
550
|
-
}).value;
|
|
551
|
-
const nonInformativeEdgeKeys = new Set(redundantEdges.map(({ edge }) => `${edge.source.toLowerCase()}\u0000${edge.target.toLowerCase()}\u0000${edge.kind}`));
|
|
552
|
-
for (const contradiction of assembly.orderingContradictions ?? [])
|
|
553
|
-
nonInformativeEdgeKeys.add(`${contradiction.holder_id.toLowerCase()}\u0000${contradiction.target_id.toLowerCase()}\u0000${contradiction.dependency_kind}`);
|
|
554
|
-
const sortedEdgesByKind = Object.fromEntries(Object.entries(edgesByKind).sort(([left], [right]) => left.localeCompare(right)));
|
|
555
|
-
const semanticEdges = Object.entries(sortedEdgesByKind).reduce((count, [kind, value]) => count + (isSemanticContextKind(kind) ? value : 0), 0);
|
|
556
|
-
const cutStructure = findRelationshipCutStructure(assembly.graph, {
|
|
557
|
-
signal,
|
|
558
|
-
}).value;
|
|
559
|
-
const outcomeReachability = profileOutcomeReachability(assembly, nodeStates, signal);
|
|
560
|
-
return {
|
|
561
|
-
findings,
|
|
562
|
-
profile: {
|
|
563
|
-
nodes: assembly.graph.nodes().length,
|
|
564
|
-
recorded_nodes: assembly.details.filter((detail) => detail.status !== "missing" && detail.status !== "external").length,
|
|
565
|
-
edges: edgeCount,
|
|
566
|
-
informative_edges: edgeCount - nonInformativeEdgeKeys.size,
|
|
567
|
-
redundant_edges: redundantEdges.length,
|
|
568
|
-
ordering_contradiction_edges: assembly.orderingContradictions?.length ?? 0,
|
|
569
|
-
edge_basis: "deduplicated_directed",
|
|
570
|
-
edges_by_kind: sortedEdgesByKind,
|
|
571
|
-
edge_share_by_kind: Object.fromEntries(Object.entries(sortedEdgesByKind).map(([kind, count]) => [
|
|
572
|
-
kind,
|
|
573
|
-
count / edgeCount,
|
|
574
|
-
])),
|
|
575
|
-
semantic_edges: semanticEdges,
|
|
576
|
-
semantic_edge_share: edgeCount === 0 ? 0 : semanticEdges / edgeCount,
|
|
577
|
-
active_nodes: tallies.active,
|
|
578
|
-
missing_nodes: tallies.missing,
|
|
579
|
-
isolated_active_nodes: tallies.isolated,
|
|
580
|
-
degree_leq_one_active_nodes: tallies.degreeLeqOne,
|
|
581
|
-
coverage_by_type: Object.fromEntries([...tallies.byType.entries()].sort(([left], [right]) => left < right ? -1 : 1)),
|
|
582
|
-
articulation_points: cutStructure.articulationPoints.length,
|
|
583
|
-
bridge_edges: cutStructure.bridges.length,
|
|
584
|
-
...outcomeReachability,
|
|
585
|
-
finding_subjects_by_code: Object.fromEntries(RELATIONSHIP_AUDIT_FINDING_CODES.map((code) => [code, 0])),
|
|
586
|
-
},
|
|
587
|
-
};
|
|
588
|
-
}
|
|
589
|
-
/**
|
|
590
|
-
* Audit one assembled workspace relationship graph against the shared
|
|
591
|
-
* governance policy and return deterministic machine-readable findings plus a
|
|
592
|
-
* structural coverage profile.
|
|
593
|
-
*
|
|
594
|
-
* The audit is read-only and never proposes inventing an edge: integrity
|
|
595
|
-
* findings report evidence for investigation, coverage findings honor explicit
|
|
596
|
-
* isolate exemptions, and cycle findings enumerate the strongly connected
|
|
597
|
-
* members so callers can decide which edge to retype, remove, or waive.
|
|
598
|
-
*/
|
|
599
|
-
export function auditWorkspaceRelationshipGraph(assembly, options = {}) {
|
|
600
|
-
const isTerminal = options.isTerminal ??
|
|
601
|
-
((status) => status === "closed" || status === "canceled");
|
|
602
|
-
const isBlocked = options.isBlocked ?? ((status) => status === "blocked");
|
|
603
|
-
const maxSampleSize = options.maxSampleSize ?? DEFAULT_MAX_SAMPLE_SIZE;
|
|
604
|
-
if (!Number.isInteger(maxSampleSize) || maxSampleSize < 1)
|
|
605
|
-
throw new TypeError(`Invalid audit sample bound: ${String(options.maxSampleSize)}`);
|
|
606
|
-
const exemptIsolates = normalizeExemptionSet(options.exemptIsolates);
|
|
607
|
-
const isolateExemptTypes = normalizeExemptionSet(options.isolateExemptTypes);
|
|
608
|
-
const nodeStates = new Map(assembly.details.map((detail) => [
|
|
609
|
-
detail.id,
|
|
610
|
-
{
|
|
611
|
-
id: detail.id,
|
|
612
|
-
status: detail.status,
|
|
613
|
-
missing: assembly.missingIdSet.has(detail.id.toLowerCase()),
|
|
614
|
-
terminal: isTerminal(detail.status),
|
|
615
|
-
...(detail.type === undefined ? {} : { type: detail.type }),
|
|
616
|
-
},
|
|
617
|
-
]));
|
|
618
|
-
options.signal?.throwIfAborted();
|
|
619
|
-
const edgesByKind = {};
|
|
620
|
-
for (const edge of assembly.graph.edges())
|
|
621
|
-
edgesByKind[edge.kind] = (edgesByKind[edge.kind] ?? 0) + 1;
|
|
622
|
-
const findings = collectIntegrityFindings(assembly, isTerminal, maxSampleSize);
|
|
623
|
-
findings.push(...collectHierarchyIntegrityFindings(assembly, maxSampleSize));
|
|
624
|
-
options.signal?.throwIfAborted();
|
|
625
|
-
findings.push(...collectOrderingStorageContradictionFindings(assembly, maxSampleSize));
|
|
626
|
-
options.signal?.throwIfAborted();
|
|
627
|
-
findings.push(...collectOrderingFindings(assembly, nodeStates, isBlocked, maxSampleSize));
|
|
628
|
-
options.signal?.throwIfAborted();
|
|
629
|
-
findings.push(...collectDuplicateEdgeFindings(assembly, nodeStates, maxSampleSize), ...collectDuplicateRowFindings(assembly, maxSampleSize));
|
|
630
|
-
options.signal?.throwIfAborted();
|
|
631
|
-
const coverage = collectCoverageReport(assembly, nodeStates, exemptIsolates, isolateExemptTypes, maxSampleSize, edgesByKind, options.signal);
|
|
632
|
-
findings.push(...coverage.findings);
|
|
633
|
-
findings.sort((left, right) => SEVERITY_RANK[left.severity] - SEVERITY_RANK[right.severity] ||
|
|
634
|
-
left.code.localeCompare(right.code) ||
|
|
635
|
-
left.sample[0].localeCompare(right.sample[0]));
|
|
636
|
-
const findingSubjectsByCode = coverage.profile.finding_subjects_by_code;
|
|
637
|
-
for (const finding of findings) {
|
|
638
|
-
findingSubjectsByCode[finding.code] += finding.count;
|
|
639
|
-
}
|
|
640
|
-
return {
|
|
641
|
-
findings,
|
|
642
|
-
profile: {
|
|
643
|
-
...coverage.profile,
|
|
644
|
-
finding_subjects_by_code: findingSubjectsByCode,
|
|
645
|
-
},
|
|
646
|
-
};
|
|
647
|
-
}
|
|
648
|
-
/** Subtract two count records, keeping only non-zero signed deltas in sorted key order. */
|
|
649
|
-
function diffCountRecords(baseline, current) {
|
|
650
|
-
const keys = [
|
|
651
|
-
...new Set([...Object.keys(baseline), ...Object.keys(current)]),
|
|
652
|
-
].sort();
|
|
653
|
-
const deltas = {};
|
|
654
|
-
for (const key of keys) {
|
|
655
|
-
const delta = (current[key] ?? 0) - (baseline[key] ?? 0);
|
|
656
|
-
if (delta !== 0)
|
|
657
|
-
deltas[key] = delta;
|
|
658
|
-
}
|
|
659
|
-
return deltas;
|
|
660
|
-
}
|
|
661
|
-
/** Read per-type coverage defensively from current or legacy runtime snapshots. */
|
|
662
|
-
function readSnapshotCoverageByType(snapshot) {
|
|
663
|
-
return snapshot.profile.coverage_by_type ?? {};
|
|
664
|
-
}
|
|
665
|
-
/** Diff per-type coverage while omitting unchanged type rows. */
|
|
666
|
-
function diffCoverageByType(baseline, current) {
|
|
667
|
-
const deltas = {};
|
|
668
|
-
const baselineCoverage = readSnapshotCoverageByType(baseline);
|
|
669
|
-
const currentCoverage = readSnapshotCoverageByType(current);
|
|
670
|
-
const types = [
|
|
671
|
-
...new Set([
|
|
672
|
-
...Object.keys(baselineCoverage),
|
|
673
|
-
...Object.keys(currentCoverage),
|
|
674
|
-
]),
|
|
675
|
-
].sort();
|
|
676
|
-
for (const type of types) {
|
|
677
|
-
const before = Object.assign({ active: 0, isolated: 0, degree_leq_one: 0 }, baselineCoverage[type]);
|
|
678
|
-
const after = Object.assign({ active: 0, isolated: 0, degree_leq_one: 0 }, currentCoverage[type]);
|
|
679
|
-
const delta = {
|
|
680
|
-
active: after.active - before.active,
|
|
681
|
-
isolated: after.isolated - before.isolated,
|
|
682
|
-
degree_leq_one: after.degree_leq_one - before.degree_leq_one,
|
|
683
|
-
};
|
|
684
|
-
if (Object.values(delta).some((value) => value !== 0))
|
|
685
|
-
deltas[type] = delta;
|
|
686
|
-
}
|
|
687
|
-
return deltas;
|
|
688
|
-
}
|
|
689
|
-
/**
|
|
690
|
-
* Compare two point-in-time audit snapshots and return the signed change in
|
|
691
|
-
* findings and structural coverage. This is the temporal primitive behind
|
|
692
|
-
* change-since-baseline audit reporting: agents track census drift across
|
|
693
|
-
* sessions without re-deriving or storing full finding collections.
|
|
694
|
-
*/
|
|
695
|
-
export function diffRelationshipAuditSnapshots(baseline, current) {
|
|
696
|
-
// Public SDK callers may compare legacy or JSON-decoded snapshots created
|
|
697
|
-
// before per-type coverage was added, even though current typed snapshots
|
|
698
|
-
// always carry this field.
|
|
699
|
-
const coverageByType = diffCoverageByType(baseline, current);
|
|
700
|
-
const { edge_share_by_kind: baselineEdgeShareByKind = {}, semantic_edges: baselineSemanticEdges = 0, semantic_edge_share: baselineSemanticEdgeShare = 0, } = baseline.profile;
|
|
701
|
-
const { edge_share_by_kind: currentEdgeShareByKind = {}, semantic_edges: currentSemanticEdges = 0, semantic_edge_share: currentSemanticEdgeShare = 0, } = current.profile;
|
|
702
|
-
const baselineRecordedNodes = baseline.profile.recorded_nodes ?? baseline.profile.nodes;
|
|
703
|
-
const currentRecordedNodes = current.profile.recorded_nodes ?? current.profile.nodes;
|
|
704
|
-
const baselineInformativeEdges = baseline.profile.informative_edges ?? baseline.profile.edges;
|
|
705
|
-
const currentInformativeEdges = current.profile.informative_edges ?? current.profile.edges;
|
|
706
|
-
const baselineRedundantEdges = baseline.profile.redundant_edges ?? 0;
|
|
707
|
-
const currentRedundantEdges = current.profile.redundant_edges ?? 0;
|
|
708
|
-
const baselineOrderingContradictionEdges = baseline.profile.ordering_contradiction_edges ?? 0;
|
|
709
|
-
const currentOrderingContradictionEdges = current.profile.ordering_contradiction_edges ?? 0;
|
|
710
|
-
return {
|
|
711
|
-
baseline_saved_at: baseline.saved_at,
|
|
712
|
-
same_snapshot: baseline.fingerprint === current.fingerprint,
|
|
713
|
-
affected_subjects_by_code: diffCountRecords(baseline.affected_subjects_by_code, current.affected_subjects_by_code),
|
|
714
|
-
profile: {
|
|
715
|
-
nodes: current.profile.nodes - baseline.profile.nodes,
|
|
716
|
-
recorded_nodes: currentRecordedNodes - baselineRecordedNodes,
|
|
717
|
-
edges: current.profile.edges - baseline.profile.edges,
|
|
718
|
-
informative_edges: currentInformativeEdges - baselineInformativeEdges,
|
|
719
|
-
redundant_edges: currentRedundantEdges - baselineRedundantEdges,
|
|
720
|
-
ordering_contradiction_edges: currentOrderingContradictionEdges - baselineOrderingContradictionEdges,
|
|
721
|
-
active_nodes: current.profile.active_nodes - baseline.profile.active_nodes,
|
|
722
|
-
missing_nodes: current.profile.missing_nodes - baseline.profile.missing_nodes,
|
|
723
|
-
isolated_active_nodes: current.profile.isolated_active_nodes -
|
|
724
|
-
baseline.profile.isolated_active_nodes,
|
|
725
|
-
degree_leq_one_active_nodes: current.profile.degree_leq_one_active_nodes -
|
|
726
|
-
baseline.profile.degree_leq_one_active_nodes,
|
|
727
|
-
edges_by_kind: diffCountRecords(baseline.profile.edges_by_kind, current.profile.edges_by_kind),
|
|
728
|
-
edge_share_by_kind: diffCountRecords(baselineEdgeShareByKind, currentEdgeShareByKind),
|
|
729
|
-
semantic_edges: currentSemanticEdges - baselineSemanticEdges,
|
|
730
|
-
semantic_edge_share: currentSemanticEdgeShare - baselineSemanticEdgeShare,
|
|
731
|
-
coverage_by_type: coverageByType,
|
|
732
|
-
},
|
|
733
|
-
};
|
|
734
|
-
}
|
|
1
|
+
!(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]="1335acb2-0730-5934-89a4-14fe4a160288")}catch(e2){}})();import{findRedundantRelationshipEdges,isTransitiveKind,orientTransitiveEdge,transitiveFamilyKey}from"./analytics.js";import{findRelationshipCutStructure}from"./centrality.js";import{getWorkspaceHierarchyIntegrity}from"./assembly.js";import{profileRelationshipLifecycles,diffLifecycleCoverage}from"./lifecycle-coverage.js";import{RELATIONSHIP_AUDIT_FINDING_CODES}from"./governance-contracts.js";const DEFAULT_MAX_SAMPLE_SIZE=25;const OUTCOME_MILESTONE_TYPE="milestone";const OUTCOME_MILESTONE_TITLE_PREFIX="outcome milestone:";const SEMANTIC_CONTEXT_KINDS=new Set(["discovered_from","incident_from","recurs_from","supersedes","verifies"]);function isSemanticContextKind(kind){return SEMANTIC_CONTEXT_KINDS.has(kind)}const SEVERITY_RANK={error:0,warning:1,info:2};function normalizeExemptionSet(values){return new Set((values??[]).filter(value=>typeof value==="string").map(value=>value.trim().toLowerCase()))}function buildFinding(code,severity,policy,describe,subjects,maxSampleSize,remediation,evidence){const sorted=[...subjects].sort((left,right)=>left.localeCompare(right));return{code,severity,policy,message:describe(sorted.length),count:sorted.length,sample:sorted.slice(0,maxSampleSize),sample_truncated:sorted.length>maxSampleSize,remediation,...evidence===void 0?{}:{evidence:[...evidence].sort((left,right)=>left.localeCompare(right)).slice(0,maxSampleSize)}}}function collectHierarchyCycleFindings(analysis,maxSampleSize){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?"Preserve historical rows unless an explicit legacy repair is approved.":"Remove or retype one exact hierarchy relation before continuing mutation."))}function collectHierarchyCardinalityFindings(analysis,maxSampleSize,legacyTerminal){const rows=analysis.cardinality_violations.filter(finding=>finding.legacy_terminal===legacyTerminal);if(rows.length===0)return[];return[buildFinding(legacyTerminal?"legacy_hierarchy_cardinality_violation":"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?"Preserve historical rows unless an explicit legacy repair is approved.":"Remove the exact surplus hierarchy rows so each child has one parent.",rows.map(finding=>`${finding.child_id} <- ${finding.parent_ids.join(", ")}`))]}function collectHierarchyDirectionFindings(analysis,maxSampleSize,legacyTerminal){const rows=analysis.divergences.filter(finding=>finding.legacy_terminal===legacyTerminal);if(rows.length===0)return[];return[buildFinding(legacyTerminal?"legacy_hierarchy_direction_violation":"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?"Preserve historical rows unless an explicit legacy repair is approved.":"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(",")}`))]}function collectHierarchyIntegrityFindings(assembly,maxSampleSize){const analysis=getWorkspaceHierarchyIntegrity(assembly);if(!analysis)return[];return[...collectHierarchyCycleFindings(analysis,maxSampleSize),...collectHierarchyCardinalityFindings(analysis,maxSampleSize,false),...collectHierarchyCardinalityFindings(analysis,maxSampleSize,true),...collectHierarchyDirectionFindings(analysis,maxSampleSize,false),...collectHierarchyDirectionFindings(analysis,maxSampleSize,true)]}function buildOrderingAdjacency(assembly){const registry=assembly.graph.registry();const successors=new Map;for(const edge of assembly.graph.edges()){const definition=registry.require(edge.kind);if(!definition.ordering)continue;const[predecessor,successor]=(definition.precedence??"source_before_target")==="target_before_source"?[edge.target,edge.source]:[edge.source,edge.target];const adjacent=successors.get(predecessor);if(adjacent)adjacent.push(successor);else successors.set(predecessor,[successor])}for(const adjacent of successors.values())adjacent.sort((left,right)=>left.localeCompare(right));return successors}function registerTarjanNode(node,state){state.indexes.set(node,state.nextIndex);state.lowLinks.set(node,state.nextIndex);state.nextIndex+=1;state.stack.push(node);state.onStack.add(node)}function visitNextTarjanNeighbor(frame,frames,successors,state){const neighbors=successors.get(frame.node)??[];if(frame.neighborIndex>=neighbors.length)return false;const neighbor=neighbors[frame.neighborIndex];frame.neighborIndex+=1;if(!state.indexes.has(neighbor)){registerTarjanNode(neighbor,state);frames.push({node:neighbor,neighborIndex:0})}else if(state.onStack.has(neighbor)){state.lowLinks.set(frame.node,Math.min(state.lowLinks.get(frame.node),state.indexes.get(neighbor)))}return true}function completeTarjanFrame(frame,frames,successors,state){frames.pop();const parent=frames.at(-1);if(parent)state.lowLinks.set(parent.node,Math.min(state.lowLinks.get(parent.node),state.lowLinks.get(frame.node)));if(state.lowLinks.get(frame.node)!==state.indexes.get(frame.node))return;const component=[];while(true){const member=state.stack.pop();state.onStack.delete(member);component.push(member);if(member===frame.node)break}if(component.length>1||component.length===1&&successors.get(component[0])?.includes(component[0]))state.components.push(component.sort((left,right)=>left.localeCompare(right)))}function collectOrderingCycles(successors){const state={indexes:new Map,lowLinks:new Map,onStack:new Set,stack:[],components:[],nextIndex:0};const roots=[...successors.keys()].sort((left,right)=>left.localeCompare(right));for(const root of roots){if(state.indexes.has(root))continue;const frames=[{node:root,neighborIndex:0}];registerTarjanNode(root,state);while(frames.length>0){const frame=frames.at(-1);if(visitNextTarjanNeighbor(frame,frames,successors,state))continue;completeTarjanFrame(frame,frames,successors,state)}}return state.components.sort((left,right)=>left[0].localeCompare(right[0]))}function formatDanglingReference(reference){return`${reference.holder_id} -> ${reference.target_id} (${reference.kind})`}function appendFinding(findings,subjects,maxSampleSize,code,severity,policy,describe,remediation){if(subjects.length===0)return;findings.push(buildFinding(code,severity,policy,describe,subjects,maxSampleSize,remediation))}function collectIntegrityFindings(assembly,isTerminal,maxSampleSize){const findings=[];const references=[...assembly.dangling.active,...assembly.dangling.legacy_terminal].filter(reference=>!reference.no_active_blocker_sentinel);appendFinding(findings,references.filter(reference=>!isTerminal(reference.holder_status)).map(formatDanglingReference),maxSampleSize,"missing_reference_active","error","Active items must not reference absent targets; missing prerequisites distort scheduling.",count=>`${count} active dangling reference(s) to absent items`,"Restore or re-create the referenced item, or remove/retype the reference on the holder.");appendFinding(findings,references.filter(reference=>isTerminal(reference.holder_status)).map(formatDanglingReference),maxSampleSize,"missing_reference_terminal","info","Terminal items may retain historical references, but absent targets remain unexplainable context.",count=>`${count} terminal dangling reference(s) to absent items`,"Leave as history debt or clean up in a dedicated changelog-safe closed-item batch.");appendFinding(findings,assembly.dangling.no_active_blocker_sentinels.map(formatDanglingReference),maxSampleSize,"legacy_no_blocker_sentinel","info","The pre-structured-dependency no-active-blocker sentinel is retired; real edges carry blocker state.",count=>`${count} legacy no-active-blocker sentinel reference(s)`,"Delete the sentinel reference; blocker state is derived from blocked_by edges.");return findings}function isActiveAuditMember(member,nodeStates){const state=nodeStates.get(member);if(!state)return true;return!state.terminal}function formatOrderingStorageContradiction(contradiction){return`${contradiction.holder_id} -> ${contradiction.target_id} (blocked_by scalar + ${contradiction.dependency_kind} dependency)`}function collectOrderingStorageContradictionFindings(assembly,maxSampleSize){const active=[];const legacy=[];for(const contradiction of assembly.orderingContradictions??[])(contradiction.legacy_terminal?legacy:active).push(formatOrderingStorageContradiction(contradiction));const findings=[];appendFinding(findings,active,maxSampleSize,"ordering_storage_contradiction","error","A scalar blocker and same-target structured ordering row must not assert opposite precedence.",count=>`${count} active scalar-versus-structured ordering contradiction(s)`,"Remove the structured dependency row that points the holder before its scalar blocker, or replace both representations with one unambiguous ordering edge.");appendFinding(findings,legacy,maxSampleSize,"legacy_ordering_storage_contradiction","info","Terminal scalar-versus-structured ordering contradictions are exact historical storage defects with no live scheduling effect.",count=>`${count} terminal scalar-versus-structured ordering contradiction(s)`,"Remove the contradictory blocks dependency row in a dedicated changelog-safe closed-item batch; preserve the scalar blocker as the historical precedence record.");return findings}function collectOrderingFindings(assembly,nodeStates,isBlocked,maxSampleSize){const findings=[];const orderingAdjacency=buildOrderingAdjacency(assembly);for(const cycle of collectOrderingCycles(orderingAdjacency)){const active=cycle.some(member=>isActiveAuditMember(member,nodeStates));const cycleMembers=new Set(cycle.map(member=>member.toLowerCase()));const contradictionEvidence=(assembly.orderingContradictions??[]).filter(row=>cycleMembers.has(row.holder_id.toLowerCase())&&cycleMembers.has(row.target_id.toLowerCase())).map(formatOrderingStorageContradiction);findings.push(active?buildFinding("ordering_cycle","error","Order-bearing relationship kinds must stay acyclic; a cycle makes every member unschedulable.",count=>`execution-order cycle across ${count} item(s)`,cycle,maxSampleSize,"Remove or retype one ordering edge inside the cycle, or split the work so precedence is linear.",contradictionEvidence.length===0?void 0:contradictionEvidence):buildFinding("legacy_ordering_cycle","info","Ordering cycles between exclusively terminal items are historical contradictions with no scheduling effect.",count=>`terminal-only execution-order cycle across ${count} item(s)`,cycle,maxSampleSize,"Leave as history debt, or clean up in a dedicated changelog-safe closed-item batch; never repair closed items ad hoc.",contradictionEvidence.length===0?void 0:contradictionEvidence))}const predecessorsByNode=new Map;for(const[predecessor,adjacent]of orderingAdjacency)for(const successor of adjacent){const predecessors=predecessorsByNode.get(successor);if(predecessors)predecessors.push(predecessor);else predecessorsByNode.set(successor,[predecessor])}const staleLifecycleBlocks=[...nodeStates.values()].filter(state=>!state.missing&&!state.terminal&&isBlocked(state.status)&&!(predecessorsByNode.get(state.id)??[]).some(predecessor=>{const predecessorState=nodeStates.get(predecessor);return predecessorState===void 0||!predecessorState.terminal})).map(state=>state.id);appendFinding(findings,staleLifecycleBlocks,maxSampleSize,"stale_lifecycle_block","warning","A blocked lifecycle status must be backed by at least one open ordering predecessor.",count=>`${count} status-blocked item(s) with no open blocker edge`,'Add a local blocker edge, declare an external blocker with --dep "id=<external-ref>,kind=blocked_by,source_kind=external", or move the item back to an active status.');return findings}function formatDuplicateEdgeGroup(group){const kinds=group.edges.map(edge=>edge.kind).sort((left,right)=>left.localeCompare(right));return`${group.from} -> ${group.to} (${kinds.join(" + ")})`}function collectDuplicateRelationshipEdgeGroups(assembly){const registry=assembly.graph.registry();const groups=new Map;for(const edge of assembly.graph.edges()){const definition=registry.require(edge.kind);if(!isTransitiveKind(definition))continue;const oriented=orientTransitiveEdge(edge,definition);if(oriented.from===oriented.to)continue;const key=`${transitiveFamilyKey(definition)}::${oriented.from}::${oriented.to}`;const group=groups.get(key);if(group)group.edges.push(edge);else groups.set(key,{...oriented,edges:[edge]})}const duplicated=[...groups.values()].filter(group=>group.edges.length>=2);for(const group of duplicated)group.edges.sort((left,right)=>left.kind.localeCompare(right.kind));return duplicated.sort((left,right)=>left.from.localeCompare(right.from)||left.to.localeCompare(right.to))}function collectDuplicateEdgeFindings(assembly,nodeStates,maxSampleSize){const active=[];const legacy=[];for(const group of collectDuplicateRelationshipEdgeGroups(assembly)){const subjects=isActiveAuditMember(group.from,nodeStates)||isActiveAuditMember(group.to,nodeStates)?active:legacy;subjects.push(formatDuplicateEdgeGroup(group))}const findings=[];appendFinding(findings,active,maxSampleSize,"duplicate_edge","info","One semantic relationship should be stored once; parallel same-family spellings (reciprocal inverse pairs included) add no information and skew degree metrics.",count=>`${count} duplicated relationship(s) between active endpoints`,"Remove all but one stored spelling of the relationship; keep the canonical direction the holder declared first.");appendFinding(findings,legacy,maxSampleSize,"legacy_duplicate_edge","info","Duplicated spellings between exclusively terminal items are historical noise with no scheduling effect.",count=>`${count} duplicated relationship(s) between terminal endpoints`,"Leave as history debt or clean up in a dedicated changelog-safe closed-item batch.");return findings}function collectDuplicateRowFindings(assembly,maxSampleSize){const active=[];const legacy=[];for(const row of assembly.duplicateRows??[]){(row.legacy_terminal?legacy:active).push(`${row.holder_id} -> ${row.target_id} (${row.kind} x${row.occurrences})`)}const findings=[];appendFinding(findings,active,maxSampleSize,"duplicate_dependency_row","warning","One dependency row must be stored once per holder; identical repeated rows are a storage-layer defect invisible to the assembled graph.",count=>`${count} exactly duplicated stored dependency row(s) on active holder(s)`,"Remove the repeated rows so the dependency is stored exactly once; graph semantics are unchanged.");appendFinding(findings,legacy,maxSampleSize,"legacy_duplicate_dependency_row","info","Identical repeated dependency rows on terminal holders are historical storage noise with no scheduling effect.",count=>`${count} exactly duplicated stored dependency row(s) on terminal holder(s)`,"Leave as history debt or clean up in a dedicated changelog-safe closed-item batch.");return findings}function buildOutcomeReverseIndex(assembly,signal){const reverse=new Map;const basisKinds={source_to_target:[],target_to_source:[],both:[]};for(const definition of assembly.graph.registry().list()){if(definition.outcomeTraversal!==void 0){basisKinds[definition.outcomeTraversal].push(definition.kind)}}const basis={source_to_target:basisKinds.source_to_target.join(","),target_to_source:basisKinds.target_to_source.join(","),both:basisKinds.both.join(",")};const append=(from,to)=>{const sources=reverse.get(to)??new Set;sources.add(from);reverse.set(to,sources)};for(const edge of assembly.graph.edges()){signal?.throwIfAborted();const definition=assembly.graph.registry().require(edge.kind);if(definition.outcomeTraversal==="source_to_target"||definition.outcomeTraversal==="both")append(edge.source,edge.target);if(definition.outcomeTraversal==="target_to_source"||definition.outcomeTraversal==="both")append(edge.target,edge.source)}return{basis,reverse}}function expandOutcomeReachability(outcomes,reverse,signal){const reachable=new Set(outcomes);const queue=[...outcomes].sort();for(let index=0;index<queue.length;index+=1){signal?.throwIfAborted();for(const source of[...reverse.get(queue[index])??[]].sort()){if(reachable.has(source))continue;reachable.add(source);queue.push(source)}}return reachable}function tallyOutcomeReachability(nodeStates,outcomes,reachable,signal){let activeReachable=0;let activeUnreachable=0;let terminal=0;let terminalReachable=0;for(const state of nodeStates.values()){signal?.throwIfAborted();if(outcomes.has(state.id))continue;if(state.missing||state.status==="external")continue;if(state.terminal){terminal+=1;if(reachable.has(state.id))terminalReachable+=1;continue}if(reachable.has(state.id))activeReachable+=1;else activeUnreachable+=1}const active=activeReachable+activeUnreachable;const terminalUnreachable=terminal-terminalReachable;const outcomeReachable=activeReachable+terminalReachable;const outcomeUnreachable=activeUnreachable+terminalUnreachable;const outcomePopulation=outcomeReachable+outcomeUnreachable;return{active_outcome_reachable_nodes:activeReachable,active_outcome_unreachable_nodes:activeUnreachable,active_outcome_reachability_basis_points:active===0?0:Math.floor(activeReachable*1e4/active),terminal_nodes:terminal,terminal_outcome_reachable_nodes:terminalReachable,terminal_outcome_unreachable_nodes:terminalUnreachable,terminal_outcome_reachability_basis_points:terminal===0?0:Math.floor(terminalReachable*1e4/terminal),outcome_reachable_nodes:outcomeReachable,outcome_unreachable_nodes:outcomeUnreachable,outcome_reachability_basis_points:outcomePopulation===0?0:Math.floor(outcomeReachable*1e4/outcomePopulation)}}function profileOutcomeReachability(assembly,nodeStates,signal){const outcomes=new Set(assembly.details.filter(detail=>detail.type?.toLowerCase()===OUTCOME_MILESTONE_TYPE&&detail.title.trim().toLowerCase().startsWith(OUTCOME_MILESTONE_TITLE_PREFIX)).map(detail=>detail.id));const{basis,reverse}=buildOutcomeReverseIndex(assembly,signal);const reachable=expandOutcomeReachability(outcomes,reverse,signal);return{outcome_nodes:outcomes.size,outcome_reachability_basis:basis,...tallyOutcomeReachability(nodeStates,outcomes,reachable,signal)}}function tallyActiveCoverageNode(tallies,state,degree,exemptIsolates,isolateExemptTypes){tallies.active+=1;const typeKey=state.type??"(untyped)";let typeProfile=tallies.byType.get(typeKey);if(!typeProfile){typeProfile={active:0,isolated:0,degree_leq_one:0};tallies.byType.set(typeKey,typeProfile)}typeProfile.active+=1;if(degree>1)return;tallies.degreeLeqOne+=1;typeProfile.degree_leq_one+=1;if(degree===0){tallies.isolated+=1;typeProfile.isolated+=1}if(exemptIsolates.has(state.id.toLowerCase())||isolateExemptTypes.has(typeKey.toLowerCase()))return;if(degree===0)tallies.isolates.push(state.id);else tallies.sparse.push(state.id)}function collectCoverageReport(assembly,nodeStates,exemptIsolates,isolateExemptTypes,maxSampleSize,edgesByKind,signal){const tallies={active:0,missing:0,isolated:0,degreeLeqOne:0,isolates:[],sparse:[],byType:new Map};for(const id of assembly.graph.nodes()){signal?.throwIfAborted();const state=nodeStates.get(id);if(!state)continue;if(state.missing){tallies.missing+=1;continue}if(state.terminal)continue;tallyActiveCoverageNode(tallies,state,assembly.graph.incidentEdges(id).length,exemptIsolates,isolateExemptTypes)}const{isolates,sparse}=tallies;const findings=[];appendFinding(findings,isolates,maxSampleSize,"isolated_active_node","warning","Active work should be reachable from the graph unless explicitly declared a valid isolate.",count=>`${count} active item(s) with no relationships`,"Link the item to its parent, prerequisite, or origin \u2014 or register it as an exempt isolate.");appendFinding(findings,sparse,maxSampleSize,"sparse_active_node","info","Active items with a single edge often miss an honest semantic partner; sparse is not automatically defective.",count=>`${count} active item(s) with exactly one relationship`,"Investigate for an honest second edge (story->task, defect family, cross-epic complement); do not fabricate one.");const edgeCount=assembly.graph.edges().length;const redundantEdges=findRedundantRelationshipEdges(assembly.graph,{signal}).value;const nonInformativeEdgeKeys=new Set(redundantEdges.map(({edge})=>`${edge.source.toLowerCase()}\0${edge.target.toLowerCase()}\0${edge.kind}`));for(const contradiction of assembly.orderingContradictions??[])nonInformativeEdgeKeys.add(`${contradiction.holder_id.toLowerCase()}\0${contradiction.target_id.toLowerCase()}\0${contradiction.dependency_kind}`);const sortedEdgesByKind=Object.fromEntries(Object.entries(edgesByKind).sort(([left],[right])=>left.localeCompare(right)));const semanticEdges=Object.entries(sortedEdgesByKind).reduce((count,[kind,value])=>count+(isSemanticContextKind(kind)?value:0),0);const cutStructure=findRelationshipCutStructure(assembly.graph,{signal}).value;const outcomeReachability=profileOutcomeReachability(assembly,nodeStates,signal);return{findings,profile:{nodes:assembly.graph.nodes().length,recorded_nodes:assembly.details.filter(detail=>detail.status!=="missing"&&detail.status!=="external").length,edges:edgeCount,informative_edges:edgeCount-nonInformativeEdgeKeys.size,redundant_edges:redundantEdges.length,ordering_contradiction_edges:assembly.orderingContradictions?.length??0,edge_basis:"deduplicated_directed",edges_by_kind:sortedEdgesByKind,edge_share_by_kind:Object.fromEntries(Object.entries(sortedEdgesByKind).map(([kind,count])=>[kind,count/edgeCount])),semantic_edges:semanticEdges,semantic_edge_share:edgeCount===0?0:semanticEdges/edgeCount,active_nodes:tallies.active,missing_nodes:tallies.missing,isolated_active_nodes:tallies.isolated,degree_leq_one_active_nodes:tallies.degreeLeqOne,coverage_by_type:Object.fromEntries([...tallies.byType.entries()].sort(([left],[right])=>left<right?-1:1)),articulation_points:cutStructure.articulationPoints.length,bridge_edges:cutStructure.bridges.length,...outcomeReachability,finding_subjects_by_code:Object.fromEntries(RELATIONSHIP_AUDIT_FINDING_CODES.map(code=>[code,0]))}}}function auditWorkspaceRelationshipGraph(assembly,options={}){const isTerminal=options.isTerminal??(status=>status==="closed"||status==="canceled");const isBlocked=options.isBlocked??(status=>status==="blocked");const maxSampleSize=options.maxSampleSize??DEFAULT_MAX_SAMPLE_SIZE;if(!Number.isInteger(maxSampleSize)||maxSampleSize<1)throw new TypeError(`Invalid audit sample bound: ${String(options.maxSampleSize)}`);const exemptIsolates=normalizeExemptionSet(options.exemptIsolates);const isolateExemptTypes=normalizeExemptionSet(options.isolateExemptTypes);const nodeStates=new Map(assembly.details.map(detail=>[detail.id,{id:detail.id,status:detail.status,missing:assembly.missingIdSet.has(detail.id.toLowerCase()),terminal:isTerminal(detail.status),...detail.type===void 0?{}:{type:detail.type}}]));options.signal?.throwIfAborted();const edgesByKind={};for(const edge of assembly.graph.edges())edgesByKind[edge.kind]=(edgesByKind[edge.kind]??0)+1;const findings=collectIntegrityFindings(assembly,isTerminal,maxSampleSize);findings.push(...collectHierarchyIntegrityFindings(assembly,maxSampleSize));options.signal?.throwIfAborted();findings.push(...collectOrderingStorageContradictionFindings(assembly,maxSampleSize));options.signal?.throwIfAborted();findings.push(...collectOrderingFindings(assembly,nodeStates,isBlocked,maxSampleSize));options.signal?.throwIfAborted();findings.push(...collectDuplicateEdgeFindings(assembly,nodeStates,maxSampleSize),...collectDuplicateRowFindings(assembly,maxSampleSize));options.signal?.throwIfAborted();const coverage=collectCoverageReport(assembly,nodeStates,exemptIsolates,isolateExemptTypes,maxSampleSize,edgesByKind,options.signal);findings.push(...coverage.findings);findings.sort((left,right)=>SEVERITY_RANK[left.severity]-SEVERITY_RANK[right.severity]||left.code.localeCompare(right.code)||left.sample[0].localeCompare(right.sample[0]));const findingSubjectsByCode=coverage.profile.finding_subjects_by_code;for(const finding of findings){findingSubjectsByCode[finding.code]+=finding.count}return{findings,profile:{...coverage.profile,...profileRelationshipLifecycles(assembly,isTerminal,isSemanticContextKind,options.signal),ordering_acyclic:!findings.some(finding=>finding.code==="ordering_cycle"||finding.code==="legacy_ordering_cycle"),active_ordering_acyclic:!findings.some(finding=>finding.code==="ordering_cycle"),finding_subjects_by_code:findingSubjectsByCode}}}function diffCountRecords(baseline,current){const keys=[...new Set([...Object.keys(baseline),...Object.keys(current)])].sort();const deltas={};for(const key of keys){const delta=(current[key]??0)-(baseline[key]??0);if(delta!==0)deltas[key]=delta}return deltas}function readSnapshotCoverageByType(snapshot){return snapshot.profile.coverage_by_type??{}}function diffCoverageByType(baseline,current){const deltas={};const baselineCoverage=readSnapshotCoverageByType(baseline);const currentCoverage=readSnapshotCoverageByType(current);const types=[...new Set([...Object.keys(baselineCoverage),...Object.keys(currentCoverage)])].sort();for(const type of types){const before=Object.assign({active:0,isolated:0,degree_leq_one:0},baselineCoverage[type]);const after=Object.assign({active:0,isolated:0,degree_leq_one:0},currentCoverage[type]);const delta={active:after.active-before.active,isolated:after.isolated-before.isolated,degree_leq_one:after.degree_leq_one-before.degree_leq_one};if(Object.values(delta).some(value=>value!==0))deltas[type]=delta}return deltas}function diffRelationshipAuditSnapshots(baseline,current){const coverageByType=diffCoverageByType(baseline,current);const{edge_share_by_kind:baselineEdgeShareByKind={},semantic_edges:baselineSemanticEdges=0,semantic_edge_share:baselineSemanticEdgeShare=0}=baseline.profile;const{edge_share_by_kind:currentEdgeShareByKind={},semantic_edges:currentSemanticEdges=0,semantic_edge_share:currentSemanticEdgeShare=0}=current.profile;const baselineRecordedNodes=baseline.profile.recorded_nodes??baseline.profile.nodes;const currentRecordedNodes=current.profile.recorded_nodes??current.profile.nodes;const baselineInformativeEdges=baseline.profile.informative_edges??baseline.profile.edges;const currentInformativeEdges=current.profile.informative_edges??current.profile.edges;const baselineRedundantEdges=baseline.profile.redundant_edges??0;const currentRedundantEdges=current.profile.redundant_edges??0;const baselineOrderingContradictionEdges=baseline.profile.ordering_contradiction_edges??0;const currentOrderingContradictionEdges=current.profile.ordering_contradiction_edges??0;return{baseline_saved_at:baseline.saved_at,same_snapshot:baseline.fingerprint===current.fingerprint,affected_subjects_by_code:diffCountRecords(baseline.affected_subjects_by_code,current.affected_subjects_by_code),profile:{nodes:current.profile.nodes-baseline.profile.nodes,recorded_nodes:currentRecordedNodes-baselineRecordedNodes,edges:current.profile.edges-baseline.profile.edges,informative_edges:currentInformativeEdges-baselineInformativeEdges,redundant_edges:currentRedundantEdges-baselineRedundantEdges,ordering_contradiction_edges:currentOrderingContradictionEdges-baselineOrderingContradictionEdges,active_nodes:current.profile.active_nodes-baseline.profile.active_nodes,missing_nodes:current.profile.missing_nodes-baseline.profile.missing_nodes,isolated_active_nodes:current.profile.isolated_active_nodes-baseline.profile.isolated_active_nodes,degree_leq_one_active_nodes:current.profile.degree_leq_one_active_nodes-baseline.profile.degree_leq_one_active_nodes,edges_by_kind:diffCountRecords(baseline.profile.edges_by_kind,current.profile.edges_by_kind),edge_share_by_kind:diffCountRecords(baselineEdgeShareByKind,currentEdgeShareByKind),semantic_edges:currentSemanticEdges-baselineSemanticEdges,semantic_edge_share:currentSemanticEdgeShare-baselineSemanticEdgeShare,coverage_by_type:coverageByType,...diffLifecycleCoverage(baseline.profile,current.profile)}}}export{auditWorkspaceRelationshipGraph,collectDuplicateRelationshipEdgeGroups,collectOrderingCycles,diffRelationshipAuditSnapshots,formatDuplicateEdgeGroup,formatOrderingStorageContradiction,isSemanticContextKind};
|
|
735
2
|
//# sourceMappingURL=governance.js.map
|
|
736
|
-
//# debugId=27de9c62-434c-566d-a000-54eddb9a16a9
|