@unbrained/pm-cli 2026.8.21 → 2026.8.22

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 (158) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +16 -0
  3. package/dist/cli/error-guidance.d.ts +9 -4
  4. package/dist/cli/error-guidance.js +16 -15
  5. package/dist/cli/main.d.ts +3 -0
  6. package/dist/cli/main.js +40 -7
  7. package/dist/cli-bundle/bundle-manifest.json +153 -153
  8. package/dist/cli-bundle/chunks/{chunk-BKYB45NQ.js → chunk-BWX4SK46.js} +48 -48
  9. package/dist/cli-bundle/chunks/chunk-EMWDM5FI.js +13 -0
  10. package/dist/cli-bundle/chunks/chunk-HYG6LRL4.js +200 -0
  11. package/dist/cli-bundle/chunks/{chunk-LARJJ3RK.js → chunk-MYYI3ZOA.js} +2 -2
  12. package/dist/cli-bundle/chunks/{chunk-BUNIEJNE.js → chunk-MZTZQTMS.js} +2 -2
  13. package/dist/cli-bundle/chunks/{chunk-LOVTQ52Z.js → chunk-QI2CT3IX.js} +2 -2
  14. package/dist/cli-bundle/chunks/chunk-SMWXHMVB.js +35 -0
  15. package/dist/cli-bundle/chunks/{chunk-N3DSV4CH.js → chunk-U6GQ4VMV.js} +6 -6
  16. package/dist/cli-bundle/chunks/{chunk-3AFAUHUE.js → chunk-VMMIL3SI.js} +2 -2
  17. package/dist/cli-bundle/chunks/chunk-WG4STC36.js +5 -0
  18. package/dist/cli-bundle/chunks/{chunk-JSLOFXGA.js → chunk-YBPJAOQQ.js} +2 -2
  19. package/dist/cli-bundle/chunks/{register-list-query-F57EWDEF.js → register-list-query-HZKBQNWW.js} +2 -2
  20. package/dist/cli-bundle/chunks/{register-mutation-7IY2D5KO.js → register-mutation-HTKEEGRD.js} +2 -2
  21. package/dist/cli-bundle/chunks/{register-operations-FALL3YTU.js → register-operations-6EMRWJJY.js} +2 -2
  22. package/dist/cli-bundle/chunks/{register-setup-HXU7IZZD.js → register-setup-QK4OTOXN.js} +2 -2
  23. package/dist/cli-bundle/focused-chunks/{chunk-FJ22HNRP.js → chunk-2ARQGDDW.js} +2 -2
  24. package/dist/cli-bundle/focused-chunks/{chunk-CKKQSJNN.js → chunk-2AZT5WXZ.js} +2 -2
  25. package/dist/cli-bundle/focused-chunks/{chunk-F4YEW5I2.js → chunk-2POVTNLY.js} +2 -2
  26. package/dist/cli-bundle/focused-chunks/chunk-3LKU6PTJ.js +4 -0
  27. package/dist/cli-bundle/focused-chunks/chunk-5SHSBMX2.js +2 -0
  28. package/dist/cli-bundle/focused-chunks/{chunk-FH6DYED3.js → chunk-6S2MO3Q3.js} +2 -2
  29. package/dist/cli-bundle/focused-chunks/{chunk-UZE7XUZK.js → chunk-A2BC5PQL.js} +2 -2
  30. package/dist/cli-bundle/focused-chunks/chunk-C4S4A4JJ.js +5 -0
  31. package/dist/cli-bundle/focused-chunks/{chunk-KDNG7TKQ.js → chunk-D6RBZEJJ.js} +2 -2
  32. package/dist/cli-bundle/focused-chunks/chunk-E2EWYPY6.js +16 -0
  33. package/dist/cli-bundle/focused-chunks/{chunk-4ERLO7QD.js → chunk-GMYPD4FX.js} +2 -2
  34. package/dist/cli-bundle/focused-chunks/chunk-L5LDSRO2.js +155 -0
  35. package/dist/cli-bundle/focused-chunks/{chunk-ZOSEELYJ.js → chunk-MB37AYN6.js} +2 -2
  36. package/dist/cli-bundle/focused-chunks/chunk-MCOR37QK.js +2 -0
  37. package/dist/cli-bundle/focused-chunks/{chunk-2NIY5NKG.js → chunk-PH2OXXVZ.js} +2 -2
  38. package/dist/cli-bundle/focused-chunks/{chunk-YKBKIEAJ.js → chunk-PMGJUFNQ.js} +2 -2
  39. package/dist/cli-bundle/focused-chunks/chunk-R6SR45XU.js +50 -0
  40. package/dist/cli-bundle/focused-chunks/chunk-WZE4LIPW.js +8 -0
  41. package/dist/cli-bundle/main.js +13 -13
  42. package/dist/cli-bundle/sdk-authoring.js +1 -1
  43. package/dist/cli-bundle/sdk-contracts.js +2 -2
  44. package/dist/cli-bundle/sdk-core.js +40 -40
  45. package/dist/cli-bundle/sdk-governance.js +1 -1
  46. package/dist/cli-bundle/sdk-graph.js +1 -1
  47. package/dist/cli-bundle/sdk-merge.js +32 -32
  48. package/dist/cli-bundle/sdk-query.js +1 -1
  49. package/dist/cli-bundle/sdk-runtime.js +1 -1
  50. package/dist/cli-bundle/sdk-testing.js +1 -1
  51. package/dist/cli-bundle/sdk.js +5 -5
  52. package/dist/core/extensions/extension-hook-runtime.js +22 -20
  53. package/dist/core/extensions/extension-types.d.ts +8 -1
  54. package/dist/core/extensions/extension-types.js +2 -2
  55. package/dist/core/extensions/loader.js +7 -3
  56. package/dist/core/extensions/service-ownership.d.ts +8 -0
  57. package/dist/core/extensions/service-ownership.js +20 -0
  58. package/dist/core/item/id.js +20 -5
  59. package/dist/core/item/item-format.js +27 -2
  60. package/dist/core/shared/errors.d.ts +4 -0
  61. package/dist/core/shared/errors.js +2 -2
  62. package/dist/sdk/agent/refusal-reachability.d.ts +1 -1
  63. package/dist/sdk/authoring.d.ts +1 -1
  64. package/dist/sdk/authoring.js +2 -2
  65. package/dist/sdk/cli-contracts/agent-output-contracts.d.ts +77 -0
  66. package/dist/sdk/cli-contracts/agent-output-contracts.js +324 -2
  67. package/dist/sdk/cli-contracts/commander-mutation-options.js +3 -3
  68. package/dist/sdk/cli-contracts/runtime-contracts.d.ts +3 -1
  69. package/dist/sdk/cli-contracts/runtime-contracts.js +4 -3
  70. package/dist/sdk/cli-contracts.d.ts +1 -1
  71. package/dist/sdk/cli-contracts.js +3 -3
  72. package/dist/sdk/extension/install-sources.d.ts +1 -1
  73. package/dist/sdk/extension/install-sources.js +6 -3
  74. package/dist/sdk/extension/output-ownership.d.ts +1 -0
  75. package/dist/sdk/extension/output-ownership.js +3 -2
  76. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +38 -14
  77. package/dist/sdk/generated/generated-error-code-catalog-part-2.js +14 -2
  78. package/dist/sdk/governance/health.d.ts +3 -0
  79. package/dist/sdk/governance/health.js +39 -7
  80. package/dist/sdk/governance/hierarchy-validation.d.ts +29 -0
  81. package/dist/sdk/governance/hierarchy-validation.js +109 -0
  82. package/dist/sdk/governance/validate.d.ts +1 -7
  83. package/dist/sdk/governance/validate.js +47 -99
  84. package/dist/sdk/graph/assembly.d.ts +6 -1
  85. package/dist/sdk/graph/assembly.js +16 -22
  86. package/dist/sdk/graph/governance-contracts.d.ts +1 -1
  87. package/dist/sdk/graph/governance-contracts.js +8 -2
  88. package/dist/sdk/graph/governance.js +46 -2
  89. package/dist/sdk/graph/hierarchy-integrity.d.ts +94 -0
  90. package/dist/sdk/graph/hierarchy-integrity.js +311 -0
  91. package/dist/sdk/graph/index.d.ts +2 -1
  92. package/dist/sdk/graph/index.js +3 -2
  93. package/dist/sdk/graph/remediation.js +32 -2
  94. package/dist/sdk/graph/run.d.ts +31 -2
  95. package/dist/sdk/graph/run.js +80 -31
  96. package/dist/sdk/graph/workspace-relationship-kind-registry.d.ts +8 -0
  97. package/dist/sdk/graph/workspace-relationship-kind-registry.js +28 -0
  98. package/dist/sdk/index.d.ts +2 -2
  99. package/dist/sdk/index.js +3 -3
  100. package/dist/sdk/item-children.js +21 -8
  101. package/dist/sdk/lifecycle/create.js +125 -96
  102. package/dist/sdk/lifecycle/hierarchy-mutation-lock.d.ts +15 -0
  103. package/dist/sdk/lifecycle/hierarchy-mutation-lock.js +25 -0
  104. package/dist/sdk/lifecycle/update.d.ts +2 -0
  105. package/dist/sdk/lifecycle/update.js +171 -83
  106. package/dist/sdk/query/complete-list.d.ts +3 -3
  107. package/dist/sdk/query/complete-list.js +46 -3
  108. package/dist/sdk/query/list.d.ts +4 -3
  109. package/dist/sdk/query/list.js +89 -49
  110. package/dist/sdk/read-output-contracts.d.ts +5 -1
  111. package/dist/sdk/read-output-contracts.js +87 -22
  112. package/dist/sdk/relationship-context.js +3 -4
  113. package/dist/sdk/relationships.js +17 -10
  114. package/dist/sdk/runtime.d.ts +3 -2
  115. package/dist/sdk/runtime.js +3 -2
  116. package/dist/sdk/test/batch.js +13 -5
  117. package/dist/sdk/test/execution.js +10 -2
  118. package/dist/types.d.ts +11 -0
  119. package/dist/types.js +2 -2
  120. package/docs/AGENT_UX_CONTRACTS.md +20 -4
  121. package/docs/COMMANDS.md +29 -14
  122. package/docs/DEPENDENCY_KIND_CONTRACT.md +31 -3
  123. package/docs/DIAGNOSTIC_OUTPUT_CONTRACTS.md +98 -0
  124. package/docs/EXTENSIONS.md +2 -1
  125. package/docs/README.md +1 -0
  126. package/docs/READ_OUTPUT_CONTRACTS.md +9 -2
  127. package/docs/RELATIONSHIP_GRAPH.md +6 -3
  128. package/docs/TESTING.md +15 -0
  129. package/marketplace.json +2 -2
  130. package/package.json +1 -1
  131. package/packages/pm-beads/package.json +1 -1
  132. package/packages/pm-calendar/package.json +1 -1
  133. package/packages/pm-command-kit/package.json +1 -1
  134. package/packages/pm-digital-twin/package.json +1 -1
  135. package/packages/pm-governance-audit/package.json +1 -1
  136. package/packages/pm-guide-shell/package.json +1 -1
  137. package/packages/pm-kanban/package.json +1 -1
  138. package/packages/pm-lifecycle-hooks/package.json +1 -1
  139. package/packages/pm-linked-test-adapters/package.json +1 -1
  140. package/packages/pm-search-advanced/package.json +1 -1
  141. package/packages/pm-templates/package.json +1 -1
  142. package/packages/pm-todos/package.json +1 -1
  143. package/packages/pm-vcs/package.json +1 -1
  144. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  145. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  146. package/sdk/public-surface.json +593 -35
  147. package/dist/cli-bundle/chunks/chunk-2MA6ZQVZ.js +0 -197
  148. package/dist/cli-bundle/chunks/chunk-7VHZS727.js +0 -5
  149. package/dist/cli-bundle/chunks/chunk-OCLPLFAY.js +0 -35
  150. package/dist/cli-bundle/chunks/chunk-S4IRYKBU.js +0 -13
  151. package/dist/cli-bundle/focused-chunks/chunk-7WZRLCYF.js +0 -50
  152. package/dist/cli-bundle/focused-chunks/chunk-EEFKYMDR.js +0 -16
  153. package/dist/cli-bundle/focused-chunks/chunk-F2A56DHV.js +0 -8
  154. package/dist/cli-bundle/focused-chunks/chunk-FV5ACOK7.js +0 -2
  155. package/dist/cli-bundle/focused-chunks/chunk-JEAVFOWM.js +0 -2
  156. package/dist/cli-bundle/focused-chunks/chunk-NJ63LOP6.js +0 -155
  157. package/dist/cli-bundle/focused-chunks/chunk-NXRTQ7HI.js +0 -2
  158. package/dist/cli-bundle/focused-chunks/chunk-TJ7EMJ46.js +0 -4
@@ -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]="7c1729f8-4a67-5746-a1bf-351f862ceb66")}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,6 +17,9 @@ 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";
22
25
  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";
@@ -355,6 +358,9 @@ function parseDependencyCreatedAt(value, currentIso) {
355
358
  if (!value || value.trim() === "" || value.trim().toLowerCase() === "now") {
356
359
  return currentIso;
357
360
  }
361
+ return normalizeDependencyTimestamp(value);
362
+ }
363
+ function normalizeDependencyTimestamp(value) {
358
364
  const parsed = Date.parse(value);
359
365
  if (!Number.isFinite(parsed)) {
360
366
  throw new PmCliError(`Invalid dependency created_at timestamp "${value}"`, EXIT_CODE.USAGE);
@@ -377,7 +383,14 @@ const DEP_ADDITION_KEYS = [
377
383
  "source_kind",
378
384
  ];
379
385
  /** Allowed CSV/markdown keys for the `--dep-remove` selector (GH-258). */
380
- const DEP_REMOVE_KEYS = ["id", "kind", "type", "source_kind"];
386
+ const DEP_REMOVE_KEYS = [
387
+ "id",
388
+ "kind",
389
+ "type",
390
+ "author",
391
+ "created_at",
392
+ "source_kind",
393
+ ];
381
394
  function looksLikeStructuredDependencyEntry(raw) {
382
395
  if (raw.startsWith("```") || raw.includes("\n")) {
383
396
  return true;
@@ -455,12 +468,18 @@ function parseDependencyRemovals(raw, prefix) {
455
468
  }
456
469
  const kindRaw = normalizeDependencyKindInput(parseOptionalDependencyString(kv.kind ?? kv.type));
457
470
  const sourceKind = normalizeDependencySourceKind(parseOptionalDependencyString(kv.source_kind));
471
+ const author = parseOptionalDependencyString(kv.author);
472
+ const createdAt = parseOptionalDependencyString(kv.created_at);
458
473
  return {
459
474
  id: normalizeDependencySeedId(idRaw, prefix, sourceKind),
460
475
  kind: kindRaw
461
476
  ? ensureEnum(kindRaw, DEPENDENCY_KIND_VALUES, "dependency kind")
462
477
  : undefined,
463
478
  source_kind: sourceKind,
479
+ author,
480
+ created_at: createdAt
481
+ ? normalizeDependencyTimestamp(createdAt)
482
+ : undefined,
464
483
  };
465
484
  }
466
485
  if (trimmed.toLowerCase() === "undefined") {
@@ -473,7 +492,24 @@ function parseDependencyRemovals(raw, prefix) {
473
492
  }
474
493
  function dependencyKey(value) {
475
494
  const sourceKind = value.source_kind?.trim().toLowerCase() ?? "";
476
- return `${value.id.trim().toLowerCase()}::${value.kind}::${sourceKind}`;
495
+ return [value.id.trim().toLowerCase(), value.kind, sourceKind].join("\u0000");
496
+ }
497
+ /** Return the complete normalized identity of one stored dependency row. */
498
+ function storedDependencyIdentityKey(value) {
499
+ const sourceKind = value.source_kind?.trim().toLowerCase() ?? "";
500
+ const author = value.author?.trim().toLowerCase() ?? "";
501
+ const rawCreatedAt = value.created_at?.trim() ?? "";
502
+ const parsedCreatedAt = Date.parse(rawCreatedAt);
503
+ const createdAt = Number.isFinite(parsedCreatedAt)
504
+ ? new Date(parsedCreatedAt).toISOString()
505
+ : rawCreatedAt;
506
+ return [
507
+ value.id.trim().toLowerCase(),
508
+ value.kind,
509
+ sourceKind,
510
+ author,
511
+ createdAt,
512
+ ].join("\u0000");
477
513
  }
478
514
  // pm-kyd6: `--blocked-by` writes the `blocked_by` scalar, but the dependency
479
515
  // graph (`pm deps`) is built only from the `dependencies` array. Mirror the
@@ -568,6 +604,17 @@ function matchesDependencySelector(value, selector) {
568
604
  selector.source_kind.trim().toLowerCase()) {
569
605
  return false;
570
606
  }
607
+ if (selector.author !== undefined &&
608
+ value.author?.trim().toLowerCase() !== selector.author.trim().toLowerCase()) {
609
+ return false;
610
+ }
611
+ if (selector.created_at !== undefined &&
612
+ (value.created_at === undefined ||
613
+ !Number.isFinite(Date.parse(value.created_at)) ||
614
+ new Date(Date.parse(value.created_at)).toISOString() !==
615
+ selector.created_at)) {
616
+ return false;
617
+ }
571
618
  return true;
572
619
  }
573
620
  function ensurePriority(raw) {
@@ -1082,6 +1129,19 @@ async function buildNoopUpdateResult(params) {
1082
1129
  warnings: ["noop_no_update_fields"],
1083
1130
  };
1084
1131
  }
1132
+ async function loadUpdateRelationshipGraph(params) {
1133
+ if (!params.fieldFlags.dep &&
1134
+ !params.fieldFlags.blockedBy &&
1135
+ !params.fieldFlags.parent &&
1136
+ !params.clearedMetadataKeys.has("parent"))
1137
+ return undefined;
1138
+ return listAllItemMetadataLight(params.pmRoot, params.settings.item_format, params.typeToFolder, undefined, params.settings.schema);
1139
+ }
1140
+ function assertUpdateHierarchyIntegrity(params) {
1141
+ if (!params.before)
1142
+ return;
1143
+ 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());
1144
+ }
1085
1145
  async function assertUpdateTrackerInitialized(pmRoot) {
1086
1146
  await assertInitializedTracker(pmRoot);
1087
1147
  }
@@ -1315,16 +1375,16 @@ function applyTypeOptionMutation(document, context, activeTypeName, changedField
1315
1375
  /** Collapse duplicate rows only for identities explicitly touched by this mutation. */
1316
1376
  function normalizeTouchedDependencyIdentities(current, additions) {
1317
1377
  const touchedKeys = new Set(additions.map((entry) => dependencyKey(entry)));
1318
- const retainedTouchedKeys = new Set();
1378
+ const retainedStoredIdentities = new Set();
1319
1379
  return current.filter((entry) => {
1320
- const key = dependencyKey(entry);
1321
- if (!touchedKeys.has(key)) {
1380
+ if (!touchedKeys.has(dependencyKey(entry))) {
1322
1381
  return true;
1323
1382
  }
1324
- if (retainedTouchedKeys.has(key)) {
1383
+ const storedIdentity = storedDependencyIdentityKey(entry);
1384
+ if (retainedStoredIdentities.has(storedIdentity)) {
1325
1385
  return false;
1326
1386
  }
1327
- retainedTouchedKeys.add(key);
1387
+ retainedStoredIdentities.add(storedIdentity);
1328
1388
  return true;
1329
1389
  });
1330
1390
  }
@@ -1342,11 +1402,13 @@ function assertDependencyRemovalSelectorsMatch(current, selectors, itemId) {
1342
1402
  id: entry.id,
1343
1403
  kind: entry.kind,
1344
1404
  source_kind: entry.source_kind,
1405
+ author: entry.author,
1406
+ created_at: entry.created_at,
1345
1407
  })),
1346
1408
  why: "Failing on a zero-match selector prevents a mistyped dependency removal from being reported as successful.",
1347
1409
  nextSteps: [
1348
1410
  `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.",
1411
+ "Retry --dep-remove with the exact id plus any kind, source_kind, author, or created_at selectors needed to identify one stored row.",
1350
1412
  ],
1351
1413
  });
1352
1414
  }
@@ -1857,82 +1919,108 @@ async function runUpdateWithContext(id, options, global, execution = {}) {
1857
1919
  return routedClose;
1858
1920
  }
1859
1921
  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({
1922
+ const relationshipRegistry = resolveWorkspaceRelationshipKindRegistry();
1923
+ const hierarchyMutationRequired = (fieldFlags.parent && !clearItemMetadataKeys.has("parent")) ||
1924
+ dependencyUpdates.additions.some((dependency) => relationshipRegistry.resolve(dependency.kind)?.hierarchy);
1925
+ const releaseHierarchyMutationLock = await acquireHierarchyMutationLock({
1866
1926
  pmRoot,
1867
- settings,
1868
- typeToFolder: typeRegistry.type_to_folder,
1869
- id,
1870
- op: resolveUpdateHistoryOperation(options, execution),
1871
1927
  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
- },
1928
+ ttlSeconds: settings.locks.ttl_seconds,
1929
+ waitMs: settings.locks.wait_ms,
1930
+ required: hierarchyMutationRequired,
1918
1931
  });
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
- };
1932
+ try {
1933
+ let graphBeforeUpdate = await loadUpdateRelationshipGraph({
1934
+ fieldFlags,
1935
+ clearedMetadataKeys: clearItemMetadataKeys,
1936
+ pmRoot,
1937
+ settings,
1938
+ typeToFolder: typeRegistry.type_to_folder,
1939
+ });
1940
+ let lifecycleTransition;
1941
+ let recurrenceHistoryContext;
1942
+ const result = await mutateItemWithHistoryContextResolver({
1943
+ pmRoot,
1944
+ settings,
1945
+ typeToFolder: typeRegistry.type_to_folder,
1946
+ id,
1947
+ op: resolveUpdateHistoryOperation(options, execution),
1948
+ author,
1949
+ message: options.message,
1950
+ resolveHistoryContext: () => recurrenceHistoryContext,
1951
+ force: options.force,
1952
+ bypassAssigneeConflict: options.ownershipMetadataBypass === true ||
1953
+ options.ownershipDependencyBypass === true,
1954
+ extensionFieldNames,
1955
+ skipNoop: true,
1956
+ mutate(document) {
1957
+ lifecycleTransition = prepareRecurrenceTransition({
1958
+ document,
1959
+ recurrenceReason: execution.recurrenceReason,
1960
+ requestedStatus: options.status,
1961
+ statusRegistry,
1962
+ });
1963
+ recurrenceHistoryContext = lifecycleTransition
1964
+ ? { recurrence: lifecycleTransition }
1965
+ : undefined;
1966
+ graphBeforeUpdate = graphBeforeUpdate?.map((item) => item.id === document.metadata.id
1967
+ ? structuredClone(document.metadata)
1968
+ : item);
1969
+ const mutation = mutateUpdateDocument(document, {
1970
+ options,
1971
+ settings,
1972
+ typeRegistry,
1973
+ statusRegistry,
1974
+ runtimeFieldRegistry,
1975
+ extensionRegistrations,
1976
+ extensionFieldNames,
1977
+ clearItemMetadataKeys,
1978
+ dependencyUpdates,
1979
+ dependencyRemovals,
1980
+ commentUpdates,
1981
+ noteUpdates,
1982
+ learningUpdates,
1983
+ fileUpdates,
1984
+ testUpdates,
1985
+ docUpdates,
1986
+ resolvedParentValue: parentReference.resolvedParentValue,
1987
+ resolvedBlockedByDependencyId,
1988
+ runtimeFieldUpdates,
1989
+ nowValue,
1990
+ nowIso,
1991
+ author,
1992
+ pmRoot,
1993
+ });
1994
+ assertUpdateHierarchyIntegrity({
1995
+ before: graphBeforeUpdate,
1996
+ document,
1997
+ statusRegistry,
1998
+ });
1999
+ return mutation;
2000
+ },
2001
+ });
2002
+ await releaseHierarchyMutationLock();
2003
+ const orderingCycleWarnings = graphBeforeUpdate
2004
+ ? collectNewOrderingCycleWarnings(graphBeforeUpdate, graphBeforeUpdate.map((item) => item.id === result.item.id ? result.item : item), result.item.id)
2005
+ : [];
2006
+ return {
2007
+ item: toItemRecord(result.item),
2008
+ changed_fields: result.changedFields,
2009
+ warnings: [
2010
+ ...workflowTransitionWarnings,
2011
+ ...parentReferenceWarnings,
2012
+ ...dependencyTargetWarnings,
2013
+ ...orderingCycleWarnings,
2014
+ ...result.warnings,
2015
+ ],
2016
+ ...(lifecycleTransition === undefined
2017
+ ? {}
2018
+ : { lifecycle_transition: lifecycleTransition }),
2019
+ };
2020
+ }
2021
+ finally {
2022
+ await releaseHierarchyMutationLock();
2023
+ }
1936
2024
  }
1937
2025
  /** Resolve the immutable operation name for ordinary, bypass, and recurrence updates. */
1938
2026
  function resolveUpdateHistoryOperation(options, execution) {
@@ -2021,4 +2109,4 @@ export const _testOnlyUpdateCommand = {
2021
2109
  resolveRuntimeUnsetDefinition: (token, registry) => resolveRuntimeUnsetFieldDefinition(token, "update", registry),
2022
2110
  };
2023
2111
  //# sourceMappingURL=update.js.map
2024
- //# debugId=d024d9cf-81f8-5690-b8f4-03620e3e9bf8
2112
+ //# debugId=7c1729f8-4a67-5746-a1bf-351f862ceb66
@@ -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
@@ -5,6 +5,7 @@ import { type RuntimeStatusRegistry } from "../../core/schema/runtime-schema.js"
5
5
  import type { GlobalOptions } from "../../core/shared/command-types.js";
6
6
  import type { ItemMetadata, ItemStatus } from "../../types/index.js";
7
7
  import type { SharedItemFilterOptions } from "./item-filter-options.js";
8
+ import { type HierarchyRelationIndexes } from "../graph/hierarchy-integrity.js";
8
9
  import { type ListCommandName, type ListProjectionMode } from "./projection-contracts.js";
9
10
  export { BRIEF_LIST_FIELDS, DEFAULT_COMPACT_LIST_FIELDS, LIST_COMMAND_DEFAULT_PROJECTIONS, listListProjectionFields, type ListCommandName, type ListProjectionMode, } from "./projection-contracts.js";
10
11
  /** Documents the list options payload exchanged by command, SDK, and package integrations. */
@@ -236,10 +237,10 @@ declare function normalizeProjectionField(field: string): string;
236
237
  declare function parseSortField(raw: string | undefined): ListSortField | undefined;
237
238
  declare function parseSortOrder(raw: string | undefined): ListSortOrder | undefined;
238
239
  declare function parseAssigneeFilter(raw: string | undefined): "assigned" | "unassigned" | undefined;
239
- declare function applyFilters(items: ListedItem[], status: ItemStatus[] | undefined, options: ListOptions, typeRegistry: ItemTypeRegistry, statusRegistry: RuntimeStatusRegistry, runtimeFieldFilters: Record<string, unknown>): ListedItem[];
240
+ declare function applyFilters(items: ListedItem[], status: ItemStatus[] | undefined, options: ListOptions, typeRegistry: ItemTypeRegistry, statusRegistry: RuntimeStatusRegistry, runtimeFieldFilters: Record<string, unknown>, parentsByChild?: HierarchyRelationIndexes["parents_by_child"]): ListedItem[];
240
241
  declare function trimNonEmpty(value: string | undefined): string | undefined;
241
- declare function withTreeMetadata(item: ListedItem, depth: number, childCount: number): ListTreeItem;
242
- declare function orderItemsAsTree(sortedItems: ListedItem[], parentRoot: string | undefined, maxDepth: number | undefined): ListedItem[];
242
+ declare function withTreeMetadata(item: ListedItem, depth: number, childCount: number, parentId?: string): ListTreeItem;
243
+ declare function orderItemsAsTree(sortedItems: ListedItem[], parentRoot: string | undefined, maxDepth: number | undefined, hierarchyIndexes?: HierarchyRelationIndexes, completeItems?: readonly ListedItem[]): ListedItem[];
243
244
  declare function compareNullableString(left: string | null, right: string | null): number;
244
245
  declare function compareNullableTimestamp(left: string | null, right: string | null): number;
245
246
  declare function compareBySortField(left: ListedItem, right: ListedItem, field: ListSortField): number;