@unbrained/pm-cli 2026.11.3 → 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 +10 -0
- package/dist/cli/error-guidance.d.ts +14 -35
- package/dist/cli/error-guidance.js +2 -2
- 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/invocation-options.js +1 -1
- package/dist/cli-bundle/bundle-manifest.json +1 -1
- package/dist/cli-bundle/chunks/{chunk-UYNMKUGA.js → chunk-2GW662DP.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-32BCU6HN.js → chunk-3OXQGGRD.js} +46 -46
- 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-MQJXZX3Q.js → chunk-6T2V3UIK.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-5JZMF4M5.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-TZ3D3DPQ.js → chunk-BRIPGUAO.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-BWAXA6S4.js +6 -0
- package/dist/cli-bundle/chunks/{chunk-5IPXDZKU.js → chunk-CERNIZLB.js} +66 -66
- package/dist/cli-bundle/chunks/chunk-DSCFJTHB.js +122 -0
- package/dist/cli-bundle/chunks/{chunk-JPKIUWLG.js → chunk-L4BKTPJ6.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-Y7H55PCY.js → chunk-NPEDFOVQ.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-2CBHQXYJ.js → chunk-ODMJ3HIB.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-DEJHTFEF.js → chunk-QN7VCOUU.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-QQHMNUZT.js +2 -0
- 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/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-I6AZA3IX.js → register-list-query-P3NOKQUA.js} +2 -2
- package/dist/cli-bundle/chunks/{register-mutation-3DAXCLXR.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/{stage-7H36OTDJ.js → stage-L3T725DS.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-5NGNO54M.js +44 -0
- package/dist/cli-bundle/focused-chunks/{chunk-ZQRCVRGS.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-LKN2WGGB.js → chunk-C7UX2ZI3.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-YVLAP3BL.js → chunk-CBG67HUP.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-TYHDZUL4.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-RY4QEMAE.js → chunk-FFO7RBLN.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-I5FODKYB.js → chunk-FXFDVM5O.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-I5LGPUWH.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-J3YOATXR.js +80 -0
- package/dist/cli-bundle/focused-chunks/{chunk-HOXIIZEO.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-F3X6UWMI.js → chunk-QEVJ3Y67.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-HJ27QLZV.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-D4XQOMG4.js → chunk-Y5GOUUYJ.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/{stage-4SQHVI3O.js → stage-WRSNJ7VI.js} +2 -2
- package/dist/cli-bundle/main.js +25 -25
- 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/reserved-host-flags.js +1 -1
- package/dist/core/history/history-rewrite.js +1 -1
- package/dist/core/shared/command-types.d.ts +7 -0
- 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/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/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 +3 -0
- 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/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 +10 -0
- 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/execution.js +2 -2
- package/dist/sdk/test/linked-command-detection.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/SDK.md +110 -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 +12 -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-7ZEFHFAU.js +0 -2
- package/dist/cli-bundle/chunks/chunk-B6UTLYLM.js +0 -3
- package/dist/cli-bundle/chunks/chunk-HBKD5HZB.js +0 -2
- package/dist/cli-bundle/chunks/chunk-L6LCLC37.js +0 -2
- package/dist/cli-bundle/chunks/chunk-NR4KABHC.js +0 -31
- package/dist/cli-bundle/chunks/chunk-PWD5RY2D.js +0 -2
- package/dist/cli-bundle/chunks/chunk-XRMYA4R7.js +0 -20
- package/dist/cli-bundle/chunks/chunk-YWS4XV4I.js +0 -6
- package/dist/cli-bundle/chunks/chunk-ZOAJNPBL.js +0 -129
- package/dist/cli-bundle/chunks/item-transaction-ZHLJTBD7.js +0 -2
- package/dist/cli-bundle/chunks/mutation-admission-ZQMRMGIB.js +0 -2
- package/dist/cli-bundle/chunks/register-operations-G3QGGQ5Z.js +0 -2
- package/dist/cli-bundle/chunks/register-setup-LDXHMNOT.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-3XGKBVJ3.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-4HY6EGPB.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-AK2CI2GI.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-HUXZCPWT.js +0 -26
- package/dist/cli-bundle/focused-chunks/chunk-IKXJ57XA.js +0 -8
- package/dist/cli-bundle/focused-chunks/chunk-MOY6KZGJ.js +0 -44
- package/dist/cli-bundle/focused-chunks/chunk-PWUTFKSI.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-PZRB7IXH.js +0 -91
- package/dist/cli-bundle/focused-chunks/chunk-WSYPVBKS.js +0 -80
- package/dist/cli-bundle/focused-chunks/chunk-WXLG7HZ5.js +0 -3
- package/dist/cli-bundle/focused-chunks/chunk-WZDFCVLX.js +0 -12
- package/dist/cli-bundle/focused-chunks/chunk-YGO3FWA2.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-ZYXQACPB.js +0 -2
- package/dist/cli-bundle/focused-chunks/item-transaction-PZ2FQ6B2.js +0 -2
- package/dist/cli-bundle/focused-chunks/mutation-admission-XOOK6XFH.js +0 -2
|
@@ -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]="e631dc72-c452-5596-8213-7e75b3b250a3")}catch{}})();import{readFileSync}from"node:fs";import path from"node:path";import{EXIT_CODE}from"../../core/shared/constants.js";import{PmCliError}from"../../core/shared/errors.js";const PM_SUBCOMMANDS_WITH_ITEM_REFERENCE=new Set(["get","history","restore","update","close","delete","append","claim","release","comments","notes","learnings","files","docs","deps","test"]),PM_GLOBAL_FLAGS_WITH_VALUE=new Set(["--pm-path","--path","--author"]),PM_ITEM_REFERENCE_FLAGS_WITH_VALUE={get:new Set(["--depth","--fields","--tree-depth","--tree_depth","--at","--format"]),history:new Set(["--limit","--field","--format"]),restore:new Set(["--message"]),update:new Set(["--title","-t","--description","-d","--body","-b","--status","-s","--close-reason","--close_reason","--priority","-p","--type","--tags","--add-tags","--add_tags","--remove-tags","--remove_tags","--deadline","--estimated-minutes","--estimate","--estimated_minutes","--acceptance-criteria","--ac","--acceptance_criteria","--definition-of-ready","--definition_of_ready","--order","--rank","--goal","--objective","--value","--impact","--outcome","--why-now","--why_now","--message","--assignee","--parent","--reviewer","--risk","--confidence","--sprint","--release","--blocked-by","--blocked_by","--blocked-reason","--blocked_reason","--unblock-note","--unblock_note","--reporter","--severity","--environment","--repro-steps","--repro_steps","--resolution","--expected-result","--expected_result","--expected","--actual-result","--actual_result","--actual","--affected-version","--affected_version","--fixed-version","--fixed_version","--component","--regression","--customer-impact","--customer_impact","--dep","--dep-remove","--dep_remove","--comment","--note","--learning","--file","--test","--doc","--reminder","--event","--type-option","--type_option","--field","--unset","--body-file"]),close:new Set(["--reason","-r","--close-reason","--duplicate-of","-d","--message","-m","--validate-close","--resolution","--expected-result","--expected_result","--expected","--actual-result","--actual_result","--actual"]),delete:new Set(["--message"]),append:new Set(["--body","--text","--message"]),claim:new Set(["--assignee","--message","--type","--tag","--priority","--assignee-filter","--parent","--sprint","--release","--max-attempts"]),release:new Set(["--assignee","--message"]),comments:new Set(["--add","--body","--comment","--file","--edit","--delete","--limit","--message"]),notes:new Set(["--add","--file","--edit","--delete","--limit","--message"]),learnings:new Set(["--add","--file","--edit","--delete","--limit","--message"]),files:new Set(["--add","--add-glob","--remove","--migrate","--note","--message"]),docs:new Set(["--add","--add-glob","--remove","--migrate","--note","--message"]),deps:new Set(["--format","--max-depth","--collapse","--node-limit","--edge-limit","--token-budget","--cursor","--direction","--kind"]),test:new Set(["--add","--add-json","--remove","--match","--only-index","--timeout","--env-set","--env-clear","--pm-context","--message"])},NPX_FLAGS_WITH_VALUE=new Set(["-p","--package"]),NPX_COMMAND_STRING_FLAGS=new Set(["-c","--call"]),PNPM_GLOBAL_FLAGS_WITH_VALUE=new Set(["-c","-C","--config","--dir","--filter","--workspace-dir"]),NPM_GLOBAL_FLAGS_WITH_VALUE=new Set(["-C","--prefix","--userconfig","--cache"]),YARN_GLOBAL_FLAGS_WITH_VALUE=new Set(["--cwd"]),BUN_GLOBAL_FLAGS_WITH_VALUE=new Set(["--cwd"]),NPM_EXEC_SUBCOMMANDS=new Set(["exec","x"]),SCRIPT_RUN_SUBCOMMANDS=new Set(["run","run-script"]),SCRIPT_RUN_FLAGS_WITH_VALUE=new Set(["-C","--dir","--cwd","-w","--workspace","--filter"]);function splitNormalizedCommandSegments(normalizedCommand){return normalizedCommand.split(/&&|\|\||\||;/).map(segment=>segment.trim()).filter(segment=>segment.length>0)}function extractPmInvocationArgsFromSegment(segment,cwd=process.cwd()){const tokens=stripLeadingEnvAssignments(segment.split(" ").filter(token=>token.length>0));if(tokens.length===0)return null;const[executable,...args]=tokens;if(isPmExecutableToken(executable)||isPmCliScriptToken(executable,cwd))return args;if(executable==="node"&&args[0]&&isPmCliScriptToken(args[0],cwd))return args.slice(1);const invocation=executable==="npx"||executable==="bunx"?parseNpxCommand(args):executable==="pnpm"?parsePnpmDlxCommand(args):executable==="npm"?parseNpmExecCommand(args):null;return invocation&&(isPmExecutableToken(invocation.command)||isPmCliPackageToken(invocation.command))?invocation.args:null}const extractReferencedPmItemIdsFromCommand=(command,idPrefix="pm")=>{const lowered=idPrefix.trim().toLowerCase();let prefixEnd=lowered.length;for(;prefixEnd>0&&lowered[prefixEnd-1]==="-";)prefixEnd-=1;const normalizedPrefix=lowered.slice(0,prefixEnd);if(normalizedPrefix.length===0)return[];const normalizedCommand=command.trim().replaceAll("\\","/").replaceAll('"',"").replaceAll("'","").replaceAll(/\s+/g," ").toLowerCase(),ids=new Set;for(const segment of splitNormalizedCommandSegments(normalizedCommand)){const invocationArgs=extractPmInvocationArgsFromSegment(segment),context=invocationArgs?resolvePmSubcommandContext(invocationArgs):null,candidate=context&&PM_SUBCOMMANDS_WITH_ITEM_REFERENCE.has(context.subcommand)?firstPositionalToken(context.remaining,PM_ITEM_REFERENCE_FLAGS_WITH_VALUE[context.subcommand]):void 0,normalizedCandidate=candidate?.trim().toLowerCase();normalizedCandidate?.startsWith(`${normalizedPrefix}-`)===!0&&normalizedCandidate.length>normalizedPrefix.length+1&&ids.add(candidate)}return[...ids].sort((left,right)=>left.localeCompare(right))};function stripLeadingEnvAssignments(tokens){let start=0;for(tokens[start]==="env"&&(start+=1);start<tokens.length;){const token=tokens[start];if(/^(?:[a-z_][a-z0-9_]*|\$env:[a-z_][a-z0-9_]*)=.*/.test(token)){start+=1;continue}break}return tokens.slice(start)}function firstPmSubcommand(args){let index=0;for(;index<args.length;){const token=args[index];if(token==="--"){index+=1;continue}if(token.startsWith("-")){if(PM_GLOBAL_FLAGS_WITH_VALUE.has(token)){index+=2;continue}index+=1;continue}return token}}function isPmExecutableToken(token){return token==="pm"||token==="pm.cmd"||token==="pm.exe"||token.endsWith("/pm")||token.endsWith("/pm.cmd")||token.endsWith("/pm.exe")}function normalizePackageSpecifier(token){const trimmed=token.trim();if(!trimmed.startsWith("@")){const versionSeparator2=trimmed.indexOf("@");return versionSeparator2===-1?trimmed:trimmed.slice(0,versionSeparator2)}const scopeSeparator=trimmed.indexOf("/");if(scopeSeparator===-1)return trimmed;const versionSeparator=trimmed.indexOf("@",scopeSeparator+1);return versionSeparator===-1?trimmed:trimmed.slice(0,versionSeparator)}function isPmCliPackageToken(token){const normalizedSpecifier=normalizePackageSpecifier(token);return normalizedSpecifier==="pm-cli"||normalizedSpecifier.endsWith("/pm-cli")||token==="pm-cli"||token.endsWith("/pm-cli")}function isPmCliScriptToken(token,cwd=process.cwd()){const normalized=token.replaceAll("\\","/");if(normalized!=="dist/cli.js"&&normalized!=="./dist/cli.js"&&!normalized.endsWith("/dist/cli.js"))return!1;const scriptPath=path.resolve(cwd,token);try{return JSON.parse(readFileSync(path.join(path.dirname(path.dirname(scriptPath)),"package.json"),"utf8")).name==="@unbrained/pm-cli"}catch{return!1}}function parseNpxCommand(tokens){let index=0;for(;index<tokens.length;){const token=tokens[index];if(token==="--"){index+=1;break}if(!token.startsWith("-"))break;const inlineCommandMatch=/^(?:-c|--call)=(.*)$/.exec(token);if(inlineCommandMatch){const command2=inlineCommandMatch[1];return command2?{command:command2,args:tokens.slice(index+1)}:null}if(NPX_COMMAND_STRING_FLAGS.has(token)){index+=1;break}index+=NPX_FLAGS_WITH_VALUE.has(token)&&!token.includes("=")?2:1}const command=tokens[index];return command?{command,args:tokens.slice(index+1)}:null}function parseLauncherSubcommand(tokens,flagsWithValue){let index=0;for(;index<tokens.length;){const token=tokens[index];if(token==="--"){index+=1;continue}if(token.startsWith("-")){if(token.includes("=")){index+=1;continue}if(flagsWithValue.has(token)){index+=2;continue}index+=1;continue}return{subcommand:token,args:tokens.slice(index+1)}}return null}function parsePnpmDlxCommand(tokens){const parsed=parseLauncherSubcommand(tokens,PNPM_GLOBAL_FLAGS_WITH_VALUE);return parsed?.subcommand!=="dlx"?null:parseNpxCommand(parsed.args)}function parseNpmExecCommand(tokens){const parsed=parseLauncherSubcommand(tokens,NPM_GLOBAL_FLAGS_WITH_VALUE);return!parsed||!NPM_EXEC_SUBCOMMANDS.has(parsed.subcommand)?null:parseNpxCommand(parsed.args)}function resolvePmSubcommandContext(args){let index=0;for(;index<args.length;){const token=args[index];if(token==="--"){index+=1;continue}if(token.startsWith("-")){if(PM_GLOBAL_FLAGS_WITH_VALUE.has(token)){index+=2;continue}index+=1;continue}return{subcommand:token,remaining:args.slice(index+1)}}return null}function firstPositionalToken(tokens,flagsWithValue=new Set){for(let index=0;index<tokens.length;index+=1){const token=tokens[index];if(token==="--")return tokens[index+1];if(!token.startsWith("-"))return token;!token.includes("=")&&(PM_GLOBAL_FLAGS_WITH_VALUE.has(token)||flagsWithValue.has(token))&&(index+=1)}}function parsedLauncherInvokesRecursiveTestAll(parsed){return!parsed||!isPmExecutableToken(parsed.command)&&!isPmCliPackageToken(parsed.command)?!1:firstPmSubcommand(parsed.args)==="test-all"}const SHELL_COMMAND_FLAG_PATTERNS=new Map([...["sh","bash","dash","zsh"].map(executable=>[executable,/^(?:-c|-[a-z]*c[a-z]*)$/u]),...["cmd","cmd.exe"].map(executable=>[executable,/^\/[ck]$/u]),...["powershell","powershell.exe","pwsh","pwsh.exe"].map(executable=>[executable,/^-(?:c|command)$/u])]),PACKAGE_EXEC_EXECUTABLES=new Set(["npx","bunx"]);function segmentInvokesRecursiveTestAll(segment){const rawTokens=segment.split(" ").filter(token=>token.length>0),tokens=stripLeadingEnvAssignments(rawTokens);if(tokens.length===0)return!1;const[executable,...args]=tokens,shellCommandFlagPattern=SHELL_COMMAND_FLAG_PATTERNS.get(executable);if(shellCommandFlagPattern){const commandFlagIndex=args.findIndex(arg=>shellCommandFlagPattern.test(arg));return commandFlagIndex>=0?segmentInvokesRecursiveTestAll(args.slice(commandFlagIndex+1).join(" ")):!1}return isPmExecutableToken(executable)||isPmCliScriptToken(executable)?firstPmSubcommand(args)==="test-all":executable==="node"&&args.length>0&&isPmCliScriptToken(args[0])?firstPmSubcommand(args.slice(1))==="test-all":PACKAGE_EXEC_EXECUTABLES.has(executable)?parsedLauncherInvokesRecursiveTestAll(parseNpxCommand(args)):executable==="pnpm"?parsedLauncherInvokesRecursiveTestAll(parsePnpmDlxCommand(args)):executable==="npm"?parsedLauncherInvokesRecursiveTestAll(parseNpmExecCommand(args)):!1}function invokesRecursiveTestAllCommand(command){const normalized=normalizeCommandForValidation(command);return splitNormalizedCommandSegments(normalized).some(segment=>segmentInvokesRecursiveTestAll(segment))}function assertNoRecursiveTestAllCommand(command){if(invokesRecursiveTestAllCommand(command))throw new PmCliError('Linked test commands must not invoke "pm test-all"; this creates recursive orchestration.',EXIT_CODE.USAGE)}function normalizeCommandForValidation(command){return command.trim().replaceAll("\\","/").replaceAll('"',"").replaceAll("'","").replaceAll(/\s+/g," ").toLowerCase()}export{BUN_GLOBAL_FLAGS_WITH_VALUE,NPM_EXEC_SUBCOMMANDS,NPM_GLOBAL_FLAGS_WITH_VALUE,NPX_COMMAND_STRING_FLAGS,NPX_FLAGS_WITH_VALUE,PM_GLOBAL_FLAGS_WITH_VALUE,PM_ITEM_REFERENCE_FLAGS_WITH_VALUE,PNPM_GLOBAL_FLAGS_WITH_VALUE,SCRIPT_RUN_FLAGS_WITH_VALUE,SCRIPT_RUN_SUBCOMMANDS,SHELL_COMMAND_FLAG_PATTERNS,YARN_GLOBAL_FLAGS_WITH_VALUE,assertNoRecursiveTestAllCommand,extractPmInvocationArgsFromSegment,extractReferencedPmItemIdsFromCommand,firstPmSubcommand,firstPositionalToken,invokesRecursiveTestAllCommand,isPmCliPackageToken,isPmCliScriptToken,isPmExecutableToken,normalizeCommandForValidation,normalizePackageSpecifier,parseLauncherSubcommand,parseNpmExecCommand,parseNpxCommand,parsePnpmDlxCommand,resolvePmSubcommandContext,segmentInvokesRecursiveTestAll,splitNormalizedCommandSegments,stripLeadingEnvAssignments};
|
|
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]="d88c7294-44f1-5b65-9467-f94ed2db11ca")}catch{}})();import{readFileSync}from"node:fs";import path from"node:path";import{EXIT_CODE}from"../../core/shared/constants.js";import{PmCliError}from"../../core/shared/errors.js";const PM_SUBCOMMANDS_WITH_ITEM_REFERENCE=new Set(["get","history","restore","update","close","delete","append","claim","release","comments","notes","learnings","files","docs","deps","test"]),PM_GLOBAL_FLAGS_WITH_VALUE=new Set(["--pm-path","--path","--author","--expect-hash"]),PM_ITEM_REFERENCE_FLAGS_WITH_VALUE={get:new Set(["--depth","--fields","--tree-depth","--tree_depth","--at","--format"]),history:new Set(["--limit","--field","--format"]),restore:new Set(["--message"]),update:new Set(["--title","-t","--description","-d","--body","-b","--status","-s","--close-reason","--close_reason","--priority","-p","--type","--tags","--add-tags","--add_tags","--remove-tags","--remove_tags","--deadline","--estimated-minutes","--estimate","--estimated_minutes","--acceptance-criteria","--ac","--acceptance_criteria","--definition-of-ready","--definition_of_ready","--order","--rank","--goal","--objective","--value","--impact","--outcome","--why-now","--why_now","--message","--assignee","--parent","--reviewer","--risk","--confidence","--sprint","--release","--blocked-by","--blocked_by","--blocked-reason","--blocked_reason","--unblock-note","--unblock_note","--reporter","--severity","--environment","--repro-steps","--repro_steps","--resolution","--expected-result","--expected_result","--expected","--actual-result","--actual_result","--actual","--affected-version","--affected_version","--fixed-version","--fixed_version","--component","--regression","--customer-impact","--customer_impact","--dep","--dep-remove","--dep_remove","--comment","--note","--learning","--file","--test","--doc","--reminder","--event","--type-option","--type_option","--field","--unset","--body-file"]),close:new Set(["--reason","-r","--close-reason","--duplicate-of","-d","--message","-m","--validate-close","--resolution","--expected-result","--expected_result","--expected","--actual-result","--actual_result","--actual"]),delete:new Set(["--message"]),append:new Set(["--body","--text","--message"]),claim:new Set(["--assignee","--message","--type","--tag","--priority","--assignee-filter","--parent","--sprint","--release","--max-attempts"]),release:new Set(["--assignee","--message"]),comments:new Set(["--add","--body","--comment","--file","--edit","--delete","--limit","--message"]),notes:new Set(["--add","--file","--edit","--delete","--limit","--message"]),learnings:new Set(["--add","--file","--edit","--delete","--limit","--message"]),files:new Set(["--add","--add-glob","--remove","--migrate","--note","--message"]),docs:new Set(["--add","--add-glob","--remove","--migrate","--note","--message"]),deps:new Set(["--format","--max-depth","--collapse","--node-limit","--edge-limit","--token-budget","--cursor","--direction","--kind"]),test:new Set(["--add","--add-json","--remove","--match","--only-index","--timeout","--env-set","--env-clear","--pm-context","--message"])},NPX_FLAGS_WITH_VALUE=new Set(["-p","--package"]),NPX_COMMAND_STRING_FLAGS=new Set(["-c","--call"]),PNPM_GLOBAL_FLAGS_WITH_VALUE=new Set(["-c","-C","--config","--dir","--filter","--workspace-dir"]),NPM_GLOBAL_FLAGS_WITH_VALUE=new Set(["-C","--prefix","--userconfig","--cache"]),YARN_GLOBAL_FLAGS_WITH_VALUE=new Set(["--cwd"]),BUN_GLOBAL_FLAGS_WITH_VALUE=new Set(["--cwd"]),NPM_EXEC_SUBCOMMANDS=new Set(["exec","x"]),SCRIPT_RUN_SUBCOMMANDS=new Set(["run","run-script"]),SCRIPT_RUN_FLAGS_WITH_VALUE=new Set(["-C","--dir","--cwd","-w","--workspace","--filter"]);function splitNormalizedCommandSegments(normalizedCommand){return normalizedCommand.split(/&&|\|\||\||;/).map(segment=>segment.trim()).filter(segment=>segment.length>0)}function extractPmInvocationArgsFromSegment(segment,cwd=process.cwd()){const tokens=stripLeadingEnvAssignments(segment.split(" ").filter(token=>token.length>0));if(tokens.length===0)return null;const[executable,...args]=tokens;if(isPmExecutableToken(executable)||isPmCliScriptToken(executable,cwd))return args;if(executable==="node"&&args[0]&&isPmCliScriptToken(args[0],cwd))return args.slice(1);const invocation=executable==="npx"||executable==="bunx"?parseNpxCommand(args):executable==="pnpm"?parsePnpmDlxCommand(args):executable==="npm"?parseNpmExecCommand(args):null;return invocation&&(isPmExecutableToken(invocation.command)||isPmCliPackageToken(invocation.command))?invocation.args:null}const extractReferencedPmItemIdsFromCommand=(command,idPrefix="pm")=>{const lowered=idPrefix.trim().toLowerCase();let prefixEnd=lowered.length;for(;prefixEnd>0&&lowered[prefixEnd-1]==="-";)prefixEnd-=1;const normalizedPrefix=lowered.slice(0,prefixEnd);if(normalizedPrefix.length===0)return[];const normalizedCommand=command.trim().replaceAll("\\","/").replaceAll('"',"").replaceAll("'","").replaceAll(/\s+/g," ").toLowerCase(),ids=new Set;for(const segment of splitNormalizedCommandSegments(normalizedCommand)){const invocationArgs=extractPmInvocationArgsFromSegment(segment),context=invocationArgs?resolvePmSubcommandContext(invocationArgs):null,candidate=context&&PM_SUBCOMMANDS_WITH_ITEM_REFERENCE.has(context.subcommand)?firstPositionalToken(context.remaining,PM_ITEM_REFERENCE_FLAGS_WITH_VALUE[context.subcommand]):void 0,normalizedCandidate=candidate?.trim().toLowerCase();normalizedCandidate?.startsWith(`${normalizedPrefix}-`)===!0&&normalizedCandidate.length>normalizedPrefix.length+1&&ids.add(candidate)}return[...ids].sort((left,right)=>left.localeCompare(right))};function stripLeadingEnvAssignments(tokens){let start=0;for(tokens[start]==="env"&&(start+=1);start<tokens.length;){const token=tokens[start];if(/^(?:[a-z_][a-z0-9_]*|\$env:[a-z_][a-z0-9_]*)=.*/.test(token)){start+=1;continue}break}return tokens.slice(start)}function firstPmSubcommand(args){let index=0;for(;index<args.length;){const token=args[index];if(token==="--"){index+=1;continue}if(token.startsWith("-")){if(PM_GLOBAL_FLAGS_WITH_VALUE.has(token)){index+=2;continue}index+=1;continue}return token}}function isPmExecutableToken(token){return token==="pm"||token==="pm.cmd"||token==="pm.exe"||token.endsWith("/pm")||token.endsWith("/pm.cmd")||token.endsWith("/pm.exe")}function normalizePackageSpecifier(token){const trimmed=token.trim();if(!trimmed.startsWith("@")){const versionSeparator2=trimmed.indexOf("@");return versionSeparator2===-1?trimmed:trimmed.slice(0,versionSeparator2)}const scopeSeparator=trimmed.indexOf("/");if(scopeSeparator===-1)return trimmed;const versionSeparator=trimmed.indexOf("@",scopeSeparator+1);return versionSeparator===-1?trimmed:trimmed.slice(0,versionSeparator)}function isPmCliPackageToken(token){const normalizedSpecifier=normalizePackageSpecifier(token);return normalizedSpecifier==="pm-cli"||normalizedSpecifier.endsWith("/pm-cli")||token==="pm-cli"||token.endsWith("/pm-cli")}function isPmCliScriptToken(token,cwd=process.cwd()){const normalized=token.replaceAll("\\","/");if(normalized!=="dist/cli.js"&&normalized!=="./dist/cli.js"&&!normalized.endsWith("/dist/cli.js"))return!1;const scriptPath=path.resolve(cwd,token);try{return JSON.parse(readFileSync(path.join(path.dirname(path.dirname(scriptPath)),"package.json"),"utf8")).name==="@unbrained/pm-cli"}catch{return!1}}function parseNpxCommand(tokens){let index=0;for(;index<tokens.length;){const token=tokens[index];if(token==="--"){index+=1;break}if(!token.startsWith("-"))break;const inlineCommandMatch=/^(?:-c|--call)=(.*)$/.exec(token);if(inlineCommandMatch){const command2=inlineCommandMatch[1];return command2?{command:command2,args:tokens.slice(index+1)}:null}if(NPX_COMMAND_STRING_FLAGS.has(token)){index+=1;break}index+=NPX_FLAGS_WITH_VALUE.has(token)&&!token.includes("=")?2:1}const command=tokens[index];return command?{command,args:tokens.slice(index+1)}:null}function parseLauncherSubcommand(tokens,flagsWithValue){let index=0;for(;index<tokens.length;){const token=tokens[index];if(token==="--"){index+=1;continue}if(token.startsWith("-")){if(token.includes("=")){index+=1;continue}if(flagsWithValue.has(token)){index+=2;continue}index+=1;continue}return{subcommand:token,args:tokens.slice(index+1)}}return null}function parsePnpmDlxCommand(tokens){const parsed=parseLauncherSubcommand(tokens,PNPM_GLOBAL_FLAGS_WITH_VALUE);return parsed?.subcommand!=="dlx"?null:parseNpxCommand(parsed.args)}function parseNpmExecCommand(tokens){const parsed=parseLauncherSubcommand(tokens,NPM_GLOBAL_FLAGS_WITH_VALUE);return!parsed||!NPM_EXEC_SUBCOMMANDS.has(parsed.subcommand)?null:parseNpxCommand(parsed.args)}function resolvePmSubcommandContext(args){let index=0;for(;index<args.length;){const token=args[index];if(token==="--"){index+=1;continue}if(token.startsWith("-")){if(PM_GLOBAL_FLAGS_WITH_VALUE.has(token)){index+=2;continue}index+=1;continue}return{subcommand:token,remaining:args.slice(index+1)}}return null}function firstPositionalToken(tokens,flagsWithValue=new Set){for(let index=0;index<tokens.length;index+=1){const token=tokens[index];if(token==="--")return tokens[index+1];if(!token.startsWith("-"))return token;!token.includes("=")&&(PM_GLOBAL_FLAGS_WITH_VALUE.has(token)||flagsWithValue.has(token))&&(index+=1)}}function parsedLauncherInvokesRecursiveTestAll(parsed){return!parsed||!isPmExecutableToken(parsed.command)&&!isPmCliPackageToken(parsed.command)?!1:firstPmSubcommand(parsed.args)==="test-all"}const SHELL_COMMAND_FLAG_PATTERNS=new Map([...["sh","bash","dash","zsh"].map(executable=>[executable,/^(?:-c|-[a-z]*c[a-z]*)$/u]),...["cmd","cmd.exe"].map(executable=>[executable,/^\/[ck]$/u]),...["powershell","powershell.exe","pwsh","pwsh.exe"].map(executable=>[executable,/^-(?:c|command)$/u])]),PACKAGE_EXEC_EXECUTABLES=new Set(["npx","bunx"]);function segmentInvokesRecursiveTestAll(segment){const rawTokens=segment.split(" ").filter(token=>token.length>0),tokens=stripLeadingEnvAssignments(rawTokens);if(tokens.length===0)return!1;const[executable,...args]=tokens,shellCommandFlagPattern=SHELL_COMMAND_FLAG_PATTERNS.get(executable);if(shellCommandFlagPattern){const commandFlagIndex=args.findIndex(arg=>shellCommandFlagPattern.test(arg));return commandFlagIndex>=0?segmentInvokesRecursiveTestAll(args.slice(commandFlagIndex+1).join(" ")):!1}return isPmExecutableToken(executable)||isPmCliScriptToken(executable)?firstPmSubcommand(args)==="test-all":executable==="node"&&args.length>0&&isPmCliScriptToken(args[0])?firstPmSubcommand(args.slice(1))==="test-all":PACKAGE_EXEC_EXECUTABLES.has(executable)?parsedLauncherInvokesRecursiveTestAll(parseNpxCommand(args)):executable==="pnpm"?parsedLauncherInvokesRecursiveTestAll(parsePnpmDlxCommand(args)):executable==="npm"?parsedLauncherInvokesRecursiveTestAll(parseNpmExecCommand(args)):!1}function invokesRecursiveTestAllCommand(command){const normalized=normalizeCommandForValidation(command);return splitNormalizedCommandSegments(normalized).some(segment=>segmentInvokesRecursiveTestAll(segment))}function assertNoRecursiveTestAllCommand(command){if(invokesRecursiveTestAllCommand(command))throw new PmCliError('Linked test commands must not invoke "pm test-all"; this creates recursive orchestration.',EXIT_CODE.USAGE)}function normalizeCommandForValidation(command){return command.trim().replaceAll("\\","/").replaceAll('"',"").replaceAll("'","").replaceAll(/\s+/g," ").toLowerCase()}export{BUN_GLOBAL_FLAGS_WITH_VALUE,NPM_EXEC_SUBCOMMANDS,NPM_GLOBAL_FLAGS_WITH_VALUE,NPX_COMMAND_STRING_FLAGS,NPX_FLAGS_WITH_VALUE,PM_GLOBAL_FLAGS_WITH_VALUE,PM_ITEM_REFERENCE_FLAGS_WITH_VALUE,PNPM_GLOBAL_FLAGS_WITH_VALUE,SCRIPT_RUN_FLAGS_WITH_VALUE,SCRIPT_RUN_SUBCOMMANDS,SHELL_COMMAND_FLAG_PATTERNS,YARN_GLOBAL_FLAGS_WITH_VALUE,assertNoRecursiveTestAllCommand,extractPmInvocationArgsFromSegment,extractReferencedPmItemIdsFromCommand,firstPmSubcommand,firstPositionalToken,invokesRecursiveTestAllCommand,isPmCliPackageToken,isPmCliScriptToken,isPmExecutableToken,normalizeCommandForValidation,normalizePackageSpecifier,parseLauncherSubcommand,parseNpmExecCommand,parseNpxCommand,parsePnpmDlxCommand,resolvePmSubcommandContext,segmentInvokesRecursiveTestAll,splitNormalizedCommandSegments,stripLeadingEnvAssignments};
|
|
2
2
|
//# sourceMappingURL=linked-command-detection.js.map
|
|
@@ -41,7 +41,7 @@ stepId?:string;
|
|
|
41
41
|
}
|
|
42
42
|
/** Options accepted by the public workspace transaction coordinator. */
|
|
43
43
|
export interface CommitWorkspaceTransactionOptions {
|
|
44
|
-
/** Tracker root that owns the journal and
|
|
44
|
+
/** Tracker root that owns the journal and the lock that serializes transaction coordinators. */
|
|
45
45
|
pmRoot:string;
|
|
46
46
|
/** Stable idempotency key reused to recover an interrupted transaction. */
|
|
47
47
|
transactionId:string;
|
|
@@ -53,7 +53,7 @@ steps:readonly WorkspaceTransactionStep[];
|
|
|
53
53
|
recoveryData?:WorkspaceTransactionJsonValue;
|
|
54
54
|
/** Lock lifetime in seconds; size this above the longest expected attempt. */
|
|
55
55
|
lockTtlSeconds?:number;
|
|
56
|
-
/** Maximum time to wait for the
|
|
56
|
+
/** Maximum time to wait for the transaction coordinator lock. */
|
|
57
57
|
lockWaitMs?:number;
|
|
58
58
|
/** Optional transition observer used by telemetry and deterministic crash tests. */
|
|
59
59
|
onTransition?(context:WorkspaceTransactionTransitionContext):void | Promise<void>;
|
|
@@ -118,10 +118,21 @@ supersededBy?:string;
|
|
|
118
118
|
/** Validate transaction identity before preview staging or acquiring any writer lock. */
|
|
119
119
|
export declare function validateWorkspaceTransactionIdentity(options:Pick<CommitWorkspaceTransactionOptions,"pmRoot" | "transactionId" | "author">):Pick<CommitWorkspaceTransactionOptions,"pmRoot" | "transactionId" | "author">;
|
|
120
120
|
/**
|
|
121
|
-
* Commit an ordered set of idempotent SDK mutations under one workspace
|
|
122
|
-
* lock. The durable journal resumes interrupted forward work, while
|
|
123
|
-
* failures append reverse-order compensations. This provides atomic
|
|
124
|
-
* replay without deleting or rewriting immutable domain histories.
|
|
121
|
+
* Commit an ordered set of idempotent SDK mutations under one workspace
|
|
122
|
+
* transaction lock. The durable journal resumes interrupted forward work, while
|
|
123
|
+
* ordinary failures append reverse-order compensations. This provides atomic
|
|
124
|
+
* logical replay without deleting or rewriting immutable domain histories.
|
|
125
|
+
*
|
|
126
|
+
* Isolation (GH-1620): the lock serializes transaction coordinators only.
|
|
127
|
+
* Ordinary SDK, CLI and MCP writers take per-item locks and do not wait for it,
|
|
128
|
+
* so a peer can commit between a step's `inspect()` and `apply()`. A step that
|
|
129
|
+
* decides from what it read must guard its write: take the decision data and
|
|
130
|
+
* `item_hash` from one `get()` in `inspect()`, then run `apply()` inside
|
|
131
|
+
* `withExpectedItemRevisions()`, which refuses with `item_revision_conflict`.
|
|
132
|
+
* The coordinator invokes `compensate()` (including during recovery) without an
|
|
133
|
+
* expected revision. Built-in item compensations detect intervening writes, but a
|
|
134
|
+
* custom adapter must fence or reconcile its own compensation, or it can restore
|
|
135
|
+
* a stale value over a peer's write.
|
|
125
136
|
*/
|
|
126
137
|
export declare function commitWorkspaceTransaction(options:CommitWorkspaceTransactionOptions):Promise<WorkspaceTransactionCommitResult>;
|
|
127
138
|
/** Read an atomically published journal without changing tracker state; undefined means no retained receipt. */
|
|
@@ -1,3 +1,3 @@
|
|
|
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]="24a18484-0b8b-55d7-aa23-08a23b8d78ce")}catch{}})();import{mkdir,readFile}from"node:fs/promises";import path from"node:path";import{createHash}from"node:crypto";import{appendWorkspaceAuditEvent,getWorkspaceHistoryPath,WORKSPACE_HISTORY_ID}from"../core/history/workspace-history.js";import{readHistoryEntries}from"./history-read.js";import{acquireLock}from"../core/lock/lock.js";import{isFileMissingError,writeFileAtomic}from"../core/fs/fs-utils.js";import{nowIso}from"../core/shared/time.js";import{EXIT_CODE}from"../core/shared/constants.js";import{PmCliError}from"../core/shared/errors.js";class WorkspaceTransactionInterruptedError extends Error{constructor(message="Workspace transaction interrupted"){super(message),this.name="WorkspaceTransactionInterruptedError"}}const WORKSPACE_TRANSACTION_LOCK_ID="sdk-workspace-transaction",TRANSACTION_ID_PATTERN=/^[a-zA-Z0-9._-]+$/,DEFAULT_LOCK_TTL_SECONDS=30,DEFAULT_LOCK_WAIT_MS=3e3;function requiredIdentifier(value,label){if(typeof value!="string")throw new TypeError(`${label} must be a string`);const normalized=value.trim();if(!normalized||!TRANSACTION_ID_PATTERN.test(normalized))throw new TypeError(`${label} must match [a-zA-Z0-9._-]+`);return normalized}function requiredText(value,label){if(typeof value!="string")throw new TypeError(`${label} must be a string`);const normalized=value.trim();if(!normalized)throw new TypeError(`${label} must be non-empty`);return normalized}function validateWorkspaceTransactionIdentity(options){return{pmRoot:requiredText(options.pmRoot,"Transaction pmRoot"),transactionId:requiredIdentifier(options.transactionId,"Transaction id"),author:requiredText(options.author,"Transaction author")}}function positiveInteger(value,fallback,label){const resolved=value??fallback;if(!Number.isInteger(resolved)||resolved<=0)throw new TypeError(`${label} must be a positive integer`);return resolved}function journalPath(pmRoot,transactionId){return path.join(pmRoot,"transactions","sdk",`${transactionId}.json`)}async function writeJournal(pmRoot,journal){journal.updatedAt=nowIso();const target=journalPath(pmRoot,journal.transactionId);await mkdir(path.dirname(target),{recursive:!0}),await writeFileAtomic(target,`${JSON.stringify(journal,null,2)}
|
|
2
2
|
`)}function hasValidJournalMetadata(journal){return Number.isInteger(journal.attempt)&&typeof journal.author=="string"&&typeof journal.createdAt=="string"&&typeof journal.updatedAt=="string"&&hasValidAbandonment(journal)}function hasValidAbandonment(journal){const retirement=journal.abandonment;return retirement===void 0?journal.schemaVersion===1&&journal.status!=="abandoned":journal.schemaVersion!==2||retirement===null||typeof retirement!="object"||journal.status==="committed"?!1:typeof retirement.reason=="string"&&retirement.reason.trim().length>0&&typeof retirement.author=="string"&&retirement.author.trim().length>0&&(retirement.supersededBy===void 0||typeof retirement.supersededBy=="string"&&TRANSACTION_ID_PATTERN.test(retirement.supersededBy)&&retirement.supersededBy!==journal.transactionId)}function createResultMap(source){return Object.assign(Object.create(null),source)}function cloneJournalValue(value,label){try{const serialized=JSON.stringify(value,(_key,nestedValue)=>{if(typeof nestedValue=="number"&&!Number.isFinite(nestedValue))throw new TypeError(`${label} contains a non-finite number`);if(nestedValue===void 0||typeof nestedValue=="function"||typeof nestedValue=="symbol")throw new TypeError(`${label} contains a non-JSON value`);return nestedValue});return JSON.parse(serialized)}catch(error){throw new TypeError(`${label} must be JSON serializable`,{cause:error})}}function cloneOptionalJournalValue(value,label){return value===void 0?void 0:cloneJournalValue(value,label)}function setJournalEntry(target,stepId,value,label){const cloned=cloneOptionalJournalValue(value,label);cloned===void 0?delete target[stepId]:target[stepId]=cloned}function isJournalMap(value){return value!==null&&typeof value=="object"&&!Array.isArray(value)}function hasUniqueKnownStepIds(value,stepIds){return Array.isArray(value)&&new Set(value).size===value.length&&value.every(stepId=>typeof stepId=="string"&&stepIds.includes(stepId))}function hasValidJournalLifecycle(journal,stepIds){if(!hasUniqueKnownStepIds(journal.startedStepIds,stepIds)||!hasUniqueKnownStepIds(journal.completedStepIds,stepIds)||!isJournalMap(journal.results)||!isJournalMap(journal.compensationData))return!1;const startedStepIds=journal.startedStepIds,started=new Set(startedStepIds),completedStepIds=journal.completedStepIds,completed=new Set(completedStepIds);return Object.keys(journal.compensationData).some(stepId=>!started.has(stepId))||Object.keys(journal.results).some(stepId=>!completed.has(stepId))?!1:journal.status==="committed"?stepIds.every(stepId=>completed.has(stepId)):!["compensated","abandoned"].includes(journal.status??"")||completed.size===0&&started.size===0}function invalidJournal(transactionId,detail){return new PmCliError(`Workspace transaction ${transactionId} journal is invalid (${detail})`,EXIT_CODE.CONFLICT,{code:"transaction_journal_invalid",required:"A valid durable journal is required before transaction recovery can continue.",why:"Corrupt journal data cannot establish which steps applied or still require compensation.",nextSteps:["Preserve the journal and inspect its integrity or restore a verified backup before retrying.","Do not switch to a new transaction id to bypass an unreadable journal; previously applied work may remain."]})}function journalPlanMismatch(transactionId){return new PmCliError(`Workspace transaction ${transactionId} journal does not match the supplied plan`,EXIT_CODE.CONFLICT,{code:"transaction_plan_mismatch",required:"Replay the original plan or safely abandon the interrupted transaction before replacing it.",why:"A journal records the plan it is resuming; applying a different plan under the same id could skip or duplicate steps.",nextSteps:["Re-run with the original document to resume or replay it.","For item batches use client.itemTransactions.status(transactionId), then client.itemTransactions.abandon(transactionId, { reason }) before committing a changed plan with a new transactionId.","Run pm ops gc --scope transactions --dry-run to inspect retention; active and explicitly abandoned journals remain protected."]})}function matchesJournalSteps(journal,recorded,requested){return requested===void 0||journal.abandonment!==void 0||journal.status==="compensated"||recorded.length===requested.length&&recorded.every((id,index)=>id===requested[index])}function parseJournal(raw,transactionId,stepIds){let parsed;try{parsed=JSON.parse(raw)}catch{throw invalidJournal(transactionId,"invalid JSON")}if(!parsed||typeof parsed!="object"||Array.isArray(parsed))throw invalidJournal(transactionId,"expected an object");const journal=parsed;if(!((journal.schemaVersion===1||journal.schemaVersion===2)&&journal.transactionId===transactionId))throw invalidJournal(transactionId,"schema version or transaction id does not match the journal file");const recordedStepIds=journal.stepIds;if(!Array.isArray(recordedStepIds)||!hasUniqueKnownStepIds(recordedStepIds,recordedStepIds)||!hasValidJournalLifecycle(journal,recordedStepIds)||!hasValidJournalMetadata(journal)||typeof journal.status!="string"||!["applying","compensating","compensated","committed","abandoned"].includes(journal.status))throw invalidJournal(transactionId,"invalid lifecycle, metadata, status or step ids");if(!matchesJournalSteps(journal,recordedStepIds,stepIds))throw journalPlanMismatch(transactionId);return{...journal,results:createResultMap(journal.results),compensationData:createResultMap(journal.compensationData)}}async function prepareJournalForApply(options,transactionId,author,stepIds,existing){let journal=existing;const recovered=journal!==void 0;if(journal?.status==="compensating"&&await compensateAppliedSteps(options,journal),journal===void 0||journal.status==="compensated"){const timestamp=nowIso();journal={schemaVersion:1,transactionId,author,status:"applying",attempt:(journal?.attempt??0)+1,createdAt:journal?.createdAt??timestamp,updatedAt:timestamp,stepIds,startedStepIds:[],completedStepIds:[],results:createResultMap(),compensationData:createResultMap(),...options.recoveryData===void 0?{}:{recoveryData:cloneJournalValue(options.recoveryData,"Transaction recovery data")}},await writeJournal(options.pmRoot,journal),await emitTransition(options,journal,"prepared")}return{journal,recovered}}async function applyPreparedTransaction(options,journal,recovered){try{await discoverAppliedSteps(journal,options.steps),await writeJournal(options.pmRoot,journal);const completed=new Set(journal.completedStepIds),started=new Set(journal.startedStepIds);for(const step of options.steps){if(completed.has(step.id))continue;const inspection=await step.inspect();let result=cloneOptionalJournalValue(inspection.result,`Step ${step.id} result`);if(inspection.state!=="applied"){const compensationData=await step.prepareCompensation?.();setJournalEntry(journal.compensationData,step.id,compensationData,`Step ${step.id} compensation data`),started.add(step.id),journal.startedStepIds=options.steps.map(candidate=>candidate.id).filter(stepId=>started.has(stepId)),await writeJournal(options.pmRoot,journal);const appliedResult=await step.apply();result=cloneOptionalJournalValue(appliedResult,`Step ${step.id} result`)}await emitTransition(options,journal,"step_applied",step.id),completed.add(step.id),journal.completedStepIds=options.steps.map(candidate=>candidate.id).filter(stepId=>completed.has(stepId)),setJournalEntry(journal.results,step.id,result,`Step ${step.id} result`),await writeJournal(options.pmRoot,journal),await emitTransition(options,journal,"step_recorded",step.id)}return await emitTransition(options,journal,"committing"),journal.status="committed",await writeJournal(options.pmRoot,journal),await emitTransition(options,journal,"committed"),{transactionId:journal.transactionId,status:"committed",recovered,results:createResultMap(journal.results)}}catch(error){if(error instanceof WorkspaceTransactionInterruptedError||journal.status==="committed")throw error;try{await compensateAppliedSteps(options,journal)}catch(compensationError){throw new AggregateError([error,compensationError],`Workspace transaction failed and compensation failed: ${String(compensationError)}`,{cause:compensationError})}throw error}}async function loadJournal(pmRoot,transactionId,stepIds){try{return parseJournal(await readFile(journalPath(pmRoot,transactionId),"utf8"),transactionId,stepIds)}catch(error){if(error!==null&&typeof error=="object"&&"code"in error&&isFileMissingError(error))return;throw error}}async function emitTransition(options,journal,transition,stepId){await options.onTransition?.({transactionId:journal.transactionId,attempt:journal.attempt,transition,...stepId===void 0?{}:{stepId}})}async function discoverAppliedSteps(journal,steps){const completed=new Set,results=createResultMap();for(const step of steps){const inspection=await step.inspect();inspection.state==="applied"&&(completed.add(step.id),setJournalEntry(results,step.id,inspection.result,`Step ${step.id} result`))}journal.results=results,journal.completedStepIds=steps.map(step=>step.id).filter(stepId=>completed.has(stepId))}async function compensateAppliedSteps(options,journal){journal.status="compensating",await writeJournal(options.pmRoot,journal),await emitTransition(options,journal,"compensating");const completed=new Set(journal.completedStepIds),started=new Set(journal.startedStepIds);for(const step of[...options.steps].reverse()){if(!started.has(step.id)&&!completed.has(step.id))continue;let shouldCompensate;try{const state=(await step.inspect()).state;shouldCompensate=started.has(step.id)&&(state==="applied"||state==="pending"&&step.compensatePending===!0)}catch{shouldCompensate=started.has(step.id)}shouldCompensate&&(await emitTransition(options,journal,"step_compensating",step.id),await step.compensate(journal.compensationData[step.id])),completed.delete(step.id),started.delete(step.id),journal.completedStepIds=options.steps.map(candidate=>candidate.id).filter(stepId=>completed.has(stepId)),journal.startedStepIds=options.steps.map(candidate=>candidate.id).filter(stepId=>started.has(stepId)),delete journal.results[step.id],delete journal.compensationData[step.id],await writeJournal(options.pmRoot,journal),await emitTransition(options,journal,"step_compensated",step.id)}journal.status="compensated",await writeJournal(options.pmRoot,journal),await emitTransition(options,journal,"compensated")}async function commitWorkspaceTransaction(options){const{pmRoot,transactionId,author}=validateWorkspaceTransactionIdentity(options),lockTtlSeconds=positiveInteger(options.lockTtlSeconds,DEFAULT_LOCK_TTL_SECONDS,"Transaction lock TTL"),lockWaitMs=positiveInteger(options.lockWaitMs,DEFAULT_LOCK_WAIT_MS,"Transaction lock wait"),steps=[...options.steps];if(steps.length===0)throw new TypeError("Workspace transaction requires at least one step");const stepIds=steps.map(step=>{const stepId=requiredIdentifier(step.id,"Transaction step id");if(stepId!==step.id)throw new TypeError("Transaction step id must not contain surrounding whitespace");return stepId});if(new Set(stepIds).size!==stepIds.length)throw new TypeError("Workspace transaction step ids must be unique");const stableOptions={...options,pmRoot,transactionId,author,steps,lockTtlSeconds,lockWaitMs},release=await acquireLock(pmRoot,WORKSPACE_TRANSACTION_LOCK_ID,lockTtlSeconds,author,!1,!1,lockWaitMs);try{const existing=await loadJournal(pmRoot,transactionId,stepIds);if(existing?.status==="abandoned"||existing?.abandonment!==void 0)throw retiredTransaction(existing);const recovered=existing!==void 0;if(existing?.status==="committed")return{transactionId,status:"committed",recovered:!0,results:createResultMap(existing.results)};const prepared=await prepareJournalForApply(stableOptions,transactionId,author,stepIds,existing);return await applyPreparedTransaction(stableOptions,prepared.journal,recovered||prepared.recovered)}finally{await release()}}function retiredTransaction(journal){const pending=journal.status!=="abandoned",supersededBy=journal.abandonment.supersededBy,retry=supersededBy===void 0?`Retry abandonment without supersededBy to finish retirement of ${JSON.stringify(journal.transactionId)}.`:`Retry abandonment with supersededBy ${JSON.stringify(supersededBy)} to finish retirement of ${JSON.stringify(journal.transactionId)}.`;return new PmCliError(pending?`Transaction ${journal.transactionId} retirement is incomplete`:`Transaction ${journal.transactionId} has been abandoned`,EXIT_CODE.CONFLICT,{code:"transaction_abandoned",required:pending?retry:"Use a new transactionId for the replacement plan.",why:pending?"Forward replay is fenced, but the recorded retirement has not finished. Preserve its replacement identity.":"An explicitly retired transaction identity cannot be resumed.",...pending?{retirement:{state:"pending",superseded_by:supersededBy??null},nextSteps:[retry]}:{}})}async function readWorkspaceTransaction(pmRoot,transactionId){const identity=validateWorkspaceTransactionIdentity({pmRoot,transactionId,author:"reader"});return loadJournal(identity.pmRoot,identity.transactionId)}function workspaceTransactionPlanDigest(journal){return createHash("sha256").update(JSON.stringify(journal.stepIds)).digest("hex")}async function abandonWorkspaceTransaction(options){const identity=validateWorkspaceTransactionIdentity(options),reason=requiredText(options.reason,"Abandonment reason"),supersededBy=options.supersededBy===void 0?void 0:requiredIdentifier(options.supersededBy,"Replacement transaction id");if(supersededBy===identity.transactionId)throw new TypeError("Replacement transaction id must differ from the abandoned id");const lockTtlSeconds=positiveInteger(options.lockTtlSeconds,DEFAULT_LOCK_TTL_SECONDS,"Transaction lock TTL"),lockWaitMs=positiveInteger(options.lockWaitMs,DEFAULT_LOCK_WAIT_MS,"Transaction lock wait"),release=await acquireLock(identity.pmRoot,WORKSPACE_TRANSACTION_LOCK_ID,lockTtlSeconds,identity.author,!1,!1,lockWaitMs);try{const journal=await loadJournal(identity.pmRoot,identity.transactionId);if(journal===void 0)throw new PmCliError(`Transaction ${identity.transactionId} was not found`,EXIT_CODE.NOT_FOUND);if(journal.status==="committed")throw new PmCliError(`Transaction ${identity.transactionId} is committed and cannot be abandoned`,EXIT_CODE.CONFLICT,{code:"transaction_committed",required:"Plan a separate explicit corrective mutation.",why:"Recovery cannot silently undo committed work."});if(journal.abandonment!==void 0&&journal.abandonment.supersededBy!==supersededBy)throw retiredTransaction(journal);if(journal.status==="abandoned")return journal;const steps=options.resolveSteps(structuredClone(journal));if(JSON.stringify(steps.map(step=>step.id))!==JSON.stringify(journal.stepIds))throw journalPlanMismatch(identity.transactionId);if(journal.abandonment===void 0){if(supersededBy!==void 0&&await loadJournal(identity.pmRoot,supersededBy)!==void 0)throw new PmCliError(`Replacement transaction ${supersededBy} already exists`,EXIT_CODE.CONFLICT);journal.schemaVersion=2,journal.abandonment={reason,author:identity.author,...supersededBy===void 0?{}:{supersededBy}},await writeJournal(identity.pmRoot,journal)}return journal.status!=="compensated"&&await compensateAppliedSteps({...options,...identity,steps},journal),await auditTransactionAbandonment(journal,identity.pmRoot,lockTtlSeconds,lockWaitMs),journal.status="abandoned",await writeJournal(identity.pmRoot,journal),journal}finally{await release()}}async function auditTransactionAbandonment(journal,pmRoot,lockTtlSeconds,lockWaitMs){const planDigest=workspaceTransactionPlanDigest(journal);if((await readHistoryEntries(getWorkspaceHistoryPath(pmRoot),WORKSPACE_HISTORY_ID)).some(entry=>entry.op==="transaction_abandon"&&entry.context?.transaction_id===journal.transactionId&&entry.context?.attempt===journal.attempt&&entry.context?.plan_digest===planDigest))return;const abandonment=journal.abandonment;await appendWorkspaceAuditEvent({pmRoot,author:abandonment.author,lockTtlSeconds,lockWaitMs,op:"transaction_abandon",message:abandonment.reason,context:{transaction_id:journal.transactionId,attempt:journal.attempt,plan_digest:planDigest,...abandonment.supersededBy===void 0?{}:{superseded_by:abandonment.supersededBy}}})}export{WorkspaceTransactionInterruptedError,abandonWorkspaceTransaction,commitWorkspaceTransaction,readWorkspaceTransaction,validateWorkspaceTransactionIdentity,workspaceTransactionPlanDigest};
|
|
3
3
|
//# sourceMappingURL=workspace-transaction.js.map
|
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/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
|
|
@@ -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}
|