@unbrained/pm-cli 2026.9.1 → 2026.9.2

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.
Files changed (49) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +10 -0
  3. package/dist/cli-bundle/bundle-manifest.json +59 -59
  4. package/dist/cli-bundle/chunks/{chunk-2MC2LCXZ.js → chunk-FZ4Z2J4F.js} +2 -2
  5. package/dist/cli-bundle/chunks/{chunk-6XIIGE7U.js → chunk-T6H5BZCP.js} +2 -2
  6. package/dist/cli-bundle/chunks/{chunk-U45JXJCC.js → chunk-UDW64J2W.js} +2 -2
  7. package/dist/cli-bundle/chunks/chunk-ZLJIFSOE.js +35 -0
  8. package/dist/cli-bundle/chunks/{register-mutation-2MVLRS3J.js → register-mutation-WFQ7MJTX.js} +2 -2
  9. package/dist/cli-bundle/chunks/{register-operations-WCIDXBU3.js → register-operations-JVTI7JPP.js} +2 -2
  10. package/dist/cli-bundle/focused-chunks/{chunk-FWPLDEXF.js → chunk-242AXYB2.js} +2 -2
  11. package/dist/cli-bundle/focused-chunks/{chunk-Y6JJIX6I.js → chunk-O67VDLOT.js} +48 -48
  12. package/dist/cli-bundle/main.js +2 -2
  13. package/dist/cli-bundle/sdk-contracts.js +1 -1
  14. package/dist/cli-bundle/sdk-core.js +1 -1
  15. package/dist/cli-bundle/sdk-governance.js +1 -1
  16. package/dist/cli-bundle/sdk-merge.js +32 -32
  17. package/dist/cli-bundle/sdk-runtime.js +1 -1
  18. package/dist/cli-bundle/sdk-testing.js +1 -1
  19. package/dist/cli-bundle/sdk.js +1 -1
  20. package/dist/core/diagnostics/remediation.js +9 -4
  21. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +14 -2
  22. package/dist/sdk/governance/health.js +77 -11
  23. package/dist/sdk/governance/validate.js +4 -2
  24. package/dist/sdk/merge/index.d.ts +1 -1
  25. package/dist/sdk/merge/index.js +2 -2
  26. package/dist/sdk/merge/receipts.d.ts +24 -0
  27. package/dist/sdk/merge/receipts.js +122 -30
  28. package/docs/CONTEXT_RECOVERY_PRIMITIVES.md +11 -2
  29. package/docs/MERGE_SAFETY.md +16 -2
  30. package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +4 -3
  31. package/marketplace.json +2 -2
  32. package/package.json +1 -1
  33. package/packages/pm-beads/package.json +1 -1
  34. package/packages/pm-calendar/package.json +1 -1
  35. package/packages/pm-command-kit/package.json +1 -1
  36. package/packages/pm-digital-twin/package.json +1 -1
  37. package/packages/pm-governance-audit/package.json +1 -1
  38. package/packages/pm-guide-shell/package.json +1 -1
  39. package/packages/pm-kanban/package.json +1 -1
  40. package/packages/pm-lifecycle-hooks/package.json +1 -1
  41. package/packages/pm-linked-test-adapters/package.json +1 -1
  42. package/packages/pm-search-advanced/package.json +1 -1
  43. package/packages/pm-templates/package.json +1 -1
  44. package/packages/pm-todos/package.json +1 -1
  45. package/packages/pm-vcs/package.json +1 -1
  46. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  47. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  48. package/sdk/public-surface.json +29 -6
  49. package/dist/cli-bundle/chunks/chunk-ALKKAUFN.js +0 -35
@@ -7,10 +7,10 @@
7
7
  * value for the coordinator that performed the merge.
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]="624fc190-4c46-5a55-b757-72e621aec35d")}catch(e){}}();
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]="df7d5bd9-8381-5961-bda1-9e8ed335afc9")}catch(e){}}();
11
11
  import { execFile } from "node:child_process";
12
12
  import { randomUUID } from "node:crypto";
13
- import { readdir } from "node:fs/promises";
13
+ import { readdir, stat } from "node:fs/promises";
14
14
  import path from "node:path";
15
15
  import { promisify } from "node:util";
16
16
  import { ensureDir, isFileAbsentError, isFileMissingError, pathExists, writeFileAtomic, } from "../../core/fs/fs-utils.js";
@@ -24,6 +24,7 @@ const RECEIPT_FIELD_MAX = 2_048;
24
24
  const RECEIPT_FILE_MAX_BYTES = 16 * 1024 * 1024;
25
25
  const RECEIPT_VALUE_MAX_DEPTH = 64;
26
26
  const RECEIPT_VALUE_MAX_NODES = 100_000;
27
+ const RECEIPT_INVALID_EVIDENCE_DETAIL_LIMIT = 100;
27
28
  const RECEIPT_KEYS = new Set([
28
29
  "version",
29
30
  "id",
@@ -42,6 +43,10 @@ const RECEIPT_KEYS = new Set([
42
43
  "value_availability",
43
44
  "evidence_source",
44
45
  ]);
46
+ /** Return whether a receipt id is safe to expose in cleartext diagnostics. */
47
+ export function isSafeReceiptId(value) {
48
+ return RECEIPT_ID_PATTERN.test(value);
49
+ }
45
50
  const RECEIPT_DECISION_KEYS = new Set([
46
51
  "field",
47
52
  "base",
@@ -163,7 +168,7 @@ function hasValidReceiptIdentity(value) {
163
168
  return !hasOnlyKeys(value, RECEIPT_KEYS) || value.version !== 1
164
169
  ? false
165
170
  : typeof value.id === "string" &&
166
- RECEIPT_ID_PATTERN.test(value.id) &&
171
+ isSafeReceiptId(value.id) &&
167
172
  typeof value.item_id === "string" &&
168
173
  RECEIPT_ITEM_ID_PATTERN.test(value.item_id) &&
169
174
  isSafeReceiptItemPath(value.item_path, value.item_id);
@@ -372,54 +377,115 @@ async function readReceiptsFromDirectory(directory, evidenceSource) {
372
377
  names = await readdir(directory);
373
378
  }
374
379
  catch (error) {
380
+ const absent = await receiptDirectoryFailureMeansAbsent(directory, error);
375
381
  return {
376
382
  receipts: [],
377
- invalid_evidence_count: isFileMissingError(error) ? 0 : 1,
383
+ invalid_evidence_count: absent ? 0 : 1,
384
+ invalid_evidence: absent
385
+ ? []
386
+ : [{ evidence_source: evidenceSource, reason: "directory_unreadable" }],
387
+ invalid_evidence_truncated: false,
378
388
  };
379
389
  }
380
390
  const receipts = [];
381
391
  let invalidEvidenceCount = 0;
392
+ const invalidEvidence = [];
393
+ const recordInvalidEvidence = (name, reason) => {
394
+ invalidEvidenceCount += 1;
395
+ if (invalidEvidence.length >= RECEIPT_INVALID_EVIDENCE_DETAIL_LIMIT)
396
+ return;
397
+ const receiptId = name.slice(0, -".json".length);
398
+ invalidEvidence.push({
399
+ evidence_source: evidenceSource,
400
+ reason,
401
+ ...(isSafeReceiptId(receiptId)
402
+ ? { receipt_id: receiptId }
403
+ : { candidate_name_hash: sha256Hex(name) }),
404
+ });
405
+ };
382
406
  for (const name of names.sort((left, right) => left.localeCompare(right))) {
383
407
  if (!name.endsWith(".json"))
384
408
  continue;
409
+ const candidate = await inspectReceiptCandidate(directory, name, evidenceSource);
410
+ if (candidate.receipt !== undefined) {
411
+ receipts.push(candidate.receipt);
412
+ }
413
+ else {
414
+ recordInvalidEvidence(name, candidate.reason);
415
+ }
416
+ }
417
+ return {
418
+ receipts,
419
+ invalid_evidence_count: invalidEvidenceCount,
420
+ invalid_evidence: invalidEvidence,
421
+ invalid_evidence_truncated: invalidEvidenceCount > invalidEvidence.length,
422
+ };
423
+ }
424
+ async function receiptDirectoryFailureMeansAbsent(directory, error, inspectAncestor = stat) {
425
+ if (!isFileMissingError(error))
426
+ return false;
427
+ let ancestor = directory;
428
+ while (true) {
385
429
  try {
386
- const receiptPath = path.join(directory, name);
387
- const raw = await readBoundedRegularReceiptFile(receiptPath);
388
- if (raw === null) {
389
- invalidEvidenceCount += 1;
390
- continue;
391
- }
392
- const parsed = JSON.parse(raw);
393
- if (!isMergeDecisionReceipt(parsed, evidenceSource) ||
394
- name !== receiptFileName(parsed.id)) {
395
- invalidEvidenceCount += 1;
396
- continue;
397
- }
398
- const { preferred: legacyPreference, evidence_source: _serializedEvidenceSource, ...receiptWithoutRuntimeKeys } = parsed;
399
- const normalizedReceipt = {
400
- ...receiptWithoutRuntimeKeys,
401
- requested_preference: parsed.requested_preference ?? legacyPreference ?? "ours",
402
- conflict_resolution: parsed.conflict_resolution ?? "preferred_side",
403
- evidence_source: evidenceSource,
404
- };
405
- receipts.push(normalizedReceipt);
430
+ return (await inspectAncestor(ancestor)).isDirectory();
406
431
  }
407
- catch {
408
- invalidEvidenceCount += 1;
432
+ catch (ancestorError) {
433
+ if (!isFileMissingError(ancestorError))
434
+ return false;
435
+ const parent = path.dirname(ancestor);
436
+ if (parent === ancestor)
437
+ return true;
438
+ ancestor = parent;
409
439
  }
410
440
  }
411
- return { receipts, invalid_evidence_count: invalidEvidenceCount };
441
+ }
442
+ async function inspectReceiptCandidate(directory, name, evidenceSource) {
443
+ let raw;
444
+ try {
445
+ raw = await readBoundedRegularReceiptFile(path.join(directory, name));
446
+ }
447
+ catch {
448
+ return { reason: "candidate_unreadable" };
449
+ }
450
+ if (raw === null)
451
+ return { reason: "candidate_not_bounded_regular_file" };
452
+ let parsed;
453
+ try {
454
+ parsed = JSON.parse(raw);
455
+ }
456
+ catch {
457
+ return { reason: "candidate_invalid_json" };
458
+ }
459
+ if (!isMergeDecisionReceipt(parsed, evidenceSource) ||
460
+ name !== receiptFileName(parsed.id)) {
461
+ return { reason: "schema_or_identity_invalid" };
462
+ }
463
+ const { preferred: legacyPreference, evidence_source: _serializedEvidenceSource, ...receiptWithoutRuntimeKeys } = parsed;
464
+ return {
465
+ receipt: {
466
+ ...receiptWithoutRuntimeKeys,
467
+ requested_preference: parsed.requested_preference ?? legacyPreference ?? "ours",
468
+ conflict_resolution: parsed.conflict_resolution ?? "preferred_side",
469
+ evidence_source: evidenceSource,
470
+ },
471
+ };
412
472
  }
413
473
  /** Inspect valid receipts and count invalid evidence without returning untrusted file contents. */
414
474
  export async function inspectMergeReceiptEvidence(cwd, options = {}) {
415
475
  const directory = await resolveReceiptDirectory(cwd);
416
476
  const local = directory === null
417
- ? { receipts: [], invalid_evidence_count: 0 }
477
+ ? {
478
+ receipts: [],
479
+ invalid_evidence_count: 0,
480
+ invalid_evidence: [],
481
+ invalid_evidence_truncated: false,
482
+ }
418
483
  : await readReceiptsFromDirectory(directory, "clone_local");
419
484
  const trackerRoot = options.pmRoot ?? path.join(cwd, ".agents", "pm");
420
485
  const durable = await readReceiptsFromDirectory(durableReceiptDirectory(trackerRoot), "durable");
421
486
  const receipts = new Map(durable.receipts.map((receipt) => [receipt.id, receipt]));
422
487
  let divergentCopyCount = 0;
488
+ const divergentCopyEvidence = [];
423
489
  for (const receipt of local.receipts) {
424
490
  const durableCopy = receipts.get(receipt.id);
425
491
  if (durableCopy !== undefined &&
@@ -427,6 +493,16 @@ export async function inspectMergeReceiptEvidence(cwd, options = {}) {
427
493
  receiptProvenanceFingerprint(receipt)) {
428
494
  receipts.delete(receipt.id);
429
495
  divergentCopyCount += 1;
496
+ if (local.invalid_evidence.length +
497
+ durable.invalid_evidence.length +
498
+ divergentCopyEvidence.length <
499
+ RECEIPT_INVALID_EVIDENCE_DETAIL_LIMIT) {
500
+ divergentCopyEvidence.push({
501
+ evidence_source: "clone_local_and_durable",
502
+ reason: "copy_provenance_mismatch",
503
+ receipt_id: receipt.id,
504
+ });
505
+ }
430
506
  continue;
431
507
  }
432
508
  receipts.set(receipt.id, durableCopy === undefined
@@ -441,6 +517,17 @@ export async function inspectMergeReceiptEvidence(cwd, options = {}) {
441
517
  invalid_evidence_count: local.invalid_evidence_count +
442
518
  durable.invalid_evidence_count +
443
519
  divergentCopyCount,
520
+ invalid_evidence: [
521
+ ...local.invalid_evidence,
522
+ ...durable.invalid_evidence,
523
+ ...divergentCopyEvidence,
524
+ ].slice(0, RECEIPT_INVALID_EVIDENCE_DETAIL_LIMIT),
525
+ invalid_evidence_truncated: local.invalid_evidence_truncated ||
526
+ durable.invalid_evidence_truncated ||
527
+ local.invalid_evidence_count +
528
+ durable.invalid_evidence_count +
529
+ divergentCopyCount >
530
+ RECEIPT_INVALID_EVIDENCE_DETAIL_LIMIT,
444
531
  clone_local_evidence_resolved: directory !== null,
445
532
  };
446
533
  }
@@ -538,12 +625,17 @@ export async function runMergeReceiptEvidenceReport(options) {
538
625
  complete,
539
626
  count: evidence.receipts.length,
540
627
  invalid_evidence_count: evidence.invalid_evidence_count,
628
+ invalid_evidence: evidence.invalid_evidence,
629
+ invalid_evidence_truncated: evidence.invalid_evidence_truncated,
541
630
  clone_local_evidence_resolved: evidence.clone_local_evidence_resolved,
542
631
  receipts: evidence.receipts,
543
632
  generated_at: nowIso(),
544
633
  };
545
634
  }
546
635
  /** Test-only seams for deterministic receipt-boundary fault coverage. */
547
- export const _testOnlyMergeReceipts = { prepareReceiptSettlement };
636
+ export const _testOnlyMergeReceipts = {
637
+ prepareReceiptSettlement,
638
+ receiptDirectoryFailureMeansAbsent,
639
+ };
548
640
  //# sourceMappingURL=receipts.js.map
549
- //# debugId=624fc190-4c46-5a55-b757-72e621aec35d
641
+ //# debugId=df7d5bd9-8381-5961-bda1-9e8ed335afc9
@@ -1,6 +1,6 @@
1
1
  # Context Integrity and Recovery Primitives
2
2
 
3
- Tracker: [pm-hu92i3](../.agents/pm/issues/pm-hu92i3.toon), [pm-nc94mk](../.agents/pm/issues/pm-nc94mk.toon), [pm-xrjy8o](../.agents/pm/issues/pm-xrjy8o.toon), [pm-f60039](../.agents/pm/issues/pm-f60039.toon), [pm-2qahia](../.agents/pm/issues/pm-2qahia.toon), [pm-z1z96w](../.agents/pm/issues/pm-z1z96w.toon), [pm-yhle2e](../.agents/pm/issues/pm-yhle2e.toon), [pm-t7wl00](../.agents/pm/issues/pm-t7wl00.toon), [pm-hiqlkh](../.agents/pm/issues/pm-hiqlkh.toon), [pm-ntnv4k](../.agents/pm/issues/pm-ntnv4k.toon), [pm-sxg7wl](../.agents/pm/issues/pm-sxg7wl.toon), [pm-xspd](../.agents/pm/chores/pm-xspd.toon)
3
+ Tracker: [pm-dn8rwl](../.agents/pm/issues/pm-dn8rwl.toon), [pm-r97901](../.agents/pm/issues/pm-r97901.toon), [pm-hu92i3](../.agents/pm/issues/pm-hu92i3.toon), [pm-nc94mk](../.agents/pm/issues/pm-nc94mk.toon), [pm-xrjy8o](../.agents/pm/issues/pm-xrjy8o.toon), [pm-f60039](../.agents/pm/issues/pm-f60039.toon), [pm-2qahia](../.agents/pm/issues/pm-2qahia.toon), [pm-z1z96w](../.agents/pm/issues/pm-z1z96w.toon), [pm-yhle2e](../.agents/pm/issues/pm-yhle2e.toon), [pm-t7wl00](../.agents/pm/issues/pm-t7wl00.toon), [pm-hiqlkh](../.agents/pm/issues/pm-hiqlkh.toon), [pm-ntnv4k](../.agents/pm/issues/pm-ntnv4k.toon), [pm-sxg7wl](../.agents/pm/issues/pm-sxg7wl.toon), [pm-xspd](../.agents/pm/chores/pm-xspd.toon)
4
4
 
5
5
  `project management = context management`: a successful command is useful only when the context it records and later returns is truthful, lossless, and actionable. These SDK-owned primitives are shared by the CLI and package integrations.
6
6
 
@@ -59,7 +59,16 @@ history.
59
59
 
60
60
  Health keeps the metadata-only cache path for clean history streams. A cached hash mismatch, chain mismatch, or writer-version skew is only a candidate: health rereads canonical item sources and verifies stream content hashes before reporting corruption. The `history_drift` details expose `cache_confirmation` candidate, confirmed, and resolved-false-positive item sets, while `cache_hit_verification: metadata_then_content_hash` identifies the authoritative fallback. Drift-cache envelopes carry both their schema version and the current item-hash capability, so changing legacy canonicalization invalidates prior verdicts and a runtime with incompatible hash semantics rebuilds the cache instead of trusting it.
61
61
 
62
- Merge reconciliation may consume a durable hash-only receipt without `--force` only when its canonical item path, complete declared-field set, and every merged-value hash exactly match the current item snapshot. Raw discarded values remain clone-local. Any incomplete or mismatched proof fails closed. SDK gates use `inspectMergeReceiptEvidence` or `runMergeReceiptEvidenceReport` to retain the distinction between no evidence, rejected evidence, and clone-local evidence whose Git directory could not be resolved; `clone_local_evidence_resolved=false` makes the loss-aware report incomplete. The list-only and legacy report compatibility projections intentionally return valid receipts only. Diverged history unions also fail closed when deterministic suffix ordering would make any patch operation inapplicable, instead of publishing a rehashed stream with a skipped branch effect.
62
+ Merge reconciliation may consume a durable hash-only receipt without `--force` only when its canonical item path, complete declared-field set, and every merged-value hash exactly match the current item snapshot. Raw discarded values remain clone-local. Any incomplete or mismatched proof fails closed. SDK gates use `inspectMergeReceiptEvidence` or `runMergeReceiptEvidenceReport` to retain the distinction between no evidence, rejected evidence, and clone-local evidence whose Git directory could not be resolved; `clone_local_evidence_resolved=false` makes the loss-aware report incomplete. Rejected evidence carries bounded privacy-safe source, reason, and receipt-id-or-hash locators, while the exact count and truncation receipt preserve completeness under an agent token budget. Cross-platform ancestor inspection distinguishes an absent receipt store from an unreadable or non-directory root. The list-only and legacy report compatibility projections intentionally return valid receipts only. Diverged history unions also fail closed when deterministic suffix ordering would make any patch operation inapplicable, instead of publishing a rehashed stream with a skipped branch effect.
63
+
64
+ History receipt summaries are durable references, not cleanup hints. Health
65
+ checks each `context.merge.receipts[].receipt_id` against valid pending and
66
+ reconciled authoritative evidence. A missing reference is returned with its
67
+ item id and one-based history line; unsafe identifiers are SHA-256 locators
68
+ rather than echoed content. Detail is capped at 100 while the exact count and a
69
+ truncation receipt preserve loss awareness. Recovery means restoring the named
70
+ receipt from an authoritative clone or backup. Rewriting append-only history or
71
+ deleting sibling evidence is never an automatic remediation.
63
72
 
64
73
  ## Strict-create recovery
65
74
 
@@ -105,8 +105,22 @@ whose candidates were all rejected. Gates and diagnostic integrations should
105
105
  use `inspectMergeReceiptEvidence`, whose `invalid_evidence_count` preserves
106
106
  that distinction without returning malformed contents.
107
107
  `runMergeReceiptEvidenceReport` and `pm merge report --json` expose the same
108
- loss-aware contract through `complete`, `invalid_evidence_count`, and
109
- `clone_local_evidence_resolved`; the CLI
108
+ loss-aware contract through `complete`, `invalid_evidence_count`, bounded
109
+ `invalid_evidence[]`, `invalid_evidence_truncated`, and
110
+ `clone_local_evidence_resolved`. Each rejected candidate reports a stable
111
+ reason plus its `clone_local`, `durable`, or copy-consistency source. A safe
112
+ receipt filename is returned as `receipt_id`; unsafe candidate names are
113
+ represented only by `candidate_name_hash`, and malformed contents are never
114
+ returned. The detail list is capped at 100 rows while the count remains exact,
115
+ so automated gates stay token-bounded. Directory traversal distinguishes a
116
+ truly absent store from a non-directory or unreadable ancestor on Windows and
117
+ POSIX instead of treating platform-specific `ENOENT`/`ENOTDIR` spellings as
118
+ equivalent. Full health also cross-checks the privacy-safe receipt summaries in
119
+ append-only history against valid pending and reconciled evidence. Missing
120
+ evidence emits `merge_receipt_history_reference_missing:<n>` with bounded item,
121
+ history-line, and receipt-id-or-hash coordinates. Its remediation enumerates
122
+ the exact references and directs recovery from an authoritative clone or
123
+ backup; it never recommends deleting receipts or rewriting history. The CLI
110
124
  exits nonzero when evidence is incomplete, even when the valid-receipt count is
111
125
  zero. Current SDK implementations always emit the new field, while its optional
112
126
  type preserves structural compatibility for existing typed adapters and test
@@ -4,12 +4,12 @@ Tracker: `pm-f05lsg`.
4
4
 
5
5
  Every catalog code is listed. An `uncovered` row is an explicit closure obligation, never an omission or implied approval.
6
6
 
7
- - Catalog error codes: 354
7
+ - Catalog error codes: 355
8
8
  - Executable error codes: 19
9
9
  - Executable-code ratchet floor: 18
10
10
  - Required executable canonical codes: `bulk_ids_input_empty`, `bulk_ids_input_missing_path`, `bulk_ids_input_unreadable`, `invalid_argument_value`, `manifest_unknown_key`, `missing_lifecycle_target`, `missing_required_argument`, `no_version_bounds_declared`, `projection_options_mutually_exclusive`, `tracker_not_initialized`, `tracker_root_missing`, `tracker_root_not_directory`, `tracker_root_unreadable`, `unknown_context_intent`, `unknown_field_projection`, `unknown_option`, `unknown_subcommand`
11
- - Uncovered error codes: 335
12
- - Coverage fraction: 0.053672
11
+ - Uncovered error codes: 336
12
+ - Coverage fraction: 0.053521
13
13
  - Closed-domain probes: 19
14
14
  - Grammar probes: 94
15
15
 
@@ -186,6 +186,7 @@ Every catalog code is listed. An `uncovered` row is an explicit closure obligati
186
186
  | `merge_decisions_unreviewed` | `merge_decisions_unreviewed` | uncovered | none | 0 |
187
187
  | `merge_git_config_unwritable` | `merge_git_config_unwritable` | uncovered | none | 0 |
188
188
  | `merge_receipt_evidence_invalid` | `merge_receipt_evidence_invalid` | uncovered | none | 0 |
189
+ | `merge_receipt_history_reference_missing` | `merge_receipt_history_reference_missing` | uncovered | none | 0 |
189
190
  | `merge_receipts_pending` | `merge_receipts_pending` | uncovered | none | 0 |
190
191
  | `merge_reconcile_receipt_evidence_untrusted` | `merge_reconcile_receipt_evidence_untrusted` | uncovered | none | 0 |
191
192
  | `merge_root_not_found` | `merge_root_not_found` | uncovered | none | 0 |
package/marketplace.json CHANGED
@@ -6,14 +6,14 @@
6
6
  },
7
7
  "metadata": {
8
8
  "description": "Official marketplace for pm CLI — native git-based project management for Claude Code and AI coding agents.",
9
- "version": "2026.9.1"
9
+ "version": "2026.9.2"
10
10
  },
11
11
  "plugins": [
12
12
  {
13
13
  "name": "pm-claude",
14
14
  "source": "./plugins/pm-claude",
15
15
  "description": "Native pm CLI integration for Claude Code — 28 MCP tools, 5 workflow skills, 14 slash commands, 4 subagents, hybrid TUI task tracking, session context injection, and coordination subagents for git-based project management without leaving Claude Code.",
16
- "version": "2026.9.1",
16
+ "version": "2026.9.2",
17
17
  "author": {
18
18
  "name": "unbrained",
19
19
  "url": "https://github.com/unbraind/pm-cli"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unbrained/pm-cli",
3
- "version": "2026.9.1",
3
+ "version": "2026.9.2",
4
4
  "description": "Git-native project management CLI for humans and agents.",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@11.10.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unbrained/pm-beads",
3
- "version": "2026.9.1",
3
+ "version": "2026.9.2",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "First-party pm package for importing Beads JSONL records.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unbrained/pm-calendar",
3
- "version": "2026.9.1",
3
+ "version": "2026.9.2",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "First-party pm package for calendar and agenda views.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unbrained/pm-command-kit",
3
- "version": "2026.9.1",
3
+ "version": "2026.9.2",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "First-party pm exemplar package for the commands capability: registerCommand, registerFlags, and registerParser.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unbrained/pm-digital-twin",
3
- "version": "2026.9.1",
3
+ "version": "2026.9.2",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "Beyond-PM exemplar: a temporal production-facility digital twin built entirely on public pm SDK and extension primitives.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unbrained/pm-governance-audit",
3
- "version": "2026.9.1",
3
+ "version": "2026.9.2",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "First-party pm package for governance audit and normalization workflows.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unbrained/pm-guide-shell",
3
- "version": "2026.9.1",
3
+ "version": "2026.9.2",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "First-party pm package for guide and shell completion workflows.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unbrained/pm-kanban",
3
- "version": "2026.9.1",
3
+ "version": "2026.9.2",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "First-party pm archetype package: a complete Kanban continuous-flow profile built on public SDK primitives.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unbrained/pm-lifecycle-hooks",
3
- "version": "2026.9.1",
3
+ "version": "2026.9.2",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "First-party pm package demonstrating safe lifecycle hook registration.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unbrained/pm-linked-test-adapters",
3
- "version": "2026.9.1",
3
+ "version": "2026.9.2",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "First-party pm package for optional linked-test run management adapters and reporters.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unbrained/pm-search-advanced",
3
- "version": "2026.9.1",
3
+ "version": "2026.9.2",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "First-party pm package for semantic, hybrid, and reindex search workflows.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unbrained/pm-templates",
3
- "version": "2026.9.1",
3
+ "version": "2026.9.2",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "First-party pm package for reusable create templates.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unbrained/pm-todos",
3
- "version": "2026.9.1",
3
+ "version": "2026.9.2",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "First-party pm package for Todo markdown import and export.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unbrained/pm-vcs-sdk-exemplar",
3
- "version": "2026.9.1",
3
+ "version": "2026.9.2",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "Beyond-PM exemplar: a VCS-style changeset workflow built entirely on public pm SDK and extension primitives.",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pm-claude",
3
3
  "description": "Native pm CLI integration for Claude Code — 28 MCP tools, 5 workflow skills, 14 slash commands, 4 subagents (coordinator, delivery-chain, triage, verification), hybrid TUI task tracking (pm as persistent store + Claude Code task panel as live view), session context injection, and full git-based project management without leaving Claude Code.",
4
- "version": "2026.9.1",
4
+ "version": "2026.9.2",
5
5
  "author": {
6
6
  "name": "unbrained",
7
7
  "url": "https://github.com/unbraind/pm-cli"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pm-codex",
3
- "version": "2026.9.1",
3
+ "version": "2026.9.2",
4
4
  "description": "Native Codex integration for pm-cli with bundled MCP tools, skills, commands, and agent workflows.",
5
5
  "author": {
6
6
  "name": "unbrained",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "aggregate_completeness": {
3
- "covered_symbols": 1824,
3
+ "covered_symbols": 1825,
4
4
  "excluded": [
5
5
  {
6
6
  "name": "_testOnlyCliContracts",
@@ -8,7 +8,7 @@
8
8
  }
9
9
  ],
10
10
  "missing": [],
11
- "required_symbols": 1825
11
+ "required_symbols": 1826
12
12
  },
13
13
  "breaking_acknowledgements": [
14
14
  {
@@ -1463,6 +1463,16 @@
1463
1463
  ],
1464
1464
  "package_version": "2026.8.31",
1465
1465
  "reason": "Add optional host-owned cursor integrity key for deployment-stable pagination; existing callers and process-local fallback remain compatible."
1466
+ },
1467
+ {
1468
+ "changes": [
1469
+ "signature ./sdk:MergeReceiptEvidenceReport",
1470
+ "signature ./sdk:MergeReceiptEvidenceScan",
1471
+ "signature ./sdk/merge:MergeReceiptEvidenceReport",
1472
+ "signature ./sdk/merge:MergeReceiptEvidenceScan"
1473
+ ],
1474
+ "package_version": "2026.9.1",
1475
+ "reason": "Expose loss-aware receipt evidence details as required runtime fields so SDK gates cannot silently discard invalid candidates."
1466
1476
  }
1467
1477
  ],
1468
1478
  "entrypoints": {
@@ -9291,13 +9301,19 @@
9291
9301
  "classification": "supported",
9292
9302
  "kind": "interface",
9293
9303
  "name": "MergeReceiptEvidenceReport",
9294
- "signature": "export interface MergeReceiptEvidenceReport { ok: boolean; complete: boolean; count: number; invalid_evidence_count: number; clone_local_evidence_resolved?: boolean; receipts: MergeDecisionReceipt[]; generated_at: string; }"
9304
+ "signature": "export interface MergeReceiptEvidenceReport { ok: boolean; complete: boolean; count: number; invalid_evidence_count: number; invalid_evidence: MergeReceiptInvalidEvidence[]; invalid_evidence_truncated: boolean; clone_local_evidence_resolved?: boolean; receipts: MergeDecisionReceipt[]; generated_at: string; }"
9295
9305
  },
9296
9306
  {
9297
9307
  "classification": "supported",
9298
9308
  "kind": "interface",
9299
9309
  "name": "MergeReceiptEvidenceScan",
9300
- "signature": "export interface MergeReceiptEvidenceScan { receipts: MergeDecisionReceipt[]; invalid_evidence_count: number; clone_local_evidence_resolved?: boolean; }"
9310
+ "signature": "export interface MergeReceiptEvidenceScan { receipts: MergeDecisionReceipt[]; invalid_evidence_count: number; invalid_evidence: MergeReceiptInvalidEvidence[]; invalid_evidence_truncated: boolean; clone_local_evidence_resolved?: boolean; }"
9311
+ },
9312
+ {
9313
+ "classification": "supported",
9314
+ "kind": "interface",
9315
+ "name": "MergeReceiptInvalidEvidence",
9316
+ "signature": "export interface MergeReceiptInvalidEvidence { evidence_source: \"clone_local\" | \"durable\" | \"clone_local_and_durable\"; reason: \"directory_unreadable\" | \"candidate_not_bounded_regular_file\" | \"candidate_unreadable\" | \"candidate_invalid_json\" | \"schema_or_identity_invalid\" | \"copy_provenance_mismatch\"; receipt_id?: string; candidate_name_hash?: string; }"
9301
9317
  },
9302
9318
  {
9303
9319
  "classification": "advanced_export",
@@ -26733,13 +26749,19 @@
26733
26749
  "classification": "supported",
26734
26750
  "kind": "interface",
26735
26751
  "name": "MergeReceiptEvidenceReport",
26736
- "signature": "export interface MergeReceiptEvidenceReport { ok: boolean; complete: boolean; count: number; invalid_evidence_count: number; clone_local_evidence_resolved?: boolean; receipts: MergeDecisionReceipt[]; generated_at: string; }"
26752
+ "signature": "export interface MergeReceiptEvidenceReport { ok: boolean; complete: boolean; count: number; invalid_evidence_count: number; invalid_evidence: MergeReceiptInvalidEvidence[]; invalid_evidence_truncated: boolean; clone_local_evidence_resolved?: boolean; receipts: MergeDecisionReceipt[]; generated_at: string; }"
26737
26753
  },
26738
26754
  {
26739
26755
  "classification": "supported",
26740
26756
  "kind": "interface",
26741
26757
  "name": "MergeReceiptEvidenceScan",
26742
- "signature": "export interface MergeReceiptEvidenceScan { receipts: MergeDecisionReceipt[]; invalid_evidence_count: number; clone_local_evidence_resolved?: boolean; }"
26758
+ "signature": "export interface MergeReceiptEvidenceScan { receipts: MergeDecisionReceipt[]; invalid_evidence_count: number; invalid_evidence: MergeReceiptInvalidEvidence[]; invalid_evidence_truncated: boolean; clone_local_evidence_resolved?: boolean; }"
26759
+ },
26760
+ {
26761
+ "classification": "supported",
26762
+ "kind": "interface",
26763
+ "name": "MergeReceiptInvalidEvidence",
26764
+ "signature": "export interface MergeReceiptInvalidEvidence { evidence_source: \"clone_local\" | \"durable\" | \"clone_local_and_durable\"; reason: \"directory_unreadable\" | \"candidate_not_bounded_regular_file\" | \"candidate_unreadable\" | \"candidate_invalid_json\" | \"schema_or_identity_invalid\" | \"copy_provenance_mismatch\"; receipt_id?: string; candidate_name_hash?: string; }"
26743
26765
  },
26744
26766
  {
26745
26767
  "classification": "supported",
@@ -32095,6 +32117,7 @@
32095
32117
  "merge_decisions_unreviewed",
32096
32118
  "merge_git_config_unwritable",
32097
32119
  "merge_receipt_evidence_invalid",
32120
+ "merge_receipt_history_reference_missing",
32098
32121
  "merge_receipts_pending",
32099
32122
  "merge_reconcile_receipt_evidence_untrusted",
32100
32123
  "merge_root_not_found",