@unbrained/pm-cli 2026.8.21 → 2026.8.23

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 (228) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +44 -0
  3. package/dist/cli/error-guidance.d.ts +22 -4
  4. package/dist/cli/error-guidance.js +73 -16
  5. package/dist/cli/main.d.ts +3 -0
  6. package/dist/cli/main.js +51 -9
  7. package/dist/cli/register-mutation.js +6 -6
  8. package/dist/cli/register-operations.d.ts +73 -0
  9. package/dist/cli/register-operations.js +39 -14
  10. package/dist/cli/register-setup.js +3 -3
  11. package/dist/cli/schema-registration-helpers.d.ts +8 -1
  12. package/dist/cli/schema-registration-helpers.js +14 -5
  13. package/dist/cli-bundle/bundle-manifest.json +149 -149
  14. package/dist/cli-bundle/chunks/chunk-3ENVHX6C.js +36 -0
  15. package/dist/cli-bundle/chunks/{chunk-LOVTQ52Z.js → chunk-DW2CGEMU.js} +2 -2
  16. package/dist/cli-bundle/chunks/chunk-EG6CLDZT.js +5 -0
  17. package/dist/cli-bundle/chunks/{chunk-LARJJ3RK.js → chunk-F3AI2JFL.js} +2 -2
  18. package/dist/cli-bundle/chunks/{chunk-N3DSV4CH.js → chunk-HWZ6CELB.js} +8 -8
  19. package/dist/cli-bundle/chunks/chunk-HZBMLSZI.js +201 -0
  20. package/dist/cli-bundle/chunks/chunk-JB4KZWBF.js +3 -0
  21. package/dist/cli-bundle/chunks/chunk-KPH73XXJ.js +2 -0
  22. package/dist/cli-bundle/chunks/{chunk-BKYB45NQ.js → chunk-KXPMB2YL.js} +50 -50
  23. package/dist/cli-bundle/chunks/{chunk-BUNIEJNE.js → chunk-QDVHNHAQ.js} +2 -2
  24. package/dist/cli-bundle/chunks/chunk-TFI5V5N2.js +13 -0
  25. package/dist/cli-bundle/chunks/{register-list-query-F57EWDEF.js → register-list-query-UJB5FMV2.js} +2 -2
  26. package/dist/cli-bundle/chunks/register-mutation-NX5XE4VX.js +20 -0
  27. package/dist/cli-bundle/chunks/register-operations-RXIJIYXC.js +2 -0
  28. package/dist/cli-bundle/chunks/{register-setup-HXU7IZZD.js → register-setup-GFUZEKRE.js} +2 -2
  29. package/dist/cli-bundle/focused-chunks/{chunk-F4YEW5I2.js → chunk-37Z4OEAP.js} +2 -2
  30. package/dist/cli-bundle/focused-chunks/chunk-3UPTYG44.js +8 -0
  31. package/dist/cli-bundle/focused-chunks/chunk-5S7E72FY.js +16 -0
  32. package/dist/cli-bundle/focused-chunks/{chunk-4ERLO7QD.js → chunk-AUFH5H35.js} +2 -2
  33. package/dist/cli-bundle/focused-chunks/chunk-E7X25GC3.js +2 -0
  34. package/dist/cli-bundle/focused-chunks/chunk-FQCSTBWE.js +4 -0
  35. package/dist/cli-bundle/focused-chunks/{chunk-FH6DYED3.js → chunk-FZS4H32O.js} +2 -2
  36. package/dist/cli-bundle/focused-chunks/chunk-IFLWKY2R.js +156 -0
  37. package/dist/cli-bundle/focused-chunks/{chunk-YKBKIEAJ.js → chunk-JOWDB2MH.js} +2 -2
  38. package/dist/cli-bundle/focused-chunks/chunk-LYVSW5VM.js +2 -0
  39. package/dist/cli-bundle/focused-chunks/chunk-MRCNF3U6.js +2 -0
  40. package/dist/cli-bundle/focused-chunks/{chunk-UZE7XUZK.js → chunk-OFVUGTLJ.js} +2 -2
  41. package/dist/cli-bundle/focused-chunks/{chunk-FJ22HNRP.js → chunk-OGLQBL46.js} +2 -2
  42. package/dist/cli-bundle/focused-chunks/chunk-OY7QAV4Y.js +51 -0
  43. package/dist/cli-bundle/focused-chunks/chunk-QGFNI65N.js +26 -0
  44. package/dist/cli-bundle/focused-chunks/{chunk-KDNG7TKQ.js → chunk-R27IDGHD.js} +2 -2
  45. package/dist/cli-bundle/focused-chunks/chunk-UYRFYCDQ.js +5 -0
  46. package/dist/cli-bundle/focused-chunks/{chunk-2NIY5NKG.js → chunk-ZHVA42R5.js} +9 -9
  47. package/dist/cli-bundle/main.js +13 -13
  48. package/dist/cli-bundle/sdk-authoring.js +1 -1
  49. package/dist/cli-bundle/sdk-contracts.js +2 -2
  50. package/dist/cli-bundle/sdk-core.js +40 -40
  51. package/dist/cli-bundle/sdk-governance.js +1 -1
  52. package/dist/cli-bundle/sdk-graph.js +1 -1
  53. package/dist/cli-bundle/sdk-merge.js +33 -32
  54. package/dist/cli-bundle/sdk-query.js +1 -1
  55. package/dist/cli-bundle/sdk-runtime.js +1 -1
  56. package/dist/cli-bundle/sdk-testing.js +1 -1
  57. package/dist/cli-bundle/sdk.js +5 -5
  58. package/dist/core/config/nested-settings.js +8 -2
  59. package/dist/core/config/positional-value.d.ts +1 -1
  60. package/dist/core/config/positional-value.js +5 -2
  61. package/dist/core/extensions/extension-hook-runtime.js +22 -20
  62. package/dist/core/extensions/extension-types.d.ts +8 -1
  63. package/dist/core/extensions/extension-types.js +2 -2
  64. package/dist/core/extensions/loader.js +7 -3
  65. package/dist/core/extensions/service-ownership.d.ts +8 -0
  66. package/dist/core/extensions/service-ownership.js +20 -0
  67. package/dist/core/history/history.js +3 -3
  68. package/dist/core/item/id.js +20 -5
  69. package/dist/core/item/item-format.js +81 -2
  70. package/dist/core/reproducibility/context.d.ts +4 -0
  71. package/dist/core/reproducibility/context.js +14 -8
  72. package/dist/core/shared/author.d.ts +55 -1
  73. package/dist/core/shared/author.js +333 -16
  74. package/dist/core/shared/constants.js +3 -2
  75. package/dist/core/shared/errors.d.ts +4 -0
  76. package/dist/core/shared/errors.js +2 -2
  77. package/dist/core/store/settings-validator.d.ts +1 -0
  78. package/dist/core/store/settings-validator.js +6 -3
  79. package/dist/core/store/settings.js +5 -2
  80. package/dist/mcp/server.d.ts +1 -1
  81. package/dist/mcp/server.js +30 -9
  82. package/dist/sdk/agent/identity-config.d.ts +63 -0
  83. package/dist/sdk/agent/identity-config.js +107 -0
  84. package/dist/sdk/agent/refusal-corpus-contracts.d.ts +76 -0
  85. package/dist/sdk/agent/refusal-corpus-contracts.js +194 -0
  86. package/dist/sdk/agent/refusal-reachability.d.ts +1 -1
  87. package/dist/sdk/authoring.d.ts +1 -1
  88. package/dist/sdk/authoring.js +2 -2
  89. package/dist/sdk/cli-contracts/agent-output-contracts.d.ts +77 -0
  90. package/dist/sdk/cli-contracts/agent-output-contracts.js +326 -2
  91. package/dist/sdk/cli-contracts/commander-mutation-options.js +3 -3
  92. package/dist/sdk/cli-contracts/enum-contracts.d.ts +4 -0
  93. package/dist/sdk/cli-contracts/enum-contracts.js +9 -2
  94. package/dist/sdk/cli-contracts/flag-contracts.js +9 -2
  95. package/dist/sdk/cli-contracts/flag-lexicon-contracts.js +4 -4
  96. package/dist/sdk/cli-contracts/registration-helpers.js +5 -2
  97. package/dist/sdk/cli-contracts/runtime-contracts.d.ts +3 -1
  98. package/dist/sdk/cli-contracts/runtime-contracts.js +4 -3
  99. package/dist/sdk/cli-contracts/tool-parameter-tables.js +22 -2
  100. package/dist/sdk/cli-contracts/tool-schema.d.ts +1 -1
  101. package/dist/sdk/cli-contracts/tool-schema.js +14 -3
  102. package/dist/sdk/cli-contracts.d.ts +4 -4
  103. package/dist/sdk/cli-contracts.js +5 -5
  104. package/dist/sdk/config.d.ts +6 -1
  105. package/dist/sdk/config.js +119 -14
  106. package/dist/sdk/context-intent-contracts.js +4 -2
  107. package/dist/sdk/core-governance.d.ts +1 -1
  108. package/dist/sdk/core-governance.js +3 -3
  109. package/dist/sdk/extension/install-sources.d.ts +1 -1
  110. package/dist/sdk/extension/install-sources.js +6 -3
  111. package/dist/sdk/extension/output-ownership.d.ts +1 -0
  112. package/dist/sdk/extension/output-ownership.js +3 -2
  113. package/dist/sdk/extension/scaffold.js +11 -5
  114. package/dist/sdk/flag-invocation-contracts.js +4 -2
  115. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +74 -2
  116. package/dist/sdk/generated/generated-error-code-catalog-part-2.js +87 -26
  117. package/dist/sdk/governance/assurance-action.js +30 -10
  118. package/dist/sdk/governance/health.d.ts +3 -0
  119. package/dist/sdk/governance/health.js +39 -7
  120. package/dist/sdk/governance/hierarchy-validation.d.ts +29 -0
  121. package/dist/sdk/governance/hierarchy-validation.js +109 -0
  122. package/dist/sdk/governance/validate.d.ts +3 -9
  123. package/dist/sdk/governance/validate.js +84 -111
  124. package/dist/sdk/graph/assembly.d.ts +6 -1
  125. package/dist/sdk/graph/assembly.js +16 -22
  126. package/dist/sdk/graph/governance-contracts.d.ts +1 -1
  127. package/dist/sdk/graph/governance-contracts.js +8 -2
  128. package/dist/sdk/graph/governance.js +46 -2
  129. package/dist/sdk/graph/hierarchy-integrity.d.ts +94 -0
  130. package/dist/sdk/graph/hierarchy-integrity.js +311 -0
  131. package/dist/sdk/graph/index.d.ts +2 -1
  132. package/dist/sdk/graph/index.js +3 -2
  133. package/dist/sdk/graph/remediation.js +32 -2
  134. package/dist/sdk/graph/run.d.ts +31 -2
  135. package/dist/sdk/graph/run.js +80 -31
  136. package/dist/sdk/graph/workspace-relationship-kind-registry.d.ts +8 -0
  137. package/dist/sdk/graph/workspace-relationship-kind-registry.js +28 -0
  138. package/dist/sdk/index.d.ts +8 -3
  139. package/dist/sdk/index.js +9 -4
  140. package/dist/sdk/item-children.js +21 -8
  141. package/dist/sdk/lifecycle/create.js +129 -97
  142. package/dist/sdk/lifecycle/hierarchy-mutation-lock.d.ts +15 -0
  143. package/dist/sdk/lifecycle/hierarchy-mutation-lock.js +25 -0
  144. package/dist/sdk/lifecycle/update.d.ts +2 -0
  145. package/dist/sdk/lifecycle/update.js +174 -84
  146. package/dist/sdk/merge/three-way.js +105 -26
  147. package/dist/sdk/query/complete-list.d.ts +3 -3
  148. package/dist/sdk/query/complete-list.js +46 -3
  149. package/dist/sdk/query/list.d.ts +4 -3
  150. package/dist/sdk/query/list.js +89 -49
  151. package/dist/sdk/read-output-budget.d.ts +21 -0
  152. package/dist/sdk/read-output-budget.js +39 -2
  153. package/dist/sdk/read-output-contracts.d.ts +10 -4
  154. package/dist/sdk/read-output-contracts.js +105 -32
  155. package/dist/sdk/relationship-context.js +3 -4
  156. package/dist/sdk/relationships.js +17 -10
  157. package/dist/sdk/reproducibility/process.d.ts +19 -0
  158. package/dist/sdk/reproducibility/process.js +88 -0
  159. package/dist/sdk/runtime-primitives.d.ts +1 -1
  160. package/dist/sdk/runtime-primitives.js +3 -3
  161. package/dist/sdk/runtime.d.ts +3 -2
  162. package/dist/sdk/runtime.js +7 -5
  163. package/dist/sdk/test/batch.d.ts +6 -0
  164. package/dist/sdk/test/batch.js +26 -6
  165. package/dist/sdk/test/entry.d.ts +1 -1
  166. package/dist/sdk/test/entry.js +3 -2
  167. package/dist/sdk/test/execution.d.ts +28 -3
  168. package/dist/sdk/test/execution.js +201 -34
  169. package/dist/sdk/test/parsers.d.ts +6 -0
  170. package/dist/sdk/test/parsers.js +21 -2
  171. package/dist/sdk/test/trust.d.ts +33 -0
  172. package/dist/sdk/test/trust.js +207 -0
  173. package/dist/types.d.ts +34 -0
  174. package/dist/types.js +2 -2
  175. package/docs/AGENT_PROVENANCE_ADR.md +41 -5
  176. package/docs/AGENT_UX_CONTRACTS.md +20 -4
  177. package/docs/COMMANDS.md +29 -14
  178. package/docs/CONFIGURATION.md +4 -0
  179. package/docs/DEPENDENCY_KIND_CONTRACT.md +31 -3
  180. package/docs/DIAGNOSTIC_OUTPUT_CONTRACTS.md +115 -0
  181. package/docs/EXTENSIONS.md +2 -1
  182. package/docs/MERGE_SAFETY.md +15 -1
  183. package/docs/README.md +1 -0
  184. package/docs/READ_OUTPUT_CONTRACTS.md +19 -2
  185. package/docs/RELATIONSHIP_GRAPH.md +6 -3
  186. package/docs/REPRODUCIBLE_WORKSPACES.md +42 -8
  187. package/docs/SDK.md +9 -0
  188. package/docs/SDK_AGENT_SESSION_CONTEXT.md +6 -4
  189. package/docs/SDK_EVIDENCE_TRACEABILITY.md +1 -1
  190. package/docs/TESTING.md +83 -1
  191. package/docs/agent-task-token-baseline.json +2 -2
  192. package/docs/generated/FLAG_LEXICON_BUDGETS.md +2 -2
  193. package/marketplace.json +2 -2
  194. package/package.json +2 -2
  195. package/packages/pm-beads/package.json +1 -1
  196. package/packages/pm-calendar/package.json +1 -1
  197. package/packages/pm-command-kit/package.json +1 -1
  198. package/packages/pm-digital-twin/package.json +1 -1
  199. package/packages/pm-governance-audit/package.json +1 -1
  200. package/packages/pm-guide-shell/package.json +1 -1
  201. package/packages/pm-kanban/package.json +1 -1
  202. package/packages/pm-lifecycle-hooks/package.json +1 -1
  203. package/packages/pm-linked-test-adapters/package.json +1 -1
  204. package/packages/pm-search-advanced/package.json +1 -1
  205. package/packages/pm-templates/package.json +1 -1
  206. package/packages/pm-todos/package.json +1 -1
  207. package/packages/pm-vcs/package.json +1 -1
  208. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  209. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  210. package/sdk/public-surface.json +1021 -53
  211. package/dist/cli-bundle/chunks/chunk-2MA6ZQVZ.js +0 -197
  212. package/dist/cli-bundle/chunks/chunk-3AFAUHUE.js +0 -2
  213. package/dist/cli-bundle/chunks/chunk-7VHZS727.js +0 -5
  214. package/dist/cli-bundle/chunks/chunk-JSLOFXGA.js +0 -3
  215. package/dist/cli-bundle/chunks/chunk-OCLPLFAY.js +0 -35
  216. package/dist/cli-bundle/chunks/chunk-S4IRYKBU.js +0 -13
  217. package/dist/cli-bundle/chunks/register-mutation-7IY2D5KO.js +0 -20
  218. package/dist/cli-bundle/chunks/register-operations-FALL3YTU.js +0 -2
  219. package/dist/cli-bundle/focused-chunks/chunk-7WZRLCYF.js +0 -50
  220. package/dist/cli-bundle/focused-chunks/chunk-CKKQSJNN.js +0 -2
  221. package/dist/cli-bundle/focused-chunks/chunk-EEFKYMDR.js +0 -16
  222. package/dist/cli-bundle/focused-chunks/chunk-F2A56DHV.js +0 -8
  223. package/dist/cli-bundle/focused-chunks/chunk-FV5ACOK7.js +0 -2
  224. package/dist/cli-bundle/focused-chunks/chunk-JEAVFOWM.js +0 -2
  225. package/dist/cli-bundle/focused-chunks/chunk-NJ63LOP6.js +0 -155
  226. package/dist/cli-bundle/focused-chunks/chunk-NXRTQ7HI.js +0 -2
  227. package/dist/cli-bundle/focused-chunks/chunk-TJ7EMJ46.js +0 -4
  228. package/dist/cli-bundle/focused-chunks/chunk-ZOSEELYJ.js +0 -26
@@ -4,7 +4,7 @@
4
4
  * Implements the pm update 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]="d024d9cf-81f8-5690-b8f4-03620e3e9bf8")}catch(e){}}();
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]="c88f01be-6abe-5fa2-9792-3f54e61bb282")}catch(e){}}();
8
8
  import { assertInitializedTracker } from "../environment/tracker-preflight.js";
9
9
  import { COMMON_MUTATION_COMMAND_OPTION_KEYS, canonicalizeCommandOptionKey, commandOptionFlagLabel, resolveItemTypeRegistry, resolveCommandOptionPolicyState, resolveTypeDefinition, resolveTypeName, validateTypeOptions, normalizeItemId, toItemRecord, buildInvalidTypeError, assertParentReferenceIsNotSelf, normalizeParentReferenceValue, validateMissingParentReference, validateSprintOrReleaseValue, applyAcceptanceCriteriaMutations, applyTagRemovals, assertNoUnknownCsvKeys, createStdinTokenResolver, looksLikeGenericKeyValueEntry, mergeAdditiveTags, parseCsvKv, parseOptionalNonNegativeInteger, parseOptionalNumber, parseTags, splitAcceptanceCriteria, resolvePriority, normalizeStatusInput, isTerminalStatus, collectRuntimeUpdateFieldValues, resolveItemTypesFilePath, resolveRuntimeFieldRegistry, resolveRuntimeStatusRegistry, describeAllowedTransitions, evaluateTransition, resolveTypeWorkflows, EXIT_CODE, PmCliError, stableValueEquals, resolveIsoOrRelative, getActiveExtensionRegistrations, collectRegisteredItemFieldNames, applyRegisteredItemFieldDefaultsAndValidation, parseRegisteredItemFieldAssignments, buildItemNotFoundError, listAllItemMetadataLight, locateItem, readLocatedItem, resolvePmRoot, readSettings, resolveAuthor, } from "../runtime-primitives.js";
10
10
  import { mutateItemWithHistoryContextResolver } from "../../core/store/item-store.js";
@@ -17,8 +17,12 @@ import { assertDependencyEdgesAllowed, assertDependencyTargetsResolvable, assert
17
17
  import { normalizeDependencySeedId, normalizeDependencySourceKind, } from "../dependency-provenance.js";
18
18
  import { resolveCanonicalRelationshipKind } from "../relationships.js";
19
19
  import { collectNewOrderingCycleWarnings } from "../graph/mutation-advisory.js";
20
+ import { assertHierarchyMutationAllowed } from "../graph/hierarchy-integrity.js";
21
+ import { resolveWorkspaceRelationshipKindRegistry } from "../graph/assembly.js";
22
+ import { acquireHierarchyMutationLock } from "./hierarchy-mutation-lock.js";
20
23
  import { DEPENDENCY_KIND_VALUES, ISSUE_SEVERITY_VALUES, RISK_VALUES, } from "../../types/index.js";
21
24
  import { parseDocs, parseFiles, parseLogSeed, parseTests, requireStringOption, } from "./create.js";
25
+ import { attachLinkedTestMutationProvenance } from "../test/trust.js";
22
26
  import { COMMON_UNSET_FIELD_DEFINITIONS_AFTER_CLOSE_REASON_BEFORE_AUTHOR, COMMON_UNSET_FIELD_DEFINITIONS_AFTER_AUTHOR, COMMON_UNSET_FIELD_DEFINITIONS_BEFORE_CLOSE_REASON, parseCommandUnsetTargets, resolveRuntimeUnsetFieldDefinition, } from "./shared-unset-fields.js";
23
27
  const UPDATE_UNSET_FIELD_DEFINITIONS = [
24
28
  ...COMMON_UNSET_FIELD_DEFINITIONS_BEFORE_CLOSE_REASON,
@@ -355,6 +359,9 @@ function parseDependencyCreatedAt(value, currentIso) {
355
359
  if (!value || value.trim() === "" || value.trim().toLowerCase() === "now") {
356
360
  return currentIso;
357
361
  }
362
+ return normalizeDependencyTimestamp(value);
363
+ }
364
+ function normalizeDependencyTimestamp(value) {
358
365
  const parsed = Date.parse(value);
359
366
  if (!Number.isFinite(parsed)) {
360
367
  throw new PmCliError(`Invalid dependency created_at timestamp "${value}"`, EXIT_CODE.USAGE);
@@ -377,7 +384,14 @@ const DEP_ADDITION_KEYS = [
377
384
  "source_kind",
378
385
  ];
379
386
  /** Allowed CSV/markdown keys for the `--dep-remove` selector (GH-258). */
380
- const DEP_REMOVE_KEYS = ["id", "kind", "type", "source_kind"];
387
+ const DEP_REMOVE_KEYS = [
388
+ "id",
389
+ "kind",
390
+ "type",
391
+ "author",
392
+ "created_at",
393
+ "source_kind",
394
+ ];
381
395
  function looksLikeStructuredDependencyEntry(raw) {
382
396
  if (raw.startsWith("```") || raw.includes("\n")) {
383
397
  return true;
@@ -455,12 +469,18 @@ function parseDependencyRemovals(raw, prefix) {
455
469
  }
456
470
  const kindRaw = normalizeDependencyKindInput(parseOptionalDependencyString(kv.kind ?? kv.type));
457
471
  const sourceKind = normalizeDependencySourceKind(parseOptionalDependencyString(kv.source_kind));
472
+ const author = parseOptionalDependencyString(kv.author);
473
+ const createdAt = parseOptionalDependencyString(kv.created_at);
458
474
  return {
459
475
  id: normalizeDependencySeedId(idRaw, prefix, sourceKind),
460
476
  kind: kindRaw
461
477
  ? ensureEnum(kindRaw, DEPENDENCY_KIND_VALUES, "dependency kind")
462
478
  : undefined,
463
479
  source_kind: sourceKind,
480
+ author,
481
+ created_at: createdAt
482
+ ? normalizeDependencyTimestamp(createdAt)
483
+ : undefined,
464
484
  };
465
485
  }
466
486
  if (trimmed.toLowerCase() === "undefined") {
@@ -473,7 +493,24 @@ function parseDependencyRemovals(raw, prefix) {
473
493
  }
474
494
  function dependencyKey(value) {
475
495
  const sourceKind = value.source_kind?.trim().toLowerCase() ?? "";
476
- return `${value.id.trim().toLowerCase()}::${value.kind}::${sourceKind}`;
496
+ return [value.id.trim().toLowerCase(), value.kind, sourceKind].join("\u0000");
497
+ }
498
+ /** Return the complete normalized identity of one stored dependency row. */
499
+ function storedDependencyIdentityKey(value) {
500
+ const sourceKind = value.source_kind?.trim().toLowerCase() ?? "";
501
+ const author = value.author?.trim().toLowerCase() ?? "";
502
+ const rawCreatedAt = value.created_at?.trim() ?? "";
503
+ const parsedCreatedAt = Date.parse(rawCreatedAt);
504
+ const createdAt = Number.isFinite(parsedCreatedAt)
505
+ ? new Date(parsedCreatedAt).toISOString()
506
+ : rawCreatedAt;
507
+ return [
508
+ value.id.trim().toLowerCase(),
509
+ value.kind,
510
+ sourceKind,
511
+ author,
512
+ createdAt,
513
+ ].join("\u0000");
477
514
  }
478
515
  // pm-kyd6: `--blocked-by` writes the `blocked_by` scalar, but the dependency
479
516
  // graph (`pm deps`) is built only from the `dependencies` array. Mirror the
@@ -554,7 +591,7 @@ function docKey(value) {
554
591
  return `${value.path}::${value.scope}`;
555
592
  }
556
593
  function testKey(value) {
557
- return `${value.command}::${value.path ?? ""}::${value.scope}::${value.pm_context_mode ?? ""}`;
594
+ return `${value.command}::${value.path ?? ""}::${value.scope}::${value.pm_context_mode ?? ""}::${value.workspace_context_mode ?? ""}`;
558
595
  }
559
596
  function matchesDependencySelector(value, selector) {
560
597
  if (value.id.trim().toLowerCase() !== selector.id.trim().toLowerCase()) {
@@ -568,6 +605,17 @@ function matchesDependencySelector(value, selector) {
568
605
  selector.source_kind.trim().toLowerCase()) {
569
606
  return false;
570
607
  }
608
+ if (selector.author !== undefined &&
609
+ value.author?.trim().toLowerCase() !== selector.author.trim().toLowerCase()) {
610
+ return false;
611
+ }
612
+ if (selector.created_at !== undefined &&
613
+ (value.created_at === undefined ||
614
+ !Number.isFinite(Date.parse(value.created_at)) ||
615
+ new Date(Date.parse(value.created_at)).toISOString() !==
616
+ selector.created_at)) {
617
+ return false;
618
+ }
571
619
  return true;
572
620
  }
573
621
  function ensurePriority(raw) {
@@ -1082,6 +1130,19 @@ async function buildNoopUpdateResult(params) {
1082
1130
  warnings: ["noop_no_update_fields"],
1083
1131
  };
1084
1132
  }
1133
+ async function loadUpdateRelationshipGraph(params) {
1134
+ if (!params.fieldFlags.dep &&
1135
+ !params.fieldFlags.blockedBy &&
1136
+ !params.fieldFlags.parent &&
1137
+ !params.clearedMetadataKeys.has("parent"))
1138
+ return undefined;
1139
+ return listAllItemMetadataLight(params.pmRoot, params.settings.item_format, params.typeToFolder, undefined, params.settings.schema);
1140
+ }
1141
+ function assertUpdateHierarchyIntegrity(params) {
1142
+ if (!params.before)
1143
+ return;
1144
+ assertHierarchyMutationAllowed(params.before, params.before.map((item) => item.id === params.document.metadata.id ? params.document.metadata : item), params.document.metadata.id, (status) => isTerminalStatus(status, params.statusRegistry), resolveWorkspaceRelationshipKindRegistry());
1145
+ }
1085
1146
  async function assertUpdateTrackerInitialized(pmRoot) {
1086
1147
  await assertInitializedTracker(pmRoot);
1087
1148
  }
@@ -1315,16 +1376,16 @@ function applyTypeOptionMutation(document, context, activeTypeName, changedField
1315
1376
  /** Collapse duplicate rows only for identities explicitly touched by this mutation. */
1316
1377
  function normalizeTouchedDependencyIdentities(current, additions) {
1317
1378
  const touchedKeys = new Set(additions.map((entry) => dependencyKey(entry)));
1318
- const retainedTouchedKeys = new Set();
1379
+ const retainedStoredIdentities = new Set();
1319
1380
  return current.filter((entry) => {
1320
- const key = dependencyKey(entry);
1321
- if (!touchedKeys.has(key)) {
1381
+ if (!touchedKeys.has(dependencyKey(entry))) {
1322
1382
  return true;
1323
1383
  }
1324
- if (retainedTouchedKeys.has(key)) {
1384
+ const storedIdentity = storedDependencyIdentityKey(entry);
1385
+ if (retainedStoredIdentities.has(storedIdentity)) {
1325
1386
  return false;
1326
1387
  }
1327
- retainedTouchedKeys.add(key);
1388
+ retainedStoredIdentities.add(storedIdentity);
1328
1389
  return true;
1329
1390
  });
1330
1391
  }
@@ -1342,11 +1403,13 @@ function assertDependencyRemovalSelectorsMatch(current, selectors, itemId) {
1342
1403
  id: entry.id,
1343
1404
  kind: entry.kind,
1344
1405
  source_kind: entry.source_kind,
1406
+ author: entry.author,
1407
+ created_at: entry.created_at,
1345
1408
  })),
1346
1409
  why: "Failing on a zero-match selector prevents a mistyped dependency removal from being reported as successful.",
1347
1410
  nextSteps: [
1348
1411
  `Inspect the current item with: pm get ${itemId} --full`,
1349
- "Retry --dep-remove with the exact id, kind, and optional source_kind shown in dependencies.",
1412
+ "Retry --dep-remove with the exact id plus any kind, source_kind, author, or created_at selectors needed to identify one stored row.",
1350
1413
  ],
1351
1414
  });
1352
1415
  }
@@ -1809,6 +1872,7 @@ async function runUpdateWithContext(id, options, global, execution = {}) {
1809
1872
  const learningUpdates = parseLogSeed("--learning", options.learning, nowIso, author);
1810
1873
  const fileUpdates = parseFiles(options.file);
1811
1874
  const testUpdates = parseTests(options.test);
1875
+ testUpdates.values = await attachLinkedTestMutationProvenance(testUpdates.values, author, nowIso);
1812
1876
  const docUpdates = parseDocs(options.doc);
1813
1877
  const workflowTransitionWarnings = [];
1814
1878
  const parentReference = await resolveParentReferenceForUpdate({
@@ -1857,82 +1921,108 @@ async function runUpdateWithContext(id, options, global, execution = {}) {
1857
1921
  return routedClose;
1858
1922
  }
1859
1923
  assertMatchingOrderRank(options);
1860
- let graphBeforeUpdate = fieldFlags.dep || fieldFlags.depRemove || fieldFlags.blockedBy
1861
- ? await listAllItemMetadataLight(pmRoot, settings.item_format, typeRegistry.type_to_folder, undefined, settings.schema)
1862
- : undefined;
1863
- let lifecycleTransition;
1864
- let recurrenceHistoryContext;
1865
- const result = await mutateItemWithHistoryContextResolver({
1924
+ const relationshipRegistry = resolveWorkspaceRelationshipKindRegistry();
1925
+ const hierarchyMutationRequired = (fieldFlags.parent && !clearItemMetadataKeys.has("parent")) ||
1926
+ dependencyUpdates.additions.some((dependency) => relationshipRegistry.resolve(dependency.kind)?.hierarchy);
1927
+ const releaseHierarchyMutationLock = await acquireHierarchyMutationLock({
1866
1928
  pmRoot,
1867
- settings,
1868
- typeToFolder: typeRegistry.type_to_folder,
1869
- id,
1870
- op: resolveUpdateHistoryOperation(options, execution),
1871
1929
  author,
1872
- message: options.message,
1873
- resolveHistoryContext: () => recurrenceHistoryContext,
1874
- force: options.force,
1875
- bypassAssigneeConflict: options.ownershipMetadataBypass === true ||
1876
- options.ownershipDependencyBypass === true,
1877
- extensionFieldNames,
1878
- skipNoop: true,
1879
- mutate(document) {
1880
- lifecycleTransition = prepareRecurrenceTransition({
1881
- document,
1882
- recurrenceReason: execution.recurrenceReason,
1883
- requestedStatus: options.status,
1884
- statusRegistry,
1885
- });
1886
- recurrenceHistoryContext = lifecycleTransition
1887
- ? { recurrence: lifecycleTransition }
1888
- : undefined;
1889
- graphBeforeUpdate = graphBeforeUpdate?.map((item) => item.id === document.metadata.id
1890
- ? structuredClone(document.metadata)
1891
- : item);
1892
- return mutateUpdateDocument(document, {
1893
- options,
1894
- settings,
1895
- typeRegistry,
1896
- statusRegistry,
1897
- runtimeFieldRegistry,
1898
- extensionRegistrations,
1899
- extensionFieldNames,
1900
- clearItemMetadataKeys,
1901
- dependencyUpdates,
1902
- dependencyRemovals,
1903
- commentUpdates,
1904
- noteUpdates,
1905
- learningUpdates,
1906
- fileUpdates,
1907
- testUpdates,
1908
- docUpdates,
1909
- resolvedParentValue: parentReference.resolvedParentValue,
1910
- resolvedBlockedByDependencyId,
1911
- runtimeFieldUpdates,
1912
- nowValue,
1913
- nowIso,
1914
- author,
1915
- pmRoot,
1916
- });
1917
- },
1930
+ ttlSeconds: settings.locks.ttl_seconds,
1931
+ waitMs: settings.locks.wait_ms,
1932
+ required: hierarchyMutationRequired,
1918
1933
  });
1919
- const orderingCycleWarnings = graphBeforeUpdate
1920
- ? collectNewOrderingCycleWarnings(graphBeforeUpdate, graphBeforeUpdate.map((item) => item.id === result.item.id ? result.item : item), result.item.id)
1921
- : [];
1922
- return {
1923
- item: toItemRecord(result.item),
1924
- changed_fields: result.changedFields,
1925
- warnings: [
1926
- ...workflowTransitionWarnings,
1927
- ...parentReferenceWarnings,
1928
- ...dependencyTargetWarnings,
1929
- ...orderingCycleWarnings,
1930
- ...result.warnings,
1931
- ],
1932
- ...(lifecycleTransition === undefined
1933
- ? {}
1934
- : { lifecycle_transition: lifecycleTransition }),
1935
- };
1934
+ try {
1935
+ let graphBeforeUpdate = await loadUpdateRelationshipGraph({
1936
+ fieldFlags,
1937
+ clearedMetadataKeys: clearItemMetadataKeys,
1938
+ pmRoot,
1939
+ settings,
1940
+ typeToFolder: typeRegistry.type_to_folder,
1941
+ });
1942
+ let lifecycleTransition;
1943
+ let recurrenceHistoryContext;
1944
+ const result = await mutateItemWithHistoryContextResolver({
1945
+ pmRoot,
1946
+ settings,
1947
+ typeToFolder: typeRegistry.type_to_folder,
1948
+ id,
1949
+ op: resolveUpdateHistoryOperation(options, execution),
1950
+ author,
1951
+ message: options.message,
1952
+ resolveHistoryContext: () => recurrenceHistoryContext,
1953
+ force: options.force,
1954
+ bypassAssigneeConflict: options.ownershipMetadataBypass === true ||
1955
+ options.ownershipDependencyBypass === true,
1956
+ extensionFieldNames,
1957
+ skipNoop: true,
1958
+ mutate(document) {
1959
+ lifecycleTransition = prepareRecurrenceTransition({
1960
+ document,
1961
+ recurrenceReason: execution.recurrenceReason,
1962
+ requestedStatus: options.status,
1963
+ statusRegistry,
1964
+ });
1965
+ recurrenceHistoryContext = lifecycleTransition
1966
+ ? { recurrence: lifecycleTransition }
1967
+ : undefined;
1968
+ graphBeforeUpdate = graphBeforeUpdate?.map((item) => item.id === document.metadata.id
1969
+ ? structuredClone(document.metadata)
1970
+ : item);
1971
+ const mutation = mutateUpdateDocument(document, {
1972
+ options,
1973
+ settings,
1974
+ typeRegistry,
1975
+ statusRegistry,
1976
+ runtimeFieldRegistry,
1977
+ extensionRegistrations,
1978
+ extensionFieldNames,
1979
+ clearItemMetadataKeys,
1980
+ dependencyUpdates,
1981
+ dependencyRemovals,
1982
+ commentUpdates,
1983
+ noteUpdates,
1984
+ learningUpdates,
1985
+ fileUpdates,
1986
+ testUpdates,
1987
+ docUpdates,
1988
+ resolvedParentValue: parentReference.resolvedParentValue,
1989
+ resolvedBlockedByDependencyId,
1990
+ runtimeFieldUpdates,
1991
+ nowValue,
1992
+ nowIso,
1993
+ author,
1994
+ pmRoot,
1995
+ });
1996
+ assertUpdateHierarchyIntegrity({
1997
+ before: graphBeforeUpdate,
1998
+ document,
1999
+ statusRegistry,
2000
+ });
2001
+ return mutation;
2002
+ },
2003
+ });
2004
+ await releaseHierarchyMutationLock();
2005
+ const orderingCycleWarnings = graphBeforeUpdate
2006
+ ? collectNewOrderingCycleWarnings(graphBeforeUpdate, graphBeforeUpdate.map((item) => item.id === result.item.id ? result.item : item), result.item.id)
2007
+ : [];
2008
+ return {
2009
+ item: toItemRecord(result.item),
2010
+ changed_fields: result.changedFields,
2011
+ warnings: [
2012
+ ...workflowTransitionWarnings,
2013
+ ...parentReferenceWarnings,
2014
+ ...dependencyTargetWarnings,
2015
+ ...orderingCycleWarnings,
2016
+ ...result.warnings,
2017
+ ],
2018
+ ...(lifecycleTransition === undefined
2019
+ ? {}
2020
+ : { lifecycle_transition: lifecycleTransition }),
2021
+ };
2022
+ }
2023
+ finally {
2024
+ await releaseHierarchyMutationLock();
2025
+ }
1936
2026
  }
1937
2027
  /** Resolve the immutable operation name for ordinary, bypass, and recurrence updates. */
1938
2028
  function resolveUpdateHistoryOperation(options, execution) {
@@ -2021,4 +2111,4 @@ export const _testOnlyUpdateCommand = {
2021
2111
  resolveRuntimeUnsetDefinition: (token, registry) => resolveRuntimeUnsetFieldDefinition(token, "update", registry),
2022
2112
  };
2023
2113
  //# sourceMappingURL=update.js.map
2024
- //# debugId=d024d9cf-81f8-5690-b8f4-03620e3e9bf8
2114
+ //# debugId=c88f01be-6abe-5fa2-9792-3f54e61bb282
@@ -9,7 +9,7 @@
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]="3c9d6924-ae7c-57c3-a346-1e2189c532fa")}catch(e){}}();
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]="393f6460-bf8b-5479-8e3e-cac6f2680823")}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
15
  import { EXIT_CODE } from "../../core/shared/constants.js";
@@ -318,6 +318,58 @@ function unionCollection(base, ours, theirs) {
318
318
  }
319
319
  return merged;
320
320
  }
321
+ function linkedTestDefinitionIdentity(value) {
322
+ /* c8 ignore start -- canonical item parsing normalizes linked-test entries to object records */
323
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
324
+ return stableStringify(value);
325
+ }
326
+ /* c8 ignore stop */
327
+ const { provenance: _provenance, ...definition } = value;
328
+ return stableStringify(definition);
329
+ }
330
+ function markMergedLinkedTest(value) {
331
+ /* c8 ignore start -- canonical item parsing normalizes linked-test entries to object records */
332
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
333
+ return value;
334
+ }
335
+ /* c8 ignore stop */
336
+ const test = value;
337
+ if (!test.provenance) {
338
+ return {
339
+ ...test,
340
+ // Legacy entries contributed by another branch must not regain implicit
341
+ // local trust. This incomplete merge marker is preserved by normalization
342
+ // and refused until an operator acknowledges it in the receiving clone.
343
+ provenance: { source_kind: "merge_union" },
344
+ };
345
+ }
346
+ return {
347
+ ...test,
348
+ provenance: {
349
+ ...test.provenance,
350
+ source_kind: "merge_union",
351
+ },
352
+ };
353
+ }
354
+ function unionLinkedTests(base, ours, theirs) {
355
+ const toEntries = (value) => Array.isArray(value) ? value : [];
356
+ const baseIds = new Set(toEntries(base).map(linkedTestDefinitionIdentity));
357
+ const oursEntries = toEntries(ours);
358
+ const oursIds = new Set(oursEntries.map(linkedTestDefinitionIdentity));
359
+ const theirsEntries = toEntries(theirs);
360
+ const theirsIds = new Set(theirsEntries.map(linkedTestDefinitionIdentity));
361
+ const merged = oursEntries.filter((entry) => {
362
+ const identity = linkedTestDefinitionIdentity(entry);
363
+ return !baseIds.has(identity) || theirsIds.has(identity);
364
+ });
365
+ for (const entry of theirsEntries) {
366
+ const identity = linkedTestDefinitionIdentity(entry);
367
+ if (!oursIds.has(identity) && !baseIds.has(identity)) {
368
+ merged.push(markMergedLinkedTest(entry));
369
+ }
370
+ }
371
+ return merged;
372
+ }
321
373
  function latestTimestamp(ours, theirs) {
322
374
  return compareTimestampStrings(String(ours), String(theirs)) >= 0
323
375
  ? ours
@@ -362,6 +414,40 @@ function parseItemMergeSide(raw, label, options) {
362
414
  throw new PmCliError(`Item merge input (${label}) is not a readable item document: ${String(error)}`, EXIT_CODE.GENERIC_FAILURE);
363
415
  }
364
416
  }
417
+ /** Merge one collection field and report whether the field was handled. */
418
+ function mergeItemUnionField(params) {
419
+ if (!params.unionFieldSet.has(params.field))
420
+ return false;
421
+ const union = params.field === "tests"
422
+ ? unionLinkedTests(params.baseValue, params.oursValue, params.theirsValue)
423
+ : unionCollection(params.baseValue, params.oursValue, params.theirsValue);
424
+ params.accumulator.merged[params.field] = union;
425
+ if (!jsonEquals(union, params.oursValue)) {
426
+ params.accumulator.unionFields.push(params.field);
427
+ }
428
+ return true;
429
+ }
430
+ /** Merge one scalar field while preserving conflict evidence. */
431
+ function mergeItemScalarField(params) {
432
+ const outcome = mergeItemScalarThreeWay(params.baseValue, params.oursValue, params.theirsValue, params.preferred, params.conflictResolution);
433
+ if (outcome.value !== undefined) {
434
+ params.accumulator.merged[params.field] = outcome.value;
435
+ }
436
+ if (outcome.conflict) {
437
+ params.accumulator.conflictFields.push(params.field);
438
+ params.accumulator.conflictDecisions.push({
439
+ field: params.field,
440
+ base: params.baseValue,
441
+ ours: params.oursValue,
442
+ theirs: params.theirsValue,
443
+ retained: outcome.value,
444
+ discarded: outcome.from_theirs ? params.oursValue : params.theirsValue,
445
+ });
446
+ }
447
+ else if (outcome.from_theirs) {
448
+ params.accumulator.fieldsFromTheirs.push(params.field);
449
+ }
450
+ }
365
451
  function mergeItemMetadataRecords(baseRecord, oursRecord, theirsRecord, preferred, conflictResolution) {
366
452
  const unionFieldSet = new Set(ITEM_UNION_COLLECTION_FIELDS);
367
453
  const latestFieldSet = new Set(ITEM_LATEST_TIMESTAMP_FIELDS);
@@ -379,39 +465,32 @@ function mergeItemMetadataRecords(baseRecord, oursRecord, theirsRecord, preferre
379
465
  const baseValue = baseRecord[field];
380
466
  const oursValue = oursRecord[field];
381
467
  const theirsValue = theirsRecord[field];
382
- if (unionFieldSet.has(field)) {
383
- const union = unionCollection(baseValue, oursValue, theirsValue);
468
+ if (mergeItemUnionField({
469
+ field,
470
+ baseValue,
471
+ oursValue,
472
+ theirsValue,
473
+ unionFieldSet,
474
+ accumulator,
475
+ })) {
384
476
  // `fieldNames` is derived from both records' own keys, so a collection
385
477
  // reached here is declared by at least one side. Preserve even an empty
386
478
  // result (`tags` is required) so serialization never drops it.
387
- accumulator.merged[field] = union;
388
- if (!jsonEquals(union, oursValue)) {
389
- accumulator.unionFields.push(field);
390
- }
391
479
  continue;
392
480
  }
393
481
  if (latestFieldSet.has(field)) {
394
482
  accumulator.merged[field] = latestTimestamp(oursValue, theirsValue);
395
483
  continue;
396
484
  }
397
- const outcome = mergeItemScalarThreeWay(baseValue, oursValue, theirsValue, preferred, conflictResolution);
398
- if (outcome.value !== undefined) {
399
- accumulator.merged[field] = outcome.value;
400
- }
401
- if (outcome.conflict) {
402
- accumulator.conflictFields.push(field);
403
- accumulator.conflictDecisions.push({
404
- field,
405
- base: baseValue,
406
- ours: oursValue,
407
- theirs: theirsValue,
408
- retained: outcome.value,
409
- discarded: outcome.from_theirs ? oursValue : theirsValue,
410
- });
411
- }
412
- else if (outcome.from_theirs) {
413
- accumulator.fieldsFromTheirs.push(field);
414
- }
485
+ mergeItemScalarField({
486
+ field,
487
+ baseValue,
488
+ oursValue,
489
+ theirsValue,
490
+ preferred,
491
+ conflictResolution,
492
+ accumulator,
493
+ });
415
494
  }
416
495
  return accumulator;
417
496
  }
@@ -625,4 +704,4 @@ export function mergeJsonDocuments(baseRaw, oursRaw, theirsRaw, options = {}) {
625
704
  };
626
705
  }
627
706
  //# sourceMappingURL=three-way.js.map
628
- //# debugId=3c9d6924-ae7c-57c3-a346-1e2189c532fa
707
+ //# debugId=393f6460-bf8b-5479-8e3e-cac6f2680823
@@ -6,7 +6,7 @@
6
6
  import type { PmReadOutputReceipt } from "../read-output-contracts.js";
7
7
  import type { ListFullResult, ListOptions } from "./list.js";
8
8
  /** Stable reasons a purported whole-workspace list cannot be trusted. */
9
- export declare const PM_COMPLETE_LIST_FINDING_CODES: readonly ["invalid_envelope", "source_incomplete", "source_unchecked", "filtered_corpus", "terminal_items_excluded", "strict_read_unproven", "page_incomplete", "count_mismatch", "projection_incomplete", "field_omission", "budget_compaction", "budget_omission", "session_projection", "invalid_item_id", "duplicate_item_id"];
9
+ export declare const PM_COMPLETE_LIST_FINDING_CODES: readonly ["invalid_envelope", "source_incomplete", "source_unchecked", "filtered_corpus", "terminal_items_excluded", "strict_read_unproven", "page_incomplete", "count_mismatch", "projection_incomplete", "field_omission", "omission_receipt_missing", "omission_receipt_invalid", "read_output_missing", "read_output_invalid", "read_output_dimensions_incomplete", "budget_compaction", "budget_omission", "session_projection", "invalid_item_id", "duplicate_item_id"];
10
10
  /** Machine-readable reason a complete-list candidate failed certification. */
11
11
  export type PmCompleteListFindingCode = (typeof PM_COMPLETE_LIST_FINDING_CODES)[number];
12
12
  /** One independently actionable complete-list contract violation. */
@@ -39,8 +39,8 @@ export interface PmCompleteListCertificate {
39
39
  export interface PmCompleteListResult extends ListFullResult {
40
40
  /** Exact proof that the returned rows represent the full tracker corpus. */
41
41
  complete_list: PmCompleteListCertificate;
42
- /** Universal read receipt when the runtime attached one. */
43
- read_output?: PmReadOutputReceipt;
42
+ /** Mandatory universal read receipt proving unbounded, uncompacted delivery. */
43
+ read_output: PmReadOutputReceipt;
44
44
  }
45
45
  /** Inspection report returned without throwing for policy and UI consumers. */
46
46
  export interface PmCompleteListInspection {
@@ -1,6 +1,6 @@
1
1
  /** Stable reasons a purported whole-workspace list cannot be trusted. */
2
2
 
3
- !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]="cc342d27-aa2d-5e84-8441-46a35c42dc00")}catch(e){}}();
3
+ !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]="c14a67b4-102d-5219-b2b3-15d39da36627")}catch(e){}}();
4
4
  export const PM_COMPLETE_LIST_FINDING_CODES = [
5
5
  "invalid_envelope",
6
6
  "source_incomplete",
@@ -12,6 +12,11 @@ export const PM_COMPLETE_LIST_FINDING_CODES = [
12
12
  "count_mismatch",
13
13
  "projection_incomplete",
14
14
  "field_omission",
15
+ "omission_receipt_missing",
16
+ "omission_receipt_invalid",
17
+ "read_output_missing",
18
+ "read_output_invalid",
19
+ "read_output_dimensions_incomplete",
15
20
  "budget_compaction",
16
21
  "budget_omission",
17
22
  "session_projection",
@@ -31,7 +36,7 @@ export class PmCompleteListValidationError extends Error {
31
36
  contract: "complete_list",
32
37
  findings,
33
38
  recovery: {
34
- suggested_retry: "pm list-all --full --strict-read --no-truncate --output-budget unbounded --json",
39
+ suggested_retry: "pm list --all --output-include full --strict-read --no-truncate --output-budget unbounded --output-limit unbounded --json",
35
40
  sdk: "await client.listAllComplete()",
36
41
  },
37
42
  };
@@ -67,6 +72,17 @@ function inspectSourceScope(candidate, findings) {
67
72
  : {};
68
73
  addFinding(findings, completeness.status === "partial", "source_incomplete", "The source scan reported unreadable tracker artifacts.");
69
74
  addFinding(findings, completeness.status !== "complete" && completeness.status !== "partial", "source_unchecked", "The source scan did not prove completeness.");
75
+ const unreadableItemCount = completeness.unreadable_item_count;
76
+ const unreadableDirectoryCount = completeness.unreadable_directory_count;
77
+ addFinding(findings, completeness.status === "complete" &&
78
+ (unreadableItemCount !== 0 || unreadableDirectoryCount !== 0) &&
79
+ Number.isSafeInteger(unreadableItemCount) &&
80
+ Number.isSafeInteger(unreadableDirectoryCount), "source_incomplete", "The source scan reports unreadable tracker artifacts despite a complete status.");
81
+ addFinding(findings, completeness.status === "complete" &&
82
+ (!Number.isSafeInteger(unreadableItemCount) ||
83
+ !Number.isSafeInteger(unreadableDirectoryCount) ||
84
+ Number(unreadableItemCount) < 0 ||
85
+ Number(unreadableDirectoryCount) < 0), "source_unchecked", "The source scan did not report safe non-negative unreadable counts.");
70
86
  const filters = isRecord(candidate.filters) ? candidate.filters : {};
71
87
  const runtimeFilters = isRecord(filters.runtime_filters)
72
88
  ? filters.runtime_filters
@@ -100,13 +116,40 @@ function inspectEnvelopeShape(candidate, filters, findings) {
100
116
  const omissionReceipt = isRecord(candidate.omission_receipt)
101
117
  ? candidate.omission_receipt
102
118
  : undefined;
119
+ addFinding(findings, omissionReceipt === undefined, "omission_receipt_missing", "The result carries no field-omission receipt.");
120
+ addFinding(findings, omissionReceipt !== undefined &&
121
+ (omissionReceipt.has_omissions !== false ||
122
+ omissionReceipt.omitted_field_group_count !== 0 ||
123
+ !Number.isSafeInteger(omissionReceipt.omitted_field_group_count) ||
124
+ !Array.isArray(omissionReceipt.omitted_field_groups) ||
125
+ omissionReceipt.omitted_field_groups.length !== 0), "omission_receipt_invalid", "The field-omission receipt is malformed or contradicts its no-omission claim.");
103
126
  addFinding(findings, omissionReceipt?.has_omissions === true, "field_omission", "The omission receipt reports withheld field groups.");
104
127
  }
128
+ /** Return whether a universal read receipt cannot prove an intact list result. */
129
+ function isInvalidCompleteListReadOutput(readOutput) {
130
+ return (readOutput.contract_version !== 1 ||
131
+ readOutput.command !== "list" ||
132
+ readOutput.within_budget !== true ||
133
+ readOutput.strings_compacted !== false ||
134
+ readOutput.rows_compacted !== false ||
135
+ readOutput.result_omitted !== false);
136
+ }
137
+ /** Return whether the receipt proves every dimension required for an unbounded full read. */
138
+ function hasCompleteListReadOutputDimensions(readOutput) {
139
+ if (!Array.isArray(readOutput.requested_dimensions))
140
+ return false;
141
+ const requestedDimensions = new Set(readOutput.requested_dimensions);
142
+ return ["include", "amount", "cost"].every((dimension) => requestedDimensions.has(dimension));
143
+ }
105
144
  /** Append universal-output and cross-call-session findings. */
106
145
  function inspectOutputScope(candidate, findings) {
107
146
  const readOutput = isRecord(candidate.read_output)
108
147
  ? candidate.read_output
109
148
  : undefined;
149
+ addFinding(findings, readOutput === undefined, "read_output_missing", "The result carries no universal read-output receipt.");
150
+ addFinding(findings, readOutput !== undefined && isInvalidCompleteListReadOutput(readOutput), "read_output_invalid", "The universal read-output receipt does not prove an intact list result.");
151
+ addFinding(findings, readOutput !== undefined &&
152
+ !hasCompleteListReadOutputDimensions(readOutput), "read_output_dimensions_incomplete", "The universal read-output receipt does not prove full, unbounded include, amount, and cost dimensions.");
110
153
  addFinding(findings, readOutput?.strings_compacted === true ||
111
154
  readOutput?.rows_compacted === true ||
112
155
  candidate.output_budget_truncation !== undefined, "budget_compaction", "Universal output shaping compacted the result.");
@@ -170,4 +213,4 @@ export function assertCompleteListResult(candidate) {
170
213
  certifyCompleteListResult(candidate);
171
214
  }
172
215
  //# sourceMappingURL=complete-list.js.map
173
- //# debugId=cc342d27-aa2d-5e84-8441-46a35c42dc00
216
+ //# debugId=c14a67b4-102d-5219-b2b3-15d39da36627