@unbrained/pm-cli 2026.8.22 → 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 (168) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +28 -0
  3. package/dist/cli/error-guidance.d.ts +13 -0
  4. package/dist/cli/error-guidance.js +62 -6
  5. package/dist/cli/main.js +13 -4
  6. package/dist/cli/register-mutation.js +6 -6
  7. package/dist/cli/register-operations.d.ts +73 -0
  8. package/dist/cli/register-operations.js +39 -14
  9. package/dist/cli/register-setup.js +3 -3
  10. package/dist/cli/schema-registration-helpers.d.ts +8 -1
  11. package/dist/cli/schema-registration-helpers.js +14 -5
  12. package/dist/cli-bundle/bundle-manifest.json +155 -155
  13. package/dist/cli-bundle/chunks/chunk-3ENVHX6C.js +36 -0
  14. package/dist/cli-bundle/chunks/{chunk-QI2CT3IX.js → chunk-DW2CGEMU.js} +2 -2
  15. package/dist/cli-bundle/chunks/{chunk-WG4STC36.js → chunk-EG6CLDZT.js} +2 -2
  16. package/dist/cli-bundle/chunks/{chunk-MYYI3ZOA.js → chunk-F3AI2JFL.js} +2 -2
  17. package/dist/cli-bundle/chunks/{chunk-U6GQ4VMV.js → chunk-HWZ6CELB.js} +4 -4
  18. package/dist/cli-bundle/chunks/chunk-HZBMLSZI.js +201 -0
  19. package/dist/cli-bundle/chunks/chunk-JB4KZWBF.js +3 -0
  20. package/dist/cli-bundle/chunks/chunk-KPH73XXJ.js +2 -0
  21. package/dist/cli-bundle/chunks/{chunk-BWX4SK46.js → chunk-KXPMB2YL.js} +58 -58
  22. package/dist/cli-bundle/chunks/{chunk-MZTZQTMS.js → chunk-QDVHNHAQ.js} +2 -2
  23. package/dist/cli-bundle/chunks/chunk-TFI5V5N2.js +13 -0
  24. package/dist/cli-bundle/chunks/{register-list-query-HZKBQNWW.js → register-list-query-UJB5FMV2.js} +2 -2
  25. package/dist/cli-bundle/chunks/register-mutation-NX5XE4VX.js +20 -0
  26. package/dist/cli-bundle/chunks/register-operations-RXIJIYXC.js +2 -0
  27. package/dist/cli-bundle/chunks/{register-setup-QK4OTOXN.js → register-setup-GFUZEKRE.js} +2 -2
  28. package/dist/cli-bundle/focused-chunks/{chunk-2POVTNLY.js → chunk-37Z4OEAP.js} +2 -2
  29. package/dist/cli-bundle/focused-chunks/chunk-3UPTYG44.js +8 -0
  30. package/dist/cli-bundle/focused-chunks/{chunk-E2EWYPY6.js → chunk-5S7E72FY.js} +2 -2
  31. package/dist/cli-bundle/focused-chunks/{chunk-GMYPD4FX.js → chunk-AUFH5H35.js} +2 -2
  32. package/dist/cli-bundle/focused-chunks/chunk-E7X25GC3.js +2 -0
  33. package/dist/cli-bundle/focused-chunks/{chunk-3LKU6PTJ.js → chunk-FQCSTBWE.js} +2 -2
  34. package/dist/cli-bundle/focused-chunks/{chunk-6S2MO3Q3.js → chunk-FZS4H32O.js} +2 -2
  35. package/dist/cli-bundle/focused-chunks/chunk-IFLWKY2R.js +156 -0
  36. package/dist/cli-bundle/focused-chunks/{chunk-PMGJUFNQ.js → chunk-JOWDB2MH.js} +2 -2
  37. package/dist/cli-bundle/focused-chunks/chunk-LYVSW5VM.js +2 -0
  38. package/dist/cli-bundle/focused-chunks/chunk-MRCNF3U6.js +2 -0
  39. package/dist/cli-bundle/focused-chunks/{chunk-A2BC5PQL.js → chunk-OFVUGTLJ.js} +2 -2
  40. package/dist/cli-bundle/focused-chunks/{chunk-2ARQGDDW.js → chunk-OGLQBL46.js} +2 -2
  41. package/dist/cli-bundle/focused-chunks/{chunk-R6SR45XU.js → chunk-OY7QAV4Y.js} +48 -47
  42. package/dist/cli-bundle/focused-chunks/chunk-QGFNI65N.js +26 -0
  43. package/dist/cli-bundle/focused-chunks/{chunk-D6RBZEJJ.js → chunk-R27IDGHD.js} +2 -2
  44. package/dist/cli-bundle/focused-chunks/chunk-UYRFYCDQ.js +5 -0
  45. package/dist/cli-bundle/focused-chunks/{chunk-PH2OXXVZ.js → chunk-ZHVA42R5.js} +9 -9
  46. package/dist/cli-bundle/main.js +13 -13
  47. package/dist/cli-bundle/sdk-authoring.js +1 -1
  48. package/dist/cli-bundle/sdk-contracts.js +2 -2
  49. package/dist/cli-bundle/sdk-core.js +40 -40
  50. package/dist/cli-bundle/sdk-governance.js +1 -1
  51. package/dist/cli-bundle/sdk-graph.js +1 -1
  52. package/dist/cli-bundle/sdk-merge.js +33 -32
  53. package/dist/cli-bundle/sdk-query.js +1 -1
  54. package/dist/cli-bundle/sdk-runtime.js +1 -1
  55. package/dist/cli-bundle/sdk-testing.js +1 -1
  56. package/dist/cli-bundle/sdk.js +5 -5
  57. package/dist/core/config/nested-settings.js +8 -2
  58. package/dist/core/config/positional-value.d.ts +1 -1
  59. package/dist/core/config/positional-value.js +5 -2
  60. package/dist/core/history/history.js +3 -3
  61. package/dist/core/item/item-format.js +66 -12
  62. package/dist/core/reproducibility/context.d.ts +4 -0
  63. package/dist/core/reproducibility/context.js +14 -8
  64. package/dist/core/shared/author.d.ts +55 -1
  65. package/dist/core/shared/author.js +333 -16
  66. package/dist/core/shared/constants.js +3 -2
  67. package/dist/core/store/settings-validator.d.ts +1 -0
  68. package/dist/core/store/settings-validator.js +6 -3
  69. package/dist/core/store/settings.js +5 -2
  70. package/dist/mcp/server.d.ts +1 -1
  71. package/dist/mcp/server.js +30 -9
  72. package/dist/sdk/agent/identity-config.d.ts +63 -0
  73. package/dist/sdk/agent/identity-config.js +107 -0
  74. package/dist/sdk/agent/refusal-corpus-contracts.d.ts +76 -0
  75. package/dist/sdk/agent/refusal-corpus-contracts.js +194 -0
  76. package/dist/sdk/cli-contracts/agent-output-contracts.js +6 -4
  77. package/dist/sdk/cli-contracts/enum-contracts.d.ts +4 -0
  78. package/dist/sdk/cli-contracts/enum-contracts.js +9 -2
  79. package/dist/sdk/cli-contracts/flag-contracts.js +9 -2
  80. package/dist/sdk/cli-contracts/flag-lexicon-contracts.js +4 -4
  81. package/dist/sdk/cli-contracts/registration-helpers.js +5 -2
  82. package/dist/sdk/cli-contracts/tool-parameter-tables.js +22 -2
  83. package/dist/sdk/cli-contracts/tool-schema.d.ts +1 -1
  84. package/dist/sdk/cli-contracts/tool-schema.js +14 -3
  85. package/dist/sdk/cli-contracts.d.ts +3 -3
  86. package/dist/sdk/cli-contracts.js +4 -4
  87. package/dist/sdk/config.d.ts +6 -1
  88. package/dist/sdk/config.js +119 -14
  89. package/dist/sdk/context-intent-contracts.js +4 -2
  90. package/dist/sdk/core-governance.d.ts +1 -1
  91. package/dist/sdk/core-governance.js +3 -3
  92. package/dist/sdk/extension/scaffold.js +11 -5
  93. package/dist/sdk/flag-invocation-contracts.js +4 -2
  94. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +50 -2
  95. package/dist/sdk/generated/generated-error-code-catalog-part-2.js +87 -38
  96. package/dist/sdk/governance/assurance-action.js +30 -10
  97. package/dist/sdk/governance/validate.d.ts +2 -2
  98. package/dist/sdk/governance/validate.js +39 -14
  99. package/dist/sdk/index.d.ts +7 -2
  100. package/dist/sdk/index.js +8 -3
  101. package/dist/sdk/lifecycle/create.js +6 -3
  102. package/dist/sdk/lifecycle/update.js +5 -3
  103. package/dist/sdk/merge/three-way.js +105 -26
  104. package/dist/sdk/read-output-budget.d.ts +21 -0
  105. package/dist/sdk/read-output-budget.js +39 -2
  106. package/dist/sdk/read-output-contracts.d.ts +5 -3
  107. package/dist/sdk/read-output-contracts.js +20 -12
  108. package/dist/sdk/reproducibility/process.d.ts +19 -0
  109. package/dist/sdk/reproducibility/process.js +88 -0
  110. package/dist/sdk/runtime-primitives.d.ts +1 -1
  111. package/dist/sdk/runtime-primitives.js +3 -3
  112. package/dist/sdk/runtime.js +6 -5
  113. package/dist/sdk/test/batch.d.ts +6 -0
  114. package/dist/sdk/test/batch.js +15 -3
  115. package/dist/sdk/test/entry.d.ts +1 -1
  116. package/dist/sdk/test/entry.js +3 -2
  117. package/dist/sdk/test/execution.d.ts +28 -3
  118. package/dist/sdk/test/execution.js +193 -34
  119. package/dist/sdk/test/parsers.d.ts +6 -0
  120. package/dist/sdk/test/parsers.js +21 -2
  121. package/dist/sdk/test/trust.d.ts +33 -0
  122. package/dist/sdk/test/trust.js +207 -0
  123. package/dist/types.d.ts +23 -0
  124. package/dist/types.js +2 -2
  125. package/docs/AGENT_PROVENANCE_ADR.md +41 -5
  126. package/docs/CONFIGURATION.md +4 -0
  127. package/docs/DIAGNOSTIC_OUTPUT_CONTRACTS.md +19 -2
  128. package/docs/MERGE_SAFETY.md +15 -1
  129. package/docs/READ_OUTPUT_CONTRACTS.md +11 -1
  130. package/docs/REPRODUCIBLE_WORKSPACES.md +42 -8
  131. package/docs/SDK.md +9 -0
  132. package/docs/SDK_AGENT_SESSION_CONTEXT.md +6 -4
  133. package/docs/SDK_EVIDENCE_TRACEABILITY.md +1 -1
  134. package/docs/TESTING.md +75 -8
  135. package/docs/agent-task-token-baseline.json +2 -2
  136. package/docs/generated/FLAG_LEXICON_BUDGETS.md +2 -2
  137. package/marketplace.json +2 -2
  138. package/package.json +2 -2
  139. package/packages/pm-beads/package.json +1 -1
  140. package/packages/pm-calendar/package.json +1 -1
  141. package/packages/pm-command-kit/package.json +1 -1
  142. package/packages/pm-digital-twin/package.json +1 -1
  143. package/packages/pm-governance-audit/package.json +1 -1
  144. package/packages/pm-guide-shell/package.json +1 -1
  145. package/packages/pm-kanban/package.json +1 -1
  146. package/packages/pm-lifecycle-hooks/package.json +1 -1
  147. package/packages/pm-linked-test-adapters/package.json +1 -1
  148. package/packages/pm-search-advanced/package.json +1 -1
  149. package/packages/pm-templates/package.json +1 -1
  150. package/packages/pm-todos/package.json +1 -1
  151. package/packages/pm-vcs/package.json +1 -1
  152. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  153. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  154. package/sdk/public-surface.json +427 -17
  155. package/dist/cli-bundle/chunks/chunk-EMWDM5FI.js +0 -13
  156. package/dist/cli-bundle/chunks/chunk-HYG6LRL4.js +0 -200
  157. package/dist/cli-bundle/chunks/chunk-SMWXHMVB.js +0 -35
  158. package/dist/cli-bundle/chunks/chunk-VMMIL3SI.js +0 -2
  159. package/dist/cli-bundle/chunks/chunk-YBPJAOQQ.js +0 -3
  160. package/dist/cli-bundle/chunks/register-mutation-HTKEEGRD.js +0 -20
  161. package/dist/cli-bundle/chunks/register-operations-6EMRWJJY.js +0 -2
  162. package/dist/cli-bundle/focused-chunks/chunk-2AZT5WXZ.js +0 -2
  163. package/dist/cli-bundle/focused-chunks/chunk-5SHSBMX2.js +0 -2
  164. package/dist/cli-bundle/focused-chunks/chunk-C4S4A4JJ.js +0 -5
  165. package/dist/cli-bundle/focused-chunks/chunk-L5LDSRO2.js +0 -155
  166. package/dist/cli-bundle/focused-chunks/chunk-MB37AYN6.js +0 -26
  167. package/dist/cli-bundle/focused-chunks/chunk-MCOR37QK.js +0 -2
  168. package/dist/cli-bundle/focused-chunks/chunk-WZE4LIPW.js +0 -8
@@ -4,7 +4,7 @@
4
4
  * Implements the pm linked test entry 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]="2b20ab37-ffbe-5c65-ac0f-800abf876e16")}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]="44e6a87d-0ca5-53af-b199-b66bb2b98fb5")}catch(e){}}();
8
8
  import { EXIT_CODE } from "../../core/shared/constants.js";
9
9
  import { PmCliError } from "../../core/shared/errors.js";
10
10
  // Keys accepted inside a structured linked-test entry. `cmd` is an alias for
@@ -18,6 +18,7 @@ export const STRUCTURED_LINKED_TEST_KEYS = [
18
18
  "timeout",
19
19
  "timeout_seconds",
20
20
  "pm_context_mode",
21
+ "workspace_context_mode",
21
22
  "env_set",
22
23
  "env_clear",
23
24
  "shared_host_safe",
@@ -71,4 +72,4 @@ export function normalizeStructuredLinkedTestEntry(kv, optionName) {
71
72
  return normalizedKv;
72
73
  }
73
74
  //# sourceMappingURL=entry.js.map
74
- //# debugId=2b20ab37-ffbe-5c65-ac0f-800abf876e16
75
+ //# debugId=44e6a87d-0ca5-53af-b199-b66bb2b98fb5
@@ -1,10 +1,11 @@
1
1
  import type { GlobalOptions } from "../../core/shared/command-types.js";
2
2
  import { resolveAuthor } from "../../core/shared/author.js";
3
3
  import { runInit } from "../init.js";
4
- import { type LinkedTestPmContextMode } from "./parsers.js";
4
+ import { type LinkedTestPmContextMode, type LinkedTestWorkspaceContextMode } from "./parsers.js";
5
5
  import { type LinkedTestRunSelection } from "../../core/test/run-selectors.js";
6
6
  import type { LinkedTest, LinkScope, TestRunMeasurement } from "../../types/index.js";
7
7
  import { type TestRunMeasurementDiff } from "./measurements.js";
8
+ import { type LinkedTestTrustDecision } from "./trust.js";
8
9
  type ResolvedLinkedTestPmContextMode = Exclude<LinkedTestPmContextMode, "auto">;
9
10
  interface LinkedTestExecutionResult {
10
11
  stdout: string;
@@ -61,6 +62,14 @@ export interface TestCommandOptions {
61
62
  pmContext?: string;
62
63
  /** Value that configures or reports override linked pm context for this contract. */
63
64
  overrideLinkedPmContext?: boolean;
65
+ /** Source-workspace visibility and working-directory mode. */
66
+ workspaceContext?: string;
67
+ /** Force the run-level workspace mode over per-test metadata. */
68
+ overrideLinkedWorkspaceContext?: boolean;
69
+ /** Explicitly permit an untrusted linked command when project policy also permits it. */
70
+ allowUntrustedLinkedTests?: boolean;
71
+ /** Record clone-local acknowledgement for the item's current linked tests. */
72
+ acknowledgeLinkedTests?: boolean;
64
73
  /** Value that configures or reports fail on context mismatch for this contract. */
65
74
  failOnContextMismatch?: boolean;
66
75
  /** Value that configures or reports fail on skipped for this contract. */
@@ -87,7 +96,7 @@ export interface TestCommandOptions {
87
96
  metricDiff?: string;
88
97
  }
89
98
  /** Restricts linked test failure category values accepted by command, SDK, and storage contracts. */
90
- export type LinkedTestFailureCategory = "infra_collision" | "assertion_failure" | "empty_run" | "timeout" | "max_buffer" | "spawn_error" | "signal";
99
+ export type LinkedTestFailureCategory = "infra_collision" | "trust_refusal" | "assertion_failure" | "empty_run" | "timeout" | "max_buffer" | "spawn_error" | "signal";
91
100
  /** Documents the test run result payload exchanged by command, SDK, and package integrations. */
92
101
  export interface TestRunResult {
93
102
  /** Value that configures or reports command for this contract. */
@@ -118,6 +127,11 @@ export interface TestRunResult {
118
127
  mismatch_detected: boolean;
119
128
  project_extensions_seeded: boolean;
120
129
  global_extensions_seeded: boolean;
130
+ requested_workspace_context_mode: LinkedTestWorkspaceContextMode;
131
+ workspace_context_mode: LinkedTestWorkspaceContextMode;
132
+ working_directory: string;
133
+ source_workspace_root: string;
134
+ trust: LinkedTestTrustDecision;
121
135
  };
122
136
  /** Value that configures or reports stdout for this contract. */
123
137
  stdout?: string;
@@ -195,7 +209,7 @@ declare function segmentInvokesRecursiveTestAll(segment: string): boolean;
195
209
  declare function parseLinkedTestTimeoutSeconds(timeoutSecondsRaw: string | undefined, timeoutAliasRaw: string | undefined): number | undefined;
196
210
  declare function parseAddJsonEntries(raw: string[] | undefined): LinkedTest[];
197
211
  declare function endLinkedTestProgress(context: LinkedTestProgressContext, executionResult: Pick<LinkedTestExecutionResult, "timedOut" | "maxBufferExceeded" | "outputTruncated" | "exitCode" | "signal">, startedAt: number, mode: LinkedTestProgressMode): void;
198
- declare function runLinkedTestCommand(command: string, timeoutMs: number, env: NodeJS.ProcessEnv, progressContext: LinkedTestProgressContext, progressMode: LinkedTestProgressMode): Promise<LinkedTestExecutionResult>;
212
+ declare function runLinkedTestCommand(command: string, timeoutMs: number, env: NodeJS.ProcessEnv, progressContext: LinkedTestProgressContext, progressMode: LinkedTestProgressMode, cwd?: string): Promise<LinkedTestExecutionResult>;
199
213
  declare function formatLinkedTestExecutionError(result: LinkedTestExecutionResult, timeoutMs: number): string;
200
214
  /** Implements classify linked test failure for the public runtime surface of this module. */
201
215
  export declare function classifyLinkedTestFailure(result: Pick<LinkedTestExecutionResult, "stdout" | "stderr" | "spawnError" | "signal" | "timedOut" | "maxBufferExceeded">): LinkedTestFailureCategory;
@@ -228,6 +242,11 @@ interface RunLinkedTestsOptions {
228
242
  requireAssertionsForPm?: boolean;
229
243
  checkContext?: boolean;
230
244
  autoPmContext?: boolean;
245
+ workspaceContext?: string;
246
+ overrideLinkedWorkspaceContext?: boolean;
247
+ pmRoot?: string;
248
+ allowUntrustedLinkedTests?: boolean;
249
+ untrustedLinkedTestsPolicyEnabled?: boolean;
231
250
  }
232
251
  interface LinkedTestSandboxLayout {
233
252
  root: string;
@@ -235,6 +254,8 @@ interface LinkedTestSandboxLayout {
235
254
  schemaGlobalPmPath: string;
236
255
  trackerProjectPmPath: string;
237
256
  trackerGlobalPmPath: string;
257
+ workspaceSnapshotRoot: string;
258
+ workspaceIsolatedRoot: string;
238
259
  }
239
260
  interface LinkedTestSandboxCounts {
240
261
  sourceProjectItemCount: number;
@@ -255,6 +276,10 @@ declare function buildLinkedTestExecutionContext(params: {
255
276
  requestedPmContextMode: LinkedTestPmContextMode;
256
277
  effectivePmContextMode: ResolvedLinkedTestPmContextMode;
257
278
  autoPmContextApplied: boolean;
279
+ requestedWorkspaceContextMode: LinkedTestWorkspaceContextMode;
280
+ workspaceContextMode: LinkedTestWorkspaceContextMode;
281
+ sourceWorkspaceRoot: string;
282
+ trust: LinkedTestTrustDecision;
258
283
  }): NonNullable<TestRunResult["execution_context"]>;
259
284
  /** Implements run linked tests for the public runtime surface of this module. */
260
285
  export declare function runLinkedTests(tests: LinkedTest[], defaultTimeoutSeconds: number | undefined, options?: RunLinkedTestsOptions): Promise<TestRunResult[]>;
@@ -4,10 +4,10 @@
4
4
  * Implements the pm test 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]="5fe0ee3e-9590-56fc-b238-e56ff1970e52")}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]="7659721c-21f5-55da-8cce-31b290de5bfb")}catch(e){}}();
8
8
  import { assertInitializedTracker } from "../environment/tracker-preflight.js";
9
9
  import { spawn } from "node:child_process";
10
- import { cp, mkdir, mkdtemp, open, readdir, rm } from "node:fs/promises";
10
+ import { cp, mkdir, mkdtemp, open, readdir, rm, symlink, } from "node:fs/promises";
11
11
  import { tmpdir } from "node:os";
12
12
  import path from "node:path";
13
13
  import { getActiveExtensionRegistrations } from "../../core/extensions/index.js";
@@ -25,12 +25,13 @@ import { readSettings } from "../../core/store/settings.js";
25
25
  import { appendTrackedTestRunSummary, buildTrackedTestRunHistory, } from "../../core/test/item-test-run-tracking.js";
26
26
  import { runInit } from "../init.js";
27
27
  import { looksLikeStructuredLinkedTestEntry, normalizeStructuredLinkedTestEntry, } from "./entry.js";
28
- import { LINKED_TEST_PM_CONTEXT_MODE_VALUES as PM_CONTEXT_MODE_VALUES, LINKED_TEST_PROTECTED_ENV_KEYS, parseLinkedTestAssertionEqualsMap, parseLinkedTestAssertionGteMap, parseLinkedTestBoolean as parseLinkedTestBooleanValue, parseLinkedTestContextMode as parseLinkedTestContextModeValue, parseLinkedTestEnvClear as parseLinkedTestEnvClearValue, parseLinkedTestEnvSet as parseLinkedTestEnvSetValue, parseLinkedTestJsonEntries, parseLinkedTestMinLines, parseLinkedTestRegexList, parseLinkedTestStringList, } from "./parsers.js";
28
+ import { LINKED_TEST_PM_CONTEXT_MODE_VALUES as PM_CONTEXT_MODE_VALUES, LINKED_TEST_PROTECTED_ENV_KEYS, parseLinkedTestAssertionEqualsMap, parseLinkedTestAssertionGteMap, parseLinkedTestBoolean as parseLinkedTestBooleanValue, parseLinkedTestContextMode as parseLinkedTestContextModeValue, parseLinkedTestEnvClear as parseLinkedTestEnvClearValue, parseLinkedTestEnvSet as parseLinkedTestEnvSetValue, parseLinkedTestJsonEntries, parseLinkedTestMinLines, parseLinkedTestRegexList, parseLinkedTestStringList, parseLinkedTestWorkspaceContextMode, } from "./parsers.js";
29
29
  import { parseOnlyIndexValue, resolveLinkedTestRunSelection, } from "../../core/test/run-selectors.js";
30
30
  import { SCOPE_VALUES } from "../../types/index.js";
31
31
  import { diffTestRunMeasurements, parseTestRunMeasurements, queryTestRunMeasurementsBelow, } from "./measurements.js";
32
32
  import { withHostEnvironmentBoundary } from "../environment/host-environment-errors.js";
33
33
  import { SOURCE_CONTEXT_ACCESS_ENV } from "../environment/source-context.js";
34
+ import { acknowledgeLinkedTests, attachLinkedTestMutationProvenance, resolveLinkedTestSourceRef, resolveLinkedTestSourceWorkspaceRoot, resolveLinkedTestTrust, } from "./trust.js";
34
35
  const TEST_OUTPUT_MAX_BUFFER_BYTES = 20 * 1024 * 1024;
35
36
  const DEFAULT_LINKED_TEST_TIMEOUT_FORCE_KILL_DELAY_MS = 3000;
36
37
  const DEFAULT_LINKED_TEST_HEARTBEAT_INTERVAL_MS = 10000;
@@ -170,6 +171,15 @@ function parsePmContextMode(raw) {
170
171
  }
171
172
  throw new PmCliError(`Invalid --pm-context value "${raw}". Expected one of: ${PM_CONTEXT_MODE_VALUES.join(", ")}`, EXIT_CODE.USAGE);
172
173
  }
174
+ function parseWorkspaceContextMode(raw) {
175
+ return parseLinkedTestWorkspaceContextMode(raw ?? "source", "--workspace-context");
176
+ }
177
+ function resolveLinkedTestWorkspaceContextMode(linkedTest, runLevelMode, overrideLinkedMode) {
178
+ if (overrideLinkedMode) {
179
+ return runLevelMode;
180
+ }
181
+ return (parseLinkedTestWorkspaceContextMode(linkedTest.workspace_context_mode, "linked test") ?? runLevelMode);
182
+ }
173
183
  function resolveLinkedTestRequestedContextMode(linkedTest, runLevelMode, overrideLinkedPmContext) {
174
184
  if (overrideLinkedPmContext) {
175
185
  return runLevelMode;
@@ -460,6 +470,7 @@ function parseAddEntry(entry) {
460
470
  scope: ensureScope(kv.scope),
461
471
  timeout_seconds: parseLinkedTestTimeoutSeconds(trimLinkedTestEntryField(kv.timeout_seconds), trimLinkedTestEntryField(kv.timeout)),
462
472
  pm_context_mode: parseLinkedTestContextModeValue(trimLinkedTestEntryField(kv.pm_context_mode), "--add"),
473
+ workspace_context_mode: parseLinkedTestWorkspaceContextMode(trimLinkedTestEntryField(kv.workspace_context_mode), "--add"),
463
474
  env_set: parseLinkedTestEnvSetValue(trimLinkedTestEntryField(kv.env_set), "--add"),
464
475
  env_clear: parseLinkedTestEnvClearValue(trimLinkedTestEntryField(kv.env_clear), "--add"),
465
476
  shared_host_safe: parseLinkedTestBooleanValue(trimLinkedTestEntryField(kv.shared_host_safe), "--add", "shared_host_safe"),
@@ -760,9 +771,9 @@ function waitForLinkedTestChildClose(child) {
760
771
  });
761
772
  });
762
773
  }
763
- function createLinkedTestChild(command, env, stdoutFd, stderrFd) {
774
+ function createLinkedTestChild(command, env, stdoutFd, stderrFd, cwd) {
764
775
  return spawn(command, {
765
- cwd: process.cwd(),
776
+ cwd,
766
777
  env,
767
778
  shell: true,
768
779
  windowsHide: true,
@@ -791,7 +802,7 @@ async function readLinkedTestCapture(capturePath) {
791
802
  await handle.close();
792
803
  }
793
804
  }
794
- async function runLinkedTestCommand(command, timeoutMs, env, progressContext, progressMode) {
805
+ async function runLinkedTestCommand(command, timeoutMs, env, progressContext, progressMode, cwd = process.cwd()) {
795
806
  const startedAt = Date.now();
796
807
  const captureRoot = await mkdtemp(path.join(tmpdir(), "pm-linked-test-output-"));
797
808
  try {
@@ -803,7 +814,7 @@ async function runLinkedTestCommand(command, timeoutMs, env, progressContext, pr
803
814
  ]);
804
815
  let child;
805
816
  try {
806
- child = createLinkedTestChild(command, env, stdoutHandle.fd, stderrHandle.fd);
817
+ child = createLinkedTestChild(command, env, stdoutHandle.fd, stderrHandle.fd, cwd);
807
818
  }
808
819
  finally {
809
820
  await Promise.all([stdoutHandle.close(), stderrHandle.close()]);
@@ -902,6 +913,7 @@ export function classifyLinkedTestFailure(result) {
902
913
  function createEmptyFailureCategoryCounts() {
903
914
  return {
904
915
  infra_collision: 0,
916
+ trust_refusal: 0,
905
917
  assertion_failure: 0,
906
918
  empty_run: 0,
907
919
  timeout: 0,
@@ -1264,8 +1276,48 @@ function createLinkedTestSandboxLayout(sandboxRoot) {
1264
1276
  schemaGlobalPmPath: path.join(sandboxRoot, "schema", "global"),
1265
1277
  trackerProjectPmPath: path.join(sandboxRoot, "tracker", "project", ".agents", "pm"),
1266
1278
  trackerGlobalPmPath: path.join(sandboxRoot, "tracker", "global"),
1279
+ workspaceSnapshotRoot: path.join(sandboxRoot, "workspace", "project"),
1280
+ workspaceIsolatedRoot: path.join(sandboxRoot, "workspace", "isolated"),
1267
1281
  };
1268
1282
  }
1283
+ function linkedTestsRequireWorkspaceSnapshot(tests, runLevelMode, overrideLinkedMode) {
1284
+ return tests.some((linkedTest) => resolveLinkedTestWorkspaceContextMode(linkedTest, runLevelMode, overrideLinkedMode) === "snapshot");
1285
+ }
1286
+ async function seedLinkedTestWorkspaceSnapshot(layout, sourceWorkspaceRoot) {
1287
+ const excludedSegments = new Set([
1288
+ ".agents",
1289
+ ".git",
1290
+ ".nyc_output",
1291
+ ".turbo",
1292
+ "coverage",
1293
+ "node_modules",
1294
+ ]);
1295
+ await mkdir(path.dirname(layout.workspaceSnapshotRoot), { recursive: true });
1296
+ await cp(sourceWorkspaceRoot, layout.workspaceSnapshotRoot, {
1297
+ recursive: true,
1298
+ force: true,
1299
+ filter(source) {
1300
+ const relative = path.relative(sourceWorkspaceRoot, source);
1301
+ if (!relative) {
1302
+ return true;
1303
+ }
1304
+ return !relative
1305
+ .split(path.sep)
1306
+ .some((segment) => excludedSegments.has(segment));
1307
+ },
1308
+ });
1309
+ const sourceNodeModules = path.join(sourceWorkspaceRoot, "node_modules");
1310
+ if (await pathExists(sourceNodeModules)) {
1311
+ await symlink(sourceNodeModules, path.join(layout.workspaceSnapshotRoot, "node_modules"), process.platform === "win32" ? "junction" : "dir");
1312
+ }
1313
+ }
1314
+ async function bindSnapshotTracker(snapshotRoot, sandboxPmPath) {
1315
+ const agentsRoot = path.join(snapshotRoot, ".agents");
1316
+ const target = path.join(agentsRoot, "pm");
1317
+ await mkdir(agentsRoot, { recursive: true });
1318
+ await rm(target, { recursive: true, force: true });
1319
+ await symlink(sandboxPmPath, target, process.platform === "win32" ? "junction" : "dir");
1320
+ }
1269
1321
  async function initializeLinkedTestSandboxes(layout, initialize = runInit, includeTrackerData = true) {
1270
1322
  const initOptions = { defaults: true, agentGuidance: "skip" };
1271
1323
  await initialize(undefined, { path: layout.schemaProjectPmPath }, initOptions);
@@ -1326,6 +1378,12 @@ function buildLinkedTestExecutionContext(params) {
1326
1378
  const selectedSandboxGlobalItemCount = trackerMode
1327
1379
  ? params.counts.trackerGlobalItemCount
1328
1380
  : params.counts.schemaGlobalItemCount;
1381
+ const workingDirectory = params.workspaceContextMode === "snapshot"
1382
+ ? params.layout.workspaceSnapshotRoot
1383
+ : params.workspaceContextMode === "isolated"
1384
+ ? params.layout.workspaceIsolatedRoot
1385
+ : params.sourceWorkspaceRoot;
1386
+ const exposedSourceWorkspaceRoot = params.workspaceContextMode === "isolated" ? "" : workingDirectory;
1329
1387
  return {
1330
1388
  requested_pm_context_mode: params.requestedPmContextMode,
1331
1389
  pm_context_mode: params.effectivePmContextMode,
@@ -1346,9 +1404,14 @@ function buildLinkedTestExecutionContext(params) {
1346
1404
  params.counts.sourceGlobalItemCount !== selectedSandboxGlobalItemCount),
1347
1405
  project_extensions_seeded: Boolean(params.sourceRoots),
1348
1406
  global_extensions_seeded: Boolean(params.sourceRoots),
1407
+ requested_workspace_context_mode: params.requestedWorkspaceContextMode,
1408
+ workspace_context_mode: params.workspaceContextMode,
1409
+ working_directory: workingDirectory,
1410
+ source_workspace_root: exposedSourceWorkspaceRoot,
1411
+ trust: params.trust,
1349
1412
  };
1350
1413
  }
1351
- function resolveLinkedTestCommandContext(params) {
1414
+ async function resolveLinkedTestCommandContext(params) {
1352
1415
  const linkedOverridePmContextMode = typeof params.linkedTest.pm_context_mode === "string" &&
1353
1416
  params.linkedTest.pm_context_mode.trim().length > 0
1354
1417
  ? parsePmContextMode(params.linkedTest.pm_context_mode)
@@ -1366,6 +1429,15 @@ function resolveLinkedTestCommandContext(params) {
1366
1429
  ? "auto"
1367
1430
  : resolveLinkedTestRequestedContextMode(params.linkedTest, params.runLevelPmContextMode, params.options?.overrideLinkedPmContext === true);
1368
1431
  const effectivePmContextMode = resolveLinkedTestEffectiveContextMode(requestedPmContextMode, isPmTrackerReadCommand);
1432
+ const selectedSandboxProjectPmPath = effectivePmContextMode === "tracker"
1433
+ ? params.layout.trackerProjectPmPath
1434
+ : params.layout.schemaProjectPmPath;
1435
+ if (params.workspaceContextMode === "snapshot") {
1436
+ await bindSnapshotTracker(params.layout.workspaceSnapshotRoot, selectedSandboxProjectPmPath);
1437
+ }
1438
+ const trust = params.options?.pmRoot
1439
+ ? await resolveLinkedTestTrust(params.options.pmRoot, params.linkedTest, params.currentSourceRef)
1440
+ : await resolveLinkedTestTrust(params.layout.root, params.linkedTest, params.currentSourceRef);
1369
1441
  return {
1370
1442
  linkedOverridePmContextMode,
1371
1443
  executionContext: buildLinkedTestExecutionContext({
@@ -1377,6 +1449,10 @@ function resolveLinkedTestCommandContext(params) {
1377
1449
  requestedPmContextMode,
1378
1450
  effectivePmContextMode,
1379
1451
  autoPmContextApplied,
1452
+ requestedWorkspaceContextMode: params.requestedWorkspaceContextMode,
1453
+ workspaceContextMode: params.workspaceContextMode,
1454
+ sourceWorkspaceRoot: params.sourceWorkspaceRoot,
1455
+ trust,
1380
1456
  }),
1381
1457
  };
1382
1458
  }
@@ -1401,14 +1477,47 @@ function buildLinkedTestAssertionFailureResult(linkedTest, executionContext, err
1401
1477
  error,
1402
1478
  };
1403
1479
  }
1404
- function resolveLinkedTestPreflightResult(params) {
1480
+ function buildLinkedTestTrustRefusalResult(linkedTest, executionContext, error) {
1481
+ return {
1482
+ command: linkedTest.command,
1483
+ path: linkedTest.path,
1484
+ status: "failed",
1485
+ exit_code: 1,
1486
+ failure_category: "trust_refusal",
1487
+ execution_context: executionContext,
1488
+ error,
1489
+ };
1490
+ }
1491
+ /** Build the fail-closed result for a command this clone has not trusted. */
1492
+ function resolveLinkedTestTrustPreflightResult(params) {
1493
+ if (params.executionContext.trust.trusted)
1494
+ return null;
1495
+ const overrideRequested = params.options?.allowUntrustedLinkedTests === true;
1496
+ const policyEnabled = params.options?.untrustedLinkedTestsPolicyEnabled === true;
1497
+ if (overrideRequested && policyEnabled)
1498
+ return null;
1499
+ const recovery = policyEnabled
1500
+ ? "Retry with --allow-untrusted-linked-tests for this invocation, or acknowledge the current commands with --acknowledge-linked-tests."
1501
+ : "Set testing.allow_untrusted_linked_tests=true, then retry with --allow-untrusted-linked-tests; alternatively acknowledge the current commands with --acknowledge-linked-tests.";
1502
+ return buildLinkedTestTrustRefusalResult(params.linkedTest, params.executionContext, `Linked test command is not trusted by this clone (source_ref=${params.executionContext.trust.source_ref ?? "unknown"}, current_ref=${params.executionContext.trust.current_source_ref ?? "unknown"}). ${recovery}`);
1503
+ }
1504
+ /** Resolve missing-command and runtime-safety skips before policy checks. */
1505
+ function resolveLinkedTestSafetyPreflightResult(params) {
1405
1506
  if (!params.linkedTest.command) {
1406
1507
  return buildLinkedTestSkippedResult(params.linkedTest, params.executionContext, "No command configured for this linked test.");
1407
1508
  }
1408
1509
  const runtimeSafetySkipReason = getRuntimeSafetySkipReason(params.linkedTest.command);
1409
- if (runtimeSafetySkipReason) {
1410
- return buildLinkedTestSkippedResult(params.linkedTest, params.executionContext, runtimeSafetySkipReason);
1411
- }
1510
+ return runtimeSafetySkipReason
1511
+ ? buildLinkedTestSkippedResult(params.linkedTest, params.executionContext, runtimeSafetySkipReason)
1512
+ : null;
1513
+ }
1514
+ function resolveLinkedTestPreflightResult(params) {
1515
+ const safetyFailure = resolveLinkedTestSafetyPreflightResult(params);
1516
+ if (safetyFailure)
1517
+ return safetyFailure;
1518
+ const trustFailure = resolveLinkedTestTrustPreflightResult(params);
1519
+ if (trustFailure)
1520
+ return trustFailure;
1412
1521
  const failOnMismatchByDefault = params.executionContext.pm_context_mode === "schema" &&
1413
1522
  params.executionContext.is_pm_tracker_read_command &&
1414
1523
  params.executionContext.mismatch_detected;
@@ -1451,11 +1560,20 @@ function buildLinkedTestExecutionEnv(params) {
1451
1560
  executionEnv.FORCE_COLOR = "0";
1452
1561
  executionEnv.PM_PATH = params.executionContext.sandbox_project_pm_path;
1453
1562
  executionEnv.PM_GLOBAL_PATH = params.executionContext.sandbox_global_pm_path;
1454
- executionEnv.PM_SOURCE_WORKSPACE_ROOT =
1455
- process.env.PM_SOURCE_WORKSPACE_ROOT ?? process.cwd();
1456
- executionEnv.PM_SOURCE_PM_PATH =
1457
- params.executionContext.source_project_pm_path;
1458
- executionEnv[SOURCE_CONTEXT_ACCESS_ENV] = "read_only";
1563
+ if (params.executionContext.workspace_context_mode === "isolated") {
1564
+ delete executionEnv.PM_SOURCE_WORKSPACE_ROOT;
1565
+ delete executionEnv.PM_SOURCE_PM_PATH;
1566
+ delete executionEnv[SOURCE_CONTEXT_ACCESS_ENV];
1567
+ }
1568
+ else {
1569
+ executionEnv.PM_SOURCE_WORKSPACE_ROOT =
1570
+ params.executionContext.source_workspace_root;
1571
+ executionEnv.PM_SOURCE_PM_PATH =
1572
+ params.executionContext.workspace_context_mode === "snapshot"
1573
+ ? params.executionContext.sandbox_project_pm_path
1574
+ : params.executionContext.source_project_pm_path;
1575
+ executionEnv[SOURCE_CONTEXT_ACCESS_ENV] = "read_only";
1576
+ }
1459
1577
  return executionEnv;
1460
1578
  }
1461
1579
  function buildLinkedTestPassedResult(linkedTest, executionContext, execution) {
@@ -1518,7 +1636,7 @@ async function runSingleLinkedTest(params) {
1518
1636
  total: params.total,
1519
1637
  timeoutMs,
1520
1638
  command,
1521
- }, params.progressMode);
1639
+ }, params.progressMode, params.executionContext.working_directory);
1522
1640
  const passed = execution.exitCode === 0 &&
1523
1641
  !execution.timedOut &&
1524
1642
  !execution.maxBufferExceeded;
@@ -1532,23 +1650,36 @@ export async function runLinkedTests(tests, defaultTimeoutSeconds, options) {
1532
1650
  const sandboxRoot = await mkdtemp(path.join(tmpdir(), "pm-linked-test-"));
1533
1651
  const layout = createLinkedTestSandboxLayout(sandboxRoot);
1534
1652
  const runLevelPmContextMode = parsePmContextMode(options?.pmContext);
1653
+ const runLevelWorkspaceContextMode = parseWorkspaceContextMode(options?.workspaceContext);
1535
1654
  const progressMode = options?.progress === true ? "always" : "auto";
1536
1655
  const runtimeDirectives = resolveRuntimeDirectives(options?.envSet, options?.envClear, options?.sharedHostSafe);
1537
1656
  const sourceRoots = options?.sourceRoots;
1657
+ const sourceWorkspaceRoot = resolveLinkedTestSourceWorkspaceRoot();
1538
1658
  const includeTrackerData = linkedTestsRequireTrackerData(tests, runLevelPmContextMode, options);
1659
+ const includeWorkspaceSnapshot = linkedTestsRequireWorkspaceSnapshot(tests, runLevelWorkspaceContextMode, options?.overrideLinkedWorkspaceContext === true);
1660
+ const currentSourceRef = await resolveLinkedTestSourceRef(sourceWorkspaceRoot);
1539
1661
  try {
1540
1662
  await initializeLinkedTestSandboxes(layout, runInit, includeTrackerData);
1663
+ await mkdir(layout.workspaceIsolatedRoot, { recursive: true });
1541
1664
  await seedLinkedTestSandboxesFromSource(layout, sourceRoots, includeTrackerData);
1665
+ if (includeWorkspaceSnapshot) {
1666
+ await seedLinkedTestWorkspaceSnapshot(layout, sourceWorkspaceRoot);
1667
+ }
1542
1668
  const counts = await countLinkedTestSandboxItems(layout, sourceRoots);
1543
1669
  for (let index = 0; index < tests.length; index += 1) {
1544
1670
  const linkedTest = tests[index];
1545
- const commandContext = resolveLinkedTestCommandContext({
1671
+ const workspaceContextMode = resolveLinkedTestWorkspaceContextMode(linkedTest, runLevelWorkspaceContextMode, options?.overrideLinkedWorkspaceContext === true);
1672
+ const commandContext = await resolveLinkedTestCommandContext({
1546
1673
  linkedTest,
1547
1674
  layout,
1548
1675
  counts,
1549
1676
  sourceRoots,
1550
1677
  runLevelPmContextMode,
1551
1678
  options,
1679
+ requestedWorkspaceContextMode: runLevelWorkspaceContextMode,
1680
+ workspaceContextMode,
1681
+ currentSourceRef,
1682
+ sourceWorkspaceRoot,
1552
1683
  });
1553
1684
  const preflightResult = resolveLinkedTestPreflightResult({
1554
1685
  linkedTest,
@@ -1602,7 +1733,8 @@ function linkedTestsHaveSameIdentity(left, right) {
1602
1733
  return (left.command === right.command &&
1603
1734
  left.path === right.path &&
1604
1735
  left.scope === right.scope &&
1605
- left.pm_context_mode === right.pm_context_mode);
1736
+ left.pm_context_mode === right.pm_context_mode &&
1737
+ left.workspace_context_mode === right.workspace_context_mode);
1606
1738
  }
1607
1739
  function appendMissingLinkedTests(current, additions) {
1608
1740
  const next = [...current];
@@ -1635,21 +1767,29 @@ function applyLinkedTestMutations(previous, adds, removes, removeIndexes) {
1635
1767
  return removeLinkedTestsBySelector(appendMissingLinkedTests(previous, adds), removes, removeIndexes);
1636
1768
  }
1637
1769
  function hasTestRuntimeDirectiveFlags(options) {
1638
- return ((options.envSet?.length ?? 0) > 0 ||
1639
- (options.envClear?.length ?? 0) > 0 ||
1640
- options.sharedHostSafe === true ||
1641
- options.pmContext !== undefined ||
1642
- options.overrideLinkedPmContext === true ||
1643
- options.failOnContextMismatch === true ||
1644
- options.failOnSkipped === true ||
1645
- options.failOnEmptyTestRun === true ||
1646
- options.requireAssertionsForPm === true ||
1647
- options.checkContext === true ||
1648
- options.autoPmContext === true);
1770
+ return [
1771
+ (options.envSet?.length ?? 0) > 0,
1772
+ (options.envClear?.length ?? 0) > 0,
1773
+ options.sharedHostSafe === true,
1774
+ options.pmContext !== undefined,
1775
+ options.overrideLinkedPmContext === true,
1776
+ options.failOnContextMismatch === true,
1777
+ options.failOnSkipped === true,
1778
+ options.failOnEmptyTestRun === true,
1779
+ options.requireAssertionsForPm === true,
1780
+ options.checkContext === true,
1781
+ options.autoPmContext === true,
1782
+ options.workspaceContext !== undefined,
1783
+ options.overrideLinkedWorkspaceContext === true,
1784
+ options.allowUntrustedLinkedTests === true,
1785
+ ].some(Boolean);
1649
1786
  }
1650
1787
  function assertTestRunFlagUsage(options) {
1651
1788
  if (hasTestRuntimeDirectiveFlags(options) && options.run !== true) {
1652
- throw new PmCliError("--env-set, --env-clear, --shared-host-safe, --pm-context, --override-linked-pm-context, --fail-on-context-mismatch, --fail-on-skipped, --fail-on-empty-test-run, --require-assertions-for-pm, --check-context, and --auto-pm-context require --run", EXIT_CODE.USAGE);
1789
+ throw new PmCliError("--env-set, --env-clear, --shared-host-safe, --pm-context, --override-linked-pm-context, --workspace-context, --override-linked-workspace-context, --allow-untrusted-linked-tests, --fail-on-context-mismatch, --fail-on-skipped, --fail-on-empty-test-run, --require-assertions-for-pm, --check-context, and --auto-pm-context require --run", EXIT_CODE.USAGE);
1790
+ }
1791
+ if (options.acknowledgeLinkedTests === true && options.run === true) {
1792
+ throw new PmCliError("--acknowledge-linked-tests records trust without executing commands; run it separately before --run", EXIT_CODE.USAGE);
1653
1793
  }
1654
1794
  const hasSelectorFlags = options.match !== undefined ||
1655
1795
  options.onlyIndex !== undefined ||
@@ -1749,6 +1889,11 @@ async function executeSelectedLinkedTests(params) {
1749
1889
  requireAssertionsForPm: options.requireAssertionsForPm,
1750
1890
  checkContext: options.checkContext,
1751
1891
  autoPmContext: options.autoPmContext,
1892
+ workspaceContext: options.workspaceContext,
1893
+ overrideLinkedWorkspaceContext: options.overrideLinkedWorkspaceContext,
1894
+ pmRoot,
1895
+ allowUntrustedLinkedTests: options.allowUntrustedLinkedTests,
1896
+ untrustedLinkedTestsPolicyEnabled: params.untrustedLinkedTestsPolicyEnabled,
1752
1897
  sourceRoots: {
1753
1898
  projectPmRoot: pmRoot,
1754
1899
  globalPmRoot: resolveGlobalPmRoot(process.cwd()),
@@ -1798,6 +1943,8 @@ function buildTrackedTestRunSummary(params) {
1798
1943
  command: result.command.trim(),
1799
1944
  requested_pm_context_mode: result.execution_context?.requested_pm_context_mode,
1800
1945
  pm_context_mode: result.execution_context?.pm_context_mode,
1946
+ workspace_context_mode: result.execution_context?.workspace_context_mode,
1947
+ trust_reason: result.execution_context?.trust.reason,
1801
1948
  })),
1802
1949
  };
1803
1950
  }
@@ -1854,6 +2001,12 @@ function buildTestMeasurementProjection(options, runs, recordedRun) {
1854
2001
  : diffTestRunMeasurements(runs, options.metricDiff),
1855
2002
  };
1856
2003
  }
2004
+ /** Acknowledge current linked commands only when the dedicated mode is active. */
2005
+ async function maybeAcknowledgeLinkedTests(options, pmRoot, tests) {
2006
+ if (options.acknowledgeLinkedTests !== true)
2007
+ return undefined;
2008
+ return acknowledgeLinkedTests(pmRoot, tests, nowIso());
2009
+ }
1857
2010
  /** Implements run test for the public runtime surface of this module. */
1858
2011
  export async function runTest(id, options, global) {
1859
2012
  const stdinResolver = createStdinTokenResolver();
@@ -1869,10 +2022,11 @@ export async function runTest(id, options, global) {
1869
2022
  const resolvedAdds = await stdinResolver.resolveList(options.add, "--add");
1870
2023
  const resolvedAddJsons = await stdinResolver.resolveList(options.addJson, "--add-json");
1871
2024
  const resolvedRemoves = await stdinResolver.resolveList(options.remove, "--remove");
1872
- const adds = [
2025
+ const parsedAdds = [
1873
2026
  ...parseAddEntries(resolvedAdds),
1874
2027
  ...parseAddJsonEntries(resolvedAddJsons),
1875
2028
  ];
2029
+ const adds = (await attachLinkedTestMutationProvenance(parsedAdds, resolveAuthor(options.author, settings.author_default), nowIso())) ?? [];
1876
2030
  const removes = parseRemoveEntries(resolvedRemoves);
1877
2031
  const removeIndexes = parseRemoveIndexes(options.removeIndex);
1878
2032
  const item = await resolveLinkedTestItem({
@@ -1886,17 +2040,22 @@ export async function runTest(id, options, global) {
1886
2040
  removeIndexes,
1887
2041
  });
1888
2042
  const runOptions = resolveTestRunOptions(options, item.tests);
2043
+ const trustAcknowledgement = await maybeAcknowledgeLinkedTests(options, pmRoot, item.tests);
1889
2044
  const runStartedAt = options.run === true ? nowIso() : undefined;
1890
2045
  const runResults = await executeSelectedLinkedTests({
1891
2046
  options,
1892
2047
  runOptions,
1893
2048
  pmRoot,
2049
+ untrustedLinkedTestsPolicyEnabled: settings.testing.allow_untrusted_linked_tests,
1894
2050
  });
1895
2051
  const failureCategories = countFailureCategories(runResults);
1896
2052
  const failOnSkippedTriggered = options.run === true &&
1897
2053
  options.failOnSkipped === true &&
1898
2054
  runResults.some((entry) => entry.status === "skipped");
1899
2055
  const warnings = buildTestWarnings(options, runOptions.runSelection, runResults);
2056
+ if (trustAcknowledgement) {
2057
+ warnings.push(`linked_test_trust_acknowledged:${trustAcknowledgement.acknowledged}`);
2058
+ }
1900
2059
  const recordedRun = await recordTestRunSummary({
1901
2060
  options,
1902
2061
  pmRoot,
@@ -1972,4 +2131,4 @@ export const _testOnlyTestCommand = {
1972
2131
  summarizeRunResultStatuses,
1973
2132
  };
1974
2133
  //# sourceMappingURL=execution.js.map
1975
- //# debugId=5fe0ee3e-9590-56fc-b238-e56ff1970e52
2134
+ //# debugId=7659721c-21f5-55da-8cce-31b290de5bfb
@@ -20,6 +20,10 @@ export declare const LINKED_TEST_ENV_NAME_PATTERN: RegExp;
20
20
  export declare const LINKED_TEST_PM_CONTEXT_MODE_VALUES: readonly ["schema", "tracker", "auto"];
21
21
  /** Restricts linked test pm context mode values accepted by command, SDK, and storage contracts. */
22
22
  export type LinkedTestPmContextMode = (typeof LINKED_TEST_PM_CONTEXT_MODE_VALUES)[number];
23
+ /** Supported values accepted by the linked test workspace context contract. */
24
+ export declare const LINKED_TEST_WORKSPACE_CONTEXT_MODE_VALUES: readonly ["source", "isolated", "snapshot"];
25
+ /** Restricts source-workspace modes accepted by command and storage contracts. */
26
+ export type LinkedTestWorkspaceContextMode = (typeof LINKED_TEST_WORKSPACE_CONTEXT_MODE_VALUES)[number];
23
27
  /** Implements parse linked test env set for the public runtime surface of this module. */
24
28
  export declare function parseLinkedTestEnvSet(raw: string | undefined, optionName: string): Record<string, string> | undefined;
25
29
  /** Implements parse linked test env clear for the public runtime surface of this module. */
@@ -28,6 +32,8 @@ export declare function parseLinkedTestEnvClear(raw: string | undefined, optionN
28
32
  export declare function parseLinkedTestBoolean(raw: string | undefined, optionName: string, fieldLabel: string): boolean | undefined;
29
33
  /** Implements parse linked test context mode for the public runtime surface of this module. */
30
34
  export declare function parseLinkedTestContextMode(raw: string | undefined, optionName: string): LinkedTest["pm_context_mode"] | undefined;
35
+ /** Parse one linked-test source-workspace context mode. */
36
+ export declare function parseLinkedTestWorkspaceContextMode(raw: string | undefined, optionName: string): LinkedTest["workspace_context_mode"] | undefined;
31
37
  /** Implements parse linked test string list for the public runtime surface of this module. */
32
38
  export declare function parseLinkedTestStringList(raw: string | undefined): string[] | undefined;
33
39
  /** Implements parse linked test regex list for the public runtime surface of this module. */
@@ -4,7 +4,7 @@
4
4
  * Implements the pm linked test parsers 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]="74163bed-a473-543e-ae6a-0d5091b3b3a9")}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]="64adcc3c-3646-5990-9442-4c4374a2dc7d")}catch(e){}}();
8
8
  import { parseOptionalNumber } from "../../core/item/parse.js";
9
9
  import { EXIT_CODE } from "../../core/shared/constants.js";
10
10
  import { PmCliError } from "../../core/shared/errors.js";
@@ -43,6 +43,12 @@ export const LINKED_TEST_PM_CONTEXT_MODE_VALUES = [
43
43
  "tracker",
44
44
  "auto",
45
45
  ];
46
+ /** Supported values accepted by the linked test workspace context contract. */
47
+ export const LINKED_TEST_WORKSPACE_CONTEXT_MODE_VALUES = [
48
+ "source",
49
+ "isolated",
50
+ "snapshot",
51
+ ];
46
52
  /** Implements parse linked test env set for the public runtime surface of this module. */
47
53
  export function parseLinkedTestEnvSet(raw, optionName) {
48
54
  if (!raw) {
@@ -119,6 +125,17 @@ export function parseLinkedTestContextMode(raw, optionName) {
119
125
  }
120
126
  throw new PmCliError(`${optionName} pm_context_mode must be one of: ${LINKED_TEST_PM_CONTEXT_MODE_VALUES.join(", ")}`, EXIT_CODE.USAGE);
121
127
  }
128
+ /** Parse one linked-test source-workspace context mode. */
129
+ export function parseLinkedTestWorkspaceContextMode(raw, optionName) {
130
+ if (!raw) {
131
+ return undefined;
132
+ }
133
+ const value = raw.trim().toLowerCase();
134
+ if (LINKED_TEST_WORKSPACE_CONTEXT_MODE_VALUES.includes(value)) {
135
+ return value;
136
+ }
137
+ throw new PmCliError(`${optionName} workspace_context_mode must be one of: ${LINKED_TEST_WORKSPACE_CONTEXT_MODE_VALUES.join(", ")}`, EXIT_CODE.USAGE);
138
+ }
122
139
  /** Implements parse linked test string list for the public runtime surface of this module. */
123
140
  export function parseLinkedTestStringList(raw) {
124
141
  if (!raw) {
@@ -476,6 +493,7 @@ function parseLinkedTestJsonEntry(rawEntry, label, optionName) {
476
493
  !LINKED_TEST_PM_CONTEXT_MODE_VALUES.includes(pmContextModeValue)) {
477
494
  throw fail(`field "pm_context_mode" must be one of: ${LINKED_TEST_PM_CONTEXT_MODE_VALUES.join(", ")}`);
478
495
  }
496
+ const workspaceContextModeValue = parseLinkedTestWorkspaceContextMode(readJsonEntryString(entry, "workspace_context_mode", fail), `${optionName} ${label}`);
479
497
  const noteValue = readJsonEntryString(entry, "note", fail);
480
498
  return {
481
499
  command,
@@ -483,6 +501,7 @@ function parseLinkedTestJsonEntry(rawEntry, label, optionName) {
483
501
  scope: readJsonEntryScope(entry, fail),
484
502
  timeout_seconds: readJsonEntryTimeoutSeconds(entry, fail),
485
503
  pm_context_mode: pmContextModeValue,
504
+ workspace_context_mode: workspaceContextModeValue,
486
505
  env_set: readJsonEntryEnvSet(entry, fail),
487
506
  env_clear: readJsonEntryEnvClear(entry, fail),
488
507
  shared_host_safe: readJsonEntryBoolean(entry, "shared_host_safe", fail),
@@ -524,4 +543,4 @@ export const _testOnlyLinkedTestParsers = {
524
543
  parseLinkedTestJsonEntry,
525
544
  };
526
545
  //# sourceMappingURL=parsers.js.map
527
- //# debugId=74163bed-a473-543e-ae6a-0d5091b3b3a9
546
+ //# debugId=64adcc3c-3646-5990-9442-4c4374a2dc7d