@unbrained/pm-cli 2026.8.27 → 2026.8.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +25 -1
  3. package/dist/cli/error-guidance.js +61 -7
  4. package/dist/cli/register-annotations.js +58 -41
  5. package/dist/cli-bundle/bundle-manifest.json +121 -121
  6. package/dist/cli-bundle/chunks/{chunk-SH6P7FXI.js → chunk-BY2FQ2NI.js} +2 -2
  7. package/dist/cli-bundle/chunks/{chunk-TNX6HC54.js → chunk-E73FDIWT.js} +2 -2
  8. package/dist/cli-bundle/chunks/{chunk-SHMDY36D.js → chunk-FEVBFFCQ.js} +2 -2
  9. package/dist/cli-bundle/chunks/{chunk-CVBBGWW5.js → chunk-M7OXRQE3.js} +10 -6
  10. package/dist/cli-bundle/chunks/{chunk-R4ETAOJC.js → chunk-NBCBFVZI.js} +2 -2
  11. package/dist/cli-bundle/chunks/{chunk-SKXLJIEK.js → chunk-NTXZHRKA.js} +45 -45
  12. package/dist/cli-bundle/chunks/{chunk-52EKTW6V.js → chunk-QE6WQXFO.js} +2 -2
  13. package/dist/cli-bundle/chunks/{chunk-MFNTKMTI.js → chunk-TIQ6AMH2.js} +2 -2
  14. package/dist/cli-bundle/chunks/chunk-X2RROGZE.js +2 -0
  15. package/dist/cli-bundle/chunks/{chunk-OS27HHBN.js → chunk-XRVVYRRO.js} +28 -28
  16. package/dist/cli-bundle/chunks/{chunk-3OO3W6FW.js → chunk-XWEQGHHG.js} +2 -2
  17. package/dist/cli-bundle/chunks/{register-list-query-EUWM6VII.js → register-list-query-J35ZPQQ5.js} +2 -2
  18. package/dist/cli-bundle/chunks/{register-mutation-FD4HSAVU.js → register-mutation-J6XJJOGU.js} +4 -4
  19. package/dist/cli-bundle/chunks/{register-operations-HRMNFEC3.js → register-operations-AE3JEMFT.js} +2 -2
  20. package/dist/cli-bundle/chunks/{register-setup-33GNICLX.js → register-setup-OQERLLWE.js} +2 -2
  21. package/dist/cli-bundle/focused-chunks/{chunk-4BR5UU52.js → chunk-4ZDRZYYJ.js} +41 -41
  22. package/dist/cli-bundle/focused-chunks/{chunk-MXTYGECH.js → chunk-AHAM2HAU.js} +2 -2
  23. package/dist/cli-bundle/focused-chunks/{chunk-SUBSWYW3.js → chunk-JZYPPMXF.js} +2 -2
  24. package/dist/cli-bundle/focused-chunks/chunk-LLNTHF5X.js +2 -0
  25. package/dist/cli-bundle/focused-chunks/chunk-LYFWQMVC.js +2 -0
  26. package/dist/cli-bundle/focused-chunks/{chunk-EKX37ZHA.js → chunk-THEPQMLX.js} +2 -2
  27. package/dist/cli-bundle/focused-chunks/chunk-YJLDHJOD.js +2 -0
  28. package/dist/cli-bundle/main.js +14 -14
  29. package/dist/cli-bundle/sdk-authoring.js +1 -1
  30. package/dist/cli-bundle/sdk-contracts.js +2 -2
  31. package/dist/cli-bundle/sdk-core.js +26 -26
  32. package/dist/cli-bundle/sdk-governance.js +1 -1
  33. package/dist/cli-bundle/sdk-merge.js +20 -20
  34. package/dist/cli-bundle/sdk-query.js +1 -1
  35. package/dist/cli-bundle/sdk-runtime.js +1 -1
  36. package/dist/cli-bundle/sdk-testing.js +1 -1
  37. package/dist/cli-bundle/sdk.js +10 -10
  38. package/dist/mcp/tool-definitions.js +5 -5
  39. package/dist/sdk/agent/command-recovery.js +3 -3
  40. package/dist/sdk/agent/task-transcript-contracts.d.ts +52 -0
  41. package/dist/sdk/agent/task-transcript-contracts.js +198 -0
  42. package/dist/sdk/annotations.d.ts +5 -2
  43. package/dist/sdk/annotations.js +66 -36
  44. package/dist/sdk/cli-bootstrap.d.ts +2 -8
  45. package/dist/sdk/cli-bootstrap.js +7 -67
  46. package/dist/sdk/cli-contracts/bootstrap-command-scanner.d.ts +23 -0
  47. package/dist/sdk/cli-contracts/bootstrap-command-scanner.js +80 -0
  48. package/dist/sdk/cli-contracts/flag-contracts.js +5 -2
  49. package/dist/sdk/cli-contracts/flag-lexicon-contracts.js +5 -5
  50. package/dist/sdk/cli-contracts/tool-parameter-tables.js +15 -2
  51. package/dist/sdk/cli-contracts/tool-schema.d.ts +1 -1
  52. package/dist/sdk/cli-contracts/tool-schema.js +16 -3
  53. package/dist/sdk/comments.d.ts +4 -0
  54. package/dist/sdk/comments.js +2 -2
  55. package/dist/sdk/completion.js +9 -5
  56. package/dist/sdk/contracts.d.ts +1 -0
  57. package/dist/sdk/contracts.js +3 -2
  58. package/dist/sdk/extension/install-sources.d.ts +13 -0
  59. package/dist/sdk/extension/install-sources.js +62 -30
  60. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +26 -2
  61. package/dist/sdk/generated/generated-error-code-catalog-part-2.js +26 -14
  62. package/dist/sdk/index.d.ts +1 -0
  63. package/dist/sdk/index.js +3 -2
  64. package/dist/sdk/learnings.d.ts +4 -0
  65. package/dist/sdk/learnings.js +7 -4
  66. package/dist/sdk/lifecycle/close.js +4 -3
  67. package/dist/sdk/notes.d.ts +4 -0
  68. package/dist/sdk/notes.js +2 -2
  69. package/dist/sdk/runtime-input.js +5 -2
  70. package/dist/sdk/runtime.d.ts +6 -6
  71. package/dist/sdk/runtime.js +8 -8
  72. package/docs/COMMANDS.md +4 -3
  73. package/docs/OUTPUT_TOKEN_ACCOUNTING.md +20 -7
  74. package/docs/RELEASING.md +9 -1
  75. package/docs/SDK.md +12 -0
  76. package/docs/SDK_CONTEXT_INTEGRITY.md +12 -1
  77. package/docs/SDK_RUNTIME_BOUNDARIES.md +10 -0
  78. package/docs/TESTING.md +6 -2
  79. package/docs/agent-task-token-baseline.json +97 -11
  80. package/docs/agent-task-transcripts.json +211 -0
  81. package/docs/generated/FLAG_LEXICON_BUDGETS.md +3 -3
  82. package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +6 -3
  83. package/docs/performance/cli-transport-overhead.md +10 -2
  84. package/marketplace.json +2 -2
  85. package/package.json +2 -2
  86. package/packages/pm-beads/package.json +1 -1
  87. package/packages/pm-calendar/package.json +1 -1
  88. package/packages/pm-command-kit/package.json +1 -1
  89. package/packages/pm-digital-twin/package.json +1 -1
  90. package/packages/pm-governance-audit/package.json +1 -1
  91. package/packages/pm-guide-shell/package.json +1 -1
  92. package/packages/pm-kanban/package.json +1 -1
  93. package/packages/pm-lifecycle-hooks/package.json +1 -1
  94. package/packages/pm-linked-test-adapters/package.json +1 -1
  95. package/packages/pm-search-advanced/package.json +1 -1
  96. package/packages/pm-templates/package.json +1 -1
  97. package/packages/pm-todos/package.json +1 -1
  98. package/packages/pm-vcs/package.json +1 -1
  99. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  100. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  101. package/sdk/public-surface.json +197 -25
  102. package/dist/cli-bundle/chunks/chunk-QTO7USTH.js +0 -2
  103. package/dist/cli-bundle/focused-chunks/chunk-2AGZ5BRT.js +0 -2
  104. package/dist/cli-bundle/focused-chunks/chunk-AQ5IYEZZ.js +0 -2
  105. package/dist/cli-bundle/focused-chunks/chunk-YHWHX6YY.js +0 -2
@@ -1,19 +1,7 @@
1
1
  /** Generated partition 2 of the exhaustive error-code catalog. */
2
2
 
3
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="b5722b34-7560-5460-8280-8666a27e5490")}catch(e){}}();
3
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="5d5ca5e6-4286-59d6-a669-29fbce3fa19f")}catch(e){}}();
4
4
  export const PM_ERROR_CODE_CATALOG_PART_2 = [
5
- {
6
- code: "missing_allowed_values",
7
- meaning: "Missing allowed values condition.",
8
- stability: "provisional",
9
- exit_code: 1,
10
- class: "generic_failure",
11
- recovery: "Inspect the structured error guidance and retry the suggested command.",
12
- sources: ["sdk/agent/refusal-closure.ts"],
13
- emitting_commands: ["*"],
14
- canonical_code: "missing_allowed_values",
15
- aliases: [],
16
- },
17
5
  {
18
6
  code: "missing_budget",
19
7
  meaning: "Missing budget condition.",
@@ -331,6 +319,30 @@ export const PM_ERROR_CODE_CATALOG_PART_2 = [
331
319
  canonical_code: "non_refusal_exit",
332
320
  aliases: [],
333
321
  },
322
+ {
323
+ code: "npm_package_archive_missing",
324
+ meaning: "Npm package archive missing condition.",
325
+ stability: "provisional",
326
+ exit_code: 1,
327
+ class: "generic_failure",
328
+ recovery: "Inspect the structured error guidance and retry the suggested command.",
329
+ sources: ["sdk/extension/install-sources.ts"],
330
+ emitting_commands: ["*"],
331
+ canonical_code: "npm_package_archive_missing",
332
+ aliases: [],
333
+ },
334
+ {
335
+ code: "npm_package_archive_unsafe",
336
+ meaning: "Npm package archive unsafe condition.",
337
+ stability: "provisional",
338
+ exit_code: 1,
339
+ class: "generic_failure",
340
+ recovery: "Inspect the structured error guidance and retry the suggested command.",
341
+ sources: ["sdk/extension/install-sources.ts"],
342
+ emitting_commands: ["*"],
343
+ canonical_code: "npm_package_archive_unsafe",
344
+ aliases: [],
345
+ },
334
346
  {
335
347
  code: "npm_package_not_found",
336
348
  meaning: "Npm package not found condition.",
@@ -2114,4 +2126,4 @@ export const PM_ERROR_CODE_CATALOG_PART_2 = [
2114
2126
  },
2115
2127
  ];
2116
2128
  //# sourceMappingURL=generated-error-code-catalog-part-2.js.map
2117
- //# debugId=b5722b34-7560-5460-8280-8666a27e5490
2129
+ //# debugId=5d5ca5e6-4286-59d6-a669-29fbce3fa19f
@@ -118,6 +118,7 @@ export * from "./query/item-filter-options.js";
118
118
  export * from "./query/parsers.js";
119
119
  export * from "./output.js";
120
120
  export * from "./output-contracts.js";
121
+ export * from "./agent/task-transcript-contracts.js";
121
122
  export * from "./relationship-analytics.js";
122
123
  export * from "./relationship-context.js";
123
124
  export * from "./relationship-history.js";
package/dist/sdk/index.js CHANGED
@@ -4,7 +4,7 @@
4
4
  * Defines public SDK APIs and package-author helpers for Index.
5
5
  */
6
6
 
7
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="ae931d1f-b7c6-5f15-aa9a-cda07f375b74")}catch(e){}}();
7
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="ea1845ab-34f5-5a1c-ad49-ac81495c1840")}catch(e){}}();
8
8
  export { PM_COMMAND_CAPABILITY_CONTRACTS, PM_COMMAND_VISIBILITY_CONTRACTS, PM_MCP_PROMPT_CONTRACTS, PM_MCP_RESOURCE_CONTRACTS, PM_MCP_TOOL_COMMAND_CONTRACTS, listPmCommandsForFamily, listPmCommandsForTier, listPmMcpToolsForProfile, renderPmCommandVisibilityMarkdown, resolvePmCommandCapabilityFamily, resolvePmCommandVisibilityTier, } from "./agent-capability-contracts.js";
9
9
  export * from "./mcp/protocol.js";
10
10
  export * from "./mcp/interactions.js";
@@ -115,6 +115,7 @@ export * from "./query/item-filter-options.js";
115
115
  export * from "./query/parsers.js";
116
116
  export * from "./output.js";
117
117
  export * from "./output-contracts.js";
118
+ export * from "./agent/task-transcript-contracts.js";
118
119
  export * from "./relationship-analytics.js";
119
120
  export * from "./relationship-context.js";
120
121
  export * from "./relationship-history.js";
@@ -175,4 +176,4 @@ export * from "./governance.js";
175
176
  export * from "./query.js";
176
177
  export * from "./testing.js";
177
178
  //# sourceMappingURL=index.js.map
178
- //# debugId=ae931d1f-b7c6-5f15-aa9a-cda07f375b74
179
+ //# debugId=ea1845ab-34f5-5a1c-ad49-ac81495c1840
@@ -22,6 +22,8 @@ export interface LearningsCommandOptions {
22
22
  limit?: string;
23
23
  /** Return complete learning history after a mutation instead of a bounded receipt. */
24
24
  fullHistory?: boolean;
25
+ /** Append only when no learning has the same resolved author and text. */
26
+ ifAbsent?: boolean;
25
27
  /** Value that configures or reports author for this contract. */
26
28
  author?: string;
27
29
  /** Human-readable explanation suitable for logs and agent-facing output. */
@@ -49,6 +51,8 @@ export interface LearningsResult {
49
51
  mutation_receipt?: AnnotationMutationReceipt;
50
52
  /** Declares whether older learnings were withheld from a mutation response. */
51
53
  omission_receipt?: AnnotationOmissionReceipt;
54
+ /** Whether a requested mutation changed persisted state. */
55
+ changed?: boolean;
52
56
  }
53
57
  /** Implements run learnings for the public runtime surface of this module. */
54
58
  export declare function runLearnings(id: string, options: LearningsCommandOptions, global: GlobalOptions): Promise<LearningsResult>;
@@ -1,5 +1,5 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="93184f29-f215-5413-9628-fb5b34523995")}catch(e){}}();
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="972816c9-b407-5b89-a744-249c9771451f")}catch(e){}}();
3
3
  import { parseAnnotationTextInput, resolveAnnotationInput, runAnnotationCommand, } from "./annotations.js";
4
4
  /** Implements run learnings for the public runtime surface of this module. */
5
5
  export async function runLearnings(id, options, global) {
@@ -11,11 +11,14 @@ export async function runLearnings(id, options, global) {
11
11
  deleteOp: "learning_delete",
12
12
  parseText: (raw) => parseAnnotationTextInput(raw),
13
13
  createEntry: (entry) => entry,
14
- bypassOwnershipConflict: Boolean(options.edit === undefined && options.delete === undefined &&
14
+ bypassOwnershipConflict: Boolean(options.edit === undefined &&
15
+ options.delete === undefined &&
15
16
  options.ownershipAppendBypass),
16
17
  conflictGuidance: {
17
18
  required: "For an approved append-only handoff on another owner's item, use the package-provided ownership bypass before considering --force.",
18
- examples: ['pm learnings pm-a1b2 --add "review learning" --author "reviewer" --force'],
19
+ examples: [
20
+ 'pm learnings pm-a1b2 --add "review learning" --author "reviewer" --force',
21
+ ],
19
22
  nextSteps: [
20
23
  "Use an installed package's narrow append-only ownership bypass when available.",
21
24
  "Use --force only when an ownership override is explicitly approved.",
@@ -24,4 +27,4 @@ export async function runLearnings(id, options, global) {
24
27
  });
25
28
  }
26
29
  //# sourceMappingURL=learnings.js.map
27
- //# debugId=93184f29-f215-5413-9628-fb5b34523995
30
+ //# debugId=972816c9-b407-5b89-a744-249c9771451f
@@ -4,11 +4,12 @@
4
4
  * Implements the pm close command surface and its agent-facing runtime behavior.
5
5
  */
6
6
 
7
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="781b5118-7b07-53c7-802f-c889cb797a96")}catch(e){}}();
7
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="9f28f7d8-993b-5201-b606-d1eab0c3ff5c")}catch(e){}}();
8
8
  import { assertInitializedTracker } from "../environment/tracker-preflight.js";
9
9
  import { toItemRecord, isTerminalStatus, resolveItemTypeRegistry, resolveRuntimeStatusRegistry, EXIT_CODE, PmCliError, buildItemNotFoundError, listAllItemMetadataLight, locateItem, mutateItem, readLocatedItem, resolvePmRoot, readSettings, resolveAuthor, resolveIsoOrRelative, shouldCompletePlanOnClose, getActiveExtensionRegistrations, } from "../runtime-primitives.js";
10
10
  import { collectBlockedByIds, indexBlockedByIds } from "../actionability.js";
11
11
  import { applyTerminalOrderingPolicy, requireTerminalReason, } from "../lifecycle-policy.js";
12
+ import { nowIso } from "../../core/shared/time.js";
12
13
  import { renderPmCommand } from "../command-line.js";
13
14
  const CLOSE_VALIDATION_FIELDS = [
14
15
  { key: "resolution", label: "resolution (--resolution)" },
@@ -415,7 +416,7 @@ export async function closeItem(id, closeReasonText, options, global) {
415
416
  // informational note instead of the warn/strict validation warning.
416
417
  const trackerItems = await listAllItemMetadataLight(pmRoot, settings.item_format, typeToFolder, undefined, settings.schema);
417
418
  const activeChildIds = findActiveChildIds(trackerItems, located.id, statusRegistry);
418
- const closedAt = new Date().toISOString();
419
+ const closedAt = nowIso();
419
420
  const completedAt = options.completedAt === undefined
420
421
  ? closedAt
421
422
  : resolveIsoOrRelative(options.completedAt, new Date(closedAt), "completed-at");
@@ -457,4 +458,4 @@ export function runClose(id, closeReasonText, options, global) {
457
458
  return closeItem(id, closeReasonText, options, global);
458
459
  }
459
460
  //# sourceMappingURL=close.js.map
460
- //# debugId=781b5118-7b07-53c7-802f-c889cb797a96
461
+ //# debugId=9f28f7d8-993b-5201-b606-d1eab0c3ff5c
@@ -30,6 +30,8 @@ export interface NotesCommandOptions {
30
30
  includeMeta?: boolean;
31
31
  /** Return complete note history after a mutation instead of a bounded receipt. */
32
32
  fullHistory?: boolean;
33
+ /** Append only when no note has the same resolved author and text. */
34
+ ifAbsent?: boolean;
33
35
  /** Value that configures or reports author for this contract. */
34
36
  author?: string;
35
37
  /** Human-readable explanation suitable for logs and agent-facing output. */
@@ -59,6 +61,8 @@ export interface NotesResult {
59
61
  mutation_receipt?: AnnotationMutationReceipt;
60
62
  /** Declares whether older notes were withheld from a mutation response. */
61
63
  omission_receipt?: AnnotationOmissionReceipt;
64
+ /** Whether a requested mutation changed persisted state. */
65
+ changed?: boolean;
62
66
  }
63
67
  /** Implements run notes for the public runtime surface of this module. */
64
68
  export declare function runNotes(id: string, options: NotesCommandOptions, global: GlobalOptions): Promise<NotesResult>;
package/dist/sdk/notes.js CHANGED
@@ -4,7 +4,7 @@
4
4
  * Implements the pm notes command surface and its agent-facing runtime behavior.
5
5
  */
6
6
 
7
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="ebd9f151-593f-5081-97da-d78f83de4914")}catch(e){}}();
7
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="216389a2-fb85-56a0-8ef2-9f41f1321c16")}catch(e){}}();
8
8
  import { EXIT_CODE, PmCliError, parseLimit, stableStringify, } from "./runtime-primitives.js";
9
9
  import { limitAnnotationEntries, parseAnnotationTextInput, resolveAnnotationInput, runAnnotationCommand, } from "./annotations.js";
10
10
  function parseStructuredEventPayload(raw) {
@@ -137,4 +137,4 @@ export async function runNotes(id, options, global) {
137
137
  };
138
138
  }
139
139
  //# sourceMappingURL=notes.js.map
140
- //# debugId=ebd9f151-593f-5081-97da-d78f83de4914
140
+ //# debugId=216389a2-fb85-56a0-8ef2-9f41f1321c16
@@ -5,7 +5,7 @@
5
5
  * primitives are shared by native action dispatchers and MCP-specific adapters.
6
6
  */
7
7
 
8
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="7949205a-ab1b-5457-961c-593498aff88b")}catch(e){}}();
8
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="fde4fea5-94d6-53e2-bfbc-bff71d828c01")}catch(e){}}();
9
9
  import { EXIT_CODE } from "../core/shared/constants.js";
10
10
  import { PmCliError } from "../core/shared/errors.js";
11
11
  import { asRecordClone } from "../core/shared/primitives.js";
@@ -202,6 +202,9 @@ const HOISTED_ACTION_OPTION_KEYS = {
202
202
  update: ["parent", "allowMissingParent", "completedAt"],
203
203
  copy: ["allowDuplicate"],
204
204
  close: ["duplicateOf", "completedAt"],
205
+ comments: ["ifAbsent"],
206
+ notes: ["ifAbsent"],
207
+ learnings: ["ifAbsent"],
205
208
  upgrade: [
206
209
  "scope",
207
210
  "dryRun",
@@ -586,4 +589,4 @@ export function updateManyOptionsFromFlat(options) {
586
589
  };
587
590
  }
588
591
  //# sourceMappingURL=runtime-input.js.map
589
- //# debugId=7949205a-ab1b-5457-961c-593498aff88b
592
+ //# debugId=fde4fea5-94d6-53e2-bfbc-bff71d828c01
@@ -157,11 +157,11 @@ export declare class PmClient {
157
157
  stats<Options extends ReadOptions<StatsCommandOptions> = StatsCommandOptions>(options?: Options): ReadPromise<StatsResult, Options>;
158
158
  /** Discover existing duplicate clusters without mutating tracker state. */
159
159
  duplicates<Options extends ReadOptions<DuplicatesCommandOptions> = DuplicatesCommandOptions>(options?: Options): ReadPromise<DuplicatesResult, Options>;
160
- /** List, add, edit, or delete item comments. */
160
+ /** List or mutate comments, including lock-scoped idempotent appends. */
161
161
  comments<Options extends ReadOptions<CommentsCommandOptions> = CommentsCommandOptions>(id: string, options?: Options): ReadPromise<CommentsResult, Options>;
162
- /** List or append private item notes. */
162
+ /** List or mutate private notes, including lock-scoped idempotent appends. */
163
163
  notes<Options extends ReadOptions<NotesCommandOptions> = NotesCommandOptions>(id: string, options?: Options): ReadPromise<NotesResult, Options>;
164
- /** List or append durable item learnings. */
164
+ /** List or mutate durable learnings, including lock-scoped idempotent appends. */
165
165
  learnings(id: string, options?: LearningsCommandOptions): Promise<LearningsResult>;
166
166
  /** Add, remove, clear, or list linked project files for an item. */
167
167
  files<Options extends ReadOptions<FilesCommandOptions> = FilesCommandOptions>(id: string, options?: Options): ReadPromise<FilesResult, Options>;
@@ -373,11 +373,11 @@ export declare function aggregate<Options extends ReadOptions<AggregateOptions>
373
373
  export declare function stats<Options extends ReadOptions<StatsCommandOptions> = StatsCommandOptions>(options?: Options, clientOptions?: PmClientOptions): ReadPromise<StatsResult, Options>;
374
374
  /** Discover duplicate clusters without constructing a reusable client. */
375
375
  export declare function duplicates<Options extends ReadOptions<DuplicatesCommandOptions> = DuplicatesCommandOptions>(options?: Options, clientOptions?: PmClientOptions): ReadPromise<DuplicatesResult, Options>;
376
- /** List, add, edit, or delete item comments without constructing a reusable client. */
376
+ /** List or mutate comments with optional idempotency without constructing a client. */
377
377
  export declare function comments<Options extends ReadOptions<CommentsCommandOptions> = CommentsCommandOptions>(id: string, options?: Options, clientOptions?: PmClientOptions): ReadPromise<CommentsResult, Options>;
378
- /** List or append private item notes without constructing a reusable client. */
378
+ /** List or mutate private notes with optional idempotency without constructing a client. */
379
379
  export declare function notes<Options extends ReadOptions<NotesCommandOptions> = NotesCommandOptions>(id: string, options?: Options, clientOptions?: PmClientOptions): ReadPromise<NotesResult, Options>;
380
- /** List or append durable item learnings without constructing a reusable client. */
380
+ /** List or mutate durable learnings with optional idempotency without constructing a client. */
381
381
  export declare function learnings(id: string, options?: LearningsCommandOptions, clientOptions?: PmClientOptions): Promise<LearningsResult>;
382
382
  /** Manage linked item files without constructing a reusable client. */
383
383
  export declare function files<Options extends ReadOptions<FilesCommandOptions> = FilesCommandOptions>(id: string, options?: Options, clientOptions?: PmClientOptions): ReadPromise<FilesResult, Options>;
@@ -4,7 +4,7 @@
4
4
  * Defines public SDK APIs and package-author helpers for Runtime.
5
5
  */
6
6
 
7
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="72111aa2-52b2-53da-a1c8-07ba53a74944")}catch(e){}}();
7
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="b67ba0fe-bbca-57ab-b3be-a4a028f21de4")}catch(e){}}();
8
8
  export { PM_GITIGNORE_END, PM_GITIGNORE_START, ensurePmGitignore, getPmGitignoreBlock, } from "./workspace.js";
9
9
  export { SEARCH_EXTENSION_FLAG_DEFINITIONS } from "./extension-contracts.js";
10
10
  export * from "./cli-contracts/agent-output-contracts.js";
@@ -212,15 +212,15 @@ export class PmClient {
212
212
  duplicates(options = {}) {
213
213
  return this.runTyped("duplicates", { options });
214
214
  }
215
- /** List, add, edit, or delete item comments. */
215
+ /** List or mutate comments, including lock-scoped idempotent appends. */
216
216
  comments(id, options = {}) {
217
217
  return this.runTyped("comments", { id, options });
218
218
  }
219
- /** List or append private item notes. */
219
+ /** List or mutate private notes, including lock-scoped idempotent appends. */
220
220
  notes(id, options = {}) {
221
221
  return this.runTyped("notes", { id, options });
222
222
  }
223
- /** List or append durable item learnings. */
223
+ /** List or mutate durable learnings, including lock-scoped idempotent appends. */
224
224
  learnings(id, options = {}) {
225
225
  return this.runTyped("learnings", { id, options });
226
226
  }
@@ -744,15 +744,15 @@ export function stats(options = {}, clientOptions = {}) {
744
744
  export function duplicates(options = {}, clientOptions = {}) {
745
745
  return new PmClient(clientOptions).duplicates(options);
746
746
  }
747
- /** List, add, edit, or delete item comments without constructing a reusable client. */
747
+ /** List or mutate comments with optional idempotency without constructing a client. */
748
748
  export function comments(id, options = {}, clientOptions = {}) {
749
749
  return new PmClient(clientOptions).comments(id, options);
750
750
  }
751
- /** List or append private item notes without constructing a reusable client. */
751
+ /** List or mutate private notes with optional idempotency without constructing a client. */
752
752
  export function notes(id, options = {}, clientOptions = {}) {
753
753
  return new PmClient(clientOptions).notes(id, options);
754
754
  }
755
- /** List or append durable item learnings without constructing a reusable client. */
755
+ /** List or mutate durable learnings with optional idempotency without constructing a client. */
756
756
  export function learnings(id, options = {}, clientOptions = {}) {
757
757
  return new PmClient(clientOptions).learnings(id, options);
758
758
  }
@@ -1933,4 +1933,4 @@ async function loadWorkspaceExtensionRegistrations(pmRoot, settings, cwd) {
1933
1933
  }
1934
1934
  }
1935
1935
  //# sourceMappingURL=runtime.js.map
1936
- //# debugId=72111aa2-52b2-53da-a1c8-07ba53a74944
1936
+ //# debugId=b67ba0fe-bbca-57ab-b3be-a4a028f21de4
package/docs/COMMANDS.md CHANGED
@@ -735,15 +735,16 @@ explicitly overriding terminal-state or lock conflicts.
735
735
 
736
736
  ```bash
737
737
  pm comments <id> "Implemented command parsing fix."
738
+ pm comments <id> "Implemented command parsing fix." --author agent-a --if-absent
738
739
  printf '%s\n' '## Verification summary' '- Linux pass' '- macOS pass' | pm comments <id> --stdin
739
740
  pm comments <id> --file docs/release-evidence.md
740
741
  pm comments <id> --edit 2 "Corrected: the regression was in the parser, not the renderer."
741
742
  pm comments <id> --delete 3
742
- pm notes <id> --add "Keep renderer changes isolated to TOON output."
743
- pm learnings <id> --add "Use runtime contracts instead of duplicating flag lists."
743
+ pm notes <id> --add "Keep renderer changes isolated to TOON output." --if-absent
744
+ pm learnings <id> --add "Use runtime contracts instead of duplicating flag lists." --if-absent
744
745
  ```
745
746
 
746
- Use comments for progress and evidence, notes for implementation context, and learnings for durable future guidance. All three accept `--text` as a hidden compatibility alias for canonical `--add`; comments also retain `--body`/`--comment`, and notes retain `--note`. Choose exactly one input source (`[text]`, an add alias, `--stdin`, or `--file`) per invocation. Conflicting alias values fail before mutation. To clean up obsolete orchestration notes, `--edit <index>` rewrites the comment at a 1-based index and `--delete <index>` removes it; both record history and honor ownership rules.
747
+ Use comments for progress and evidence, notes for implementation context, and learnings for durable future guidance. All three accept `--text` as a hidden compatibility alias for canonical `--add`; comments also retain `--body`/`--comment`, and notes retain `--note`. Choose exactly one input source (`[text]`, an add alias, `--stdin`, or `--file`) per invocation. Conflicting alias values fail before mutation. For retrying agents, `--if-absent` makes any annotation append idempotent by the resolved author plus exact normalized stored text: the first call returns `changed: true` and `mutation_receipt.changed_count: 1`; an exact retry returns `changed: false`, the existing entry position, and `changed_count: 0` without writing item or history state. Different authors remain distinct, and omitting `--if-absent` deliberately preserves duplicate-appending compatibility. The flag is append-only and is rejected for list, edit, and delete operations. To clean up obsolete orchestration notes, `--edit <index>` rewrites the comment at a 1-based index and `--delete <index>` removes it; both record history and honor ownership rules.
747
748
 
748
749
  ## Linked Artifacts
749
750
 
@@ -1,6 +1,6 @@
1
1
  # Output Token Accounting
2
2
 
3
- Tracker references: [pm-t5dt4z](../.agents/pm/tasks/pm-t5dt4z.toon) and [pm-g3n00m](../.agents/pm/stories/pm-g3n00m.toon).
3
+ Tracker references: [pm-t5dt4z](../.agents/pm/tasks/pm-t5dt4z.toon), [pm-g3n00m](../.agents/pm/stories/pm-g3n00m.toon), [pm-8pnj](../.agents/pm/features/pm-8pnj.toon), [pm-f05lsg](../.agents/pm/features/pm-f05lsg.toon), and [pm-srns](../.agents/pm/issues/pm-srns.toon).
4
4
 
5
5
  ## Agent Quick Context
6
6
 
@@ -39,20 +39,33 @@ The command still exits with its normal non-zero status; the receipt is additive
39
39
 
40
40
  ## Release-Level Task Entitlement
41
41
 
42
- [`agent-task-token-baseline.json`](agent-task-token-baseline.json) is the externally shipped release baseline. The gate executes the built CLI in an isolated workspace and covers:
42
+ [`agent-task-transcripts.json`](agent-task-transcripts.json) is the SDK-validated, versioned golden corpus. [`agent-task-token-baseline.json`](agent-task-token-baseline.json) is its externally shipped release ratchet. The gate executes the built CLI against independent, identically seeded accounting-on and accounting-off workspaces. Its five complete workflows cover:
43
43
 
44
- - a small-workspace read;
45
- - a scaled-workspace context read;
46
- - a returning-agent item read with a required-field completeness assertion;
47
- - a failing command with bounded recovery output.
44
+ - bounded triage, scaled-workspace orientation, and returning-agent inspection;
45
+ - a closed-domain refusal followed by the exact advertised shell-free retry;
46
+ - an unknown option after valid flags followed by a corrected command;
47
+ - create, inspect, close, and final-state confirmation through mutation receipts;
48
+ - successful bulk partial-effect and no-effect exits without collapsing them into exit zero.
48
49
 
49
- Each invocation is independently byte-counted, its section sum is checked, and its consumed field is retained. A seeded million-token regression proves the ratchet fails. Run it with:
50
+ Every step verifies its public SDK output family, canonical successful or refusal exit status, required own-property paths, declared `expected_field_values`, and refusal identity where applicable. Recovery steps must declare a successful output family instead of chaining one refusal to another, every refusal in a completed task must have a later successful `recovery_for` step, and every completed task must terminate with successful output. Successful steps cannot carry refusal-only metadata. Dot-separated `required_fields` and `expected_field_values` paths are traversed structurally from the output root, so incidental prose or nested key names cannot satisfy completeness or terminal-state assertions. The report publishes bytes and estimated tokens for each step and completed task, retry counts, corpus digest, and composite cost. Accounting-on application payloads must be byte-equivalent to their independently captured accounting-off payloads after removing only the receipt. Receipt byte and token fields are independently measured rather than trusted. Runtime refusals verify that their self-reported `total_bytes` matches the independent transport and that `total_estimated_tokens` equals `ceil(total_bytes / 4)`; Commander usage refusals that happen before accounting attachment are measured directly from the captured transport and labeled `independent_transport`.
51
+
52
+ The baseline fails closed on corpus digest, task identity, step identity, missing or non-finite per-step and per-task ceilings, and missing or non-finite composite cost ceilings. A seeded million-token completed-task regression proves the ratchet fails. Run it with:
50
53
 
51
54
  ```bash
52
55
  pnpm quality:agent-task-token
53
56
  node scripts/release/agent-task-token-gate.mjs --negative-control
54
57
  ```
55
58
 
59
+ Package authors can validate their own corpus with the same public contract before replay:
60
+
61
+ ```ts
62
+ import { parsePmAgentTaskTranscriptCorpus } from "@unbrained/pm-cli/sdk/contracts";
63
+
64
+ const corpus = parsePmAgentTaskTranscriptCorpus(JSON.parse(source));
65
+ ```
66
+
67
+ The parser rejects unknown versions, empty tasks or steps, duplicate identities, output families that disagree with the command contract, refusal-only metadata on successful steps, terminal or otherwise unrecovered refusals, and recovery edges that do not point from a successful step to an earlier refusal.
68
+
56
69
  Refresh the committed ceiling only after an intentional reviewed output change:
57
70
 
58
71
  ```bash
package/docs/RELEASING.md CHANGED
@@ -29,6 +29,8 @@ provenance is tracked by [pm-u1baah](../.agents/pm/issues/pm-u1baah.toon), and
29
29
  authoritative blocker-recovery run selection by
30
30
  [pm-db8onn](../.agents/pm/issues/pm-db8onn.toon), and queued automatic
31
31
  same-day recovery by [pm-dm2vfz](../.agents/pm/issues/pm-dm2vfz.toon).
32
+ Bounded Sentry request latency is tracked by
33
+ [pm-b9g2cs](../.agents/pm/issues/pm-b9g2cs.toon).
32
34
 
33
35
  ## Version Policy
34
36
 
@@ -210,6 +212,12 @@ events, and every `generic_failure` or `dependency_failed` remain blocking.
210
212
  This keeps rewording independent from release policy and makes stale or broad
211
213
  message allowlists impossible.
212
214
 
215
+ Sentry API requests use a 120-second deadline by default. Operators can set
216
+ `--sentry-request-timeout-ms` between `1` and `300000` when reproducing
217
+ provider latency, while the emitted gate receipt records the effective value.
218
+ The release workflow pins `120000`; query timeouts remain fail-closed and must
219
+ not be treated as an empty issue set.
220
+
213
221
  If private reliability checks identify repeated user friction, either confirm the current release already contains the remediation with regression coverage or fix it before continuing.
214
222
 
215
223
  The build writes `dist/cli-bundle/bundle-manifest.json` atomically with SHA-256 digests for every emitted bundle file. At startup, `pm` reports `bundle_integrity_torn_install` only when a module-loader failure is accompanied by manifest proof that an upgrade or rebuild changed, removed, or corrupted the active bundle. Reinstall `@unbrained/pm-cli` and retry after that diagnostic. Ordinary `ERR_MODULE_NOT_FOUND` and export failures with an intact manifest remain unexpected failures and must continue to block reliability gates.
@@ -358,7 +366,7 @@ git push origin v<version>
358
366
  tracked source path (apart from managed-extension install metadata).
359
367
  - static quality gate (shared complexity, duplication, dead/orphan module, file/folder hygiene, source/exported docstring coverage profile)
360
368
  - temporary-project compatibility gate against latest published tracker data
361
- - reliability threshold gate (Sentry severity threshold, bounded to a recent-activity window via `--sentry-window-days` (default `14`, `0` = unbounded) so a stale benign unresolved issue cannot block every scheduled release; `--telemetry-mode` gate policy: `off` | `best-effort` | `required`). Scheduled `auto-release.yml` failures open/update an `Auto Release blocked` GitHub issue so blocked daily releases are never silently skipped.
369
+ - reliability threshold gate (Sentry severity threshold, bounded to a recent-activity window via `--sentry-window-days` (default `14`, `0` = unbounded) so a stale benign unresolved issue cannot block every scheduled release; Sentry requests use the fail-closed bounded `--sentry-request-timeout-ms` contract (default `120000`, maximum `300000`); `--telemetry-mode` gate policy: `off` | `best-effort` | `required`). Scheduled `auto-release.yml` failures open/update an `Auto Release blocked` GitHub issue so blocked daily releases are never silently skipped.
362
370
  - sandboxed `pm` coverage
363
371
  - optional Sentry release metadata and sourcemap upload when `SENTRY_AUTH_TOKEN` is configured
364
372
  - npm pack dry run and npx tarball smoke test
package/docs/SDK.md CHANGED
@@ -1512,6 +1512,18 @@ edit/delete semantics, ownership guidance, history mutation metadata, and stable
1512
1512
  list pagination. MCP tool actions intentionally omit file input to prevent host
1513
1513
  filesystem access. Package authors can build custom annotation presentation
1514
1514
  layers without importing CLI modules.
1515
+ `CommentsCommandOptions.ifAbsent`, `NotesCommandOptions.ifAbsent`, and
1516
+ `LearningsCommandOptions.ifAbsent` give retrying agents one explicit idempotent
1517
+ annotation append contract. Equality is evaluated under the item writer lock
1518
+ after author resolution and text normalization, using the resolved author plus
1519
+ exact stored text. The first append returns `changed: true` and
1520
+ `mutation_receipt.changed_count: 1`; an exact retry returns the existing entry,
1521
+ `changed: false`, and `changed_count: 0` without changing the item, history, or
1522
+ derived search state. Different authors remain distinct, and the default
1523
+ without `ifAbsent` continues to append intentional duplicates. The option is
1524
+ valid only for append input and fails closed for list, edit, or delete modes.
1525
+ CLI `comments|notes|learnings --if-absent` and the corresponding MCP actions
1526
+ with `options.ifAbsent: true` are thin transports over this SDK behavior.
1515
1527
  `PmClient.notes` also accepts `addJson` for a validated structured context event. The persisted entry remains backward-readable through canonical `text` while exposing typed `format: "json"`, `data`, and `event_type` fields. `since`, `eventType`, `limit`, and `includeMeta` form the bounded query contract; the collection continues to use field-aware union merge semantics for concurrent branches.
1516
1528
 
1517
1529
  Customization convenience methods are the SDK baseline for project-specific pm
@@ -1,6 +1,6 @@
1
1
  # SDK Context Integrity
2
2
 
3
- Tracker: [pm-0k19l7](../.agents/pm/issues/pm-0k19l7.toon), [pm-9stazf](../.agents/pm/issues/pm-9stazf.toon), [pm-tu71](../.agents/pm/issues/pm-tu71.toon), [pm-0xmajx](../.agents/pm/issues/pm-0xmajx.toon), [pm-7rrqsk](../.agents/pm/issues/pm-7rrqsk.toon), [pm-ety1qc](../.agents/pm/issues/pm-ety1qc.toon), [pm-lu6sca](../.agents/pm/features/pm-lu6sca.toon), [pm-5y05kq](../.agents/pm/issues/pm-5y05kq.toon), [pm-gjjurs](../.agents/pm/issues/pm-gjjurs.toon), [pm-h97qxd](../.agents/pm/issues/pm-h97qxd.toon), [pm-h06944](../.agents/pm/issues/pm-h06944.toon), [pm-5t33or](../.agents/pm/features/pm-5t33or.toon), [pm-in23qu](../.agents/pm/issues/pm-in23qu.toon), [pm-h8tpeh](../.agents/pm/features/pm-h8tpeh.toon), [pm-okgxwa](../.agents/pm/issues/pm-okgxwa.toon), [pm-22rzjp](../.agents/pm/issues/pm-22rzjp.toon), [pm-76fkpp](../.agents/pm/issues/pm-76fkpp.toon), [pm-igdvfq](../.agents/pm/issues/pm-igdvfq.toon), [pm-643e0k](../.agents/pm/issues/pm-643e0k.toon), [pm-larv4r](../.agents/pm/issues/pm-larv4r.toon), [pm-mcxk8v](../.agents/pm/issues/pm-mcxk8v.toon), and [pm-2zkvxm](../.agents/pm/issues/pm-2zkvxm.toon).
3
+ Tracker: [pm-0k19l7](../.agents/pm/issues/pm-0k19l7.toon), [pm-9stazf](../.agents/pm/issues/pm-9stazf.toon), [pm-tu71](../.agents/pm/issues/pm-tu71.toon), [pm-0xmajx](../.agents/pm/issues/pm-0xmajx.toon), [pm-7rrqsk](../.agents/pm/issues/pm-7rrqsk.toon), [pm-ety1qc](../.agents/pm/issues/pm-ety1qc.toon), [pm-lu6sca](../.agents/pm/features/pm-lu6sca.toon), [pm-5y05kq](../.agents/pm/issues/pm-5y05kq.toon), [pm-gjjurs](../.agents/pm/issues/pm-gjjurs.toon), [pm-h97qxd](../.agents/pm/issues/pm-h97qxd.toon), [pm-h06944](../.agents/pm/issues/pm-h06944.toon), [pm-5t33or](../.agents/pm/features/pm-5t33or.toon), [pm-in23qu](../.agents/pm/issues/pm-in23qu.toon), [pm-h8tpeh](../.agents/pm/features/pm-h8tpeh.toon), [pm-okgxwa](../.agents/pm/issues/pm-okgxwa.toon), [pm-22rzjp](../.agents/pm/issues/pm-22rzjp.toon), [pm-76fkpp](../.agents/pm/issues/pm-76fkpp.toon), [pm-igdvfq](../.agents/pm/issues/pm-igdvfq.toon), [pm-643e0k](../.agents/pm/issues/pm-643e0k.toon), [pm-larv4r](../.agents/pm/issues/pm-larv4r.toon), [pm-mcxk8v](../.agents/pm/issues/pm-mcxk8v.toon), [pm-2zkvxm](../.agents/pm/issues/pm-2zkvxm.toon), and [pm-ea1yh2](../.agents/pm/issues/pm-ea1yh2.toon).
4
4
 
5
5
  Current closure tranche: [pm-fs8q9x](../.agents/pm/tasks/pm-fs8q9x.toon), [pm-gy885b](../.agents/pm/issues/pm-gy885b.toon), and [pm-f05lsg](../.agents/pm/features/pm-f05lsg.toon).
6
6
 
@@ -82,6 +82,17 @@ The structured `option_scope` is `declared_on_path`, `declared_elsewhere`, or
82
82
  case, while the third names the nearest current-path spellings and explicitly
83
83
  terminates the otherwise-unbounded command search.
84
84
 
85
+ ## Retry-safe annotation mutations
86
+
87
+ Comments, notes, and learnings expose one SDK-owned `ifAbsent` append contract.
88
+ The item writer lock compares the resolved author and exact normalized stored
89
+ text, so concurrent retries create one entry and one history event. The winning
90
+ append reports `changed: true` and `mutation_receipt.changed_count: 1`; later
91
+ exact retries return the existing entry with `changed: false` and
92
+ `changed_count: 0`. Default appends remain duplicate-preserving. CLI
93
+ `--if-absent` and MCP `ifAbsent` are thin transports, and `--full-history`
94
+ remains the explicit escape hatch from bounded mutation receipts.
95
+
85
96
  ## Semantic flag and spelling contracts
86
97
 
87
98
  `listPmFlagLexicon()` classifies flags by meaning rather than spelling alone.
@@ -61,6 +61,16 @@ published error vocabulary while still sharing classification, path redaction,
61
61
  and recovery guidance. Workspace snapshots use this compatibility path for
62
62
  their stable storage, resource, and permission fault codes.
63
63
 
64
+ Package archives use one bounded validation and extraction boundary whether
65
+ they come from a local path or `npm pack`. The SDK rejects links, escaping
66
+ paths, unsupported entry types, oversized archives, and decompression growth
67
+ before extraction. If npm reports an archive it did not create, callers receive
68
+ the path-redacted `npm_package_archive_missing` refusal instead of a raw system
69
+ `tar` exception; an archive reported outside the isolated pack destination is
70
+ rejected as `npm_package_archive_unsafe`. This keeps package install behavior
71
+ portable and prevents an untrusted registry artifact or package-manager result
72
+ from bypassing the local-archive policy.
73
+
64
74
  ## CLI refusal ownership
65
75
 
66
76
  CLI adapters preserve SDK error codes, exit semantics, and actionable recovery
package/docs/TESTING.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  This page describes safe local tests, linked tests, coverage, and release-readiness checks.
4
4
 
5
- Tracked implementation updates: [pm-52eh](../.agents/pm/features/pm-52eh.toon), [pm-mcxr](../.agents/pm/issues/pm-mcxr.toon), [pm-u42x](../.agents/pm/issues/pm-u42x.toon), [pm-atfm](../.agents/pm/features/pm-atfm.toon), [pm-xmp5](../.agents/pm/tasks/pm-xmp5.toon), [pm-39cqqx](../.agents/pm/tasks/pm-39cqqx.toon), [pm-5cgm2z](../.agents/pm/chores/pm-5cgm2z.toon), [pm-avv3wx](../.agents/pm/issues/pm-avv3wx.toon), [pm-rizqb6](../.agents/pm/issues/pm-rizqb6.toon), [pm-95h7pg](../.agents/pm/issues/pm-95h7pg.toon), [pm-giks4s](../.agents/pm/issues/pm-giks4s.toon), [pm-xa3t0o](../.agents/pm/issues/pm-xa3t0o.toon), [pm-e97jyf](../.agents/pm/issues/pm-e97jyf.toon), [pm-efkvdy](../.agents/pm/issues/pm-efkvdy.toon), and [pm-ed28wi](../.agents/pm/issues/pm-ed28wi.toon).
5
+ Tracked implementation updates: [pm-52eh](../.agents/pm/features/pm-52eh.toon), [pm-mcxr](../.agents/pm/issues/pm-mcxr.toon), [pm-u42x](../.agents/pm/issues/pm-u42x.toon), [pm-atfm](../.agents/pm/features/pm-atfm.toon), [pm-xmp5](../.agents/pm/tasks/pm-xmp5.toon), [pm-39cqqx](../.agents/pm/tasks/pm-39cqqx.toon), [pm-5cgm2z](../.agents/pm/chores/pm-5cgm2z.toon), [pm-avv3wx](../.agents/pm/issues/pm-avv3wx.toon), [pm-rizqb6](../.agents/pm/issues/pm-rizqb6.toon), [pm-95h7pg](../.agents/pm/issues/pm-95h7pg.toon), [pm-giks4s](../.agents/pm/issues/pm-giks4s.toon), [pm-xa3t0o](../.agents/pm/issues/pm-xa3t0o.toon), [pm-e97jyf](../.agents/pm/issues/pm-e97jyf.toon), [pm-efkvdy](../.agents/pm/issues/pm-efkvdy.toon), [pm-ed28wi](../.agents/pm/issues/pm-ed28wi.toon), and [pm-5ug5xq](../.agents/pm/issues/pm-5ug5xq.toon).
6
6
 
7
7
  ## Agent Quick Context
8
8
 
@@ -43,7 +43,11 @@ claims to the same canonical gate IDs. Hosted-only environment isolation and
43
43
  tracker-integrity steps remain explicit entries with reasons rather than
44
44
  silently disappearing from local parity.
45
45
 
46
- `node scripts/run-tests.mjs` wraps Vitest in temporary tracker roots, then cleans them up.
46
+ `node scripts/run-tests.mjs` wraps Vitest in temporary tracker roots, disables
47
+ external Sentry delivery for the build, test workers, and their nested CLI
48
+ children, then cleans the roots up. Instrumentation tests can still exercise
49
+ Sentry initialization through their mocked module boundary; ordinary negative
50
+ fixtures must never create production incidents from a developer host.
47
51
 
48
52
  Public SDK changes additionally run semantic surface and import-cost contracts:
49
53