@unbrained/pm-cli 2026.9.4 → 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 +26 -1
- 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 +153 -153
- package/dist/cli-bundle/chunks/{chunk-RI4XNOYS.js → chunk-3HBDTJRK.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-66VPKFP2.js +5 -0
- package/dist/cli-bundle/chunks/{chunk-I2AS367G.js → chunk-HUAQITVW.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-HI6BHPCU.js → chunk-IBYREGSP.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-LUI4E32I.js +231 -0
- package/dist/cli-bundle/chunks/{chunk-YW4QK2G7.js → chunk-QPZHQY2O.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-X6CMGV6L.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-V72UV7QS.js → chunk-WWAEGW4V.js} +57 -57
- package/dist/cli-bundle/chunks/{chunk-A24WAVE6.js → chunk-ZRAQS3UA.js} +2 -2
- package/dist/cli-bundle/chunks/{register-list-query-Z5YYSJJG.js → register-list-query-AYKGODWT.js} +3 -3
- package/dist/cli-bundle/chunks/{register-mutation-ELSP6I4I.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-E2AE72QG.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-53IHPICX.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-7N2TV7WZ.js → chunk-4OSHLNUE.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-65N6JHAN.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-AJR3OP2E.js → chunk-B2TTQGFG.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-GUBM3RGY.js +20 -0
- package/dist/cli-bundle/focused-chunks/{chunk-25UJBH4M.js → chunk-H5FOGAVD.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-JX3RCVCX.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-6PWPQKDN.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-3646BZ4W.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-NBMJD6MK.js → chunk-WOCIPMW7.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-MHKUIPJW.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 -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 -39
- 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 +8 -8
- 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.js +37 -5
- 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 +4 -4
- 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/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/generated/generated-error-code-catalog-part-1.js +98 -50
- package/dist/sdk/generated/generated-error-code-catalog-part-2.js +50 -2
- 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 +32 -37
- package/dist/sdk/history-read.d.ts +3 -2
- package/dist/sdk/history-read.js +6 -3
- package/dist/sdk/history-repair.d.ts +5 -0
- package/dist/sdk/history-repair.js +14 -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/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 +1 -1
- package/dist/sdk/runtime-primitives.js +3 -3
- package/dist/types.d.ts +2 -0
- package/dist/types.js +2 -2
- package/docs/AGENT_GUIDE.md +36 -0
- package/docs/HISTORY_RECOVERY.md +146 -0
- package/docs/README.md +1 -0
- package/docs/RELEASING.md +25 -11
- package/docs/SDK_CONTEXT_TRUTH_CONTRACTS.md +24 -1
- package/docs/SDK_EXECUTION_CONTRACTS.md +105 -0
- 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/generated/FLAG_LEXICON_BUDGETS.md +10 -10
- package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +11 -3
- package/marketplace.json +2 -2
- package/package.json +4 -5
- package/packages/pm-beads/package.json +1 -1
- package/packages/pm-calendar/package.json +1 -1
- package/packages/pm-command-kit/package.json +1 -1
- package/packages/pm-digital-twin/package.json +1 -1
- package/packages/pm-governance-audit/package.json +1 -1
- package/packages/pm-guide-shell/package.json +1 -1
- package/packages/pm-kanban/package.json +1 -1
- package/packages/pm-lifecycle-hooks/package.json +1 -1
- package/packages/pm-linked-test-adapters/package.json +1 -1
- package/packages/pm-search-advanced/package.json +1 -1
- package/packages/pm-templates/package.json +1 -1
- package/packages/pm-todos/package.json +1 -1
- package/packages/pm-vcs/package.json +1 -1
- package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
- package/plugins/pm-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-6XSKPNCF.js +0 -230
- package/dist/cli-bundle/chunks/chunk-OGBVU67G.js +0 -5
- package/dist/cli-bundle/chunks/chunk-TXW6VL33.js +0 -38
- package/dist/cli-bundle/chunks/chunk-VGLHECAP.js +0 -9
- package/dist/cli-bundle/chunks/register-operations-OH7R3ZT4.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-4YRAVI43.js +0 -12
- package/dist/cli-bundle/focused-chunks/chunk-5P4HVOWG.js +0 -176
- package/dist/cli-bundle/focused-chunks/chunk-636VVQ7F.js +0 -54
- package/dist/cli-bundle/focused-chunks/chunk-B3A6MJYE.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-BFDDJ2T2.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-BHUIRZWQ.js +0 -9
- package/dist/cli-bundle/focused-chunks/chunk-JADHHPY6.js +0 -6
- package/dist/cli-bundle/focused-chunks/chunk-NSKBLHVI.js +0 -20
- package/dist/cli-bundle/focused-chunks/chunk-P2GOAOQN.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-YARWJM2B.js +0 -2
- package/sdk/public-surface.json +0 -33034
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module sdk/history/salvage
|
|
3
|
+
* Recover an invalid JSONL suffix while preserving every verified prefix byte.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
!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]="49af8ec4-dd6b-56ef-8833-9c776fa35c85")}catch(e){}}();
|
|
7
|
+
import { isUtf8 } from "node:buffer";
|
|
8
|
+
import { readFile } from "node:fs/promises";
|
|
9
|
+
import { isFileMissingError, readFileIfExists, } from "../../core/fs/fs-utils.js";
|
|
10
|
+
import { acquireLock } from "../../core/lock/lock.js";
|
|
11
|
+
import { createHistoryEntry, sealHistoryRecord, } from "../../core/history/history.js";
|
|
12
|
+
import { checkHistoryRewriteOwnership, writeHistoryRawWithRollback, } from "../../core/history/history-rewrite.js";
|
|
13
|
+
import { replayHistoryToTarget } from "../../core/history/projection.js";
|
|
14
|
+
import { replayToItemDocument, verifyHistoryChainWithVersion, } from "../../core/history/replay.js";
|
|
15
|
+
import { findFirstMergeConflictMarker } from "../../core/shared/conflict-markers.js";
|
|
16
|
+
import { EXIT_CODE } from "../../core/shared/constants.js";
|
|
17
|
+
import { PmCliError } from "../../core/shared/errors.js";
|
|
18
|
+
import { sha256Hex } from "../../core/shared/serialization.js";
|
|
19
|
+
import { nowIso } from "../../core/shared/time.js";
|
|
20
|
+
import { runActiveOnReadHooks, runActiveOnWriteHooks, } from "../../core/extensions/index.js";
|
|
21
|
+
import { locateItem, readLocatedItemSnapshot, } from "../../core/store/item-store.js";
|
|
22
|
+
/** Decode a whole record or identify an invalid suffix without hiding a valid record. */
|
|
23
|
+
function decodeSalvageLine(line) {
|
|
24
|
+
let parsed;
|
|
25
|
+
try {
|
|
26
|
+
parsed = JSON.parse(line);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// A complete record followed by crash padding is not a disposable line.
|
|
30
|
+
// Refuse instead of silently dropping that record along with the padding.
|
|
31
|
+
if (line.trimStart().startsWith("{") && line.includes("}")) {
|
|
32
|
+
throw new PmCliError("History salvage cannot discard a potentially complete record with trailing corruption.", EXIT_CODE.CONFLICT, { code: "history_salvage_refused" });
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
37
|
+
throw new PmCliError("History salvage would conceal interior corruption or a malformed record.", EXIT_CODE.CONFLICT, { code: "history_salvage_refused" });
|
|
38
|
+
}
|
|
39
|
+
const entry = parsed;
|
|
40
|
+
if (!Array.isArray(entry.patch) ||
|
|
41
|
+
![
|
|
42
|
+
entry.ts,
|
|
43
|
+
entry.author,
|
|
44
|
+
entry.op,
|
|
45
|
+
entry.before_hash,
|
|
46
|
+
entry.after_hash,
|
|
47
|
+
].every((field) => typeof field === "string")) {
|
|
48
|
+
throw new PmCliError("History salvage requires complete history records before the invalid tail.", EXIT_CODE.CONFLICT, { code: "history_salvage_refused" });
|
|
49
|
+
}
|
|
50
|
+
return entry;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Select a hash-verified prefix. Any parseable JSON after the first invalid
|
|
54
|
+
* line makes the corruption interior and therefore unsalvageable by truncation.
|
|
55
|
+
*/
|
|
56
|
+
export function inspectHistoryTail(raw) {
|
|
57
|
+
if (findFirstMergeConflictMarker(raw)) {
|
|
58
|
+
throw new PmCliError("Resolve history merge conflicts before salvage.", EXIT_CODE.CONFLICT, { code: "history_salvage_refused" });
|
|
59
|
+
}
|
|
60
|
+
const entries = [];
|
|
61
|
+
let offset = 0;
|
|
62
|
+
let invalidOffset;
|
|
63
|
+
let invalidLine = 0;
|
|
64
|
+
for (const [index, line] of raw.split("\n").entries()) {
|
|
65
|
+
if (!line.trim()) {
|
|
66
|
+
offset += line.length + 1;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
const parsed = decodeSalvageLine(line);
|
|
70
|
+
if (parsed === null) {
|
|
71
|
+
if (invalidOffset === undefined) {
|
|
72
|
+
invalidOffset = offset;
|
|
73
|
+
invalidLine = index + 1;
|
|
74
|
+
}
|
|
75
|
+
offset += line.length + 1;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (invalidOffset !== undefined) {
|
|
79
|
+
throw new PmCliError("History salvage would discard a record or conceal interior corruption.", EXIT_CODE.CONFLICT, {
|
|
80
|
+
code: "history_salvage_refused",
|
|
81
|
+
required: "Recover the damaged record from version control or a backup; no bytes were changed.",
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
entries.push(parsed);
|
|
85
|
+
offset += line.length + 1;
|
|
86
|
+
}
|
|
87
|
+
const verification = verifyHistoryChainWithVersion(entries);
|
|
88
|
+
if (!verification.ok || verification.item_hash_version === undefined) {
|
|
89
|
+
throw new PmCliError("History salvage requires a nonempty verified prefix.", EXIT_CODE.CONFLICT, {
|
|
90
|
+
code: "history_salvage_refused",
|
|
91
|
+
verification_errors: verification.errors,
|
|
92
|
+
required: "Recover an intact history prefix from version control or a backup.",
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
entries,
|
|
97
|
+
itemHashVersion: verification.item_hash_version,
|
|
98
|
+
prefix: invalidOffset === undefined ? raw : raw.slice(0, invalidOffset),
|
|
99
|
+
receipt: invalidOffset === undefined
|
|
100
|
+
? null
|
|
101
|
+
: {
|
|
102
|
+
first_invalid_line: invalidLine,
|
|
103
|
+
discarded_bytes: Buffer.byteLength(raw.slice(invalidOffset)),
|
|
104
|
+
discarded_sha256: sha256Hex(raw.slice(invalidOffset)),
|
|
105
|
+
retained_entries: entries.length,
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/** Prefer the live item's owner; history supplies ownership only when its file is unreadable. */
|
|
110
|
+
async function readSalvageOwnership(subject, settings, fallback) {
|
|
111
|
+
if (!subject.located)
|
|
112
|
+
return { itemRaw: null, document: fallback };
|
|
113
|
+
const snapshot = await readLocatedItemSnapshot(subject.located, {
|
|
114
|
+
schema: settings.schema,
|
|
115
|
+
});
|
|
116
|
+
return { itemRaw: snapshot.raw, document: snapshot.document ?? fallback };
|
|
117
|
+
}
|
|
118
|
+
/** Refuse lossy decoding so suffix receipts and rollback always describe original bytes. */
|
|
119
|
+
async function readSalvageStream(historyPath) {
|
|
120
|
+
let bytes;
|
|
121
|
+
try {
|
|
122
|
+
bytes = await readFile(historyPath);
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
if (isFileMissingError(error))
|
|
126
|
+
return null;
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
129
|
+
if (!isUtf8(bytes)) {
|
|
130
|
+
throw new PmCliError("History salvage requires UTF-8 input; recover binary corruption from a byte-preserving backup.", EXIT_CODE.CONFLICT, { code: "history_salvage_refused" });
|
|
131
|
+
}
|
|
132
|
+
return bytes.toString("utf8");
|
|
133
|
+
}
|
|
134
|
+
/** Salvage one explicitly selected stream under its normal ownership and lock policy. */
|
|
135
|
+
export async function salvageHistoryTail(params) {
|
|
136
|
+
const { subject, settings, options, author } = params;
|
|
137
|
+
if ([
|
|
138
|
+
options.normalizeProvenance,
|
|
139
|
+
options.auditOperation,
|
|
140
|
+
options.auditContext,
|
|
141
|
+
options.auditContextById,
|
|
142
|
+
options.forceAuditEntry,
|
|
143
|
+
options.mergeReceiptProof,
|
|
144
|
+
options.mergeReceiptProofById,
|
|
145
|
+
].some(Boolean)) {
|
|
146
|
+
throw new PmCliError("Tail salvage cannot be combined with other repair modes.", EXIT_CODE.USAGE);
|
|
147
|
+
}
|
|
148
|
+
const raw = await readSalvageStream(subject.historyPath);
|
|
149
|
+
if (raw === null)
|
|
150
|
+
throw new PmCliError(`No history stream exists for ${subject.id}.`, EXIT_CODE.NOT_FOUND);
|
|
151
|
+
await runActiveOnReadHooks({ path: subject.historyPath, scope: "project" });
|
|
152
|
+
const selected = inspectHistoryTail(raw);
|
|
153
|
+
const replay = replayHistoryToTarget(selected.entries, selected.entries.length - 1);
|
|
154
|
+
const document = replayToItemDocument(replay);
|
|
155
|
+
if (typeof replay.metadata.id === "string" &&
|
|
156
|
+
replay.metadata.id !== subject.id) {
|
|
157
|
+
throw new PmCliError("History belongs to another item; salvage refused.", EXIT_CODE.CONFLICT, { code: "history_salvage_refused" });
|
|
158
|
+
}
|
|
159
|
+
const ownership = await readSalvageOwnership(subject, settings, document);
|
|
160
|
+
const warnings = checkHistoryRewriteOwnership({
|
|
161
|
+
itemDocument: ownership.document,
|
|
162
|
+
subjectId: subject.id,
|
|
163
|
+
author,
|
|
164
|
+
force: options.force,
|
|
165
|
+
settings,
|
|
166
|
+
});
|
|
167
|
+
const version = selected.itemHashVersion;
|
|
168
|
+
const dryRun = options.dryRun === true;
|
|
169
|
+
const changed = selected.receipt !== null;
|
|
170
|
+
if (changed && !dryRun) {
|
|
171
|
+
const release = await acquireLock(params.pmRoot, subject.id, settings.locks.ttl_seconds, author, Boolean(options.force), settings.governance.force_required_for_stale_lock, settings.locks.wait_ms);
|
|
172
|
+
try {
|
|
173
|
+
const currentRaw = await readSalvageStream(subject.historyPath);
|
|
174
|
+
const located = await locateItem(params.pmRoot, subject.id, settings.id_prefix, settings.item_format, params.typeRegistry.type_to_folder);
|
|
175
|
+
const currentItem = located
|
|
176
|
+
? await readFileIfExists(located.itemPath)
|
|
177
|
+
: null;
|
|
178
|
+
if (currentRaw !== raw || currentItem !== ownership.itemRaw) {
|
|
179
|
+
throw new PmCliError("History or item changed while waiting for salvage lock; retry.", EXIT_CODE.CONFLICT);
|
|
180
|
+
}
|
|
181
|
+
const audit = sealHistoryRecord({
|
|
182
|
+
...createHistoryEntry({
|
|
183
|
+
nowIso: nowIso(),
|
|
184
|
+
author,
|
|
185
|
+
op: "history_salvage",
|
|
186
|
+
before: document,
|
|
187
|
+
after: document,
|
|
188
|
+
message: options.message,
|
|
189
|
+
context: { history_salvage: selected.receipt },
|
|
190
|
+
}),
|
|
191
|
+
before_hash: selected.entries.at(-1).after_hash,
|
|
192
|
+
after_hash: selected.entries.at(-1).after_hash,
|
|
193
|
+
item_hash_version: version,
|
|
194
|
+
});
|
|
195
|
+
await writeHistoryRawWithRollback({
|
|
196
|
+
historyPath: subject.historyPath,
|
|
197
|
+
historyRawUnderLock: raw,
|
|
198
|
+
nextHistoryRaw: `${selected.prefix}${JSON.stringify(audit)}\n`,
|
|
199
|
+
});
|
|
200
|
+
warnings.push(...(await runActiveOnWriteHooks({
|
|
201
|
+
path: subject.historyPath,
|
|
202
|
+
scope: "project",
|
|
203
|
+
op: "history_salvage:history",
|
|
204
|
+
})));
|
|
205
|
+
}
|
|
206
|
+
finally {
|
|
207
|
+
await release();
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return {
|
|
211
|
+
id: subject.id,
|
|
212
|
+
dry_run: dryRun,
|
|
213
|
+
changed,
|
|
214
|
+
history: {
|
|
215
|
+
path: subject.historyPath,
|
|
216
|
+
entries_scanned: selected.entries.length,
|
|
217
|
+
chain_drift_before: false,
|
|
218
|
+
entries_rehashed: 0,
|
|
219
|
+
entries_patch_repaired: 0,
|
|
220
|
+
converted_replace_to_add: 0,
|
|
221
|
+
skipped_ops: 0,
|
|
222
|
+
reconciled_with_item: false,
|
|
223
|
+
audit_entry_added: changed && !dryRun,
|
|
224
|
+
verify_ok: true,
|
|
225
|
+
verify_errors: [],
|
|
226
|
+
item_hash_version_before: version,
|
|
227
|
+
item_hash_version_after: version,
|
|
228
|
+
version_disposition: "preserved",
|
|
229
|
+
},
|
|
230
|
+
item: {
|
|
231
|
+
exists: subject.located !== null,
|
|
232
|
+
path: subject.located?.itemPath ?? null,
|
|
233
|
+
matched_chain_before: null,
|
|
234
|
+
},
|
|
235
|
+
provenance_normalization: {
|
|
236
|
+
requested: false,
|
|
237
|
+
changed: false,
|
|
238
|
+
events_changed: 0,
|
|
239
|
+
observations_removed: 0,
|
|
240
|
+
invalid_values: [],
|
|
241
|
+
},
|
|
242
|
+
...(selected.receipt ? { salvage: selected.receipt } : {}),
|
|
243
|
+
warnings,
|
|
244
|
+
generated_at: nowIso(),
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
//# sourceMappingURL=salvage.js.map
|
|
248
|
+
//# debugId=49af8ec4-dd6b-56ef-8833-9c776fa35c85
|
|
@@ -4,16 +4,17 @@
|
|
|
4
4
|
* Implements the pm history compact command surface and its agent-facing runtime behavior.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
7
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="26c39ecd-5d72-5cc9-b22b-6abc33fbf4ac")}catch(e){}}();
|
|
8
8
|
import { assertInitializedTracker } from "./environment/tracker-preflight.js";
|
|
9
9
|
import fs from "node:fs/promises";
|
|
10
10
|
import path from "node:path";
|
|
11
|
-
import { createHistoryEntry, hashHistoryStream,
|
|
11
|
+
import { createHistoryEntry, hashHistoryStream, sealHistoryRecord, SUPPORTED_HISTORY_ITEM_HASH_VERSIONS, } from "../core/history/history.js";
|
|
12
12
|
import { selectHistoryCompactBulkTargets, } from "../core/history/history-compact-bulk.js";
|
|
13
13
|
import { executeHistoryRewrite, writeHistoryRawWithRollback, } from "../core/history/history-rewrite.js";
|
|
14
|
-
import { cloneEmptyReplayDocument, historyEntriesToRaw,
|
|
14
|
+
import { cloneEmptyReplayDocument, historyEntriesToRaw, replayHashVerificationCandidates, replayToItemDocument, toReplayDocument, verifyHistoryChain, } from "../core/history/replay.js";
|
|
15
15
|
import { applyHistoryPatch } from "../core/history/projection.js";
|
|
16
16
|
import { readHistoryEntries } from "../core/history/read.js";
|
|
17
|
+
import { historyVersionOffset } from "../core/history/version-address.js";
|
|
17
18
|
import { resolveItemTypeRegistry } from "../core/item/type-registry.js";
|
|
18
19
|
import { lifecycleClassifierFromStatusRegistry } from "../core/governance/metadata-coverage.js";
|
|
19
20
|
import { resolveRuntimeStatusRegistry } from "../core/schema/runtime-schema.js";
|
|
@@ -29,6 +30,7 @@ import { resolvePmRoot } from "../core/store/paths.js";
|
|
|
29
30
|
import { readSettings } from "../core/store/settings.js";
|
|
30
31
|
import { resolveAuthor } from "../core/shared/author.js";
|
|
31
32
|
import { resolveHistorySubject } from "./history-redact.js";
|
|
33
|
+
/** Translate a durable version or timestamp boundary into physical compacted and retained counts. */
|
|
32
34
|
function parseBeforeBoundary(before, entries) {
|
|
33
35
|
if (before === undefined) {
|
|
34
36
|
return {
|
|
@@ -44,12 +46,13 @@ function parseBeforeBoundary(before, entries) {
|
|
|
44
46
|
}
|
|
45
47
|
if (/^\d+$/.test(raw)) {
|
|
46
48
|
const version = Number.parseInt(raw, 10);
|
|
49
|
+
const offset = historyVersionOffset(entries);
|
|
47
50
|
if (!Number.isSafeInteger(version) ||
|
|
48
|
-
version < 1 ||
|
|
49
|
-
version > entries.length + 1) {
|
|
50
|
-
throw new PmCliError(`history-compact --before version must be between 1 and ${entries.length + 1}.`, EXIT_CODE.USAGE);
|
|
51
|
+
version < offset + 1 ||
|
|
52
|
+
version > offset + entries.length + 1) {
|
|
53
|
+
throw new PmCliError(`history-compact --before version must be between ${offset + 1} and ${offset + entries.length + 1}.`, EXIT_CODE.USAGE);
|
|
51
54
|
}
|
|
52
|
-
const compactCount =
|
|
55
|
+
const compactCount = version - offset - 1;
|
|
53
56
|
return {
|
|
54
57
|
kind: "version",
|
|
55
58
|
raw,
|
|
@@ -79,18 +82,13 @@ function parseBeforeBoundary(before, entries) {
|
|
|
79
82
|
retainedCount: entries.length - compactCount,
|
|
80
83
|
};
|
|
81
84
|
}
|
|
85
|
+
/** Fold the already verified stream once, capturing its compaction checkpoint. */
|
|
82
86
|
function replayHistoryAndResolveCheckpoint(entries, compactCount) {
|
|
83
87
|
let replay = cloneEmptyReplayDocument();
|
|
84
88
|
let checkpoint = cloneEmptyReplayDocument();
|
|
85
89
|
for (let index = 0; index < entries.length; index += 1) {
|
|
86
90
|
const entry = entries[index];
|
|
87
|
-
if (replayHash(replay) !== entry.before_hash) {
|
|
88
|
-
throw new PmCliError(`history-compact detected before-hash drift at entry ${index + 1}; run pm history-repair first.`, EXIT_CODE.CONFLICT);
|
|
89
|
-
}
|
|
90
91
|
replay = applyHistoryPatch(replay, entry.patch, index + 1, entry.op);
|
|
91
|
-
if (replayHash(replay) !== entry.after_hash) {
|
|
92
|
-
throw new PmCliError(`history-compact detected after-hash drift at entry ${index + 1}; run pm history-repair first.`, EXIT_CODE.CONFLICT);
|
|
93
|
-
}
|
|
94
92
|
if (index + 1 === compactCount) {
|
|
95
93
|
checkpoint = structuredClone(replay);
|
|
96
94
|
}
|
|
@@ -100,23 +98,7 @@ function replayHistoryAndResolveCheckpoint(entries, compactCount) {
|
|
|
100
98
|
finalReplay: replay,
|
|
101
99
|
};
|
|
102
100
|
}
|
|
103
|
-
|
|
104
|
-
let replay = structuredClone(seed);
|
|
105
|
-
const rewritten = [];
|
|
106
|
-
for (const [index, entry] of retainedEntries.entries()) {
|
|
107
|
-
const beforeHash = replayHash(replay);
|
|
108
|
-
replay = applyHistoryPatch(replay, entry.patch, retainedEntryOffset + index + 1, entry.op);
|
|
109
|
-
rewritten.push(resealHistoryRewrite(entry, {
|
|
110
|
-
...entry,
|
|
111
|
-
before_hash: beforeHash,
|
|
112
|
-
after_hash: replayHash(replay),
|
|
113
|
-
}));
|
|
114
|
-
}
|
|
115
|
-
return {
|
|
116
|
-
entries: rewritten,
|
|
117
|
-
finalReplay: replay,
|
|
118
|
-
};
|
|
119
|
-
}
|
|
101
|
+
/** Snapshot the live item and compare it with the stream's supported historical hash surfaces. */
|
|
120
102
|
async function loadHistoryCompactCurrentItem(subject, settings, historyEntries) {
|
|
121
103
|
const loadedItem = subject.located
|
|
122
104
|
? await readLocatedItem(subject.located, { schema: settings.schema })
|
|
@@ -128,11 +110,12 @@ async function loadHistoryCompactCurrentItem(subject, settings, historyEntries)
|
|
|
128
110
|
matchedChainBefore: null,
|
|
129
111
|
};
|
|
130
112
|
}
|
|
113
|
+
const lastEntry = historyEntries[historyEntries.length - 1];
|
|
131
114
|
return {
|
|
132
115
|
loadedItem,
|
|
133
116
|
currentItemRawBeforeLock: loadedItem.raw,
|
|
134
|
-
matchedChainBefore:
|
|
135
|
-
|
|
117
|
+
matchedChainBefore: SUPPORTED_HISTORY_ITEM_HASH_VERSIONS.some((version) => (lastEntry.item_hash_version === undefined || lastEntry.item_hash_version === version) &&
|
|
118
|
+
replayHashVerificationCandidates(toReplayDocument(loadedItem.document), version).includes(lastEntry.after_hash)),
|
|
136
119
|
};
|
|
137
120
|
}
|
|
138
121
|
function buildHistoryCompactMessage(options, boundary) {
|
|
@@ -151,6 +134,7 @@ function buildHistoryCompactBaselineMessage(boundary) {
|
|
|
151
134
|
}
|
|
152
135
|
return `history-compact baseline snapshot before ${boundary.raw}.`;
|
|
153
136
|
}
|
|
137
|
+
/** Replace the pruned prefix with a durable-addressed checkpoint while retaining original records and hash semantics. */
|
|
154
138
|
function buildHistoryCompactEntries(params) {
|
|
155
139
|
if (!params.changed) {
|
|
156
140
|
return {
|
|
@@ -161,9 +145,9 @@ function buildHistoryCompactEntries(params) {
|
|
|
161
145
|
}
|
|
162
146
|
const { checkpoint, finalReplay } = replayHistoryAndResolveCheckpoint(params.historyEntries, params.boundary.compactCount);
|
|
163
147
|
const retained = params.historyEntries.slice(params.boundary.compactCount);
|
|
164
|
-
const
|
|
148
|
+
const offset = historyVersionOffset(params.historyEntries, true);
|
|
165
149
|
const baselineEntry = createHistoryEntry({
|
|
166
|
-
nowIso:
|
|
150
|
+
nowIso: params.historyEntries[params.boundary.compactCount - 1].ts,
|
|
167
151
|
author: params.author,
|
|
168
152
|
op: "history_compact_baseline",
|
|
169
153
|
before: replayToItemDocument(cloneEmptyReplayDocument()),
|
|
@@ -172,6 +156,8 @@ function buildHistoryCompactEntries(params) {
|
|
|
172
156
|
context: {
|
|
173
157
|
history_compaction: {
|
|
174
158
|
contract_version: 1,
|
|
159
|
+
version_offset: offset === null ? null : offset + params.boundary.compactCount - 1,
|
|
160
|
+
checkpoint_timestamp_semantics: "last_compacted_entry",
|
|
175
161
|
pruned_entry_count: params.boundary.compactCount,
|
|
176
162
|
pruned_stream_digest: hashHistoryStream(params.historyEntries.slice(0, params.boundary.compactCount)),
|
|
177
163
|
retained_proof: "stream_digest_and_checkpoint",
|
|
@@ -179,7 +165,15 @@ function buildHistoryCompactEntries(params) {
|
|
|
179
165
|
},
|
|
180
166
|
},
|
|
181
167
|
});
|
|
182
|
-
const
|
|
168
|
+
const checkpointEntry = params.historyEntries[params.boundary.compactCount - 1];
|
|
169
|
+
// Preserve the checkpoint's epoch and semantic hash variant. Do not introduce
|
|
170
|
+
// an explicit epoch into an unversioned stream: it would pin later legacy reads.
|
|
171
|
+
const rewrittenEntries = [sealHistoryRecord({
|
|
172
|
+
...baselineEntry,
|
|
173
|
+
before_hash: params.historyEntries[0].before_hash,
|
|
174
|
+
after_hash: checkpointEntry.after_hash,
|
|
175
|
+
item_hash_version: checkpointEntry.item_hash_version,
|
|
176
|
+
}), ...retained];
|
|
183
177
|
if (!params.dryRun) {
|
|
184
178
|
rewrittenEntries.push(createHistoryEntry({
|
|
185
179
|
nowIso: nowIso(),
|
|
@@ -279,7 +273,8 @@ export async function runHistoryCompact(id, options, global) {
|
|
|
279
273
|
rewrittenEntries,
|
|
280
274
|
})));
|
|
281
275
|
}
|
|
282
|
-
const
|
|
276
|
+
const offset = historyVersionOffset(historyEntries, true);
|
|
277
|
+
const firstRetainedEntry = boundary.retainedCount > 0 && offset !== null ? offset + boundary.compactCount + 1 : null;
|
|
283
278
|
return {
|
|
284
279
|
id: subject.id,
|
|
285
280
|
dry_run: dryRun,
|
|
@@ -530,4 +525,4 @@ export async function runHistoryCompactBulk(options, global) {
|
|
|
530
525
|
};
|
|
531
526
|
}
|
|
532
527
|
//# sourceMappingURL=history-compact.js.map
|
|
533
|
-
//# debugId=
|
|
528
|
+
//# debugId=26c39ecd-5d72-5cc9-b22b-6abc33fbf4ac
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { applyHistoryPatch, ensureMaterializedHistoryTarget, extractPatchFailureContext, replayHistoryToTarget, resolveHistoryTarget, type ResolvedHistoryTarget } from "../core/history/projection.js";
|
|
2
2
|
import { readHistoryEntries } from "../core/history/read.js";
|
|
3
3
|
import type { HistoryEntry, ItemDocument } from "../types/index.js";
|
|
4
|
+
export { findHistoryIdentityDiscontinuities, type HistoryIdentityDiscontinuity, } from "../core/history/identity.js";
|
|
4
5
|
/** Workspace resolution controls accepted by {@link getItemAt}. */
|
|
5
6
|
export interface GetItemAtOptions {
|
|
6
7
|
/** Explicit tracker root, equivalent to the CLI global `--pm-path`. */
|
|
@@ -14,8 +15,8 @@ export interface GetItemAtResult {
|
|
|
14
15
|
document: ItemDocument;
|
|
15
16
|
/** Always true, preventing consumers from confusing this with current state. */
|
|
16
17
|
reconstructed: true;
|
|
17
|
-
/**
|
|
18
|
-
as_of_version: number;
|
|
18
|
+
/** Durable one-based version; null when a legacy checkpoint lost its mapping. */
|
|
19
|
+
as_of_version: number | null;
|
|
19
20
|
/** Timestamp of the final included history entry. */
|
|
20
21
|
as_of_timestamp: string;
|
|
21
22
|
/** Normalized target resolution metadata shared with restore. */
|
package/dist/sdk/history-read.js
CHANGED
|
@@ -7,11 +7,12 @@
|
|
|
7
7
|
* systems without acquiring locks or appending history.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
!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]="
|
|
10
|
+
!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]="6fe766e2-93e8-5653-beb5-16bd92efae51")}catch(e){}}();
|
|
11
11
|
import { assertInitializedTracker } from "./environment/tracker-preflight.js";
|
|
12
12
|
import { getActiveExtensionRegistrations } from "../core/extensions/index.js";
|
|
13
13
|
import { applyHistoryPatch, ensureMaterializedHistoryTarget, extractPatchFailureContext, replayHistoryToTarget, resolveHistoryTarget, } from "../core/history/projection.js";
|
|
14
14
|
import { readHistoryEntries } from "../core/history/read.js";
|
|
15
|
+
import { historyVersionOffset } from "../core/history/version-address.js";
|
|
15
16
|
import { hashDocument, hashDocumentVerificationCandidates, hashEmptyDocument, } from "../core/history/history.js";
|
|
16
17
|
import { replayToCanonicalItemDocument, verifyHistoryChainWithVersion, } from "../core/history/replay.js";
|
|
17
18
|
import { resolveItemTypeRegistry } from "../core/item/type-registry.js";
|
|
@@ -20,6 +21,7 @@ import { PmCliError } from "../core/shared/errors.js";
|
|
|
20
21
|
import { resolvePmRoot } from "../core/store/paths.js";
|
|
21
22
|
import { readSettings } from "../core/store/settings.js";
|
|
22
23
|
import { resolveHistorySubject } from "./history-redact.js";
|
|
24
|
+
export { findHistoryIdentityDiscontinuities, } from "../core/history/identity.js";
|
|
23
25
|
/**
|
|
24
26
|
* Verify a history chain and optionally compare it with a current document.
|
|
25
27
|
*
|
|
@@ -80,10 +82,11 @@ export async function getItemAt(id, target, options = {}) {
|
|
|
80
82
|
throw new PmCliError(`History target resolved to item ${document.metadata.id}, expected ${subject.id}.`, EXIT_CODE.GENERIC_FAILURE);
|
|
81
83
|
}
|
|
82
84
|
const targetEntry = history[resolvedTarget.historyIndex];
|
|
85
|
+
const offset = historyVersionOffset(history, true);
|
|
83
86
|
return {
|
|
84
87
|
document,
|
|
85
88
|
reconstructed: true,
|
|
86
|
-
as_of_version: resolvedTarget.historyIndex + 1,
|
|
89
|
+
as_of_version: offset === null ? null : offset + resolvedTarget.historyIndex + 1,
|
|
87
90
|
as_of_timestamp: targetEntry.ts,
|
|
88
91
|
target: resolvedTarget,
|
|
89
92
|
history_length: history.length,
|
|
@@ -91,4 +94,4 @@ export async function getItemAt(id, target, options = {}) {
|
|
|
91
94
|
}
|
|
92
95
|
export { applyHistoryPatch, ensureMaterializedHistoryTarget, extractPatchFailureContext, readHistoryEntries, replayHistoryToTarget, resolveHistoryTarget, };
|
|
93
96
|
//# sourceMappingURL=history-read.js.map
|
|
94
|
-
//# debugId=
|
|
97
|
+
//# debugId=6fe766e2-93e8-5653-beb5-16bd92efae51
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type HistorySalvageReceipt } from "./history/salvage.js";
|
|
1
2
|
import { type ReplayDocument } from "../core/history/replay.js";
|
|
2
3
|
import type { GlobalOptions } from "../core/shared/command-types.js";
|
|
3
4
|
import type { HistoryEntry } from "../types/index.js";
|
|
@@ -5,6 +6,8 @@ import { type MergeDecisionReceipt } from "./merge/receipts.js";
|
|
|
5
6
|
import { type ProvenanceNormalizationReceipt } from "./governance/provenance-health.js";
|
|
6
7
|
/** Documents the history repair command options payload exchanged by command, SDK, and package integrations. */
|
|
7
8
|
export interface HistoryRepairCommandOptions {
|
|
9
|
+
/** Remove only an invalid trailing suffix after a verified prefix; never re-anchor or reconcile. */
|
|
10
|
+
salvageTail?: boolean;
|
|
8
11
|
/** Value that configures or reports dry run for this contract. */
|
|
9
12
|
dryRun?: boolean;
|
|
10
13
|
/** Value that configures or reports author for this contract. */
|
|
@@ -36,6 +39,8 @@ export interface HistoryRepairCommandOptions {
|
|
|
36
39
|
}
|
|
37
40
|
/** Documents the history repair result payload exchanged by command, SDK, and package integrations. */
|
|
38
41
|
export interface HistoryRepairResult {
|
|
42
|
+
/** Present when invalid trailing bytes were selected for explicit salvage. */
|
|
43
|
+
salvage?: HistorySalvageReceipt;
|
|
39
44
|
/** Stable identifier used to reference this record across commands and storage. */
|
|
40
45
|
id: string;
|
|
41
46
|
/** Value that configures or reports dry run for this contract. */
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
* Implements the pm history repair command surface and its agent-facing runtime behavior.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
7
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="d397bc22-d0ae-5c3d-a1bf-7f4c38ca31be")}catch(e){}}();
|
|
8
8
|
import { assertInitializedTracker } from "./environment/tracker-preflight.js";
|
|
9
|
+
import { salvageHistoryTail } from "./history/salvage.js";
|
|
9
10
|
import { realpath } from "node:fs/promises";
|
|
10
11
|
import path from "node:path";
|
|
11
12
|
import jsonPatch from "fast-json-patch";
|
|
@@ -389,6 +390,14 @@ export async function runHistoryRepair(id, options, global) {
|
|
|
389
390
|
const settings = await readSettings(pmRoot);
|
|
390
391
|
const typeRegistry = resolveItemTypeRegistry(settings, getActiveExtensionRegistrations());
|
|
391
392
|
const subject = await resolveHistorySubject(pmRoot, id, settings, typeRegistry.type_to_folder);
|
|
393
|
+
if (options.salvageTail === true) {
|
|
394
|
+
return salvageHistoryTail({ pmRoot, subject, settings, typeRegistry, options, author: resolveAuthor(options.author, settings.author_default) });
|
|
395
|
+
}
|
|
396
|
+
return repairHistorySubject({ pmRoot, subject, settings, typeRegistry, options });
|
|
397
|
+
}
|
|
398
|
+
/** Reanchor or reconcile one resolved subject, separately from byte-preserving salvage. */
|
|
399
|
+
async function repairHistorySubject(params) {
|
|
400
|
+
const { pmRoot, subject, settings, typeRegistry, options } = params;
|
|
392
401
|
if (!(await pathExists(subject.historyPath))) {
|
|
393
402
|
throw new PmCliError(`No history stream exists for ${subject.id}.`, EXIT_CODE.NOT_FOUND);
|
|
394
403
|
}
|
|
@@ -537,6 +546,9 @@ export function assertHistoryRepairTarget(id, all) {
|
|
|
537
546
|
}
|
|
538
547
|
/** Bulk drift repair: scan every item's history stream with the same drift scan `pm health` uses, then run the audited single-stream repair (ownership check, lock, no-drift verification, audit marker) for each drifted stream. One failing stream never aborts the rest; failures are collected per row and the caller decides the exit code from `totals.failed`. */
|
|
539
548
|
export async function runHistoryRepairAll(options, global) {
|
|
549
|
+
if (options.salvageTail === true) {
|
|
550
|
+
throw new PmCliError("Tail salvage requires one explicit item ID; --all is not supported.", EXIT_CODE.USAGE);
|
|
551
|
+
}
|
|
540
552
|
const pmRoot = resolvePmRoot(process.cwd(), global.path);
|
|
541
553
|
await assertInitializedTracker(pmRoot);
|
|
542
554
|
const settings = await readSettings(pmRoot);
|
|
@@ -608,4 +620,4 @@ export async function runHistoryRepairAll(options, global) {
|
|
|
608
620
|
};
|
|
609
621
|
}
|
|
610
622
|
//# sourceMappingURL=history-repair.js.map
|
|
611
|
-
//# debugId=
|
|
623
|
+
//# debugId=d397bc22-d0ae-5c3d-a1bf-7f4c38ca31be
|
|
@@ -62,6 +62,10 @@ export interface ReleaseResult {
|
|
|
62
62
|
previous_assignee: string | null;
|
|
63
63
|
/** Value that configures or reports forced for this contract. */
|
|
64
64
|
forced: boolean;
|
|
65
|
+
/** Stable handoff diagnostics shared by CLI and MCP consumers. */
|
|
66
|
+
warnings?: string[];
|
|
67
|
+
/** Concrete recovery commands when releasing ownership leaves work in progress. */
|
|
68
|
+
suggestions?: string[];
|
|
65
69
|
}
|
|
66
70
|
/** Documents the claim mutation options payload exchanged by command, SDK, and package integrations. */
|
|
67
71
|
export interface ClaimMutationOptions {
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* Implements the pm claim command surface and its agent-facing runtime behavior.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
7
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="a4d95016-05b9-5272-8044-2649da644e98")}catch(e){}}();
|
|
8
8
|
import { assertInitializedTracker } from "../environment/tracker-preflight.js";
|
|
9
|
-
import { resolveRuntimeStatusRegistry, statusIsTerminal, EXIT_CODE, PmCliError, toItemRecord, mutateItem, resolvePmRoot, readSettings, resolveAuthor, resolveClaimPrincipal, } from "../runtime-primitives.js";
|
|
9
|
+
import { resolveRuntimeStatusRegistry, statusIsTerminal, EXIT_CODE, PmCliError, toItemRecord, mutateItem, resolvePmRoot, readSettings, resolveAuthor, resolveClaimPrincipal, normalizeStatusForRegistry, } from "../runtime-primitives.js";
|
|
10
10
|
import { wrapOwnershipConflict } from "../annotations.js";
|
|
11
11
|
import { describeItemOwnershipConflict } from "../ownership-source.js";
|
|
12
12
|
import { runNext, } from "../query/next.js";
|
|
@@ -226,12 +226,18 @@ export async function runRelease(id, force, global, options = {}) {
|
|
|
226
226
|
principal: claimPrincipal,
|
|
227
227
|
itemId: result.item.id,
|
|
228
228
|
});
|
|
229
|
+
const statusRegistry = resolveRuntimeStatusRegistry(settings.schema);
|
|
230
|
+
const stillInProgress = normalizeStatusForRegistry(result.item.status, statusRegistry) === normalizeStatusForRegistry(statusRegistry.in_progress_status, statusRegistry);
|
|
229
231
|
return {
|
|
230
232
|
item: toItemRecord(result.item),
|
|
231
233
|
released_by: author,
|
|
232
234
|
previous_assignee: previousAssignee,
|
|
233
235
|
forced: force,
|
|
236
|
+
...(stillInProgress ? {
|
|
237
|
+
warnings: ["released_unclaimed_in_progress"],
|
|
238
|
+
suggestions: [`${result.item.id} remains ${result.item.status} and unclaimed. If work is paused, run pm pause-task ${result.item.id} to move it to ${statusRegistry.open_status}.`],
|
|
239
|
+
} : {}),
|
|
234
240
|
};
|
|
235
241
|
}
|
|
236
242
|
//# sourceMappingURL=claim.js.map
|
|
237
|
-
//# debugId=
|
|
243
|
+
//# debugId=a4d95016-05b9-5272-8044-2649da644e98
|