@unbrained/pm-cli 2026.10.5 → 2026.10.6

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 (114) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +26 -1
  3. package/dist/cli/register-setup.js +1 -1
  4. package/dist/cli/runtime/projection-retry.js +1 -1
  5. package/dist/cli-bundle/bundle-manifest.json +158 -158
  6. package/dist/cli-bundle/chunks/{chunk-Z66DWDAU.js → chunk-25EW53VO.js} +39 -38
  7. package/dist/cli-bundle/chunks/{chunk-YVBRASXO.js → chunk-3CCVTV6Q.js} +2 -2
  8. package/dist/cli-bundle/chunks/{chunk-5MNOWZPN.js → chunk-3REXBTST.js} +2 -2
  9. package/dist/cli-bundle/chunks/chunk-4WB55TBC.js +80 -0
  10. package/dist/cli-bundle/chunks/{chunk-OOLYASYJ.js → chunk-6STTPXOC.js} +2 -2
  11. package/dist/cli-bundle/chunks/{chunk-AJ7H6XUF.js → chunk-ATB4ZZBQ.js} +2 -2
  12. package/dist/cli-bundle/chunks/{chunk-N4YGSG6F.js → chunk-D4G43LBV.js} +38 -38
  13. package/dist/cli-bundle/chunks/{chunk-HRJ6G6ZG.js → chunk-K6D6DMRT.js} +28 -28
  14. package/dist/cli-bundle/chunks/chunk-KIRHWPZB.js +2 -0
  15. package/dist/cli-bundle/chunks/chunk-O67BH435.js +6 -0
  16. package/dist/cli-bundle/chunks/{chunk-3CYOBJUK.js → chunk-RJHLD64E.js} +2 -2
  17. package/dist/cli-bundle/chunks/{chunk-WFZ4UEOB.js → chunk-TUKO5NF7.js} +2 -2
  18. package/dist/cli-bundle/chunks/{chunk-VZ63DEVJ.js → chunk-XJD47LUH.js} +2 -2
  19. package/dist/cli-bundle/chunks/{register-list-query-75QXHIFH.js → register-list-query-53WYLNZT.js} +2 -2
  20. package/dist/cli-bundle/chunks/{register-mutation-YS54SCHG.js → register-mutation-34XXUBVI.js} +2 -2
  21. package/dist/cli-bundle/chunks/{register-operations-6KHZQSYJ.js → register-operations-WANO6EIM.js} +2 -2
  22. package/dist/cli-bundle/chunks/register-setup-WT37UPBQ.js +2 -0
  23. package/dist/cli-bundle/focused-chunks/{chunk-GITOYRUW.js → chunk-6KFKQD2D.js} +2 -2
  24. package/dist/cli-bundle/focused-chunks/{chunk-UDYRVBOZ.js → chunk-FOHRPFIT.js} +2 -2
  25. package/dist/cli-bundle/focused-chunks/{chunk-DDRN42PB.js → chunk-GRQXL5G5.js} +2 -2
  26. package/dist/cli-bundle/focused-chunks/{chunk-ABV67XCC.js → chunk-JZ4WGUVH.js} +2 -2
  27. package/dist/cli-bundle/focused-chunks/{chunk-B5RSLVE6.js → chunk-NM3PM6O6.js} +2 -2
  28. package/dist/cli-bundle/focused-chunks/chunk-OT7HK7SG.js +2 -0
  29. package/dist/cli-bundle/focused-chunks/{chunk-2CVX3KR3.js → chunk-PCLU34KB.js} +2 -2
  30. package/dist/cli-bundle/focused-chunks/{chunk-5C3IDDW7.js → chunk-QQIE4L42.js} +2 -2
  31. package/dist/cli-bundle/focused-chunks/chunk-RPUNVKXK.js +2 -0
  32. package/dist/cli-bundle/focused-chunks/{chunk-LE4JUCRR.js → chunk-RXSVJPZ7.js} +2 -2
  33. package/dist/cli-bundle/focused-chunks/chunk-SPN5QTZ3.js +30 -0
  34. package/dist/cli-bundle/focused-chunks/{chunk-CLDPTMQY.js → chunk-SUZJNPGB.js} +2 -2
  35. package/dist/cli-bundle/focused-chunks/{chunk-YMU42COG.js → chunk-W4BG5HHS.js} +2 -2
  36. package/dist/cli-bundle/focused-chunks/{chunk-C224METM.js → chunk-Y5HXFDTZ.js} +2 -2
  37. package/dist/cli-bundle/focused-chunks/{chunk-ECBOPEJE.js → chunk-ZK5SOOPC.js} +61 -61
  38. package/dist/cli-bundle/focused-chunks/chunk-ZQK2XUSS.js +12 -0
  39. package/dist/cli-bundle/main.js +21 -21
  40. package/dist/cli-bundle/sdk-authoring.js +1 -1
  41. package/dist/cli-bundle/sdk-contracts.js +1 -1
  42. package/dist/cli-bundle/sdk-core.js +31 -30
  43. package/dist/cli-bundle/sdk-governance.js +1 -1
  44. package/dist/cli-bundle/sdk-graph.js +1 -1
  45. package/dist/cli-bundle/sdk-merge.js +42 -42
  46. package/dist/cli-bundle/sdk-query.js +1 -1
  47. package/dist/cli-bundle/sdk-runtime.js +1 -1
  48. package/dist/cli-bundle/sdk-testing.js +1 -1
  49. package/dist/cli-bundle/sdk.js +2 -2
  50. package/dist/core/extensions/extension-types.d.ts +2 -0
  51. package/dist/core/extensions/extension-types.js +1 -1
  52. package/dist/sdk/cli-bootstrap.d.ts +5 -1
  53. package/dist/sdk/cli-bootstrap.js +1 -1
  54. package/dist/sdk/cli-contracts/flag-contracts.js +1 -1
  55. package/dist/sdk/cli-contracts/flag-lexicon-contracts.js +1 -1
  56. package/dist/sdk/cli-contracts/tool-parameter-tables.js +1 -1
  57. package/dist/sdk/cli-contracts/tool-schema.d.ts +2 -2
  58. package/dist/sdk/cli-contracts/tool-schema.js +1 -1
  59. package/dist/sdk/extension/managed-state.d.ts +2 -0
  60. package/dist/sdk/extension/managed-state.js +1 -1
  61. package/dist/sdk/extension/managed-update-status.d.ts +3 -0
  62. package/dist/sdk/extension/managed-update-status.js +2 -0
  63. package/dist/sdk/extension/source-resolution.d.ts +2 -6
  64. package/dist/sdk/extension/source-resolution.js +1 -1
  65. package/dist/sdk/extension/update-check.d.ts +10 -0
  66. package/dist/sdk/extension/update-check.js +1 -1
  67. package/dist/sdk/extension-command-context.js +2 -1
  68. package/dist/sdk/extension.d.ts +4 -0
  69. package/dist/sdk/extension.js +1 -1
  70. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +1 -1
  71. package/dist/sdk/generated/generated-error-code-catalog-part-2.js +1 -1
  72. package/dist/sdk/public-surface.json +1 -1
  73. package/dist/sdk/query/get.js +1 -1
  74. package/dist/sdk/read-output-contracts.js +1 -1
  75. package/dist/sdk/runtime-input.js +1 -1
  76. package/dist/sdk/test/execution.d.ts +2 -1
  77. package/dist/sdk/test/execution.js +3 -3
  78. package/docs/DEVELOPMENT_DEPENDENCY_SECURITY.md +36 -0
  79. package/docs/GET_READ_EVIDENCE.md +17 -1
  80. package/docs/OUTPUT_PROJECTION_CONTRACTS.md +23 -0
  81. package/docs/PR_REVIEW_LOOP.md +21 -4
  82. package/docs/README.md +1 -0
  83. package/docs/RELEASING.md +18 -0
  84. package/docs/SDK_CONFIGURATION_SAFETY.md +123 -0
  85. package/docs/generated/FLAG_LEXICON_BUDGETS.md +3 -3
  86. package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +4 -3
  87. package/marketplace.json +2 -2
  88. package/package.json +3 -3
  89. package/packages/pm-beads/package.json +1 -1
  90. package/packages/pm-calendar/package.json +1 -1
  91. package/packages/pm-command-kit/package.json +1 -1
  92. package/packages/pm-digital-twin/package.json +1 -1
  93. package/packages/pm-governance-audit/package.json +1 -1
  94. package/packages/pm-guide-shell/package.json +1 -1
  95. package/packages/pm-kanban/package.json +1 -1
  96. package/packages/pm-lifecycle-hooks/package.json +1 -1
  97. package/packages/pm-linked-test-adapters/package.json +1 -1
  98. package/packages/pm-search-advanced/package.json +1 -1
  99. package/packages/pm-templates/package.json +1 -1
  100. package/packages/pm-todos/package.json +1 -1
  101. package/packages/pm-vcs/package.json +1 -1
  102. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  103. package/plugins/pm-claude/package.json +2 -2
  104. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  105. package/plugins/pm-codex/package.json +2 -2
  106. package/plugins/pm-codex/plugin.json +1 -1
  107. package/dist/cli-bundle/chunks/chunk-D332RXZ7.js +0 -80
  108. package/dist/cli-bundle/chunks/chunk-KRVI56QE.js +0 -6
  109. package/dist/cli-bundle/chunks/chunk-TGMONGWX.js +0 -2
  110. package/dist/cli-bundle/chunks/register-setup-RTATEN2M.js +0 -2
  111. package/dist/cli-bundle/focused-chunks/chunk-DOVTUKJQ.js +0 -2
  112. package/dist/cli-bundle/focused-chunks/chunk-GUIF3WVC.js +0 -30
  113. package/dist/cli-bundle/focused-chunks/chunk-RHIH6XIW.js +0 -12
  114. package/dist/cli-bundle/focused-chunks/chunk-XV5RV4ZT.js +0 -2
@@ -1,8 +1,8 @@
1
- (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="6700ed53-3964-5cc4-ab5e-9759acefdae2")}catch{}})();import{readJsonPathValue,splitJsonPathSegments}from"./json-path.js";import{seedLinkedTestWorkspaceSnapshot}from"./workspace-snapshot.js";import{assertInitializedTracker}from"../environment/tracker-preflight.js";import{spawn}from"node:child_process";import{cp,mkdir,mkdtemp,open,readdir,rm,symlink}from"node:fs/promises";import{tmpdir}from"node:os";import path from"node:path";import{getActiveExtensionRegistrations}from"../../core/extensions/index.js";import{isFileMissingError,pathExists,readFileIfExists}from"../../core/fs/fs-utils.js";import{getWorkspaceHistoryPath,writeWorkspaceJsonWithHistory}from"../../core/history/workspace-history.js";import{resolveItemTypeRegistry}from"../../core/item/type-registry.js";import{createStdinTokenResolver,parseCsvKv,parseOptionalNumber}from"../../core/item/parse.js";import{EXIT_CODE}from"../../core/shared/constants.js";import{resolveAuthor}from"../../core/shared/author.js";import{PmCliError}from"../../core/shared/errors.js";import{stableValueEquals}from"../../core/shared/serialization.js";import{nowIso}from"../../core/shared/time.js";import{locateItem,mutateItem,readLocatedItem}from"../../core/store/item-store.js";import{getSettingsPath,ITEM_FILE_EXTENSIONS,resolveGlobalPmRoot,resolvePmRoot}from"../../core/store/paths.js";import{readSettings}from"../../core/store/settings.js";import{appendTrackedTestRunSummary,buildTrackedTestRunHistory}from"../../core/test/item-test-run-tracking.js";import{runInit}from"../init.js";import{looksLikeStructuredLinkedTestEntry,normalizeStructuredLinkedTestEntry}from"./entry.js";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";import{parseOnlyIndexValue,resolveLinkedTestRunSelection}from"../../core/test/run-selectors.js";import{SCOPE_VALUES}from"../../types/index.js";import{diffTestRunMeasurements,parseTestRunMeasurements,queryTestRunMeasurementsBelow}from"./measurements.js";import{withHostEnvironmentBoundary}from"../environment/host-environment-errors.js";import{SOURCE_CONTEXT_ACCESS_ENV}from"../environment/source-context.js";import{acknowledgeLinkedTests,attachLinkedTestMutationProvenance,resolveLinkedTestSourceRef,resolveLinkedTestSourceWorkspaceRoot,resolveLinkedTestTrust}from"./trust.js";const TEST_OUTPUT_MAX_BUFFER_BYTES=20*1024*1024,DEFAULT_LINKED_TEST_TIMEOUT_FORCE_KILL_DELAY_MS=3e3,DEFAULT_LINKED_TEST_HEARTBEAT_INTERVAL_MS=1e4,DEFAULT_LINKED_TEST_PIPE_CLOSE_GRACE_MS=5e3,MAX_LINKED_TEST_COMMAND_LABEL_LENGTH=120,LINKED_TEST_TRACKER_DIRS_TO_SKIP=new Set(["locks","extensions","runtime"]),LINKED_TEST_ITEM_COUNT_DIRS_TO_SKIP=new Set(["history","index","search","extensions","locks","runtime"]),LINKED_TEST_INFRA_COLLISION_PATTERNS=[/eaddrinuse/i,/address already in use/i,/port\s+\d+\s+is already in use/i,/web server[^.\n]*already running/i,/failed to listen on/i],LINKED_TEST_LOCK_ACQUISITION_PATTERN=/\b(?:could not|failed to|unable to)\s+acquire\b[^\n.]*\block\b/i,LINKED_TEST_LOCK_CONTENTION_CONTEXT_PATTERN=/\b(?:already running|timed?\s*out|timeout|held by another|lock\s+is\s+held)\b/i,PM_TRACKER_READ_SUBCOMMANDS=new Set(["activity","calendar","context","ctx","deps","get","health","history","list","list-all","list-blocked","list-canceled","list-closed","list-draft","list-in-progress","list-open","search","stats","test-all","validate"]);function readPositiveIntegerEnv(name,fallback){const raw=process.env[name];if(typeof raw!="string"||raw.trim().length===0)return fallback;const parsed=Number.parseInt(raw,10);return!Number.isFinite(parsed)||parsed<=0?fallback:parsed}function linkedTestTimeoutForceKillDelayMs(){return readPositiveIntegerEnv("PM_LINKED_TEST_TIMEOUT_FORCE_KILL_DELAY_MS",DEFAULT_LINKED_TEST_TIMEOUT_FORCE_KILL_DELAY_MS)}function linkedTestHeartbeatIntervalMs(){return readPositiveIntegerEnv("PM_LINKED_TEST_HEARTBEAT_INTERVAL_MS",DEFAULT_LINKED_TEST_HEARTBEAT_INTERVAL_MS)}function linkedTestPipeCloseGraceMs(){return readPositiveIntegerEnv("PM_LINKED_TEST_PIPE_CLOSE_GRACE_MS",DEFAULT_LINKED_TEST_PIPE_CLOSE_GRACE_MS)}function resolveTrackedRunId(kind){const fromEnv=process.env.PM_BACKGROUND_TEST_RUN_ID?.trim();return fromEnv&&fromEnv.length>0?fromEnv:`${kind}-local-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,8)}`}function summarizeRunResultStatuses(results){let passed=0,failed=0,skipped=0;for(const entry of results){if(entry.status==="passed"){passed+=1;continue}if(entry.status==="failed"){failed+=1;continue}skipped+=1}return{passed,failed,skipped}}function summarizeContextPreflight(runResults){let checkedPmCommands=0,trackerReadCommands=0,mismatches=0,autoRemediated=0;for(const result of runResults){const context=result.execution_context;!context||context.is_pm_command!==!0||(checkedPmCommands+=1,context.is_pm_tracker_read_command===!0&&(trackerReadCommands+=1),context.mismatch_detected===!0&&(mismatches+=1),context.auto_pm_context_applied===!0&&(autoRemediated+=1))}return{checked_pm_commands:checkedPmCommands,tracker_read_commands:trackerReadCommands,mismatches,auto_remediated:autoRemediated}}function ensureScope(raw){const value=raw??"project";if(!SCOPE_VALUES.includes(value))throw new PmCliError(`Invalid scope "${raw}"`,EXIT_CODE.USAGE);return value}function parsePmContextMode(raw){if(!raw)return"schema";const normalized=raw.trim().toLowerCase();if(PM_CONTEXT_MODE_VALUES.includes(normalized))return normalized;throw new PmCliError(`Invalid --pm-context value "${raw}". Expected one of: ${PM_CONTEXT_MODE_VALUES.join(", ")}`,EXIT_CODE.USAGE)}function parseWorkspaceContextMode(raw){return parseLinkedTestWorkspaceContextMode(raw??"source","--workspace-context")}function resolveLinkedTestWorkspaceContextMode(linkedTest,runLevelMode,overrideLinkedMode){return overrideLinkedMode?runLevelMode:parseLinkedTestWorkspaceContextMode(linkedTest.workspace_context_mode,"linked test")??runLevelMode}function resolveLinkedTestRequestedContextMode(linkedTest,runLevelMode,overrideLinkedPmContext){return overrideLinkedPmContext||typeof linkedTest.pm_context_mode!="string"||linkedTest.pm_context_mode.trim().length===0?runLevelMode:parsePmContextMode(linkedTest.pm_context_mode)}function resolveLinkedTestEffectiveContextMode(requestedMode,isPmTrackerReadCommand){return requestedMode==="auto"?isPmTrackerReadCommand?"tracker":"schema":requestedMode}function hasLinkedTestAssertions(linkedTest){return Object.entries(linkedTest).filter(([key])=>key.startsWith("assert_")).some(([,value])=>value==null?!1:Array.isArray(value)?value.length>0:typeof value=="object"?Object.keys(value).length>0:typeof value=="string"?value.trim().length>0:!0)}function buildPmContextMismatchHint(params){const{executionContext,runLevelPmContextMode,linkedOverridePmContextMode}=params;return!executionContext.is_pm_tracker_read_command||!executionContext.mismatch_detected?"":runLevelPmContextMode==="tracker"&&linkedOverridePmContextMode==="schema"?" Linked test metadata pm_context_mode=schema overrides run-level --pm-context tracker. Set pm_context_mode=tracker (or auto) on the linked test, or remove the override, to run against seeded tracker data.":executionContext.pm_context_mode==="schema"?" Use --auto-pm-context to route PM tracker-read linked commands through seeded tracker data automatically. Alternatively, use --pm-context tracker for the whole run.":""}function mergeEnvSetDirectives(entries,optionName){const merged={};if(!entries)return merged;for(const entry of entries){const parsed=parseLinkedTestEnvSetValue(entry,optionName);if(parsed)for(const[key,value]of Object.entries(parsed))merged[key]=value}return merged}function mergeEnvClearDirectives(entries,optionName){if(!entries)return[];const values=[];for(const entry of entries){const parsed=parseLinkedTestEnvClearValue(entry,optionName);parsed&&values.push(...parsed)}return[...new Set(values)]}import{BUN_GLOBAL_FLAGS_WITH_VALUE,NPM_GLOBAL_FLAGS_WITH_VALUE,PNPM_GLOBAL_FLAGS_WITH_VALUE,SCRIPT_RUN_FLAGS_WITH_VALUE,SCRIPT_RUN_SUBCOMMANDS,YARN_GLOBAL_FLAGS_WITH_VALUE,firstPmSubcommand,extractPmInvocationArgsFromSegment,isPmCliPackageToken,isPmCliScriptToken,isPmExecutableToken,parseLauncherSubcommand,parseNpmExecCommand,parseNpxCommand,parsePnpmDlxCommand,resolvePmSubcommandContext,splitNormalizedCommandSegments,stripLeadingEnvAssignments}from"./linked-command-detection.js";import{extractReferencedPmItemIdsFromCommand}from"./linked-command-detection.js";function commandInvokesPmCli(command){const normalizedCommand=normalizeCommandForValidation(command);return splitNormalizedCommandSegments(normalizedCommand).some(segment=>extractPmInvocationArgsFromSegment(segment)!==null)}function commandInvokesPmTrackerReadCommand(command){const normalizedCommand=normalizeCommandForValidation(command);return splitNormalizedCommandSegments(normalizedCommand).some(segment=>{const invocationArgs=extractPmInvocationArgsFromSegment(segment);if(!invocationArgs)return!1;const context=resolvePmSubcommandContext(invocationArgs);return context?PM_TRACKER_READ_SUBCOMMANDS.has(context.subcommand):!1})}function resolveDirectRunnerSubcommand(parsed){if(parsed)return SCRIPT_RUN_SUBCOMMANDS.has(parsed.subcommand)?parseLauncherSubcommand(parsed.args,SCRIPT_RUN_FLAGS_WITH_VALUE)?.subcommand:parsed.subcommand}function firstDirectTestRunnerSubcommand(executable,args){if(executable==="npx"||executable==="bunx")return parseNpxCommand(args)?.command;if(executable==="pnpm")return resolveDirectRunnerSubcommand(parseLauncherSubcommand(args,PNPM_GLOBAL_FLAGS_WITH_VALUE));if(executable==="npm")return resolveDirectRunnerSubcommand(parseLauncherSubcommand(args,NPM_GLOBAL_FLAGS_WITH_VALUE));if(executable==="yarn")return resolveDirectRunnerSubcommand(parseLauncherSubcommand(args,YARN_GLOBAL_FLAGS_WITH_VALUE));if(executable==="bun")return resolveDirectRunnerSubcommand(parseLauncherSubcommand(args,BUN_GLOBAL_FLAGS_WITH_VALUE))}function isDirectTestRunnerSubcommand(token){return token?token==="vitest"||token==="test"||token.startsWith("test:"):!1}function parsedLauncherInvokesRecursiveTestAll(parsed){return!parsed||!isPmExecutableToken(parsed.command)&&!isPmCliPackageToken(parsed.command)?!1:firstPmSubcommand(parsed.args)==="test-all"}const SHELL_COMMAND_FLAG_PATTERNS=new Map([...["sh","bash","dash","zsh"].map(executable=>[executable,/^(?:-c|-[a-z]*c[a-z]*)$/u]),...["cmd","cmd.exe"].map(executable=>[executable,/^\/[ck]$/u]),...["powershell","powershell.exe","pwsh","pwsh.exe"].map(executable=>[executable,/^-(?:c|command)$/u])]),PACKAGE_EXEC_EXECUTABLES=new Set(["npx","bunx"]);function segmentInvokesRecursiveTestAll(segment){const rawTokens=segment.split(" ").filter(token=>token.length>0),tokens=stripLeadingEnvAssignments(rawTokens);if(tokens.length===0)return!1;const[executable,...args]=tokens,shellCommandFlagPattern=SHELL_COMMAND_FLAG_PATTERNS.get(executable);if(shellCommandFlagPattern){const commandFlagIndex=args.findIndex(arg=>shellCommandFlagPattern.test(arg));return commandFlagIndex>=0?segmentInvokesRecursiveTestAll(args.slice(commandFlagIndex+1).join(" ")):!1}return isPmExecutableToken(executable)||isPmCliScriptToken(executable)?firstPmSubcommand(args)==="test-all":executable==="node"&&args.length>0&&isPmCliScriptToken(args[0])?firstPmSubcommand(args.slice(1))==="test-all":PACKAGE_EXEC_EXECUTABLES.has(executable)?parsedLauncherInvokesRecursiveTestAll(parseNpxCommand(args)):executable==="pnpm"?parsedLauncherInvokesRecursiveTestAll(parsePnpmDlxCommand(args)):executable==="npm"?parsedLauncherInvokesRecursiveTestAll(parseNpmExecCommand(args)):!1}function invokesRecursiveTestAllCommand(command){const normalized=normalizeCommandForValidation(command);return splitNormalizedCommandSegments(normalized).some(segment=>segmentInvokesRecursiveTestAll(segment))}function assertNoRecursiveTestAllCommand(command){if(invokesRecursiveTestAllCommand(command))throw new PmCliError('Linked test commands must not invoke "pm test-all"; this creates recursive orchestration.',EXIT_CODE.USAGE)}function normalizeCommandForValidation(command){return command.trim().replaceAll("\\","/").replaceAll('"',"").replaceAll("'","").replaceAll(/\s+/g," ").toLowerCase()}function commandUsesSandboxRunner(normalizedCommand){return normalizedCommand.includes("node scripts/run-tests.mjs ")||normalizedCommand.endsWith("node scripts/run-tests.mjs")||normalizedCommand.includes("node ./scripts/run-tests.mjs ")||normalizedCommand.endsWith("node ./scripts/run-tests.mjs")}function segmentInvokesDirectTestRunner(normalizedSegment){const rawTokens=normalizedSegment.split(" ").filter(token=>token.length>0),tokens=stripLeadingEnvAssignments(rawTokens);if(tokens.length===0)return!1;const[executable,...args]=tokens,shellCommandFlagPattern=SHELL_COMMAND_FLAG_PATTERNS.get(executable);if(shellCommandFlagPattern){const commandFlagIndex=args.findIndex(argument=>shellCommandFlagPattern.test(argument));return commandFlagIndex>=0&&segmentInvokesDirectTestRunner(args.slice(commandFlagIndex+1).join(" "))}return executable==="vitest"||executable.endsWith("/vitest")||executable.endsWith("/vitest.mjs")?!0:executable==="node"?nodeArgsInvokeDirectTestRunner(args):packageManagerInvokesDirectTestRunner(executable,args)}function nodeArgsInvokeDirectTestRunner(args){return args.includes("--test")||args.some(arg=>arg==="vitest"||arg==="vitest.mjs"||arg.endsWith("/vitest")||arg.endsWith("/vitest.mjs"))}function packageManagerInvokesDirectTestRunner(executable,args){return executable==="npx"||executable==="bunx"?isDirectTestRunnerSubcommand(parseNpxCommand(args)?.command):executable==="pnpm"||executable==="npm"?isDirectTestRunnerSubcommand((executable==="pnpm"?parsePnpmDlxCommand(args):parseNpmExecCommand(args))?.command)||isDirectTestRunnerSubcommand(firstDirectTestRunnerSubcommand(executable,args)):(executable==="yarn"||executable==="bun")&&isDirectTestRunnerSubcommand(firstDirectTestRunnerSubcommand(executable,args))}function classifyLinkedTestCommandSafety(command){const normalized=normalizeCommandForValidation(command),segments=splitNormalizedCommandSegments(normalized),directRunner=segments.some(segment=>!commandUsesSandboxRunner(segment)&&segmentInvokesDirectTestRunner(segment)),sandboxRunner=segments.some(commandUsesSandboxRunner);return{accepted:!0,command_kind:directRunner?"direct_runner":sandboxRunner?"sandbox_runner":"other",tracker_isolation:"runtime_injected",trust_boundary:"provenance",advisory:directRunner?"Accepted with runtime-injected tracker isolation. Prefer the repository sandbox runner when available for reproducible build and coverage setup.":"Accepted with runtime-injected tracker isolation and provenance checks before execution."}}function getRuntimeSafetySkipReason(command){if(invokesRecursiveTestAllCommand(command))return'Linked test command skipped: Linked test commands must not invoke "pm test-all"; this creates recursive orchestration.'}function parseAddEntries(raw){return raw?raw.map(parseAddEntry):[]}function parseAddEntry(entry){const trimmed=entry.trim(),kv=looksLikeStructuredLinkedTestEntry(trimmed)?normalizeStructuredLinkedTestEntry(parseCsvKv(entry,"--add"),"--add"):{command:trimmed},command=trimLinkedTestEntryField(kv.command);if(!command)throw new PmCliError("--add requires command=<value> or a bare command (path=<value> is optional metadata)",EXIT_CODE.USAGE);return assertNoRecursiveTestAllCommand(command),{command,path:trimLinkedTestEntryField(kv.path),scope:ensureScope(kv.scope),timeout_seconds:parseLinkedTestTimeoutSeconds(trimLinkedTestEntryField(kv.timeout_seconds),trimLinkedTestEntryField(kv.timeout)),pm_context_mode:parseLinkedTestContextModeValue(trimLinkedTestEntryField(kv.pm_context_mode),"--add"),workspace_context_mode:parseLinkedTestWorkspaceContextMode(trimLinkedTestEntryField(kv.workspace_context_mode),"--add"),env_set:parseLinkedTestEnvSetValue(trimLinkedTestEntryField(kv.env_set),"--add"),env_clear:parseLinkedTestEnvClearValue(trimLinkedTestEntryField(kv.env_clear),"--add"),shared_host_safe:parseLinkedTestBooleanValue(trimLinkedTestEntryField(kv.shared_host_safe),"--add","shared_host_safe"),assert_stdout_contains:parseLinkedTestStringList(trimLinkedTestEntryField(kv.assert_stdout_contains)),assert_stdout_regex:parseLinkedTestRegexList(trimLinkedTestEntryField(kv.assert_stdout_regex),"--add","assert_stdout_regex"),assert_stderr_contains:parseLinkedTestStringList(trimLinkedTestEntryField(kv.assert_stderr_contains)),assert_stderr_regex:parseLinkedTestRegexList(trimLinkedTestEntryField(kv.assert_stderr_regex),"--add","assert_stderr_regex"),assert_stdout_min_lines:parseLinkedTestMinLines(trimLinkedTestEntryField(kv.assert_stdout_min_lines),"--add"),assert_json_field_equals:parseLinkedTestAssertionEqualsMap(trimLinkedTestEntryField(kv.assert_json_field_equals),"--add"),assert_json_field_gte:parseLinkedTestAssertionGteMap(trimLinkedTestEntryField(kv.assert_json_field_gte),"--add"),note:trimLinkedTestEntryField(kv.note)}}function trimLinkedTestEntryField(value){const trimmed=value?.trim();return trimmed&&trimmed.length>0?trimmed:void 0}function parseLinkedTestTimeoutSeconds(timeoutSecondsRaw,timeoutAliasRaw){if(timeoutSecondsRaw&&timeoutAliasRaw&&timeoutSecondsRaw!==timeoutAliasRaw)throw new PmCliError("--add timeout and timeout_seconds must match when both are provided",EXIT_CODE.USAGE);const timeoutRaw=timeoutSecondsRaw??timeoutAliasRaw;if(timeoutRaw===void 0)return;const timeout=Math.floor(parseOptionalNumber(timeoutRaw,"timeout_seconds"));if(timeout<=0)throw new PmCliError("--add timeout_seconds must be a positive integer",EXIT_CODE.USAGE);return timeout}function parseAddJsonEntries(raw){return raw?raw.flatMap(entry=>{const parsed=parseLinkedTestJsonEntries(entry,"--add-json");for(const linkedTest of parsed){const command=linkedTest.command;if(!command)throw new PmCliError("--add-json requires a non-empty command string",EXIT_CODE.USAGE);assertNoRecursiveTestAllCommand(command)}return parsed}):[]}function parseRemoveEntries(raw){return raw?raw.map(entry=>{const trimmed=entry.trim();if(!trimmed)throw new PmCliError("--remove requires command or path value",EXIT_CODE.USAGE);const identityPrefix=/^(command|path)\s*=/i.exec(trimmed);if(identityPrefix){const value=trimmed.slice(identityPrefix[0].length).trim();if(!value)throw new PmCliError("--remove requires a non-empty command=<value> or path=<value>",EXIT_CODE.USAGE);return{kind:identityPrefix[1].toLowerCase(),value}}if(/^[A-Za-z_][\w-]*=\S+$/u.test(trimmed))throw new PmCliError("--remove structured selectors require command=<value> or path=<value>",EXIT_CODE.USAGE);if(/^(?:[-*+]\s+)?(?:path|command)\s*:/i.test(trimmed)||trimmed.startsWith("```")){const kv=parseCsvKv(trimmed,"--remove"),kind=kv.path!==void 0?"path":"command",value=kv.path??kv.command;if(!value?.trim())throw new PmCliError("--remove requires command=<value> and/or path=<value>",EXIT_CODE.USAGE);return{kind,value:value.trim()}}return{kind:"either",value:trimmed}}):[]}function parseRemoveIndexes(raw){return raw?[...new Set(raw.map(value=>{const parsed=typeof value=="number"?value:Number.parseInt(value,10);if(!Number.isInteger(parsed)||parsed<1||String(parsed)!==String(value).trim())throw new PmCliError("--remove-index must be a positive integer",EXIT_CODE.USAGE);return parsed}))]:[]}function closeLinkedTestStdin(child){try{child.stdin?.end()}catch{}}function summarizeLinkedTestCommand(command){const normalized=command.trim().replaceAll(/\s+/g," ");return normalized.length<=MAX_LINKED_TEST_COMMAND_LABEL_LENGTH?normalized:`${normalized.slice(0,MAX_LINKED_TEST_COMMAND_LABEL_LENGTH-3)}...`}function shouldEmitLinkedTestProgress(mode){return mode==="off"?!1:mode==="always"?!0:process.stderr.isTTY===!0}function emitLinkedTestProgress(message){try{process.stderr.write(`${message}
1
+ (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="ae567115-9f40-5b3d-872d-73d1d543bc04")}catch{}})();import{readJsonPathValue,splitJsonPathSegments}from"./json-path.js";import{seedLinkedTestWorkspaceSnapshot}from"./workspace-snapshot.js";import{assertInitializedTracker}from"../environment/tracker-preflight.js";import{spawn}from"node:child_process";import{cp,mkdir,mkdtemp,open,readdir,rm,symlink}from"node:fs/promises";import{tmpdir}from"node:os";import path from"node:path";import{getActiveExtensionRegistrations}from"../../core/extensions/index.js";import{isFileMissingError,pathExists,readFileIfExists}from"../../core/fs/fs-utils.js";import{getWorkspaceHistoryPath,writeWorkspaceJsonWithHistory}from"../../core/history/workspace-history.js";import{resolveItemTypeRegistry}from"../../core/item/type-registry.js";import{createStdinTokenResolver,parseCsvKv,parseOptionalNumber}from"../../core/item/parse.js";import{EXIT_CODE}from"../../core/shared/constants.js";import{resolveAuthor}from"../../core/shared/author.js";import{PmCliError}from"../../core/shared/errors.js";import{stableValueEquals}from"../../core/shared/serialization.js";import{nowIso}from"../../core/shared/time.js";import{locateItem,mutateItem,readLocatedItem}from"../../core/store/item-store.js";import{getSettingsPath,ITEM_FILE_EXTENSIONS,resolveGlobalPmRoot,resolvePmRoot}from"../../core/store/paths.js";import{readSettings}from"../../core/store/settings.js";import{appendTrackedTestRunSummary,buildTrackedTestRunHistory}from"../../core/test/item-test-run-tracking.js";import{runInit}from"../init.js";import{looksLikeStructuredLinkedTestEntry,normalizeStructuredLinkedTestEntry}from"./entry.js";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";import{parseOnlyIndexValue,resolveLinkedTestRunSelection}from"../../core/test/run-selectors.js";import{SCOPE_VALUES}from"../../types/index.js";import{diffTestRunMeasurements,parseTestRunMeasurements,queryTestRunMeasurementsBelow}from"./measurements.js";import{withHostEnvironmentBoundary}from"../environment/host-environment-errors.js";import{SOURCE_CONTEXT_ACCESS_ENV}from"../environment/source-context.js";import{acknowledgeLinkedTests,attachLinkedTestMutationProvenance,resolveLinkedTestSourceRef,resolveLinkedTestSourceWorkspaceRoot,resolveLinkedTestTrust}from"./trust.js";const TEST_OUTPUT_MAX_BUFFER_BYTES=20*1024*1024,DEFAULT_LINKED_TEST_TIMEOUT_FORCE_KILL_DELAY_MS=3e3,DEFAULT_LINKED_TEST_HEARTBEAT_INTERVAL_MS=1e4,DEFAULT_LINKED_TEST_PIPE_CLOSE_GRACE_MS=5e3,MAX_LINKED_TEST_COMMAND_LABEL_LENGTH=120,LINKED_TEST_TRACKER_DIRS_TO_SKIP=new Set(["locks","extensions","runtime"]),LINKED_TEST_ITEM_COUNT_DIRS_TO_SKIP=new Set(["history","index","search","extensions","locks","runtime"]),LINKED_TEST_INFRA_COLLISION_PATTERNS=[/eaddrinuse/i,/address already in use/i,/port\s+\d+\s+is already in use/i,/web server[^.\n]*already running/i,/failed to listen on/i],LINKED_TEST_LOCK_ACQUISITION_PATTERN=/\b(?:could not|failed to|unable to)\s+acquire\b[^\n.]*\block\b/i,LINKED_TEST_LOCK_CONTENTION_CONTEXT_PATTERN=/\b(?:already running|timed?\s*out|timeout|held by another|lock\s+is\s+held)\b/i,PM_TRACKER_READ_SUBCOMMANDS=new Set(["activity","calendar","context","ctx","deps","get","health","history","list","list-all","list-blocked","list-canceled","list-closed","list-draft","list-in-progress","list-open","search","stats","test-all","validate"]);function readPositiveIntegerEnv(name,fallback){const raw=process.env[name];if(typeof raw!="string"||raw.trim().length===0)return fallback;const parsed=Number.parseInt(raw,10);return!Number.isFinite(parsed)||parsed<=0?fallback:parsed}function linkedTestTimeoutForceKillDelayMs(){return readPositiveIntegerEnv("PM_LINKED_TEST_TIMEOUT_FORCE_KILL_DELAY_MS",DEFAULT_LINKED_TEST_TIMEOUT_FORCE_KILL_DELAY_MS)}function linkedTestHeartbeatIntervalMs(){return readPositiveIntegerEnv("PM_LINKED_TEST_HEARTBEAT_INTERVAL_MS",DEFAULT_LINKED_TEST_HEARTBEAT_INTERVAL_MS)}function linkedTestPipeCloseGraceMs(){return readPositiveIntegerEnv("PM_LINKED_TEST_PIPE_CLOSE_GRACE_MS",DEFAULT_LINKED_TEST_PIPE_CLOSE_GRACE_MS)}function resolveTrackedRunId(kind){const fromEnv=process.env.PM_BACKGROUND_TEST_RUN_ID?.trim();return fromEnv&&fromEnv.length>0?fromEnv:`${kind}-local-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,8)}`}function summarizeRunResultStatuses(results){let passed=0,failed=0,skipped=0;for(const entry of results){if(entry.status==="passed"){passed+=1;continue}if(entry.status==="failed"){failed+=1;continue}skipped+=1}return{passed,failed,skipped}}function summarizeContextPreflight(runResults){let checkedPmCommands=0,trackerReadCommands=0,mismatches=0,autoRemediated=0;for(const result of runResults){const context=result.execution_context;!context||context.is_pm_command!==!0||(checkedPmCommands+=1,context.is_pm_tracker_read_command===!0&&(trackerReadCommands+=1),context.mismatch_detected===!0&&(mismatches+=1),context.auto_pm_context_applied===!0&&(autoRemediated+=1))}return{checked_pm_commands:checkedPmCommands,tracker_read_commands:trackerReadCommands,mismatches,auto_remediated:autoRemediated}}function ensureScope(raw){const value=raw??"project";if(!SCOPE_VALUES.includes(value))throw new PmCliError(`Invalid scope "${raw}"`,EXIT_CODE.USAGE);return value}function parsePmContextMode(raw){if(!raw)return"schema";const normalized=raw.trim().toLowerCase();if(PM_CONTEXT_MODE_VALUES.includes(normalized))return normalized;throw new PmCliError(`Invalid --pm-context value "${raw}". Expected one of: ${PM_CONTEXT_MODE_VALUES.join(", ")}`,EXIT_CODE.USAGE)}function parseWorkspaceContextMode(raw){return parseLinkedTestWorkspaceContextMode(raw??"source","--workspace-context")}function resolveLinkedTestWorkspaceContextMode(linkedTest,runLevelMode,overrideLinkedMode){return overrideLinkedMode?runLevelMode:parseLinkedTestWorkspaceContextMode(linkedTest.workspace_context_mode,"linked test")??runLevelMode}function resolveLinkedTestRequestedContextMode(linkedTest,runLevelMode,overrideLinkedPmContext){return overrideLinkedPmContext||typeof linkedTest.pm_context_mode!="string"||linkedTest.pm_context_mode.trim().length===0?runLevelMode:parsePmContextMode(linkedTest.pm_context_mode)}function resolveLinkedTestEffectiveContextMode(requestedMode,isPmTrackerReadCommand){return requestedMode==="auto"?isPmTrackerReadCommand?"tracker":"schema":requestedMode}function hasLinkedTestAssertions(linkedTest){return Object.entries(linkedTest).filter(([key])=>key.startsWith("assert_")).some(([,value])=>value==null?!1:Array.isArray(value)?value.length>0:typeof value=="object"?Object.keys(value).length>0:typeof value=="string"?value.trim().length>0:!0)}function buildPmContextMismatchHint(params){const{executionContext,runLevelPmContextMode,linkedOverridePmContextMode}=params;return!executionContext.is_pm_tracker_read_command||!executionContext.mismatch_detected?"":runLevelPmContextMode==="tracker"&&linkedOverridePmContextMode==="schema"?" Linked test metadata pm_context_mode=schema overrides run-level --pm-context tracker. Set pm_context_mode=tracker (or auto) on the linked test, or remove the override, to run against seeded tracker data.":executionContext.pm_context_mode==="schema"?" Use --auto-pm-context to route PM tracker-read linked commands through seeded tracker data automatically. Alternatively, use --pm-context tracker for the whole run.":""}function mergeEnvSetDirectives(entries,optionName){const merged={};if(!entries)return merged;for(const entry of entries){const parsed=parseLinkedTestEnvSetValue(entry,optionName);if(parsed)for(const[key,value]of Object.entries(parsed))merged[key]=value}return merged}function mergeEnvClearDirectives(entries,optionName){if(!entries)return[];const values=[];for(const entry of entries){const parsed=parseLinkedTestEnvClearValue(entry,optionName);parsed&&values.push(...parsed)}return[...new Set(values)]}import{BUN_GLOBAL_FLAGS_WITH_VALUE,NPM_GLOBAL_FLAGS_WITH_VALUE,PNPM_GLOBAL_FLAGS_WITH_VALUE,SCRIPT_RUN_FLAGS_WITH_VALUE,SCRIPT_RUN_SUBCOMMANDS,YARN_GLOBAL_FLAGS_WITH_VALUE,firstPmSubcommand,extractPmInvocationArgsFromSegment,isPmCliPackageToken,isPmCliScriptToken,isPmExecutableToken,parseLauncherSubcommand,parseNpmExecCommand,parseNpxCommand,parsePnpmDlxCommand,resolvePmSubcommandContext,splitNormalizedCommandSegments,stripLeadingEnvAssignments}from"./linked-command-detection.js";import{extractReferencedPmItemIdsFromCommand}from"./linked-command-detection.js";function commandInvokesPmCli(command){const normalizedCommand=normalizeCommandForValidation(command);return splitNormalizedCommandSegments(normalizedCommand).some(segment=>extractPmInvocationArgsFromSegment(segment)!==null)}function commandInvokesPmTrackerReadCommand(command){const normalizedCommand=normalizeCommandForValidation(command);return splitNormalizedCommandSegments(normalizedCommand).some(segment=>{const invocationArgs=extractPmInvocationArgsFromSegment(segment);if(!invocationArgs)return!1;const context=resolvePmSubcommandContext(invocationArgs);return context?PM_TRACKER_READ_SUBCOMMANDS.has(context.subcommand):!1})}function resolveDirectRunnerSubcommand(parsed){if(parsed)return SCRIPT_RUN_SUBCOMMANDS.has(parsed.subcommand)?parseLauncherSubcommand(parsed.args,SCRIPT_RUN_FLAGS_WITH_VALUE)?.subcommand:parsed.subcommand}function firstDirectTestRunnerSubcommand(executable,args){if(executable==="npx"||executable==="bunx")return parseNpxCommand(args)?.command;if(executable==="pnpm")return resolveDirectRunnerSubcommand(parseLauncherSubcommand(args,PNPM_GLOBAL_FLAGS_WITH_VALUE));if(executable==="npm")return resolveDirectRunnerSubcommand(parseLauncherSubcommand(args,NPM_GLOBAL_FLAGS_WITH_VALUE));if(executable==="yarn")return resolveDirectRunnerSubcommand(parseLauncherSubcommand(args,YARN_GLOBAL_FLAGS_WITH_VALUE));if(executable==="bun")return resolveDirectRunnerSubcommand(parseLauncherSubcommand(args,BUN_GLOBAL_FLAGS_WITH_VALUE))}function isDirectTestRunnerSubcommand(token){return token?token==="vitest"||token==="test"||token.startsWith("test:"):!1}function parsedLauncherInvokesRecursiveTestAll(parsed){return!parsed||!isPmExecutableToken(parsed.command)&&!isPmCliPackageToken(parsed.command)?!1:firstPmSubcommand(parsed.args)==="test-all"}const SHELL_COMMAND_FLAG_PATTERNS=new Map([...["sh","bash","dash","zsh"].map(executable=>[executable,/^(?:-c|-[a-z]*c[a-z]*)$/u]),...["cmd","cmd.exe"].map(executable=>[executable,/^\/[ck]$/u]),...["powershell","powershell.exe","pwsh","pwsh.exe"].map(executable=>[executable,/^-(?:c|command)$/u])]),PACKAGE_EXEC_EXECUTABLES=new Set(["npx","bunx"]);function segmentInvokesRecursiveTestAll(segment){const rawTokens=segment.split(" ").filter(token=>token.length>0),tokens=stripLeadingEnvAssignments(rawTokens);if(tokens.length===0)return!1;const[executable,...args]=tokens,shellCommandFlagPattern=SHELL_COMMAND_FLAG_PATTERNS.get(executable);if(shellCommandFlagPattern){const commandFlagIndex=args.findIndex(arg=>shellCommandFlagPattern.test(arg));return commandFlagIndex>=0?segmentInvokesRecursiveTestAll(args.slice(commandFlagIndex+1).join(" ")):!1}return isPmExecutableToken(executable)||isPmCliScriptToken(executable)?firstPmSubcommand(args)==="test-all":executable==="node"&&args.length>0&&isPmCliScriptToken(args[0])?firstPmSubcommand(args.slice(1))==="test-all":PACKAGE_EXEC_EXECUTABLES.has(executable)?parsedLauncherInvokesRecursiveTestAll(parseNpxCommand(args)):executable==="pnpm"?parsedLauncherInvokesRecursiveTestAll(parsePnpmDlxCommand(args)):executable==="npm"?parsedLauncherInvokesRecursiveTestAll(parseNpmExecCommand(args)):!1}function invokesRecursiveTestAllCommand(command){const normalized=normalizeCommandForValidation(command);return splitNormalizedCommandSegments(normalized).some(segment=>segmentInvokesRecursiveTestAll(segment))}function assertNoRecursiveTestAllCommand(command){if(invokesRecursiveTestAllCommand(command))throw new PmCliError('Linked test commands must not invoke "pm test-all"; this creates recursive orchestration.',EXIT_CODE.USAGE)}function normalizeCommandForValidation(command){return command.trim().replaceAll("\\","/").replaceAll('"',"").replaceAll("'","").replaceAll(/\s+/g," ").toLowerCase()}function commandUsesSandboxRunner(normalizedCommand){return normalizedCommand.includes("node scripts/run-tests.mjs ")||normalizedCommand.endsWith("node scripts/run-tests.mjs")||normalizedCommand.includes("node ./scripts/run-tests.mjs ")||normalizedCommand.endsWith("node ./scripts/run-tests.mjs")}function segmentInvokesDirectTestRunner(normalizedSegment){const rawTokens=normalizedSegment.split(" ").filter(token=>token.length>0),tokens=stripLeadingEnvAssignments(rawTokens);if(tokens.length===0)return!1;const[executable,...args]=tokens,shellCommandFlagPattern=SHELL_COMMAND_FLAG_PATTERNS.get(executable);if(shellCommandFlagPattern){const commandFlagIndex=args.findIndex(argument=>shellCommandFlagPattern.test(argument));return commandFlagIndex>=0&&segmentInvokesDirectTestRunner(args.slice(commandFlagIndex+1).join(" "))}return executable==="vitest"||executable.endsWith("/vitest")||executable.endsWith("/vitest.mjs")?!0:executable==="node"?nodeArgsInvokeDirectTestRunner(args):packageManagerInvokesDirectTestRunner(executable,args)}function nodeArgsInvokeDirectTestRunner(args){return args.includes("--test")||args.some(arg=>arg==="vitest"||arg==="vitest.mjs"||arg.endsWith("/vitest")||arg.endsWith("/vitest.mjs"))}function packageManagerInvokesDirectTestRunner(executable,args){return executable==="npx"||executable==="bunx"?isDirectTestRunnerSubcommand(parseNpxCommand(args)?.command):executable==="pnpm"||executable==="npm"?isDirectTestRunnerSubcommand((executable==="pnpm"?parsePnpmDlxCommand(args):parseNpmExecCommand(args))?.command)||isDirectTestRunnerSubcommand(firstDirectTestRunnerSubcommand(executable,args)):(executable==="yarn"||executable==="bun")&&isDirectTestRunnerSubcommand(firstDirectTestRunnerSubcommand(executable,args))}function classifyLinkedTestCommandSafety(command){const normalized=normalizeCommandForValidation(command),segments=splitNormalizedCommandSegments(normalized),directRunner=segments.some(segment=>!commandUsesSandboxRunner(segment)&&segmentInvokesDirectTestRunner(segment)),sandboxRunner=segments.some(commandUsesSandboxRunner);return{accepted:!0,command_kind:directRunner?"direct_runner":sandboxRunner?"sandbox_runner":"other",tracker_isolation:"runtime_injected",trust_boundary:"provenance",advisory:directRunner?"Accepted with runtime-injected tracker isolation. Prefer the repository sandbox runner when available for reproducible build and coverage setup.":"Accepted with runtime-injected tracker isolation and provenance checks before execution."}}function getRuntimeSafetySkipReason(command){if(invokesRecursiveTestAllCommand(command))return'Linked test command skipped: Linked test commands must not invoke "pm test-all"; this creates recursive orchestration.'}function parseAddEntries(raw){return raw?raw.map(parseAddEntry):[]}function parseAddEntry(entry){const trimmed=entry.trim(),kv=looksLikeStructuredLinkedTestEntry(trimmed)?normalizeStructuredLinkedTestEntry(parseCsvKv(entry,"--add"),"--add"):{command:trimmed},command=trimLinkedTestEntryField(kv.command);if(!command)throw new PmCliError("--add requires command=<value> or a bare command (path=<value> is optional metadata)",EXIT_CODE.USAGE);return assertNoRecursiveTestAllCommand(command),{command,path:trimLinkedTestEntryField(kv.path),scope:ensureScope(kv.scope),timeout_seconds:parseLinkedTestTimeoutSeconds(trimLinkedTestEntryField(kv.timeout_seconds),trimLinkedTestEntryField(kv.timeout)),pm_context_mode:parseLinkedTestContextModeValue(trimLinkedTestEntryField(kv.pm_context_mode),"--add"),workspace_context_mode:parseLinkedTestWorkspaceContextMode(trimLinkedTestEntryField(kv.workspace_context_mode),"--add"),env_set:parseLinkedTestEnvSetValue(trimLinkedTestEntryField(kv.env_set),"--add"),env_clear:parseLinkedTestEnvClearValue(trimLinkedTestEntryField(kv.env_clear),"--add"),shared_host_safe:parseLinkedTestBooleanValue(trimLinkedTestEntryField(kv.shared_host_safe),"--add","shared_host_safe"),assert_stdout_contains:parseLinkedTestStringList(trimLinkedTestEntryField(kv.assert_stdout_contains)),assert_stdout_regex:parseLinkedTestRegexList(trimLinkedTestEntryField(kv.assert_stdout_regex),"--add","assert_stdout_regex"),assert_stderr_contains:parseLinkedTestStringList(trimLinkedTestEntryField(kv.assert_stderr_contains)),assert_stderr_regex:parseLinkedTestRegexList(trimLinkedTestEntryField(kv.assert_stderr_regex),"--add","assert_stderr_regex"),assert_stdout_min_lines:parseLinkedTestMinLines(trimLinkedTestEntryField(kv.assert_stdout_min_lines),"--add"),assert_json_field_equals:parseLinkedTestAssertionEqualsMap(trimLinkedTestEntryField(kv.assert_json_field_equals),"--add"),assert_json_field_gte:parseLinkedTestAssertionGteMap(trimLinkedTestEntryField(kv.assert_json_field_gte),"--add"),note:trimLinkedTestEntryField(kv.note)}}function trimLinkedTestEntryField(value){const trimmed=value?.trim();return trimmed&&trimmed.length>0?trimmed:void 0}function parseLinkedTestTimeoutSeconds(timeoutSecondsRaw,timeoutAliasRaw){if(timeoutSecondsRaw&&timeoutAliasRaw&&timeoutSecondsRaw!==timeoutAliasRaw)throw new PmCliError("--add timeout and timeout_seconds must match when both are provided",EXIT_CODE.USAGE);const timeoutRaw=timeoutSecondsRaw??timeoutAliasRaw;if(timeoutRaw===void 0)return;const timeout=Math.floor(parseOptionalNumber(timeoutRaw,"timeout_seconds"));if(timeout<=0)throw new PmCliError("--add timeout_seconds must be a positive integer",EXIT_CODE.USAGE);return timeout}function parseAddJsonEntries(raw){return raw?raw.flatMap(entry=>{const parsed=parseLinkedTestJsonEntries(entry,"--add-json");for(const linkedTest of parsed){const command=linkedTest.command;if(!command)throw new PmCliError("--add-json requires a non-empty command string",EXIT_CODE.USAGE);assertNoRecursiveTestAllCommand(command)}return parsed}):[]}function parseRemoveEntries(raw){return raw?raw.map(entry=>{const trimmed=entry.trim();if(!trimmed)throw new PmCliError("--remove requires command or path value",EXIT_CODE.USAGE);const identityPrefix=/^(command|path)\s*=/i.exec(trimmed);if(identityPrefix){const value=trimmed.slice(identityPrefix[0].length).trim();if(!value)throw new PmCliError("--remove requires a non-empty command=<value> or path=<value>",EXIT_CODE.USAGE);return{kind:identityPrefix[1].toLowerCase(),value}}if(/^[A-Za-z_][\w-]*=\S+$/u.test(trimmed))throw new PmCliError("--remove structured selectors require command=<value> or path=<value>",EXIT_CODE.USAGE);if(/^(?:[-*+]\s+)?(?:path|command)\s*:/i.test(trimmed)||trimmed.startsWith("```")){const kv=parseCsvKv(trimmed,"--remove"),kind=kv.path!==void 0?"path":"command",value=kv.path??kv.command;if(!value?.trim())throw new PmCliError("--remove requires command=<value> and/or path=<value>",EXIT_CODE.USAGE);return{kind,value:value.trim()}}return{kind:"either",value:trimmed}}):[]}function parseRemoveIndexes(raw){return raw?[...new Set(raw.map(value=>{const parsed=typeof value=="number"?value:Number.parseInt(value,10);if(!Number.isInteger(parsed)||parsed<1||String(parsed)!==String(value).trim())throw new PmCliError("--remove-index must be a positive integer",EXIT_CODE.USAGE);return parsed}))]:[]}function closeLinkedTestStdin(child){try{child.stdin?.end()}catch{}}function summarizeLinkedTestCommand(command){const normalized=command.trim().replaceAll(/\s+/g," ");return normalized.length<=MAX_LINKED_TEST_COMMAND_LABEL_LENGTH?normalized:`${normalized.slice(0,MAX_LINKED_TEST_COMMAND_LABEL_LENGTH-3)}...`}function shouldEmitLinkedTestProgress(mode){return mode==="off"?!1:mode==="always"?!0:process.stderr.isTTY===!0}function emitLinkedTestProgress(message){try{process.stderr.write(`${message}
2
2
  `)}catch{}}function beginLinkedTestProgress(context,mode){if(!shouldEmitLinkedTestProgress(mode))return null;const commandLabel=summarizeLinkedTestCommand(context.command),startAt=Date.now();emitLinkedTestProgress(`[pm test] linked-test ${context.index}/${context.total} start timeout_ms=${context.timeoutMs} command="${commandLabel}"`);const heartbeat=setInterval(()=>{const elapsedMs=Date.now()-startAt;emitLinkedTestProgress(`[pm test] linked-test ${context.index}/${context.total} running elapsed_ms=${elapsedMs} command="${commandLabel}"`)},linkedTestHeartbeatIntervalMs());return heartbeat.unref?.(),heartbeat}function endLinkedTestProgress(context,executionResult,startedAt,mode){if(!shouldEmitLinkedTestProgress(mode))return;const commandLabel=summarizeLinkedTestCommand(context.command),elapsedMs=Date.now()-startedAt,statusLabel=executionResult.timedOut||executionResult.maxBufferExceeded||executionResult.exitCode!==0?"failed":"passed",reasonTokens=[];executionResult.timedOut&&reasonTokens.push("reason=timeout"),executionResult.maxBufferExceeded&&reasonTokens.push("reason=max_buffer"),executionResult.outputTruncated&&reasonTokens.push("output=truncated"),executionResult.signal&&reasonTokens.push(`signal=${executionResult.signal}`);const exitLabel=executionResult.exitCode===null?"null":String(executionResult.exitCode),reasonSuffix=reasonTokens.length>0?` ${reasonTokens.join(" ")}`:"";emitLinkedTestProgress(`[pm test] linked-test ${context.index}/${context.total} end status=${statusLabel} exit_code=${exitLabel} elapsed_ms=${elapsedMs}${reasonSuffix} command="${commandLabel}"`)}async function killProcessTree(pid){if(!(!Number.isInteger(pid)||pid<=0)){if(process.platform==="win32"){await new Promise(resolve=>{const killer=spawn("taskkill",["/pid",String(pid),"/T","/F"],{stdio:"ignore",windowsHide:!0});killer.on("error",()=>resolve()),killer.on("close",()=>resolve())});return}try{process.kill(-pid,"SIGKILL");return}catch{}try{process.kill(pid,"SIGKILL")}catch{}}}function clearLinkedTestTimers(timers){timers.heartbeat&&clearInterval(timers.heartbeat),timers.timedOutTimer&&(clearTimeout(timers.timedOutTimer),timers.timedOutTimer=null),timers.forceKillTimer&&(clearTimeout(timers.forceKillTimer),timers.forceKillTimer=null)}function createLinkedTestTerminationRequester(child,timers){let terminationRequested=!1;return async()=>{if(terminationRequested)return;terminationRequested=!0;const pid=child.pid;if(!pid||pid<=0){try{child.kill("SIGTERM")}catch{}return}if(process.platform==="win32"){await killProcessTree(pid);return}try{process.kill(-pid,"SIGTERM")}catch{try{child.kill("SIGTERM")}catch{}}timers.forceKillTimer=setTimeout(()=>{killProcessTree(pid)},linkedTestTimeoutForceKillDelayMs()),timers.forceKillTimer.unref?.()}}function waitForLinkedTestChildClose(child){return new Promise(resolve=>{let settled=!1,pipeCloseGraceTimer=null;const settle=(value,destroyPipes=!1)=>{settled||(settled=!0,pipeCloseGraceTimer&&(clearTimeout(pipeCloseGraceTimer),pipeCloseGraceTimer=null),destroyPipes&&(child.stdin?.destroy(),child.stdout?.destroy(),child.stderr?.destroy()),resolve(value))};child.on("exit",(exitCode,exitSignal)=>{settled||(pipeCloseGraceTimer=setTimeout(()=>{settle({code:exitCode,signal:exitSignal},!0)},linkedTestPipeCloseGraceMs()),pipeCloseGraceTimer.unref?.())}),child.on("close",(closeCode,closeSignal)=>{settle({code:closeCode,signal:closeSignal})})})}function createLinkedTestChild(command,env,stdoutFd,stderrFd,cwd){return spawn(command,{cwd,env,shell:!0,windowsHide:!0,detached:process.platform!=="win32",stdio:["pipe",stdoutFd,stderrFd]})}async function readLinkedTestCapture(capturePath){const handle=await open(capturePath,"r");try{const size=(await handle.stat()).size,retainedBytes=Math.min(size,TEST_OUTPUT_MAX_BUFFER_BYTES),buffer=Buffer.alloc(retainedBytes);return retainedBytes>0&&await handle.read(buffer,0,retainedBytes,0),{text:buffer.toString("utf8"),truncated:size>TEST_OUTPUT_MAX_BUFFER_BYTES}}finally{await handle.close()}}async function runLinkedTestCommand(command,timeoutMs,env,progressContext,progressMode,cwd=process.cwd()){const startedAt=Date.now(),captureRoot=await mkdtemp(path.join(tmpdir(),"pm-linked-test-output-"));try{const stdoutPath=path.join(captureRoot,"stdout.log"),stderrPath=path.join(captureRoot,"stderr.log"),[stdoutHandle,stderrHandle]=await Promise.all([open(stdoutPath,"w"),open(stderrPath,"w")]);let child;try{child=createLinkedTestChild(command,env,stdoutHandle.fd,stderrHandle.fd,cwd)}catch(error){throw await Promise.all([stdoutHandle.close(),stderrHandle.close()]),error}const childClose=waitForLinkedTestChildClose(child);let spawnError;child.on("error",error=>{spawnError=error.message}),await Promise.all([stdoutHandle.close(),stderrHandle.close()]),closeLinkedTestStdin(child);const timers={heartbeat:beginLinkedTestProgress(progressContext,progressMode),forceKillTimer:null,timedOutTimer:null},requestTermination=createLinkedTestTerminationRequester(child,timers);let timedOut=!1;timers.timedOutTimer=setTimeout(()=>{timedOut=!0,requestTermination()},timeoutMs),timers.timedOutTimer.unref?.();const{code,signal}=await childClose;clearLinkedTestTimers(timers);const[stdout,stderr]=await Promise.all([readLinkedTestCapture(stdoutPath),readLinkedTestCapture(stderrPath)]),outputTruncated=stdout.truncated||stderr.truncated,truncationNotice=outputTruncated?`
3
3
  [pm linked-test output truncated at ${TEST_OUTPUT_MAX_BUFFER_BYTES} bytes per stream; the child process continued to completion]
4
4
  `:"",executionResult={stdout:`${stdout.text}${truncationNotice}`,stderr:stderr.text,exitCode:code,signal,timedOut,maxBufferExceeded:!1,outputTruncated,spawnError};return endLinkedTestProgress(progressContext,executionResult,startedAt,progressMode),executionResult}finally{await rm(captureRoot,{recursive:!0,force:!0})}}function formatLinkedTestExecutionError(result,timeoutMs){const details=[];result.maxBufferExceeded&&details.push(`Linked test output exceeded maxBuffer=${TEST_OUTPUT_MAX_BUFFER_BYTES} bytes. Reduce output volume or split the command.`),result.timedOut&&timeoutMs>0&&details.push(`Linked test timed out after ${timeoutMs}ms.`);const signalMessage=result.signal?`Linked test command terminated by signal ${result.signal}.`:void 0,baseMessage=result.spawnError?.trim()||signalMessage||"Linked test command failed.";return details.length===0?baseMessage:`${baseMessage} ${details.join(" ")}`}function hasInfraCollisionSignal(result){const combined=[result.spawnError??"",result.stderr,result.stdout].join(`
5
- `);return LINKED_TEST_INFRA_COLLISION_PATTERNS.some(pattern=>pattern.test(combined))||combined.split(/\r?\n/u).some(line=>LINKED_TEST_LOCK_ACQUISITION_PATTERN.test(line)&&LINKED_TEST_LOCK_CONTENTION_CONTEXT_PATTERN.test(line))}function classifyLinkedTestFailure(result){return hasInfraCollisionSignal(result)?"infra_collision":result.timedOut?"timeout":result.maxBufferExceeded?"max_buffer":result.spawnError?"spawn_error":result.signal?"signal":"assertion_failure"}function createEmptyFailureCategoryCounts(){return{infra_collision:0,trust_refusal:0,assertion_failure:0,empty_run:0,timeout:0,max_buffer:0,spawn_error:0,signal:0}}function countFailureCategories(runResults){const counts=createEmptyFailureCategoryCounts();for(const result of runResults)result.status!=="failed"||!result.failure_category||(counts[result.failure_category]+=1);return counts}function applyEnvDirectiveStage(env,directives){for(const[key,value]of Object.entries(directives.env_set))LINKED_TEST_PROTECTED_ENV_KEYS.has(key.toUpperCase())||(env[key]=value);for(const key of directives.env_clear)LINKED_TEST_PROTECTED_ENV_KEYS.has(key.toUpperCase())||delete env[key]}function applySharedHostSafeDefaults(env){env.PORT===void 0&&(env.PORT="0"),env.HOST===void 0&&(env.HOST="127.0.0.1"),env.PM_SHARED_HOST_SAFE===void 0&&(env.PM_SHARED_HOST_SAFE="1"),env.PLAYWRIGHT_HTML_OPEN===void 0&&(env.PLAYWRIGHT_HTML_OPEN="never"),env.PW_TEST_HTML_REPORT_OPEN===void 0&&(env.PW_TEST_HTML_REPORT_OPEN="never")}function resolveEffectiveLinkedTestDirectives(runtimeDirectives,linkedTest){const envSet={...runtimeDirectives.env_set,...linkedTest.env_set},envClear=[...new Set([...runtimeDirectives.env_clear,...linkedTest.env_clear??[]])],sharedHostSafe=linkedTest.shared_host_safe??runtimeDirectives.shared_host_safe;return{env_set:envSet,env_clear:envClear,shared_host_safe:sharedHostSafe}}function resolveRuntimeDirectives(envSetEntries,envClearEntries,sharedHostSafe){return{env_set:mergeEnvSetDirectives(envSetEntries,"--env-set"),env_clear:mergeEnvClearDirectives(envClearEntries,"--env-clear"),shared_host_safe:sharedHostSafe===!0}}async function copyIntoSandboxIfPresent(sourcePath,targetPath,recursive=!1,makeDirectory=mkdir){if(await pathExists(sourcePath))try{await withHostEnvironmentBoundary("seed_linked_test",async()=>{if(await makeDirectory(path.dirname(targetPath),{recursive:!0}),recursive){await cp(sourcePath,targetPath,{recursive:!0,force:!0});return}await cp(sourcePath,targetPath,{force:!0})},{why:"Linked tests need an isolated PM context without mutating the source tracker.",nextSteps:["Reclaim capacity on the filesystem that hosts the linked-test temporary directory."]})}catch(error){if(typeof error=="object"&&error!==null&&"code"in error&&isFileMissingError(error))return;throw error}}async function seedLinkedTestSandbox(sandboxPmPath,sandboxGlobalPath,sourceRoots){await copyIntoSandboxIfPresent(getSettingsPath(sourceRoots.projectPmRoot),getSettingsPath(sandboxPmPath)),await copyIntoSandboxIfPresent(path.join(sourceRoots.projectPmRoot,"extensions"),path.join(sandboxPmPath,"extensions"),!0),await copyIntoSandboxIfPresent(getSettingsPath(sourceRoots.globalPmRoot),getSettingsPath(sandboxGlobalPath)),await copyIntoSandboxIfPresent(path.join(sourceRoots.globalPmRoot,"extensions"),path.join(sandboxGlobalPath,"extensions"),!0)}async function seedLinkedTestTrackerData(sourceRoot,sandboxRoot){if(!await pathExists(sourceRoot))return;const entries=await readdir(sourceRoot,{withFileTypes:!0});for(const entry of entries){if(LINKED_TEST_TRACKER_DIRS_TO_SKIP.has(entry.name))continue;const sourcePath=path.join(sourceRoot,entry.name),targetPath=path.join(sandboxRoot,entry.name);if(entry.isDirectory()){await copyIntoSandboxIfPresent(sourcePath,targetPath,!0);continue}entry.isFile()&&await copyIntoSandboxIfPresent(sourcePath,targetPath)}}async function seedMissingLinkedTestSettingsHistory(sourceRoot,sandboxRoot){const sourceSettingsRaw=await readFileIfExists(getSettingsPath(sourceRoot));if(sourceSettingsRaw===null||await pathExists(getWorkspaceHistoryPath(sourceRoot)))return;const sandboxSettings=await readSettings(sandboxRoot);await rm(getWorkspaceHistoryPath(sandboxRoot),{force:!0}),await rm(getSettingsPath(sandboxRoot),{force:!0}),await writeWorkspaceJsonWithHistory({pmRoot:sandboxRoot,filePath:getSettingsPath(sandboxRoot),raw:sourceSettingsRaw,op:"settings:write",author:resolveAuthor(void 0,sandboxSettings.author_default),lockTtlSeconds:sandboxSettings.locks.ttl_seconds,lockWaitMs:sandboxSettings.locks.wait_ms,recordCreation:!0,message:"Seed linked-test tracker settings history"})}async function countLinkedTestItemFiles(pmRoot){if(!await pathExists(pmRoot))return 0;let total=0;const entries=await readdir(pmRoot,{withFileTypes:!0});for(const entry of entries){if(!entry.isDirectory()||LINKED_TEST_ITEM_COUNT_DIRS_TO_SKIP.has(entry.name))continue;const folderPath=path.join(pmRoot,entry.name);let files;try{files=await readdir(folderPath,{withFileTypes:!0})}catch{continue}for(const file of files)file.isFile()&&ITEM_FILE_EXTENSIONS.some(extension=>file.name.toLowerCase().endsWith(extension))&&(total+=1)}return total}function resolveLinkedTestFailureExitCode(execution){const rawExitCode=typeof execution.exitCode=="number"?execution.exitCode:1;return(execution.timedOut||execution.maxBufferExceeded)&&rawExitCode===0?1:rawExitCode}function parseAssertionLiteral(raw){const trimmed=raw.trim();if(trimmed==="true")return!0;if(trimmed==="false")return!1;if(trimmed==="null")return null;const numeric=Number(trimmed);if(trimmed.length>0&&Number.isFinite(numeric))return numeric;if(trimmed.startsWith("{")&&trimmed.endsWith("}")||trimmed.startsWith("[")&&trimmed.endsWith("]"))try{return JSON.parse(trimmed)}catch{}return trimmed}function compareAssertionValues(actual,expected){return typeof actual=="object"&&actual!==null&&typeof expected=="object"&&expected!==null?stableValueEquals(actual,expected):Object.is(actual,expected)}function evaluateLinkedTestAssertions(linkedTest,stdout,stderr){const failures=[];failures.push(...evaluateContainsAssertions("stdout",stdout,linkedTest.assert_stdout_contains)),failures.push(...evaluateRegexAssertions("stdout",stdout,linkedTest.assert_stdout_regex)),failures.push(...evaluateContainsAssertions("stderr",stderr,linkedTest.assert_stderr_contains)),failures.push(...evaluateRegexAssertions("stderr",stderr,linkedTest.assert_stderr_regex)),failures.push(...evaluateStdoutLineCountAssertion(stdout,linkedTest.assert_stdout_min_lines));const jsonEqualsAssertions=linkedTest.assert_json_field_equals??{},jsonGteAssertions=linkedTest.assert_json_field_gte??{};if(!(Object.keys(jsonEqualsAssertions).length>0||Object.keys(jsonGteAssertions).length>0))return failures;let parsedJson;try{parsedJson=JSON.parse(stdout)}catch(error){return failures.push(`stdout is not valid JSON for assert_json_field_* checks: ${error instanceof Error?error.message:String(error)}`),failures}for(const[fieldPath,expectedRaw]of Object.entries(jsonEqualsAssertions)){const resolved=readJsonPathValue(parsedJson,fieldPath);if(!resolved.found){failures.push(`assert_json_field_equals missing path "${fieldPath}"`);continue}const expected=parseAssertionLiteral(expectedRaw);compareAssertionValues(resolved.value,expected)||failures.push(`assert_json_field_equals mismatch at "${fieldPath}" (expected=${JSON.stringify(expected)} actual=${JSON.stringify(resolved.value)})`)}for(const[fieldPath,expectedMinimum]of Object.entries(jsonGteAssertions)){const resolved=readJsonPathValue(parsedJson,fieldPath);if(!resolved.found){failures.push(`assert_json_field_gte missing path "${fieldPath}"`);continue}if(typeof resolved.value!="number"||!Number.isFinite(resolved.value)){failures.push(`assert_json_field_gte path "${fieldPath}" resolved to non-numeric value`);continue}resolved.value<expectedMinimum&&failures.push(`assert_json_field_gte failed at "${fieldPath}" (expected >= ${expectedMinimum}, actual ${resolved.value})`)}return failures}function evaluateContainsAssertions(streamName,output,expectedValues){const failures=[];for(const expected of expectedValues??[])output.includes(expected)||failures.push(`${streamName} missing required text: "${expected}"`);return failures}function evaluateRegexAssertions(streamName,output,patterns){const failures=[];for(const pattern of patterns??[])try{new RegExp(pattern,"m").test(output)||failures.push(`${streamName} failed regex assertion: /${pattern}/m`)}catch(error){failures.push(`${streamName} regex assertion is invalid: /${pattern}/ (${error instanceof Error?error.message:String(error)})`)}return failures}function evaluateStdoutLineCountAssertion(stdout,minimum){if(typeof minimum!="number")return[];const lineCount=stdout.split(/\r?\n/).map(line=>line.trim()).filter(line=>line.length>0).length;return lineCount<minimum?[`stdout line count ${lineCount} is below required minimum ${minimum}`]:[]}const EMPTY_LINKED_TEST_RUN_PATTERNS=[{code:"no_projects_matched_filters",regex:/\bNo projects matched the filters\b/i},{code:"no_test_files_found",regex:/\bNo test files found\b/i},{code:"no_tests_found",regex:/\bNo tests found\b/i},{code:"no_matching_tests",regex:/\bNo matching tests?\b/i},{code:"collected_zero_items",regex:/\bcollected 0 items?\b/i},{code:"reported_zero_passes",regex:/(?:^\s*(?:#|ℹ)?\s*tests\s+0\s*$[\s\S]*^\s*(?:#|ℹ)?\s*pass\s+0\s*$|^\s*(?:#|ℹ)?\s*pass\s+0\s*$[\s\S]*^\s*(?:#|ℹ)?\s*tests\s+0\s*$)/imu},{code:"reported_zero_tests",regex:/^\s*(?:#|ℹ)?\s*tests\s+0\s*$/imu}],POSITIVE_LINKED_TEST_RUN_PATTERNS=[/^\s*(?:#|ℹ)?\s*tests\s+[1-9]\d*\s*$/imu,/^\s*(?:#|ℹ)?\s*pass\s+[1-9]\d*\s*$/imu,/\bTests?\s+[1-9]\d*\s+passed\b/iu,/\b[1-9]\d*\s+passed\b/iu,/\bTests:\s+(?:.*\b)?[1-9]\d*\s+passed\b/iu];function detectEmptyLinkedTestRun(stdout,stderr){const combined=`${stdout}
5
+ `);return LINKED_TEST_INFRA_COLLISION_PATTERNS.some(pattern=>pattern.test(combined))||combined.split(/\r?\n/u).some(line=>LINKED_TEST_LOCK_ACQUISITION_PATTERN.test(line)&&LINKED_TEST_LOCK_CONTENTION_CONTEXT_PATTERN.test(line))}function classifyLinkedTestFailure(result){return hasInfraCollisionSignal(result)?"infra_collision":result.timedOut?"timeout":result.maxBufferExceeded?"max_buffer":result.spawnError?"spawn_error":result.signal?"signal":"assertion_failure"}function createEmptyFailureCategoryCounts(){return{infra_collision:0,trust_refusal:0,assertion_failure:0,empty_run:0,timeout:0,max_buffer:0,spawn_error:0,signal:0}}function countFailureCategories(runResults){const counts=createEmptyFailureCategoryCounts();for(const result of runResults)result.status!=="failed"||!result.failure_category||(counts[result.failure_category]+=1);return counts}function applyEnvDirectiveStage(env,directives){for(const[key,value]of Object.entries(directives.env_set))LINKED_TEST_PROTECTED_ENV_KEYS.has(key.toUpperCase())||(env[key]=value);for(const key of directives.env_clear)LINKED_TEST_PROTECTED_ENV_KEYS.has(key.toUpperCase())||delete env[key]}function applySharedHostSafeDefaults(env){env.PORT===void 0&&(env.PORT="0"),env.HOST===void 0&&(env.HOST="127.0.0.1"),env.PM_SHARED_HOST_SAFE===void 0&&(env.PM_SHARED_HOST_SAFE="1"),env.PLAYWRIGHT_HTML_OPEN===void 0&&(env.PLAYWRIGHT_HTML_OPEN="never"),env.PW_TEST_HTML_REPORT_OPEN===void 0&&(env.PW_TEST_HTML_REPORT_OPEN="never")}function resolveEffectiveLinkedTestDirectives(runtimeDirectives,linkedTest){const envSet={...runtimeDirectives.env_set,...linkedTest.env_set},envClear=[...new Set([...runtimeDirectives.env_clear,...linkedTest.env_clear??[]])],sharedHostSafe=linkedTest.shared_host_safe??runtimeDirectives.shared_host_safe;return{env_set:envSet,env_clear:envClear,shared_host_safe:sharedHostSafe}}function resolveRuntimeDirectives(envSetEntries,envClearEntries,sharedHostSafe){return{env_set:mergeEnvSetDirectives(envSetEntries,"--env-set"),env_clear:mergeEnvClearDirectives(envClearEntries,"--env-clear"),shared_host_safe:sharedHostSafe===!0}}async function copyIntoSandboxIfPresent(sourcePath,targetPath,recursive=!1,makeDirectory=mkdir){if(await pathExists(sourcePath))try{await withHostEnvironmentBoundary("seed_linked_test",async()=>{if(await makeDirectory(path.dirname(targetPath),{recursive:!0}),recursive){await cp(sourcePath,targetPath,{recursive:!0,force:!0});return}await cp(sourcePath,targetPath,{force:!0})},{why:"Linked tests need an isolated PM context without mutating the source tracker.",nextSteps:["Reclaim capacity on the filesystem that hosts the linked-test temporary directory."]})}catch(error){if(typeof error=="object"&&error!==null&&"code"in error&&isFileMissingError(error))return;throw error}}async function seedLinkedTestSandbox(sandboxPmPath,sandboxGlobalPath,sourceRoots,auditSettings=!1){for(const[sourceRoot,sandboxRoot]of[[sourceRoots.projectPmRoot,sandboxPmPath],[sourceRoots.globalPmRoot,sandboxGlobalPath]])if(auditSettings){const raw=await readFileIfExists(getSettingsPath(sourceRoot));if(raw!==null){const settings=await readSettings(sandboxRoot);await writeWorkspaceJsonWithHistory({pmRoot:sandboxRoot,filePath:getSettingsPath(sandboxRoot),raw,op:"settings:write",author:resolveAuthor(void 0,settings.author_default),lockTtlSeconds:settings.locks.ttl_seconds,lockWaitMs:settings.locks.wait_ms,recordCreation:!0,message:"Seed linked-test schema settings history"})}}else await copyIntoSandboxIfPresent(getSettingsPath(sourceRoot),getSettingsPath(sandboxRoot));await copyIntoSandboxIfPresent(path.join(sourceRoots.projectPmRoot,"extensions"),path.join(sandboxPmPath,"extensions"),!0),await copyIntoSandboxIfPresent(path.join(sourceRoots.globalPmRoot,"extensions"),path.join(sandboxGlobalPath,"extensions"),!0)}async function seedLinkedTestTrackerData(sourceRoot,sandboxRoot){if(!await pathExists(sourceRoot))return;const entries=await readdir(sourceRoot,{withFileTypes:!0});for(const entry of entries){if(LINKED_TEST_TRACKER_DIRS_TO_SKIP.has(entry.name))continue;const sourcePath=path.join(sourceRoot,entry.name),targetPath=path.join(sandboxRoot,entry.name);if(entry.isDirectory()){await copyIntoSandboxIfPresent(sourcePath,targetPath,!0);continue}entry.isFile()&&await copyIntoSandboxIfPresent(sourcePath,targetPath)}}async function seedMissingLinkedTestSettingsHistory(sourceRoot,sandboxRoot){const sourceSettingsRaw=await readFileIfExists(getSettingsPath(sourceRoot));if(sourceSettingsRaw===null||await pathExists(getWorkspaceHistoryPath(sourceRoot)))return;const sandboxSettings=await readSettings(sandboxRoot);await rm(getWorkspaceHistoryPath(sandboxRoot),{force:!0}),await rm(getSettingsPath(sandboxRoot),{force:!0}),await writeWorkspaceJsonWithHistory({pmRoot:sandboxRoot,filePath:getSettingsPath(sandboxRoot),raw:sourceSettingsRaw,op:"settings:write",author:resolveAuthor(void 0,sandboxSettings.author_default),lockTtlSeconds:sandboxSettings.locks.ttl_seconds,lockWaitMs:sandboxSettings.locks.wait_ms,recordCreation:!0,message:"Seed linked-test tracker settings history"})}async function countLinkedTestItemFiles(pmRoot){if(!await pathExists(pmRoot))return 0;let total=0;const entries=await readdir(pmRoot,{withFileTypes:!0});for(const entry of entries){if(!entry.isDirectory()||LINKED_TEST_ITEM_COUNT_DIRS_TO_SKIP.has(entry.name))continue;const folderPath=path.join(pmRoot,entry.name);let files;try{files=await readdir(folderPath,{withFileTypes:!0})}catch{continue}for(const file of files)file.isFile()&&ITEM_FILE_EXTENSIONS.some(extension=>file.name.toLowerCase().endsWith(extension))&&(total+=1)}return total}function resolveLinkedTestFailureExitCode(execution){const rawExitCode=typeof execution.exitCode=="number"?execution.exitCode:1;return(execution.timedOut||execution.maxBufferExceeded)&&rawExitCode===0?1:rawExitCode}function parseAssertionLiteral(raw){const trimmed=raw.trim();if(trimmed==="true")return!0;if(trimmed==="false")return!1;if(trimmed==="null")return null;const numeric=Number(trimmed);if(trimmed.length>0&&Number.isFinite(numeric))return numeric;if(trimmed.startsWith("{")&&trimmed.endsWith("}")||trimmed.startsWith("[")&&trimmed.endsWith("]"))try{return JSON.parse(trimmed)}catch{}return trimmed}function compareAssertionValues(actual,expected){return typeof actual=="object"&&actual!==null&&typeof expected=="object"&&expected!==null?stableValueEquals(actual,expected):Object.is(actual,expected)}function evaluateLinkedTestAssertions(linkedTest,stdout,stderr){const failures=[];failures.push(...evaluateContainsAssertions("stdout",stdout,linkedTest.assert_stdout_contains)),failures.push(...evaluateRegexAssertions("stdout",stdout,linkedTest.assert_stdout_regex)),failures.push(...evaluateContainsAssertions("stderr",stderr,linkedTest.assert_stderr_contains)),failures.push(...evaluateRegexAssertions("stderr",stderr,linkedTest.assert_stderr_regex)),failures.push(...evaluateStdoutLineCountAssertion(stdout,linkedTest.assert_stdout_min_lines));const jsonEqualsAssertions=linkedTest.assert_json_field_equals??{},jsonGteAssertions=linkedTest.assert_json_field_gte??{};if(!(Object.keys(jsonEqualsAssertions).length>0||Object.keys(jsonGteAssertions).length>0))return failures;let parsedJson;try{parsedJson=JSON.parse(stdout)}catch(error){return failures.push(`stdout is not valid JSON for assert_json_field_* checks: ${error instanceof Error?error.message:String(error)}`),failures}for(const[fieldPath,expectedRaw]of Object.entries(jsonEqualsAssertions)){const resolved=readJsonPathValue(parsedJson,fieldPath);if(!resolved.found){failures.push(`assert_json_field_equals missing path "${fieldPath}"`);continue}const expected=parseAssertionLiteral(expectedRaw);compareAssertionValues(resolved.value,expected)||failures.push(`assert_json_field_equals mismatch at "${fieldPath}" (expected=${JSON.stringify(expected)} actual=${JSON.stringify(resolved.value)})`)}for(const[fieldPath,expectedMinimum]of Object.entries(jsonGteAssertions)){const resolved=readJsonPathValue(parsedJson,fieldPath);if(!resolved.found){failures.push(`assert_json_field_gte missing path "${fieldPath}"`);continue}if(typeof resolved.value!="number"||!Number.isFinite(resolved.value)){failures.push(`assert_json_field_gte path "${fieldPath}" resolved to non-numeric value`);continue}resolved.value<expectedMinimum&&failures.push(`assert_json_field_gte failed at "${fieldPath}" (expected >= ${expectedMinimum}, actual ${resolved.value})`)}return failures}function evaluateContainsAssertions(streamName,output,expectedValues){const failures=[];for(const expected of expectedValues??[])output.includes(expected)||failures.push(`${streamName} missing required text: "${expected}"`);return failures}function evaluateRegexAssertions(streamName,output,patterns){const failures=[];for(const pattern of patterns??[])try{new RegExp(pattern,"m").test(output)||failures.push(`${streamName} failed regex assertion: /${pattern}/m`)}catch(error){failures.push(`${streamName} regex assertion is invalid: /${pattern}/ (${error instanceof Error?error.message:String(error)})`)}return failures}function evaluateStdoutLineCountAssertion(stdout,minimum){if(typeof minimum!="number")return[];const lineCount=stdout.split(/\r?\n/).map(line=>line.trim()).filter(line=>line.length>0).length;return lineCount<minimum?[`stdout line count ${lineCount} is below required minimum ${minimum}`]:[]}const EMPTY_LINKED_TEST_RUN_PATTERNS=[{code:"no_projects_matched_filters",regex:/\bNo projects matched the filters\b/i},{code:"no_test_files_found",regex:/\bNo test files found\b/i},{code:"no_tests_found",regex:/\bNo tests found\b/i},{code:"no_matching_tests",regex:/\bNo matching tests?\b/i},{code:"collected_zero_items",regex:/\bcollected 0 items?\b/i},{code:"reported_zero_passes",regex:/(?:^\s*(?:#|ℹ)?\s*tests\s+0\s*$[\s\S]*^\s*(?:#|ℹ)?\s*pass\s+0\s*$|^\s*(?:#|ℹ)?\s*pass\s+0\s*$[\s\S]*^\s*(?:#|ℹ)?\s*tests\s+0\s*$)/imu},{code:"reported_zero_tests",regex:/^\s*(?:#|ℹ)?\s*tests\s+0\s*$/imu}],POSITIVE_LINKED_TEST_RUN_PATTERNS=[/^\s*(?:#|ℹ)?\s*tests\s+[1-9]\d*\s*$/imu,/^\s*(?:#|ℹ)?\s*pass\s+[1-9]\d*\s*$/imu,/\bTests?\s+[1-9]\d*\s+passed\b/iu,/\b[1-9]\d*\s+passed\b/iu,/\bTests:\s+(?:.*\b)?[1-9]\d*\s+passed\b/iu];function detectEmptyLinkedTestRun(stdout,stderr){const combined=`${stdout}
6
6
  ${stderr}`;for(const pattern of EMPTY_LINKED_TEST_RUN_PATTERNS)if(pattern.regex.test(combined))return{code:pattern.code};return null}function hasPositiveLinkedTestRunReceipt(stdout,stderr){const combined=`${stdout}
7
- ${stderr}`;return POSITIVE_LINKED_TEST_RUN_PATTERNS.some(pattern=>pattern.test(combined))}function commandUsesTestNameFilter(command){const normalized=normalizeCommandForValidation(command);return/(?:^|\s)(?:--test-name-pattern|--testnamepattern)(?:=|\s)/u.test(normalized)?!0:splitNormalizedCommandSegments(normalized).some(segment=>/(?:^|\s)-t(?:=|\s)/u.test(segment)&&segmentInvokesDirectTestRunner(segment))}function createLinkedTestSandboxLayout(sandboxRoot){return{root:sandboxRoot,schemaProjectPmPath:path.join(sandboxRoot,"schema","project",".agents","pm"),schemaGlobalPmPath:path.join(sandboxRoot,"schema","global"),trackerProjectPmPath:path.join(sandboxRoot,"tracker","project",".agents","pm"),trackerGlobalPmPath:path.join(sandboxRoot,"tracker","global"),workspaceSnapshotRoot:path.join(sandboxRoot,"workspace","project"),workspaceIsolatedRoot:path.join(sandboxRoot,"workspace","isolated")}}function linkedTestsRequireWorkspaceSnapshot(tests,runLevelMode,overrideLinkedMode){return tests.some(linkedTest=>resolveLinkedTestWorkspaceContextMode(linkedTest,runLevelMode,overrideLinkedMode)==="snapshot")}async function bindSnapshotTracker(snapshotRoot,sandboxPmPath){const agentsRoot=path.join(snapshotRoot,".agents"),target=path.join(agentsRoot,"pm");await mkdir(agentsRoot,{recursive:!0}),await rm(target,{recursive:!0,force:!0}),await symlink(sandboxPmPath,target,process.platform==="win32"?"junction":"dir")}async function initializeLinkedTestSandboxes(layout,initialize=runInit,includeTrackerData=!0){const initOptions={defaults:!0,agentGuidance:"skip"};await initialize(void 0,{path:layout.schemaProjectPmPath},initOptions),await initialize(void 0,{path:layout.schemaGlobalPmPath},initOptions),includeTrackerData&&(await initialize(void 0,{path:layout.trackerProjectPmPath},initOptions),await initialize(void 0,{path:layout.trackerGlobalPmPath},initOptions))}async function seedLinkedTestSandboxesFromSource(layout,sourceRoots,includeTrackerData){sourceRoots&&(await seedLinkedTestSandbox(layout.schemaProjectPmPath,layout.schemaGlobalPmPath,sourceRoots),includeTrackerData&&(await seedLinkedTestSandbox(layout.trackerProjectPmPath,layout.trackerGlobalPmPath,sourceRoots),await seedLinkedTestTrackerData(sourceRoots.projectPmRoot,layout.trackerProjectPmPath),await seedLinkedTestTrackerData(sourceRoots.globalPmRoot,layout.trackerGlobalPmPath),await seedMissingLinkedTestSettingsHistory(sourceRoots.projectPmRoot,layout.trackerProjectPmPath),await seedMissingLinkedTestSettingsHistory(sourceRoots.globalPmRoot,layout.trackerGlobalPmPath)))}function linkedTestsRequireTrackerData(tests,runLevelPmContextMode,options){return tests.some(linkedTest=>{const command=linkedTest.command??"",isPmTrackerReadCommand=command.length>0&&commandInvokesPmTrackerReadCommand(command),requestedPmContextMode=options?.autoPmContext===!0&&isPmTrackerReadCommand?"auto":resolveLinkedTestRequestedContextMode(linkedTest,runLevelPmContextMode,options?.overrideLinkedPmContext===!0);return resolveLinkedTestEffectiveContextMode(requestedPmContextMode,isPmTrackerReadCommand)==="tracker"})}async function countLinkedTestSandboxItems(layout,sourceRoots){return{sourceProjectItemCount:sourceRoots?await countLinkedTestItemFiles(sourceRoots.projectPmRoot):0,sourceGlobalItemCount:sourceRoots?await countLinkedTestItemFiles(sourceRoots.globalPmRoot):0,schemaProjectItemCount:await countLinkedTestItemFiles(layout.schemaProjectPmPath),schemaGlobalItemCount:await countLinkedTestItemFiles(layout.schemaGlobalPmPath),trackerProjectItemCount:await countLinkedTestItemFiles(layout.trackerProjectPmPath),trackerGlobalItemCount:await countLinkedTestItemFiles(layout.trackerGlobalPmPath)}}function buildLinkedTestExecutionContext(params){const trackerMode=params.effectivePmContextMode==="tracker",selectedSandboxProjectPmPath=trackerMode?params.layout.trackerProjectPmPath:params.layout.schemaProjectPmPath,selectedSandboxGlobalPmPath=trackerMode?params.layout.trackerGlobalPmPath:params.layout.schemaGlobalPmPath,selectedSandboxProjectItemCount=trackerMode?params.counts.trackerProjectItemCount:params.counts.schemaProjectItemCount,selectedSandboxGlobalItemCount=trackerMode?params.counts.trackerGlobalItemCount:params.counts.schemaGlobalItemCount,workingDirectory=params.workspaceContextMode==="snapshot"?params.layout.workspaceSnapshotRoot:params.workspaceContextMode==="isolated"?params.layout.workspaceIsolatedRoot:params.sourceWorkspaceRoot,exposedSourceWorkspaceRoot=params.workspaceContextMode==="isolated"?"":workingDirectory;return{requested_pm_context_mode:params.requestedPmContextMode,pm_context_mode:params.effectivePmContextMode,auto_pm_context_applied:params.autoPmContextApplied,is_pm_command:params.isPmCommand,is_pm_tracker_read_command:params.isPmTrackerReadCommand,source_project_pm_path:params.sourceRoots?.projectPmRoot??"",sandbox_project_pm_path:selectedSandboxProjectPmPath,source_global_pm_path:params.sourceRoots?.globalPmRoot??"",sandbox_global_pm_path:selectedSandboxGlobalPmPath,source_project_item_count:params.counts.sourceProjectItemCount,sandbox_project_item_count:selectedSandboxProjectItemCount,source_global_item_count:params.counts.sourceGlobalItemCount,sandbox_global_item_count:selectedSandboxGlobalItemCount,mismatch_detected:params.isPmCommand&&(params.counts.sourceProjectItemCount!==selectedSandboxProjectItemCount||params.counts.sourceGlobalItemCount!==selectedSandboxGlobalItemCount),project_extensions_seeded:!!params.sourceRoots,global_extensions_seeded:!!params.sourceRoots,requested_workspace_context_mode:params.requestedWorkspaceContextMode,workspace_context_mode:params.workspaceContextMode,working_directory:workingDirectory,source_workspace_root:exposedSourceWorkspaceRoot,trust:params.trust}}async function resolveLinkedTestCommandContext(params){const linkedOverridePmContextMode=typeof params.linkedTest.pm_context_mode=="string"&&params.linkedTest.pm_context_mode.trim().length>0?parsePmContextMode(params.linkedTest.pm_context_mode):void 0,command=typeof params.linkedTest.command=="string"&&params.linkedTest.command.length>0?params.linkedTest.command:void 0,isPmCommand=command?commandInvokesPmCli(command):!1,isPmTrackerReadCommand=isPmCommand&&command?commandInvokesPmTrackerReadCommand(command):!1,autoPmContextApplied=params.options?.autoPmContext===!0&&isPmTrackerReadCommand,requestedPmContextMode=autoPmContextApplied?"auto":resolveLinkedTestRequestedContextMode(params.linkedTest,params.runLevelPmContextMode,params.options?.overrideLinkedPmContext===!0),effectivePmContextMode=resolveLinkedTestEffectiveContextMode(requestedPmContextMode,isPmTrackerReadCommand),selectedSandboxProjectPmPath=effectivePmContextMode==="tracker"?params.layout.trackerProjectPmPath:params.layout.schemaProjectPmPath;params.workspaceContextMode==="snapshot"&&await bindSnapshotTracker(params.layout.workspaceSnapshotRoot,selectedSandboxProjectPmPath);const trust=params.options?.pmRoot?await resolveLinkedTestTrust(params.options.pmRoot,params.linkedTest,params.currentSourceRef):await resolveLinkedTestTrust(params.layout.root,params.linkedTest,params.currentSourceRef);return{linkedOverridePmContextMode,executionContext:buildLinkedTestExecutionContext({layout:params.layout,counts:params.counts,sourceRoots:params.sourceRoots,isPmCommand,isPmTrackerReadCommand,requestedPmContextMode,effectivePmContextMode,autoPmContextApplied,requestedWorkspaceContextMode:params.requestedWorkspaceContextMode,workspaceContextMode:params.workspaceContextMode,sourceWorkspaceRoot:params.sourceWorkspaceRoot,trust})}}function buildLinkedTestSkippedResult(linkedTest,executionContext,error){return{command:linkedTest.command,path:linkedTest.path,status:"skipped",execution_context:executionContext,error}}function buildLinkedTestAssertionFailureResult(linkedTest,executionContext,error,output){return{command:linkedTest.command,path:linkedTest.path,status:"failed",exit_code:1,failure_category:"assertion_failure",execution_context:executionContext,...output?{stdout:output.stdout,stderr:output.stderr}:{},error}}function buildLinkedTestTrustRefusalResult(linkedTest,executionContext,error){return{command:linkedTest.command,path:linkedTest.path,status:"failed",exit_code:1,failure_category:"trust_refusal",execution_context:executionContext,error}}function resolveLinkedTestTrustPreflightResult(params){if(params.executionContext.trust.trusted)return null;const overrideRequested=params.options?.allowUntrustedLinkedTests===!0,policyEnabled=params.options?.untrustedLinkedTestsPolicyEnabled===!0;if(overrideRequested&&policyEnabled)return null;const recovery=policyEnabled?"Retry with --allow-untrusted-linked-tests for this invocation, or acknowledge the current commands with --acknowledge-linked-tests.":"Set testing.allow_untrusted_linked_tests=true, then retry with --allow-untrusted-linked-tests; alternatively acknowledge the current commands with --acknowledge-linked-tests.";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}`)}function resolveLinkedTestSafetyPreflightResult(params){if(!params.linkedTest.command)return buildLinkedTestSkippedResult(params.linkedTest,params.executionContext,"No command configured for this linked test.");const runtimeSafetySkipReason=getRuntimeSafetySkipReason(params.linkedTest.command);return runtimeSafetySkipReason?buildLinkedTestSkippedResult(params.linkedTest,params.executionContext,runtimeSafetySkipReason):null}function resolveUnboundPmContextFailure(params){return params.executionContext.pm_context_mode!=="none"?null:params.executionContext.workspace_context_mode==="source"?buildLinkedTestAssertionFailureResult(params.linkedTest,params.executionContext,"pm_context_mode=none requires workspace_context_mode=isolated or snapshot so implicit tracker discovery cannot reach the source workspace."):params.executionContext.is_pm_command?buildLinkedTestAssertionFailureResult(params.linkedTest,params.executionContext,"pm_context_mode=none is for self-isolating SDK tests; direct PM commands require schema, tracker, or auto context."):null}function resolvePmContextMismatchFailure(params){const failOnMismatchByDefault=params.executionContext.pm_context_mode==="schema"&&params.executionContext.is_pm_tracker_read_command&&params.executionContext.mismatch_detected,failOnMismatchByFlag=params.options?.failOnContextMismatch===!0&&params.executionContext.is_pm_command&&params.executionContext.mismatch_detected;if(!failOnMismatchByDefault&&!failOnMismatchByFlag)return null;const mismatchHint=buildPmContextMismatchHint({executionContext:params.executionContext,runLevelPmContextMode:params.runLevelPmContextMode,linkedOverridePmContextMode:params.linkedOverridePmContextMode}),mismatchPrefix=params.options?.checkContext===!0?"Linked test preflight PM context mismatch detected":"Linked test PM context mismatch detected";return buildLinkedTestAssertionFailureResult(params.linkedTest,params.executionContext,`${mismatchPrefix} (source_project_items=${params.executionContext.source_project_item_count}, sandbox_project_items=${params.executionContext.sandbox_project_item_count}).${mismatchHint}`)}function resolveLinkedTestPreflightResult(params){return resolveLinkedTestSafetyPreflightResult(params)||resolveLinkedTestTrustPreflightResult(params)||resolveUnboundPmContextFailure(params)||resolvePmContextMismatchFailure(params)||(params.options?.requireAssertionsForPm===!0&&params.executionContext.is_pm_command&&!hasLinkedTestAssertions(params.linkedTest)?buildLinkedTestAssertionFailureResult(params.linkedTest,params.executionContext,"Linked PM command requires assertions when --require-assertions-for-pm is enabled."):null)}function buildLinkedTestExecutionEnv(params){const effectiveDirectives=resolveEffectiveLinkedTestDirectives(params.runtimeDirectives,params.linkedTest),executionEnv={...process.env,SENTRY_ENVIRONMENT:"test"};return applyEnvDirectiveStage(executionEnv,params.runtimeDirectives),applyEnvDirectiveStage(executionEnv,{env_set:params.linkedTest.env_set??{},env_clear:params.linkedTest.env_clear??[]}),effectiveDirectives.shared_host_safe&&applySharedHostSafeDefaults(executionEnv),executionEnv.FORCE_COLOR="0",params.executionContext.pm_context_mode==="none"?delete executionEnv.PM_PATH:executionEnv.PM_PATH=params.executionContext.sandbox_project_pm_path,executionEnv.PM_GLOBAL_PATH=params.executionContext.sandbox_global_pm_path,params.executionContext.workspace_context_mode==="isolated"?(delete executionEnv.PM_SOURCE_WORKSPACE_ROOT,delete executionEnv.PM_SOURCE_PM_PATH,delete executionEnv[SOURCE_CONTEXT_ACCESS_ENV]):(executionEnv.PM_SOURCE_WORKSPACE_ROOT=params.executionContext.source_workspace_root,executionEnv.PM_SOURCE_PM_PATH=params.executionContext.workspace_context_mode==="snapshot"?params.executionContext.sandbox_project_pm_path:params.executionContext.source_project_pm_path,executionEnv[SOURCE_CONTEXT_ACCESS_ENV]="read_only"),executionEnv}function buildLinkedTestPassedResult(linkedTest,executionContext,execution){return{command:linkedTest.command,path:linkedTest.path,status:"passed",exit_code:0,execution_context:executionContext,stdout:execution.stdout,stderr:execution.stderr}}function buildLinkedTestEmptyRunResult(params){return{command:params.linkedTest.command,path:params.linkedTest.path,status:"failed",exit_code:1,failure_category:"empty_run",execution_context:params.executionContext,stdout:params.execution.stdout,stderr:params.execution.stderr,error:`Linked test reported an empty test run (${params.emptyRunSignal.code}). Update the test selection so at least one test executes; unfiltered commands may opt out by omitting --fail-on-empty-test-run.`}}function buildLinkedTestCommandFailureResult(params){return{command:params.linkedTest.command,path:params.linkedTest.path,status:"failed",exit_code:resolveLinkedTestFailureExitCode(params.execution),failure_category:classifyLinkedTestFailure(params.execution),execution_context:params.executionContext,stdout:params.execution.stdout,stderr:params.execution.stderr,error:formatLinkedTestExecutionError(params.execution,params.timeoutMs)}}function buildLinkedTestPassedExecutionResult(params){if(params.options?.failOnEmptyTestRun===!0||commandUsesTestNameFilter(params.linkedTest.command??"")){const emptyRunSignal=detectEmptyLinkedTestRun(params.execution.stdout,params.execution.stderr);if(emptyRunSignal)return buildLinkedTestEmptyRunResult({...params,emptyRunSignal});if(commandUsesTestNameFilter(params.linkedTest.command??"")&&!hasPositiveLinkedTestRunReceipt(params.execution.stdout,params.execution.stderr))return buildLinkedTestEmptyRunResult({...params,emptyRunSignal:{code:"missing_positive_execution_receipt"}})}const assertionFailures=evaluateLinkedTestAssertions(params.linkedTest,params.execution.stdout,params.execution.stderr);return assertionFailures.length>0?buildLinkedTestAssertionFailureResult(params.linkedTest,params.executionContext,`Linked test assertion(s) failed: ${assertionFailures.join("; ")}`,params.execution):buildLinkedTestPassedResult(params.linkedTest,params.executionContext,params.execution)}async function runSingleLinkedTest(params){const command=params.linkedTest.command??"",timeoutMs=(params.linkedTest.timeout_seconds??params.defaultTimeoutSeconds??120)*1e3,execution=await runLinkedTestCommand(command,timeoutMs,buildLinkedTestExecutionEnv(params),{index:params.index+1,total:params.total,timeoutMs,command},params.progressMode,params.executionContext.working_directory);return execution.exitCode===0&&!execution.timedOut&&!execution.maxBufferExceeded?buildLinkedTestPassedExecutionResult({...params,execution}):buildLinkedTestCommandFailureResult({...params,execution,timeoutMs})}async function runLinkedTests(tests,defaultTimeoutSeconds,options){const results=[],sandboxRoot=await mkdtemp(path.join(tmpdir(),"pm-linked-test-")),layout=createLinkedTestSandboxLayout(sandboxRoot),runLevelPmContextMode=parsePmContextMode(options?.pmContext),runLevelWorkspaceContextMode=parseWorkspaceContextMode(options?.workspaceContext),progressMode=options?.progress===!0?"always":"auto",runtimeDirectives=resolveRuntimeDirectives(options?.envSet,options?.envClear,options?.sharedHostSafe),sourceRoots=options?.sourceRoots,sourceWorkspaceRoot=resolveLinkedTestSourceWorkspaceRoot(),includeTrackerData=linkedTestsRequireTrackerData(tests,runLevelPmContextMode,options),includeWorkspaceSnapshot=linkedTestsRequireWorkspaceSnapshot(tests,runLevelWorkspaceContextMode,options?.overrideLinkedWorkspaceContext===!0),currentSourceRef=await resolveLinkedTestSourceRef(sourceWorkspaceRoot);try{await initializeLinkedTestSandboxes(layout,runInit,includeTrackerData),await mkdir(layout.workspaceIsolatedRoot,{recursive:!0}),await seedLinkedTestSandboxesFromSource(layout,sourceRoots,includeTrackerData),includeWorkspaceSnapshot&&await seedLinkedTestWorkspaceSnapshot(sourceWorkspaceRoot,layout.workspaceSnapshotRoot);const counts=await countLinkedTestSandboxItems(layout,sourceRoots);for(let index=0;index<tests.length;index+=1){const linkedTest=tests[index],workspaceContextMode=resolveLinkedTestWorkspaceContextMode(linkedTest,runLevelWorkspaceContextMode,options?.overrideLinkedWorkspaceContext===!0),commandContext=await resolveLinkedTestCommandContext({linkedTest,layout,counts,sourceRoots,runLevelPmContextMode,options,requestedWorkspaceContextMode:runLevelWorkspaceContextMode,workspaceContextMode,currentSourceRef,sourceWorkspaceRoot}),preflightResult=resolveLinkedTestPreflightResult({linkedTest,executionContext:commandContext.executionContext,runLevelPmContextMode,linkedOverridePmContextMode:commandContext.linkedOverridePmContextMode,options});if(preflightResult){results.push(preflightResult);continue}results.push(await runSingleLinkedTest({linkedTest,index,total:tests.length,defaultTimeoutSeconds,runtimeDirectives,progressMode,executionContext:commandContext.executionContext,options}))}}finally{await rm(layout.root,{recursive:!0,force:!0,maxRetries:3,retryDelay:100})}return results}async function readLinkedTestItem(params){const{id,pmRoot,settings,typeToFolder}=params,located=await locateItem(pmRoot,id,settings.id_prefix,settings.item_format,typeToFolder);if(!located)throw new PmCliError(`Item ${id} not found`,EXIT_CODE.NOT_FOUND);const loaded=await readLocatedItem(located,{schema:settings.schema});return{itemId:located.id,tests:loaded.document.metadata.tests??[],testRuns:loaded.document.metadata.test_runs??[],changed:!1,removed:0}}function linkedTestsHaveSameIdentity(left,right){return left.command===right.command&&left.path===right.path&&left.scope===right.scope&&left.pm_context_mode===right.pm_context_mode&&left.workspace_context_mode===right.workspace_context_mode}function appendMissingLinkedTests(current,additions){const next=[...current];for(const addition of additions)next.some(entry=>linkedTestsHaveSameIdentity(entry,addition))||next.push(addition);return next}function removeLinkedTestsBySelector(current,removals,removeIndexes){if(removals.length===0&&removeIndexes.length===0)return{tests:current,removed:0};const indexSet=new Set(removeIndexes),tests=current.filter((entry,index)=>indexSet.has(index+1)?!1:!removals.some(selector=>selector.kind==="command"?entry.command===selector.value:selector.kind==="path"?entry.path===selector.value:entry.command===selector.value||entry.path===selector.value));return{tests,removed:current.length-tests.length}}function applyLinkedTestMutations(previous,adds,removes,removeIndexes){return removeLinkedTestsBySelector(appendMissingLinkedTests(previous,adds),removes,removeIndexes)}function hasTestRuntimeDirectiveFlags(options){return[(options.envSet?.length??0)>0,(options.envClear?.length??0)>0,options.sharedHostSafe===!0,options.pmContext!==void 0,options.overrideLinkedPmContext===!0,options.failOnContextMismatch===!0,options.failOnSkipped===!0,options.failOnEmptyTestRun===!0,options.requireAssertionsForPm===!0,options.checkContext===!0,options.autoPmContext===!0,options.workspaceContext!==void 0,options.overrideLinkedWorkspaceContext===!0,options.allowUntrustedLinkedTests===!0].some(Boolean)}function assertTestRunFlagUsage(options){if(hasTestRuntimeDirectiveFlags(options)&&options.run!==!0)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);if(options.acknowledgeLinkedTests===!0&&options.run===!0)throw new PmCliError("--acknowledge-linked-tests records trust without executing commands; run it separately before --run",EXIT_CODE.USAGE);if((options.match!==void 0||options.onlyIndex!==void 0||options.onlyLast===!0)&&options.run!==!0)throw new PmCliError("--match, --only-index, and --only-last require --run",EXIT_CODE.USAGE)}async function resolveLinkedTestItem(params){const{id,options,pmRoot,settings,typeToFolder,adds,removes,removeIndexes}=params;if(adds.length===0&&removes.length===0&&removeIndexes.length===0)return readLinkedTestItem({id,pmRoot,settings,typeToFolder});let removed=0;const result=await mutateItem({pmRoot,settings,id,op:adds.length>0?"tests_add":"tests_remove",author:resolveAuthor(options.author,settings.author_default),message:options.message,force:options.force,skipNoop:!0,mutate(document){const previous=document.metadata.tests??[],mutation=applyLinkedTestMutations(previous,adds,removes,removeIndexes);if(removed=mutation.removed,(removes.length>0||removeIndexes.length>0)&&removed===0)throw new PmCliError("No linked tests matched the requested removal selector",EXIT_CODE.NOT_FOUND,{code:"linked_test_remove_no_match",unmatched:[...removes.map(entry=>`${entry.kind}=${entry.value}`),...removeIndexes.map(index=>`index=${index}`)],required:"List linked tests and retry with an exact command/path or a current 1-based --remove-index.",examples:[`pm test ${id} --list`,`pm test ${id} --remove-index 1`]});return document.metadata.tests=mutation.tests,{changedFields:stableValueEquals(previous,mutation.tests)?[]:["tests"]}}});return{itemId:result.item.id,tests:result.item.tests??[],testRuns:result.item.test_runs??[],changed:result.changedFields.length>0,removed}}function resolveTestRunOptions(options,tests){assertTestRunFlagUsage(options);const selectorInput={match:options.match,onlyIndex:options.onlyIndex===void 0?void 0:parseOnlyIndexValue(options.onlyIndex),onlyLast:options.onlyLast},runSelection=options.run===!0?resolveLinkedTestRunSelection(tests,selectorInput):void 0,defaultTimeoutSeconds=options.timeout===void 0?void 0:parseOptionalNumber(options.timeout,"timeout");if(defaultTimeoutSeconds!==void 0&&defaultTimeoutSeconds<=0)throw new PmCliError("Test timeout must be a positive number",EXIT_CODE.USAGE);return{defaultTimeoutSeconds,pmContextMode:parsePmContextMode(options.pmContext),runSelection,testsToRun:runSelection?.selected??tests}}async function executeSelectedLinkedTests(params){const{options,runOptions,pmRoot}=params;return options.run!==!0?[]:await runLinkedTests(runOptions.testsToRun,runOptions.defaultTimeoutSeconds,{progress:options.progress,envSet:options.envSet,envClear:options.envClear,sharedHostSafe:options.sharedHostSafe,pmContext:runOptions.pmContextMode,overrideLinkedPmContext:options.overrideLinkedPmContext,failOnContextMismatch:options.failOnContextMismatch,failOnEmptyTestRun:options.failOnEmptyTestRun,requireAssertionsForPm:options.requireAssertionsForPm,checkContext:options.checkContext,autoPmContext:options.autoPmContext,workspaceContext:options.workspaceContext,overrideLinkedWorkspaceContext:options.overrideLinkedWorkspaceContext,pmRoot,allowUntrustedLinkedTests:options.allowUntrustedLinkedTests,untrustedLinkedTestsPolicyEnabled:params.untrustedLinkedTestsPolicyEnabled,sourceRoots:{projectPmRoot:pmRoot,globalPmRoot:resolveGlobalPmRoot(process.cwd())}})}function buildTestWarnings(options,runSelection,runResults){const warnings=[];if(runSelection&&runSelection.selector!==null&&warnings.push(`linked_test_selection:${runSelection.selector}=${runSelection.requested};selected=${runSelection.selected_count};skipped=${runSelection.skipped_count};indexes=${runSelection.selected_indexes.join(",")}`),options.run===!0&&options.checkContext===!0){const preflight=summarizeContextPreflight(runResults);warnings.push(`context_preflight:checked_pm_commands=${preflight.checked_pm_commands};tracker_read_commands=${preflight.tracker_read_commands};mismatches=${preflight.mismatches};auto_remediated=${preflight.auto_remediated}`)}return warnings}function buildTrackedTestRunSummary(params){const summary=summarizeRunResultStatuses(params.runResults),parsedAttempt=Number.parseInt(process.env.PM_BACKGROUND_TEST_RUN_ATTEMPT?.trim()??"",10),resumedFrom=process.env.PM_BACKGROUND_TEST_RUN_RESUMED_FROM?.trim(),recordedAt=nowIso(),measurements=parseTestRunMeasurements(params.options.measure,recordedAt);return{run_id:resolveTrackedRunId("test"),kind:"test",status:summary.failed>0||params.failOnSkippedTriggered?"failed":"passed",started_at:params.runStartedAt,finished_at:recordedAt,recorded_at:recordedAt,attempt:Number.isFinite(parsedAttempt)&&parsedAttempt>=1?parsedAttempt:void 0,resumed_from:resumedFrom&&resumedFrom.length>0?resumedFrom:void 0,passed:summary.passed,failed:summary.failed,skipped:summary.skipped,fail_on_skipped_triggered:params.failOnSkippedTriggered?!0:void 0,measurements:measurements.length>0?measurements:void 0,executions:params.runResults.filter(result=>result.command?.trim()).slice(0,32).map(result=>({command:result.command.trim(),requested_pm_context_mode:result.execution_context?.requested_pm_context_mode,pm_context_mode:result.execution_context?.pm_context_mode,workspace_context_mode:result.execution_context?.workspace_context_mode,trust_reason:result.execution_context?.trust.reason}))}}async function recordTestRunSummary(params){const{options,pmRoot,settings,itemId,runStartedAt,runResults,failOnSkippedTriggered,warnings}=params;if(options.run!==!0||!runStartedAt)return;const entry=buildTrackedTestRunSummary({options,runStartedAt,runResults,failOnSkippedTriggered});if(settings.testing.record_results_to_items!==!0)return{entry,receipt:{recorded:!1,reason:"tracking_disabled",run_id:entry.run_id,recovery_command:"pm config project set test-result-tracking --policy enabled"}};try{return await appendTrackedTestRunSummary({pmRoot,settings,itemId,author:resolveAuthor(options.author,settings.author_default),message:`Track test run summary (${entry.run_id})`,entry}),{entry,receipt:{recorded:!0,reason:"recorded",run_id:entry.run_id}}}catch(error){return warnings.push(`test_result_tracking_failed:${itemId}:${error instanceof Error?error.message:String(error)}`),{entry,receipt:{recorded:!1,reason:"write_failed",run_id:entry.run_id}}}}function buildTestMeasurementProjection(options,runs,recordedRun){let metricBelow;if(options.metricBelow!==void 0){const separator=options.metricBelow.indexOf("="),name=options.metricBelow.slice(0,separator).trim(),thresholdText=options.metricBelow.slice(separator+1).trim(),threshold=Number(thresholdText);if(separator<=0||name.length===0||thresholdText.length===0||!Number.isFinite(threshold))throw new PmCliError("--metric-below must use name=value",EXIT_CODE.USAGE);metricBelow=queryTestRunMeasurementsBelow(runs,name,threshold)}return{measurements:recordedRun?.measurements,metric_below:metricBelow,metric_diff:options.metricDiff===void 0?void 0:diffTestRunMeasurements(runs,options.metricDiff)}}async function maybeAcknowledgeLinkedTests(options,pmRoot,tests){if(options.acknowledgeLinkedTests===!0)return acknowledgeLinkedTests(pmRoot,tests,nowIso())}function projectTestResult(input){const{item,runResults,failureCategories,runSelection,failOnSkippedTriggered,warnings,recording,measurementProjection}=input;return{ok:runResults.every(entry=>entry.status!=="failed")&&failOnSkippedTriggered!==!0,id:item.itemId,tests:item.tests,run_results:runResults,failure_categories:failureCategories,selection:runSelection?{selector:runSelection.selector,requested:runSelection.requested,selected_indexes:runSelection.selected_indexes,selected_count:runSelection.selected_count,skipped_count:runSelection.skipped_count}:void 0,fail_on_skipped_triggered:failOnSkippedTriggered?!0:void 0,warnings:warnings.length>0?warnings:void 0,changed:item.changed||recording?.receipt.recorded===!0,...recording?{evidence_recording:recording.receipt}:{},count:item.tests.length,...item.removed>0?{removed:item.removed}:{},...measurementProjection}}async function runTest(id,options,global2){const stdinResolver=createStdinTokenResolver(),pmRoot=resolvePmRoot(process.cwd(),global2.path);await assertInitializedTracker(pmRoot);const settings=await readSettings(pmRoot);if((options.measure?.length??0)>0&&options.run!==!0)throw new PmCliError("--measure requires --run",EXIT_CODE.USAGE);parseTestRunMeasurements(options.measure,"validation"),buildTestMeasurementProjection(options,[],void 0);const typeRegistry=resolveItemTypeRegistry(settings,getActiveExtensionRegistrations()),resolvedAdds=await stdinResolver.resolveList(options.add,"--add"),resolvedAddJsons=await stdinResolver.resolveList(options.addJson,"--add-json"),resolvedRemoves=await stdinResolver.resolveList(options.remove,"--remove"),parsedAdds=[...parseAddEntries(resolvedAdds),...parseAddJsonEntries(resolvedAddJsons)],adds=await attachLinkedTestMutationProvenance(parsedAdds,resolveAuthor(options.author,settings.author_default),nowIso())??[],removes=parseRemoveEntries(resolvedRemoves),removeIndexes=parseRemoveIndexes(options.removeIndex),item=await resolveLinkedTestItem({id,options,pmRoot,settings,typeToFolder:typeRegistry.type_to_folder,adds,removes,removeIndexes}),runOptions=resolveTestRunOptions(options,item.tests),trustAcknowledgement=await maybeAcknowledgeLinkedTests(options,pmRoot,item.tests),runStartedAt=options.run===!0?nowIso():void 0,runResults=await executeSelectedLinkedTests({options,runOptions,pmRoot,untrustedLinkedTestsPolicyEnabled:settings.testing.allow_untrusted_linked_tests}),failureCategories=countFailureCategories(runResults),failOnSkippedTriggered=options.run===!0&&options.failOnSkipped===!0&&runResults.some(entry=>entry.status==="skipped"),warnings=buildTestWarnings(options,runOptions.runSelection,runResults);trustAcknowledgement&&warnings.push(`linked_test_trust_acknowledged:${trustAcknowledgement.acknowledged}`);const recording=await recordTestRunSummary({options,pmRoot,settings,itemId:item.itemId,runStartedAt,runResults,failOnSkippedTriggered,warnings}),runs=recording?.receipt.recorded?buildTrackedTestRunHistory([...item.testRuns,recording.entry]):item.testRuns,measurementProjection=buildTestMeasurementProjection(options,runs,recording?.entry);return projectTestResult({item,runResults,failureCategories,runSelection:runOptions.runSelection,failOnSkippedTriggered,warnings,recording,measurementProjection})}const _testOnlyTestCommand={buildLinkedTestExecutionContext,buildPmContextMismatchHint,commandInvokesPmCli,commandInvokesPmTrackerReadCommand,commandUsesTestNameFilter,copyIntoSandboxIfPresent,countLinkedTestItemFiles,ensureScope,evaluateLinkedTestAssertions,endLinkedTestProgress,extractPmInvocationArgsFromSegment,firstDirectTestRunnerSubcommand,formatLinkedTestExecutionError,hasLinkedTestAssertions,initializeLinkedTestSandboxes,linkedTestsRequireTrackerData,parseAddJsonEntries,parseLinkedTestTimeoutSeconds,parsePmContextMode,readJsonPathValue,resolveAuthor,resolveDirectRunnerSubcommand,resolveLinkedTestEffectiveContextMode,resolveLinkedTestFailureExitCode,resolveLinkedTestRequestedContextMode,resolveTestRunOptions,resolveTrackedRunId,runLinkedTestCommand,seedMissingLinkedTestSettingsHistory,seedLinkedTestSandbox,seedLinkedTestTrackerData,segmentInvokesRecursiveTestAll,splitJsonPathSegments,summarizeRunResultStatuses};export{_testOnlyTestCommand,classifyLinkedTestCommandSafety,classifyLinkedTestFailure,countFailureCategories,extractReferencedPmItemIdsFromCommand,resolveLinkedTestFailureExitCode,runLinkedTests,runTest,summarizeContextPreflight};
7
+ ${stderr}`;return POSITIVE_LINKED_TEST_RUN_PATTERNS.some(pattern=>pattern.test(combined))}function commandUsesTestNameFilter(command){const normalized=normalizeCommandForValidation(command);return/(?:^|\s)(?:--test-name-pattern|--testnamepattern)(?:=|\s)/u.test(normalized)?!0:splitNormalizedCommandSegments(normalized).some(segment=>/(?:^|\s)-t(?:=|\s)/u.test(segment)&&segmentInvokesDirectTestRunner(segment))}function createLinkedTestSandboxLayout(sandboxRoot){return{root:sandboxRoot,schemaProjectPmPath:path.join(sandboxRoot,"schema","project",".agents","pm"),schemaGlobalPmPath:path.join(sandboxRoot,"schema","global"),trackerProjectPmPath:path.join(sandboxRoot,"tracker","project",".agents","pm"),trackerGlobalPmPath:path.join(sandboxRoot,"tracker","global"),workspaceSnapshotRoot:path.join(sandboxRoot,"workspace","project"),workspaceIsolatedRoot:path.join(sandboxRoot,"workspace","isolated")}}function linkedTestsRequireWorkspaceSnapshot(tests,runLevelMode,overrideLinkedMode){return tests.some(linkedTest=>resolveLinkedTestWorkspaceContextMode(linkedTest,runLevelMode,overrideLinkedMode)==="snapshot")}async function bindSnapshotTracker(snapshotRoot,sandboxPmPath){const agentsRoot=path.join(snapshotRoot,".agents"),target=path.join(agentsRoot,"pm");await mkdir(agentsRoot,{recursive:!0}),await rm(target,{recursive:!0,force:!0}),await symlink(sandboxPmPath,target,process.platform==="win32"?"junction":"dir")}async function initializeLinkedTestSandboxes(layout,initialize=runInit,includeTrackerData=!0){const initOptions={defaults:!0,agentGuidance:"skip"};await initialize(void 0,{path:layout.schemaProjectPmPath},initOptions),await initialize(void 0,{path:layout.schemaGlobalPmPath},initOptions),includeTrackerData&&(await initialize(void 0,{path:layout.trackerProjectPmPath},initOptions),await initialize(void 0,{path:layout.trackerGlobalPmPath},initOptions))}async function seedLinkedTestSandboxesFromSource(layout,sourceRoots,includeTrackerData){sourceRoots&&(await seedLinkedTestSandbox(layout.schemaProjectPmPath,layout.schemaGlobalPmPath,sourceRoots,!0),includeTrackerData&&(await seedLinkedTestSandbox(layout.trackerProjectPmPath,layout.trackerGlobalPmPath,sourceRoots),await seedLinkedTestTrackerData(sourceRoots.projectPmRoot,layout.trackerProjectPmPath),await seedLinkedTestTrackerData(sourceRoots.globalPmRoot,layout.trackerGlobalPmPath),await seedMissingLinkedTestSettingsHistory(sourceRoots.projectPmRoot,layout.trackerProjectPmPath),await seedMissingLinkedTestSettingsHistory(sourceRoots.globalPmRoot,layout.trackerGlobalPmPath)))}function linkedTestsRequireTrackerData(tests,runLevelPmContextMode,options){return tests.some(linkedTest=>{const command=linkedTest.command??"",isPmTrackerReadCommand=command.length>0&&commandInvokesPmTrackerReadCommand(command),requestedPmContextMode=options?.autoPmContext===!0&&isPmTrackerReadCommand?"auto":resolveLinkedTestRequestedContextMode(linkedTest,runLevelPmContextMode,options?.overrideLinkedPmContext===!0);return resolveLinkedTestEffectiveContextMode(requestedPmContextMode,isPmTrackerReadCommand)==="tracker"})}async function countLinkedTestSandboxItems(layout,sourceRoots){return{sourceProjectItemCount:sourceRoots?await countLinkedTestItemFiles(sourceRoots.projectPmRoot):0,sourceGlobalItemCount:sourceRoots?await countLinkedTestItemFiles(sourceRoots.globalPmRoot):0,schemaProjectItemCount:await countLinkedTestItemFiles(layout.schemaProjectPmPath),schemaGlobalItemCount:await countLinkedTestItemFiles(layout.schemaGlobalPmPath),trackerProjectItemCount:await countLinkedTestItemFiles(layout.trackerProjectPmPath),trackerGlobalItemCount:await countLinkedTestItemFiles(layout.trackerGlobalPmPath)}}function buildLinkedTestExecutionContext(params){const trackerMode=params.effectivePmContextMode==="tracker",selectedSandboxProjectPmPath=trackerMode?params.layout.trackerProjectPmPath:params.layout.schemaProjectPmPath,selectedSandboxGlobalPmPath=trackerMode?params.layout.trackerGlobalPmPath:params.layout.schemaGlobalPmPath,selectedSandboxProjectItemCount=trackerMode?params.counts.trackerProjectItemCount:params.counts.schemaProjectItemCount,selectedSandboxGlobalItemCount=trackerMode?params.counts.trackerGlobalItemCount:params.counts.schemaGlobalItemCount,workingDirectory=params.workspaceContextMode==="snapshot"?params.layout.workspaceSnapshotRoot:params.workspaceContextMode==="isolated"?params.layout.workspaceIsolatedRoot:params.sourceWorkspaceRoot,exposedSourceWorkspaceRoot=params.workspaceContextMode==="isolated"?"":workingDirectory;return{requested_pm_context_mode:params.requestedPmContextMode,pm_context_mode:params.effectivePmContextMode,auto_pm_context_applied:params.autoPmContextApplied,is_pm_command:params.isPmCommand,is_pm_tracker_read_command:params.isPmTrackerReadCommand,source_project_pm_path:params.sourceRoots?.projectPmRoot??"",sandbox_project_pm_path:selectedSandboxProjectPmPath,source_global_pm_path:params.sourceRoots?.globalPmRoot??"",sandbox_global_pm_path:selectedSandboxGlobalPmPath,source_project_item_count:params.counts.sourceProjectItemCount,sandbox_project_item_count:selectedSandboxProjectItemCount,source_global_item_count:params.counts.sourceGlobalItemCount,sandbox_global_item_count:selectedSandboxGlobalItemCount,mismatch_detected:params.isPmCommand&&(params.counts.sourceProjectItemCount!==selectedSandboxProjectItemCount||params.counts.sourceGlobalItemCount!==selectedSandboxGlobalItemCount),project_extensions_seeded:!!params.sourceRoots,global_extensions_seeded:!!params.sourceRoots,requested_workspace_context_mode:params.requestedWorkspaceContextMode,workspace_context_mode:params.workspaceContextMode,working_directory:workingDirectory,source_workspace_root:exposedSourceWorkspaceRoot,trust:params.trust}}async function resolveLinkedTestCommandContext(params){const linkedOverridePmContextMode=typeof params.linkedTest.pm_context_mode=="string"&&params.linkedTest.pm_context_mode.trim().length>0?parsePmContextMode(params.linkedTest.pm_context_mode):void 0,command=typeof params.linkedTest.command=="string"&&params.linkedTest.command.length>0?params.linkedTest.command:void 0,isPmCommand=command?commandInvokesPmCli(command):!1,isPmTrackerReadCommand=isPmCommand&&command?commandInvokesPmTrackerReadCommand(command):!1,autoPmContextApplied=params.options?.autoPmContext===!0&&isPmTrackerReadCommand,requestedPmContextMode=autoPmContextApplied?"auto":resolveLinkedTestRequestedContextMode(params.linkedTest,params.runLevelPmContextMode,params.options?.overrideLinkedPmContext===!0),effectivePmContextMode=resolveLinkedTestEffectiveContextMode(requestedPmContextMode,isPmTrackerReadCommand),selectedSandboxProjectPmPath=effectivePmContextMode==="tracker"?params.layout.trackerProjectPmPath:params.layout.schemaProjectPmPath;params.workspaceContextMode==="snapshot"&&await bindSnapshotTracker(params.layout.workspaceSnapshotRoot,selectedSandboxProjectPmPath);const trust=params.options?.pmRoot?await resolveLinkedTestTrust(params.options.pmRoot,params.linkedTest,params.currentSourceRef):await resolveLinkedTestTrust(params.layout.root,params.linkedTest,params.currentSourceRef);return{linkedOverridePmContextMode,executionContext:buildLinkedTestExecutionContext({layout:params.layout,counts:params.counts,sourceRoots:params.sourceRoots,isPmCommand,isPmTrackerReadCommand,requestedPmContextMode,effectivePmContextMode,autoPmContextApplied,requestedWorkspaceContextMode:params.requestedWorkspaceContextMode,workspaceContextMode:params.workspaceContextMode,sourceWorkspaceRoot:params.sourceWorkspaceRoot,trust})}}function buildLinkedTestSkippedResult(linkedTest,executionContext,error){return{command:linkedTest.command,path:linkedTest.path,status:"skipped",execution_context:executionContext,error}}function buildLinkedTestAssertionFailureResult(linkedTest,executionContext,error,output){return{command:linkedTest.command,path:linkedTest.path,status:"failed",exit_code:1,failure_category:"assertion_failure",execution_context:executionContext,...output?{stdout:output.stdout,stderr:output.stderr}:{},error}}function buildLinkedTestTrustRefusalResult(linkedTest,executionContext,error){return{command:linkedTest.command,path:linkedTest.path,status:"failed",exit_code:1,failure_category:"trust_refusal",execution_context:executionContext,error}}function resolveLinkedTestTrustPreflightResult(params){if(params.executionContext.trust.trusted)return null;const overrideRequested=params.options?.allowUntrustedLinkedTests===!0,policyEnabled=params.options?.untrustedLinkedTestsPolicyEnabled===!0;if(overrideRequested&&policyEnabled)return null;const recovery=policyEnabled?"Retry with --allow-untrusted-linked-tests for this invocation, or acknowledge the current commands with --acknowledge-linked-tests.":"Set testing.allow_untrusted_linked_tests=true, then retry with --allow-untrusted-linked-tests; alternatively acknowledge the current commands with --acknowledge-linked-tests.";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}`)}function resolveLinkedTestSafetyPreflightResult(params){if(!params.linkedTest.command)return buildLinkedTestSkippedResult(params.linkedTest,params.executionContext,"No command configured for this linked test.");const runtimeSafetySkipReason=getRuntimeSafetySkipReason(params.linkedTest.command);return runtimeSafetySkipReason?buildLinkedTestSkippedResult(params.linkedTest,params.executionContext,runtimeSafetySkipReason):null}function resolveUnboundPmContextFailure(params){return params.executionContext.pm_context_mode!=="none"?null:params.executionContext.workspace_context_mode==="source"?buildLinkedTestAssertionFailureResult(params.linkedTest,params.executionContext,"pm_context_mode=none requires workspace_context_mode=isolated or snapshot so implicit tracker discovery cannot reach the source workspace."):params.executionContext.is_pm_command?buildLinkedTestAssertionFailureResult(params.linkedTest,params.executionContext,"pm_context_mode=none is for self-isolating SDK tests; direct PM commands require schema, tracker, or auto context."):null}function resolvePmContextMismatchFailure(params){const failOnMismatchByDefault=params.executionContext.pm_context_mode==="schema"&&params.executionContext.is_pm_tracker_read_command&&params.executionContext.mismatch_detected,failOnMismatchByFlag=params.options?.failOnContextMismatch===!0&&params.executionContext.is_pm_command&&params.executionContext.mismatch_detected;if(!failOnMismatchByDefault&&!failOnMismatchByFlag)return null;const mismatchHint=buildPmContextMismatchHint({executionContext:params.executionContext,runLevelPmContextMode:params.runLevelPmContextMode,linkedOverridePmContextMode:params.linkedOverridePmContextMode}),mismatchPrefix=params.options?.checkContext===!0?"Linked test preflight PM context mismatch detected":"Linked test PM context mismatch detected";return buildLinkedTestAssertionFailureResult(params.linkedTest,params.executionContext,`${mismatchPrefix} (source_project_items=${params.executionContext.source_project_item_count}, sandbox_project_items=${params.executionContext.sandbox_project_item_count}).${mismatchHint}`)}function resolveLinkedTestPreflightResult(params){return resolveLinkedTestSafetyPreflightResult(params)||resolveLinkedTestTrustPreflightResult(params)||resolveUnboundPmContextFailure(params)||resolvePmContextMismatchFailure(params)||(params.options?.requireAssertionsForPm===!0&&params.executionContext.is_pm_command&&!hasLinkedTestAssertions(params.linkedTest)?buildLinkedTestAssertionFailureResult(params.linkedTest,params.executionContext,"Linked PM command requires assertions when --require-assertions-for-pm is enabled."):null)}function buildLinkedTestExecutionEnv(params){const effectiveDirectives=resolveEffectiveLinkedTestDirectives(params.runtimeDirectives,params.linkedTest),executionEnv={...process.env,SENTRY_ENVIRONMENT:"test"};return applyEnvDirectiveStage(executionEnv,params.runtimeDirectives),applyEnvDirectiveStage(executionEnv,{env_set:params.linkedTest.env_set??{},env_clear:params.linkedTest.env_clear??[]}),effectiveDirectives.shared_host_safe&&applySharedHostSafeDefaults(executionEnv),executionEnv.FORCE_COLOR="0",params.executionContext.pm_context_mode==="none"?delete executionEnv.PM_PATH:executionEnv.PM_PATH=params.executionContext.sandbox_project_pm_path,executionEnv.PM_GLOBAL_PATH=params.executionContext.sandbox_global_pm_path,params.executionContext.workspace_context_mode==="isolated"?(delete executionEnv.PM_SOURCE_WORKSPACE_ROOT,delete executionEnv.PM_SOURCE_PM_PATH,delete executionEnv[SOURCE_CONTEXT_ACCESS_ENV]):(executionEnv.PM_SOURCE_WORKSPACE_ROOT=params.executionContext.source_workspace_root,executionEnv.PM_SOURCE_PM_PATH=params.executionContext.workspace_context_mode==="snapshot"?params.executionContext.sandbox_project_pm_path:params.executionContext.source_project_pm_path,executionEnv[SOURCE_CONTEXT_ACCESS_ENV]="read_only"),executionEnv}function buildLinkedTestPassedResult(linkedTest,executionContext,execution){return{command:linkedTest.command,path:linkedTest.path,status:"passed",exit_code:0,execution_context:executionContext,stdout:execution.stdout,stderr:execution.stderr}}function buildLinkedTestEmptyRunResult(params){return{command:params.linkedTest.command,path:params.linkedTest.path,status:"failed",exit_code:1,failure_category:"empty_run",execution_context:params.executionContext,stdout:params.execution.stdout,stderr:params.execution.stderr,error:`Linked test reported an empty test run (${params.emptyRunSignal.code}). Update the test selection so at least one test executes; unfiltered commands may opt out by omitting --fail-on-empty-test-run.`}}function buildLinkedTestCommandFailureResult(params){return{command:params.linkedTest.command,path:params.linkedTest.path,status:"failed",exit_code:resolveLinkedTestFailureExitCode(params.execution),failure_category:classifyLinkedTestFailure(params.execution),execution_context:params.executionContext,stdout:params.execution.stdout,stderr:params.execution.stderr,error:formatLinkedTestExecutionError(params.execution,params.timeoutMs)}}function buildLinkedTestPassedExecutionResult(params){if(params.options?.failOnEmptyTestRun===!0||commandUsesTestNameFilter(params.linkedTest.command??"")){const emptyRunSignal=detectEmptyLinkedTestRun(params.execution.stdout,params.execution.stderr);if(emptyRunSignal)return buildLinkedTestEmptyRunResult({...params,emptyRunSignal});if(commandUsesTestNameFilter(params.linkedTest.command??"")&&!hasPositiveLinkedTestRunReceipt(params.execution.stdout,params.execution.stderr))return buildLinkedTestEmptyRunResult({...params,emptyRunSignal:{code:"missing_positive_execution_receipt"}})}const assertionFailures=evaluateLinkedTestAssertions(params.linkedTest,params.execution.stdout,params.execution.stderr);return assertionFailures.length>0?buildLinkedTestAssertionFailureResult(params.linkedTest,params.executionContext,`Linked test assertion(s) failed: ${assertionFailures.join("; ")}`,params.execution):buildLinkedTestPassedResult(params.linkedTest,params.executionContext,params.execution)}async function runSingleLinkedTest(params){const command=params.linkedTest.command??"",timeoutMs=(params.linkedTest.timeout_seconds??params.defaultTimeoutSeconds??120)*1e3,execution=await runLinkedTestCommand(command,timeoutMs,buildLinkedTestExecutionEnv(params),{index:params.index+1,total:params.total,timeoutMs,command},params.progressMode,params.executionContext.working_directory);return execution.exitCode===0&&!execution.timedOut&&!execution.maxBufferExceeded?buildLinkedTestPassedExecutionResult({...params,execution}):buildLinkedTestCommandFailureResult({...params,execution,timeoutMs})}async function runLinkedTests(tests,defaultTimeoutSeconds,options){const results=[],sandboxRoot=await mkdtemp(path.join(tmpdir(),"pm-linked-test-")),layout=createLinkedTestSandboxLayout(sandboxRoot),runLevelPmContextMode=parsePmContextMode(options?.pmContext),runLevelWorkspaceContextMode=parseWorkspaceContextMode(options?.workspaceContext),progressMode=options?.progress===!0?"always":"auto",runtimeDirectives=resolveRuntimeDirectives(options?.envSet,options?.envClear,options?.sharedHostSafe),sourceRoots=options?.sourceRoots,sourceWorkspaceRoot=resolveLinkedTestSourceWorkspaceRoot(),includeTrackerData=linkedTestsRequireTrackerData(tests,runLevelPmContextMode,options),includeWorkspaceSnapshot=linkedTestsRequireWorkspaceSnapshot(tests,runLevelWorkspaceContextMode,options?.overrideLinkedWorkspaceContext===!0),currentSourceRef=await resolveLinkedTestSourceRef(sourceWorkspaceRoot);try{await initializeLinkedTestSandboxes(layout,runInit,includeTrackerData),await mkdir(layout.workspaceIsolatedRoot,{recursive:!0}),await seedLinkedTestSandboxesFromSource(layout,sourceRoots,includeTrackerData),includeWorkspaceSnapshot&&await seedLinkedTestWorkspaceSnapshot(sourceWorkspaceRoot,layout.workspaceSnapshotRoot);const counts=await countLinkedTestSandboxItems(layout,sourceRoots);for(let index=0;index<tests.length;index+=1){const linkedTest=tests[index],workspaceContextMode=resolveLinkedTestWorkspaceContextMode(linkedTest,runLevelWorkspaceContextMode,options?.overrideLinkedWorkspaceContext===!0),commandContext=await resolveLinkedTestCommandContext({linkedTest,layout,counts,sourceRoots,runLevelPmContextMode,options,requestedWorkspaceContextMode:runLevelWorkspaceContextMode,workspaceContextMode,currentSourceRef,sourceWorkspaceRoot}),preflightResult=resolveLinkedTestPreflightResult({linkedTest,executionContext:commandContext.executionContext,runLevelPmContextMode,linkedOverridePmContextMode:commandContext.linkedOverridePmContextMode,options});if(preflightResult){results.push(preflightResult);continue}results.push(await runSingleLinkedTest({linkedTest,index,total:tests.length,defaultTimeoutSeconds,runtimeDirectives,progressMode,executionContext:commandContext.executionContext,options}))}}finally{await rm(layout.root,{recursive:!0,force:!0,maxRetries:3,retryDelay:100})}return results}async function readLinkedTestItem(params){const{id,pmRoot,settings,typeToFolder}=params,located=await locateItem(pmRoot,id,settings.id_prefix,settings.item_format,typeToFolder);if(!located)throw new PmCliError(`Item ${id} not found`,EXIT_CODE.NOT_FOUND);const loaded=await readLocatedItem(located,{schema:settings.schema});return{itemId:located.id,tests:loaded.document.metadata.tests??[],testRuns:loaded.document.metadata.test_runs??[],changed:!1,removed:0}}function linkedTestsHaveSameIdentity(left,right){return left.command===right.command&&left.path===right.path&&left.scope===right.scope&&left.pm_context_mode===right.pm_context_mode&&left.workspace_context_mode===right.workspace_context_mode}function appendMissingLinkedTests(current,additions){const next=[...current];for(const addition of additions)next.some(entry=>linkedTestsHaveSameIdentity(entry,addition))||next.push(addition);return next}function removeLinkedTestsBySelector(current,removals,removeIndexes){if(removals.length===0&&removeIndexes.length===0)return{tests:current,removed:0};const indexSet=new Set(removeIndexes),tests=current.filter((entry,index)=>indexSet.has(index+1)?!1:!removals.some(selector=>selector.kind==="command"?entry.command===selector.value:selector.kind==="path"?entry.path===selector.value:entry.command===selector.value||entry.path===selector.value));return{tests,removed:current.length-tests.length}}function applyLinkedTestMutations(previous,adds,removes,removeIndexes){return removeLinkedTestsBySelector(appendMissingLinkedTests(previous,adds),removes,removeIndexes)}function hasTestRuntimeDirectiveFlags(options){return[(options.envSet?.length??0)>0,(options.envClear?.length??0)>0,options.sharedHostSafe===!0,options.pmContext!==void 0,options.overrideLinkedPmContext===!0,options.failOnContextMismatch===!0,options.failOnSkipped===!0,options.failOnEmptyTestRun===!0,options.requireAssertionsForPm===!0,options.checkContext===!0,options.autoPmContext===!0,options.workspaceContext!==void 0,options.overrideLinkedWorkspaceContext===!0,options.allowUntrustedLinkedTests===!0].some(Boolean)}function assertTestRunFlagUsage(options){if(hasTestRuntimeDirectiveFlags(options)&&options.run!==!0)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);if(options.acknowledgeLinkedTests===!0&&options.run===!0)throw new PmCliError("--acknowledge-linked-tests records trust without executing commands; run it separately before --run",EXIT_CODE.USAGE);if((options.match!==void 0||options.onlyIndex!==void 0||options.onlyLast===!0)&&options.run!==!0)throw new PmCliError("--match, --only-index, and --only-last require --run",EXIT_CODE.USAGE)}async function resolveLinkedTestItem(params){const{id,options,pmRoot,settings,typeToFolder,adds,removes,removeIndexes}=params;if(adds.length===0&&removes.length===0&&removeIndexes.length===0)return readLinkedTestItem({id,pmRoot,settings,typeToFolder});let removed=0;const result=await mutateItem({pmRoot,settings,id,op:adds.length>0?"tests_add":"tests_remove",author:resolveAuthor(options.author,settings.author_default),message:options.message,force:options.force,skipNoop:!0,mutate(document){const previous=document.metadata.tests??[],mutation=applyLinkedTestMutations(previous,adds,removes,removeIndexes);if(removed=mutation.removed,(removes.length>0||removeIndexes.length>0)&&removed===0)throw new PmCliError("No linked tests matched the requested removal selector",EXIT_CODE.NOT_FOUND,{code:"linked_test_remove_no_match",unmatched:[...removes.map(entry=>`${entry.kind}=${entry.value}`),...removeIndexes.map(index=>`index=${index}`)],required:"List linked tests and retry with an exact command/path or a current 1-based --remove-index.",examples:[`pm test ${id} --list`,`pm test ${id} --remove-index 1`]});return document.metadata.tests=mutation.tests,{changedFields:stableValueEquals(previous,mutation.tests)?[]:["tests"]}}});return{itemId:result.item.id,tests:result.item.tests??[],testRuns:result.item.test_runs??[],changed:result.changedFields.length>0,removed}}function resolveTestRunOptions(options,tests){assertTestRunFlagUsage(options);const selectorInput={match:options.match,onlyIndex:options.onlyIndex===void 0?void 0:parseOnlyIndexValue(options.onlyIndex),onlyLast:options.onlyLast},runSelection=options.run===!0?resolveLinkedTestRunSelection(tests,selectorInput):void 0,defaultTimeoutSeconds=options.timeout===void 0?void 0:parseOptionalNumber(options.timeout,"timeout");if(defaultTimeoutSeconds!==void 0&&defaultTimeoutSeconds<=0)throw new PmCliError("Test timeout must be a positive number",EXIT_CODE.USAGE);return{defaultTimeoutSeconds,pmContextMode:parsePmContextMode(options.pmContext),runSelection,testsToRun:runSelection?.selected??tests}}async function executeSelectedLinkedTests(params){const{options,runOptions,pmRoot}=params;return options.run!==!0?[]:await runLinkedTests(runOptions.testsToRun,runOptions.defaultTimeoutSeconds,{progress:options.progress,envSet:options.envSet,envClear:options.envClear,sharedHostSafe:options.sharedHostSafe,pmContext:runOptions.pmContextMode,overrideLinkedPmContext:options.overrideLinkedPmContext,failOnContextMismatch:options.failOnContextMismatch,failOnEmptyTestRun:options.failOnEmptyTestRun,requireAssertionsForPm:options.requireAssertionsForPm,checkContext:options.checkContext,autoPmContext:options.autoPmContext,workspaceContext:options.workspaceContext,overrideLinkedWorkspaceContext:options.overrideLinkedWorkspaceContext,pmRoot,allowUntrustedLinkedTests:options.allowUntrustedLinkedTests,untrustedLinkedTestsPolicyEnabled:params.untrustedLinkedTestsPolicyEnabled,sourceRoots:{projectPmRoot:pmRoot,globalPmRoot:resolveGlobalPmRoot(process.cwd())}})}function buildTestWarnings(options,runSelection,runResults){const warnings=[];if(runSelection&&runSelection.selector!==null&&warnings.push(`linked_test_selection:${runSelection.selector}=${runSelection.requested};selected=${runSelection.selected_count};skipped=${runSelection.skipped_count};indexes=${runSelection.selected_indexes.join(",")}`),options.run===!0&&options.checkContext===!0){const preflight=summarizeContextPreflight(runResults);warnings.push(`context_preflight:checked_pm_commands=${preflight.checked_pm_commands};tracker_read_commands=${preflight.tracker_read_commands};mismatches=${preflight.mismatches};auto_remediated=${preflight.auto_remediated}`)}return warnings}function buildTrackedTestRunSummary(params){const summary=summarizeRunResultStatuses(params.runResults),parsedAttempt=Number.parseInt(process.env.PM_BACKGROUND_TEST_RUN_ATTEMPT?.trim()??"",10),resumedFrom=process.env.PM_BACKGROUND_TEST_RUN_RESUMED_FROM?.trim(),recordedAt=nowIso(),measurements=parseTestRunMeasurements(params.options.measure,recordedAt);return{run_id:resolveTrackedRunId("test"),kind:"test",status:summary.failed>0||params.failOnSkippedTriggered?"failed":"passed",started_at:params.runStartedAt,finished_at:recordedAt,recorded_at:recordedAt,attempt:Number.isFinite(parsedAttempt)&&parsedAttempt>=1?parsedAttempt:void 0,resumed_from:resumedFrom&&resumedFrom.length>0?resumedFrom:void 0,passed:summary.passed,failed:summary.failed,skipped:summary.skipped,fail_on_skipped_triggered:params.failOnSkippedTriggered?!0:void 0,measurements:measurements.length>0?measurements:void 0,executions:params.runResults.filter(result=>result.command?.trim()).slice(0,32).map(result=>({command:result.command.trim(),requested_pm_context_mode:result.execution_context?.requested_pm_context_mode,pm_context_mode:result.execution_context?.pm_context_mode,workspace_context_mode:result.execution_context?.workspace_context_mode,trust_reason:result.execution_context?.trust.reason}))}}async function recordTestRunSummary(params){const{options,pmRoot,settings,itemId,runStartedAt,runResults,failOnSkippedTriggered,warnings}=params;if(options.run!==!0||!runStartedAt)return;const entry=buildTrackedTestRunSummary({options,runStartedAt,runResults,failOnSkippedTriggered});if(settings.testing.record_results_to_items!==!0)return{entry,receipt:{recorded:!1,reason:"tracking_disabled",run_id:entry.run_id,recovery_command:"pm config project set test-result-tracking --policy enabled"}};try{return await appendTrackedTestRunSummary({pmRoot,settings,itemId,author:resolveAuthor(options.author,settings.author_default),message:`Track test run summary (${entry.run_id})`,entry}),{entry,receipt:{recorded:!0,reason:"recorded",run_id:entry.run_id}}}catch(error){return warnings.push(`test_result_tracking_failed:${itemId}:${error instanceof Error?error.message:String(error)}`),{entry,receipt:{recorded:!1,reason:"write_failed",run_id:entry.run_id}}}}function buildTestMeasurementProjection(options,runs,recordedRun){let metricBelow;if(options.metricBelow!==void 0){const separator=options.metricBelow.indexOf("="),name=options.metricBelow.slice(0,separator).trim(),thresholdText=options.metricBelow.slice(separator+1).trim(),threshold=Number(thresholdText);if(separator<=0||name.length===0||thresholdText.length===0||!Number.isFinite(threshold))throw new PmCliError("--metric-below must use name=value",EXIT_CODE.USAGE);metricBelow=queryTestRunMeasurementsBelow(runs,name,threshold)}return{measurements:recordedRun?.measurements,metric_below:metricBelow,metric_diff:options.metricDiff===void 0?void 0:diffTestRunMeasurements(runs,options.metricDiff)}}async function maybeAcknowledgeLinkedTests(options,pmRoot,tests){if(options.acknowledgeLinkedTests===!0)return acknowledgeLinkedTests(pmRoot,tests,nowIso())}function projectTestResult(input){const{item,runResults,failureCategories,runSelection,failOnSkippedTriggered,warnings,recording,measurementProjection}=input;return{ok:runResults.every(entry=>entry.status!=="failed")&&failOnSkippedTriggered!==!0,id:item.itemId,tests:item.tests,run_results:runResults,failure_categories:failureCategories,selection:runSelection?{selector:runSelection.selector,requested:runSelection.requested,selected_indexes:runSelection.selected_indexes,selected_count:runSelection.selected_count,skipped_count:runSelection.skipped_count}:void 0,fail_on_skipped_triggered:failOnSkippedTriggered?!0:void 0,warnings:warnings.length>0?warnings:void 0,changed:item.changed||recording?.receipt.recorded===!0,...recording?{evidence_recording:recording.receipt}:{},count:item.tests.length,...item.removed>0?{removed:item.removed}:{},...measurementProjection}}async function runTest(id,options,global2){const stdinResolver=createStdinTokenResolver(),pmRoot=resolvePmRoot(process.cwd(),global2.path);await assertInitializedTracker(pmRoot);const settings=await readSettings(pmRoot);if((options.measure?.length??0)>0&&options.run!==!0)throw new PmCliError("--measure requires --run",EXIT_CODE.USAGE);parseTestRunMeasurements(options.measure,"validation"),buildTestMeasurementProjection(options,[],void 0);const typeRegistry=resolveItemTypeRegistry(settings,getActiveExtensionRegistrations()),resolvedAdds=await stdinResolver.resolveList(options.add,"--add"),resolvedAddJsons=await stdinResolver.resolveList(options.addJson,"--add-json"),resolvedRemoves=await stdinResolver.resolveList(options.remove,"--remove"),parsedAdds=[...parseAddEntries(resolvedAdds),...parseAddJsonEntries(resolvedAddJsons)],adds=await attachLinkedTestMutationProvenance(parsedAdds,resolveAuthor(options.author,settings.author_default),nowIso())??[],removes=parseRemoveEntries(resolvedRemoves),removeIndexes=parseRemoveIndexes(options.removeIndex),item=await resolveLinkedTestItem({id,options,pmRoot,settings,typeToFolder:typeRegistry.type_to_folder,adds,removes,removeIndexes}),runOptions=resolveTestRunOptions(options,item.tests),trustAcknowledgement=await maybeAcknowledgeLinkedTests(options,pmRoot,item.tests),runStartedAt=options.run===!0?nowIso():void 0,runResults=await executeSelectedLinkedTests({options,runOptions,pmRoot,untrustedLinkedTestsPolicyEnabled:settings.testing.allow_untrusted_linked_tests}),failureCategories=countFailureCategories(runResults),failOnSkippedTriggered=options.run===!0&&options.failOnSkipped===!0&&runResults.some(entry=>entry.status==="skipped"),warnings=buildTestWarnings(options,runOptions.runSelection,runResults);trustAcknowledgement&&warnings.push(`linked_test_trust_acknowledged:${trustAcknowledgement.acknowledged}`);const recording=await recordTestRunSummary({options,pmRoot,settings,itemId:item.itemId,runStartedAt,runResults,failOnSkippedTriggered,warnings}),runs=recording?.receipt.recorded?buildTrackedTestRunHistory([...item.testRuns,recording.entry]):item.testRuns,measurementProjection=buildTestMeasurementProjection(options,runs,recording?.entry);return projectTestResult({item,runResults,failureCategories,runSelection:runOptions.runSelection,failOnSkippedTriggered,warnings,recording,measurementProjection})}const _testOnlyTestCommand={buildLinkedTestExecutionContext,buildPmContextMismatchHint,commandInvokesPmCli,commandInvokesPmTrackerReadCommand,commandUsesTestNameFilter,copyIntoSandboxIfPresent,countLinkedTestItemFiles,ensureScope,evaluateLinkedTestAssertions,endLinkedTestProgress,extractPmInvocationArgsFromSegment,firstDirectTestRunnerSubcommand,formatLinkedTestExecutionError,hasLinkedTestAssertions,initializeLinkedTestSandboxes,linkedTestsRequireTrackerData,parseAddJsonEntries,parseLinkedTestTimeoutSeconds,parsePmContextMode,readJsonPathValue,resolveAuthor,resolveDirectRunnerSubcommand,resolveLinkedTestEffectiveContextMode,resolveLinkedTestFailureExitCode,resolveLinkedTestRequestedContextMode,resolveTestRunOptions,resolveTrackedRunId,runLinkedTestCommand,seedMissingLinkedTestSettingsHistory,seedLinkedTestSandbox,seedLinkedTestTrackerData,segmentInvokesRecursiveTestAll,splitJsonPathSegments,summarizeRunResultStatuses};export{_testOnlyTestCommand,classifyLinkedTestCommandSafety,classifyLinkedTestFailure,countFailureCategories,extractReferencedPmItemIdsFromCommand,resolveLinkedTestFailureExitCode,runLinkedTests,runTest,summarizeContextPreflight};
8
8
  //# sourceMappingURL=execution.js.map
@@ -20,6 +20,42 @@ nightly quality and release paths enforce it. The registry-owned local
20
20
  preflight also requires the static gate and permits no skip. Trivy separately
21
21
  includes development dependencies in its required repository scan.
22
22
 
23
+ ## Indexed source-map offsets
24
+
25
+ [pm-dba47](../.agents/pm/issues/pm-dba47.toon) tracks Dependabot alert 47 and
26
+ the new source-map cause of Scorecard alert 29.
27
+ [GHSA-68fv-2mgg-jv7q](https://github.com/advisories/GHSA-68fv-2mgg-jv7q)
28
+ affects `source-map-js` before 1.2.2. Unchecked indexed-map section offsets can
29
+ block the event loop. The dependency is used by the development lint and test
30
+ graph through `@eslint/css-tree`, `magicast` and `postcss`; this inventory does
31
+ not establish a production exploit. The pnpm override requires a patched 1.x
32
+ release while retaining normal release-age policy and frozen installation.
33
+ The pinned pnpm 11 uses its default one-day minimum release age; the separate
34
+ seven-day Dependabot cooldown governs version-update scheduling. This repair
35
+ adds no age override or exclusion.
36
+
37
+ A clean audit feed is only evidence about that feed at its observation time.
38
+ GitHub and OSV reported this advisory while the npm audit response was still
39
+ empty. A freshly updated Trivy database also admitted both vulnerable and
40
+ patched temporary fixtures, so that scan did not establish regression sensitivity.
41
+ The independent safe constructor control rejected an oversized indexed-map
42
+ offset only on the patched version, without serializing the huge offset.
43
+ Read the actual locked graph and every available scanner report before
44
+ claiming that findings are retired. Native scans of merged main must confirm
45
+ retirement independently of green PR workflow jobs.
46
+
47
+ ## Worker diagnostics
48
+
49
+ [pm-cql43](../.agents/pm/issues/pm-cql43.toon) tracks CodeQL alert 43 in the
50
+ test worker bridge. Its captured scanner flow contains a generated temporary
51
+ path, so it does not prove a credential leak. The debug path nevertheless
52
+ previously broadcast arbitrary CLI arguments and stderr. Debug failures now
53
+ emit only a fixed notification; the complete captured result remains available
54
+ to deliberate assertions. A real worker regression uses synthetic private
55
+ input and verifies both notification privacy and preserved result contents,
56
+ plus silence when debugging is disabled or the command succeeds. The harness
57
+ is excluded from the published package.
58
+
23
59
  ## File matching dependency removal
24
60
 
25
61
  [GHSA-vfj7-8cjw-p6xm](https://github.com/advisories/GHSA-vfj7-8cjw-p6xm)
@@ -1,7 +1,8 @@
1
1
  # Actionable item-read evidence
2
2
 
3
3
  Trackers: [pm-gh1388](../.agents/pm/issues/pm-gh1388.toon),
4
- [pm-gh1389](../.agents/pm/issues/pm-gh1389.toon).
4
+ [pm-gh1389](../.agents/pm/issues/pm-gh1389.toon),
5
+ [pm-gh1409](../.agents/pm/issues/pm-gh1409.toon).
5
6
 
6
7
  The public SDK item query supplies the same evidence to CLI and MCP callers.
7
8
  Standard and deep current reads include a `blockers` facet when the item declares
@@ -14,10 +15,25 @@ status, so they cannot silently authorize work.
14
15
  Short local references resolve to their verified full IDs and count once even
15
16
  when both forms are stored. Rows under `open` are unresolved by definition;
16
17
  they omit the redundant `resolved: false` flag while retaining resolver context.
18
+ Local comparison keys ignore case for deduplication, while filesystem lookups
19
+ retain the declared spelling. Imported mixed-case IDs therefore resolve to their
20
+ live status on both case-sensitive and case-insensitive filesystems without
21
+ weakening the embedded-identity check.
17
22
  Legacy text that is not a portable filename remains unresolved and never causes
18
23
  a lookup outside the registered item folders.
19
24
  A target file with a different embedded item identity refuses the read with an
20
25
  identity conflict rather than borrowing an unrelated item's terminal status.
26
+ Physical filename verification also runs when the probe and embedded IDs match:
27
+ case-insensitive access can open a differently spelled leaf with the same probe.
28
+ Verification gives an exact leaf precedence and sorts equally
29
+ preferred case aliases deterministically. A directory-read failure refuses the
30
+ query with `blocker_identity_read_failed` and access-restoration guidance. The
31
+ public SDK retains the original error as its cause; the read never invents a
32
+ missing prerequisite or changes item/history bytes. Native case-insensitive
33
+ fixtures intentionally share a physical destination, while case-sensitive
34
+ fixtures retain colliding leaves and exercise the same identity refusal.
35
+ If the physical leaf disappears between the document read and verification,
36
+ the query refuses the identity instead of treating the probe as proof.
21
37
 
22
38
  `blockers.scope` is `declared`: the facet resolves forward declarations from this
23
39
  item, without enumerating unrelated items. Reverse `blocks` relationships require
@@ -21,6 +21,11 @@ is tracked by [pm-8nev0o](../.agents/pm/issues/pm-8nev0o.toon) and
21
21
  projection discovery is tracked by
22
22
  [pm-q4isdq](../.agents/pm/issues/pm-q4isdq.toon). Shared graph defaults and
23
23
  scale enforcement are tracked by [pm-mfy1ux](../.agents/pm/issues/pm-mfy1ux.toon).
24
+ Combined ceiling coordinates, explicit-selector receipt cost, and executable
25
+ global projection retries are tracked by
26
+ [pm-gh1411](../.agents/pm/issues/pm-gh1411.toon),
27
+ [pm-gh1408](../.agents/pm/issues/pm-gh1408.toon), and
28
+ [pm-gh1386](../.agents/pm/issues/pm-gh1386.toon).
24
29
 
25
30
  For enforcement of declared MCP input options in repository tests, see
26
31
  [MCP Option Conformance](MCP_OPTION_CONFORMANCE.md).
@@ -199,6 +204,15 @@ when several groups are needed. This remains truthful for leaf items because
199
204
  an explicit children projection returns an empty rollup instead of silently
200
205
  conflating “no children” with “children were not requested.”
201
206
 
207
+ Explicit `get --fields` selection defines the requested answer. Its receipt
208
+ describes that selection, so intentionally unselected groups do not generate
209
+ restoration rows. The existing complete-receipt shape remains available, and
210
+ requested empty collections and canonical identity remain explicit. Default
211
+ and depth reads retain material-group restoration instructions. A token budget
212
+ that shortens requested strings or collections still emits the shared
213
+ `read_output` and budget-truncation evidence; a complete field-selection
214
+ receipt does not certify that later budget shaping preserved every value.
215
+
202
216
  ## Intent Budgets
203
217
 
204
218
  Built-in read intents apply valid command-specific defaults and disclose the
@@ -274,6 +288,10 @@ Full/projection conflicts retain the selected full or brief mode and remove the
274
288
  conflicting projection and intent controls. If that correction cannot be
275
289
  constructed safely, the error does not advertise an automatic retry.
276
290
  Tracked by [pm-gh1364](../.agents/pm/issues/pm-gh1364.toon).
291
+ Global `--output-include` conflicts are removed with their consumed values,
292
+ including attached values and options before the subcommand. The surviving
293
+ command-local mode keeps its original option position. An ambiguous mode
294
+ location does not produce an automatic retry.
277
295
 
278
296
  For a budget-compacted producer page, follow
279
297
  `output_budget_truncation.recovery.cursor` with `--output-cursor`, keeping the
@@ -284,6 +302,11 @@ actual filter and scope changes still invalidate its cursor. Emitted `count`
284
302
  and `summary.returned_focus` describe delivered rows; matching-population totals
285
303
  remain separate, including explicit count-only aggregates. Context hierarchy companions may repeat while continuing a
286
304
  focus collection. Tracked by [pm-gh1371](../.agents/pm/issues/pm-gh1371.toon).
305
+ When `--output-limit` and `--output-budget` both reduce a page, snapshot counts
306
+ and fingerprints are captured before either row ceiling. Producer cursors
307
+ rebase from the original page coordinate, so an unchanged output continuation
308
+ is accepted and deletion of its last delivered identity cannot skip withheld
309
+ rows through positional fallback. Filter and tracker-scope checks still apply.
287
310
 
288
311
  The first cursor page carries the complete projection, filtering, sorting,
289
312
  completeness, row, and omission contracts. Continuation pages replace those
@@ -1,6 +1,6 @@
1
1
  # Pull Request Review Loop
2
2
 
3
- Trackers: [pm-hq28](../.agents/pm/tasks/pm-hq28.toon), [pm-cp5pbo](../.agents/pm/tasks/pm-cp5pbo.toon), [pm-8we38i](../.agents/pm/issues/pm-8we38i.toon)
3
+ Trackers: [pm-hq28](../.agents/pm/tasks/pm-hq28.toon), [pm-0fxa](../.agents/pm/tasks/pm-0fxa.toon), [pm-zpwfzy](../.agents/pm/issues/pm-zpwfzy.toon), [pm-cp5pbo](../.agents/pm/tasks/pm-cp5pbo.toon), [pm-8we38i](../.agents/pm/issues/pm-8we38i.toon)
4
4
 
5
5
  Use `scripts/reviews/pr-review-loop.mjs` to inventory every GitHub pull-request
6
6
  conversation surface before deciding that review is complete. The inventory includes
@@ -46,10 +46,27 @@ reply thread. Legacy calls without the flag retain their existing behavior.
46
46
  After every push or reviewer retrigger, run `watch`. It delegates waiting to
47
47
  `gh pr checks --watch`, because reviewer agents report completion through GitHub
48
48
  checks, and only fetches the complete conversation inventory after those checks
49
- finish. A failed reviewer check is still a completed review signal: `watch` records
49
+ finish. Native watch success covers emitted checks only: a required provider may
50
+ never emit its context. The helper unions classic branch-protection requirements
51
+ with all effective ruleset requirements, compares those names with the complete
52
+ head rollup, and records `mergeReadiness.requiredContexts`, `missingContexts`, and
53
+ GitHub's `mergeStateStatus`. An attempt is `passed` only when no required name is
54
+ missing and GitHub reports `CLEAN`. GitHub's merge state also retains enforcement
55
+ of expected check publishers and other merge requirements; a matching name alone
56
+ does not establish that its expected app passed. Missing contexts and blocked or
57
+ unknown merge state remain `incomplete`; native watch failures remain `failed`.
58
+ Superseded attempts carry `superseded: true` and never certify readiness. Unavailable policy or status reads
59
+ fail visibly instead of certifying readiness. Successful
60
+ coverage uploads alone cannot certify a downstream provider's patch status.
61
+ The direct `watch` command emits its complete JSON receipt before exiting `1`
62
+ for `incomplete` or `failed`, and exits `0` only for `passed`. Shell automation
63
+ can therefore stop on the exit status while retaining all findings for triage.
64
+
65
+ A failed reviewer check is still a completed review signal: `watch` records
50
66
  the failed outcome and returns all findings instead of aborting before inventory.
51
- If the PR head changes while checks are running, the helper automatically watches
52
- the new head, up to three consecutive attempts, before returning exact-head state.
67
+ If the PR head or target branch changes during the wait or final readiness read,
68
+ the helper watches the new target, up to three consecutive attempts, before
69
+ returning exact-head state. It does not poll an absent required provider status.
53
70
  A review pass is complete only when every bot surface in that inventory has been
54
71
  handled appropriately, every actionable thread is resolved, and required checks
55
72
  have completed successfully.
package/docs/README.md CHANGED
@@ -50,6 +50,7 @@ pm guide release --json
50
50
  - [Noun–Verb CLI Grammar](CLI_GRAMMAR.md) - accepted command architecture, exhaustive destination census, hidden aliases, and the surface-growth gate.
51
51
  - [SDK Primitive Inventory](SDK_PRIMITIVE_INVENTORY.md) - SDK-first migration map and private-import ratchet for CLI/MCP layering.
52
52
  - [Package SDK Contract Conformance](PACKAGE_SDK_CONTRACT_CONFORMANCE.md) - authoritative public types, `typeof` module derivation, and the first-party parity gate.
53
+ - [SDK Configuration and Diagnostic Safety](SDK_CONFIGURATION_SAFETY.md) - explicit settings ownership, transient npm freshness, audited schema sandboxes, and mutation-safe help.
53
54
  - [SDK Action and Boundary Conformance](SDK_ACTION_CONFORMANCE.md) - derived CLI/SDK/MCP action vocabulary, public-import ratchets, intent budget diagnostics, and package-runner proof.
54
55
  - [MCP 2026-07-28 Protocol Decision](MCP_2026_07_28.md) - stateless request metadata, discovery, result envelopes, explicit legacy boundary, and migration policy.
55
56
  - [Progressive Tool Discovery](PROGRESSIVE_TOOL_DISCOVERY.md) - opt-in bounded MCP catalogs, public SDK ranking and pagination, canonical results, and compatibility isolation.
package/docs/RELEASING.md CHANGED
@@ -494,6 +494,24 @@ gh run list --workflow Release --limit 5
494
494
  gh run watch <run-id> --exit-status
495
495
  ```
496
496
 
497
+ ## Verified Coverage Uploads
498
+
499
+ Tracked by [pm-2x67z9](../.agents/pm/issues/pm-2x67z9.toon). CI downloads the
500
+ official Codecov CLI from an immutable GitHub release and checks the reviewed
501
+ SHA-256 before making it executable. Both coverage and test-result uploads
502
+ require that verifier to succeed and retain mandatory upload failures plus
503
+ the exact PR-head identity. The pinned action's supported `url` input selects
504
+ the official `https://codecov.io` Cloud host for the current coverage and
505
+ test-result APIs; certificate validation remains required for every request.
506
+ Both uploads disable verbose logging because debug output can expose signed
507
+ storage upload URLs in public Actions logs. Normal upload-result diagnostics
508
+ remain available, and upload errors still fail the required gate.
509
+ Failed tests can still upload diagnostic reports
510
+ after successful verification. Update the release URL and digest together;
511
+ verify the official artifact and its adoption age before changing those pins.
512
+ The shell regression executes the real checksum with approved and corrupt
513
+ download fixtures; a failed check must prevent executable permission and uploads.
514
+
497
515
  ## Post-Release Verification
498
516
 
499
517
  Use the [rolling reliability report](RELEASE_RELIABILITY.md) as well as the