@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.
- package/.claude-plugin/marketplace.json +2 -2
- package/CHANGELOG.md +44 -0
- package/dist/cli/error-guidance.d.ts +22 -4
- package/dist/cli/error-guidance.js +73 -16
- package/dist/cli/main.d.ts +3 -0
- package/dist/cli/main.js +51 -9
- package/dist/cli/register-mutation.js +6 -6
- package/dist/cli/register-operations.d.ts +73 -0
- package/dist/cli/register-operations.js +39 -14
- package/dist/cli/register-setup.js +3 -3
- package/dist/cli/schema-registration-helpers.d.ts +8 -1
- package/dist/cli/schema-registration-helpers.js +14 -5
- package/dist/cli-bundle/bundle-manifest.json +149 -149
- package/dist/cli-bundle/chunks/chunk-3ENVHX6C.js +36 -0
- package/dist/cli-bundle/chunks/{chunk-LOVTQ52Z.js → chunk-DW2CGEMU.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-EG6CLDZT.js +5 -0
- package/dist/cli-bundle/chunks/{chunk-LARJJ3RK.js → chunk-F3AI2JFL.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-N3DSV4CH.js → chunk-HWZ6CELB.js} +8 -8
- package/dist/cli-bundle/chunks/chunk-HZBMLSZI.js +201 -0
- package/dist/cli-bundle/chunks/chunk-JB4KZWBF.js +3 -0
- package/dist/cli-bundle/chunks/chunk-KPH73XXJ.js +2 -0
- package/dist/cli-bundle/chunks/{chunk-BKYB45NQ.js → chunk-KXPMB2YL.js} +50 -50
- package/dist/cli-bundle/chunks/{chunk-BUNIEJNE.js → chunk-QDVHNHAQ.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-TFI5V5N2.js +13 -0
- package/dist/cli-bundle/chunks/{register-list-query-F57EWDEF.js → register-list-query-UJB5FMV2.js} +2 -2
- package/dist/cli-bundle/chunks/register-mutation-NX5XE4VX.js +20 -0
- package/dist/cli-bundle/chunks/register-operations-RXIJIYXC.js +2 -0
- package/dist/cli-bundle/chunks/{register-setup-HXU7IZZD.js → register-setup-GFUZEKRE.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-F4YEW5I2.js → chunk-37Z4OEAP.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-3UPTYG44.js +8 -0
- package/dist/cli-bundle/focused-chunks/chunk-5S7E72FY.js +16 -0
- package/dist/cli-bundle/focused-chunks/{chunk-4ERLO7QD.js → chunk-AUFH5H35.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-E7X25GC3.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-FQCSTBWE.js +4 -0
- package/dist/cli-bundle/focused-chunks/{chunk-FH6DYED3.js → chunk-FZS4H32O.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-IFLWKY2R.js +156 -0
- package/dist/cli-bundle/focused-chunks/{chunk-YKBKIEAJ.js → chunk-JOWDB2MH.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-LYVSW5VM.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-MRCNF3U6.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-UZE7XUZK.js → chunk-OFVUGTLJ.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-FJ22HNRP.js → chunk-OGLQBL46.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-OY7QAV4Y.js +51 -0
- package/dist/cli-bundle/focused-chunks/chunk-QGFNI65N.js +26 -0
- package/dist/cli-bundle/focused-chunks/{chunk-KDNG7TKQ.js → chunk-R27IDGHD.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-UYRFYCDQ.js +5 -0
- package/dist/cli-bundle/focused-chunks/{chunk-2NIY5NKG.js → chunk-ZHVA42R5.js} +9 -9
- package/dist/cli-bundle/main.js +13 -13
- package/dist/cli-bundle/sdk-authoring.js +1 -1
- package/dist/cli-bundle/sdk-contracts.js +2 -2
- package/dist/cli-bundle/sdk-core.js +40 -40
- package/dist/cli-bundle/sdk-governance.js +1 -1
- package/dist/cli-bundle/sdk-graph.js +1 -1
- package/dist/cli-bundle/sdk-merge.js +33 -32
- package/dist/cli-bundle/sdk-query.js +1 -1
- package/dist/cli-bundle/sdk-runtime.js +1 -1
- package/dist/cli-bundle/sdk-testing.js +1 -1
- package/dist/cli-bundle/sdk.js +5 -5
- package/dist/core/config/nested-settings.js +8 -2
- package/dist/core/config/positional-value.d.ts +1 -1
- package/dist/core/config/positional-value.js +5 -2
- package/dist/core/extensions/extension-hook-runtime.js +22 -20
- package/dist/core/extensions/extension-types.d.ts +8 -1
- package/dist/core/extensions/extension-types.js +2 -2
- package/dist/core/extensions/loader.js +7 -3
- package/dist/core/extensions/service-ownership.d.ts +8 -0
- package/dist/core/extensions/service-ownership.js +20 -0
- package/dist/core/history/history.js +3 -3
- package/dist/core/item/id.js +20 -5
- package/dist/core/item/item-format.js +81 -2
- package/dist/core/reproducibility/context.d.ts +4 -0
- package/dist/core/reproducibility/context.js +14 -8
- package/dist/core/shared/author.d.ts +55 -1
- package/dist/core/shared/author.js +333 -16
- package/dist/core/shared/constants.js +3 -2
- package/dist/core/shared/errors.d.ts +4 -0
- package/dist/core/shared/errors.js +2 -2
- package/dist/core/store/settings-validator.d.ts +1 -0
- package/dist/core/store/settings-validator.js +6 -3
- package/dist/core/store/settings.js +5 -2
- package/dist/mcp/server.d.ts +1 -1
- package/dist/mcp/server.js +30 -9
- package/dist/sdk/agent/identity-config.d.ts +63 -0
- package/dist/sdk/agent/identity-config.js +107 -0
- package/dist/sdk/agent/refusal-corpus-contracts.d.ts +76 -0
- package/dist/sdk/agent/refusal-corpus-contracts.js +194 -0
- package/dist/sdk/agent/refusal-reachability.d.ts +1 -1
- package/dist/sdk/authoring.d.ts +1 -1
- package/dist/sdk/authoring.js +2 -2
- package/dist/sdk/cli-contracts/agent-output-contracts.d.ts +77 -0
- package/dist/sdk/cli-contracts/agent-output-contracts.js +326 -2
- package/dist/sdk/cli-contracts/commander-mutation-options.js +3 -3
- package/dist/sdk/cli-contracts/enum-contracts.d.ts +4 -0
- package/dist/sdk/cli-contracts/enum-contracts.js +9 -2
- package/dist/sdk/cli-contracts/flag-contracts.js +9 -2
- package/dist/sdk/cli-contracts/flag-lexicon-contracts.js +4 -4
- package/dist/sdk/cli-contracts/registration-helpers.js +5 -2
- package/dist/sdk/cli-contracts/runtime-contracts.d.ts +3 -1
- package/dist/sdk/cli-contracts/runtime-contracts.js +4 -3
- package/dist/sdk/cli-contracts/tool-parameter-tables.js +22 -2
- package/dist/sdk/cli-contracts/tool-schema.d.ts +1 -1
- package/dist/sdk/cli-contracts/tool-schema.js +14 -3
- package/dist/sdk/cli-contracts.d.ts +4 -4
- package/dist/sdk/cli-contracts.js +5 -5
- package/dist/sdk/config.d.ts +6 -1
- package/dist/sdk/config.js +119 -14
- package/dist/sdk/context-intent-contracts.js +4 -2
- package/dist/sdk/core-governance.d.ts +1 -1
- package/dist/sdk/core-governance.js +3 -3
- package/dist/sdk/extension/install-sources.d.ts +1 -1
- package/dist/sdk/extension/install-sources.js +6 -3
- package/dist/sdk/extension/output-ownership.d.ts +1 -0
- package/dist/sdk/extension/output-ownership.js +3 -2
- package/dist/sdk/extension/scaffold.js +11 -5
- package/dist/sdk/flag-invocation-contracts.js +4 -2
- package/dist/sdk/generated/generated-error-code-catalog-part-1.js +74 -2
- package/dist/sdk/generated/generated-error-code-catalog-part-2.js +87 -26
- package/dist/sdk/governance/assurance-action.js +30 -10
- package/dist/sdk/governance/health.d.ts +3 -0
- package/dist/sdk/governance/health.js +39 -7
- package/dist/sdk/governance/hierarchy-validation.d.ts +29 -0
- package/dist/sdk/governance/hierarchy-validation.js +109 -0
- package/dist/sdk/governance/validate.d.ts +3 -9
- package/dist/sdk/governance/validate.js +84 -111
- package/dist/sdk/graph/assembly.d.ts +6 -1
- package/dist/sdk/graph/assembly.js +16 -22
- package/dist/sdk/graph/governance-contracts.d.ts +1 -1
- package/dist/sdk/graph/governance-contracts.js +8 -2
- package/dist/sdk/graph/governance.js +46 -2
- package/dist/sdk/graph/hierarchy-integrity.d.ts +94 -0
- package/dist/sdk/graph/hierarchy-integrity.js +311 -0
- package/dist/sdk/graph/index.d.ts +2 -1
- package/dist/sdk/graph/index.js +3 -2
- package/dist/sdk/graph/remediation.js +32 -2
- package/dist/sdk/graph/run.d.ts +31 -2
- package/dist/sdk/graph/run.js +80 -31
- package/dist/sdk/graph/workspace-relationship-kind-registry.d.ts +8 -0
- package/dist/sdk/graph/workspace-relationship-kind-registry.js +28 -0
- package/dist/sdk/index.d.ts +8 -3
- package/dist/sdk/index.js +9 -4
- package/dist/sdk/item-children.js +21 -8
- package/dist/sdk/lifecycle/create.js +129 -97
- package/dist/sdk/lifecycle/hierarchy-mutation-lock.d.ts +15 -0
- package/dist/sdk/lifecycle/hierarchy-mutation-lock.js +25 -0
- package/dist/sdk/lifecycle/update.d.ts +2 -0
- package/dist/sdk/lifecycle/update.js +174 -84
- package/dist/sdk/merge/three-way.js +105 -26
- package/dist/sdk/query/complete-list.d.ts +3 -3
- package/dist/sdk/query/complete-list.js +46 -3
- package/dist/sdk/query/list.d.ts +4 -3
- package/dist/sdk/query/list.js +89 -49
- package/dist/sdk/read-output-budget.d.ts +21 -0
- package/dist/sdk/read-output-budget.js +39 -2
- package/dist/sdk/read-output-contracts.d.ts +10 -4
- package/dist/sdk/read-output-contracts.js +105 -32
- package/dist/sdk/relationship-context.js +3 -4
- package/dist/sdk/relationships.js +17 -10
- package/dist/sdk/reproducibility/process.d.ts +19 -0
- package/dist/sdk/reproducibility/process.js +88 -0
- package/dist/sdk/runtime-primitives.d.ts +1 -1
- package/dist/sdk/runtime-primitives.js +3 -3
- package/dist/sdk/runtime.d.ts +3 -2
- package/dist/sdk/runtime.js +7 -5
- package/dist/sdk/test/batch.d.ts +6 -0
- package/dist/sdk/test/batch.js +26 -6
- package/dist/sdk/test/entry.d.ts +1 -1
- package/dist/sdk/test/entry.js +3 -2
- package/dist/sdk/test/execution.d.ts +28 -3
- package/dist/sdk/test/execution.js +201 -34
- package/dist/sdk/test/parsers.d.ts +6 -0
- package/dist/sdk/test/parsers.js +21 -2
- package/dist/sdk/test/trust.d.ts +33 -0
- package/dist/sdk/test/trust.js +207 -0
- package/dist/types.d.ts +34 -0
- package/dist/types.js +2 -2
- package/docs/AGENT_PROVENANCE_ADR.md +41 -5
- package/docs/AGENT_UX_CONTRACTS.md +20 -4
- package/docs/COMMANDS.md +29 -14
- package/docs/CONFIGURATION.md +4 -0
- package/docs/DEPENDENCY_KIND_CONTRACT.md +31 -3
- package/docs/DIAGNOSTIC_OUTPUT_CONTRACTS.md +115 -0
- package/docs/EXTENSIONS.md +2 -1
- package/docs/MERGE_SAFETY.md +15 -1
- package/docs/README.md +1 -0
- package/docs/READ_OUTPUT_CONTRACTS.md +19 -2
- package/docs/RELATIONSHIP_GRAPH.md +6 -3
- package/docs/REPRODUCIBLE_WORKSPACES.md +42 -8
- package/docs/SDK.md +9 -0
- package/docs/SDK_AGENT_SESSION_CONTEXT.md +6 -4
- package/docs/SDK_EVIDENCE_TRACEABILITY.md +1 -1
- package/docs/TESTING.md +83 -1
- package/docs/agent-task-token-baseline.json +2 -2
- package/docs/generated/FLAG_LEXICON_BUDGETS.md +2 -2
- package/marketplace.json +2 -2
- package/package.json +2 -2
- package/packages/pm-beads/package.json +1 -1
- package/packages/pm-calendar/package.json +1 -1
- package/packages/pm-command-kit/package.json +1 -1
- package/packages/pm-digital-twin/package.json +1 -1
- package/packages/pm-governance-audit/package.json +1 -1
- package/packages/pm-guide-shell/package.json +1 -1
- package/packages/pm-kanban/package.json +1 -1
- package/packages/pm-lifecycle-hooks/package.json +1 -1
- package/packages/pm-linked-test-adapters/package.json +1 -1
- package/packages/pm-search-advanced/package.json +1 -1
- package/packages/pm-templates/package.json +1 -1
- package/packages/pm-todos/package.json +1 -1
- package/packages/pm-vcs/package.json +1 -1
- package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
- package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
- package/sdk/public-surface.json +1021 -53
- package/dist/cli-bundle/chunks/chunk-2MA6ZQVZ.js +0 -197
- package/dist/cli-bundle/chunks/chunk-3AFAUHUE.js +0 -2
- package/dist/cli-bundle/chunks/chunk-7VHZS727.js +0 -5
- package/dist/cli-bundle/chunks/chunk-JSLOFXGA.js +0 -3
- package/dist/cli-bundle/chunks/chunk-OCLPLFAY.js +0 -35
- package/dist/cli-bundle/chunks/chunk-S4IRYKBU.js +0 -13
- package/dist/cli-bundle/chunks/register-mutation-7IY2D5KO.js +0 -20
- package/dist/cli-bundle/chunks/register-operations-FALL3YTU.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-7WZRLCYF.js +0 -50
- package/dist/cli-bundle/focused-chunks/chunk-CKKQSJNN.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-EEFKYMDR.js +0 -16
- package/dist/cli-bundle/focused-chunks/chunk-F2A56DHV.js +0 -8
- package/dist/cli-bundle/focused-chunks/chunk-FV5ACOK7.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-JEAVFOWM.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-NJ63LOP6.js +0 -155
- package/dist/cli-bundle/focused-chunks/chunk-NXRTQ7HI.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-TJ7EMJ46.js +0 -4
- package/dist/cli-bundle/focused-chunks/chunk-ZOSEELYJ.js +0 -26
|
@@ -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]="
|
|
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
|
|
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
|
|
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
|
-
|
|
1410
|
-
|
|
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
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
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
|
|
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
|
|
1639
|
-
(options.
|
|
1640
|
-
options.
|
|
1641
|
-
options.
|
|
1642
|
-
options.
|
|
1643
|
-
options.
|
|
1644
|
-
options.
|
|
1645
|
-
options.
|
|
1646
|
-
options.
|
|
1647
|
-
options.
|
|
1648
|
-
options.
|
|
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()),
|
|
@@ -1791,6 +1936,16 @@ function buildTrackedTestRunSummary(params) {
|
|
|
1791
1936
|
skipped: summary.skipped,
|
|
1792
1937
|
fail_on_skipped_triggered: params.failOnSkippedTriggered ? true : undefined,
|
|
1793
1938
|
measurements: measurements.length > 0 ? measurements : undefined,
|
|
1939
|
+
executions: params.runResults
|
|
1940
|
+
.filter((result) => result.command?.trim())
|
|
1941
|
+
.slice(0, 32)
|
|
1942
|
+
.map((result) => ({
|
|
1943
|
+
command: result.command.trim(),
|
|
1944
|
+
requested_pm_context_mode: result.execution_context?.requested_pm_context_mode,
|
|
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,
|
|
1948
|
+
})),
|
|
1794
1949
|
};
|
|
1795
1950
|
}
|
|
1796
1951
|
async function recordTestRunSummary(params) {
|
|
@@ -1846,6 +2001,12 @@ function buildTestMeasurementProjection(options, runs, recordedRun) {
|
|
|
1846
2001
|
: diffTestRunMeasurements(runs, options.metricDiff),
|
|
1847
2002
|
};
|
|
1848
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
|
+
}
|
|
1849
2010
|
/** Implements run test for the public runtime surface of this module. */
|
|
1850
2011
|
export async function runTest(id, options, global) {
|
|
1851
2012
|
const stdinResolver = createStdinTokenResolver();
|
|
@@ -1861,10 +2022,11 @@ export async function runTest(id, options, global) {
|
|
|
1861
2022
|
const resolvedAdds = await stdinResolver.resolveList(options.add, "--add");
|
|
1862
2023
|
const resolvedAddJsons = await stdinResolver.resolveList(options.addJson, "--add-json");
|
|
1863
2024
|
const resolvedRemoves = await stdinResolver.resolveList(options.remove, "--remove");
|
|
1864
|
-
const
|
|
2025
|
+
const parsedAdds = [
|
|
1865
2026
|
...parseAddEntries(resolvedAdds),
|
|
1866
2027
|
...parseAddJsonEntries(resolvedAddJsons),
|
|
1867
2028
|
];
|
|
2029
|
+
const adds = (await attachLinkedTestMutationProvenance(parsedAdds, resolveAuthor(options.author, settings.author_default), nowIso())) ?? [];
|
|
1868
2030
|
const removes = parseRemoveEntries(resolvedRemoves);
|
|
1869
2031
|
const removeIndexes = parseRemoveIndexes(options.removeIndex);
|
|
1870
2032
|
const item = await resolveLinkedTestItem({
|
|
@@ -1878,17 +2040,22 @@ export async function runTest(id, options, global) {
|
|
|
1878
2040
|
removeIndexes,
|
|
1879
2041
|
});
|
|
1880
2042
|
const runOptions = resolveTestRunOptions(options, item.tests);
|
|
2043
|
+
const trustAcknowledgement = await maybeAcknowledgeLinkedTests(options, pmRoot, item.tests);
|
|
1881
2044
|
const runStartedAt = options.run === true ? nowIso() : undefined;
|
|
1882
2045
|
const runResults = await executeSelectedLinkedTests({
|
|
1883
2046
|
options,
|
|
1884
2047
|
runOptions,
|
|
1885
2048
|
pmRoot,
|
|
2049
|
+
untrustedLinkedTestsPolicyEnabled: settings.testing.allow_untrusted_linked_tests,
|
|
1886
2050
|
});
|
|
1887
2051
|
const failureCategories = countFailureCategories(runResults);
|
|
1888
2052
|
const failOnSkippedTriggered = options.run === true &&
|
|
1889
2053
|
options.failOnSkipped === true &&
|
|
1890
2054
|
runResults.some((entry) => entry.status === "skipped");
|
|
1891
2055
|
const warnings = buildTestWarnings(options, runOptions.runSelection, runResults);
|
|
2056
|
+
if (trustAcknowledgement) {
|
|
2057
|
+
warnings.push(`linked_test_trust_acknowledged:${trustAcknowledgement.acknowledged}`);
|
|
2058
|
+
}
|
|
1892
2059
|
const recordedRun = await recordTestRunSummary({
|
|
1893
2060
|
options,
|
|
1894
2061
|
pmRoot,
|
|
@@ -1964,4 +2131,4 @@ export const _testOnlyTestCommand = {
|
|
|
1964
2131
|
summarizeRunResultStatuses,
|
|
1965
2132
|
};
|
|
1966
2133
|
//# sourceMappingURL=execution.js.map
|
|
1967
|
-
//# debugId=
|
|
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. */
|
package/dist/sdk/test/parsers.js
CHANGED
|
@@ -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]="
|
|
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=
|
|
546
|
+
//# debugId=64adcc3c-3646-5990-9442-4c4374a2dc7d
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { LinkedTest } from "../../types/index.js";
|
|
2
|
+
/** Classification of one linked-test command against the current clone. */
|
|
3
|
+
export interface LinkedTestTrustDecision {
|
|
4
|
+
/** Stable hash binding the decision to command, context, and provenance. */
|
|
5
|
+
fingerprint: string;
|
|
6
|
+
/** Whether execution may proceed without an untrusted-command override. */
|
|
7
|
+
trusted: boolean;
|
|
8
|
+
/** Machine-readable reason for the decision. */
|
|
9
|
+
reason: "legacy" | "local_mutation" | "local_source_ref" | "acknowledged" | "invalid_provenance" | "foreign_source_ref";
|
|
10
|
+
/** Source ref recorded when the command entered tracker data. */
|
|
11
|
+
source_ref?: string;
|
|
12
|
+
/** Current source ref used for the local decision. */
|
|
13
|
+
current_source_ref?: string;
|
|
14
|
+
}
|
|
15
|
+
/** Resolve the current Git branch without invoking a shell. */
|
|
16
|
+
export declare function resolveLinkedTestSourceRef(cwd?: string): Promise<string | undefined>;
|
|
17
|
+
/** Resolve the source checkout shared by linked-test provenance and execution. */
|
|
18
|
+
export declare function resolveLinkedTestSourceWorkspaceRoot(cwd?: string): string;
|
|
19
|
+
/** Attach immutable author/time/ref provenance to newly stored commands. */
|
|
20
|
+
export declare function attachLinkedTestProvenance(tests: LinkedTest[] | undefined, author: string, createdAt: string, sourceRef: string | undefined): LinkedTest[] | undefined;
|
|
21
|
+
/** Attach provenance while avoiding Git inspection for an empty mutation. */
|
|
22
|
+
export declare function attachLinkedTestMutationProvenance(tests: LinkedTest[] | undefined, author: string, createdAt: string): Promise<LinkedTest[] | undefined>;
|
|
23
|
+
/** Produce the clone-local trust identity for one linked-test entry. */
|
|
24
|
+
export declare function linkedTestTrustFingerprint(test: LinkedTest): string;
|
|
25
|
+
/** Resolve whether one stored command is trusted by this clone. */
|
|
26
|
+
export declare function resolveLinkedTestTrust(pmRoot: string, test: LinkedTest, currentSourceRef?: string): Promise<LinkedTestTrustDecision>;
|
|
27
|
+
/** Resolve a collection with one ledger read for validation and batch runs. */
|
|
28
|
+
export declare function resolveLinkedTestTrustBatch(pmRoot: string, tests: LinkedTest[], currentSourceRef?: string): Promise<LinkedTestTrustDecision[]>;
|
|
29
|
+
/** Persist explicit clone-local acknowledgement for selected linked tests. */
|
|
30
|
+
export declare function acknowledgeLinkedTests(pmRoot: string, tests: LinkedTest[], acknowledgedAt: string): Promise<{
|
|
31
|
+
acknowledged: number;
|
|
32
|
+
fingerprints: string[];
|
|
33
|
+
}>;
|