@unbrained/pm-cli 2026.11.2 → 2026.11.4

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 (242) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +24 -0
  3. package/dist/cli/error-guidance.d.ts +23 -35
  4. package/dist/cli/error-guidance.js +4 -3
  5. package/dist/cli/expected-revisions.d.ts +55 -0
  6. package/dist/cli/expected-revisions.js +2 -0
  7. package/dist/cli/main.js +2 -2
  8. package/dist/cli/runtime/field-route-retry.d.ts +15 -0
  9. package/dist/cli/runtime/field-route-retry.js +2 -0
  10. package/dist/cli/runtime/invocation-options.js +1 -1
  11. package/dist/cli-bundle/bundle-manifest.json +1 -1
  12. package/dist/cli-bundle/chunks/{chunk-3A4NVHYS.js → chunk-2GW662DP.js} +2 -2
  13. package/dist/cli-bundle/chunks/chunk-3OXQGGRD.js +115 -0
  14. package/dist/cli-bundle/chunks/chunk-5BVZ32G7.js +2 -0
  15. package/dist/cli-bundle/chunks/chunk-5S6RT5LX.js +9 -0
  16. package/dist/cli-bundle/chunks/chunk-5Z6LWFDL.js +31 -0
  17. package/dist/cli-bundle/chunks/{chunk-RGHLQGEX.js → chunk-6T2V3UIK.js} +2 -2
  18. package/dist/cli-bundle/chunks/{chunk-JRXD7SIL.js → chunk-7PGNAHNW.js} +2 -2
  19. package/dist/cli-bundle/chunks/chunk-7RCI7AAA.js +20 -0
  20. package/dist/cli-bundle/chunks/chunk-AQ5UHHQS.js +2 -0
  21. package/dist/cli-bundle/chunks/chunk-BK2XAQVC.js +2 -0
  22. package/dist/cli-bundle/chunks/{chunk-ABFFRQRO.js → chunk-BRIPGUAO.js} +2 -2
  23. package/dist/cli-bundle/chunks/chunk-BWAXA6S4.js +6 -0
  24. package/dist/cli-bundle/chunks/{chunk-HOGKWJU5.js → chunk-CERNIZLB.js} +66 -66
  25. package/dist/cli-bundle/chunks/chunk-DSCFJTHB.js +122 -0
  26. package/dist/cli-bundle/chunks/{chunk-VEOFWE32.js → chunk-L4BKTPJ6.js} +2 -2
  27. package/dist/cli-bundle/chunks/{chunk-NGRPD3ZO.js → chunk-NPEDFOVQ.js} +2 -2
  28. package/dist/cli-bundle/chunks/{chunk-J3JM36EH.js → chunk-O4ZLEN4T.js} +2 -2
  29. package/dist/cli-bundle/chunks/{chunk-533L6D2N.js → chunk-ODMJ3HIB.js} +3 -3
  30. package/dist/cli-bundle/chunks/{chunk-KJLGBLR3.js → chunk-QN7VCOUU.js} +2 -2
  31. package/dist/cli-bundle/chunks/chunk-QQHMNUZT.js +2 -0
  32. package/dist/cli-bundle/chunks/{chunk-D52QQ7LP.js → chunk-RSAN2YUU.js} +2 -2
  33. package/dist/cli-bundle/chunks/chunk-SRDO2NJU.js +2 -0
  34. package/dist/cli-bundle/chunks/chunk-T56MB6SF.js +2 -0
  35. package/dist/cli-bundle/chunks/chunk-TX2W6G37.js +3 -0
  36. package/dist/cli-bundle/chunks/{chunk-GK2CHAT3.js → chunk-UJMQP2HP.js} +2 -2
  37. package/dist/cli-bundle/chunks/{indexed-receipt-blobs-IEJ2QOW3.js → indexed-receipt-blobs-I6RQGD6Y.js} +2 -2
  38. package/dist/cli-bundle/chunks/item-revision-UAZOBP2Q.js +2 -0
  39. package/dist/cli-bundle/chunks/item-transaction-N5F4LXYV.js +2 -0
  40. package/dist/cli-bundle/chunks/mutation-admission-QZQI2NN5.js +2 -0
  41. package/dist/cli-bundle/chunks/{register-list-query-NZTK3AXA.js → register-list-query-P3NOKQUA.js} +2 -2
  42. package/dist/cli-bundle/chunks/{register-mutation-2OFAMOVH.js → register-mutation-A6I5K5N6.js} +2 -2
  43. package/dist/cli-bundle/chunks/register-operations-SPKB3TNM.js +2 -0
  44. package/dist/cli-bundle/chunks/register-setup-ZZRHJ4IS.js +2 -0
  45. package/dist/cli-bundle/chunks/{settlement-hook-UTCEZBFT.js → settlement-hook-JHXLUBEN.js} +2 -2
  46. package/dist/cli-bundle/chunks/{stage-VLI4OBVX.js → stage-L3T725DS.js} +2 -2
  47. package/dist/cli-bundle/focused-chunks/chunk-5NGNO54M.js +44 -0
  48. package/dist/cli-bundle/focused-chunks/{chunk-PCCZ72C3.js → chunk-A2246ZM7.js} +54 -54
  49. package/dist/cli-bundle/focused-chunks/chunk-B6U44WPW.js +2 -0
  50. package/dist/cli-bundle/focused-chunks/chunk-BAW3DGQO.js +91 -0
  51. package/dist/cli-bundle/focused-chunks/chunk-BHNY3KPJ.js +2 -0
  52. package/dist/cli-bundle/focused-chunks/chunk-C3O6JJPO.js +2 -0
  53. package/dist/cli-bundle/focused-chunks/{chunk-745IDMTD.js → chunk-C7UX2ZI3.js} +2 -2
  54. package/dist/cli-bundle/focused-chunks/chunk-CBG67HUP.js +23 -0
  55. package/dist/cli-bundle/focused-chunks/{chunk-NJFMG3IK.js → chunk-CO2DNPH7.js} +2 -2
  56. package/dist/cli-bundle/focused-chunks/chunk-CXTDEQ3N.js +8 -0
  57. package/dist/cli-bundle/focused-chunks/chunk-FEPE2CE4.js +2 -0
  58. package/dist/cli-bundle/focused-chunks/chunk-FFO7RBLN.js +2 -0
  59. package/dist/cli-bundle/focused-chunks/{chunk-QNXXOFLQ.js → chunk-FXFDVM5O.js} +2 -2
  60. package/dist/cli-bundle/focused-chunks/{chunk-QVFE5LIX.js → chunk-G6ERMFJ5.js} +2 -2
  61. package/dist/cli-bundle/focused-chunks/chunk-I5LGPUWH.js +2 -0
  62. package/dist/cli-bundle/focused-chunks/{chunk-MQVBWFJF.js → chunk-IDAYTYNN.js} +2 -2
  63. package/dist/cli-bundle/focused-chunks/chunk-J3YOATXR.js +80 -0
  64. package/dist/cli-bundle/focused-chunks/{chunk-3UD6F2KQ.js → chunk-J6FDCAMH.js} +2 -2
  65. package/dist/cli-bundle/focused-chunks/chunk-J7M52SCZ.js +2 -0
  66. package/dist/cli-bundle/focused-chunks/chunk-JQS7TYQ6.js +12 -0
  67. package/dist/cli-bundle/focused-chunks/chunk-MEJKELWC.js +26 -0
  68. package/dist/cli-bundle/focused-chunks/chunk-PMKR4MJI.js +2 -0
  69. package/dist/cli-bundle/focused-chunks/chunk-PNPYQDHU.js +2 -0
  70. package/dist/cli-bundle/focused-chunks/chunk-Q4OIRSKL.js +2 -0
  71. package/dist/cli-bundle/focused-chunks/{chunk-I4Z2SCV5.js → chunk-QEVJ3Y67.js} +2 -2
  72. package/dist/cli-bundle/focused-chunks/{chunk-Q23K6WLV.js → chunk-SDLIUZEL.js} +2 -2
  73. package/dist/cli-bundle/focused-chunks/chunk-TFYTTDAK.js +2 -0
  74. package/dist/cli-bundle/focused-chunks/chunk-W3CSX6GI.js +3 -0
  75. package/dist/cli-bundle/focused-chunks/{chunk-B6OOKKOX.js → chunk-Y5GOUUYJ.js} +3 -3
  76. package/dist/cli-bundle/focused-chunks/{indexed-receipt-blobs-FT3KF6VJ.js → indexed-receipt-blobs-7XGVHYAO.js} +2 -2
  77. package/dist/cli-bundle/focused-chunks/item-revision-L6GVT42D.js +2 -0
  78. package/dist/cli-bundle/focused-chunks/item-transaction-GYGP264N.js +2 -0
  79. package/dist/cli-bundle/focused-chunks/mutation-admission-UWTH56OD.js +2 -0
  80. package/dist/cli-bundle/focused-chunks/{settlement-hook-KMIBD4F2.js → settlement-hook-H56537UV.js} +2 -2
  81. package/dist/cli-bundle/focused-chunks/{stage-6B3O2GZ6.js → stage-WRSNJ7VI.js} +2 -2
  82. package/dist/cli-bundle/main.js +30 -29
  83. package/dist/cli-bundle/sdk-authoring.js +1 -1
  84. package/dist/cli-bundle/sdk-contracts.js +5 -5
  85. package/dist/cli-bundle/sdk-core.js +1 -1
  86. package/dist/cli-bundle/sdk-governance.js +1 -1
  87. package/dist/cli-bundle/sdk-graph.js +1 -1
  88. package/dist/cli-bundle/sdk-merge.js +57 -57
  89. package/dist/cli-bundle/sdk-query.js +1 -1
  90. package/dist/cli-bundle/sdk-runtime.js +1 -1
  91. package/dist/cli-bundle/sdk-testing.js +1 -1
  92. package/dist/cli-bundle/sdk.js +11 -11
  93. package/dist/core/config/positional-value.d.ts +1 -1
  94. package/dist/core/config/positional-value.js +1 -1
  95. package/dist/core/extensions/extension-hook-runtime.js +1 -1
  96. package/dist/core/extensions/item-fields.d.ts +1 -1
  97. package/dist/core/extensions/item-fields.js +1 -1
  98. package/dist/core/extensions/reserved-host-flags.js +1 -1
  99. package/dist/core/history/history-rewrite.js +1 -1
  100. package/dist/core/schema/runtime-field-values.d.ts +30 -0
  101. package/dist/core/schema/runtime-field-values.js +1 -1
  102. package/dist/core/sentry/helpers.js +1 -1
  103. package/dist/core/sentry/instrument.js +1 -1
  104. package/dist/core/shared/command-types.d.ts +7 -0
  105. package/dist/core/shared/error-details.d.ts +98 -0
  106. package/dist/core/shared/error-details.js +2 -0
  107. package/dist/core/shared/errors.d.ts +37 -0
  108. package/dist/core/shared/errors.js +1 -1
  109. package/dist/core/store/item-mutation-precondition.d.ts +70 -6
  110. package/dist/core/store/item-mutation-precondition.js +1 -1
  111. package/dist/core/store/item-store.js +1 -1
  112. package/dist/core/store/settings-validator.js +1 -1
  113. package/dist/core/store/settings.js +1 -1
  114. package/dist/mcp/server.js +2 -2
  115. package/dist/sdk/cli-contracts/bootstrap-command-scanner.js +1 -1
  116. package/dist/sdk/cli-contracts/completeness.js +1 -1
  117. package/dist/sdk/cli-contracts/enum-contracts.d.ts +11 -0
  118. package/dist/sdk/cli-contracts/enum-contracts.js +1 -1
  119. package/dist/sdk/cli-contracts/flag-lexicon-contracts.js +1 -1
  120. package/dist/sdk/cli-contracts/registration-helpers.js +1 -1
  121. package/dist/sdk/cli-contracts/tool-parameter-tables.js +1 -1
  122. package/dist/sdk/cli-contracts/tool-schema.js +1 -1
  123. package/dist/sdk/cli-program.js +1 -1
  124. package/dist/sdk/config.d.ts +1 -1
  125. package/dist/sdk/config.js +1 -1
  126. package/dist/sdk/environment/project-runtime-compatibility.js +1 -1
  127. package/dist/sdk/errors.d.ts +6 -2
  128. package/dist/sdk/errors.js +1 -1
  129. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +1 -1
  130. package/dist/sdk/generated/generated-error-code-catalog-part-2.js +1 -1
  131. package/dist/sdk/governance/validate.js +1 -1
  132. package/dist/sdk/history/salvage.js +3 -3
  133. package/dist/sdk/history-compact.d.ts +7 -1
  134. package/dist/sdk/history-compact.js +1 -1
  135. package/dist/sdk/history-repair.d.ts +2 -1
  136. package/dist/sdk/history-repair.js +1 -1
  137. package/dist/sdk/index.d.ts +4 -1
  138. package/dist/sdk/index.js +1 -1
  139. package/dist/sdk/lifecycle/checkpoint-rollback.d.ts +10 -2
  140. package/dist/sdk/lifecycle/checkpoint-rollback.js +1 -1
  141. package/dist/sdk/lifecycle/claim.js +1 -1
  142. package/dist/sdk/lifecycle/close-many.d.ts +11 -2
  143. package/dist/sdk/lifecycle/close-many.js +1 -1
  144. package/dist/sdk/lifecycle/close.d.ts +15 -0
  145. package/dist/sdk/lifecycle/close.js +1 -1
  146. package/dist/sdk/lifecycle/core-field-recovery.d.ts +26 -0
  147. package/dist/sdk/lifecycle/core-field-recovery.js +1 -1
  148. package/dist/sdk/lifecycle/create.js +1 -1
  149. package/dist/sdk/lifecycle/item-revision.d.ts +162 -0
  150. package/dist/sdk/lifecycle/item-revision.js +2 -0
  151. package/dist/sdk/lifecycle/restore.js +1 -1
  152. package/dist/sdk/lifecycle/start-blockers.d.ts +52 -0
  153. package/dist/sdk/lifecycle/start-blockers.js +2 -0
  154. package/dist/sdk/lifecycle/task-composition.d.ts +11 -0
  155. package/dist/sdk/lifecycle/task-composition.js +1 -1
  156. package/dist/sdk/lifecycle/update-many.d.ts +11 -2
  157. package/dist/sdk/lifecycle/update-many.js +1 -1
  158. package/dist/sdk/lifecycle/update.js +2 -2
  159. package/dist/sdk/public-surface.json +1 -1
  160. package/dist/sdk/query/declared-blockers.d.ts +16 -0
  161. package/dist/sdk/query/declared-blockers.js +2 -0
  162. package/dist/sdk/query/get.d.ts +7 -0
  163. package/dist/sdk/query/get.js +1 -1
  164. package/dist/sdk/query/projection-contracts.js +1 -1
  165. package/dist/sdk/runtime-input.d.ts +12 -0
  166. package/dist/sdk/runtime-input.js +1 -1
  167. package/dist/sdk/runtime.d.ts +11 -1
  168. package/dist/sdk/runtime.js +1 -1
  169. package/dist/sdk/structured-mutations.js +1 -1
  170. package/dist/sdk/test/batch.js +2 -2
  171. package/dist/sdk/test/entry.d.ts +5 -0
  172. package/dist/sdk/test/entry.js +2 -2
  173. package/dist/sdk/test/execution.d.ts +1 -2
  174. package/dist/sdk/test/execution.js +2 -2
  175. package/dist/sdk/test/linked-command-detection.d.ts +10 -0
  176. package/dist/sdk/test/linked-command-detection.js +1 -1
  177. package/dist/sdk/test/update.d.ts +2 -0
  178. package/dist/sdk/test/update.js +1 -1
  179. package/dist/sdk/workspace-transaction.d.ts +17 -6
  180. package/dist/sdk/workspace-transaction.js +1 -1
  181. package/dist/types.d.ts +6 -0
  182. package/dist/types.js +1 -1
  183. package/docs/COMMANDS.md +91 -0
  184. package/docs/CONFIGURATION.md +1 -0
  185. package/docs/EXTENSIONS.md +2 -2
  186. package/docs/LINKED_TEST_EDITS.md +38 -5
  187. package/docs/SDK.md +185 -1
  188. package/docs/agent-task-token-baseline.json +1 -1
  189. package/docs/generated/FLAG_LEXICON_BUDGETS.md +111 -111
  190. package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +15 -3
  191. package/marketplace.json +1 -1
  192. package/package.json +1 -1
  193. package/packages/pm-beads/package.json +1 -1
  194. package/packages/pm-calendar/package.json +1 -1
  195. package/packages/pm-command-kit/package.json +1 -1
  196. package/packages/pm-digital-twin/package.json +1 -1
  197. package/packages/pm-governance-audit/package.json +1 -1
  198. package/packages/pm-guide-shell/package.json +1 -1
  199. package/packages/pm-kanban/package.json +1 -1
  200. package/packages/pm-lifecycle-hooks/package.json +1 -1
  201. package/packages/pm-linked-test-adapters/package.json +1 -1
  202. package/packages/pm-search-advanced/package.json +1 -1
  203. package/packages/pm-templates/package.json +1 -1
  204. package/packages/pm-todos/package.json +1 -1
  205. package/packages/pm-vcs/package.json +1 -1
  206. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  207. package/plugins/pm-claude/package.json +1 -1
  208. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  209. package/plugins/pm-codex/package.json +1 -1
  210. package/plugins/pm-codex/plugin.json +1 -1
  211. package/runtime-dependencies.json +1 -1
  212. package/dist/cli-bundle/chunks/chunk-3ZQQVJT2.js +0 -20
  213. package/dist/cli-bundle/chunks/chunk-4HW253W2.js +0 -2
  214. package/dist/cli-bundle/chunks/chunk-4OELCF3O.js +0 -2
  215. package/dist/cli-bundle/chunks/chunk-BFZUJ5QP.js +0 -115
  216. package/dist/cli-bundle/chunks/chunk-DAZFCSDV.js +0 -2
  217. package/dist/cli-bundle/chunks/chunk-EN27ZQSV.js +0 -2
  218. package/dist/cli-bundle/chunks/chunk-K46GDAKX.js +0 -6
  219. package/dist/cli-bundle/chunks/chunk-OJR2YGXF.js +0 -31
  220. package/dist/cli-bundle/chunks/chunk-QHHFXNSQ.js +0 -3
  221. package/dist/cli-bundle/chunks/chunk-ZNWDYDFF.js +0 -129
  222. package/dist/cli-bundle/chunks/item-transaction-JHPBH6N7.js +0 -2
  223. package/dist/cli-bundle/chunks/mutation-admission-QUNQZCZ5.js +0 -2
  224. package/dist/cli-bundle/chunks/register-operations-RFMM5KZ6.js +0 -2
  225. package/dist/cli-bundle/chunks/register-setup-6B6Q7M5L.js +0 -2
  226. package/dist/cli-bundle/focused-chunks/chunk-4NVPDZLX.js +0 -12
  227. package/dist/cli-bundle/focused-chunks/chunk-4UUE3YXZ.js +0 -2
  228. package/dist/cli-bundle/focused-chunks/chunk-5UDDRPYR.js +0 -8
  229. package/dist/cli-bundle/focused-chunks/chunk-AK2CI2GI.js +0 -2
  230. package/dist/cli-bundle/focused-chunks/chunk-EJJWOFP5.js +0 -91
  231. package/dist/cli-bundle/focused-chunks/chunk-FUGYXUUL.js +0 -80
  232. package/dist/cli-bundle/focused-chunks/chunk-HMXVTWIV.js +0 -3
  233. package/dist/cli-bundle/focused-chunks/chunk-JLN5CFYM.js +0 -2
  234. package/dist/cli-bundle/focused-chunks/chunk-K4FMZB4Y.js +0 -23
  235. package/dist/cli-bundle/focused-chunks/chunk-PF553UVP.js +0 -44
  236. package/dist/cli-bundle/focused-chunks/chunk-RUCG6UWN.js +0 -2
  237. package/dist/cli-bundle/focused-chunks/chunk-SGWYNU7T.js +0 -2
  238. package/dist/cli-bundle/focused-chunks/chunk-TGS5RXBY.js +0 -26
  239. package/dist/cli-bundle/focused-chunks/chunk-WD43E7RU.js +0 -2
  240. package/dist/cli-bundle/focused-chunks/chunk-YM4AMP7H.js +0 -2
  241. package/dist/cli-bundle/focused-chunks/item-transaction-F4SN7TXJ.js +0 -2
  242. package/dist/cli-bundle/focused-chunks/mutation-admission-7R2FXJ5O.js +0 -2
package/dist/types.d.ts CHANGED
@@ -89,6 +89,10 @@ export type GovernanceCloseValidationDefault = (typeof GOVERNANCE_CLOSE_VALIDATI
89
89
  export declare const GOVERNANCE_WORKFLOW_ENFORCEMENT_VALUES:readonly ["off","warn","strict"];
90
90
  /** Restricts governance workflow enforcement values accepted by command, SDK, and storage contracts. */
91
91
  export type GovernanceWorkflowEnforcement = (typeof GOVERNANCE_WORKFLOW_ENFORCEMENT_VALUES)[number];
92
+ /** Supported values for starting an item whose declared blocker is still open. */
93
+ export declare const GOVERNANCE_START_BLOCKER_ENFORCEMENT_VALUES:readonly ["off","warn","strict"];
94
+ /** Start-with-open-blockers admission mode: refuse (`strict`, default), warn, or ignore. */
95
+ export type GovernanceStartBlockerEnforcement = (typeof GOVERNANCE_START_BLOCKER_ENFORCEMENT_VALUES)[number];
92
96
  /** Supported create/copy near-duplicate governance modes. */
93
97
  export declare const GOVERNANCE_DUPLICATE_DETECTION_MODE_VALUES:readonly ["off","advisory","strict"];
94
98
  /** Restricts create/copy near-duplicate governance behavior. */
@@ -945,6 +949,8 @@ force_required_for_stale_lock:boolean;
945
949
  create_default_type?:string;
946
950
  /** Per-type allowed-transition enforcement mode for `pm update --status`. Read raw from settings (not preset-derived) so existing projects are unaffected when unset; defaults to "off". */
947
951
  workflow_enforcement?:GovernanceWorkflowEnforcement;
952
+ /** Admission for moving an item with an open declared blocker into progress. Read raw (not preset-derived); unset means "strict". */
953
+ start_blocker_enforcement?:GovernanceStartBlockerEnforcement;
948
954
  /** Create/copy near-duplicate enforcement mode. */
949
955
  duplicate_detection_mode:GovernanceDuplicateDetectionMode;
950
956
  /** Inclusive similarity floor on the zero-to-one scale. */
package/dist/types.js CHANGED
@@ -1,2 +1,2 @@
1
- (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="bc25122b-94e0-510e-bdbd-b10e3c255d68")}catch{}})();const BUILTIN_ITEM_TYPE_VALUES=["Epic","Feature","Task","Chore","Issue","Decision","Event","Reminder","Milestone","Meeting","Plan"],ITEM_TYPE_VALUES=BUILTIN_ITEM_TYPE_VALUES,STATUS_VALUES=["draft","open","in_progress","blocked","closed","canceled"],RUNTIME_STATUS_ROLE_VALUES=["draft","active","blocked","terminal","terminal_done","terminal_canceled","default_open","default_close","default_cancel"],RUNTIME_FIELD_TYPE_VALUES=["string","number","boolean","string_array","array","object"],RUNTIME_FIELD_COMMAND_VALUES=["create","update","update_many","list","search","calendar","context"],RUNTIME_UNKNOWN_FIELD_POLICY_VALUES=["allow","warn","reject"],DEPENDENCY_KIND_VALUES=["blocks","parent","child","parent_child","child_of","related","related_to","discovered_from","blocked_by","incident_from","epic","implements","recurs_from","supersedes","task","verifies"],SCOPE_VALUES=["project","global"],RISK_VALUES=["low","medium","high","critical"],ISSUE_SEVERITY_VALUES=["low","medium","high","critical"],CONFIDENCE_TEXT_VALUES=["low","medium","high"],ITEM_FORMAT_VALUES=["toon","json_markdown"],SPRINT_RELEASE_FORMAT_POLICY_VALUES=["warn","strict_error"],PARENT_REFERENCE_POLICY_VALUES=["warn","strict_error"],VALIDATE_METADATA_PROFILE_VALUES=["core","strict","custom"],GOVERNANCE_PRESET_VALUES=["minimal","default","strict","custom"],GOVERNANCE_OWNERSHIP_ENFORCEMENT_VALUES=["none","warn","strict"],GOVERNANCE_CREATE_MODE_DEFAULT_VALUES=["progressive","strict"],GOVERNANCE_CLOSE_VALIDATION_DEFAULT_VALUES=["off","warn","strict"],GOVERNANCE_WORKFLOW_ENFORCEMENT_VALUES=["off","warn","strict"],GOVERNANCE_DUPLICATE_DETECTION_MODE_VALUES=["off","advisory","strict"],VALIDATE_METADATA_REQUIRED_FIELD_VALUES=["author","acceptance_criteria","estimated_minutes","close_reason","reviewer","risk","confidence","sprint","release"],RECURRENCE_FREQUENCY_VALUES=["daily","weekly","monthly","yearly"],RECURRENCE_WEEKDAY_VALUES=["mon","tue","wed","thu","fri","sat","sun"];function weekdayOrderIndex(value){return RECURRENCE_WEEKDAY_VALUES.indexOf(value)}const PLAN_MODE_VALUES=["draft","research","review","approved","executing","paused","completed","superseded"],PLAN_STEP_STATUS_VALUES=["pending","in_progress","completed","blocked","skipped","superseded"],PLAN_HARNESS_VALUES=["codex","claude-code","cursor","generic"],PLAN_STEP_LINK_KIND_VALUES=["related","blocks","blocked_by","depends_on","discovered_from","implements","recurs_from","verifies","supersedes"],CONTEXT_DEPTH_VALUES=["brief","standard","deep","full"],CONTEXT_SECTION_VALUES=["hierarchy","activity","progress","recently_created","unparented","blockers","files","workload","staleness","tests"];export{BUILTIN_ITEM_TYPE_VALUES,CONFIDENCE_TEXT_VALUES,CONTEXT_DEPTH_VALUES,CONTEXT_SECTION_VALUES,DEPENDENCY_KIND_VALUES,GOVERNANCE_CLOSE_VALIDATION_DEFAULT_VALUES,GOVERNANCE_CREATE_MODE_DEFAULT_VALUES,GOVERNANCE_DUPLICATE_DETECTION_MODE_VALUES,GOVERNANCE_OWNERSHIP_ENFORCEMENT_VALUES,GOVERNANCE_PRESET_VALUES,GOVERNANCE_WORKFLOW_ENFORCEMENT_VALUES,ISSUE_SEVERITY_VALUES,ITEM_FORMAT_VALUES,ITEM_TYPE_VALUES,PARENT_REFERENCE_POLICY_VALUES,PLAN_HARNESS_VALUES,PLAN_MODE_VALUES,PLAN_STEP_LINK_KIND_VALUES,PLAN_STEP_STATUS_VALUES,RECURRENCE_FREQUENCY_VALUES,RECURRENCE_WEEKDAY_VALUES,RISK_VALUES,RUNTIME_FIELD_COMMAND_VALUES,RUNTIME_FIELD_TYPE_VALUES,RUNTIME_STATUS_ROLE_VALUES,RUNTIME_UNKNOWN_FIELD_POLICY_VALUES,SCOPE_VALUES,SPRINT_RELEASE_FORMAT_POLICY_VALUES,STATUS_VALUES,VALIDATE_METADATA_PROFILE_VALUES,VALIDATE_METADATA_REQUIRED_FIELD_VALUES,weekdayOrderIndex};
1
+ (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="b181d38e-7120-5d34-98d9-fd9b4619b9d9")}catch{}})();const BUILTIN_ITEM_TYPE_VALUES=["Epic","Feature","Task","Chore","Issue","Decision","Event","Reminder","Milestone","Meeting","Plan"],ITEM_TYPE_VALUES=BUILTIN_ITEM_TYPE_VALUES,STATUS_VALUES=["draft","open","in_progress","blocked","closed","canceled"],RUNTIME_STATUS_ROLE_VALUES=["draft","active","blocked","terminal","terminal_done","terminal_canceled","default_open","default_close","default_cancel"],RUNTIME_FIELD_TYPE_VALUES=["string","number","boolean","string_array","array","object"],RUNTIME_FIELD_COMMAND_VALUES=["create","update","update_many","list","search","calendar","context"],RUNTIME_UNKNOWN_FIELD_POLICY_VALUES=["allow","warn","reject"],DEPENDENCY_KIND_VALUES=["blocks","parent","child","parent_child","child_of","related","related_to","discovered_from","blocked_by","incident_from","epic","implements","recurs_from","supersedes","task","verifies"],SCOPE_VALUES=["project","global"],RISK_VALUES=["low","medium","high","critical"],ISSUE_SEVERITY_VALUES=["low","medium","high","critical"],CONFIDENCE_TEXT_VALUES=["low","medium","high"],ITEM_FORMAT_VALUES=["toon","json_markdown"],SPRINT_RELEASE_FORMAT_POLICY_VALUES=["warn","strict_error"],PARENT_REFERENCE_POLICY_VALUES=["warn","strict_error"],VALIDATE_METADATA_PROFILE_VALUES=["core","strict","custom"],GOVERNANCE_PRESET_VALUES=["minimal","default","strict","custom"],GOVERNANCE_OWNERSHIP_ENFORCEMENT_VALUES=["none","warn","strict"],GOVERNANCE_CREATE_MODE_DEFAULT_VALUES=["progressive","strict"],GOVERNANCE_CLOSE_VALIDATION_DEFAULT_VALUES=["off","warn","strict"],GOVERNANCE_WORKFLOW_ENFORCEMENT_VALUES=["off","warn","strict"],GOVERNANCE_START_BLOCKER_ENFORCEMENT_VALUES=["off","warn","strict"],GOVERNANCE_DUPLICATE_DETECTION_MODE_VALUES=["off","advisory","strict"],VALIDATE_METADATA_REQUIRED_FIELD_VALUES=["author","acceptance_criteria","estimated_minutes","close_reason","reviewer","risk","confidence","sprint","release"],RECURRENCE_FREQUENCY_VALUES=["daily","weekly","monthly","yearly"],RECURRENCE_WEEKDAY_VALUES=["mon","tue","wed","thu","fri","sat","sun"];function weekdayOrderIndex(value){return RECURRENCE_WEEKDAY_VALUES.indexOf(value)}const PLAN_MODE_VALUES=["draft","research","review","approved","executing","paused","completed","superseded"],PLAN_STEP_STATUS_VALUES=["pending","in_progress","completed","blocked","skipped","superseded"],PLAN_HARNESS_VALUES=["codex","claude-code","cursor","generic"],PLAN_STEP_LINK_KIND_VALUES=["related","blocks","blocked_by","depends_on","discovered_from","implements","recurs_from","verifies","supersedes"],CONTEXT_DEPTH_VALUES=["brief","standard","deep","full"],CONTEXT_SECTION_VALUES=["hierarchy","activity","progress","recently_created","unparented","blockers","files","workload","staleness","tests"];export{BUILTIN_ITEM_TYPE_VALUES,CONFIDENCE_TEXT_VALUES,CONTEXT_DEPTH_VALUES,CONTEXT_SECTION_VALUES,DEPENDENCY_KIND_VALUES,GOVERNANCE_CLOSE_VALIDATION_DEFAULT_VALUES,GOVERNANCE_CREATE_MODE_DEFAULT_VALUES,GOVERNANCE_DUPLICATE_DETECTION_MODE_VALUES,GOVERNANCE_OWNERSHIP_ENFORCEMENT_VALUES,GOVERNANCE_PRESET_VALUES,GOVERNANCE_START_BLOCKER_ENFORCEMENT_VALUES,GOVERNANCE_WORKFLOW_ENFORCEMENT_VALUES,ISSUE_SEVERITY_VALUES,ITEM_FORMAT_VALUES,ITEM_TYPE_VALUES,PARENT_REFERENCE_POLICY_VALUES,PLAN_HARNESS_VALUES,PLAN_MODE_VALUES,PLAN_STEP_LINK_KIND_VALUES,PLAN_STEP_STATUS_VALUES,RECURRENCE_FREQUENCY_VALUES,RECURRENCE_WEEKDAY_VALUES,RISK_VALUES,RUNTIME_FIELD_COMMAND_VALUES,RUNTIME_FIELD_TYPE_VALUES,RUNTIME_STATUS_ROLE_VALUES,RUNTIME_UNKNOWN_FIELD_POLICY_VALUES,SCOPE_VALUES,SPRINT_RELEASE_FORMAT_POLICY_VALUES,STATUS_VALUES,VALIDATE_METADATA_PROFILE_VALUES,VALIDATE_METADATA_REQUIRED_FIELD_VALUES,weekdayOrderIndex};
2
2
  //# sourceMappingURL=types.js.map
package/docs/COMMANDS.md CHANGED
@@ -963,6 +963,97 @@ pm release <id>
963
963
  `--author` on claim/release flows, not a metadata update. Use `--force` only when
964
964
  explicitly overriding terminal-state or lock conflicts.
965
965
 
966
+ Starting work respects declared blockers
967
+ ([pm-urzubi](../.agents/pm/issues/pm-urzubi.toon)). `pm update <id> --status
968
+ in_progress`, `pm claim <id> --start`, `pm claim --next --start` and the
969
+ `start-task` action refuse with `start_blocked_by_open_dependency` (exit 4)
970
+ when the item moves into the start status while a `blocked_by` target is still
971
+ open. An item already in that status is not rechecked, so `claim --start` on it
972
+ succeeds. The check runs before any write: the
973
+ explicit `claim <id> --start` judges it under the claim's item lock, and its
974
+ status update judges again under its own lock. When the first check refuses,
975
+ nothing is written. When a blocker opens between the two writes, the start
976
+ refuses and, if it took the item from no owner, releases that claim. The release
977
+ is fenced by assignee and claim principal, so another principal's takeover is
978
+ never cleared; principal is pm's unit of ownership, so a concurrent reclaim by
979
+ the same principal is indistinguishable from the original claim. If the item already had an owner, or that
980
+ release cannot run, the claim stays and the refusal's `why` reports the partial
981
+ completion. The
982
+ refusal lists the open blockers in `details.open_blockers` and suggests the
983
+ exact `pm update` that drops the edge. When no single command can do that
984
+ safely (clearing the `blocked_by` scalar would also drop an independent
985
+ `blocked_by` edge, or a legacy row's short id, alias kind or external locator
986
+ would not match a removal selector; see
987
+ [pm-4yxzvd](../.agents/pm/issues/pm-4yxzvd.toon)), it suggests inspecting the
988
+ item instead. The check uses the effective start status, including the
989
+ registered `in_progress` fallback when the workflow names `open`.
990
+ A terminal blocker never blocks, and an update that removes the blocker in the
991
+ same call is admitted. `--force` starts anyway and records
992
+ `start_blocker_override:<id>:<blockers>`. `governance.start_blocker_enforcement`
993
+ selects `strict` (default), `warn` (start and record
994
+ `start_with_open_blockers:<id>:<blockers>`), or `off`. A blocker whose record
995
+ cannot be judged (an invalid or non-UTF-8 document, an unsupported item format,
996
+ or an ambiguous or conflicting identity) refuses under `strict` with that typed
997
+ error. `warn` and `--force` still admit the start and record
998
+ `start_blocker_unverifiable:<id>:<code>`. Filesystem failures and merge-conflict
999
+ markers always propagate.
1000
+
1001
+ ### Expected revisions (`--expect-hash`)
1002
+
1003
+ Read-decide-write agents guard a write with the revision they decided on
1004
+ ([pm-gh1576](../.agents/pm/features/pm-gh1576.toon)). `pm get <id> --json`
1005
+ reports `item_hash`. The global `--expect-hash <item_hash>` flag makes every
1006
+ locked write the command makes to that item refuse with `item_revision_conflict`
1007
+ (exit 4) if the item changed since:
1008
+
1009
+ ```bash
1010
+ H=$(pm get pm-a1b2 --json | jq -r .item_hash)
1011
+ pm close pm-a1b2 "Delivered by PR 1" --expect-hash "$H"
1012
+ H1=$(pm get pm-a1b2 --json | jq -r .item_hash)
1013
+ H2=$(pm get pm-c3d4 --json | jq -r .item_hash)
1014
+ pm update-many --ids pm-a1b2,pm-c3d4 --priority 1 \
1015
+ --expect-hash "pm-a1b2=$H1" --expect-hash "pm-c3d4=$H2"
1016
+ ```
1017
+
1018
+ A bare hash binds to the command's `<id>` argument (for `plan`, the id after
1019
+ the subcommand; `plan create` has none). `<id>=<item_hash>`
1020
+ (repeatable or comma-separated) names targets explicitly and is required for
1021
+ commands without one item argument. The refusal reports `expected_revision`,
1022
+ `current_revision`, and `details.changes_since` (the newest 10 history entries
1023
+ since the declared revision, with their top-level `fields`). The refused write
1024
+ is suppressed; writes the same command already committed (for example the
1025
+ claim of a composed `claim --start`) stay durable, and the message then says
1026
+ so ("Earlier writes by this same invocation already committed"). Read commands refuse the flag with
1027
+ `expect_hash_requires_mutation`, and an empty value (such as an unset `$H`)
1028
+ refuses with `expect_hash_invalid` instead of running unguarded. Bulk
1029
+ `update-many`/`close-many` fail only stale rows, forward and `--rollback` alike, each
1030
+ carrying `error_code`, `expected_revision`, `current_revision` and
1031
+ `details.changes_since`. When `close` auto-unblocks a guarded dependent whose revision
1032
+ moved, the close stays committed and the command refuses with that dependent's
1033
+ `item_revision_conflict` once the fan-out finishes. `side_effects` names every
1034
+ write that committed first: the close and each dependent already unblocked. `close-many` reports such a row as `closed` (`update-many` closing
1035
+ through `--status`, as `updated`), with the dependent's conflict under
1036
+ `dependent_revision_conflict`. A guarded `restore` refuses to recover
1037
+ over a live file that cannot be read as a document; an unguarded restore still
1038
+ recovers it. `deps` only reads, so it refuses the flag. History maintenance
1039
+ (`history-redact`, `history-repair` including `--salvage-tail`, `history-compact`)
1040
+ is checked under the stream lock. Its bulk forms (`history-compact --ids`,
1041
+ `history-repair --all`) carry the same typed conflict fields on refused rows. An
1042
+ item file whose embedded id differs from its physical file name (even by case on a
1043
+ case-sensitive filesystem) binds no revision: reading or declaring one refuses with
1044
+ `item_identity_conflict` until the identity is repaired. `validate --auto-fix`/`--prune-missing` refuses typed once every fix
1045
+ was attempted. When an extension parser or preflight override rewrites the
1046
+ invocation's tracker or arguments, a guarded invocation refuses with
1047
+ `expect_hash_invocation_rewritten`, since which item it writes cannot be proved.
1048
+ The same refusal covers a parser pass during final dispatch, which must also keep
1049
+ the guard admissible (adding `--background` late refuses with
1050
+ `expect_hash_background_unsupported`). Inventory commands
1051
+ (`package inventory`, `extension inventory`) never write items, so they validate
1052
+ the flag and then refuse with `expect_hash_requires_mutation`. An item the command never wrote produces the
1053
+ warning `expect_hash_unchecked:<id>:no_locked_write_reached_this_item`. MCP
1054
+ tools accept the same tokens as `expectHash`; the SDK exposes
1055
+ `withExpectedItemRevisions` (see [SDK](SDK.md#expected-item-revisions)).
1056
+
966
1057
  ## Logs
967
1058
 
968
1059
  ```bash
@@ -103,6 +103,7 @@ If the existing `settings.json` is valid JSON but fails schema validation (for e
103
103
  | `item_types.definitions[]` | custom item types and type options |
104
104
  | `governance.create_default_type` | default `--type` used by `pm create "title"` when `--type` is omitted (defaults to `Task`); must resolve to a known item type |
105
105
  | `governance.workflow_enforcement` | per-type transition enforcement mode for `pm update --status` (`off` default, `warn`, or `strict`) |
106
+ | `governance.start_blocker_enforcement` | starting an item (`update --status in_progress`, `claim --start`) while a declared `blocked_by` target is open: `strict` (default; refuse unless `--force`), `warn`, or `off` |
106
107
  | `governance.duplicate_detection_mode` | create/copy similarity policy: `off`, `advisory`, or `strict`; the minimal preset uses `off`, while the default and strict presets use `strict` |
107
108
  | `governance.duplicate_detection_threshold` | inclusive likely-duplicate score from `0` through `1` (default `0.8`) |
108
109
  | `governance.duplicate_detection_limit` | maximum matches attached to a create/copy advisory from `0` through `20` (default `3`) |
@@ -306,7 +306,7 @@ Use the public SDK barrel. Do not deep-import from `src/core` or `dist/core`.
306
306
  - `api.extension` is a read-only identity (`name`, `layer`, `version`, `capabilities`, `pm_min_version?`, `pm_max_version?`, `source_package?`) for self-identifying logs and version gating without re-reading the manifest.
307
307
  - `api.registerCommand(definition)` adds package-owned commands; `read_output` opts readers into the [SDK output contract](SDK.md#minimal-command-extension).
308
308
  - `api.registerFlags(command, flags)` adds runtime command flags. A flag may declare `value_type` (canonical; the legacy `type` alias is honored only when `value_type` is absent), `list: true` to accumulate repeated/comma-joined values like core `--tags`, a `default` applied when the flag is omitted, and `value_validation: "strict"` to refuse malformed `number`/`boolean` tokens before the handler runs (the default `"coerce"` forwards the raw token). See [Flag Contracts](SDK.md#flag-contracts).
309
- - `api.registerItemFields(fields)` adds custom metadata fields. Agents can set declared fields with repeatable `pm create --field name=value` and `pm update <id> --field name=value`; undeclared names are rejected. `SchemaFieldDefinition.type` is the same closed union used by `pm schema add-field`: `string | number | boolean | string_array | array | object`. Unsupported values fail at compile time for TypeScript authors and at activation for JavaScript packages, with a did-you-mean hint on typos. `string_array` is the repeatable string collection; `array` and `object` require JSON containers. Active fields support `get`, `list` and `search` projections with `--fields name` or `--fields item.name`, preserving declared value types through `PmClient` and CLI reads. Disabled packages do not contribute selectors. Tracked by [pm-gh1571](../.agents/pm/issues/pm-gh1571.toon).
309
+ - `api.registerItemFields(fields)` adds custom metadata fields. Agents can set declared fields with repeatable `pm create --field name=value` and `pm update <id> --field name=value`; undeclared names are rejected. Extension declarations are matched first; a name that no active extension declares but the project schema does (for example `gate_evidence`) is refused with `project_schema_field_requires_declared_flag`, which names a CLI flag and an SDK option that each set only that field (each is omitted when every spelling is shared with another declared field or with an option of the command itself) and, on the CLI, an executable `suggested_retry_args` that rewrites only that assignment. The guidance follows the invoking scope (a bulk `update-many` row is routed under `update_many`, not `update`), and when the same invocation already sets that field through a declared flag, alias or option, no route or retry is offered, because the retry would carry two values for one field ([pm-wftvem](https://github.com/unbraind/pm-cli/blob/main/.agents/pm/issues/pm-wftvem.toon); tracker record excluded from npm); reserved core keys keep `core_item_field_requires_dedicated_flag`. `SchemaFieldDefinition.type` is the same closed union used by `pm schema add-field`: `string | number | boolean | string_array | array | object`. Unsupported values fail at compile time for TypeScript authors and at activation for JavaScript packages, with a did-you-mean hint on typos. `string_array` is the repeatable string collection; `array` and `object` require JSON containers. Active fields support `get`, `list` and `search` projections with `--fields name` or `--fields item.name`, preserving declared value types through `PmClient` and CLI reads. Disabled packages do not contribute selectors. Tracked by [pm-gh1571](../.agents/pm/issues/pm-gh1571.toon).
310
310
  - Custom fields whose camel-case option name is already owned by a canonical MCP input remain usable through `options.<name>`, but authoring surfaces report the collision instead of allowing silent top-level shadowing. `pm schema add-field`, `pm profile lint`, and `pm package doctor` name the field, canonical `mcp_tool_input` owner, and nested recovery path. SDK authors can preflight the same rule with `resolvePmToolCustomFieldCollision`.
311
311
  - `api.registerItemTypes(types)` adds custom item types.
312
312
  - `api.registerRelationshipKinds(definitions)` adds validated graph semantics. Definitions declare direction, inverse spelling, ordering/precedence, hierarchy, cardinality, lifecycle, aliases, payload schema, compatibility version, and self-edge policy. Active definitions govern CLI, MCP, and SDK dependency mutation admission as well as workspace graph assembly; mutation aliases are stored canonically. Requires the `schema` capability and is governed by the `schema.relationshipkinds` policy surface. See [relationship mutation admission](RELATIONSHIP_GRAPH.md#contract), tracked by [pm-7dsolo](../.agents/pm/issues/pm-7dsolo.toon).
@@ -426,7 +426,7 @@ import { createExtensionTestHarness } from "@unbrained/pm-cli/sdk/testing";
426
426
  import { activateExtensionForTest } from "@unbrained/pm-cli/sdk/testing";
427
427
  ```
428
428
 
429
- Runtime modules use static SDK imports; installed copies receive a host SDK link. Use `createPmCliExpectedError(message, { exitCode, context })` for expected user/action failures from package commands. It creates an `Error` named `PmCliError` with a structural `exitCode`, so separately installed package code still gets expected-error handling and Sentry filtering. Commands that need to render a structured gate report and still fail CI may instead return an object with `exit_code` from `1` through `255`; optional string `code` and `remediation` fields are preserved by the host. The result is rendered normally, and the CLI exits with the declared status. Thrown plain objects also preserve bounded `code` and `remediation` fields in the host error contract.
429
+ Runtime modules use static SDK imports; installed copies receive a host SDK link. Use `createPmCliExpectedError(message, { exitCode, context })` for expected user/action failures from package commands. It creates an `Error` named `PmCliError` with a structural `exitCode`, so separately installed package code still gets expected-error handling and Sentry filtering. Commands that need to render a structured gate report and still fail CI may instead return an object with `exit_code` from `1` through `255`; optional string `code` and `remediation` fields are preserved by the host. The result is rendered normally, and the CLI exits with the declared status. Thrown plain objects also preserve bounded `code` and `remediation` fields in the host error contract; those bounds never split a surrogate pair. Put structured, package-owned facts in `context.details` (plain JSON, at most 4,096 bytes when rendered) rather than spreading them over host fields. The host renders them in `--json`, text and MCP refusals and never sends them to telemetry or Sentry; see [Producer-owned diagnostics](SDK.md#producer-owned-diagnostics-contextdetails).
430
430
  Prefer the `define*` builders for exported registration definitions (`defineCommand`, `defineFlag`, `defineSearchProvider`, `defineAfterCommandHook`, and the matching override/import/export/hook helpers; see ADR [pm-3mph](../.agents/pm/decisions/pm-3mph.toon)). They are zero-cost identity functions that preserve object literal types and contextually type function parameters before the definitions reach `api.register*`; runtime validation remains in the loader, and behavior validation remains in `sdk/testing`.
431
431
  Packages that extend core list or search behavior should import `LIST_FILTER_EXTENSION_FLAG_DEFINITIONS`, `SEARCH_EXTENSION_FLAG_DEFINITIONS`, or `toExtensionFlagDefinitions` from `@unbrained/pm-cli/sdk/authoring` instead of copying CLI option tables. For example: `api.registerFlags("my search", SEARCH_EXTENSION_FLAG_DEFINITIONS)`.
432
432
  The adapter expands aliases into registration-ready definitions and preserves string/boolean behavior, list accumulation, repeatability, requiredness, descriptions, and value names from the canonical CLI contracts. Use `toExtensionFlagDefinitions` with another exported CLI flag contract for a narrower baseline.
@@ -21,11 +21,44 @@ index and metadata field names in `context.linked_test_update`.
21
21
  Use `null` to clear an optional field, such as a note, path or timeout. Scope is
22
22
  required and cannot be cleared. Every entry must retain a nonblank command or path:
23
23
  replace a path-only entry's path rather than clearing its last identity.
24
- Unknown fields, command changes, provenance
25
- changes and editor-stamp overrides are refused. To change the command, remove
26
- and add it through the ordinary provenance-recording workflow. Editing context
27
- or environment controls changes the clone-local trust fingerprint, so any prior
28
- acknowledgment must be reviewed again.
24
+ Unknown fields, provenance changes and editor-stamp overrides are refused with
25
+ `linked_test_patch_fields_unsupported`, which lists the editable fields in
26
+ `recovery.allowed_values`. Editing context or environment controls changes the
27
+ clone-local trust fingerprint, so any prior acknowledgment must be reviewed again.
28
+
29
+ A command is the entry's immutable identity, so a `command` or `cmd` key is
30
+ refused with `linked_test_command_immutable` before anything is written. The
31
+ refusal's `recovery.suggested_retry_args` is the supported replacement
32
+ ([pm-1rfy0y](https://github.com/unbraind/pm-cli/blob/main/.agents/pm/issues/pm-1rfy0y.toon);
33
+ tracker record excluded from npm): one `pm test` invocation
34
+ that removes the selected entry (by its exact command, or by its locked position
35
+ when the entry has no unique command) and adds the new command with the entry's
36
+ other metadata and any other fields from your patch. The replacement records
37
+ fresh provenance in a single `tests_add` history event, leaves every other entry
38
+ untouched, and is appended at the end of the list:
39
+
40
+ ```bash
41
+ pm test <item-id> --update-index 1 --set-json '{"command":"node scripts/check-v2.mjs"}' --json
42
+ # refused: linked_test_command_immutable; run the advertised retry, for example
43
+ pm test <item-id> --remove 'command=node scripts/check.mjs' --add-json '{"command":"node scripts/check-v2.mjs","scope":"project","note":"Verified coverage gate"}'
44
+ ```
45
+
46
+ Repeating the current command, or sending an empty one, is refused with the same
47
+ code and a pointer to the editable fields instead of a replacement. Other fields
48
+ in the same patch are validated before the retry is offered, so an unsupported
49
+ key, `scope: null` or an invalid value refuses instead of being silently dropped.
50
+ When the replacement would have the same identity (command, path, scope and
51
+ context modes) as another entry, the add would be deduplicated away, so no retry
52
+ is offered; the refusal names that entry's `--update-index` and the selected
53
+ entry's `--remove-index` instead. Conflicting `command` and `cmd` values, and a
54
+ replacement that would invoke `pm test-all`, refuse without a retry. Explicit
55
+ `--author`, `--message` and `--force` on the refused edit are carried into the
56
+ retry, so ownership and history attribution match the original request.
57
+ The retry is a separate mutation: if other writers may change the item's tests
58
+ in between, list them again before running it. An atomic replacement under one
59
+ item lock is tracked by
60
+ [pm-5igljl](https://github.com/unbraind/pm-cli/blob/main/.agents/pm/issues/pm-5igljl.toon)
61
+ (tracker record excluded from npm).
29
62
 
30
63
  Exactly one selector must match the current collection under the item lock.
31
64
  Absent selectors and ambiguous command/path matches refuse the whole edit and
package/docs/SDK.md CHANGED
@@ -1307,8 +1307,21 @@ atomic evidence-backed completion tracked by
1307
1307
  commands that must coordinate several SDK mutations. A plan supplies a stable
1308
1308
  transaction id plus ordered steps. Each step can inspect its durable state,
1309
1309
  apply an idempotent forward mutation, and append an idempotent compensation.
1310
- The coordinator serializes transaction writers with one workspace lock and
1310
+ The coordinator serializes transaction coordinators with one workspace lock and
1311
1311
  persists `.agents/pm/transactions/sdk/<transaction-id>.json` after every step.
1312
+ That lock does not exclude ordinary writers ([GH-1620](https://github.com/unbraind/pm-cli/issues/1620)):
1313
+ `update`, `close`, `pm comments --add` and every other SDK, CLI or MCP mutation
1314
+ take per-item locks, so a peer can commit between a step's `inspect()` and
1315
+ `apply()`. A step that decides from what it read guards its write with an
1316
+ [expected item revision](#expected-item-revisions): in `inspect()`, take both
1317
+ the decision data and `item_hash` from one `get()`, then run the write inside
1318
+ `withExpectedItemRevisions()`. The
1319
+ write then refuses with `item_revision_conflict` instead of overwriting the
1320
+ peer's change, and the transaction compensates. The coordinator invokes
1321
+ `compensate()`, including during recovery, without an expected revision.
1322
+ Built-in item compensations detect intervening writes. A custom adapter must
1323
+ fence or reconcile its own compensation, or it can restore a stale value over a
1324
+ peer's write.
1312
1325
  Before invoking a pending step, it also persists that step's durable ownership
1313
1326
  marker. Recovery and compensation therefore distinguish mutations begun by
1314
1327
  this transaction from matching domain state that existed before the attempt.
@@ -1348,6 +1361,102 @@ failures and the journal keeps recovery data. Repeated compensation recognizes
1348
1361
  its durable marker even when a peer adds context afterward. Legacy timestamp
1349
1362
  receipts remain supported, with ambiguous partial effects refused.
1350
1363
 
1364
+ ### Expected item revisions
1365
+
1366
+ Expected-revision preconditions are tracked by
1367
+ [pm-gh1576](../.agents/pm/features/pm-gh1576.toon).
1368
+
1369
+ Read-decide-write agents and packages guard a write with the revision their
1370
+ decision was based on. Every current `pm get` / `PmClient.get` read reports
1371
+ `item_hash`, the SHA-256 of the canonical item document and the same value as
1372
+ the item's latest history `after_hash` (a field-projected read includes it only
1373
+ when the selection names the bare `item_hash`, for example `--fields title,tags,item_hash`;
1374
+ the item-qualified `item.item_hash` still projects a custom metadata field of that name).
1375
+ `readItemRevision(id)` returns it alone. Take the hash from the same read your
1376
+ decision uses: a separate read could observe a newer revision than the data you
1377
+ decided on. Run the write inside `withExpectedItemRevisions`:
1378
+
1379
+ ```ts
1380
+ import { PmClient, isItemRevisionConflict } from "@unbrained/pm-cli/sdk";
1381
+
1382
+ const pm = new PmClient({ pmRoot });
1383
+ // Decide from the same read that reports the revision, never from a separate one.
1384
+ const read = await pm.get(id);
1385
+ const reason = `Delivered by ${read.item.tags?.find((tag) => tag.startsWith("delivery-pr:"))}`;
1386
+ try {
1387
+ const { result, unchecked } = await pm.withExpectedItemRevisions(
1388
+ [{ id, itemHash: read.item_hash! }],
1389
+ () => pm.close(id, reason),
1390
+ );
1391
+ } catch (error) {
1392
+ if (!isItemRevisionConflict(error)) throw error;
1393
+ // error.context.current_revision and error.context.details.changes_since
1394
+ // describe what changed; re-read, decide again, retry with the new hash.
1395
+ }
1396
+ ```
1397
+
1398
+ The check runs inside the locked item store, after the item lock is held and
1399
+ before that write. It therefore covers every item write the wrapped
1400
+ operation makes: lifecycle commands, annotations, linked files/docs/tests,
1401
+ dependency edits, `restore`, `delete`, history maintenance (`history-redact`,
1402
+ `history-repair`, `history-compact`, checked under the stream lock), bulk
1403
+ transactions and extension commands that mutate through the SDK. A `delete`
1404
+ advances the expectation to the item's absent (tombstone) revision, and a
1405
+ `restore` of a deleted item is judged under its own id, so a peer deletion
1406
+ between the read and the restore refuses instead of being resurrected over. A mismatch refuses that write with `item_revision_conflict`
1407
+ (exit 4); writes the operation already committed stay durable. The refusal carries `item_id`, `expected_revision`,
1408
+ `current_revision` and `details` with `changes_since`, the newest
1409
+ 10 history entries after the declared revision
1410
+ (`op`, `author`, `ts`, top-level `fields`), plus `changes_since_omitted`.
1411
+ `expected_revision_recorded: false` means no history entry ever had that hash.
1412
+ After one of the operation's own writes commits, the expectation advances to
1413
+ that revision, so a multi-write composition such as `claim --start` does not
1414
+ refuse itself; an interleaved peer write still refuses the next step. The
1415
+ result lists `checked` ids and `unchecked` ids that the operation never wrote:
1416
+ an unchecked expectation proved nothing. Expectations bind to the item's embedded id, so every
1417
+ spelling that locates the item (a short id, or a case variant on a
1418
+ case-insensitive filesystem) is guarded. The scope is shared process-wide across the
1419
+ published SDK entrypoints (`/sdk`, `/sdk/core`, ...), and
1420
+ `isItemRevisionConflict` checks the refusal structurally, so mixing entrypoints
1421
+ neither drops a guard nor hides its conflict. Trackers are compared by physical
1422
+ path (`realpath`), so a symlinked spelling of the same tracker is guarded too. `PmClient.readItemRevision` and
1423
+ `PmClient.withExpectedItemRevisions` resolve targets inside the client's
1424
+ extension context, so extension item types bind exactly as `get` reads them,
1425
+ then run the operation outside it, so its own actions (extension commands
1426
+ included) activate and dispatch normally. The standalone `readItemRevision` /
1427
+ `withExpectedItemRevisions` functions use the registrations already active in
1428
+ the calling context. A deleted item with retained history binds too: its
1429
+ revision is the empty tombstone its `delete` recorded (the delete entry's
1430
+ `after_hash`), so a guarded `restore` or history maintenance of a deleted item
1431
+ can declare it. Bulk `update-many`
1432
+ and `close-many` keep their partial-success semantics: a stale row fails with
1433
+ `error_code: "item_revision_conflict"` plus its `expected_revision`,
1434
+ `current_revision` and the same `details` (`changes_since`,
1435
+ `changes_since_omitted`) a single-item refusal carries, while fresh rows
1436
+ proceed. When a peer writes while `restore` or history maintenance waits for
1437
+ its lock, a declared revision turns the generic drift refusal into this typed
1438
+ conflict. Linked-test result recording (`test --run`, `test-all` with
1439
+ `test_result_tracking` enabled) stays best-effort, except that a stale declared
1440
+ revision refuses with `item_revision_conflict` instead of a warning. A guard cannot reach a `--background` test worker,
1441
+ which runs in another process, so `test`/`test-all --background` with an
1442
+ expected revision refuses with `expect_hash_background_unsupported`. Only the CLI invocation scope reads `GlobalOptions.expectHash`;
1443
+ direct `run*` SDK calls ignore it, so guard SDK writes with the functions above
1444
+ or `runAction`/MCP `expectHash`.
1445
+
1446
+ Transports use the same grammar. The CLI's global `--expect-hash <item_hash>`
1447
+ binds to the command's `<id>`; `--expect-hash <id>=<item_hash>` (repeatable or
1448
+ comma-separated) names targets explicitly and is required for bulk commands.
1449
+ MCP tools and `PmClient.run` accept `expectHash` (string or string array) on
1450
+ every item-writing action, at the top level or inside `options`; either
1451
+ placement is enforced. A native read action refuses the flag with
1452
+ `expect_hash_requires_mutation`, and a malformed, supplied-but-empty or `null`
1453
+ value (for example an unset shell variable) refuses with `expect_hash_invalid`
1454
+ rather than silently dropping the guard. An expectation no write reached is reported as the
1455
+ warning `expect_hash_unchecked:<id>:no_locked_write_reached_this_item`. This
1456
+ precondition guards the gap between an agent's read and its write; lease
1457
+ fencing ([pm-gh1549](../.agents/pm/issues/pm-gh1549.toon)) still guards the
1458
+ write itself against lock takeover, and the two compose.
1459
+
1351
1460
  ### Schema evolution and workspace history
1352
1461
 
1353
1462
  Tracked by [pm-dijg](../.agents/pm/features/pm-dijg.toon) and
@@ -3129,6 +3238,81 @@ propagates `code` and `remediation` through the command-handler result contract.
3129
3238
  Invalid `exit_code` values are rejected rather than truncated or silently
3130
3239
  treated as success.
3131
3240
 
3241
+ ### Producer-owned diagnostics (`context.details`)
3242
+
3243
+ Tracked by [pm-gh1631](https://github.com/unbraind/pm-cli/blob/main/.agents/pm/issues/pm-gh1631.toon)
3244
+ (tracker record excluded from npm). Host fields such as `code`, `required` and
3245
+ `recovery` describe the refusal in the host's own vocabulary. Facts that belong
3246
+ to the package go in `context.details`, a plain JSON object the host never
3247
+ interprets:
3248
+
3249
+ ```ts
3250
+ import { createPmCliExpectedError } from "@unbrained/pm-cli/sdk/runtime";
3251
+
3252
+ throw createPmCliExpectedError("No changelog section for 2026.11.1", {
3253
+ exitCode: 1,
3254
+ context: {
3255
+ code: "release_notes_missing",
3256
+ details: {
3257
+ selection_report: { filters: { version: "2026.11.1" }, excluded: { status: 1 } },
3258
+ },
3259
+ },
3260
+ });
3261
+ ```
3262
+
3263
+ - **Shape.** Values are `null`, booleans, finite numbers, well-formed strings,
3264
+ arrays and plain objects, nested at most `PM_CLI_ERROR_DETAILS_LIMITS.max_depth`
3265
+ (8) containers deep. `__proto__` keys and accessor properties are refused;
3266
+ getters are never run. `createPmCliExpectedError` throws a `TypeError` whose
3267
+ message names only the reason (`unsupported_value`, `non_finite_number`,
3268
+ `malformed_unicode`, `reserved_key`, `depth_limit`, `not_plain_object`); the
3269
+ offending path is on its non-enumerable `path` property, so crash reporting
3270
+ of that fault never carries your keys. A well-formed value is kept as a
3271
+ detached copy, so later changes to your object do not alter a raised
3272
+ refusal; an oversized one is replaced at construction by the host-set
3273
+ receipt `context.details_omitted: { reason: "byte_limit", byte_limit }`, so a
3274
+ later change cannot alter the refusal either. `isPmCliErrorDetails(value)`
3275
+ performs the same check.
3276
+ - **Rendering.** `--json` errors carry the object as `details`, text output
3277
+ renders it as an indented JSON `Details:` block, and MCP tool errors carry it
3278
+ inside the forwarded context. Diagnostics are rendered whole or not at all:
3279
+ above `PM_CLI_ERROR_DETAILS_LIMITS.max_bytes` (4,096 bytes of compact UTF-8
3280
+ JSON), or when a separately bundled error carries a malformed shape, the
3281
+ envelope carries `details_omitted: { reason, path?, byte_limit }` instead.
3282
+ Admitted details that would push the pretty-printed `--json` envelope past
3283
+ the error diagnostic budget are replaced the same way, with reason
3284
+ `diagnostic_budget`.
3285
+ The size is counted while walking and the walk stops at the ceiling, so
3286
+ shared or repeated structures cannot amplify the work; `path` is capped at
3287
+ 160 bytes. The CLI and MCP read a separately bundled error's context once as
3288
+ plain data (`snapshotPmCliErrorContext`): getters are never run, and a
3289
+ context that cannot be inspected becomes empty. MCP admits `details` from that
3290
+ snapshot with the same bounded walk before serializing anything, then
3291
+ serializes the remaining host fields once (a context-level `toJSON` is
3292
+ ignored), so no serializer can expand or replace the diagnostics; a context
3293
+ JSON cannot encode (for example a bigint member) is forwarded as
3294
+ `{ code, context_unserializable: true }` so the response is still written. When the text output exceeds its token ceiling, the
3295
+ text renderer drops the explanation (`why`) first and then the `Details:`
3296
+ block (together with the example and next-step limits). A block dropped this
3297
+ way gets no `Details omitted:` line, only the generic notice that points to
3298
+ `--json`, which keeps the details. Custom SDK hosts apply the
3299
+ same bound with `projectPmCliErrorDetails(value)` (returns `{ details }`,
3300
+ `{ details_omitted }` or `undefined`) or, for a context forwarded as-is,
3301
+ `projectPmCliErrorContextDetails(context)`.
3302
+ - **Privacy.** Telemetry and Sentry never receive `details`. The telemetry
3303
+ classification is built without it. A crash report carries an error
3304
+ context only as its top-level primitive fields, read without running
3305
+ getters or `toJSON`, so diagnostics cannot reach it under another key: this
3306
+ applies to `error_context` and to the error-data entry, which is rebuilt
3307
+ whole from the original exception (its own primitive fields plus the safe
3308
+ context), because the integration serializes the error, including any
3309
+ error-level `toJSON`, before `beforeSend`. Serialized copies whose original
3310
+ cannot be recovered are discarded: embedded contexts in console-capture
3311
+ data, breadcrumbs and transactions are sent as empty objects, and complete
3312
+ serialized console `arguments` are sent as empty arrays, because an error's
3313
+ own `toJSON` can move diagnostics out of any named slot. The custom-host
3314
+ projections drop a context-level `toJSON` for the same reason.
3315
+
3132
3316
  ## Package Runtime Imports
3133
3317
 
3134
3318
  Third-party packages should import from stable public SDK subpaths:
@@ -1 +1 @@
1
- {"version":5,"transcript_version":2,"transcript_digest":"sha256:b4b151f3d36860e73cd535d0520e9f03b4ecec4444ee81a8b868896b5b0bba47","estimator":"ceil(utf8_bytes / 4)","comparison_scope":"plan_recovery_tracker_root_canonicalized","measurement_scope":"output_before_token_accounting","published_with_release":true,"orientation":{"canonical_task_id":"orientation-context-intent","required_capabilities":["actionable_work","active_ownership","workspace_state"],"measured_winner_tokens":1051,"protocols":[{"task_id":"orientation-context-intent","command_count":1,"estimated_tokens":1051,"capabilities":["actionable_work","active_ownership","workspace_state"],"capability_evidence":{"actionable_work":[{"step_id":"context","field_path":"hierarchy"}],"active_ownership":[{"step_id":"context","field_path":"activity"}],"workspace_state":[{"step_id":"context","field_path":"summary.active_items"}]}},{"task_id":"orientation-operating-rules","command_count":4,"estimated_tokens":2067,"capabilities":["actionable_work","active_ownership","workspace_state"],"capability_evidence":{"actionable_work":[{"step_id":"open","field_path":"items"}],"active_ownership":[{"step_id":"in-progress","field_path":"items"}],"workspace_state":[{"step_id":"context","field_path":"summary.active_items"}]}},{"task_id":"orientation-contracts-next","command_count":2,"estimated_tokens":5213,"capabilities":["actionable_work","active_ownership","workspace_state"],"capability_evidence":{"actionable_work":[{"step_id":"next","field_path":"recommended.id"}],"active_ownership":[{"step_id":"next","field_path":"held_by_others"}],"workspace_state":[{"step_id":"next","field_path":"summary.in_progress"}]}}]},"tasks":[{"id":"orientation-context-intent","max_estimated_tokens":1051,"steps":[{"id":"context","max_estimated_tokens":1051,"accounting_mode":"self_reported"}]},{"id":"orientation-contracts-next","max_estimated_tokens":5213,"steps":[{"id":"contracts","max_estimated_tokens":4481,"accounting_mode":"self_reported"},{"id":"next","max_estimated_tokens":732,"accounting_mode":"self_reported"}]},{"id":"orientation-operating-rules","max_estimated_tokens":2067,"steps":[{"id":"context","max_estimated_tokens":1015,"accounting_mode":"self_reported"},{"id":"search","max_estimated_tokens":164,"accounting_mode":"self_reported"},{"id":"open","max_estimated_tokens":712,"accounting_mode":"self_reported"},{"id":"in-progress","max_estimated_tokens":176,"accounting_mode":"self_reported"}]},{"id":"context-bootstrap","max_estimated_tokens":2005,"steps":[{"id":"triage","max_estimated_tokens":619,"accounting_mode":"self_reported"},{"id":"orient","max_estimated_tokens":1015,"accounting_mode":"self_reported"},{"id":"inspect","max_estimated_tokens":371,"accounting_mode":"self_reported"}]},{"id":"closed-domain-recovery","max_estimated_tokens":630,"steps":[{"id":"refuse-intent","max_estimated_tokens":259,"accounting_mode":"self_reported"},{"id":"retry-intent","max_estimated_tokens":371,"accounting_mode":"self_reported"}]},{"id":"unknown-option-recovery","max_estimated_tokens":596,"steps":[{"id":"refuse-option","max_estimated_tokens":433,"accounting_mode":"self_reported"},{"id":"retry-option","max_estimated_tokens":163,"accounting_mode":"self_reported"}]},{"id":"lifecycle-mutation","max_estimated_tokens":873,"steps":[{"id":"create","max_estimated_tokens":20,"accounting_mode":"self_reported"},{"id":"inspect-open","max_estimated_tokens":363,"accounting_mode":"self_reported"},{"id":"close","max_estimated_tokens":92,"accounting_mode":"self_reported"},{"id":"inspect-closed","max_estimated_tokens":398,"accounting_mode":"self_reported"}]},{"id":"bulk-effect-outcomes","max_estimated_tokens":392,"steps":[{"id":"create-bulk-target","max_estimated_tokens":20,"accounting_mode":"self_reported"},{"id":"partial-effect","max_estimated_tokens":240,"accounting_mode":"self_reported"},{"id":"no-effect","max_estimated_tokens":132,"accounting_mode":"self_reported"}]},{"id":"plan-execution-mutations","max_estimated_tokens":806,"steps":[{"id":"create-plan","max_estimated_tokens":21,"accounting_mode":"self_reported"},{"id":"add-step","max_estimated_tokens":254,"accounting_mode":"self_reported"},{"id":"complete-step","max_estimated_tokens":253,"accounting_mode":"self_reported"},{"id":"inspect-plan","max_estimated_tokens":278,"accounting_mode":"self_reported"}]}],"composite_max_estimated_tokens":13633}
1
+ {"version":5,"transcript_version":2,"transcript_digest":"sha256:b4b151f3d36860e73cd535d0520e9f03b4ecec4444ee81a8b868896b5b0bba47","estimator":"ceil(utf8_bytes / 4)","comparison_scope":"plan_recovery_tracker_root_canonicalized","measurement_scope":"output_before_token_accounting","published_with_release":true,"orientation":{"canonical_task_id":"orientation-context-intent","required_capabilities":["actionable_work","active_ownership","workspace_state"],"measured_winner_tokens":1051,"protocols":[{"task_id":"orientation-context-intent","command_count":1,"estimated_tokens":1051,"capabilities":["actionable_work","active_ownership","workspace_state"],"capability_evidence":{"actionable_work":[{"step_id":"context","field_path":"hierarchy"}],"active_ownership":[{"step_id":"context","field_path":"activity"}],"workspace_state":[{"step_id":"context","field_path":"summary.active_items"}]}},{"task_id":"orientation-operating-rules","command_count":4,"estimated_tokens":2067,"capabilities":["actionable_work","active_ownership","workspace_state"],"capability_evidence":{"actionable_work":[{"step_id":"open","field_path":"items"}],"active_ownership":[{"step_id":"in-progress","field_path":"items"}],"workspace_state":[{"step_id":"context","field_path":"summary.active_items"}]}},{"task_id":"orientation-contracts-next","command_count":2,"estimated_tokens":5213,"capabilities":["actionable_work","active_ownership","workspace_state"],"capability_evidence":{"actionable_work":[{"step_id":"next","field_path":"recommended.id"}],"active_ownership":[{"step_id":"next","field_path":"held_by_others"}],"workspace_state":[{"step_id":"next","field_path":"summary.in_progress"}]}}]},"tasks":[{"id":"orientation-context-intent","max_estimated_tokens":1051,"steps":[{"id":"context","max_estimated_tokens":1051,"accounting_mode":"self_reported"}]},{"id":"orientation-contracts-next","max_estimated_tokens":5213,"steps":[{"id":"contracts","max_estimated_tokens":4481,"accounting_mode":"self_reported"},{"id":"next","max_estimated_tokens":732,"accounting_mode":"self_reported"}]},{"id":"orientation-operating-rules","max_estimated_tokens":2067,"steps":[{"id":"context","max_estimated_tokens":1015,"accounting_mode":"self_reported"},{"id":"search","max_estimated_tokens":164,"accounting_mode":"self_reported"},{"id":"open","max_estimated_tokens":712,"accounting_mode":"self_reported"},{"id":"in-progress","max_estimated_tokens":176,"accounting_mode":"self_reported"}]},{"id":"context-bootstrap","max_estimated_tokens":2025,"steps":[{"id":"triage","max_estimated_tokens":619,"accounting_mode":"self_reported"},{"id":"orient","max_estimated_tokens":1015,"accounting_mode":"self_reported"},{"id":"inspect","max_estimated_tokens":391,"accounting_mode":"self_reported"}]},{"id":"closed-domain-recovery","max_estimated_tokens":650,"steps":[{"id":"refuse-intent","max_estimated_tokens":259,"accounting_mode":"self_reported"},{"id":"retry-intent","max_estimated_tokens":391,"accounting_mode":"self_reported"}]},{"id":"unknown-option-recovery","max_estimated_tokens":596,"steps":[{"id":"refuse-option","max_estimated_tokens":433,"accounting_mode":"self_reported"},{"id":"retry-option","max_estimated_tokens":163,"accounting_mode":"self_reported"}]},{"id":"lifecycle-mutation","max_estimated_tokens":915,"steps":[{"id":"create","max_estimated_tokens":20,"accounting_mode":"self_reported"},{"id":"inspect-open","max_estimated_tokens":384,"accounting_mode":"self_reported"},{"id":"close","max_estimated_tokens":92,"accounting_mode":"self_reported"},{"id":"inspect-closed","max_estimated_tokens":419,"accounting_mode":"self_reported"}]},{"id":"bulk-effect-outcomes","max_estimated_tokens":392,"steps":[{"id":"create-bulk-target","max_estimated_tokens":20,"accounting_mode":"self_reported"},{"id":"partial-effect","max_estimated_tokens":240,"accounting_mode":"self_reported"},{"id":"no-effect","max_estimated_tokens":132,"accounting_mode":"self_reported"}]},{"id":"plan-execution-mutations","max_estimated_tokens":806,"steps":[{"id":"create-plan","max_estimated_tokens":21,"accounting_mode":"self_reported"},{"id":"add-step","max_estimated_tokens":254,"accounting_mode":"self_reported"},{"id":"complete-step","max_estimated_tokens":253,"accounting_mode":"self_reported"},{"id":"inspect-plan","max_estimated_tokens":278,"accounting_mode":"self_reported"}]}],"composite_max_estimated_tokens":13715}