@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.
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +24 -0
- package/dist/cli/error-guidance.d.ts +23 -35
- package/dist/cli/error-guidance.js +4 -3
- package/dist/cli/expected-revisions.d.ts +55 -0
- package/dist/cli/expected-revisions.js +2 -0
- package/dist/cli/main.js +2 -2
- package/dist/cli/runtime/field-route-retry.d.ts +15 -0
- package/dist/cli/runtime/field-route-retry.js +2 -0
- package/dist/cli/runtime/invocation-options.js +1 -1
- package/dist/cli-bundle/bundle-manifest.json +1 -1
- package/dist/cli-bundle/chunks/{chunk-3A4NVHYS.js → chunk-2GW662DP.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-3OXQGGRD.js +115 -0
- package/dist/cli-bundle/chunks/chunk-5BVZ32G7.js +2 -0
- package/dist/cli-bundle/chunks/chunk-5S6RT5LX.js +9 -0
- package/dist/cli-bundle/chunks/chunk-5Z6LWFDL.js +31 -0
- package/dist/cli-bundle/chunks/{chunk-RGHLQGEX.js → chunk-6T2V3UIK.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-JRXD7SIL.js → chunk-7PGNAHNW.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-7RCI7AAA.js +20 -0
- package/dist/cli-bundle/chunks/chunk-AQ5UHHQS.js +2 -0
- package/dist/cli-bundle/chunks/chunk-BK2XAQVC.js +2 -0
- package/dist/cli-bundle/chunks/{chunk-ABFFRQRO.js → chunk-BRIPGUAO.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-BWAXA6S4.js +6 -0
- package/dist/cli-bundle/chunks/{chunk-HOGKWJU5.js → chunk-CERNIZLB.js} +66 -66
- package/dist/cli-bundle/chunks/chunk-DSCFJTHB.js +122 -0
- package/dist/cli-bundle/chunks/{chunk-VEOFWE32.js → chunk-L4BKTPJ6.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-NGRPD3ZO.js → chunk-NPEDFOVQ.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-J3JM36EH.js → chunk-O4ZLEN4T.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-533L6D2N.js → chunk-ODMJ3HIB.js} +3 -3
- package/dist/cli-bundle/chunks/{chunk-KJLGBLR3.js → chunk-QN7VCOUU.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-QQHMNUZT.js +2 -0
- package/dist/cli-bundle/chunks/{chunk-D52QQ7LP.js → chunk-RSAN2YUU.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-SRDO2NJU.js +2 -0
- package/dist/cli-bundle/chunks/chunk-T56MB6SF.js +2 -0
- package/dist/cli-bundle/chunks/chunk-TX2W6G37.js +3 -0
- package/dist/cli-bundle/chunks/{chunk-GK2CHAT3.js → chunk-UJMQP2HP.js} +2 -2
- package/dist/cli-bundle/chunks/{indexed-receipt-blobs-IEJ2QOW3.js → indexed-receipt-blobs-I6RQGD6Y.js} +2 -2
- package/dist/cli-bundle/chunks/item-revision-UAZOBP2Q.js +2 -0
- package/dist/cli-bundle/chunks/item-transaction-N5F4LXYV.js +2 -0
- package/dist/cli-bundle/chunks/mutation-admission-QZQI2NN5.js +2 -0
- package/dist/cli-bundle/chunks/{register-list-query-NZTK3AXA.js → register-list-query-P3NOKQUA.js} +2 -2
- package/dist/cli-bundle/chunks/{register-mutation-2OFAMOVH.js → register-mutation-A6I5K5N6.js} +2 -2
- package/dist/cli-bundle/chunks/register-operations-SPKB3TNM.js +2 -0
- package/dist/cli-bundle/chunks/register-setup-ZZRHJ4IS.js +2 -0
- package/dist/cli-bundle/chunks/{settlement-hook-UTCEZBFT.js → settlement-hook-JHXLUBEN.js} +2 -2
- package/dist/cli-bundle/chunks/{stage-VLI4OBVX.js → stage-L3T725DS.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-5NGNO54M.js +44 -0
- package/dist/cli-bundle/focused-chunks/{chunk-PCCZ72C3.js → chunk-A2246ZM7.js} +54 -54
- package/dist/cli-bundle/focused-chunks/chunk-B6U44WPW.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-BAW3DGQO.js +91 -0
- package/dist/cli-bundle/focused-chunks/chunk-BHNY3KPJ.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-C3O6JJPO.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-745IDMTD.js → chunk-C7UX2ZI3.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-CBG67HUP.js +23 -0
- package/dist/cli-bundle/focused-chunks/{chunk-NJFMG3IK.js → chunk-CO2DNPH7.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-CXTDEQ3N.js +8 -0
- package/dist/cli-bundle/focused-chunks/chunk-FEPE2CE4.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-FFO7RBLN.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-QNXXOFLQ.js → chunk-FXFDVM5O.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-QVFE5LIX.js → chunk-G6ERMFJ5.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-I5LGPUWH.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-MQVBWFJF.js → chunk-IDAYTYNN.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-J3YOATXR.js +80 -0
- package/dist/cli-bundle/focused-chunks/{chunk-3UD6F2KQ.js → chunk-J6FDCAMH.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-J7M52SCZ.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-JQS7TYQ6.js +12 -0
- package/dist/cli-bundle/focused-chunks/chunk-MEJKELWC.js +26 -0
- package/dist/cli-bundle/focused-chunks/chunk-PMKR4MJI.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-PNPYQDHU.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-Q4OIRSKL.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-I4Z2SCV5.js → chunk-QEVJ3Y67.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-Q23K6WLV.js → chunk-SDLIUZEL.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-TFYTTDAK.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-W3CSX6GI.js +3 -0
- package/dist/cli-bundle/focused-chunks/{chunk-B6OOKKOX.js → chunk-Y5GOUUYJ.js} +3 -3
- package/dist/cli-bundle/focused-chunks/{indexed-receipt-blobs-FT3KF6VJ.js → indexed-receipt-blobs-7XGVHYAO.js} +2 -2
- package/dist/cli-bundle/focused-chunks/item-revision-L6GVT42D.js +2 -0
- package/dist/cli-bundle/focused-chunks/item-transaction-GYGP264N.js +2 -0
- package/dist/cli-bundle/focused-chunks/mutation-admission-UWTH56OD.js +2 -0
- package/dist/cli-bundle/focused-chunks/{settlement-hook-KMIBD4F2.js → settlement-hook-H56537UV.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{stage-6B3O2GZ6.js → stage-WRSNJ7VI.js} +2 -2
- package/dist/cli-bundle/main.js +30 -29
- package/dist/cli-bundle/sdk-authoring.js +1 -1
- package/dist/cli-bundle/sdk-contracts.js +5 -5
- package/dist/cli-bundle/sdk-core.js +1 -1
- package/dist/cli-bundle/sdk-governance.js +1 -1
- package/dist/cli-bundle/sdk-graph.js +1 -1
- package/dist/cli-bundle/sdk-merge.js +57 -57
- package/dist/cli-bundle/sdk-query.js +1 -1
- package/dist/cli-bundle/sdk-runtime.js +1 -1
- package/dist/cli-bundle/sdk-testing.js +1 -1
- package/dist/cli-bundle/sdk.js +11 -11
- package/dist/core/config/positional-value.d.ts +1 -1
- package/dist/core/config/positional-value.js +1 -1
- package/dist/core/extensions/extension-hook-runtime.js +1 -1
- package/dist/core/extensions/item-fields.d.ts +1 -1
- package/dist/core/extensions/item-fields.js +1 -1
- package/dist/core/extensions/reserved-host-flags.js +1 -1
- package/dist/core/history/history-rewrite.js +1 -1
- package/dist/core/schema/runtime-field-values.d.ts +30 -0
- package/dist/core/schema/runtime-field-values.js +1 -1
- package/dist/core/sentry/helpers.js +1 -1
- package/dist/core/sentry/instrument.js +1 -1
- package/dist/core/shared/command-types.d.ts +7 -0
- package/dist/core/shared/error-details.d.ts +98 -0
- package/dist/core/shared/error-details.js +2 -0
- package/dist/core/shared/errors.d.ts +37 -0
- package/dist/core/shared/errors.js +1 -1
- package/dist/core/store/item-mutation-precondition.d.ts +70 -6
- package/dist/core/store/item-mutation-precondition.js +1 -1
- package/dist/core/store/item-store.js +1 -1
- package/dist/core/store/settings-validator.js +1 -1
- package/dist/core/store/settings.js +1 -1
- package/dist/mcp/server.js +2 -2
- package/dist/sdk/cli-contracts/bootstrap-command-scanner.js +1 -1
- package/dist/sdk/cli-contracts/completeness.js +1 -1
- package/dist/sdk/cli-contracts/enum-contracts.d.ts +11 -0
- package/dist/sdk/cli-contracts/enum-contracts.js +1 -1
- package/dist/sdk/cli-contracts/flag-lexicon-contracts.js +1 -1
- package/dist/sdk/cli-contracts/registration-helpers.js +1 -1
- package/dist/sdk/cli-contracts/tool-parameter-tables.js +1 -1
- package/dist/sdk/cli-contracts/tool-schema.js +1 -1
- package/dist/sdk/cli-program.js +1 -1
- package/dist/sdk/config.d.ts +1 -1
- package/dist/sdk/config.js +1 -1
- package/dist/sdk/environment/project-runtime-compatibility.js +1 -1
- package/dist/sdk/errors.d.ts +6 -2
- package/dist/sdk/errors.js +1 -1
- package/dist/sdk/generated/generated-error-code-catalog-part-1.js +1 -1
- package/dist/sdk/generated/generated-error-code-catalog-part-2.js +1 -1
- package/dist/sdk/governance/validate.js +1 -1
- package/dist/sdk/history/salvage.js +3 -3
- package/dist/sdk/history-compact.d.ts +7 -1
- package/dist/sdk/history-compact.js +1 -1
- package/dist/sdk/history-repair.d.ts +2 -1
- package/dist/sdk/history-repair.js +1 -1
- package/dist/sdk/index.d.ts +4 -1
- package/dist/sdk/index.js +1 -1
- package/dist/sdk/lifecycle/checkpoint-rollback.d.ts +10 -2
- package/dist/sdk/lifecycle/checkpoint-rollback.js +1 -1
- package/dist/sdk/lifecycle/claim.js +1 -1
- package/dist/sdk/lifecycle/close-many.d.ts +11 -2
- package/dist/sdk/lifecycle/close-many.js +1 -1
- package/dist/sdk/lifecycle/close.d.ts +15 -0
- package/dist/sdk/lifecycle/close.js +1 -1
- package/dist/sdk/lifecycle/core-field-recovery.d.ts +26 -0
- package/dist/sdk/lifecycle/core-field-recovery.js +1 -1
- package/dist/sdk/lifecycle/create.js +1 -1
- package/dist/sdk/lifecycle/item-revision.d.ts +162 -0
- package/dist/sdk/lifecycle/item-revision.js +2 -0
- package/dist/sdk/lifecycle/restore.js +1 -1
- package/dist/sdk/lifecycle/start-blockers.d.ts +52 -0
- package/dist/sdk/lifecycle/start-blockers.js +2 -0
- package/dist/sdk/lifecycle/task-composition.d.ts +11 -0
- package/dist/sdk/lifecycle/task-composition.js +1 -1
- package/dist/sdk/lifecycle/update-many.d.ts +11 -2
- package/dist/sdk/lifecycle/update-many.js +1 -1
- package/dist/sdk/lifecycle/update.js +2 -2
- package/dist/sdk/public-surface.json +1 -1
- package/dist/sdk/query/declared-blockers.d.ts +16 -0
- package/dist/sdk/query/declared-blockers.js +2 -0
- package/dist/sdk/query/get.d.ts +7 -0
- package/dist/sdk/query/get.js +1 -1
- package/dist/sdk/query/projection-contracts.js +1 -1
- package/dist/sdk/runtime-input.d.ts +12 -0
- package/dist/sdk/runtime-input.js +1 -1
- package/dist/sdk/runtime.d.ts +11 -1
- package/dist/sdk/runtime.js +1 -1
- package/dist/sdk/structured-mutations.js +1 -1
- package/dist/sdk/test/batch.js +2 -2
- package/dist/sdk/test/entry.d.ts +5 -0
- package/dist/sdk/test/entry.js +2 -2
- package/dist/sdk/test/execution.d.ts +1 -2
- package/dist/sdk/test/execution.js +2 -2
- package/dist/sdk/test/linked-command-detection.d.ts +10 -0
- package/dist/sdk/test/linked-command-detection.js +1 -1
- package/dist/sdk/test/update.d.ts +2 -0
- package/dist/sdk/test/update.js +1 -1
- package/dist/sdk/workspace-transaction.d.ts +17 -6
- package/dist/sdk/workspace-transaction.js +1 -1
- package/dist/types.d.ts +6 -0
- package/dist/types.js +1 -1
- package/docs/COMMANDS.md +91 -0
- package/docs/CONFIGURATION.md +1 -0
- package/docs/EXTENSIONS.md +2 -2
- package/docs/LINKED_TEST_EDITS.md +38 -5
- package/docs/SDK.md +185 -1
- package/docs/agent-task-token-baseline.json +1 -1
- package/docs/generated/FLAG_LEXICON_BUDGETS.md +111 -111
- package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +15 -3
- package/marketplace.json +1 -1
- package/package.json +1 -1
- package/packages/pm-beads/package.json +1 -1
- package/packages/pm-calendar/package.json +1 -1
- package/packages/pm-command-kit/package.json +1 -1
- package/packages/pm-digital-twin/package.json +1 -1
- package/packages/pm-governance-audit/package.json +1 -1
- package/packages/pm-guide-shell/package.json +1 -1
- package/packages/pm-kanban/package.json +1 -1
- package/packages/pm-lifecycle-hooks/package.json +1 -1
- package/packages/pm-linked-test-adapters/package.json +1 -1
- package/packages/pm-search-advanced/package.json +1 -1
- package/packages/pm-templates/package.json +1 -1
- package/packages/pm-todos/package.json +1 -1
- package/packages/pm-vcs/package.json +1 -1
- package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
- package/plugins/pm-claude/package.json +1 -1
- package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/pm-codex/package.json +1 -1
- package/plugins/pm-codex/plugin.json +1 -1
- package/runtime-dependencies.json +1 -1
- package/dist/cli-bundle/chunks/chunk-3ZQQVJT2.js +0 -20
- package/dist/cli-bundle/chunks/chunk-4HW253W2.js +0 -2
- package/dist/cli-bundle/chunks/chunk-4OELCF3O.js +0 -2
- package/dist/cli-bundle/chunks/chunk-BFZUJ5QP.js +0 -115
- package/dist/cli-bundle/chunks/chunk-DAZFCSDV.js +0 -2
- package/dist/cli-bundle/chunks/chunk-EN27ZQSV.js +0 -2
- package/dist/cli-bundle/chunks/chunk-K46GDAKX.js +0 -6
- package/dist/cli-bundle/chunks/chunk-OJR2YGXF.js +0 -31
- package/dist/cli-bundle/chunks/chunk-QHHFXNSQ.js +0 -3
- package/dist/cli-bundle/chunks/chunk-ZNWDYDFF.js +0 -129
- package/dist/cli-bundle/chunks/item-transaction-JHPBH6N7.js +0 -2
- package/dist/cli-bundle/chunks/mutation-admission-QUNQZCZ5.js +0 -2
- package/dist/cli-bundle/chunks/register-operations-RFMM5KZ6.js +0 -2
- package/dist/cli-bundle/chunks/register-setup-6B6Q7M5L.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-4NVPDZLX.js +0 -12
- package/dist/cli-bundle/focused-chunks/chunk-4UUE3YXZ.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-5UDDRPYR.js +0 -8
- package/dist/cli-bundle/focused-chunks/chunk-AK2CI2GI.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-EJJWOFP5.js +0 -91
- package/dist/cli-bundle/focused-chunks/chunk-FUGYXUUL.js +0 -80
- package/dist/cli-bundle/focused-chunks/chunk-HMXVTWIV.js +0 -3
- package/dist/cli-bundle/focused-chunks/chunk-JLN5CFYM.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-K4FMZB4Y.js +0 -23
- package/dist/cli-bundle/focused-chunks/chunk-PF553UVP.js +0 -44
- package/dist/cli-bundle/focused-chunks/chunk-RUCG6UWN.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-SGWYNU7T.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-TGS5RXBY.js +0 -26
- package/dist/cli-bundle/focused-chunks/chunk-WD43E7RU.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-YM4AMP7H.js +0 -2
- package/dist/cli-bundle/focused-chunks/item-transaction-F4SN7TXJ.js +0 -2
- 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]="
|
|
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
|
package/docs/CONFIGURATION.md
CHANGED
|
@@ -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`) |
|
package/docs/EXTENSIONS.md
CHANGED
|
@@ -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,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
|
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":
|
|
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}
|