@unbrained/pm-cli 2026.8.21 → 2026.8.23

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 (228) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +44 -0
  3. package/dist/cli/error-guidance.d.ts +22 -4
  4. package/dist/cli/error-guidance.js +73 -16
  5. package/dist/cli/main.d.ts +3 -0
  6. package/dist/cli/main.js +51 -9
  7. package/dist/cli/register-mutation.js +6 -6
  8. package/dist/cli/register-operations.d.ts +73 -0
  9. package/dist/cli/register-operations.js +39 -14
  10. package/dist/cli/register-setup.js +3 -3
  11. package/dist/cli/schema-registration-helpers.d.ts +8 -1
  12. package/dist/cli/schema-registration-helpers.js +14 -5
  13. package/dist/cli-bundle/bundle-manifest.json +149 -149
  14. package/dist/cli-bundle/chunks/chunk-3ENVHX6C.js +36 -0
  15. package/dist/cli-bundle/chunks/{chunk-LOVTQ52Z.js → chunk-DW2CGEMU.js} +2 -2
  16. package/dist/cli-bundle/chunks/chunk-EG6CLDZT.js +5 -0
  17. package/dist/cli-bundle/chunks/{chunk-LARJJ3RK.js → chunk-F3AI2JFL.js} +2 -2
  18. package/dist/cli-bundle/chunks/{chunk-N3DSV4CH.js → chunk-HWZ6CELB.js} +8 -8
  19. package/dist/cli-bundle/chunks/chunk-HZBMLSZI.js +201 -0
  20. package/dist/cli-bundle/chunks/chunk-JB4KZWBF.js +3 -0
  21. package/dist/cli-bundle/chunks/chunk-KPH73XXJ.js +2 -0
  22. package/dist/cli-bundle/chunks/{chunk-BKYB45NQ.js → chunk-KXPMB2YL.js} +50 -50
  23. package/dist/cli-bundle/chunks/{chunk-BUNIEJNE.js → chunk-QDVHNHAQ.js} +2 -2
  24. package/dist/cli-bundle/chunks/chunk-TFI5V5N2.js +13 -0
  25. package/dist/cli-bundle/chunks/{register-list-query-F57EWDEF.js → register-list-query-UJB5FMV2.js} +2 -2
  26. package/dist/cli-bundle/chunks/register-mutation-NX5XE4VX.js +20 -0
  27. package/dist/cli-bundle/chunks/register-operations-RXIJIYXC.js +2 -0
  28. package/dist/cli-bundle/chunks/{register-setup-HXU7IZZD.js → register-setup-GFUZEKRE.js} +2 -2
  29. package/dist/cli-bundle/focused-chunks/{chunk-F4YEW5I2.js → chunk-37Z4OEAP.js} +2 -2
  30. package/dist/cli-bundle/focused-chunks/chunk-3UPTYG44.js +8 -0
  31. package/dist/cli-bundle/focused-chunks/chunk-5S7E72FY.js +16 -0
  32. package/dist/cli-bundle/focused-chunks/{chunk-4ERLO7QD.js → chunk-AUFH5H35.js} +2 -2
  33. package/dist/cli-bundle/focused-chunks/chunk-E7X25GC3.js +2 -0
  34. package/dist/cli-bundle/focused-chunks/chunk-FQCSTBWE.js +4 -0
  35. package/dist/cli-bundle/focused-chunks/{chunk-FH6DYED3.js → chunk-FZS4H32O.js} +2 -2
  36. package/dist/cli-bundle/focused-chunks/chunk-IFLWKY2R.js +156 -0
  37. package/dist/cli-bundle/focused-chunks/{chunk-YKBKIEAJ.js → chunk-JOWDB2MH.js} +2 -2
  38. package/dist/cli-bundle/focused-chunks/chunk-LYVSW5VM.js +2 -0
  39. package/dist/cli-bundle/focused-chunks/chunk-MRCNF3U6.js +2 -0
  40. package/dist/cli-bundle/focused-chunks/{chunk-UZE7XUZK.js → chunk-OFVUGTLJ.js} +2 -2
  41. package/dist/cli-bundle/focused-chunks/{chunk-FJ22HNRP.js → chunk-OGLQBL46.js} +2 -2
  42. package/dist/cli-bundle/focused-chunks/chunk-OY7QAV4Y.js +51 -0
  43. package/dist/cli-bundle/focused-chunks/chunk-QGFNI65N.js +26 -0
  44. package/dist/cli-bundle/focused-chunks/{chunk-KDNG7TKQ.js → chunk-R27IDGHD.js} +2 -2
  45. package/dist/cli-bundle/focused-chunks/chunk-UYRFYCDQ.js +5 -0
  46. package/dist/cli-bundle/focused-chunks/{chunk-2NIY5NKG.js → chunk-ZHVA42R5.js} +9 -9
  47. package/dist/cli-bundle/main.js +13 -13
  48. package/dist/cli-bundle/sdk-authoring.js +1 -1
  49. package/dist/cli-bundle/sdk-contracts.js +2 -2
  50. package/dist/cli-bundle/sdk-core.js +40 -40
  51. package/dist/cli-bundle/sdk-governance.js +1 -1
  52. package/dist/cli-bundle/sdk-graph.js +1 -1
  53. package/dist/cli-bundle/sdk-merge.js +33 -32
  54. package/dist/cli-bundle/sdk-query.js +1 -1
  55. package/dist/cli-bundle/sdk-runtime.js +1 -1
  56. package/dist/cli-bundle/sdk-testing.js +1 -1
  57. package/dist/cli-bundle/sdk.js +5 -5
  58. package/dist/core/config/nested-settings.js +8 -2
  59. package/dist/core/config/positional-value.d.ts +1 -1
  60. package/dist/core/config/positional-value.js +5 -2
  61. package/dist/core/extensions/extension-hook-runtime.js +22 -20
  62. package/dist/core/extensions/extension-types.d.ts +8 -1
  63. package/dist/core/extensions/extension-types.js +2 -2
  64. package/dist/core/extensions/loader.js +7 -3
  65. package/dist/core/extensions/service-ownership.d.ts +8 -0
  66. package/dist/core/extensions/service-ownership.js +20 -0
  67. package/dist/core/history/history.js +3 -3
  68. package/dist/core/item/id.js +20 -5
  69. package/dist/core/item/item-format.js +81 -2
  70. package/dist/core/reproducibility/context.d.ts +4 -0
  71. package/dist/core/reproducibility/context.js +14 -8
  72. package/dist/core/shared/author.d.ts +55 -1
  73. package/dist/core/shared/author.js +333 -16
  74. package/dist/core/shared/constants.js +3 -2
  75. package/dist/core/shared/errors.d.ts +4 -0
  76. package/dist/core/shared/errors.js +2 -2
  77. package/dist/core/store/settings-validator.d.ts +1 -0
  78. package/dist/core/store/settings-validator.js +6 -3
  79. package/dist/core/store/settings.js +5 -2
  80. package/dist/mcp/server.d.ts +1 -1
  81. package/dist/mcp/server.js +30 -9
  82. package/dist/sdk/agent/identity-config.d.ts +63 -0
  83. package/dist/sdk/agent/identity-config.js +107 -0
  84. package/dist/sdk/agent/refusal-corpus-contracts.d.ts +76 -0
  85. package/dist/sdk/agent/refusal-corpus-contracts.js +194 -0
  86. package/dist/sdk/agent/refusal-reachability.d.ts +1 -1
  87. package/dist/sdk/authoring.d.ts +1 -1
  88. package/dist/sdk/authoring.js +2 -2
  89. package/dist/sdk/cli-contracts/agent-output-contracts.d.ts +77 -0
  90. package/dist/sdk/cli-contracts/agent-output-contracts.js +326 -2
  91. package/dist/sdk/cli-contracts/commander-mutation-options.js +3 -3
  92. package/dist/sdk/cli-contracts/enum-contracts.d.ts +4 -0
  93. package/dist/sdk/cli-contracts/enum-contracts.js +9 -2
  94. package/dist/sdk/cli-contracts/flag-contracts.js +9 -2
  95. package/dist/sdk/cli-contracts/flag-lexicon-contracts.js +4 -4
  96. package/dist/sdk/cli-contracts/registration-helpers.js +5 -2
  97. package/dist/sdk/cli-contracts/runtime-contracts.d.ts +3 -1
  98. package/dist/sdk/cli-contracts/runtime-contracts.js +4 -3
  99. package/dist/sdk/cli-contracts/tool-parameter-tables.js +22 -2
  100. package/dist/sdk/cli-contracts/tool-schema.d.ts +1 -1
  101. package/dist/sdk/cli-contracts/tool-schema.js +14 -3
  102. package/dist/sdk/cli-contracts.d.ts +4 -4
  103. package/dist/sdk/cli-contracts.js +5 -5
  104. package/dist/sdk/config.d.ts +6 -1
  105. package/dist/sdk/config.js +119 -14
  106. package/dist/sdk/context-intent-contracts.js +4 -2
  107. package/dist/sdk/core-governance.d.ts +1 -1
  108. package/dist/sdk/core-governance.js +3 -3
  109. package/dist/sdk/extension/install-sources.d.ts +1 -1
  110. package/dist/sdk/extension/install-sources.js +6 -3
  111. package/dist/sdk/extension/output-ownership.d.ts +1 -0
  112. package/dist/sdk/extension/output-ownership.js +3 -2
  113. package/dist/sdk/extension/scaffold.js +11 -5
  114. package/dist/sdk/flag-invocation-contracts.js +4 -2
  115. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +74 -2
  116. package/dist/sdk/generated/generated-error-code-catalog-part-2.js +87 -26
  117. package/dist/sdk/governance/assurance-action.js +30 -10
  118. package/dist/sdk/governance/health.d.ts +3 -0
  119. package/dist/sdk/governance/health.js +39 -7
  120. package/dist/sdk/governance/hierarchy-validation.d.ts +29 -0
  121. package/dist/sdk/governance/hierarchy-validation.js +109 -0
  122. package/dist/sdk/governance/validate.d.ts +3 -9
  123. package/dist/sdk/governance/validate.js +84 -111
  124. package/dist/sdk/graph/assembly.d.ts +6 -1
  125. package/dist/sdk/graph/assembly.js +16 -22
  126. package/dist/sdk/graph/governance-contracts.d.ts +1 -1
  127. package/dist/sdk/graph/governance-contracts.js +8 -2
  128. package/dist/sdk/graph/governance.js +46 -2
  129. package/dist/sdk/graph/hierarchy-integrity.d.ts +94 -0
  130. package/dist/sdk/graph/hierarchy-integrity.js +311 -0
  131. package/dist/sdk/graph/index.d.ts +2 -1
  132. package/dist/sdk/graph/index.js +3 -2
  133. package/dist/sdk/graph/remediation.js +32 -2
  134. package/dist/sdk/graph/run.d.ts +31 -2
  135. package/dist/sdk/graph/run.js +80 -31
  136. package/dist/sdk/graph/workspace-relationship-kind-registry.d.ts +8 -0
  137. package/dist/sdk/graph/workspace-relationship-kind-registry.js +28 -0
  138. package/dist/sdk/index.d.ts +8 -3
  139. package/dist/sdk/index.js +9 -4
  140. package/dist/sdk/item-children.js +21 -8
  141. package/dist/sdk/lifecycle/create.js +129 -97
  142. package/dist/sdk/lifecycle/hierarchy-mutation-lock.d.ts +15 -0
  143. package/dist/sdk/lifecycle/hierarchy-mutation-lock.js +25 -0
  144. package/dist/sdk/lifecycle/update.d.ts +2 -0
  145. package/dist/sdk/lifecycle/update.js +174 -84
  146. package/dist/sdk/merge/three-way.js +105 -26
  147. package/dist/sdk/query/complete-list.d.ts +3 -3
  148. package/dist/sdk/query/complete-list.js +46 -3
  149. package/dist/sdk/query/list.d.ts +4 -3
  150. package/dist/sdk/query/list.js +89 -49
  151. package/dist/sdk/read-output-budget.d.ts +21 -0
  152. package/dist/sdk/read-output-budget.js +39 -2
  153. package/dist/sdk/read-output-contracts.d.ts +10 -4
  154. package/dist/sdk/read-output-contracts.js +105 -32
  155. package/dist/sdk/relationship-context.js +3 -4
  156. package/dist/sdk/relationships.js +17 -10
  157. package/dist/sdk/reproducibility/process.d.ts +19 -0
  158. package/dist/sdk/reproducibility/process.js +88 -0
  159. package/dist/sdk/runtime-primitives.d.ts +1 -1
  160. package/dist/sdk/runtime-primitives.js +3 -3
  161. package/dist/sdk/runtime.d.ts +3 -2
  162. package/dist/sdk/runtime.js +7 -5
  163. package/dist/sdk/test/batch.d.ts +6 -0
  164. package/dist/sdk/test/batch.js +26 -6
  165. package/dist/sdk/test/entry.d.ts +1 -1
  166. package/dist/sdk/test/entry.js +3 -2
  167. package/dist/sdk/test/execution.d.ts +28 -3
  168. package/dist/sdk/test/execution.js +201 -34
  169. package/dist/sdk/test/parsers.d.ts +6 -0
  170. package/dist/sdk/test/parsers.js +21 -2
  171. package/dist/sdk/test/trust.d.ts +33 -0
  172. package/dist/sdk/test/trust.js +207 -0
  173. package/dist/types.d.ts +34 -0
  174. package/dist/types.js +2 -2
  175. package/docs/AGENT_PROVENANCE_ADR.md +41 -5
  176. package/docs/AGENT_UX_CONTRACTS.md +20 -4
  177. package/docs/COMMANDS.md +29 -14
  178. package/docs/CONFIGURATION.md +4 -0
  179. package/docs/DEPENDENCY_KIND_CONTRACT.md +31 -3
  180. package/docs/DIAGNOSTIC_OUTPUT_CONTRACTS.md +115 -0
  181. package/docs/EXTENSIONS.md +2 -1
  182. package/docs/MERGE_SAFETY.md +15 -1
  183. package/docs/README.md +1 -0
  184. package/docs/READ_OUTPUT_CONTRACTS.md +19 -2
  185. package/docs/RELATIONSHIP_GRAPH.md +6 -3
  186. package/docs/REPRODUCIBLE_WORKSPACES.md +42 -8
  187. package/docs/SDK.md +9 -0
  188. package/docs/SDK_AGENT_SESSION_CONTEXT.md +6 -4
  189. package/docs/SDK_EVIDENCE_TRACEABILITY.md +1 -1
  190. package/docs/TESTING.md +83 -1
  191. package/docs/agent-task-token-baseline.json +2 -2
  192. package/docs/generated/FLAG_LEXICON_BUDGETS.md +2 -2
  193. package/marketplace.json +2 -2
  194. package/package.json +2 -2
  195. package/packages/pm-beads/package.json +1 -1
  196. package/packages/pm-calendar/package.json +1 -1
  197. package/packages/pm-command-kit/package.json +1 -1
  198. package/packages/pm-digital-twin/package.json +1 -1
  199. package/packages/pm-governance-audit/package.json +1 -1
  200. package/packages/pm-guide-shell/package.json +1 -1
  201. package/packages/pm-kanban/package.json +1 -1
  202. package/packages/pm-lifecycle-hooks/package.json +1 -1
  203. package/packages/pm-linked-test-adapters/package.json +1 -1
  204. package/packages/pm-search-advanced/package.json +1 -1
  205. package/packages/pm-templates/package.json +1 -1
  206. package/packages/pm-todos/package.json +1 -1
  207. package/packages/pm-vcs/package.json +1 -1
  208. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  209. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  210. package/sdk/public-surface.json +1021 -53
  211. package/dist/cli-bundle/chunks/chunk-2MA6ZQVZ.js +0 -197
  212. package/dist/cli-bundle/chunks/chunk-3AFAUHUE.js +0 -2
  213. package/dist/cli-bundle/chunks/chunk-7VHZS727.js +0 -5
  214. package/dist/cli-bundle/chunks/chunk-JSLOFXGA.js +0 -3
  215. package/dist/cli-bundle/chunks/chunk-OCLPLFAY.js +0 -35
  216. package/dist/cli-bundle/chunks/chunk-S4IRYKBU.js +0 -13
  217. package/dist/cli-bundle/chunks/register-mutation-7IY2D5KO.js +0 -20
  218. package/dist/cli-bundle/chunks/register-operations-FALL3YTU.js +0 -2
  219. package/dist/cli-bundle/focused-chunks/chunk-7WZRLCYF.js +0 -50
  220. package/dist/cli-bundle/focused-chunks/chunk-CKKQSJNN.js +0 -2
  221. package/dist/cli-bundle/focused-chunks/chunk-EEFKYMDR.js +0 -16
  222. package/dist/cli-bundle/focused-chunks/chunk-F2A56DHV.js +0 -8
  223. package/dist/cli-bundle/focused-chunks/chunk-FV5ACOK7.js +0 -2
  224. package/dist/cli-bundle/focused-chunks/chunk-JEAVFOWM.js +0 -2
  225. package/dist/cli-bundle/focused-chunks/chunk-NJ63LOP6.js +0 -155
  226. package/dist/cli-bundle/focused-chunks/chunk-NXRTQ7HI.js +0 -2
  227. package/dist/cli-bundle/focused-chunks/chunk-TJ7EMJ46.js +0 -4
  228. package/dist/cli-bundle/focused-chunks/chunk-ZOSEELYJ.js +0 -26
@@ -0,0 +1,207 @@
1
+ /**
2
+ * @module sdk/test/trust
3
+ *
4
+ * Provides provenance and clone-local trust decisions for stored linked-test
5
+ * commands. Tracker items remain portable while execution acknowledgements stay
6
+ * outside the merge-unioned project record.
7
+ */
8
+
9
+ !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]="30a61f26-5db8-50ba-af37-1990a4742ce8")}catch(e){}}();
10
+ import { execFile } from "node:child_process";
11
+ import { readFile } from "node:fs/promises";
12
+ import path from "node:path";
13
+ import { promisify } from "node:util";
14
+ import { ensureDir, writeFileAtomic, } from "../../core/fs/fs-utils.js";
15
+ import { sha256Hex, stableStringify } from "../../core/shared/serialization.js";
16
+ import { isTimestampLiteral } from "../../core/shared/time.js";
17
+ import { getRuntimePath } from "../../core/store/paths.js";
18
+ const execFileAsync = promisify(execFile);
19
+ const LINKED_TEST_TRUST_LEDGER_VERSION = 1;
20
+ const LINKED_TEST_TRUST_LEDGER_FILE = "linked-test-trust.json";
21
+ function trustLedgerPath(pmRoot) {
22
+ return path.join(getRuntimePath(pmRoot), LINKED_TEST_TRUST_LEDGER_FILE);
23
+ }
24
+ function emptyTrustLedger() {
25
+ return { version: LINKED_TEST_TRUST_LEDGER_VERSION, acknowledged: {} };
26
+ }
27
+ async function readTrustLedger(pmRoot) {
28
+ try {
29
+ const parsed = JSON.parse(await readFile(trustLedgerPath(pmRoot), "utf8"));
30
+ if (parsed.version !== LINKED_TEST_TRUST_LEDGER_VERSION ||
31
+ typeof parsed.acknowledged !== "object" ||
32
+ parsed.acknowledged === null ||
33
+ Array.isArray(parsed.acknowledged)) {
34
+ return emptyTrustLedger();
35
+ }
36
+ return {
37
+ version: LINKED_TEST_TRUST_LEDGER_VERSION,
38
+ acknowledged: parsed.acknowledged,
39
+ };
40
+ }
41
+ catch {
42
+ // A missing, corrupt, or unreadable ledger grants no acknowledgements.
43
+ // The local/source-ref provenance checks remain independent of this file.
44
+ return emptyTrustLedger();
45
+ }
46
+ }
47
+ /** Resolve the current Git branch without invoking a shell. */
48
+ export async function resolveLinkedTestSourceRef(cwd = process.cwd()) {
49
+ const hostedRef = process.env.GITHUB_HEAD_REF?.trim() || process.env.GITHUB_REF_NAME?.trim();
50
+ if (hostedRef && hostedRef !== "merge") {
51
+ return hostedRef;
52
+ }
53
+ try {
54
+ const { stdout } = await execFileAsync("git", ["symbolic-ref", "--quiet", "--short", "HEAD"], { cwd, encoding: "utf8", windowsHide: true, timeout: 10_000 });
55
+ const ref = stdout.trim();
56
+ /* c8 ignore start -- successful git symbolic-ref output is non-empty by contract */
57
+ if (ref.length === 0)
58
+ return undefined;
59
+ /* c8 ignore stop */
60
+ return ref;
61
+ }
62
+ catch {
63
+ return undefined;
64
+ }
65
+ }
66
+ /** Resolve the source checkout shared by linked-test provenance and execution. */
67
+ export function resolveLinkedTestSourceWorkspaceRoot(cwd = process.cwd()) {
68
+ return process.env.PM_SOURCE_WORKSPACE_ROOT?.trim() || cwd;
69
+ }
70
+ function hasValidLinkedTestProvenance(provenance) {
71
+ if (provenance === null || typeof provenance !== "object")
72
+ return false;
73
+ return (typeof provenance.author === "string" &&
74
+ provenance.author.trim().length > 0 &&
75
+ typeof provenance.created_at === "string" &&
76
+ isTimestampLiteral(provenance.created_at) &&
77
+ (provenance.source_kind === "local_mutation" ||
78
+ provenance.source_kind === "merge_union"));
79
+ }
80
+ /** Build the optional ref portion shared by clone-local trust decisions. */
81
+ function linkedTestTrustRefs(sourceRef, currentSourceRef) {
82
+ return {
83
+ ...(sourceRef ? { source_ref: sourceRef } : {}),
84
+ ...(currentSourceRef ? { current_source_ref: currentSourceRef } : {}),
85
+ };
86
+ }
87
+ /** Attach immutable author/time/ref provenance to newly stored commands. */
88
+ export function attachLinkedTestProvenance(tests, author, createdAt, sourceRef) {
89
+ return tests?.map((test) => test.command && !test.provenance
90
+ ? {
91
+ ...test,
92
+ provenance: {
93
+ author,
94
+ created_at: createdAt,
95
+ source_kind: "local_mutation",
96
+ ...(sourceRef ? { source_ref: sourceRef } : {}),
97
+ },
98
+ }
99
+ : test);
100
+ }
101
+ /** Attach provenance while avoiding Git inspection for an empty mutation. */
102
+ export async function attachLinkedTestMutationProvenance(tests, author, createdAt) {
103
+ if (!tests || tests.length === 0)
104
+ return tests;
105
+ const sourceWorkspaceRoot = resolveLinkedTestSourceWorkspaceRoot();
106
+ return attachLinkedTestProvenance(tests, author, createdAt, await resolveLinkedTestSourceRef(sourceWorkspaceRoot));
107
+ }
108
+ /** Produce the clone-local trust identity for one linked-test entry. */
109
+ export function linkedTestTrustFingerprint(test) {
110
+ return sha256Hex(stableStringify({
111
+ command: test.command ?? null,
112
+ path: test.path ?? null,
113
+ scope: test.scope,
114
+ pm_context_mode: test.pm_context_mode ?? null,
115
+ workspace_context_mode: test.workspace_context_mode ?? null,
116
+ env_set: test.env_set ?? null,
117
+ env_clear: test.env_clear ?? null,
118
+ shared_host_safe: test.shared_host_safe ?? null,
119
+ provenance: test.provenance ?? null,
120
+ provenance_invalid: test.provenance_invalid ?? null,
121
+ }));
122
+ }
123
+ /** Resolve whether one stored command is trusted by this clone. */
124
+ export async function resolveLinkedTestTrust(pmRoot, test, currentSourceRef) {
125
+ const ledger = await readTrustLedger(pmRoot);
126
+ return resolveLinkedTestTrustFromLedger(test, currentSourceRef, ledger);
127
+ }
128
+ function resolveLinkedTestTrustFromLedger(test, currentSourceRef, ledger) {
129
+ const fingerprint = linkedTestTrustFingerprint(test);
130
+ const sourceRef = test.provenance?.source_ref;
131
+ if (test.provenance === undefined &&
132
+ test.provenance_invalid !== true) {
133
+ return { fingerprint, trusted: true, reason: "legacy" };
134
+ }
135
+ const acknowledged = Object.hasOwn(ledger.acknowledged, fingerprint);
136
+ if (test.provenance_invalid === true ||
137
+ !hasValidLinkedTestProvenance(test.provenance)) {
138
+ if (acknowledged) {
139
+ return {
140
+ fingerprint,
141
+ trusted: true,
142
+ reason: "acknowledged",
143
+ ...linkedTestTrustRefs(undefined, currentSourceRef),
144
+ };
145
+ }
146
+ return {
147
+ fingerprint,
148
+ trusted: false,
149
+ reason: "invalid_provenance",
150
+ ...linkedTestTrustRefs(undefined, currentSourceRef),
151
+ };
152
+ }
153
+ if (test.provenance.source_kind === "local_mutation" &&
154
+ sourceRef === undefined) {
155
+ return {
156
+ fingerprint,
157
+ trusted: true,
158
+ reason: "local_mutation",
159
+ ...linkedTestTrustRefs(undefined, currentSourceRef),
160
+ };
161
+ }
162
+ if (test.provenance.source_kind === "local_mutation" &&
163
+ sourceRef !== undefined &&
164
+ sourceRef === currentSourceRef) {
165
+ return {
166
+ fingerprint,
167
+ trusted: true,
168
+ reason: "local_source_ref",
169
+ source_ref: sourceRef,
170
+ current_source_ref: currentSourceRef,
171
+ };
172
+ }
173
+ if (acknowledged) {
174
+ return {
175
+ fingerprint,
176
+ trusted: true,
177
+ reason: "acknowledged",
178
+ ...linkedTestTrustRefs(sourceRef, currentSourceRef),
179
+ };
180
+ }
181
+ return {
182
+ fingerprint,
183
+ trusted: false,
184
+ reason: "foreign_source_ref",
185
+ ...linkedTestTrustRefs(sourceRef, currentSourceRef),
186
+ };
187
+ }
188
+ /** Resolve a collection with one ledger read for validation and batch runs. */
189
+ export async function resolveLinkedTestTrustBatch(pmRoot, tests, currentSourceRef) {
190
+ const ledger = await readTrustLedger(pmRoot);
191
+ return tests.map((test) => resolveLinkedTestTrustFromLedger(test, currentSourceRef, ledger));
192
+ }
193
+ /** Persist explicit clone-local acknowledgement for selected linked tests. */
194
+ export async function acknowledgeLinkedTests(pmRoot, tests, acknowledgedAt) {
195
+ const ledger = await readTrustLedger(pmRoot);
196
+ const fingerprints = tests
197
+ .filter((test) => typeof test.command === "string" && test.command.length > 0)
198
+ .map(linkedTestTrustFingerprint);
199
+ for (const fingerprint of fingerprints) {
200
+ ledger.acknowledged[fingerprint] = { acknowledged_at: acknowledgedAt };
201
+ }
202
+ await ensureDir(path.dirname(trustLedgerPath(pmRoot)));
203
+ await writeFileAtomic(trustLedgerPath(pmRoot), `${JSON.stringify(ledger, null, 2)}\n`);
204
+ return { acknowledged: fingerprints.length, fingerprints };
205
+ }
206
+ //# sourceMappingURL=trust.js.map
207
+ //# debugId=30a61f26-5db8-50ba-af37-1990a4742ce8
package/dist/types.d.ts CHANGED
@@ -166,6 +166,17 @@ export interface LinkedFile {
166
166
  /** Value that configures or reports note for this contract. */
167
167
  note?: string;
168
168
  }
169
+ /** Valid immutable provenance captured when a linked command enters tracker data. */
170
+ export interface LinkedTestProvenance {
171
+ /** Author identity recorded by the originating mutation. */
172
+ author: string;
173
+ /** ISO 8601 time at which the command entered tracker data. */
174
+ created_at: string;
175
+ /** Mutation path that introduced the command. */
176
+ source_kind: "local_mutation" | "merge_union";
177
+ /** Best-effort Git branch or hosted head ref of the originating mutation. */
178
+ source_ref?: string;
179
+ }
169
180
  /** Documents the linked test payload exchanged by command, SDK, and package integrations. */
170
181
  export interface LinkedTest {
171
182
  /** Value that configures or reports command for this contract. */
@@ -178,6 +189,12 @@ export interface LinkedTest {
178
189
  timeout_seconds?: number;
179
190
  /** Strategy used to control pm context behavior. */
180
191
  pm_context_mode?: "schema" | "tracker" | "auto";
192
+ /** Strategy used for source-workspace visibility and command working directory. */
193
+ workspace_context_mode?: "source" | "isolated" | "snapshot";
194
+ /** Immutable provenance captured when the linked command enters tracker data. */
195
+ provenance?: LinkedTestProvenance;
196
+ /** Explicit fail-closed marker retained when stored provenance is malformed. */
197
+ provenance_invalid?: true;
181
198
  /** Value that configures or reports env set for this contract. */
182
199
  env_set?: Record<string, string>;
183
200
  /** Value that configures or reports env clear for this contract. */
@@ -558,6 +575,19 @@ export interface TestRunMeasurement {
558
575
  /** ISO 8601 timestamp binding the measurement to its producing run. */
559
576
  recorded_at: string;
560
577
  }
578
+ /** Bounded command and tracker-context provenance for one linked-test execution. */
579
+ export interface ItemTestRunExecution {
580
+ /** Exact linked command selected for execution. */
581
+ command: string;
582
+ /** Context mode requested before linked-test overrides or auto-remediation. */
583
+ requested_pm_context_mode?: "schema" | "tracker" | "auto";
584
+ /** Effective context mode used by the linked command. */
585
+ pm_context_mode?: "schema" | "tracker" | "auto";
586
+ /** Effective source-workspace mode used by the linked command. */
587
+ workspace_context_mode?: "source" | "isolated" | "snapshot";
588
+ /** Trust classification applied before execution. */
589
+ trust_reason?: "legacy" | "local_mutation" | "local_source_ref" | "acknowledged" | "invalid_provenance" | "foreign_source_ref";
590
+ }
561
591
  /** Documents the item test run summary payload exchanged by command, SDK, and package integrations. */
562
592
  export interface ItemTestRunSummary {
563
593
  /** Executes the id operation through the package runtime. */
@@ -590,6 +620,8 @@ export interface ItemTestRunSummary {
590
620
  fail_on_skipped_triggered?: boolean;
591
621
  /** Bounded, deterministic quantitative evidence produced by this run. */
592
622
  measurements?: TestRunMeasurement[];
623
+ /** Commands and effective tracker contexts bound to this recorded run. */
624
+ executions?: ItemTestRunExecution[];
593
625
  }
594
626
  /** Documents the item metadata payload exchanged by command, SDK, and package integrations. */
595
627
  export interface ItemMetadata {
@@ -1110,6 +1142,8 @@ export interface PmSettings {
1110
1142
  /** Value that configures or reports testing for this contract. */
1111
1143
  testing: {
1112
1144
  record_results_to_items: boolean;
1145
+ /** Permit explicit one-shot execution of commands not yet trusted by this clone. */
1146
+ allow_untrusted_linked_tests: boolean;
1113
1147
  };
1114
1148
  /** Value that configures or reports telemetry for this contract. */
1115
1149
  telemetry: {
package/dist/types.js CHANGED
@@ -5,7 +5,7 @@
5
5
  */
6
6
  /** Supported values accepted by the builtin item type contract. */
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]="f6ab5bae-53b3-5feb-b89f-46e64afe7058")}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]="64223a83-1287-57ac-915d-dbd5e375a6eb")}catch(e){}}();
9
9
  export const BUILTIN_ITEM_TYPE_VALUES = [
10
10
  "Epic",
11
11
  "Feature",
@@ -243,4 +243,4 @@ export const CONTEXT_SECTION_VALUES = [
243
243
  "tests",
244
244
  ];
245
245
  //# sourceMappingURL=types.js.map
246
- //# debugId=f6ab5bae-53b3-5feb-b89f-46e64afe7058
246
+ //# debugId=64223a83-1287-57ac-915d-dbd5e375a6eb
@@ -10,7 +10,10 @@ Implementation lineage:
10
10
  resolution and patch-free historical reads implemented by
11
11
  [pm-ffz0a9](../.agents/pm/issues/pm-ffz0a9.toon),
12
12
  [pm-v8gfi7](../.agents/pm/issues/pm-v8gfi7.toon), and
13
- [pm-3yxwv5](../.agents/pm/issues/pm-3yxwv5.toon).
13
+ [pm-3yxwv5](../.agents/pm/issues/pm-3yxwv5.toon). Cross-harness adapters and
14
+ operator-managed probe/vocabulary controls are tracked by
15
+ [pm-c0lrdm](../.agents/pm/features/pm-c0lrdm.toon) and
16
+ [pm-yds9dt](../.agents/pm/chores/pm-yds9dt.toon).
14
17
 
15
18
  Status: accepted amendment to
16
19
  [pm-qwuber](../.agents/pm/decisions/pm-qwuber.toon). The original stable-author
@@ -51,10 +54,13 @@ Provenance values are descriptive context, never authentication or
51
54
  authorization principals. Values are trimmed, length-bounded, and obtained only
52
55
  from literal descriptor keys, trusted caller data, or a named bounded resolver.
53
56
  Detection does not spawn processes, traverse process trees, evaluate user
54
- regexes, or access the network. The built-in Claude resolver may read only the
55
- tail of the current session's harness-owned JSONL file to recover its recorded
56
- model/version. It caps file bytes, lines, and line length, extracts only those
57
- two allow-listed values, and fails closed. `agent_identity.probes_enabled` or
57
+ regexes, or access the network. The built-in Claude and Codex resolvers read
58
+ only bounded windows of the current session's harness-owned JSONL file. Claude
59
+ uses the recent tail to recover its recorded model/version. Codex uses the
60
+ recent tail plus a bounded initial-head fallback for oversized sessions to
61
+ recover allow-listed `turn_context.model` and `turn_context.effort` values.
62
+ Both cap traversal, file bytes, lines, and line length, ignore all other fields,
63
+ never follow symlinks, and fail closed. `agent_identity.probes_enabled` or
58
64
  `PM_AGENT_PROBES=off` disables every local resolver without disabling ordinary
59
65
  environment, argv, client, or host detection.
60
66
 
@@ -113,6 +119,11 @@ fabricate precision that was never captured.
113
119
  The aggregate and core SDK entrypoints export:
114
120
 
115
121
  - `AGENT_PROVENANCE_DIMENSIONS`;
122
+ - `BUILTIN_AGENT_PROVENANCE_ADAPTERS`,
123
+ `listAgentProvenanceAdapters()`, and
124
+ `registerAgentProvenanceAdapters()`;
125
+ - `normalizeAgentProvenanceAdapterValue()` for stable model-family and effort
126
+ vocabulary projections that retain the bounded raw observation;
116
127
  - `detectAgentIdentity()` and `detectHarnessIdentity()`;
117
128
  - `analyzeAgentProvenanceDescriptorCoverage()` for the descriptor capability
118
129
  matrix and negative controls;
@@ -138,6 +149,31 @@ Legacy author interpretation is workspace-owned data under
138
149
  version and whether a harness was `recorded`, resolved by `vocabulary`, or
139
150
  remains `unresolved`; immutable authors and hashes are never rewritten.
140
151
 
152
+ `pm config get agent-identity-vocabulary`, `config list`, and `config export`
153
+ publish only its `version` and `alias_count`. Typed mutations use the existing
154
+ config transport:
155
+
156
+ ```bash
157
+ pm config set agent-identity-probes-enabled false
158
+ pm config set agent-identity-vocabulary --policy preview-add \
159
+ --value "Legacy Codex=codex" --criterion "Legacy Codex" --criterion "Alice"
160
+ pm config set agent-identity-vocabulary --policy add \
161
+ --value "Legacy Codex=codex"
162
+ pm config set agent-identity-vocabulary --policy remove --value "Legacy Codex"
163
+ ```
164
+
165
+ `preview-*` policies perform no write and report the exact residual unique
166
+ author count without returning author spellings. Real add/remove/clear changes
167
+ bump the vocabulary revision once; identical adds and absent removes are
168
+ idempotent. Aliases for already canonical `harness:<name>` authors fail closed.
169
+
170
+ Every built-in interactive harness has a contract-versioned adapter with an
171
+ implementation version, priority, covered dimensions, source classes,
172
+ normalization revisions, confidence, waivers, and immutable probe bounds.
173
+ Packages may register a new namespace or explicitly replace a built-in only at
174
+ a higher priority. Equal-priority ambiguity and descriptor mismatch fail
175
+ closed, and the disposer restores the prior adapter.
176
+
141
177
  The SDK/CLI matrix classifies every input as shared, positional, transport,
142
178
  presentation, local adapter, scope selector, compatibility alias, or SDK-native.
143
179
  An unknown CLI flag fails closed as `unclassified`. Committed test baselines cap
@@ -1,6 +1,6 @@
1
1
  # Agent UX Contracts
2
2
 
3
- Tracker references: [pm-v1yo](../.agents/pm/issues/pm-v1yo.toon), [pm-i6pi](../.agents/pm/issues/pm-i6pi.toon), [pm-um4g](../.agents/pm/issues/pm-um4g.toon), [pm-tmhs](../.agents/pm/issues/pm-tmhs.toon), [pm-6m1i](../.agents/pm/issues/pm-6m1i.toon), [pm-cj9v](../.agents/pm/issues/pm-cj9v.toon), [pm-yp56](../.agents/pm/issues/pm-yp56.toon), [pm-gos426](../.agents/pm/issues/pm-gos426.toon), and [pm-flnefm](../.agents/pm/issues/pm-flnefm.toon).
3
+ Tracker references: [pm-v1yo](../.agents/pm/issues/pm-v1yo.toon), [pm-i6pi](../.agents/pm/issues/pm-i6pi.toon), [pm-um4g](../.agents/pm/issues/pm-um4g.toon), [pm-tmhs](../.agents/pm/issues/pm-tmhs.toon), [pm-6m1i](../.agents/pm/issues/pm-6m1i.toon), [pm-cj9v](../.agents/pm/issues/pm-cj9v.toon), [pm-yp56](../.agents/pm/issues/pm-yp56.toon), [pm-gos426](../.agents/pm/issues/pm-gos426.toon), [pm-flnefm](../.agents/pm/issues/pm-flnefm.toon), [pm-rggtvd](../.agents/pm/issues/pm-rggtvd.toon), and [pm-vk7zek](../.agents/pm/issues/pm-vk7zek.toon).
4
4
 
5
5
  These contracts keep common agent loops deterministic, token-efficient, and recoverable. Runtime contracts and `--help --json` remain the exact source for available flags.
6
6
 
@@ -13,9 +13,25 @@ The public SDK exports `collectNewOrderingCycleWarnings(beforeItems, afterItems,
13
13
  Dependency removal is lossless. `--dep-remove` rejects the same malformed
14
14
  shorthand as `--dep`, and a selector that matches nothing returns the typed
15
15
  `dependency_remove_no_match` refusal instead of a successful no-op. Exact
16
- duplicate rows can be normalized without delete-then-add risk by re-adding the
17
- same `id`, `kind`, and `source_kind`; the mutation keeps one canonical row and
18
- never removes the logical edge.
16
+ rows can be retired by selecting `id`, `kind` (or its input-only `type` alias),
17
+ `source_kind`, `author`, and `created_at`; omitted coordinates intentionally broaden the match. Exact
18
+ duplicate rows can still be normalized without delete-then-add risk by
19
+ re-adding the same identity; the mutation keeps one canonical row and never
20
+ removes the logical edge. Identity comparison includes normalized `id`, `kind`,
21
+ `source_kind`, `author`, and creation instant, preserving provenance-distinct
22
+ siblings.
23
+
24
+ Hierarchy mutations are stricter than ordering advisories. `pm create` and
25
+ `pm update` refuse a new cycle, registry cardinality breach, or contradictory
26
+ scalar/dependency parent before writing. The structured refusal names the
27
+ changed holder plus the cycle members or competing parents. Existing legacy
28
+ debt remains mutable only toward a cleaner state, so repair does not require a
29
+ global bypass. Read paths canonicalize every registry-declared hierarchy kind;
30
+ an item cannot disappear from `pm list --parent` or `pm get --tree` merely
31
+ because an integration used a dependency row instead of the scalar field.
32
+ `pm update-many` applies the same hierarchy guard independently to each matched
33
+ item: one refused row is reported as failed while other rows may still commit,
34
+ so a bulk hierarchy mutation is not atomic across the complete match set.
19
35
 
20
36
  `pm graph audit` uses two explicit units:
21
37
 
package/docs/COMMANDS.md CHANGED
@@ -6,6 +6,8 @@ Tracked implementation updates: [pm-52eh](../.agents/pm/features/pm-52eh.toon),
6
6
 
7
7
  Workspace-integrity contracts are tracked by [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), and [pm-larv4r](../.agents/pm/issues/pm-larv4r.toon).
8
8
 
9
+ Create-safe init prefix normalization is tracked by [pm-gh1073](../.agents/pm/issues/pm-gh1073.toon).
10
+
9
11
  ```bash
10
12
  pm <command> --help
11
13
  pm <command> --help --json
@@ -49,21 +51,21 @@ JSON ceiling; TOON uses the smaller default agent ceiling.
49
51
 
50
52
  ## Command Families
51
53
 
52
- | Family | Commands | Purpose |
53
- | ------------ | ------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
54
- | Bootstrap | `init`, `config`, `health`, `telemetry` | create and inspect tracker setup |
54
+ | Family | Commands | Purpose |
55
+ | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
56
+ | Bootstrap | `init`, `config`, `health`, `telemetry` | create and inspect tracker setup |
55
57
  | Lifecycle | `create`, `copy`, `focus`, `claim`, `update`, `item reopen`, `append`, `close`, `release`, `delete`, `start-task`, `pause-task`, `close-task` | mutate item state, including terminal-work recurrence without duplicate creation |
56
- | Bulk | `item mutate`, `item complete`, `update-many`, `close-many` | atomically commit heterogeneous SDK mutation batches or evidence-backed completion, or apply one change across a matched, dry-run-previewed set with a rollback checkpoint |
57
- | Scheduling | `meet`, `event`, `remind` | low-friction Meeting/Event/Reminder creation |
58
- | Planning | `plan create`, `plan add-step`, `plan update-step`, `plan complete-step`, `plan link`, `plan approve`, `plan materialize` | agent-optimized living plans with ordered steps, evidence, decisions, validation, and materialization |
59
- | Links | `files`, `docs`, `test`, `deps` | connect items to artifacts, tests, and relationships |
60
- | Verification | `test`, `test-all`, `test-runs`†, `validate`, `gc` | run linked tests and repository checks |
61
- | History | `history`, `history-compact`, `history-redact`, `history-repair`, `activity`, `restore`, `stats` | inspect, compact, redact, re-anchor, and recover item state |
62
- | Schema | `schema add/remove-*` / `rename-type` / `rename-field` / `remap-status` / `apply-preset` | manage config-driven types, statuses, and fields; losslessly migrate existing items when definitions evolve; adopt presets or infer types |
63
- | Profiles | `profile list` / `show` / `apply` / `lint` | compose item types, statuses, fields, workflows, config, templates, and recommended packages into archetype bundles (agile/ops/research); `apply` stages every dimension idempotently; `lint` reports author-time consistency findings without writing |
64
- | Calendar | `calendar`, `cal` | project deadlines, reminders, and events |
65
- | Packages | `install`, `upgrade`, `package`, `packages`, `extension`, package/extension command groups | install, upgrade, manage, and run package-backed extension commands |
66
- | Machines | `contracts`, `help`, optional `guide`/`completion` | command contracts plus optional guide-shell docs routing and shell helpers |
58
+ | Bulk | `item mutate`, `item complete`, `update-many`, `close-many` | atomically commit heterogeneous SDK mutation batches or evidence-backed completion, or apply one change across a matched, dry-run-previewed set with a rollback checkpoint |
59
+ | Scheduling | `meet`, `event`, `remind` | low-friction Meeting/Event/Reminder creation |
60
+ | Planning | `plan create`, `plan add-step`, `plan update-step`, `plan complete-step`, `plan link`, `plan approve`, `plan materialize` | agent-optimized living plans with ordered steps, evidence, decisions, validation, and materialization |
61
+ | Links | `files`, `docs`, `test`, `deps` | connect items to artifacts, tests, and relationships |
62
+ | Verification | `test`, `test-all`, `test-runs`†, `validate`, `gc` | run linked tests and repository checks |
63
+ | History | `history`, `history-compact`, `history-redact`, `history-repair`, `activity`, `restore`, `stats` | inspect, compact, redact, re-anchor, and recover item state |
64
+ | Schema | `schema add/remove-*` / `rename-type` / `rename-field` / `remap-status` / `apply-preset` | manage config-driven types, statuses, and fields; losslessly migrate existing items when definitions evolve; adopt presets or infer types |
65
+ | Profiles | `profile list` / `show` / `apply` / `lint` | compose item types, statuses, fields, workflows, config, templates, and recommended packages into archetype bundles (agile/ops/research); `apply` stages every dimension idempotently; `lint` reports author-time consistency findings without writing |
66
+ | Calendar | `calendar`, `cal` | project deadlines, reminders, and events |
67
+ | Packages | `install`, `upgrade`, `package`, `packages`, `extension`, package/extension command groups | install, upgrade, manage, and run package-backed extension commands |
68
+ | Machines | `contracts`, `help`, optional `guide`/`completion` | command contracts plus optional guide-shell docs routing and shell helpers |
67
69
 
68
70
  `†` `test-runs` subcommands are provided by the optional `linked-test-adapters` package (`pm install linked-test-adapters --project`).
69
71
 
@@ -89,6 +91,15 @@ ID prefix without relying on the legacy positional form. `pm init ops` remains
89
91
  compatible, and supplying both forms is accepted only when they normalize to the
90
92
  same prefix; conflicting values fail with `init_id_prefix_conflict` instead of
91
93
  silently choosing one.
94
+ Prefixes normalize through the same primitive used by item creation: letters
95
+ become lowercase, each run of whitespace or punctuation becomes one `-`, edge
96
+ separators are removed, and exactly one trailing `-` is stored. For example,
97
+ `"Release Candidate"` persists as `release-candidate-`; an input with no ASCII
98
+ letters or digits falls back to `pm-`. Path-like values remain invalid tracker
99
+ prefixes when passed through `--id-prefix`; a path-like positional value remains
100
+ the advanced tracker-root target described below. To repair an older unsafe
101
+ value, rerun init with a safe prefix and explicit replacement intent, for example
102
+ `pm init --id-prefix "release candidate" --force --defaults`.
92
103
  Use `pm init --workspace <dir>` when `<dir>` is a project root; it creates `<dir>/.agents/pm`. A path-like positional remains the advanced tracker-root form and writes tracker files directly at that path. Both explicit target forms return `target.mode`, `target.tracker_root`, and tracker-scoped executable `next_steps` so agents can run the suggestions from any working directory.
93
104
  Implicit init discovery also reports whether the selected tracker is local or was found in an ancestor. If `pm init <name>` would change an ancestor tracker, the refusal names both directories and gives the safe current-directory retry: `pm init <name> --pm-path "$PWD/.agents/pm" --defaults`.
94
105
  `pm init --agent-guidance ask` is the default behavior: prompt in TTY only when AGENTS/CLAUDE guidance is missing and no decline is recorded.
@@ -163,6 +174,10 @@ Use `duplicates --status all` for a true whole-history duplicate check. The
163
174
  result echoes `filters.statuses: null` for the unrestricted corpus; named or
164
175
  custom statuses are normalized through the runtime status registry and an
165
176
  unknown status fails instead of returning a false-clean result.
177
+ As a read-only structured surface, `duplicates` accepts the universal output
178
+ controls, including `--output-format json`, `--lean`, projection/amount
179
+ controls, and token accounting, while its default TOON output remains bounded.
180
+ Tracked by [pm-gh1076](../.agents/pm/issues/pm-gh1076.toon).
166
181
  Use `pm get <id>` to read a single item by ID — the single-item read primitive used throughout the agent loop. It accepts `--fields <list>` and `--depth brief|standard|deep|full` for token-minimal projections, and `--tree`/`--tree-depth <n>` to include descendants. Standard/deep reads expose a normalized `schedule` facet (`deadline`, `start_at`, `end_at`, `location`, reminders, and events) when scheduling metadata exists. Container-oriented built-ins (Epic, Feature, Milestone, and Plan) plus custom types automatically expose type-agnostic child counts and continuation metadata. Standard depth keeps that rollup counts-only; `--depth deep|full` or an explicit `--fields id,children` request adds the deterministic bounded child sample. Built-in leaf reads avoid a workspace scan unless children are explicitly requested. `pm get <id> --json` returns the `body` inside the `item` object (`.item.body`); see [Full results, totals, and bodies](#full-results-totals-and-bodies). To duplicate an existing item as a starting point, `pm copy <id> --title "New title"` clones it into a fresh id with lifecycle fields reset.
167
182
 
168
183
  When the strongest duplicate match is terminal because the same work recurred,
@@ -29,6 +29,7 @@ pm config project export --json
29
29
  pm config project get item-format --json
30
30
  pm config project set item-format --format toon
31
31
  pm config project set test-result-tracking --policy enabled
32
+ pm config project set untrusted-linked-test-execution --policy disabled
32
33
  ```
33
34
 
34
35
  `config set <key> <value>` also accepts the value as a positional argument; pm routes
@@ -85,6 +86,7 @@ When `settings.json` cannot be loaded, `pm` falls back to built-in defaults and
85
86
  | `history.compact_policy.max_entries` | when the policy is enabled, the entry count above which a stream is flagged by `pm health` and the default `pm history-compact --all-over` threshold (default `500`) |
86
87
  | `history.compact_policy.trigger` | policy intent when enabled: `health_warn` (advisory only) or `auto` (scheduled sweeps expected) |
87
88
  | `testing.record_results_to_items` | persist bounded linked-test summaries |
89
+ | `testing.allow_untrusted_linked_tests` | permit the explicit `pm test --allow-untrusted-linked-tests` one-run override (default `false`; does not trust or execute commands by itself) |
88
90
  | `validation.sprint_release_format` | `warn` or `strict_error` |
89
91
  | `validation.parent_reference` | `warn` or `strict_error` (`strict_error` is the built-in default; `pm create --allow-missing-parent` is the explicit escape hatch) |
90
92
  | `item_types.definitions[]` | custom item types and type options |
@@ -115,6 +117,7 @@ pm config project set schema_unknown_field_policy reject # (schema.unknown_fie
115
117
  pm config project set governance-duplicate-detection-mode strict # (governance.duplicate_detection_mode) off | advisory | strict
116
118
  pm config project set governance-duplicate-detection-threshold 0.8 # (governance.duplicate_detection_threshold) ratio 0..1
117
119
  pm config project set governance-duplicate-detection-limit 3 # (governance.duplicate_detection_limit) integer 0..20
120
+ pm config project set untrusted-linked-test-execution disabled # (testing.allow_untrusted_linked_tests) enabled | disabled
118
121
  ```
119
122
 
120
123
  Duplicate governance uses the shared SDK scorer and bounded metadata query
@@ -227,6 +230,7 @@ pm config project set sprint-release-format-policy --policy warn
227
230
  pm config project set parent-reference-policy --policy strict_error
228
231
  pm config project set history-missing-stream-policy --policy auto_create
229
232
  pm config project set test-result-tracking --policy enabled
233
+ pm config project set untrusted-linked-test-execution --policy disabled
230
234
  ```
231
235
 
232
236
  Use standalone checks when validating a repository:
@@ -1,6 +1,6 @@
1
1
  # Dependency-kind contract
2
2
 
3
- Tracker: [pm-4020c5](../.agents/pm/issues/pm-4020c5.toon), [pm-jkbqt8](../.agents/pm/issues/pm-jkbqt8.toon), [pm-q6n8sj](../.agents/pm/issues/pm-q6n8sj.toon), [pm-ouyq3n](../.agents/pm/issues/pm-ouyq3n.toon), [pm-gos426](../.agents/pm/issues/pm-gos426.toon), and [pm-flnefm](../.agents/pm/issues/pm-flnefm.toon).
3
+ Tracker: [pm-4020c5](../.agents/pm/issues/pm-4020c5.toon), [pm-jkbqt8](../.agents/pm/issues/pm-jkbqt8.toon), [pm-q6n8sj](../.agents/pm/issues/pm-q6n8sj.toon), [pm-ouyq3n](../.agents/pm/issues/pm-ouyq3n.toon), [pm-gos426](../.agents/pm/issues/pm-gos426.toon), [pm-flnefm](../.agents/pm/issues/pm-flnefm.toon), [pm-rggtvd](../.agents/pm/issues/pm-rggtvd.toon), and [pm-vk7zek](../.agents/pm/issues/pm-vk7zek.toon).
4
4
 
5
5
  Dependency rows have one canonical stored spelling per relationship meaning. Command inputs remain compatibility-friendly: hyphens normalize to underscores and the aliases below are accepted, but `pm create` and `pm update` persist the canonical kind. Existing historical rows are never rewritten implicitly.
6
6
 
@@ -15,9 +15,17 @@ Dependency rows have one canonical stored spelling per relationship meaning. Com
15
15
 
16
16
  The SDK relationship registry is authoritative. `canonicalizeRelationshipKind()` rejects unknown spellings, while `resolveCanonicalRelationshipKind()` supports validation flows that need an undefined result. `pm contracts` publishes `relationship_kind_contracts` with canonical names, aliases, inverses, and ordering/hierarchy semantics.
17
17
 
18
+ Extension kinds may opt into both ordering and hierarchy semantics. Unless they
19
+ declare an explicit `traversal`, ordering is the primary traversal and context
20
+ role family; hierarchy direction and cardinality remain enforceable integrity
21
+ constraints. This ordering-first precedence is shared by registry snapshots and
22
+ direct-edge context explanations.
23
+
18
24
  Dependency additions and removals share the same lossless input grammar. A
19
- bare value is an item id; structured input uses `id=<id>` plus an optional
20
- canonical `kind`/`type` and `source_kind`. Punctuation-shaped shorthand such as
25
+ bare value is an item id; structured removal input uses `id=<id>` plus optional
26
+ canonical `kind` (with `type` accepted only as an input alias), `source_kind`, `author`, and ISO `created_at`
27
+ selectors. Supplying all stored coordinates retires exactly one dependency row
28
+ without deleting siblings that share an id and kind. Punctuation-shaped shorthand such as
21
29
  `OTHER,related` is rejected with `dependency_flag_value_invalid` on both
22
30
  `--dep` and `--dep-remove`, before prefix normalization can turn it into a
23
31
  dangling id. A removal selector that matches no stored row fails with
@@ -25,6 +33,26 @@ dangling id. A removal selector that matches no stored row fails with
25
33
  available identities. Re-adding a stored dependency identity is idempotent; if
26
34
  legacy storage contains that exact identity more than once, the same mutation
27
35
  collapses the touched copies to one without creating an edge-absence window.
36
+ The stored identity includes normalized `id`, `kind`, `source_kind`, `author`,
37
+ and creation instant, so provenance-distinct sibling rows remain independent.
38
+
39
+ ## Hierarchy integrity
40
+
41
+ The registry's `hierarchy`, `hierarchyDirection`, `outgoing`, and `incoming`
42
+ fields are executable contracts. Scalar `parent` metadata and every registered
43
+ hierarchy dependency spelling normalize into the same parent-to-child relation.
44
+ That normalized relation powers `pm list --parent`, list/get tree projections,
45
+ child rollups, graph traversal, graph analysis, graph audit, validate, and
46
+ health. Extensions therefore gain hierarchy behavior by registering semantics;
47
+ consumers do not hard-code kind names.
48
+
49
+ Create and update transactions reject a newly introduced hierarchy cycle,
50
+ registry cardinality violation, or scalar/dependency direction contradiction
51
+ before persistence. Existing debt remains readable and repairable. Active debt
52
+ is gate-failing in graph governance and health; defects confined to terminal
53
+ items are labeled legacy and are never mutated automatically. `pm validate`
54
+ reports cycle, cardinality, and parent-divergence counts and exact bounded rows,
55
+ while `pm graph analyze` exposes a compact `hierarchy` summary.
28
56
 
29
57
  `recurs_from` has no alias: a later occurrence points to an earlier occurrence.
30
58
  It is persistent after both items become terminal and carries temporal identity,