@unbrained/pm-cli 2026.9.7 → 2026.9.8
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 +5 -0
- package/.agents/skills/pm-user/references/ECOSYSTEM_PLANNING.md +95 -0
- package/.claude-plugin/marketplace.json +2 -2
- package/CHANGELOG.md +211 -181
- 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.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-3BZZA6A3.js +5 -0
- package/dist/cli-bundle/chunks/chunk-AMRDVX36.js +235 -0
- package/dist/cli-bundle/chunks/chunk-DAM4CBBR.js +2202 -0
- package/dist/cli-bundle/chunks/{chunk-WYRZZ5ZE.js → chunk-DEZPZOQ7.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-FLDWYEND.js +77 -0
- package/dist/cli-bundle/chunks/{chunk-NPN5I67J.js → chunk-G5ADGQ2D.js} +3 -3
- package/dist/cli-bundle/chunks/chunk-JAAEYHUA.js +2 -0
- package/dist/cli-bundle/chunks/{chunk-JX4DH4WC.js → chunk-KR3RP4FH.js} +3 -3
- package/dist/cli-bundle/chunks/chunk-LEZOUXG6.js +2 -0
- package/dist/cli-bundle/chunks/chunk-NSAPNEYH.js +2 -0
- package/dist/cli-bundle/chunks/chunk-QOEKOECV.js +29 -0
- package/dist/cli-bundle/chunks/chunk-RHSFOMHU.js +2 -0
- package/dist/cli-bundle/chunks/chunk-XOD2PCB6.js +20 -0
- package/dist/cli-bundle/chunks/{chunk-JT6XF6GE.js → chunk-YVEQ2AMO.js} +4 -4
- package/dist/cli-bundle/chunks/register-list-query-KSK3R63M.js +11 -0
- package/dist/cli-bundle/chunks/register-mutation-IQ5C7U6O.js +20 -0
- package/dist/cli-bundle/chunks/{register-operations-I5W3KMGB.js → register-operations-DWVHR2NC.js} +2 -2
- package/dist/cli-bundle/chunks/register-setup-BQ5SVD4B.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-3WRPWLA7.js +3 -0
- package/dist/cli-bundle/focused-chunks/chunk-7MBUII7U.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-COPBO5HW.js +8 -0
- package/dist/cli-bundle/focused-chunks/chunk-DUN4IXYN.js +20 -0
- package/dist/cli-bundle/focused-chunks/{chunk-LIJIA4TY.js → chunk-EE5QSM4Z.js} +3 -3
- package/dist/cli-bundle/focused-chunks/chunk-HJVARYM3.js +6 -0
- package/dist/cli-bundle/focused-chunks/chunk-IAUOUGVR.js +177 -0
- package/dist/cli-bundle/focused-chunks/chunk-KOOMJ54F.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-MZDXX7JY.js +56 -0
- package/dist/cli-bundle/focused-chunks/{chunk-M6PBRFSH.js → chunk-OPFHFGDD.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-QHNETP77.js +9 -0
- package/dist/cli-bundle/focused-chunks/{chunk-3BZEOXLT.js → chunk-RMWES4M6.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-RXT4P5QH.js +10 -0
- package/dist/cli-bundle/focused-chunks/chunk-SDOL6FM2.js +12 -0
- package/dist/cli-bundle/focused-chunks/chunk-SQV4AAUC.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-PEFABF7E.js → chunk-WE3IUNIW.js} +3 -3
- package/dist/cli-bundle/focused-chunks/chunk-YALOUBWJ.js +2 -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 +29 -29
- 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/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.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.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 +157 -837
- 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.js +1 -2261
- package/dist/sdk/governance/workspace-position.js +1 -163
- package/dist/sdk/governance.d.ts +1 -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 -0
- 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.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.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.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.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.js +1 -4
- package/dist/sdk/runtime-stats-options.js +1 -38
- 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.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.js +1 -245
- package/docs/AGENT_GUIDE.md +1 -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 +3 -0
- package/docs/SCRIPTING.md +21 -0
- package/docs/TESTING.md +23 -0
- package/docs/generated/FLAG_LEXICON_BUDGETS.md +5 -5
- package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +2 -2
- package/marketplace.json +2 -2
- package/package.json +1 -1
- package/packages/pm-beads/package.json +1 -1
- package/packages/pm-calendar/package.json +1 -1
- package/packages/pm-command-kit/package.json +1 -1
- package/packages/pm-digital-twin/package.json +1 -1
- package/packages/pm-governance-audit/package.json +1 -1
- package/packages/pm-guide-shell/package.json +1 -1
- package/packages/pm-kanban/package.json +1 -1
- package/packages/pm-lifecycle-hooks/package.json +1 -1
- package/packages/pm-linked-test-adapters/package.json +1 -1
- package/packages/pm-search-advanced/package.json +1 -1
- package/packages/pm-templates/package.json +1 -1
- package/packages/pm-todos/package.json +1 -1
- package/packages/pm-vcs/package.json +1 -1
- package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
- package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
- package/scripts/finalize-build.mjs +45 -3
- package/dist/cli-bundle/chunks/chunk-AZDFJGH6.js +0 -9
- package/dist/cli-bundle/chunks/chunk-BAX2BYUW.js +0 -2111
- package/dist/cli-bundle/chunks/chunk-CKOL5U7J.js +0 -77
- 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-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-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,776 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* @module sdk/merge/three-way
|
|
3
|
-
|
|
4
|
-
* Field-aware three-way merge primitives for every mergeable tracker artifact
|
|
5
|
-
* class: append-only history JSONL streams, item documents (TOON or
|
|
6
|
-
* JSON+Markdown), and key-level JSON configuration/schema files. These
|
|
7
|
-
* primitives are pure (content in, content out) so the CLI merge driver, the
|
|
8
|
-
* MCP surface, and package integrations all share one merge semantics
|
|
9
|
-
* definition for multi-branch agent workflows.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
!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]="4d5434c7-544b-5061-89eb-6e4074e58e30")}catch(e){}}();
|
|
13
|
-
import { historyEntriesToRaw, reanchorHistoryEntries, } from "../../core/history/replay.js";
|
|
14
|
-
import { canonicalDocument, parseItemDocument, serializeItemDocument, } from "../../core/item/item-format.js";
|
|
15
|
-
import { splitAcceptanceCriteria } from "../../core/item/parse.js";
|
|
16
|
-
import { EXIT_CODE } from "../../core/shared/constants.js";
|
|
17
|
-
import { findFirstMergeConflictMarker } from "../../core/shared/conflict-markers.js";
|
|
18
|
-
import { PmCliError } from "../../core/shared/errors.js";
|
|
19
|
-
import { sha256Hex, stableStringify } from "../../core/shared/serialization.js";
|
|
20
|
-
import { compareTimestampStrings } from "../../core/shared/time.js";
|
|
21
|
-
/** JSON-stable marker used when a conflict decision refers to an absent scalar. */
|
|
22
|
-
export const ITEM_SCALAR_MISSING_VALUE = Object.freeze({
|
|
23
|
-
pm_item_scalar_missing: true,
|
|
24
|
-
});
|
|
25
|
-
/** Return whether receipt evidence represents an absent item scalar. */
|
|
26
|
-
export function isItemScalarMissingValue(value) {
|
|
27
|
-
return (typeof value === "object" &&
|
|
28
|
-
value !== null &&
|
|
29
|
-
!Array.isArray(value) &&
|
|
30
|
-
Object.keys(value).length === 1 &&
|
|
31
|
-
value.pm_item_scalar_missing === true);
|
|
32
|
-
}
|
|
33
|
-
/** Encode an absent scalar so every decision property survives JSON serialization. */
|
|
34
|
-
export function encodeItemScalarDecisionValue(value) {
|
|
35
|
-
return value === undefined ? ITEM_SCALAR_MISSING_VALUE : value;
|
|
36
|
-
}
|
|
37
|
-
/** Hash scalars in disjoint present/missing domains for collision-free evidence. */
|
|
38
|
-
export function hashItemScalarDecisionValue(value) {
|
|
39
|
-
return sha256Hex(stableStringify(value === undefined
|
|
40
|
-
? { pm_item_scalar_presence: "missing" }
|
|
41
|
-
: { pm_item_scalar_presence: "present", value }));
|
|
42
|
-
}
|
|
43
|
-
function parseHistoryJsonl(raw, label) {
|
|
44
|
-
const conflictMarker = findFirstMergeConflictMarker(raw);
|
|
45
|
-
if (conflictMarker) {
|
|
46
|
-
throw new PmCliError(`History merge input (${label}) contains unresolved conflict markers at line ${conflictMarker.line}. Merge drivers must receive clean per-side versions.`, EXIT_CODE.GENERIC_FAILURE);
|
|
47
|
-
}
|
|
48
|
-
const entries = [];
|
|
49
|
-
for (const [index, rawLine] of raw.split(/\r?\n/).entries()) {
|
|
50
|
-
const line = rawLine.trim();
|
|
51
|
-
if (!line) {
|
|
52
|
-
continue;
|
|
53
|
-
}
|
|
54
|
-
try {
|
|
55
|
-
const parsed = JSON.parse(line);
|
|
56
|
-
if (typeof parsed !== "object" ||
|
|
57
|
-
parsed === null ||
|
|
58
|
-
Array.isArray(parsed)) {
|
|
59
|
-
throw new TypeError("history entry must be an object");
|
|
60
|
-
}
|
|
61
|
-
entries.push(parsed);
|
|
62
|
-
}
|
|
63
|
-
catch {
|
|
64
|
-
throw new PmCliError(`History merge input (${label}) contains invalid JSON at line ${index + 1}.`, EXIT_CODE.GENERIC_FAILURE);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
return entries;
|
|
68
|
-
}
|
|
69
|
-
function historyEntryIdentity(entry) {
|
|
70
|
-
return stableStringify({
|
|
71
|
-
ts: entry.ts,
|
|
72
|
-
author: entry.author,
|
|
73
|
-
op: entry.op,
|
|
74
|
-
patch: entry.patch,
|
|
75
|
-
message: entry.message ?? null,
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
function commonHistoryPrefixLength(ours, theirs) {
|
|
79
|
-
const limit = Math.min(ours.length, theirs.length);
|
|
80
|
-
let shared = 0;
|
|
81
|
-
while (shared < limit &&
|
|
82
|
-
historyEntryIdentity(ours[shared]) === historyEntryIdentity(theirs[shared])) {
|
|
83
|
-
shared += 1;
|
|
84
|
-
}
|
|
85
|
-
return shared;
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* Deterministically merge two diverged versions of one append-only history
|
|
89
|
-
* JSONL stream. Identical streams and one-sided fast-forwards keep the winning
|
|
90
|
-
* side byte-for-byte; genuinely diverged streams are merged as the shared
|
|
91
|
-
* prefix plus both suffixes ordered by timestamp (ours-first on ties), then
|
|
92
|
-
* hash re-anchored so the resulting chain verifies again. No side's events are
|
|
93
|
-
* ever discarded: content divergence is preserved for the post-merge
|
|
94
|
-
* reconciliation pass (`pm validate` + `pm history-repair`) instead of being
|
|
95
|
-
* silently resolved by last-writer-wins. If either suffix cannot apply after
|
|
96
|
-
* deterministic ordering, the merge fails closed instead of publishing a
|
|
97
|
-
* hash-valid stream that omitted a branch effect.
|
|
98
|
-
*/
|
|
99
|
-
export function mergeHistoryStreams(baseRaw, oursRaw, theirsRaw) {
|
|
100
|
-
parseHistoryJsonl(baseRaw, "base");
|
|
101
|
-
const ours = parseHistoryJsonl(oursRaw, "ours");
|
|
102
|
-
const theirs = parseHistoryJsonl(theirsRaw, "theirs");
|
|
103
|
-
const shared = commonHistoryPrefixLength(ours, theirs);
|
|
104
|
-
if (shared === ours.length && shared === theirs.length) {
|
|
105
|
-
return {
|
|
106
|
-
merged: historyEntriesToRaw(ours),
|
|
107
|
-
strategy: "identical",
|
|
108
|
-
common_entries: shared,
|
|
109
|
-
entries_from_ours: 0,
|
|
110
|
-
entries_from_theirs: 0,
|
|
111
|
-
entries_total: ours.length,
|
|
112
|
-
reanchored: false,
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
if (shared === theirs.length) {
|
|
116
|
-
return {
|
|
117
|
-
merged: historyEntriesToRaw(ours),
|
|
118
|
-
strategy: "fast_forward_ours",
|
|
119
|
-
common_entries: shared,
|
|
120
|
-
entries_from_ours: ours.length - shared,
|
|
121
|
-
entries_from_theirs: 0,
|
|
122
|
-
entries_total: ours.length,
|
|
123
|
-
reanchored: false,
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
if (shared === ours.length) {
|
|
127
|
-
return {
|
|
128
|
-
merged: historyEntriesToRaw(theirs),
|
|
129
|
-
strategy: "fast_forward_theirs",
|
|
130
|
-
common_entries: shared,
|
|
131
|
-
entries_from_ours: 0,
|
|
132
|
-
entries_from_theirs: theirs.length - shared,
|
|
133
|
-
entries_total: theirs.length,
|
|
134
|
-
reanchored: false,
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
const oursSuffix = ours.slice(shared);
|
|
138
|
-
const seenIdentities = new Set(oursSuffix.map(historyEntryIdentity));
|
|
139
|
-
const theirsSuffix = theirs
|
|
140
|
-
.slice(shared)
|
|
141
|
-
.filter((entry) => !seenIdentities.has(historyEntryIdentity(entry)));
|
|
142
|
-
const mergedSuffix = [...oursSuffix, ...theirsSuffix].sort((left, right) => {
|
|
143
|
-
const byTs = compareTimestampStrings(left.ts, right.ts);
|
|
144
|
-
if (byTs !== 0) {
|
|
145
|
-
return byTs;
|
|
146
|
-
}
|
|
147
|
-
const leftFromOurs = seenIdentities.has(historyEntryIdentity(left));
|
|
148
|
-
const rightFromOurs = seenIdentities.has(historyEntryIdentity(right));
|
|
149
|
-
if (leftFromOurs !== rightFromOurs) {
|
|
150
|
-
return Number(rightFromOurs) - Number(leftFromOurs);
|
|
151
|
-
}
|
|
152
|
-
return historyEntryIdentity(left).localeCompare(historyEntryIdentity(right));
|
|
153
|
-
});
|
|
154
|
-
const reanchored = reanchorHistoryEntries([...ours.slice(0, shared), ...mergedSuffix], undefined, { continuousHashSurface: true });
|
|
155
|
-
if (reanchored.skippedOps > 0) {
|
|
156
|
-
throw new PmCliError(`History union cannot preserve ${reanchored.skippedOps} patch operation(s) after deterministic suffix ordering; leave the file unresolved for manual recovery.`, EXIT_CODE.GENERIC_FAILURE);
|
|
157
|
-
}
|
|
158
|
-
return {
|
|
159
|
-
merged: historyEntriesToRaw(reanchored.entries),
|
|
160
|
-
strategy: "union_reanchor",
|
|
161
|
-
common_entries: shared,
|
|
162
|
-
entries_from_ours: oursSuffix.length,
|
|
163
|
-
entries_from_theirs: theirsSuffix.length,
|
|
164
|
-
entries_total: reanchored.entries.length,
|
|
165
|
-
reanchored: true,
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
function parseRelationshipJsonl(raw, label) {
|
|
169
|
-
const conflictMarker = findFirstMergeConflictMarker(raw);
|
|
170
|
-
if (conflictMarker) {
|
|
171
|
-
throw new PmCliError(`Relationship merge input (${label}) contains unresolved conflict markers at line ${conflictMarker.line}. Merge drivers must receive clean per-side versions.`, EXIT_CODE.GENERIC_FAILURE);
|
|
172
|
-
}
|
|
173
|
-
const events = [];
|
|
174
|
-
for (const [index, rawLine] of raw.split(/\r?\n/).entries()) {
|
|
175
|
-
const line = rawLine.trim();
|
|
176
|
-
if (!line) {
|
|
177
|
-
continue;
|
|
178
|
-
}
|
|
179
|
-
let parsed;
|
|
180
|
-
try {
|
|
181
|
-
parsed = JSON.parse(line);
|
|
182
|
-
}
|
|
183
|
-
catch {
|
|
184
|
-
throw new PmCliError(`Relationship merge input (${label}) contains invalid JSON at line ${index + 1}.`, EXIT_CODE.GENERIC_FAILURE);
|
|
185
|
-
}
|
|
186
|
-
if (typeof parsed !== "object" ||
|
|
187
|
-
parsed === null ||
|
|
188
|
-
Array.isArray(parsed) ||
|
|
189
|
-
typeof parsed.eventId !== "string" ||
|
|
190
|
-
typeof parsed.sequence !== "number") {
|
|
191
|
-
throw new PmCliError(`Relationship merge input (${label}) line ${index + 1} is not a relationship event (eventId + sequence required).`, EXIT_CODE.GENERIC_FAILURE);
|
|
192
|
-
}
|
|
193
|
-
events.push(parsed);
|
|
194
|
-
}
|
|
195
|
-
return events;
|
|
196
|
-
}
|
|
197
|
-
function relationshipEventsToRaw(events) {
|
|
198
|
-
return events.length === 0
|
|
199
|
-
? ""
|
|
200
|
-
: `${events.map((event) => JSON.stringify(event)).join("\n")}\n`;
|
|
201
|
-
}
|
|
202
|
-
function renumberRelationshipEvents(events) {
|
|
203
|
-
return events.map((event, index) => ({ ...event, sequence: index + 1 }));
|
|
204
|
-
}
|
|
205
|
-
/**
|
|
206
|
-
* Deterministically merge two diverged versions of one append-only
|
|
207
|
-
* relationship event JSONL store. The store's loader enforces strictly
|
|
208
|
-
* consecutive one-based `sequence` values, so a plain text union of two
|
|
209
|
-
* diverged streams is always unreadable; this merge keeps the shared prefix,
|
|
210
|
-
* unions both divergent suffixes by `eventId` (ordered by event timestamp,
|
|
211
|
-
* ours-first on ties), and renumbers every merged sequence consecutively so
|
|
212
|
-
* the resulting stream loads again. No side's events are ever discarded.
|
|
213
|
-
*/
|
|
214
|
-
export function mergeRelationshipEventStreams(baseRaw, oursRaw, theirsRaw) {
|
|
215
|
-
parseRelationshipJsonl(baseRaw, "base");
|
|
216
|
-
const ours = parseRelationshipJsonl(oursRaw, "ours");
|
|
217
|
-
const theirs = parseRelationshipJsonl(theirsRaw, "theirs");
|
|
218
|
-
const limit = Math.min(ours.length, theirs.length);
|
|
219
|
-
let shared = 0;
|
|
220
|
-
while (shared < limit && ours[shared].eventId === theirs[shared].eventId) {
|
|
221
|
-
shared += 1;
|
|
222
|
-
}
|
|
223
|
-
if (shared === ours.length && shared === theirs.length) {
|
|
224
|
-
return {
|
|
225
|
-
merged: relationshipEventsToRaw(ours),
|
|
226
|
-
strategy: "identical",
|
|
227
|
-
common_entries: shared,
|
|
228
|
-
entries_from_ours: 0,
|
|
229
|
-
entries_from_theirs: 0,
|
|
230
|
-
entries_total: ours.length,
|
|
231
|
-
reanchored: false,
|
|
232
|
-
};
|
|
233
|
-
}
|
|
234
|
-
if (shared === theirs.length) {
|
|
235
|
-
return {
|
|
236
|
-
merged: relationshipEventsToRaw(ours),
|
|
237
|
-
strategy: "fast_forward_ours",
|
|
238
|
-
common_entries: shared,
|
|
239
|
-
entries_from_ours: ours.length - shared,
|
|
240
|
-
entries_from_theirs: 0,
|
|
241
|
-
entries_total: ours.length,
|
|
242
|
-
reanchored: false,
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
if (shared === ours.length) {
|
|
246
|
-
return {
|
|
247
|
-
merged: relationshipEventsToRaw(theirs),
|
|
248
|
-
strategy: "fast_forward_theirs",
|
|
249
|
-
common_entries: shared,
|
|
250
|
-
entries_from_ours: 0,
|
|
251
|
-
entries_from_theirs: theirs.length - shared,
|
|
252
|
-
entries_total: theirs.length,
|
|
253
|
-
reanchored: false,
|
|
254
|
-
};
|
|
255
|
-
}
|
|
256
|
-
const oursSuffix = ours.slice(shared);
|
|
257
|
-
const oursEventIds = new Set(oursSuffix.map((event) => event.eventId));
|
|
258
|
-
const theirsSuffix = theirs
|
|
259
|
-
.slice(shared)
|
|
260
|
-
.filter((event) => !oursEventIds.has(event.eventId));
|
|
261
|
-
const mergedSuffix = [...oursSuffix, ...theirsSuffix].sort((left, right) => {
|
|
262
|
-
if (left.timestamp !== undefined && right.timestamp !== undefined) {
|
|
263
|
-
const byTs = compareTimestampStrings(left.timestamp, right.timestamp);
|
|
264
|
-
if (byTs !== 0) {
|
|
265
|
-
return byTs;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
else if (left.timestamp === undefined && right.timestamp !== undefined) {
|
|
269
|
-
return 1;
|
|
270
|
-
}
|
|
271
|
-
else if (left.timestamp !== undefined) {
|
|
272
|
-
return -1;
|
|
273
|
-
}
|
|
274
|
-
const leftFromOurs = oursEventIds.has(left.eventId);
|
|
275
|
-
const rightFromOurs = oursEventIds.has(right.eventId);
|
|
276
|
-
if (leftFromOurs !== rightFromOurs) {
|
|
277
|
-
return Number(rightFromOurs) - Number(leftFromOurs);
|
|
278
|
-
}
|
|
279
|
-
// Array.prototype.sort is stable: returning zero preserves each side's
|
|
280
|
-
// original append order when its events share a timestamp.
|
|
281
|
-
return 0;
|
|
282
|
-
});
|
|
283
|
-
const merged = renumberRelationshipEvents([
|
|
284
|
-
...ours.slice(0, shared),
|
|
285
|
-
...mergedSuffix,
|
|
286
|
-
]);
|
|
287
|
-
return {
|
|
288
|
-
merged: relationshipEventsToRaw(merged),
|
|
289
|
-
strategy: "union_reanchor",
|
|
290
|
-
common_entries: shared,
|
|
291
|
-
entries_from_ours: oursSuffix.length,
|
|
292
|
-
entries_from_theirs: theirsSuffix.length,
|
|
293
|
-
entries_total: merged.length,
|
|
294
|
-
reanchored: true,
|
|
295
|
-
};
|
|
296
|
-
}
|
|
297
|
-
/**
|
|
298
|
-
* Item metadata collections merged by element-identity set semantics instead
|
|
299
|
-
* of whole-field three-way comparison: concurrent commutative appends from two
|
|
300
|
-
* branches (notes on A, tags on B, a test run on each) must merge cleanly.
|
|
301
|
-
* Element identity is the stable serialization of the normalized element, so
|
|
302
|
-
* an element edited on one side counts as remove-old + add-new and both sides'
|
|
303
|
-
* contributions survive. Plan collections are deliberately excluded: plan
|
|
304
|
-
* steps carry cross-element ordering invariants that set-union would corrupt,
|
|
305
|
-
* so they stay under scalar three-way semantics with explicit conflicts.
|
|
306
|
-
*/
|
|
307
|
-
export const ITEM_UNION_COLLECTION_FIELDS = [
|
|
308
|
-
"tags",
|
|
309
|
-
"dependencies",
|
|
310
|
-
"comments",
|
|
311
|
-
"notes",
|
|
312
|
-
"learnings",
|
|
313
|
-
"files",
|
|
314
|
-
"tests",
|
|
315
|
-
"test_runs",
|
|
316
|
-
"docs",
|
|
317
|
-
"reminders",
|
|
318
|
-
"events",
|
|
319
|
-
];
|
|
320
|
-
/** Delimiter-backed list fields that use the same lossless element merge as native metadata collections while retaining their legacy scalar storage shape. */
|
|
321
|
-
const ITEM_UNION_DELIMITED_FIELDS = ["acceptance_criteria"];
|
|
322
|
-
/** Item metadata fields resolved by deterministic latest-timestamp-wins instead of three-way conflict, so disjoint-field edits never conflict on the shared freshness scalar every mutation rewrites. */
|
|
323
|
-
export const ITEM_LATEST_TIMESTAMP_FIELDS = ["updated_at"];
|
|
324
|
-
function jsonEquals(left, right) {
|
|
325
|
-
if (left === undefined || right === undefined) {
|
|
326
|
-
return left === undefined && right === undefined;
|
|
327
|
-
}
|
|
328
|
-
return stableStringify(left) === stableStringify(right);
|
|
329
|
-
}
|
|
330
|
-
function unionCollection(base, ours, theirs) {
|
|
331
|
-
const toEntries = (value) => Array.isArray(value) ? value : [];
|
|
332
|
-
const baseIds = new Set(toEntries(base).map((entry) => stableStringify(entry)));
|
|
333
|
-
const oursEntries = toEntries(ours);
|
|
334
|
-
const oursIds = new Set(oursEntries.map((entry) => stableStringify(entry)));
|
|
335
|
-
const theirsEntries = toEntries(theirs);
|
|
336
|
-
const theirsIds = new Set(theirsEntries.map((entry) => stableStringify(entry)));
|
|
337
|
-
const merged = oursEntries.filter((entry) => {
|
|
338
|
-
const identity = stableStringify(entry);
|
|
339
|
-
// An element kept by ours survives unless theirs deleted it (present in
|
|
340
|
-
// base, absent from theirs).
|
|
341
|
-
return !baseIds.has(identity) || theirsIds.has(identity);
|
|
342
|
-
});
|
|
343
|
-
for (const entry of theirsEntries) {
|
|
344
|
-
const identity = stableStringify(entry);
|
|
345
|
-
if (!oursIds.has(identity) && !baseIds.has(identity)) {
|
|
346
|
-
merged.push(entry);
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
return merged;
|
|
350
|
-
}
|
|
351
|
-
function linkedTestDefinitionIdentity(value) {
|
|
352
|
-
/* c8 ignore start -- canonical item parsing normalizes linked-test entries to object records */
|
|
353
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
354
|
-
return stableStringify(value);
|
|
355
|
-
}
|
|
356
|
-
/* c8 ignore stop */
|
|
357
|
-
const { provenance: _provenance, ...definition } = value;
|
|
358
|
-
return stableStringify(definition);
|
|
359
|
-
}
|
|
360
|
-
function markMergedLinkedTest(value) {
|
|
361
|
-
/* c8 ignore start -- canonical item parsing normalizes linked-test entries to object records */
|
|
362
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
363
|
-
return value;
|
|
364
|
-
}
|
|
365
|
-
/* c8 ignore stop */
|
|
366
|
-
const test = value;
|
|
367
|
-
if (!test.provenance) {
|
|
368
|
-
return {
|
|
369
|
-
...test,
|
|
370
|
-
// Legacy entries contributed by another branch must not regain implicit
|
|
371
|
-
// local trust. This incomplete merge marker is preserved by normalization
|
|
372
|
-
// and refused until an operator acknowledges it in the receiving clone.
|
|
373
|
-
provenance: { source_kind: "merge_union" },
|
|
374
|
-
};
|
|
375
|
-
}
|
|
376
|
-
return {
|
|
377
|
-
...test,
|
|
378
|
-
provenance: {
|
|
379
|
-
...test.provenance,
|
|
380
|
-
source_kind: "merge_union",
|
|
381
|
-
},
|
|
382
|
-
};
|
|
383
|
-
}
|
|
384
|
-
function unionLinkedTests(base, ours, theirs) {
|
|
385
|
-
const toEntries = (value) => Array.isArray(value) ? value : [];
|
|
386
|
-
const baseIds = new Set(toEntries(base).map(linkedTestDefinitionIdentity));
|
|
387
|
-
const oursEntries = toEntries(ours);
|
|
388
|
-
const oursIds = new Set(oursEntries.map(linkedTestDefinitionIdentity));
|
|
389
|
-
const theirsEntries = toEntries(theirs);
|
|
390
|
-
const theirsIds = new Set(theirsEntries.map(linkedTestDefinitionIdentity));
|
|
391
|
-
const merged = oursEntries.filter((entry) => {
|
|
392
|
-
const identity = linkedTestDefinitionIdentity(entry);
|
|
393
|
-
return !baseIds.has(identity) || theirsIds.has(identity);
|
|
394
|
-
});
|
|
395
|
-
for (const entry of theirsEntries) {
|
|
396
|
-
const identity = linkedTestDefinitionIdentity(entry);
|
|
397
|
-
if (!oursIds.has(identity) && !baseIds.has(identity)) {
|
|
398
|
-
merged.push(markMergedLinkedTest(entry));
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
return merged;
|
|
402
|
-
}
|
|
403
|
-
function latestTimestamp(ours, theirs) {
|
|
404
|
-
return compareTimestampStrings(String(ours), String(theirs)) >= 0
|
|
405
|
-
? ours
|
|
406
|
-
: theirs;
|
|
407
|
-
}
|
|
408
|
-
function mergeScalarThreeWay(base, ours, theirs, preferred) {
|
|
409
|
-
if (jsonEquals(ours, theirs)) {
|
|
410
|
-
return { value: ours, from_theirs: false, conflict: false };
|
|
411
|
-
}
|
|
412
|
-
if (jsonEquals(base, ours)) {
|
|
413
|
-
return { value: theirs, from_theirs: true, conflict: false };
|
|
414
|
-
}
|
|
415
|
-
if (jsonEquals(base, theirs)) {
|
|
416
|
-
return { value: ours, from_theirs: false, conflict: false };
|
|
417
|
-
}
|
|
418
|
-
return {
|
|
419
|
-
value: preferred === "theirs" ? theirs : ours,
|
|
420
|
-
from_theirs: preferred === "theirs",
|
|
421
|
-
conflict: true,
|
|
422
|
-
};
|
|
423
|
-
}
|
|
424
|
-
/** Resolve one item scalar with explicit recency and deterministic tie evidence. */
|
|
425
|
-
function mergeItemScalarThreeWay(base, ours, theirs, preferred, conflictResolution, oursUpdatedAt, theirsUpdatedAt) {
|
|
426
|
-
const outcome = mergeScalarThreeWay(base, ours, theirs, preferred);
|
|
427
|
-
if (!outcome.conflict) {
|
|
428
|
-
return { ...outcome, conflict: false };
|
|
429
|
-
}
|
|
430
|
-
if (conflictResolution === "preferred_side") {
|
|
431
|
-
return {
|
|
432
|
-
...outcome,
|
|
433
|
-
resolution_basis: "requested_preference",
|
|
434
|
-
retained_side: outcome.from_theirs ? "theirs" : "ours",
|
|
435
|
-
};
|
|
436
|
-
}
|
|
437
|
-
const timestampOrder = compareTimestampStrings(oursUpdatedAt, theirsUpdatedAt);
|
|
438
|
-
if (conflictResolution === "latest_document_update" && timestampOrder !== 0) {
|
|
439
|
-
const retainTheirs = timestampOrder < 0;
|
|
440
|
-
return {
|
|
441
|
-
value: retainTheirs ? theirs : ours,
|
|
442
|
-
from_theirs: retainTheirs,
|
|
443
|
-
conflict: true,
|
|
444
|
-
resolution_basis: "document_updated_at",
|
|
445
|
-
retained_side: retainTheirs ? "theirs" : "ours",
|
|
446
|
-
};
|
|
447
|
-
}
|
|
448
|
-
const retainTheirs = stableStringify(encodeItemScalarDecisionValue(theirs)).localeCompare(stableStringify(encodeItemScalarDecisionValue(ours))) < 0;
|
|
449
|
-
return {
|
|
450
|
-
value: retainTheirs ? theirs : ours,
|
|
451
|
-
from_theirs: retainTheirs,
|
|
452
|
-
conflict: true,
|
|
453
|
-
resolution_basis: "stable_value_tiebreak",
|
|
454
|
-
retained_side: retainTheirs ? "theirs" : "ours",
|
|
455
|
-
};
|
|
456
|
-
}
|
|
457
|
-
function toMetadataRecord(document) {
|
|
458
|
-
return { ...document.metadata };
|
|
459
|
-
}
|
|
460
|
-
/** Parse one merge side and reject values reserved for internal absence evidence. */
|
|
461
|
-
function parseItemMergeSide(raw, label, options) {
|
|
462
|
-
try {
|
|
463
|
-
const document = canonicalDocument(parseItemDocument(raw, options), options);
|
|
464
|
-
const reservedField = Object.entries(document.metadata).find(([, value]) => isItemScalarMissingValue(value))?.[0];
|
|
465
|
-
if (reservedField !== undefined) {
|
|
466
|
-
throw new TypeError(`metadata field ${reservedField} uses the reserved missing-scalar evidence marker`);
|
|
467
|
-
}
|
|
468
|
-
return document;
|
|
469
|
-
}
|
|
470
|
-
catch (error) {
|
|
471
|
-
throw new PmCliError(`Item merge input (${label}) is not a readable item document: ${String(error)}`, EXIT_CODE.GENERIC_FAILURE);
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
/** Merge one collection field and report whether the field was handled. */
|
|
475
|
-
function mergeItemUnionField(params) {
|
|
476
|
-
if (!params.unionFieldSet.has(params.field))
|
|
477
|
-
return false;
|
|
478
|
-
const union = params.field === "tests"
|
|
479
|
-
? unionLinkedTests(params.baseValue, params.oursValue, params.theirsValue)
|
|
480
|
-
: params.field === "acceptance_criteria"
|
|
481
|
-
? unionCollection(splitAcceptanceCriteria(params.baseValue), splitAcceptanceCriteria(params.oursValue), splitAcceptanceCriteria(params.theirsValue)).join("; ")
|
|
482
|
-
: unionCollection(params.baseValue, params.oursValue, params.theirsValue);
|
|
483
|
-
params.accumulator.merged[params.field] = union;
|
|
484
|
-
if (!jsonEquals(union, params.oursValue)) {
|
|
485
|
-
params.accumulator.unionFields.push(params.field);
|
|
486
|
-
}
|
|
487
|
-
return true;
|
|
488
|
-
}
|
|
489
|
-
/** Merge one scalar field while preserving conflict evidence. */
|
|
490
|
-
function mergeItemScalarField(params) {
|
|
491
|
-
const outcome = mergeItemScalarThreeWay(params.baseValue, params.oursValue, params.theirsValue, params.preferred, params.conflictResolution, params.oursUpdatedAt, params.theirsUpdatedAt);
|
|
492
|
-
if (outcome.value !== undefined) {
|
|
493
|
-
params.accumulator.merged[params.field] = outcome.value;
|
|
494
|
-
}
|
|
495
|
-
if (outcome.conflict) {
|
|
496
|
-
params.accumulator.conflictFields.push(params.field);
|
|
497
|
-
params.accumulator.conflictDecisions.push({
|
|
498
|
-
field: params.field,
|
|
499
|
-
base: encodeItemScalarDecisionValue(params.baseValue),
|
|
500
|
-
ours: encodeItemScalarDecisionValue(params.oursValue),
|
|
501
|
-
theirs: encodeItemScalarDecisionValue(params.theirsValue),
|
|
502
|
-
retained: encodeItemScalarDecisionValue(outcome.value),
|
|
503
|
-
discarded: encodeItemScalarDecisionValue(outcome.from_theirs ? params.oursValue : params.theirsValue),
|
|
504
|
-
retained_side: outcome.retained_side,
|
|
505
|
-
resolution_basis: outcome.resolution_basis,
|
|
506
|
-
});
|
|
507
|
-
}
|
|
508
|
-
else if (outcome.from_theirs) {
|
|
509
|
-
params.accumulator.fieldsFromTheirs.push(params.field);
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
function mergeItemMetadataRecords(baseRecord, oursRecord, theirsRecord, preferred, conflictResolution, oursUpdatedAt, theirsUpdatedAt) {
|
|
513
|
-
const unionFieldSet = new Set([
|
|
514
|
-
...ITEM_UNION_COLLECTION_FIELDS,
|
|
515
|
-
...ITEM_UNION_DELIMITED_FIELDS,
|
|
516
|
-
]);
|
|
517
|
-
const latestFieldSet = new Set(ITEM_LATEST_TIMESTAMP_FIELDS);
|
|
518
|
-
const fieldNames = [
|
|
519
|
-
...new Set([...Object.keys(oursRecord), ...Object.keys(theirsRecord)]),
|
|
520
|
-
].sort((left, right) => left.localeCompare(right));
|
|
521
|
-
const accumulator = {
|
|
522
|
-
merged: {},
|
|
523
|
-
conflictFields: [],
|
|
524
|
-
fieldsFromTheirs: [],
|
|
525
|
-
unionFields: [],
|
|
526
|
-
conflictDecisions: [],
|
|
527
|
-
};
|
|
528
|
-
for (const field of fieldNames) {
|
|
529
|
-
const baseValue = baseRecord[field];
|
|
530
|
-
const oursValue = oursRecord[field];
|
|
531
|
-
const theirsValue = theirsRecord[field];
|
|
532
|
-
if (mergeItemUnionField({
|
|
533
|
-
field,
|
|
534
|
-
baseValue,
|
|
535
|
-
oursValue,
|
|
536
|
-
theirsValue,
|
|
537
|
-
unionFieldSet,
|
|
538
|
-
accumulator,
|
|
539
|
-
})) {
|
|
540
|
-
// `fieldNames` is derived from both records' own keys, so a collection
|
|
541
|
-
// reached here is declared by at least one side. Preserve even an empty
|
|
542
|
-
// result (`tags` is required) so serialization never drops it.
|
|
543
|
-
continue;
|
|
544
|
-
}
|
|
545
|
-
if (latestFieldSet.has(field)) {
|
|
546
|
-
accumulator.merged[field] = latestTimestamp(oursValue, theirsValue);
|
|
547
|
-
continue;
|
|
548
|
-
}
|
|
549
|
-
mergeItemScalarField({
|
|
550
|
-
field,
|
|
551
|
-
baseValue,
|
|
552
|
-
oursValue,
|
|
553
|
-
theirsValue,
|
|
554
|
-
preferred,
|
|
555
|
-
conflictResolution,
|
|
556
|
-
oursUpdatedAt,
|
|
557
|
-
theirsUpdatedAt,
|
|
558
|
-
accumulator,
|
|
559
|
-
});
|
|
560
|
-
}
|
|
561
|
-
return accumulator;
|
|
562
|
-
}
|
|
563
|
-
/**
|
|
564
|
-
* Field-aware three-way merge of one tracker item document. Commutative
|
|
565
|
-
* collections (notes, tags, tests, ...) union by element identity so
|
|
566
|
-
* concurrent appends never conflict; `updated_at` resolves to the latest
|
|
567
|
-
* timestamp so disjoint-field edits never collide on the shared freshness
|
|
568
|
-
* scalar; every other metadata field and the body use classic three-way
|
|
569
|
-
* resolution. Unresolvable both-sides-changed conflicts use the configured
|
|
570
|
-
* scalar policy but remain reported in `conflict_fields`, and the merged output
|
|
571
|
-
* is always canonically serialized — count-prefixed TOON array headers are
|
|
572
|
-
* recomputed, so the hand-resolution corruption class (stale `notes[N]`
|
|
573
|
-
* headers) cannot be produced by this merge.
|
|
574
|
-
*/
|
|
575
|
-
export function mergeItemDocuments(baseRaw, oursRaw, theirsRaw, options = {}) {
|
|
576
|
-
const preferred = options.preferred ?? "ours";
|
|
577
|
-
const conflictResolution = options.conflictResolution ?? "preferred_side";
|
|
578
|
-
// An empty base means both branches created the file independently
|
|
579
|
-
// (add/add); three-way falls back to treating every differing field as an
|
|
580
|
-
// ours/theirs decision, which the scalar resolver already models.
|
|
581
|
-
const hasBase = baseRaw.trim().length > 0;
|
|
582
|
-
const ours = parseItemMergeSide(oursRaw, "ours", options);
|
|
583
|
-
const theirs = parseItemMergeSide(theirsRaw, "theirs", options);
|
|
584
|
-
const base = hasBase ? parseItemMergeSide(baseRaw, "base", options) : ours;
|
|
585
|
-
const baseRecord = hasBase
|
|
586
|
-
? toMetadataRecord(base)
|
|
587
|
-
: {};
|
|
588
|
-
const oursRecord = toMetadataRecord(ours);
|
|
589
|
-
const theirsRecord = toMetadataRecord(theirs);
|
|
590
|
-
const { merged, conflictFields, fieldsFromTheirs, unionFields, conflictDecisions, } = mergeItemMetadataRecords(baseRecord, oursRecord, theirsRecord, preferred, conflictResolution, ours.metadata.updated_at, theirs.metadata.updated_at);
|
|
591
|
-
const bodyOutcome = mergeItemScalarThreeWay(hasBase ? base.body : "", ours.body, theirs.body, preferred, conflictResolution, ours.metadata.updated_at, theirs.metadata.updated_at);
|
|
592
|
-
if (bodyOutcome.conflict) {
|
|
593
|
-
conflictFields.push("body");
|
|
594
|
-
conflictDecisions.push({
|
|
595
|
-
field: "body",
|
|
596
|
-
base: encodeItemScalarDecisionValue(hasBase ? base.body : ""),
|
|
597
|
-
ours: encodeItemScalarDecisionValue(ours.body),
|
|
598
|
-
theirs: encodeItemScalarDecisionValue(theirs.body),
|
|
599
|
-
retained: encodeItemScalarDecisionValue(bodyOutcome.value),
|
|
600
|
-
discarded: encodeItemScalarDecisionValue(bodyOutcome.from_theirs ? ours.body : theirs.body),
|
|
601
|
-
retained_side: bodyOutcome.retained_side,
|
|
602
|
-
resolution_basis: bodyOutcome.resolution_basis,
|
|
603
|
-
});
|
|
604
|
-
}
|
|
605
|
-
else if (bodyOutcome.from_theirs) {
|
|
606
|
-
fieldsFromTheirs.push("body");
|
|
607
|
-
}
|
|
608
|
-
const mergedRaw = serializeItemDocument({
|
|
609
|
-
metadata: merged,
|
|
610
|
-
body: bodyOutcome.value,
|
|
611
|
-
}, options);
|
|
612
|
-
return {
|
|
613
|
-
merged: mergedRaw,
|
|
614
|
-
conflict_fields: conflictFields,
|
|
615
|
-
fields_from_theirs: fieldsFromTheirs,
|
|
616
|
-
union_fields: unionFields,
|
|
617
|
-
conflict_decisions: conflictDecisions,
|
|
618
|
-
requested_preference: preferred,
|
|
619
|
-
requested_preference_applied: conflictResolution === "preferred_side",
|
|
620
|
-
conflict_resolution: conflictResolution,
|
|
621
|
-
};
|
|
622
|
-
}
|
|
623
|
-
function isPlainObject(value) {
|
|
624
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
625
|
-
}
|
|
626
|
-
function parseJsonSide(raw, label) {
|
|
627
|
-
const conflictMarker = findFirstMergeConflictMarker(raw);
|
|
628
|
-
if (conflictMarker) {
|
|
629
|
-
throw new PmCliError(`JSON merge input (${label}) contains unresolved conflict markers at line ${conflictMarker.line}.`, EXIT_CODE.GENERIC_FAILURE);
|
|
630
|
-
}
|
|
631
|
-
if (raw.trim().length === 0) {
|
|
632
|
-
return undefined;
|
|
633
|
-
}
|
|
634
|
-
try {
|
|
635
|
-
return JSON.parse(raw);
|
|
636
|
-
}
|
|
637
|
-
catch {
|
|
638
|
-
throw new PmCliError(`JSON merge input (${label}) is not valid JSON.`, EXIT_CODE.GENERIC_FAILURE);
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
function mergeAdditiveJsonArrays(base, ours, theirs) {
|
|
642
|
-
if (!Array.isArray(base) || !Array.isArray(ours) || !Array.isArray(theirs)) {
|
|
643
|
-
return null;
|
|
644
|
-
}
|
|
645
|
-
const countEntries = (entries) => {
|
|
646
|
-
const counts = new Map();
|
|
647
|
-
for (const entry of entries) {
|
|
648
|
-
const identity = stableStringify(entry);
|
|
649
|
-
counts.set(identity, (counts.get(identity) ?? 0) + 1);
|
|
650
|
-
}
|
|
651
|
-
return counts;
|
|
652
|
-
};
|
|
653
|
-
const baseCounts = countEntries(base);
|
|
654
|
-
const oursCounts = countEntries(ours);
|
|
655
|
-
const theirsCounts = countEntries(theirs);
|
|
656
|
-
for (const [identity, baseCount] of baseCounts) {
|
|
657
|
-
if ((oursCounts.get(identity) ?? 0) < baseCount ||
|
|
658
|
-
(theirsCounts.get(identity) ?? 0) < baseCount) {
|
|
659
|
-
return null;
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
const unmatchedOursCounts = new Map(oursCounts);
|
|
663
|
-
const additionsFromTheirs = [];
|
|
664
|
-
for (const entry of theirs) {
|
|
665
|
-
const identity = stableStringify(entry);
|
|
666
|
-
const unmatchedOursCount = unmatchedOursCounts.get(identity) ?? 0;
|
|
667
|
-
if (unmatchedOursCount > 0) {
|
|
668
|
-
unmatchedOursCounts.set(identity, unmatchedOursCount - 1);
|
|
669
|
-
}
|
|
670
|
-
else {
|
|
671
|
-
additionsFromTheirs.push(entry);
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
return {
|
|
675
|
-
merged: [...ours, ...additionsFromTheirs],
|
|
676
|
-
acceptedTheirs: additionsFromTheirs.length > 0,
|
|
677
|
-
};
|
|
678
|
-
}
|
|
679
|
-
function mergeJsonObjects(base, ours, theirs, preferred, pathPrefix, conflictPaths, pathsFromTheirs) {
|
|
680
|
-
if (!isPlainObject(ours) || !isPlainObject(theirs)) {
|
|
681
|
-
return null;
|
|
682
|
-
}
|
|
683
|
-
const baseObject = isPlainObject(base) ? base : {};
|
|
684
|
-
const merged = {};
|
|
685
|
-
const keys = [
|
|
686
|
-
...new Set([...Object.keys(ours), ...Object.keys(theirs)]),
|
|
687
|
-
].sort((left, right) => left.localeCompare(right));
|
|
688
|
-
for (const key of keys) {
|
|
689
|
-
const childPath = pathPrefix.length > 0 ? `${pathPrefix}.${key}` : key;
|
|
690
|
-
const child = mergeJsonKey(baseObject, ours, theirs, key, preferred, childPath, conflictPaths, pathsFromTheirs);
|
|
691
|
-
if (child !== undefined) {
|
|
692
|
-
merged[key] = child;
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
return merged;
|
|
696
|
-
}
|
|
697
|
-
function mergeJsonValue(base, ours, theirs, preferred, pathPrefix, conflictPaths, pathsFromTheirs) {
|
|
698
|
-
if (jsonEquals(ours, theirs)) {
|
|
699
|
-
return ours;
|
|
700
|
-
}
|
|
701
|
-
const additiveArrayMerge = mergeAdditiveJsonArrays(base, ours, theirs);
|
|
702
|
-
if (additiveArrayMerge !== null) {
|
|
703
|
-
if (additiveArrayMerge.acceptedTheirs) {
|
|
704
|
-
pathsFromTheirs.push(pathPrefix);
|
|
705
|
-
}
|
|
706
|
-
return additiveArrayMerge.merged;
|
|
707
|
-
}
|
|
708
|
-
const mergedObject = mergeJsonObjects(base, ours, theirs, preferred, pathPrefix, conflictPaths, pathsFromTheirs);
|
|
709
|
-
if (mergedObject !== null) {
|
|
710
|
-
return mergedObject;
|
|
711
|
-
}
|
|
712
|
-
const outcome = mergeScalarThreeWay(base, ours, theirs, preferred);
|
|
713
|
-
if (outcome.conflict) {
|
|
714
|
-
conflictPaths.push(pathPrefix);
|
|
715
|
-
}
|
|
716
|
-
else if (outcome.from_theirs) {
|
|
717
|
-
pathsFromTheirs.push(pathPrefix);
|
|
718
|
-
}
|
|
719
|
-
return outcome.value;
|
|
720
|
-
}
|
|
721
|
-
function mergeJsonKey(base, ours, theirs, key, preferred, childPath, conflictPaths, pathsFromTheirs) {
|
|
722
|
-
const inOurs = Object.prototype.hasOwnProperty.call(ours, key);
|
|
723
|
-
const inTheirs = Object.prototype.hasOwnProperty.call(theirs, key);
|
|
724
|
-
const inBase = Object.prototype.hasOwnProperty.call(base, key);
|
|
725
|
-
if (inOurs && inTheirs) {
|
|
726
|
-
return mergeJsonValue(base[key], ours[key], theirs[key], preferred, childPath, conflictPaths, pathsFromTheirs);
|
|
727
|
-
}
|
|
728
|
-
if (inOurs) {
|
|
729
|
-
// Present only in ours: either ours added it (keep) or theirs deleted it
|
|
730
|
-
// (honor the delete when ours left it untouched from base).
|
|
731
|
-
if (inBase && jsonEquals(base[key], ours[key])) {
|
|
732
|
-
pathsFromTheirs.push(childPath);
|
|
733
|
-
return undefined;
|
|
734
|
-
}
|
|
735
|
-
if (inBase) {
|
|
736
|
-
conflictPaths.push(childPath);
|
|
737
|
-
return preferred === "theirs" ? undefined : ours[key];
|
|
738
|
-
}
|
|
739
|
-
return ours[key];
|
|
740
|
-
}
|
|
741
|
-
// Present only in theirs (mirror of the branch above).
|
|
742
|
-
if (inBase && jsonEquals(base[key], theirs[key])) {
|
|
743
|
-
return undefined;
|
|
744
|
-
}
|
|
745
|
-
if (inBase) {
|
|
746
|
-
conflictPaths.push(childPath);
|
|
747
|
-
return preferred === "theirs" ? theirs[key] : undefined;
|
|
748
|
-
}
|
|
749
|
-
pathsFromTheirs.push(childPath);
|
|
750
|
-
return theirs[key];
|
|
751
|
-
}
|
|
752
|
-
/**
|
|
753
|
-
* Key-level three-way merge for the tracker's JSON configuration artifacts.
|
|
754
|
-
* Nested objects merge recursively per key, while arrays compose only when
|
|
755
|
-
* both branches preserve every base element and contribute additive entries.
|
|
756
|
-
* Removals or edits retain scalar three-way conflict semantics. The output is
|
|
757
|
-
* always valid JSON, so merged configuration cannot regress into the
|
|
758
|
-
* silent-defaults fallback that raw conflict markers cause.
|
|
759
|
-
*/
|
|
760
|
-
export function mergeJsonDocuments(baseRaw, oursRaw, theirsRaw, options = {}) {
|
|
761
|
-
const preferred = options.preferred ?? "ours";
|
|
762
|
-
const base = parseJsonSide(baseRaw, "base");
|
|
763
|
-
const ours = parseJsonSide(oursRaw, "ours");
|
|
764
|
-
const theirs = parseJsonSide(theirsRaw, "theirs");
|
|
765
|
-
const conflictPaths = [];
|
|
766
|
-
const pathsFromTheirs = [];
|
|
767
|
-
const merged = mergeJsonValue(base, ours, theirs, preferred, "", conflictPaths, pathsFromTheirs);
|
|
768
|
-
return {
|
|
769
|
-
merged: `${JSON.stringify(merged ?? null, null, 2)}\n`,
|
|
770
|
-
conflict_paths: conflictPaths,
|
|
771
|
-
paths_from_theirs: pathsFromTheirs,
|
|
772
|
-
preferred,
|
|
773
|
-
};
|
|
774
|
-
}
|
|
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]="c7f975a2-1793-5989-a7bb-b23705b623d0")}catch(e2){}})();import{historyEntriesToRaw,reanchorHistoryEntries}from"../../core/history/replay.js";import{canonicalDocument,parseItemDocument,serializeItemDocument}from"../../core/item/item-format.js";import{splitAcceptanceCriteria}from"../../core/item/parse.js";import{EXIT_CODE}from"../../core/shared/constants.js";import{findFirstMergeConflictMarker}from"../../core/shared/conflict-markers.js";import{PmCliError}from"../../core/shared/errors.js";import{sha256Hex,stableStringify}from"../../core/shared/serialization.js";import{compareTimestampStrings}from"../../core/shared/time.js";const ITEM_SCALAR_MISSING_VALUE=Object.freeze({pm_item_scalar_missing:true});function isItemScalarMissingValue(value){return typeof value==="object"&&value!==null&&!Array.isArray(value)&&Object.keys(value).length===1&&value.pm_item_scalar_missing===true}function encodeItemScalarDecisionValue(value){return value===void 0?ITEM_SCALAR_MISSING_VALUE:value}function hashItemScalarDecisionValue(value){return sha256Hex(stableStringify(value===void 0?{pm_item_scalar_presence:"missing"}:{pm_item_scalar_presence:"present",value}))}function parseHistoryJsonl(raw,label){const conflictMarker=findFirstMergeConflictMarker(raw);if(conflictMarker){throw new PmCliError(`History merge input (${label}) contains unresolved conflict markers at line ${conflictMarker.line}. Merge drivers must receive clean per-side versions.`,EXIT_CODE.GENERIC_FAILURE)}const entries=[];for(const[index,rawLine]of raw.split(/\r?\n/).entries()){const line=rawLine.trim();if(!line){continue}try{const parsed=JSON.parse(line);if(typeof parsed!=="object"||parsed===null||Array.isArray(parsed)){throw new TypeError("history entry must be an object")}entries.push(parsed)}catch{throw new PmCliError(`History merge input (${label}) contains invalid JSON at line ${index+1}.`,EXIT_CODE.GENERIC_FAILURE)}}return entries}function historyEntryIdentity(entry){return stableStringify({ts:entry.ts,author:entry.author,op:entry.op,patch:entry.patch,message:entry.message??null})}function commonHistoryPrefixLength(ours,theirs){const limit=Math.min(ours.length,theirs.length);let shared=0;while(shared<limit&&historyEntryIdentity(ours[shared])===historyEntryIdentity(theirs[shared])){shared+=1}return shared}function mergeHistoryStreams(baseRaw,oursRaw,theirsRaw){parseHistoryJsonl(baseRaw,"base");const ours=parseHistoryJsonl(oursRaw,"ours");const theirs=parseHistoryJsonl(theirsRaw,"theirs");const shared=commonHistoryPrefixLength(ours,theirs);if(shared===ours.length&&shared===theirs.length){return{merged:historyEntriesToRaw(ours),strategy:"identical",common_entries:shared,entries_from_ours:0,entries_from_theirs:0,entries_total:ours.length,reanchored:false}}if(shared===theirs.length){return{merged:historyEntriesToRaw(ours),strategy:"fast_forward_ours",common_entries:shared,entries_from_ours:ours.length-shared,entries_from_theirs:0,entries_total:ours.length,reanchored:false}}if(shared===ours.length){return{merged:historyEntriesToRaw(theirs),strategy:"fast_forward_theirs",common_entries:shared,entries_from_ours:0,entries_from_theirs:theirs.length-shared,entries_total:theirs.length,reanchored:false}}const oursSuffix=ours.slice(shared);const seenIdentities=new Set(oursSuffix.map(historyEntryIdentity));const theirsSuffix=theirs.slice(shared).filter(entry=>!seenIdentities.has(historyEntryIdentity(entry)));const mergedSuffix=[...oursSuffix,...theirsSuffix].sort((left,right)=>{const byTs=compareTimestampStrings(left.ts,right.ts);if(byTs!==0){return byTs}const leftFromOurs=seenIdentities.has(historyEntryIdentity(left));const rightFromOurs=seenIdentities.has(historyEntryIdentity(right));if(leftFromOurs!==rightFromOurs){return Number(rightFromOurs)-Number(leftFromOurs)}return historyEntryIdentity(left).localeCompare(historyEntryIdentity(right))});const reanchored=reanchorHistoryEntries([...ours.slice(0,shared),...mergedSuffix],void 0,{continuousHashSurface:true});if(reanchored.skippedOps>0){throw new PmCliError(`History union cannot preserve ${reanchored.skippedOps} patch operation(s) after deterministic suffix ordering; leave the file unresolved for manual recovery.`,EXIT_CODE.GENERIC_FAILURE)}return{merged:historyEntriesToRaw(reanchored.entries),strategy:"union_reanchor",common_entries:shared,entries_from_ours:oursSuffix.length,entries_from_theirs:theirsSuffix.length,entries_total:reanchored.entries.length,reanchored:true}}function parseRelationshipJsonl(raw,label){const conflictMarker=findFirstMergeConflictMarker(raw);if(conflictMarker){throw new PmCliError(`Relationship merge input (${label}) contains unresolved conflict markers at line ${conflictMarker.line}. Merge drivers must receive clean per-side versions.`,EXIT_CODE.GENERIC_FAILURE)}const events=[];for(const[index,rawLine]of raw.split(/\r?\n/).entries()){const line=rawLine.trim();if(!line){continue}let parsed;try{parsed=JSON.parse(line)}catch{throw new PmCliError(`Relationship merge input (${label}) contains invalid JSON at line ${index+1}.`,EXIT_CODE.GENERIC_FAILURE)}if(typeof parsed!=="object"||parsed===null||Array.isArray(parsed)||typeof parsed.eventId!=="string"||typeof parsed.sequence!=="number"){throw new PmCliError(`Relationship merge input (${label}) line ${index+1} is not a relationship event (eventId + sequence required).`,EXIT_CODE.GENERIC_FAILURE)}events.push(parsed)}return events}function relationshipEventsToRaw(events){return events.length===0?"":`${events.map(event=>JSON.stringify(event)).join("\n")}
|
|
2
|
+
`}function renumberRelationshipEvents(events){return events.map((event,index)=>({...event,sequence:index+1}))}function mergeRelationshipEventStreams(baseRaw,oursRaw,theirsRaw){parseRelationshipJsonl(baseRaw,"base");const ours=parseRelationshipJsonl(oursRaw,"ours");const theirs=parseRelationshipJsonl(theirsRaw,"theirs");const limit=Math.min(ours.length,theirs.length);let shared=0;while(shared<limit&&ours[shared].eventId===theirs[shared].eventId){shared+=1}if(shared===ours.length&&shared===theirs.length){return{merged:relationshipEventsToRaw(ours),strategy:"identical",common_entries:shared,entries_from_ours:0,entries_from_theirs:0,entries_total:ours.length,reanchored:false}}if(shared===theirs.length){return{merged:relationshipEventsToRaw(ours),strategy:"fast_forward_ours",common_entries:shared,entries_from_ours:ours.length-shared,entries_from_theirs:0,entries_total:ours.length,reanchored:false}}if(shared===ours.length){return{merged:relationshipEventsToRaw(theirs),strategy:"fast_forward_theirs",common_entries:shared,entries_from_ours:0,entries_from_theirs:theirs.length-shared,entries_total:theirs.length,reanchored:false}}const oursSuffix=ours.slice(shared);const oursEventIds=new Set(oursSuffix.map(event=>event.eventId));const theirsSuffix=theirs.slice(shared).filter(event=>!oursEventIds.has(event.eventId));const mergedSuffix=[...oursSuffix,...theirsSuffix].sort((left,right)=>{if(left.timestamp!==void 0&&right.timestamp!==void 0){const byTs=compareTimestampStrings(left.timestamp,right.timestamp);if(byTs!==0){return byTs}}else if(left.timestamp===void 0&&right.timestamp!==void 0){return 1}else if(left.timestamp!==void 0){return-1}const leftFromOurs=oursEventIds.has(left.eventId);const rightFromOurs=oursEventIds.has(right.eventId);if(leftFromOurs!==rightFromOurs){return Number(rightFromOurs)-Number(leftFromOurs)}return 0});const merged=renumberRelationshipEvents([...ours.slice(0,shared),...mergedSuffix]);return{merged:relationshipEventsToRaw(merged),strategy:"union_reanchor",common_entries:shared,entries_from_ours:oursSuffix.length,entries_from_theirs:theirsSuffix.length,entries_total:merged.length,reanchored:true}}const ITEM_UNION_COLLECTION_FIELDS=["tags","dependencies","comments","notes","learnings","files","tests","test_runs","docs","reminders","events"];const ITEM_UNION_DELIMITED_FIELDS=["acceptance_criteria"];const ITEM_LATEST_TIMESTAMP_FIELDS=["updated_at"];function jsonEquals(left,right){if(left===void 0||right===void 0){return left===void 0&&right===void 0}return stableStringify(left)===stableStringify(right)}function unionCollection(base,ours,theirs){const toEntries=value=>Array.isArray(value)?value:[];const baseIds=new Set(toEntries(base).map(entry=>stableStringify(entry)));const oursEntries=toEntries(ours);const oursIds=new Set(oursEntries.map(entry=>stableStringify(entry)));const theirsEntries=toEntries(theirs);const theirsIds=new Set(theirsEntries.map(entry=>stableStringify(entry)));const merged=oursEntries.filter(entry=>{const identity=stableStringify(entry);return!baseIds.has(identity)||theirsIds.has(identity)});for(const entry of theirsEntries){const identity=stableStringify(entry);if(!oursIds.has(identity)&&!baseIds.has(identity)){merged.push(entry)}}return merged}function linkedTestDefinitionIdentity(value){if(typeof value!=="object"||value===null||Array.isArray(value)){return stableStringify(value)}const{provenance:_provenance,...definition}=value;return stableStringify(definition)}function markMergedLinkedTest(value){if(typeof value!=="object"||value===null||Array.isArray(value)){return value}const test=value;if(!test.provenance){return{...test,provenance:{source_kind:"merge_union"}}}return{...test,provenance:{...test.provenance,source_kind:"merge_union"}}}function unionLinkedTests(base,ours,theirs){const toEntries=value=>Array.isArray(value)?value:[];const baseIds=new Set(toEntries(base).map(linkedTestDefinitionIdentity));const oursEntries=toEntries(ours);const oursIds=new Set(oursEntries.map(linkedTestDefinitionIdentity));const theirsEntries=toEntries(theirs);const theirsIds=new Set(theirsEntries.map(linkedTestDefinitionIdentity));const merged=oursEntries.filter(entry=>{const identity=linkedTestDefinitionIdentity(entry);return!baseIds.has(identity)||theirsIds.has(identity)});for(const entry of theirsEntries){const identity=linkedTestDefinitionIdentity(entry);if(!oursIds.has(identity)&&!baseIds.has(identity)){merged.push(markMergedLinkedTest(entry))}}return merged}function latestTimestamp(ours,theirs){return compareTimestampStrings(String(ours),String(theirs))>=0?ours:theirs}function mergeScalarThreeWay(base,ours,theirs,preferred){if(jsonEquals(ours,theirs)){return{value:ours,from_theirs:false,conflict:false}}if(jsonEquals(base,ours)){return{value:theirs,from_theirs:true,conflict:false}}if(jsonEquals(base,theirs)){return{value:ours,from_theirs:false,conflict:false}}return{value:preferred==="theirs"?theirs:ours,from_theirs:preferred==="theirs",conflict:true}}function mergeItemScalarThreeWay(base,ours,theirs,preferred,conflictResolution,oursUpdatedAt,theirsUpdatedAt){const outcome=mergeScalarThreeWay(base,ours,theirs,preferred);if(!outcome.conflict){return{...outcome,conflict:false}}if(conflictResolution==="preferred_side"){return{...outcome,resolution_basis:"requested_preference",retained_side:outcome.from_theirs?"theirs":"ours"}}const timestampOrder=compareTimestampStrings(oursUpdatedAt,theirsUpdatedAt);if(conflictResolution==="latest_document_update"&×tampOrder!==0){const retainTheirs2=timestampOrder<0;return{value:retainTheirs2?theirs:ours,from_theirs:retainTheirs2,conflict:true,resolution_basis:"document_updated_at",retained_side:retainTheirs2?"theirs":"ours"}}const retainTheirs=stableStringify(encodeItemScalarDecisionValue(theirs)).localeCompare(stableStringify(encodeItemScalarDecisionValue(ours)))<0;return{value:retainTheirs?theirs:ours,from_theirs:retainTheirs,conflict:true,resolution_basis:"stable_value_tiebreak",retained_side:retainTheirs?"theirs":"ours"}}function toMetadataRecord(document){return{...document.metadata}}function parseItemMergeSide(raw,label,options){try{const document=canonicalDocument(parseItemDocument(raw,options),options);const reservedField=Object.entries(document.metadata).find(([,value])=>isItemScalarMissingValue(value))?.[0];if(reservedField!==void 0){throw new TypeError(`metadata field ${reservedField} uses the reserved missing-scalar evidence marker`)}return document}catch(error){throw new PmCliError(`Item merge input (${label}) is not a readable item document: ${String(error)}`,EXIT_CODE.GENERIC_FAILURE)}}function mergeItemUnionField(params){if(!params.unionFieldSet.has(params.field))return false;const union=params.field==="tests"?unionLinkedTests(params.baseValue,params.oursValue,params.theirsValue):params.field==="acceptance_criteria"?unionCollection(splitAcceptanceCriteria(params.baseValue),splitAcceptanceCriteria(params.oursValue),splitAcceptanceCriteria(params.theirsValue)).join("; "):unionCollection(params.baseValue,params.oursValue,params.theirsValue);params.accumulator.merged[params.field]=union;if(!jsonEquals(union,params.oursValue)){params.accumulator.unionFields.push(params.field)}return true}function mergeItemScalarField(params){const outcome=mergeItemScalarThreeWay(params.baseValue,params.oursValue,params.theirsValue,params.preferred,params.conflictResolution,params.oursUpdatedAt,params.theirsUpdatedAt);if(outcome.value!==void 0){params.accumulator.merged[params.field]=outcome.value}if(outcome.conflict){params.accumulator.conflictFields.push(params.field);params.accumulator.conflictDecisions.push({field:params.field,base:encodeItemScalarDecisionValue(params.baseValue),ours:encodeItemScalarDecisionValue(params.oursValue),theirs:encodeItemScalarDecisionValue(params.theirsValue),retained:encodeItemScalarDecisionValue(outcome.value),discarded:encodeItemScalarDecisionValue(outcome.from_theirs?params.oursValue:params.theirsValue),retained_side:outcome.retained_side,resolution_basis:outcome.resolution_basis})}else if(outcome.from_theirs){params.accumulator.fieldsFromTheirs.push(params.field)}}function mergeItemMetadataRecords(baseRecord,oursRecord,theirsRecord,preferred,conflictResolution,oursUpdatedAt,theirsUpdatedAt){const unionFieldSet=new Set([...ITEM_UNION_COLLECTION_FIELDS,...ITEM_UNION_DELIMITED_FIELDS]);const latestFieldSet=new Set(ITEM_LATEST_TIMESTAMP_FIELDS);const fieldNames=[...new Set([...Object.keys(oursRecord),...Object.keys(theirsRecord)])].sort((left,right)=>left.localeCompare(right));const accumulator={merged:{},conflictFields:[],fieldsFromTheirs:[],unionFields:[],conflictDecisions:[]};for(const field of fieldNames){const baseValue=baseRecord[field];const oursValue=oursRecord[field];const theirsValue=theirsRecord[field];if(mergeItemUnionField({field,baseValue,oursValue,theirsValue,unionFieldSet,accumulator})){continue}if(latestFieldSet.has(field)){accumulator.merged[field]=latestTimestamp(oursValue,theirsValue);continue}mergeItemScalarField({field,baseValue,oursValue,theirsValue,preferred,conflictResolution,oursUpdatedAt,theirsUpdatedAt,accumulator})}return accumulator}function mergeItemDocuments(baseRaw,oursRaw,theirsRaw,options={}){const preferred=options.preferred??"ours";const conflictResolution=options.conflictResolution??"preferred_side";const hasBase=baseRaw.trim().length>0;const ours=parseItemMergeSide(oursRaw,"ours",options);const theirs=parseItemMergeSide(theirsRaw,"theirs",options);const base=hasBase?parseItemMergeSide(baseRaw,"base",options):ours;const baseRecord=hasBase?toMetadataRecord(base):{};const oursRecord=toMetadataRecord(ours);const theirsRecord=toMetadataRecord(theirs);const{merged,conflictFields,fieldsFromTheirs,unionFields,conflictDecisions}=mergeItemMetadataRecords(baseRecord,oursRecord,theirsRecord,preferred,conflictResolution,ours.metadata.updated_at,theirs.metadata.updated_at);const bodyOutcome=mergeItemScalarThreeWay(hasBase?base.body:"",ours.body,theirs.body,preferred,conflictResolution,ours.metadata.updated_at,theirs.metadata.updated_at);if(bodyOutcome.conflict){conflictFields.push("body");conflictDecisions.push({field:"body",base:encodeItemScalarDecisionValue(hasBase?base.body:""),ours:encodeItemScalarDecisionValue(ours.body),theirs:encodeItemScalarDecisionValue(theirs.body),retained:encodeItemScalarDecisionValue(bodyOutcome.value),discarded:encodeItemScalarDecisionValue(bodyOutcome.from_theirs?ours.body:theirs.body),retained_side:bodyOutcome.retained_side,resolution_basis:bodyOutcome.resolution_basis})}else if(bodyOutcome.from_theirs){fieldsFromTheirs.push("body")}const mergedRaw=serializeItemDocument({metadata:merged,body:bodyOutcome.value},options);return{merged:mergedRaw,conflict_fields:conflictFields,fields_from_theirs:fieldsFromTheirs,union_fields:unionFields,conflict_decisions:conflictDecisions,requested_preference:preferred,requested_preference_applied:conflictResolution==="preferred_side",conflict_resolution:conflictResolution}}function isPlainObject(value){return typeof value==="object"&&value!==null&&!Array.isArray(value)}function parseJsonSide(raw,label){const conflictMarker=findFirstMergeConflictMarker(raw);if(conflictMarker){throw new PmCliError(`JSON merge input (${label}) contains unresolved conflict markers at line ${conflictMarker.line}.`,EXIT_CODE.GENERIC_FAILURE)}if(raw.trim().length===0){return void 0}try{return JSON.parse(raw)}catch{throw new PmCliError(`JSON merge input (${label}) is not valid JSON.`,EXIT_CODE.GENERIC_FAILURE)}}function mergeAdditiveJsonArrays(base,ours,theirs){if(!Array.isArray(base)||!Array.isArray(ours)||!Array.isArray(theirs)){return null}const countEntries=entries=>{const counts=new Map;for(const entry of entries){const identity=stableStringify(entry);counts.set(identity,(counts.get(identity)??0)+1)}return counts};const baseCounts=countEntries(base);const oursCounts=countEntries(ours);const theirsCounts=countEntries(theirs);for(const[identity,baseCount]of baseCounts){if((oursCounts.get(identity)??0)<baseCount||(theirsCounts.get(identity)??0)<baseCount){return null}}const unmatchedOursCounts=new Map(oursCounts);const additionsFromTheirs=[];for(const entry of theirs){const identity=stableStringify(entry);const unmatchedOursCount=unmatchedOursCounts.get(identity)??0;if(unmatchedOursCount>0){unmatchedOursCounts.set(identity,unmatchedOursCount-1)}else{additionsFromTheirs.push(entry)}}return{merged:[...ours,...additionsFromTheirs],acceptedTheirs:additionsFromTheirs.length>0}}function mergeJsonObjects(base,ours,theirs,preferred,pathPrefix,conflictPaths,pathsFromTheirs){if(!isPlainObject(ours)||!isPlainObject(theirs)){return null}const baseObject=isPlainObject(base)?base:{};const merged={};const keys=[...new Set([...Object.keys(ours),...Object.keys(theirs)])].sort((left,right)=>left.localeCompare(right));for(const key of keys){const childPath=pathPrefix.length>0?`${pathPrefix}.${key}`:key;const child=mergeJsonKey(baseObject,ours,theirs,key,preferred,childPath,conflictPaths,pathsFromTheirs);if(child!==void 0){merged[key]=child}}return merged}function mergeJsonValue(base,ours,theirs,preferred,pathPrefix,conflictPaths,pathsFromTheirs){if(jsonEquals(ours,theirs)){return ours}const additiveArrayMerge=mergeAdditiveJsonArrays(base,ours,theirs);if(additiveArrayMerge!==null){if(additiveArrayMerge.acceptedTheirs){pathsFromTheirs.push(pathPrefix)}return additiveArrayMerge.merged}const mergedObject=mergeJsonObjects(base,ours,theirs,preferred,pathPrefix,conflictPaths,pathsFromTheirs);if(mergedObject!==null){return mergedObject}const outcome=mergeScalarThreeWay(base,ours,theirs,preferred);if(outcome.conflict){conflictPaths.push(pathPrefix)}else if(outcome.from_theirs){pathsFromTheirs.push(pathPrefix)}return outcome.value}function mergeJsonKey(base,ours,theirs,key,preferred,childPath,conflictPaths,pathsFromTheirs){const inOurs=Object.prototype.hasOwnProperty.call(ours,key);const inTheirs=Object.prototype.hasOwnProperty.call(theirs,key);const inBase=Object.prototype.hasOwnProperty.call(base,key);if(inOurs&&inTheirs){return mergeJsonValue(base[key],ours[key],theirs[key],preferred,childPath,conflictPaths,pathsFromTheirs)}if(inOurs){if(inBase&&jsonEquals(base[key],ours[key])){pathsFromTheirs.push(childPath);return void 0}if(inBase){conflictPaths.push(childPath);return preferred==="theirs"?void 0:ours[key]}return ours[key]}if(inBase&&jsonEquals(base[key],theirs[key])){return void 0}if(inBase){conflictPaths.push(childPath);return preferred==="theirs"?theirs[key]:void 0}pathsFromTheirs.push(childPath);return theirs[key]}function mergeJsonDocuments(baseRaw,oursRaw,theirsRaw,options={}){const preferred=options.preferred??"ours";const base=parseJsonSide(baseRaw,"base");const ours=parseJsonSide(oursRaw,"ours");const theirs=parseJsonSide(theirsRaw,"theirs");const conflictPaths=[];const pathsFromTheirs=[];const merged=mergeJsonValue(base,ours,theirs,preferred,"",conflictPaths,pathsFromTheirs);return{merged:`${JSON.stringify(merged??null,null,2)}
|
|
3
|
+
`,conflict_paths:conflictPaths,paths_from_theirs:pathsFromTheirs,preferred}}export{ITEM_LATEST_TIMESTAMP_FIELDS,ITEM_SCALAR_MISSING_VALUE,ITEM_UNION_COLLECTION_FIELDS,encodeItemScalarDecisionValue,hashItemScalarDecisionValue,isItemScalarMissingValue,mergeHistoryStreams,mergeItemDocuments,mergeJsonDocuments,mergeRelationshipEventStreams};
|
|
775
4
|
//# sourceMappingURL=three-way.js.map
|
|
776
|
-
//# debugId=4d5434c7-544b-5061-89eb-6e4074e58e30
|