@unbrained/pm-cli 2026.9.3 → 2026.9.5
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/HARNESS_COMPATIBILITY.md +1 -1
- package/.agents/skills/README.md +2 -2
- package/.agents/skills/pm-developer/SKILL.md +4 -1
- package/.agents/skills/pm-developer/references/COMMAND_PLAYBOOK.md +10 -0
- package/.agents/skills/pm-developer/references/GRAPH_AND_RELATIONSHIPS.md +3 -1
- package/.agents/skills/pm-extensions/SKILL.md +2 -2
- package/.agents/skills/pm-extensions/references/LIFECYCLE.md +3 -3
- package/.agents/skills/pm-extensions/references/TROUBLESHOOTING.md +3 -3
- package/.agents/skills/pm-user/SKILL.md +2 -0
- package/.agents/skills/pm-user/references/BACKLOG_SHAPING.md +5 -0
- package/.agents/skills/pm-user/references/WORKFLOWS.md +20 -5
- package/.claude-plugin/marketplace.json +2 -2
- package/CHANGELOG.md +52 -0
- package/dist/cli/commander-usage.d.ts +2 -0
- package/dist/cli/commander-usage.js +15 -12
- package/dist/cli/help-json-payload.js +12 -7
- package/dist/cli/main.js +15 -6
- package/dist/cli/register-list-query.js +4 -2
- package/dist/cli/register-mutation.js +4 -2
- package/dist/cli/register-operations.js +6 -2
- package/dist/cli-bundle/bundle-manifest.json +154 -154
- package/dist/cli-bundle/chunks/{chunk-EDPIS7EB.js → chunk-3HBDTJRK.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-66VPKFP2.js +5 -0
- package/dist/cli-bundle/chunks/{chunk-Y4PJ6HPK.js → chunk-HUAQITVW.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-DKGP3QLE.js → chunk-IBYREGSP.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-LUI4E32I.js +231 -0
- package/dist/cli-bundle/chunks/{chunk-UPPM2FOZ.js → chunk-QPZHQY2O.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-34HASX3S.js → chunk-SF6SM6U5.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-SVK5Y64D.js +40 -0
- package/dist/cli-bundle/chunks/chunk-V5GBD5KV.js +9 -0
- package/dist/cli-bundle/chunks/{chunk-LENC656S.js → chunk-WWAEGW4V.js} +67 -70
- package/dist/cli-bundle/chunks/chunk-ZRAQS3UA.js +8 -0
- package/dist/cli-bundle/chunks/{register-list-query-W2ZTOMKP.js → register-list-query-AYKGODWT.js} +3 -3
- package/dist/cli-bundle/chunks/{register-mutation-RVND5NCG.js → register-mutation-YZYG53SQ.js} +4 -4
- package/dist/cli-bundle/chunks/register-operations-KFSCC7QV.js +2 -0
- package/dist/cli-bundle/chunks/{register-setup-D3ZFHY3L.js → register-setup-UJCRVGBG.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-24IG6ONG.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-M5M6SVHS.js → chunk-3ASU6WGI.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-3ITLIBKS.js +56 -0
- package/dist/cli-bundle/focused-chunks/chunk-3NQD5YDG.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-S27XMBIN.js → chunk-4OSHLNUE.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-65N6JHAN.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-5RFXUPN5.js → chunk-B2TTQGFG.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-GUBM3RGY.js +20 -0
- package/dist/cli-bundle/focused-chunks/{chunk-Q45NHPF2.js → chunk-H5FOGAVD.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-JX3RCVCX.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-D7KXFBXV.js → chunk-KWWCBWFO.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-MV752SUC.js +12 -0
- package/dist/cli-bundle/focused-chunks/chunk-PKIPMCFS.js +6 -0
- package/dist/cli-bundle/focused-chunks/{chunk-4HJFY4MB.js → chunk-QL66QNWG.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-RJMR2JIF.js +9 -0
- package/dist/cli-bundle/focused-chunks/chunk-WIUPWIZR.js +177 -0
- package/dist/cli-bundle/focused-chunks/chunk-WOCIPMW7.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-GT7MXDLW.js → chunk-YEF5PQQ2.js} +2 -2
- package/dist/cli-bundle/main.js +22 -15
- package/dist/cli-bundle/sdk-authoring.js +1 -1
- package/dist/cli-bundle/sdk-contracts.js +1 -1
- package/dist/cli-bundle/sdk-core.js +29 -32
- 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 +45 -37
- 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 +10 -10
- package/dist/core/diagnostics/remediation.js +4 -4
- package/dist/core/extensions/extension-types.d.ts +2 -0
- package/dist/core/extensions/extension-types.js +2 -2
- package/dist/core/history/drift-scan.d.ts +5 -0
- package/dist/core/history/drift-scan.js +16 -6
- package/dist/core/history/event-classification.d.ts +1 -1
- package/dist/core/history/event-classification.js +3 -2
- package/dist/core/history/history.d.ts +37 -1
- package/dist/core/history/history.js +311 -59
- package/dist/core/history/identity.d.ts +15 -0
- package/dist/core/history/identity.js +31 -0
- package/dist/core/history/projection.js +50 -17
- package/dist/core/history/read.js +6 -3
- package/dist/core/history/replay.d.ts +1 -2
- package/dist/core/history/replay.js +70 -36
- package/dist/core/history/version-address.d.ts +13 -0
- package/dist/core/history/version-address.js +33 -0
- package/dist/core/item/actionability.d.ts +21 -1
- package/dist/core/item/actionability.js +45 -10
- package/dist/core/item/id.js +8 -4
- package/dist/core/item/item-type-definition.d.ts +2 -0
- package/dist/core/item/item-type-definition.js +9 -2
- package/dist/core/item/type-registry.d.ts +5 -1
- package/dist/core/item/type-registry.js +9 -4
- package/dist/core/lock/lock.d.ts +10 -0
- package/dist/core/lock/lock.js +42 -3
- package/dist/core/shared/author.d.ts +3 -1
- package/dist/core/shared/author.js +111 -23
- package/dist/core/shared/errors.d.ts +8 -0
- package/dist/core/shared/errors.js +2 -2
- package/dist/core/store/item-store.d.ts +14 -0
- package/dist/core/store/item-store.js +48 -10
- package/dist/core/store/settings-validator.js +3 -2
- package/dist/sdk/actionability.d.ts +1 -1
- package/dist/sdk/actionability.js +3 -3
- package/dist/sdk/cli-contracts/command-aliases.js +11 -2
- package/dist/sdk/cli-contracts/completeness.js +4 -3
- package/dist/sdk/cli-contracts/flag-contracts.js +29 -5
- package/dist/sdk/cli-contracts/flag-lexicon-contracts.js +19 -12
- package/dist/sdk/cli-contracts/tool-parameter-tables.js +14 -2
- package/dist/sdk/cli-contracts/tool-schema.d.ts +2 -2
- package/dist/sdk/cli-contracts/tool-schema.js +19 -6
- package/dist/sdk/context-intent-contracts.js +3 -3
- package/dist/sdk/context-usage.js +9 -11
- package/dist/sdk/define.d.ts +1 -1
- package/dist/sdk/define.js +2 -2
- package/dist/sdk/environment/agent-environment.d.ts +296 -0
- package/dist/sdk/environment/agent-environment.js +467 -0
- package/dist/sdk/environment/project-runtime-compatibility.d.ts +19 -1
- package/dist/sdk/environment/project-runtime-compatibility.js +118 -21
- package/dist/sdk/generated/generated-error-code-catalog-part-1.js +98 -38
- package/dist/sdk/generated/generated-error-code-catalog-part-2.js +50 -2
- package/dist/sdk/governance/health.js +13 -44
- package/dist/sdk/governance/merge-receipt-history.d.ts +62 -0
- package/dist/sdk/governance/merge-receipt-history.js +207 -0
- package/dist/sdk/governance/provenance-health.js +9 -3
- package/dist/sdk/governance/validate-history-drift.js +16 -5
- package/dist/sdk/guide-topics.js +116 -2
- package/dist/sdk/history/salvage.d.ts +35 -0
- package/dist/sdk/history/salvage.js +248 -0
- package/dist/sdk/history-compact.js +41 -37
- package/dist/sdk/history-read.d.ts +3 -2
- package/dist/sdk/history-read.js +6 -3
- package/dist/sdk/history-redact.js +58 -23
- package/dist/sdk/history-repair.d.ts +5 -0
- package/dist/sdk/history-repair.js +28 -6
- package/dist/sdk/index.d.ts +2 -0
- package/dist/sdk/index.js +4 -2
- package/dist/sdk/lifecycle/claim.d.ts +4 -0
- package/dist/sdk/lifecycle/claim.js +9 -3
- package/dist/sdk/lifecycle/restore.d.ts +1 -0
- package/dist/sdk/lifecycle/restore.js +19 -7
- package/dist/sdk/merge/driver.d.ts +3 -1
- package/dist/sdk/merge/driver.js +9 -9
- package/dist/sdk/merge/index.d.ts +1 -1
- package/dist/sdk/merge/index.js +3 -3
- package/dist/sdk/merge/receipts.d.ts +17 -6
- package/dist/sdk/merge/receipts.js +246 -75
- package/dist/sdk/merge/reconcile.d.ts +9 -7
- package/dist/sdk/merge/reconcile.js +175 -12
- package/dist/sdk/merge/three-way.d.ts +22 -2
- package/dist/sdk/merge/three-way.js +92 -25
- package/dist/sdk/output-projection.d.ts +3 -1
- package/dist/sdk/output-projection.js +11 -3
- package/dist/sdk/public-surface.json +1 -0
- package/dist/sdk/query/context.d.ts +9 -0
- package/dist/sdk/query/context.js +33 -21
- package/dist/sdk/query/get.d.ts +2 -2
- package/dist/sdk/query/get.js +4 -3
- package/dist/sdk/query/history.d.ts +11 -0
- package/dist/sdk/query/history.js +34 -10
- package/dist/sdk/query/next.d.ts +15 -1
- package/dist/sdk/query/next.js +43 -20
- package/dist/sdk/query.d.ts +1 -0
- package/dist/sdk/query.js +3 -2
- package/dist/sdk/runtime-primitives.d.ts +3 -3
- package/dist/sdk/runtime-primitives.js +5 -5
- package/dist/types.d.ts +27 -0
- package/dist/types.js +2 -2
- package/docs/AGENT_ENVIRONMENTS.md +47 -0
- package/docs/AGENT_GUIDE.md +36 -0
- package/docs/AGENT_PROVENANCE_ADR.md +17 -2
- package/docs/CONTEXT_INTEGRITY_CONTRACTS.md +38 -1
- package/docs/HISTORY_RECOVERY.md +146 -0
- package/docs/MERGE_SAFETY.md +119 -23
- package/docs/README.md +2 -0
- package/docs/RELEASING.md +25 -11
- package/docs/SDK_CONTEXT_EVIDENCE_CONTRACTS.md +24 -2
- package/docs/SDK_CONTEXT_TRUTH_CONTRACTS.md +24 -1
- package/docs/SDK_EXECUTION_CONTRACTS.md +105 -0
- package/docs/SDK_RUNTIME_BOUNDARIES.md +17 -1
- package/docs/TESTING.md +5 -0
- package/docs/agent-task-token-baseline.json +17 -17
- package/docs/agent-task-transcripts.json +2 -1
- package/docs/examples/sdk-agent-environment/README.md +14 -0
- package/docs/examples/sdk-agent-environment/package.json +8 -0
- package/docs/examples/sdk-agent-environment/run-agent-environment.mjs +154 -0
- package/docs/generated/FLAG_LEXICON_BUDGETS.md +10 -10
- package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +12 -3
- package/marketplace.json +2 -2
- package/package.json +5 -6
- 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-claude/skills/pm-audit/SKILL.md +2 -2
- package/plugins/pm-claude/skills/pm-developer/SKILL.md +2 -2
- package/plugins/pm-claude/skills/pm-planner/SKILL.md +2 -2
- package/plugins/pm-claude/skills/pm-release/SKILL.md +2 -2
- package/plugins/pm-claude/skills/pm-workflow/SKILL.md +3 -3
- package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/pm-codex/skills/pm-auditor/SKILL.md +2 -2
- package/plugins/pm-codex/skills/pm-native/SKILL.md +3 -4
- package/plugins/pm-codex/skills/pm-release/SKILL.md +2 -2
- package/scripts/finalize-build.mjs +13 -1
- package/dist/cli-bundle/chunks/chunk-AY324UJ5.js +0 -9
- package/dist/cli-bundle/chunks/chunk-E5RBBCXO.js +0 -230
- package/dist/cli-bundle/chunks/chunk-IEHHE6GA.js +0 -5
- package/dist/cli-bundle/chunks/chunk-L7B7343B.js +0 -8
- package/dist/cli-bundle/chunks/chunk-LISESA7W.js +0 -35
- package/dist/cli-bundle/chunks/register-operations-TTROIVC4.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-3ZBI35HU.js +0 -176
- package/dist/cli-bundle/focused-chunks/chunk-4RRHC3S4.js +0 -6
- package/dist/cli-bundle/focused-chunks/chunk-4WR46RMG.js +0 -9
- package/dist/cli-bundle/focused-chunks/chunk-BWKHEIVF.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-GHCZ3YFR.js +0 -20
- package/dist/cli-bundle/focused-chunks/chunk-KBLZRU62.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-KZ2NB3ET.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-OL5EH6I3.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-QKHCSUGJ.js +0 -51
- package/dist/cli-bundle/focused-chunks/chunk-TXE4TSHM.js +0 -12
- package/dist/cli-bundle/focused-chunks/chunk-UCLO3XDU.js +0 -2
- package/sdk/public-surface.json +0 -32524
|
@@ -1,14 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module sdk/merge/reconcile
|
|
3
|
+
*
|
|
4
|
+
* Provides the post-merge SDK workflow that previews or repairs every drifted
|
|
5
|
+
* item-history stream, then verifies history and storage integrity in one call.
|
|
6
|
+
*/
|
|
1
7
|
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
8
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="3a3372be-37e4-578a-8be9-9ad7bc585dbd")}catch(e){}}();
|
|
9
|
+
import { readFile } from "node:fs/promises";
|
|
10
|
+
import path from "node:path";
|
|
3
11
|
import { EXIT_CODE } from "../../core/shared/constants.js";
|
|
4
12
|
import { PmCliError } from "../../core/shared/errors.js";
|
|
13
|
+
import { compareTimestampStrings } from "../../core/shared/time.js";
|
|
5
14
|
import { resolvePmRoot } from "../../core/store/paths.js";
|
|
6
|
-
import {
|
|
15
|
+
import { mapWithFixedConcurrency } from "../extension/concurrency.js";
|
|
16
|
+
import { runHealth } from "../governance/health.js";
|
|
17
|
+
import { DURABLE_MERGE_RECEIPT_INTRODUCED_AT, extractHistoryMergeReceiptReferences, isPreDurableDispositionEligibleReference, } from "../governance/merge-receipt-history.js";
|
|
7
18
|
import { runValidate } from "../governance/validate.js";
|
|
8
|
-
import {
|
|
19
|
+
import { runHistoryRepair, runHistoryRepairAll, } from "../history-repair.js";
|
|
9
20
|
import { findGitWorkspaceRoot } from "./install.js";
|
|
10
|
-
import {
|
|
21
|
+
import { inspectMergeReceiptEvidence, markMergeReceiptReconciled, summarizeMergeReceipt, } from "./receipts.js";
|
|
11
22
|
const RECEIPT_ONLY_REPAIR_CONCURRENCY = 4;
|
|
23
|
+
/** Extract bounded item, line, and receipt coordinates from health evidence. */
|
|
24
|
+
function missingReceiptCoordinates(health) {
|
|
25
|
+
const integrity = health.checks.find((check) => check.name === "integrity");
|
|
26
|
+
const counts = integrity?.details.counts;
|
|
27
|
+
const count = typeof counts === "object" &&
|
|
28
|
+
counts !== null &&
|
|
29
|
+
!Array.isArray(counts) &&
|
|
30
|
+
typeof counts
|
|
31
|
+
.missing_merge_receipt_history_references === "number"
|
|
32
|
+
? counts
|
|
33
|
+
.missing_merge_receipt_history_references
|
|
34
|
+
: 0;
|
|
35
|
+
const details = integrity?.details.missing_merge_receipt_history_reference_details;
|
|
36
|
+
const truncated = integrity?.details
|
|
37
|
+
.missing_merge_receipt_history_reference_details_truncated === true;
|
|
38
|
+
const coordinates = Array.isArray(details)
|
|
39
|
+
? details.flatMap((detail) => {
|
|
40
|
+
if (typeof detail !== "object" ||
|
|
41
|
+
detail === null ||
|
|
42
|
+
Array.isArray(detail)) {
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
const record = detail;
|
|
46
|
+
return typeof record.item_id === "string" &&
|
|
47
|
+
typeof record.history_line === "number" &&
|
|
48
|
+
Number.isSafeInteger(record.history_line) &&
|
|
49
|
+
typeof record.receipt_id === "string"
|
|
50
|
+
? [
|
|
51
|
+
{
|
|
52
|
+
item_id: record.item_id,
|
|
53
|
+
history_line: record.history_line,
|
|
54
|
+
receipt_id: record.receipt_id,
|
|
55
|
+
},
|
|
56
|
+
]
|
|
57
|
+
: [];
|
|
58
|
+
})
|
|
59
|
+
: [];
|
|
60
|
+
return { count, truncated, coordinates };
|
|
61
|
+
}
|
|
62
|
+
/** Re-read history and retain only exact pre-durable clone-local references. */
|
|
63
|
+
async function identifyLegacyDispositionCandidates(pmRoot, coordinates) {
|
|
64
|
+
const candidates = [];
|
|
65
|
+
for (const coordinate of coordinates) {
|
|
66
|
+
let raw;
|
|
67
|
+
try {
|
|
68
|
+
raw = await readFile(path.join(pmRoot, "history", `${coordinate.item_id}.jsonl`), "utf8");
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const line = raw.split(/\r?\n/)[coordinate.history_line - 1];
|
|
74
|
+
if (line === undefined || line.trim().length === 0)
|
|
75
|
+
continue;
|
|
76
|
+
let parsed;
|
|
77
|
+
try {
|
|
78
|
+
parsed = JSON.parse(line);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
const reference = extractHistoryMergeReceiptReferences(parsed, coordinate.item_id).find((entry) => entry.receiptId === coordinate.receipt_id);
|
|
84
|
+
if (reference === undefined ||
|
|
85
|
+
!isPreDurableDispositionEligibleReference(reference) ||
|
|
86
|
+
reference.eventTimestamp === undefined ||
|
|
87
|
+
compareTimestampStrings(reference.eventTimestamp, DURABLE_MERGE_RECEIPT_INTRODUCED_AT) >= 0) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
candidates.push({
|
|
91
|
+
...coordinate,
|
|
92
|
+
original_event_ts: reference.eventTimestamp,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
return candidates;
|
|
96
|
+
}
|
|
97
|
+
/** Append auditable dispositions for eligible receipts that cannot be restored. */
|
|
98
|
+
async function recordLegacyReceiptDispositions(params) {
|
|
99
|
+
if (params.options.dryRun === true ||
|
|
100
|
+
params.options.force !== true ||
|
|
101
|
+
params.candidates.length === 0) {
|
|
102
|
+
return 0;
|
|
103
|
+
}
|
|
104
|
+
const byItem = new Map();
|
|
105
|
+
for (const candidate of params.candidates) {
|
|
106
|
+
const itemCandidates = byItem.get(candidate.item_id) ?? [];
|
|
107
|
+
itemCandidates.push(candidate);
|
|
108
|
+
byItem.set(candidate.item_id, itemCandidates);
|
|
109
|
+
}
|
|
110
|
+
for (const [itemId, itemCandidates] of byItem) {
|
|
111
|
+
await runHistoryRepair(itemId, {
|
|
112
|
+
author: params.options.author ?? params.global.author,
|
|
113
|
+
message: params.options.message ??
|
|
114
|
+
"accept unrecoverable pre-durable merge receipt references",
|
|
115
|
+
force: true,
|
|
116
|
+
auditOperation: "merge_reconcile",
|
|
117
|
+
forceAuditEntry: true,
|
|
118
|
+
auditContext: {
|
|
119
|
+
merge: {
|
|
120
|
+
missing_receipt_dispositions: itemCandidates.map((candidate) => ({
|
|
121
|
+
receipt_id: candidate.receipt_id,
|
|
122
|
+
original_history_line: candidate.history_line,
|
|
123
|
+
original_event_ts: candidate.original_event_ts,
|
|
124
|
+
reason: "legacy_clone_local_only",
|
|
125
|
+
})),
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
}, params.global);
|
|
129
|
+
}
|
|
130
|
+
return params.candidates.length;
|
|
131
|
+
}
|
|
132
|
+
/** Execute one receipt-backed repair while preserving its settled result. */
|
|
12
133
|
async function runReceiptOnlyRepair(params) {
|
|
13
134
|
try {
|
|
14
135
|
return {
|
|
@@ -30,6 +151,7 @@ async function runReceiptOnlyRepair(params) {
|
|
|
30
151
|
return { status: "rejected", reason };
|
|
31
152
|
}
|
|
32
153
|
}
|
|
154
|
+
/** Fold settled receipt repairs into the aggregate reconciliation result. */
|
|
33
155
|
function appendReceiptOnlyResults(params) {
|
|
34
156
|
params.results.forEach((settled, index) => {
|
|
35
157
|
const id = params.receiptOnlyIds[index];
|
|
@@ -59,15 +181,14 @@ function appendReceiptOnlyResults(params) {
|
|
|
59
181
|
params.repair.totals.failed += 1;
|
|
60
182
|
});
|
|
61
183
|
}
|
|
184
|
+
/** Settle only receipts named by successful trusted repair evidence. */
|
|
62
185
|
async function settleProvenReceipts(params) {
|
|
63
186
|
if (params.dryRun || params.repair.totals.failed > 0)
|
|
64
187
|
return 0;
|
|
65
188
|
const trustedReceiptIds = new Set(params.repair.streams.flatMap((stream) => stream.merge_receipt_proof?.trusted
|
|
66
189
|
? stream.merge_receipt_proof.receipt_ids
|
|
67
190
|
: []));
|
|
68
|
-
const receiptsToSettle = params.
|
|
69
|
-
? params.pendingReceipts
|
|
70
|
-
: params.pendingReceipts.filter((receipt) => trustedReceiptIds.has(receipt.id));
|
|
191
|
+
const receiptsToSettle = params.pendingReceipts.filter((receipt) => trustedReceiptIds.has(receipt.id));
|
|
71
192
|
await Promise.all(receiptsToSettle.map((receipt) => markMergeReceiptReconciled(params.gitWorkspaceRoot ?? process.cwd(), receipt, { requireExisting: true })));
|
|
72
193
|
return receiptsToSettle.length;
|
|
73
194
|
}
|
|
@@ -80,6 +201,14 @@ async function settleProvenReceipts(params) {
|
|
|
80
201
|
export async function runMergeReconcile(options, global) {
|
|
81
202
|
const dryRun = options.dryRun === true;
|
|
82
203
|
const pmRoot = resolvePmRoot(process.cwd(), global.path);
|
|
204
|
+
const preflightHealth = await runHealth(global, {
|
|
205
|
+
checkOnly: true,
|
|
206
|
+
full: true,
|
|
207
|
+
skipVectors: true,
|
|
208
|
+
skipDrift: true,
|
|
209
|
+
});
|
|
210
|
+
const missingBefore = missingReceiptCoordinates(preflightHealth);
|
|
211
|
+
const legacyDispositionCandidates = await identifyLegacyDispositionCandidates(pmRoot, missingBefore.coordinates);
|
|
83
212
|
const gitWorkspaceRoot = await findGitWorkspaceRoot(pmRoot);
|
|
84
213
|
const receiptEvidence = await inspectMergeReceiptEvidence(gitWorkspaceRoot ?? process.cwd(), {
|
|
85
214
|
includeLossless: true,
|
|
@@ -94,6 +223,11 @@ export async function runMergeReconcile(options, global) {
|
|
|
94
223
|
},
|
|
95
224
|
});
|
|
96
225
|
}
|
|
226
|
+
const recordedLegacyDispositionCount = await recordLegacyReceiptDispositions({
|
|
227
|
+
candidates: legacyDispositionCandidates,
|
|
228
|
+
options,
|
|
229
|
+
global,
|
|
230
|
+
});
|
|
97
231
|
const pendingReceipts = receiptEvidence.receipts;
|
|
98
232
|
const receiptsByItem = new Map();
|
|
99
233
|
for (const receipt of pendingReceipts) {
|
|
@@ -140,20 +274,38 @@ export async function runMergeReconcile(options, global) {
|
|
|
140
274
|
});
|
|
141
275
|
const reconciledReceiptCount = await settleProvenReceipts({
|
|
142
276
|
dryRun,
|
|
143
|
-
force: options.force === true,
|
|
144
277
|
pendingReceipts,
|
|
145
278
|
repair,
|
|
146
279
|
gitWorkspaceRoot,
|
|
147
280
|
});
|
|
148
281
|
const validation = await runValidate({ checkHistoryDrift: true, checkStorageIntegrity: true }, global);
|
|
282
|
+
const postflightHealth = recordedLegacyDispositionCount === 0
|
|
283
|
+
? preflightHealth
|
|
284
|
+
: await runHealth(global, {
|
|
285
|
+
checkOnly: true,
|
|
286
|
+
full: true,
|
|
287
|
+
skipVectors: true,
|
|
288
|
+
skipDrift: true,
|
|
289
|
+
});
|
|
290
|
+
const missingAfter = missingReceiptCoordinates(postflightHealth);
|
|
149
291
|
const mergeChecksGreen = validation.checks.every((check) => check.status === "ok");
|
|
150
292
|
const receiptSettlementComplete = dryRun
|
|
151
293
|
? pendingReceipts.length === 0
|
|
152
294
|
: reconciledReceiptCount === pendingReceipts.length;
|
|
153
|
-
const ok =
|
|
154
|
-
|
|
295
|
+
const ok = [
|
|
296
|
+
repair.totals.failed === 0,
|
|
297
|
+
mergeChecksGreen,
|
|
298
|
+
receiptSettlementComplete,
|
|
299
|
+
missingAfter.count === 0,
|
|
300
|
+
!missingAfter.truncated,
|
|
301
|
+
].every(Boolean);
|
|
302
|
+
const boundedBatchGuidance = missingBefore.truncated || missingAfter.truncated
|
|
303
|
+
? "Missing receipt coordinates exceed the bounded detail response; each reviewed --force pass can disposition only the reported eligible batch. Repeat dry-run and --force passes until receipts.missing_history_references_after=0 and pm merge reconcile returns ok=true."
|
|
304
|
+
: undefined;
|
|
305
|
+
const guidance = (dryRun
|
|
155
306
|
? [
|
|
156
307
|
"Review repair.streams, then rerun pm merge reconcile without --dry-run to apply audited repairs.",
|
|
308
|
+
boundedBatchGuidance,
|
|
157
309
|
"No Git hook is installed automatically; invoke this command from an explicit post-merge hook only when your repository policy opts in.",
|
|
158
310
|
]
|
|
159
311
|
: ok
|
|
@@ -163,8 +315,15 @@ export async function runMergeReconcile(options, global) {
|
|
|
163
315
|
]
|
|
164
316
|
: [
|
|
165
317
|
"Reconciliation remains incomplete; inspect repair failures and non-green validation checks before retrying.",
|
|
318
|
+
boundedBatchGuidance,
|
|
319
|
+
...(legacyDispositionCandidates.length > 0 &&
|
|
320
|
+
recordedLegacyDispositionCount === 0
|
|
321
|
+
? [
|
|
322
|
+
"Pre-durable clone-local-only receipt references are eligible for an audited disposition; rerun with --force after reviewing receipts.legacy_disposition_eligible.",
|
|
323
|
+
]
|
|
324
|
+
: []),
|
|
166
325
|
"Do not commit repaired history streams as reconciled until pm merge reconcile returns ok=true.",
|
|
167
|
-
];
|
|
326
|
+
]).filter((message) => message !== undefined);
|
|
168
327
|
return {
|
|
169
328
|
ok,
|
|
170
329
|
dry_run: dryRun,
|
|
@@ -173,6 +332,10 @@ export async function runMergeReconcile(options, global) {
|
|
|
173
332
|
pending_before: pendingReceipts.length,
|
|
174
333
|
reconciled: reconciledReceiptCount,
|
|
175
334
|
summaries: pendingReceipts.map(summarizeMergeReceipt),
|
|
335
|
+
missing_history_references_before: missingBefore.count,
|
|
336
|
+
legacy_disposition_eligible: legacyDispositionCandidates.length,
|
|
337
|
+
legacy_disposition_recorded: recordedLegacyDispositionCount,
|
|
338
|
+
missing_history_references_after: missingAfter.count,
|
|
176
339
|
},
|
|
177
340
|
validation,
|
|
178
341
|
guidance,
|
|
@@ -180,4 +343,4 @@ export async function runMergeReconcile(options, global) {
|
|
|
180
343
|
};
|
|
181
344
|
}
|
|
182
345
|
//# sourceMappingURL=reconcile.js.map
|
|
183
|
-
//# debugId=
|
|
346
|
+
//# debugId=3a3372be-37e4-578a-8be9-9ad7bc585dbd
|
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
import { type ItemDocumentFormatOptions } from "../../core/item/item-format.js";
|
|
2
2
|
/** Restricts which side of a three-way merge wins an unresolvable conflict. */
|
|
3
3
|
export type MergePreferredSide = "ours" | "theirs";
|
|
4
|
+
/** Declares how competing item scalar writes select their retained value. */
|
|
5
|
+
export type ItemScalarConflictResolution = "preferred_side" | "stable_value_order" | "latest_document_update";
|
|
6
|
+
/** Explains the exact evidence used to select one conflicting item scalar. */
|
|
7
|
+
export type ItemScalarResolutionBasis = "requested_preference" | "document_updated_at" | "stable_value_tiebreak";
|
|
8
|
+
/** JSON-stable marker used when a conflict decision refers to an absent scalar. */
|
|
9
|
+
export declare const ITEM_SCALAR_MISSING_VALUE: Readonly<{
|
|
10
|
+
readonly pm_item_scalar_missing: true;
|
|
11
|
+
}>;
|
|
12
|
+
/** Return whether receipt evidence represents an absent item scalar. */
|
|
13
|
+
export declare function isItemScalarMissingValue(value: unknown): value is typeof ITEM_SCALAR_MISSING_VALUE;
|
|
14
|
+
/** Encode an absent scalar so every decision property survives JSON serialization. */
|
|
15
|
+
export declare function encodeItemScalarDecisionValue(value: unknown): unknown;
|
|
16
|
+
/** Hash scalars in disjoint present/missing domains for collision-free evidence. */
|
|
17
|
+
export declare function hashItemScalarDecisionValue(value: unknown): string;
|
|
4
18
|
/** Restricts the strategy labels reported by the history stream merge. */
|
|
5
19
|
export type HistoryMergeStrategy = "identical" | "fast_forward_ours" | "fast_forward_theirs" | "union_reanchor";
|
|
6
20
|
/** Documents the history stream merge result payload exchanged by command, SDK, and package integrations. */
|
|
@@ -87,6 +101,10 @@ export interface ItemMergeConflictDecision {
|
|
|
87
101
|
retained: unknown;
|
|
88
102
|
/** Value not retained in the merged item. */
|
|
89
103
|
discarded: unknown;
|
|
104
|
+
/** Branch coordinate whose value was retained. */
|
|
105
|
+
retained_side?: MergePreferredSide;
|
|
106
|
+
/** Evidence class that selected the retained value. */
|
|
107
|
+
resolution_basis?: ItemScalarResolutionBasis;
|
|
90
108
|
}
|
|
91
109
|
/** Documents the item document merge result payload exchanged by command, SDK, and package integrations. */
|
|
92
110
|
export interface ItemDocumentMergeResult {
|
|
@@ -102,8 +120,10 @@ export interface ItemDocumentMergeResult {
|
|
|
102
120
|
conflict_decisions: ItemMergeConflictDecision[];
|
|
103
121
|
/** Side requested by the caller; stable-value decisions can retain either side. */
|
|
104
122
|
requested_preference: MergePreferredSide;
|
|
123
|
+
/** Whether the requested side participates in item scalar selection under the declared policy. */
|
|
124
|
+
requested_preference_applied: boolean;
|
|
105
125
|
/** Scalar-conflict selection contract used for this merge. */
|
|
106
|
-
conflict_resolution:
|
|
126
|
+
conflict_resolution: ItemScalarConflictResolution;
|
|
107
127
|
}
|
|
108
128
|
/**
|
|
109
129
|
* Field-aware three-way merge of one tracker item document. Commutative
|
|
@@ -121,7 +141,7 @@ export declare function mergeItemDocuments(baseRaw: string, oursRaw: string, the
|
|
|
121
141
|
/** Side that wins unresolvable conflicts (default "ours"). */
|
|
122
142
|
preferred?: MergePreferredSide;
|
|
123
143
|
/** Direction-independent scalar selection used by the Git driver. */
|
|
124
|
-
conflictResolution?:
|
|
144
|
+
conflictResolution?: ItemScalarConflictResolution;
|
|
125
145
|
}): ItemDocumentMergeResult;
|
|
126
146
|
/** Documents the JSON document merge result payload exchanged by command, SDK, and package integrations. */
|
|
127
147
|
export interface JsonDocumentMergeResult {
|
|
@@ -9,14 +9,37 @@
|
|
|
9
9
|
* definition for multi-branch agent workflows.
|
|
10
10
|
*/
|
|
11
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]="
|
|
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
13
|
import { historyEntriesToRaw, reanchorHistoryEntries, } from "../../core/history/replay.js";
|
|
14
14
|
import { canonicalDocument, parseItemDocument, serializeItemDocument, } from "../../core/item/item-format.js";
|
|
15
|
+
import { splitAcceptanceCriteria } from "../../core/item/parse.js";
|
|
15
16
|
import { EXIT_CODE } from "../../core/shared/constants.js";
|
|
16
17
|
import { findFirstMergeConflictMarker } from "../../core/shared/conflict-markers.js";
|
|
17
18
|
import { PmCliError } from "../../core/shared/errors.js";
|
|
18
|
-
import { stableStringify } from "../../core/shared/serialization.js";
|
|
19
|
+
import { sha256Hex, stableStringify } from "../../core/shared/serialization.js";
|
|
19
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
|
+
}
|
|
20
43
|
function parseHistoryJsonl(raw, label) {
|
|
21
44
|
const conflictMarker = findFirstMergeConflictMarker(raw);
|
|
22
45
|
if (conflictMarker) {
|
|
@@ -294,10 +317,15 @@ export const ITEM_UNION_COLLECTION_FIELDS = [
|
|
|
294
317
|
"reminders",
|
|
295
318
|
"events",
|
|
296
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"];
|
|
297
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. */
|
|
298
323
|
export const ITEM_LATEST_TIMESTAMP_FIELDS = ["updated_at"];
|
|
299
324
|
function jsonEquals(left, right) {
|
|
300
|
-
|
|
325
|
+
if (left === undefined || right === undefined) {
|
|
326
|
+
return left === undefined && right === undefined;
|
|
327
|
+
}
|
|
328
|
+
return stableStringify(left) === stableStringify(right);
|
|
301
329
|
}
|
|
302
330
|
function unionCollection(base, ours, theirs) {
|
|
303
331
|
const toEntries = (value) => Array.isArray(value) ? value : [];
|
|
@@ -393,24 +421,51 @@ function mergeScalarThreeWay(base, ours, theirs, preferred) {
|
|
|
393
421
|
conflict: true,
|
|
394
422
|
};
|
|
395
423
|
}
|
|
396
|
-
|
|
424
|
+
/** Resolve one item scalar with explicit recency and deterministic tie evidence. */
|
|
425
|
+
function mergeItemScalarThreeWay(base, ours, theirs, preferred, conflictResolution, oursUpdatedAt, theirsUpdatedAt) {
|
|
397
426
|
const outcome = mergeScalarThreeWay(base, ours, theirs, preferred);
|
|
398
|
-
if (!outcome.conflict
|
|
399
|
-
return outcome;
|
|
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
|
+
};
|
|
400
447
|
}
|
|
401
|
-
const retainTheirs = stableStringify(theirs).localeCompare(stableStringify(ours)) < 0;
|
|
448
|
+
const retainTheirs = stableStringify(encodeItemScalarDecisionValue(theirs)).localeCompare(stableStringify(encodeItemScalarDecisionValue(ours))) < 0;
|
|
402
449
|
return {
|
|
403
450
|
value: retainTheirs ? theirs : ours,
|
|
404
451
|
from_theirs: retainTheirs,
|
|
405
452
|
conflict: true,
|
|
453
|
+
resolution_basis: "stable_value_tiebreak",
|
|
454
|
+
retained_side: retainTheirs ? "theirs" : "ours",
|
|
406
455
|
};
|
|
407
456
|
}
|
|
408
457
|
function toMetadataRecord(document) {
|
|
409
458
|
return { ...document.metadata };
|
|
410
459
|
}
|
|
460
|
+
/** Parse one merge side and reject values reserved for internal absence evidence. */
|
|
411
461
|
function parseItemMergeSide(raw, label, options) {
|
|
412
462
|
try {
|
|
413
|
-
|
|
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;
|
|
414
469
|
}
|
|
415
470
|
catch (error) {
|
|
416
471
|
throw new PmCliError(`Item merge input (${label}) is not a readable item document: ${String(error)}`, EXIT_CODE.GENERIC_FAILURE);
|
|
@@ -422,7 +477,9 @@ function mergeItemUnionField(params) {
|
|
|
422
477
|
return false;
|
|
423
478
|
const union = params.field === "tests"
|
|
424
479
|
? unionLinkedTests(params.baseValue, params.oursValue, params.theirsValue)
|
|
425
|
-
:
|
|
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);
|
|
426
483
|
params.accumulator.merged[params.field] = union;
|
|
427
484
|
if (!jsonEquals(union, params.oursValue)) {
|
|
428
485
|
params.accumulator.unionFields.push(params.field);
|
|
@@ -431,7 +488,7 @@ function mergeItemUnionField(params) {
|
|
|
431
488
|
}
|
|
432
489
|
/** Merge one scalar field while preserving conflict evidence. */
|
|
433
490
|
function mergeItemScalarField(params) {
|
|
434
|
-
const outcome = mergeItemScalarThreeWay(params.baseValue, params.oursValue, params.theirsValue, params.preferred, params.conflictResolution);
|
|
491
|
+
const outcome = mergeItemScalarThreeWay(params.baseValue, params.oursValue, params.theirsValue, params.preferred, params.conflictResolution, params.oursUpdatedAt, params.theirsUpdatedAt);
|
|
435
492
|
if (outcome.value !== undefined) {
|
|
436
493
|
params.accumulator.merged[params.field] = outcome.value;
|
|
437
494
|
}
|
|
@@ -439,19 +496,24 @@ function mergeItemScalarField(params) {
|
|
|
439
496
|
params.accumulator.conflictFields.push(params.field);
|
|
440
497
|
params.accumulator.conflictDecisions.push({
|
|
441
498
|
field: params.field,
|
|
442
|
-
base: params.baseValue,
|
|
443
|
-
ours: params.oursValue,
|
|
444
|
-
theirs: params.theirsValue,
|
|
445
|
-
retained: outcome.value,
|
|
446
|
-
discarded: outcome.from_theirs ? params.oursValue : params.theirsValue,
|
|
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,
|
|
447
506
|
});
|
|
448
507
|
}
|
|
449
508
|
else if (outcome.from_theirs) {
|
|
450
509
|
params.accumulator.fieldsFromTheirs.push(params.field);
|
|
451
510
|
}
|
|
452
511
|
}
|
|
453
|
-
function mergeItemMetadataRecords(baseRecord, oursRecord, theirsRecord, preferred, conflictResolution) {
|
|
454
|
-
const unionFieldSet = new Set(
|
|
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
|
+
]);
|
|
455
517
|
const latestFieldSet = new Set(ITEM_LATEST_TIMESTAMP_FIELDS);
|
|
456
518
|
const fieldNames = [
|
|
457
519
|
...new Set([...Object.keys(oursRecord), ...Object.keys(theirsRecord)]),
|
|
@@ -491,6 +553,8 @@ function mergeItemMetadataRecords(baseRecord, oursRecord, theirsRecord, preferre
|
|
|
491
553
|
theirsValue,
|
|
492
554
|
preferred,
|
|
493
555
|
conflictResolution,
|
|
556
|
+
oursUpdatedAt,
|
|
557
|
+
theirsUpdatedAt,
|
|
494
558
|
accumulator,
|
|
495
559
|
});
|
|
496
560
|
}
|
|
@@ -523,17 +587,19 @@ export function mergeItemDocuments(baseRaw, oursRaw, theirsRaw, options = {}) {
|
|
|
523
587
|
: {};
|
|
524
588
|
const oursRecord = toMetadataRecord(ours);
|
|
525
589
|
const theirsRecord = toMetadataRecord(theirs);
|
|
526
|
-
const { merged, conflictFields, fieldsFromTheirs, unionFields, conflictDecisions, } = mergeItemMetadataRecords(baseRecord, oursRecord, theirsRecord, preferred, conflictResolution);
|
|
527
|
-
const bodyOutcome = mergeItemScalarThreeWay(hasBase ? base.body : "", ours.body, theirs.body, preferred, conflictResolution);
|
|
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);
|
|
528
592
|
if (bodyOutcome.conflict) {
|
|
529
593
|
conflictFields.push("body");
|
|
530
594
|
conflictDecisions.push({
|
|
531
595
|
field: "body",
|
|
532
|
-
base: hasBase ? base.body : "",
|
|
533
|
-
ours: ours.body,
|
|
534
|
-
theirs: theirs.body,
|
|
535
|
-
retained: bodyOutcome.value,
|
|
536
|
-
discarded: bodyOutcome.from_theirs ? ours.body : theirs.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,
|
|
537
603
|
});
|
|
538
604
|
}
|
|
539
605
|
else if (bodyOutcome.from_theirs) {
|
|
@@ -550,6 +616,7 @@ export function mergeItemDocuments(baseRaw, oursRaw, theirsRaw, options = {}) {
|
|
|
550
616
|
union_fields: unionFields,
|
|
551
617
|
conflict_decisions: conflictDecisions,
|
|
552
618
|
requested_preference: preferred,
|
|
619
|
+
requested_preference_applied: conflictResolution === "preferred_side",
|
|
553
620
|
conflict_resolution: conflictResolution,
|
|
554
621
|
};
|
|
555
622
|
}
|
|
@@ -706,4 +773,4 @@ export function mergeJsonDocuments(baseRaw, oursRaw, theirsRaw, options = {}) {
|
|
|
706
773
|
};
|
|
707
774
|
}
|
|
708
775
|
//# sourceMappingURL=three-way.js.map
|
|
709
|
-
//# debugId=
|
|
776
|
+
//# debugId=4d5434c7-544b-5061-89eb-6e4074e58e30
|
|
@@ -13,6 +13,8 @@ export interface OutputOmissionReceipt {
|
|
|
13
13
|
omitted_field_group_count: number;
|
|
14
14
|
/** Withheld groups and the exact opt-in that restores each one. */
|
|
15
15
|
omitted_field_groups: OutputProjectionFieldGroup[];
|
|
16
|
+
/** Population counted by context summary regardless of bounded focus rows. */
|
|
17
|
+
summary_scope?: "matching_items";
|
|
16
18
|
}
|
|
17
19
|
/** Self-describing projection metadata that built-ins and extensions can use to derive receipts without command-specific code. */
|
|
18
20
|
export interface OutputProjectionDeclaration {
|
|
@@ -59,7 +61,7 @@ export declare const PM_READ_ROW_CONTRACTS: {
|
|
|
59
61
|
readonly fields: "supported";
|
|
60
62
|
};
|
|
61
63
|
readonly next: {
|
|
62
|
-
readonly row_keys: readonly ["recommended", "ready", "decision_needed", "blocked", "held_by_others"];
|
|
64
|
+
readonly row_keys: readonly ["recommended", "ready", "decision_needed", "gate_needed", "containers", "blocked", "held_by_others"];
|
|
63
65
|
readonly fields: "unsupported";
|
|
64
66
|
};
|
|
65
67
|
readonly search: {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Declares machine-readable field-group omissions for bounded command results.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
7
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="db6180ed-0b18-5bf8-bcc1-24171e2dfdc4")}catch(e){}}();
|
|
8
8
|
import { EXIT_CODE } from "../core/shared/constants.js";
|
|
9
9
|
import { PmCliError } from "../core/shared/errors.js";
|
|
10
10
|
const MATERIAL_FIELD_GROUPS_BY_RESULT = new WeakMap();
|
|
@@ -24,6 +24,8 @@ export const PM_READ_ROW_CONTRACTS = {
|
|
|
24
24
|
"recommended",
|
|
25
25
|
"ready",
|
|
26
26
|
"decision_needed",
|
|
27
|
+
"gate_needed",
|
|
28
|
+
"containers",
|
|
27
29
|
"blocked",
|
|
28
30
|
"held_by_others",
|
|
29
31
|
],
|
|
@@ -208,6 +210,8 @@ const READ_RESULT_SENTINEL_KEYS = {
|
|
|
208
210
|
"recommended",
|
|
209
211
|
"ready",
|
|
210
212
|
"decision_needed",
|
|
213
|
+
"gate_needed",
|
|
214
|
+
"containers",
|
|
211
215
|
"blocked",
|
|
212
216
|
"held_by_others",
|
|
213
217
|
],
|
|
@@ -314,7 +318,11 @@ function resolveDeclaredProjectionReceipt(result) {
|
|
|
314
318
|
function resolveContextReceipt(result) {
|
|
315
319
|
if (!Array.isArray(result.sections_included))
|
|
316
320
|
return undefined;
|
|
317
|
-
|
|
321
|
+
const receipt = createOutputOmissionReceipt(CONTEXT_FIELD_GROUPS, new Set(result.sections_included.filter((section) => typeof section === "string")));
|
|
322
|
+
if (isRecord(result.summary) && result.summary.scope === "matching_items") {
|
|
323
|
+
receipt.summary_scope = "matching_items";
|
|
324
|
+
}
|
|
325
|
+
return receipt;
|
|
318
326
|
}
|
|
319
327
|
function resolveGetReceipt(result) {
|
|
320
328
|
if (!isRecord(result.item))
|
|
@@ -420,4 +428,4 @@ export function attachOutputOmissionReceipt(command, result) {
|
|
|
420
428
|
: { ...disclosedResult, omission_receipt: receipt };
|
|
421
429
|
}
|
|
422
430
|
//# sourceMappingURL=output-projection.js.map
|
|
423
|
-
//# debugId=
|
|
431
|
+
//# debugId=db6180ed-0b18-5bf8-bcc1-24171e2dfdc4
|