@unbrained/pm-cli 2026.8.28 → 2026.8.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/CHANGELOG.md +30 -0
- package/dist/cli-bundle/bundle-manifest.json +125 -125
- package/dist/cli-bundle/chunks/{chunk-E73FDIWT.js → chunk-2MKLQL2E.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-NTXZHRKA.js → chunk-2UETTHY6.js} +46 -46
- package/dist/cli-bundle/chunks/{chunk-FEVBFFCQ.js → chunk-3TEF3HM5.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-BY2FQ2NI.js → chunk-7SKMLSMX.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-7X6OZEIV.js +34 -0
- package/dist/cli-bundle/chunks/{chunk-M7OXRQE3.js → chunk-CMIEDWAY.js} +50 -50
- package/dist/cli-bundle/chunks/{chunk-XWEQGHHG.js → chunk-GNWPTEBW.js} +64 -61
- package/dist/cli-bundle/chunks/{chunk-X2RROGZE.js → chunk-MTTKAUBX.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-N6OR6FAR.js +13 -0
- package/dist/cli-bundle/chunks/{chunk-NBCBFVZI.js → chunk-TGNAA66W.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-QE6WQXFO.js → chunk-WS2GJQVG.js} +2 -2
- package/dist/cli-bundle/chunks/{register-list-query-J35ZPQQ5.js → register-list-query-OMKJ5ONN.js} +2 -2
- package/dist/cli-bundle/chunks/{register-mutation-J6XJJOGU.js → register-mutation-QBSNCOY3.js} +2 -2
- package/dist/cli-bundle/chunks/{register-operations-AE3JEMFT.js → register-operations-UC77FQZC.js} +2 -2
- package/dist/cli-bundle/chunks/{register-setup-OQERLLWE.js → register-setup-GLQ5SYV3.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-HC7ODMH3.js → chunk-3AVAWVZA.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-3UMBF26I.js +158 -0
- package/dist/cli-bundle/focused-chunks/{chunk-YVVZ3LQ6.js → chunk-73EDXGYE.js} +16 -15
- package/dist/cli-bundle/focused-chunks/chunk-AXZ5AJAF.js +50 -0
- package/dist/cli-bundle/focused-chunks/{chunk-AHAM2HAU.js → chunk-G6PATS5E.js} +3 -3
- package/dist/cli-bundle/focused-chunks/chunk-RKOKG43G.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-TDK3OCX3.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-FC2AXLB5.js → chunk-XXOWRJZ5.js} +2 -2
- package/dist/cli-bundle/main.js +2 -2
- package/dist/cli-bundle/sdk-contracts.js +1 -1
- package/dist/cli-bundle/sdk-core.js +31 -32
- package/dist/cli-bundle/sdk-governance.js +1 -1
- package/dist/cli-bundle/sdk-graph.js +1 -1
- package/dist/cli-bundle/sdk-merge.js +34 -32
- package/dist/cli-bundle/sdk-query.js +1 -1
- package/dist/cli-bundle/sdk-runtime.js +1 -1
- package/dist/cli-bundle/sdk-testing.js +1 -1
- package/dist/cli-bundle/sdk.js +8 -8
- package/dist/core/diagnostics/remediation.js +16 -6
- package/dist/core/history/drift-scan.d.ts +2 -0
- package/dist/core/history/drift-scan.js +72 -36
- package/dist/core/history/history.d.ts +3 -3
- package/dist/core/history/history.js +5 -5
- package/dist/core/history/replay.d.ts +1 -1
- package/dist/core/history/replay.js +50 -19
- package/dist/core/item/actionability.d.ts +7 -1
- package/dist/core/item/actionability.js +44 -11
- package/dist/core/item/dependency-reference.d.ts +10 -0
- package/dist/core/item/dependency-reference.js +21 -0
- package/dist/core/item/parse.d.ts +4 -0
- package/dist/core/item/parse.js +38 -16
- package/dist/core/shared/author.js +69 -17
- package/dist/sdk/dependency-provenance.d.ts +51 -2
- package/dist/sdk/dependency-provenance.js +91 -7
- package/dist/sdk/extension/install-sources.d.ts +1 -7
- package/dist/sdk/extension/install-sources.js +94 -33
- package/dist/sdk/generated/generated-error-code-catalog-part-1.js +50 -14
- package/dist/sdk/generated/generated-error-code-catalog-part-2.js +62 -26
- package/dist/sdk/governance/health.js +86 -22
- package/dist/sdk/governance/validate-history-drift.js +5 -2
- package/dist/sdk/graph/assembly.js +51 -22
- package/dist/sdk/history-repair.d.ts +1 -1
- package/dist/sdk/history-repair.js +20 -10
- package/dist/sdk/lifecycle/create.js +43 -19
- package/dist/sdk/linked-artifacts.js +21 -31
- package/dist/sdk/merge/reconcile.js +3 -16
- package/dist/sdk/query/next.d.ts +6 -0
- package/dist/sdk/query/next.js +7 -2
- package/dist/sdk/test/entry.d.ts +1 -1
- package/dist/sdk/test/entry.js +5 -9
- package/dist/sdk/test/execution.d.ts +3 -0
- package/dist/sdk/test/execution.js +54 -13
- package/docs/CONTEXT_RECOVERY_PRIMITIVES.md +67 -0
- package/docs/MERGE_SAFETY.md +23 -10
- package/docs/README.md +1 -0
- package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +10 -4
- package/marketplace.json +2 -2
- package/package.json +8 -8
- package/packages/pm-beads/package.json +1 -1
- package/packages/pm-calendar/package.json +1 -1
- package/packages/pm-command-kit/package.json +1 -1
- package/packages/pm-digital-twin/package.json +1 -1
- package/packages/pm-governance-audit/package.json +1 -1
- package/packages/pm-guide-shell/package.json +1 -1
- package/packages/pm-kanban/package.json +1 -1
- package/packages/pm-lifecycle-hooks/package.json +1 -1
- package/packages/pm-linked-test-adapters/package.json +1 -1
- package/packages/pm-search-advanced/package.json +1 -1
- package/packages/pm-templates/package.json +1 -1
- package/packages/pm-todos/package.json +1 -1
- package/packages/pm-vcs/package.json +1 -1
- package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
- package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
- package/sdk/public-surface.json +100 -11
- package/dist/cli-bundle/chunks/chunk-TIQ6AMH2.js +0 -13
- package/dist/cli-bundle/chunks/chunk-XRVVYRRO.js +0 -35
- package/dist/cli-bundle/focused-chunks/chunk-4ZDRZYYJ.js +0 -50
- package/dist/cli-bundle/focused-chunks/chunk-XDPYBQCF.js +0 -156
- package/dist/cli-bundle/focused-chunks/chunk-Y5A7SJJ7.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-YJLDHJOD.js +0 -2
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Implements the pm history repair command surface and its agent-facing runtime behavior.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
7
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="04dbdc08-ad98-543f-96dc-fde635bae92e")}catch(e){}}();
|
|
8
8
|
import { assertInitializedTracker } from "./environment/tracker-preflight.js";
|
|
9
9
|
import { realpath } from "node:fs/promises";
|
|
10
10
|
import path from "node:path";
|
|
@@ -27,7 +27,7 @@ import { listAllItemMetadataWithBody, readLocatedItem, } from "../core/store/ite
|
|
|
27
27
|
import { resolvePmRoot } from "../core/store/paths.js";
|
|
28
28
|
import { readSettings } from "../core/store/settings.js";
|
|
29
29
|
import { resolveHistorySubject } from "./history-redact.js";
|
|
30
|
-
import { summarizeMergeReceipt, } from "./merge/receipts.js";
|
|
30
|
+
import { inspectMergeReceiptEvidence, summarizeMergeReceipt, } from "./merge/receipts.js";
|
|
31
31
|
import { listInvalidProvenanceHistoryStreamIds, normalizeInvalidHistoryProvenance, } from "./governance/provenance-health.js";
|
|
32
32
|
function replayFieldValue(document, field) {
|
|
33
33
|
return field === "body"
|
|
@@ -148,8 +148,6 @@ async function canonicalRealPath(value) {
|
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
150
|
async function verifyReceiptAgainstSnapshot(params) {
|
|
151
|
-
if (params.receipt.evidence_source !== "clone_local")
|
|
152
|
-
return null;
|
|
153
151
|
const receiptPath = await canonicalRealPath(path.resolve(params.gitWorkspaceRoot, params.receipt.item_path));
|
|
154
152
|
if (receiptPath !== params.currentPath)
|
|
155
153
|
return null;
|
|
@@ -159,11 +157,11 @@ async function verifyReceiptAgainstSnapshot(params) {
|
|
|
159
157
|
...params.receipt.union_fields,
|
|
160
158
|
...params.receipt.decisions.map((decision) => decision.field),
|
|
161
159
|
]),
|
|
162
|
-
].sort(
|
|
160
|
+
].sort();
|
|
163
161
|
const hashes = params.receipt.merged_field_hashes;
|
|
164
162
|
if (!hashes || declaredFields.length === 0)
|
|
165
163
|
return null;
|
|
166
|
-
const hashFields = Object.keys(hashes).sort(
|
|
164
|
+
const hashFields = Object.keys(hashes).sort();
|
|
167
165
|
if (declaredFields.length !== hashFields.length ||
|
|
168
166
|
declaredFields.some((field, index) => field !== hashFields[index])) {
|
|
169
167
|
return null;
|
|
@@ -214,7 +212,7 @@ async function verifyMergeReceiptProof(params) {
|
|
|
214
212
|
reconciliationFields.every((field) => trustedFields.has(field))))) {
|
|
215
213
|
return {
|
|
216
214
|
trusted: true,
|
|
217
|
-
reason: "
|
|
215
|
+
reason: "trusted_merge_driver_hash_evidence",
|
|
218
216
|
receipt_ids: trustedReceipts.map(({ receipt }) => receipt.id),
|
|
219
217
|
};
|
|
220
218
|
}
|
|
@@ -225,7 +223,18 @@ async function verifyMergeReceiptProof(params) {
|
|
|
225
223
|
};
|
|
226
224
|
}
|
|
227
225
|
async function resolveHistoryRepairMergeEvidence(params) {
|
|
228
|
-
const
|
|
226
|
+
const requestedReceiptIds = new Set(params.options.mergeReceiptProof?.receipts
|
|
227
|
+
.filter((receipt) => receipt.item_id === params.subjectId)
|
|
228
|
+
.map((receipt) => receipt.id) ?? []);
|
|
229
|
+
const authoritativeReceiptEvidence = params.options.mergeReceiptProof
|
|
230
|
+
? await inspectMergeReceiptEvidence(params.options.mergeReceiptProof.gitWorkspaceRoot ?? process.cwd(), {
|
|
231
|
+
includeReconciled: true,
|
|
232
|
+
includeLossless: true,
|
|
233
|
+
pmRoot: params.pmRoot,
|
|
234
|
+
})
|
|
235
|
+
: undefined;
|
|
236
|
+
const subjectMergeReceipts = authoritativeReceiptEvidence?.receipts.filter((receipt) => receipt.item_id === params.subjectId &&
|
|
237
|
+
requestedReceiptIds.has(receipt.id)) ?? [];
|
|
229
238
|
const mergeReceiptProof = params.options.mergeReceiptProof
|
|
230
239
|
? await verifyMergeReceiptProof({
|
|
231
240
|
proof: {
|
|
@@ -244,7 +253,7 @@ async function resolveHistoryRepairMergeEvidence(params) {
|
|
|
244
253
|
params.options.force !== true) {
|
|
245
254
|
throw new PmCliError(`Merge receipt evidence for ${params.subjectId} does not prove the exact item snapshot (${mergeReceiptProof.reason}).`, EXIT_CODE.CONFLICT, {
|
|
246
255
|
code: "merge_reconcile_receipt_evidence_untrusted",
|
|
247
|
-
required: "Use clone-local
|
|
256
|
+
required: "Use authoritative clone-local or durable receipt evidence whose canonical path, declared fields, and merged-value hashes match the current item, or review the mismatch and rerun with --force.",
|
|
248
257
|
recovery: { suggested_retry: "pm merge reconcile --dry-run" },
|
|
249
258
|
});
|
|
250
259
|
}
|
|
@@ -405,6 +414,7 @@ export async function runHistoryRepair(id, options, global) {
|
|
|
405
414
|
: undefined;
|
|
406
415
|
const { mergeReceiptProof, effectiveAuditContext } = await resolveHistoryRepairMergeEvidence({
|
|
407
416
|
options,
|
|
417
|
+
pmRoot,
|
|
408
418
|
subjectId: subject.id,
|
|
409
419
|
itemReplayContext,
|
|
410
420
|
reconciliation,
|
|
@@ -581,4 +591,4 @@ export async function runHistoryRepairAll(options, global) {
|
|
|
581
591
|
};
|
|
582
592
|
}
|
|
583
593
|
//# sourceMappingURL=history-repair.js.map
|
|
584
|
-
//# debugId=
|
|
594
|
+
//# debugId=04dbdc08-ad98-543f-96dc-fde635bae92e
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* Implements the pm create command surface and its agent-facing runtime behavior.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
7
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="c9bfbfd4-3d34-5fa7-b264-6bf9ed5f7f81")}catch(e){}}();
|
|
8
8
|
import { assertInitializedTracker } from "../environment/tracker-preflight.js";
|
|
9
|
-
import { resolveMutationStdinTokenFields } from "../../core/item/parse.js";
|
|
9
|
+
import { assertNoAmbiguousBareCommaEntry, looksLikeStructuredKeyValueEntry, resolveMutationStdinTokenFields, } from "../../core/item/parse.js";
|
|
10
10
|
import { removeFileIfExists, writeFileAtomic, appendHistoryEntry, createHistoryEntry, generateItemId, normalizeItemId, canonicalDocument, normalizeItemMetadata, serializeItemDocument, assertParentReferenceIsNotSelf, isPlaceholderReferenceToken, normalizeParentReferenceValue, validateMissingParentReference, validateSprintOrReleaseValue, assertNoUnknownCsvKeys, looksLikeGenericKeyValueEntry, mergeAdditiveTags, parseCsvKv, parseOptionalNonNegativeInteger, parseOptionalNumber, parseTags, shouldResolveMutationStdinTokens, transferMutationStdinTokenPolicy, resolvePriority, getFocusedItem, normalizeStatusInput, isTerminalStatus, canonicalizeCommandOptionKey, commandOptionFlagLabel, resolveItemTypeRegistry, resolveCommandOptionPolicyState, resolveTypeDefinition, resolveTypeName, validateTypeOptions, acquireLock, printError, buildInvalidTypeError, resolveTypeSynonym, collectRuntimeCreateFieldValues, resolveItemTypesFilePath, resolveRuntimeFieldRegistry, resolveRuntimeStatusRegistry, EXIT_CODE, ITEM_METADATA_KEY_ORDER, PmCliError, nowIso, resolveIsoOrRelative, getActiveExtensionRegistrations, projectAfterCommandItemSnapshot, recordAfterCommandAffectedItem, runActiveCommandHandler, runActiveBeforeMutationHooks, runActiveOnWriteHooks, collectRegisteredItemFieldNames, applyRegisteredItemFieldDefaultsAndValidation, parseRegisteredItemFieldAssignments, listAllItemMetadataLight, locateItem, createMutationGuardSdk, getHistoryPath, getItemPath, resolvePmRoot, readSettings, resolveAuthor, } from "../runtime-primitives.js";
|
|
11
11
|
import { requireTerminalReasonWithRecovery } from "../lifecycle-policy.js";
|
|
12
12
|
import { evaluateSimilarityGovernance, similarityAdvisoryWarnings, } from "../similarity.js";
|
|
@@ -19,7 +19,7 @@ import { normalizeRiskInput, normalizeSeverityInput, parseConfidenceInput, parse
|
|
|
19
19
|
import { applyLegacyNoneCollectionNormalizers, createLegacyNoneCollectionNormalizers, assertNoLegacyNoneToken, assertNoLegacyNoneTokens, isLegacyNoneToken, } from "./legacy-none-tokens.js";
|
|
20
20
|
import { suggestNextLifecycleTransition, } from "./lifecycle-transitions.js";
|
|
21
21
|
import { parseLinkedTestAssertionEqualsMap, parseLinkedTestAssertionGteMap, parseLinkedTestBoolean, parseLinkedTestContextMode, parseLinkedTestEnvClear, parseLinkedTestEnvSet, parseLinkedTestMinLines, parseLinkedTestRegexList, parseLinkedTestStringList, parseLinkedTestWorkspaceContextMode, } from "../test/parsers.js";
|
|
22
|
-
import {
|
|
22
|
+
import { STRUCTURED_LINKED_TEST_KEYS, normalizeStructuredLinkedTestEntry, } from "../test/entry.js";
|
|
23
23
|
import { attachLinkedTestMutationProvenance } from "../test/trust.js";
|
|
24
24
|
import { COMMON_UNSET_FIELD_DEFINITIONS_AFTER_AUTHOR, COMMON_UNSET_FIELD_DEFINITIONS_BEFORE_AUTHOR, parseCommandUnsetTargets, resolveRuntimeUnsetFieldDefinition, } from "./shared-unset-fields.js";
|
|
25
25
|
import { ensureEnumValue } from "./recurrence-parsers.js";
|
|
@@ -202,13 +202,7 @@ function normalizeDependencyKindInput(raw) {
|
|
|
202
202
|
: raw;
|
|
203
203
|
}
|
|
204
204
|
function looksLikeStructuredEntry(raw, keys) {
|
|
205
|
-
if (raw
|
|
206
|
-
return true;
|
|
207
|
-
}
|
|
208
|
-
const keyPattern = keys
|
|
209
|
-
.map((key) => key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"))
|
|
210
|
-
.join("|");
|
|
211
|
-
if (new RegExp(`^(?:[-*+]\\s+)?(?:${keyPattern})\\s*[:=]`, "i").test(raw)) {
|
|
205
|
+
if (looksLikeStructuredKeyValueEntry(raw, keys)) {
|
|
212
206
|
return true;
|
|
213
207
|
}
|
|
214
208
|
// A first-key typo (e.g. `bogus=v,id=pm-2`) must still be parsed so the unknown
|
|
@@ -281,6 +275,7 @@ export function parseFiles(raw) {
|
|
|
281
275
|
const values = raw.map((entry) => {
|
|
282
276
|
const trimmedEntry = entry.trim();
|
|
283
277
|
const isStructured = looksLikeStructuredEntry(trimmedEntry, LINKED_ARTIFACT_SEED_KEYS);
|
|
278
|
+
assertNoAmbiguousBareCommaEntry(trimmedEntry, "--file", "file path", isStructured);
|
|
284
279
|
const kv = isStructured
|
|
285
280
|
? parseCsvKv(entry, "--file")
|
|
286
281
|
: { path: trimmedEntry };
|
|
@@ -305,7 +300,9 @@ export function parseTests(raw) {
|
|
|
305
300
|
assertNoLegacyNoneTokens(raw, "--test", "Use --clear-tests to clear linked tests.");
|
|
306
301
|
const values = raw.map((entry) => {
|
|
307
302
|
const trimmedEntry = entry.trim();
|
|
308
|
-
const
|
|
303
|
+
const isStructured = looksLikeStructuredEntry(trimmedEntry, STRUCTURED_LINKED_TEST_KEYS);
|
|
304
|
+
assertNoAmbiguousBareCommaEntry(trimmedEntry, "--test", "test command", isStructured);
|
|
305
|
+
const kv = isStructured
|
|
309
306
|
? normalizeStructuredLinkedTestEntry(parseCsvKv(entry, "--test"), "--test")
|
|
310
307
|
: { command: trimmedEntry };
|
|
311
308
|
const command = parseOptionalString(kv.command);
|
|
@@ -1331,6 +1328,19 @@ function assertNoCreateFieldUnsetConflicts(registeredItemFieldValues, runtimeCre
|
|
|
1331
1328
|
}
|
|
1332
1329
|
}
|
|
1333
1330
|
/** Throw the aggregated `missing_required_option` error when a type's required create options and/or required type-option keys were not all supplied in one invocation. Builds a type-aware valid example and staged-onboarding next steps (a strict-mode invocation also carries a compact recovery envelope). A no-op when nothing is missing. */
|
|
1331
|
+
/** Truthful empty-collection alternatives for strict required create fields. */
|
|
1332
|
+
const CLEAR_FLAG_BY_REQUIRED_CREATE_FLAG = {
|
|
1333
|
+
"--comment": "--clear-comments",
|
|
1334
|
+
"--dep": "--clear-deps",
|
|
1335
|
+
"--doc": "--clear-docs",
|
|
1336
|
+
"--event": "--clear-events",
|
|
1337
|
+
"--file": "--clear-files",
|
|
1338
|
+
"--learning": "--clear-learnings",
|
|
1339
|
+
"--note": "--clear-notes",
|
|
1340
|
+
"--reminder": "--clear-reminders",
|
|
1341
|
+
"--test": "--clear-tests",
|
|
1342
|
+
"--type-option": "--clear-type-options",
|
|
1343
|
+
};
|
|
1334
1344
|
function assertNoMissingRequiredCreateOptions(params) {
|
|
1335
1345
|
const { combinedMissingFlags, typeDefinition, missingRequiredCreateFlags, missingRequiredTypeOptionKeys, openStatus, type, createMode, missingRequiredRuntimeFields, } = params;
|
|
1336
1346
|
if (combinedMissingFlags.length === 0) {
|
|
@@ -1359,12 +1369,12 @@ function assertNoMissingRequiredCreateOptions(params) {
|
|
|
1359
1369
|
].join(" ");
|
|
1360
1370
|
const nextSteps = [
|
|
1361
1371
|
`Run "pm create --help --type ${type}" for type-aware required option guidance.`,
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1372
|
+
...combinedMissingFlags
|
|
1373
|
+
.filter((flag) => flag === "--title")
|
|
1374
|
+
.map(() => 'Title can also be passed as the first positional argument (example: pm create "Your title" --type ' +
|
|
1365
1375
|
type +
|
|
1366
|
-
").")
|
|
1367
|
-
|
|
1376
|
+
")."),
|
|
1377
|
+
];
|
|
1368
1378
|
if (createMode === "strict") {
|
|
1369
1379
|
nextSteps.push('For staged onboarding, retry with "--create-mode progressive".');
|
|
1370
1380
|
if (SCHEDULE_CREATE_PRESET_TYPES.has(type)) {
|
|
@@ -1374,21 +1384,35 @@ function assertNoMissingRequiredCreateOptions(params) {
|
|
|
1374
1384
|
if (runtimeFieldSyntax.length > 0) {
|
|
1375
1385
|
nextSteps.push(`Required custom field formats: ${runtimeFieldSyntax.map((field) => field.description).join("; ")}.`);
|
|
1376
1386
|
}
|
|
1387
|
+
const collectionClearFlags = [
|
|
1388
|
+
...new Set(combinedMissingFlags.flatMap((flag) => {
|
|
1389
|
+
const normalizedFlag = flag.split(" ", 1)[0];
|
|
1390
|
+
const clearFlag = CLEAR_FLAG_BY_REQUIRED_CREATE_FLAG[normalizedFlag];
|
|
1391
|
+
return clearFlag ? [clearFlag] : [];
|
|
1392
|
+
})),
|
|
1393
|
+
];
|
|
1394
|
+
if (collectionClearFlags.length > 0) {
|
|
1395
|
+
nextSteps.push(`If the truthful value is an empty collection, use ${collectionClearFlags.join(", ")} instead of inventing metadata.`);
|
|
1396
|
+
}
|
|
1377
1397
|
const errorMessage = combinedMissingFlags.length === 1
|
|
1378
1398
|
? `Missing required option ${combinedMissingFlags[0]} for type "${type}"`
|
|
1379
1399
|
: `Missing required options ${combinedMissingFlags.join(", ")} for type "${type}"`;
|
|
1380
1400
|
throw new PmCliError(errorMessage, EXIT_CODE.USAGE, {
|
|
1381
1401
|
code: "missing_required_option",
|
|
1382
1402
|
required: `Provide all required create options and type options for type "${type}" in one invocation.`,
|
|
1383
|
-
examples: [
|
|
1403
|
+
examples: [
|
|
1404
|
+
...collectionClearFlags.map((flag) => `pm create --type ${type} ${flag} ...`),
|
|
1405
|
+
nextValidExample,
|
|
1406
|
+
],
|
|
1384
1407
|
nextSteps,
|
|
1385
1408
|
recovery: createMode === "strict"
|
|
1386
1409
|
? {
|
|
1387
1410
|
recovery_mode: "compact",
|
|
1388
1411
|
missing_required_fields: combinedMissingFlags,
|
|
1389
1412
|
suggested_flags: [
|
|
1390
|
-
|
|
1413
|
+
...collectionClearFlags,
|
|
1391
1414
|
...combinedMissingFlags,
|
|
1415
|
+
"--create-mode progressive",
|
|
1392
1416
|
],
|
|
1393
1417
|
}
|
|
1394
1418
|
: undefined,
|
|
@@ -1926,4 +1950,4 @@ export const _testOnlyCreateCommand = {
|
|
|
1926
1950
|
typeOptionExampleValue,
|
|
1927
1951
|
};
|
|
1928
1952
|
//# sourceMappingURL=create.js.map
|
|
1929
|
-
//# debugId=
|
|
1953
|
+
//# debugId=c9bfbfd4-3d34-5fa7-b264-6bf9ed5f7f81
|
|
@@ -4,22 +4,21 @@
|
|
|
4
4
|
* Implements the pm linked artifacts command surface and its agent-facing runtime behavior.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
7
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="accd0229-6974-5950-8cea-74f517706878")}catch(e){}}();
|
|
8
8
|
import { assertInitializedTracker } from "./environment/tracker-preflight.js";
|
|
9
9
|
import fs from "node:fs/promises";
|
|
10
10
|
import path from "node:path";
|
|
11
11
|
import fg from "fast-glob";
|
|
12
12
|
import { isFileAbsentError } from "../core/fs/fs-utils.js";
|
|
13
13
|
import { getActiveExtensionRegistrations } from "../core/extensions/index.js";
|
|
14
|
-
import { assertNoUnknownCsvKeys, createStdinTokenResolver, looksLikeGenericKeyValueEntry, parseCsvKv, } from "../core/item/parse.js";
|
|
14
|
+
import { assertNoAmbiguousBareCommaEntry, assertNoUnknownCsvKeys, createStdinTokenResolver, looksLikeGenericKeyValueEntry, parseCsvKv, } from "../core/item/parse.js";
|
|
15
15
|
import { resolveItemTypeRegistry } from "../core/item/type-registry.js";
|
|
16
16
|
import { isRemoteLinkedArtifactReference } from "../core/validate/linked-artifact-reference.js";
|
|
17
17
|
import { EXIT_CODE } from "../core/shared/constants.js";
|
|
18
18
|
import { PmCliError } from "../core/shared/errors.js";
|
|
19
|
-
import { splitCommaList } from "../core/shared/split-comma-list.js";
|
|
20
19
|
import { stableValueEquals } from "../core/shared/serialization.js";
|
|
21
20
|
import { locateItem, mutateItem, readLocatedItem, } from "../core/store/item-store.js";
|
|
22
|
-
import { resolvePmRoot, resolveWorkspaceRoot
|
|
21
|
+
import { resolvePmRoot, resolveWorkspaceRoot } from "../core/store/paths.js";
|
|
23
22
|
import { readSettings } from "../core/store/settings.js";
|
|
24
23
|
import { SCOPE_VALUES } from "../types/index.js";
|
|
25
24
|
import { resolveAuthor } from "../core/shared/author.js";
|
|
@@ -63,7 +62,9 @@ export function looksLikeStructuredPathEntry(raw) {
|
|
|
63
62
|
// leading assignment syntax through the structured-key allowlist so typos
|
|
64
63
|
// and malformed URL-plus-note input remain atomic failures (GH-258,
|
|
65
64
|
// GH-1000).
|
|
66
|
-
const
|
|
65
|
+
const firstCommaIndex = raw.indexOf(",");
|
|
66
|
+
const leadingSegment = raw.slice(0, firstCommaIndex >= 0 ? firstCommaIndex : raw.length);
|
|
67
|
+
const equalsIndex = leadingSegment.indexOf("=");
|
|
67
68
|
if (equalsIndex >= 0 &&
|
|
68
69
|
!/\s/u.test(raw) &&
|
|
69
70
|
(/^[A-Za-z][A-Za-z0-9+.-]*:\/\//u.test(raw) ||
|
|
@@ -71,18 +72,7 @@ export function looksLikeStructuredPathEntry(raw) {
|
|
|
71
72
|
[raw.indexOf("/"), raw.indexOf("\\")].some((separatorIndex) => separatorIndex >= 0 && separatorIndex < equalsIndex))) {
|
|
72
73
|
return false;
|
|
73
74
|
}
|
|
74
|
-
return
|
|
75
|
-
}
|
|
76
|
-
function expandBareCommaSeparatedAddEntries(raw) {
|
|
77
|
-
return raw.flatMap((entry) => {
|
|
78
|
-
const trimmed = entry.trim();
|
|
79
|
-
if (trimmed.length === 0 ||
|
|
80
|
-
looksLikeStructuredPathEntry(trimmed) ||
|
|
81
|
-
!trimmed.includes(",")) {
|
|
82
|
-
return [entry];
|
|
83
|
-
}
|
|
84
|
-
return splitCommaList(trimmed);
|
|
85
|
-
});
|
|
75
|
+
return leadingSegment.includes("=") || looksLikeGenericKeyValueEntry(raw);
|
|
86
76
|
}
|
|
87
77
|
/** Whether a Markdown destination contains only balanced parentheses. */
|
|
88
78
|
function hasBalancedParentheses(value) {
|
|
@@ -136,21 +126,21 @@ export function parseAddEntries(raw, bareNoun) {
|
|
|
136
126
|
if (reference !== undefined)
|
|
137
127
|
return [reference];
|
|
138
128
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
129
|
+
assertNoAmbiguousBareCommaEntry(trimmed, "--add", `${bareNoun} path`, looksLikeStructuredPathEntry(trimmed));
|
|
130
|
+
const kv = looksLikeStructuredPathEntry(trimmed)
|
|
131
|
+
? parseCsvKv(entry, "--add")
|
|
132
|
+
: { path: trimmed };
|
|
133
|
+
assertNoUnknownCsvKeys(kv, "--add", LINKED_ARTIFACT_ADD_KEYS);
|
|
134
|
+
if (!kv.path) {
|
|
135
|
+
throw new PmCliError(`--add requires path=<value> or a bare ${bareNoun} path`, EXIT_CODE.USAGE);
|
|
136
|
+
}
|
|
137
|
+
return [
|
|
138
|
+
{
|
|
149
139
|
path: kv.path,
|
|
150
140
|
scope: ensureScope(kv.scope),
|
|
151
141
|
note: kv.note?.trim() || undefined,
|
|
152
|
-
}
|
|
153
|
-
|
|
142
|
+
},
|
|
143
|
+
];
|
|
154
144
|
});
|
|
155
145
|
}
|
|
156
146
|
/** Implements parse add glob entries for the public runtime surface of this module. */
|
|
@@ -439,7 +429,7 @@ async function buildLinkedArtifactResult(params) {
|
|
|
439
429
|
migrations_applied: params.migrationsApplied && params.migrationsApplied > 0
|
|
440
430
|
? params.migrationsApplied
|
|
441
431
|
: undefined,
|
|
442
|
-
validation: params.options.validatePaths
|
|
432
|
+
validation: params.options.validatePaths || params.changed
|
|
443
433
|
? await validateLinkedPaths(paths, params.workspaceRoot)
|
|
444
434
|
: undefined,
|
|
445
435
|
};
|
|
@@ -538,4 +528,4 @@ export function renameArtifactsResultKey(result, key) {
|
|
|
538
528
|
return out;
|
|
539
529
|
}
|
|
540
530
|
//# sourceMappingURL=linked-artifacts.js.map
|
|
541
|
-
//# debugId=
|
|
531
|
+
//# debugId=accd0229-6974-5950-8cea-74f517706878
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="8d85d220-dad2-5234-8819-65632275eb47")}catch(e){}}();
|
|
3
3
|
import { EXIT_CODE } from "../../core/shared/constants.js";
|
|
4
4
|
import { PmCliError } from "../../core/shared/errors.js";
|
|
5
5
|
import { resolvePmRoot } from "../../core/store/paths.js";
|
|
6
6
|
import { runHistoryRepair, runHistoryRepairAll, } from "../history-repair.js";
|
|
7
7
|
import { runValidate } from "../governance/validate.js";
|
|
8
|
-
import { inspectMergeReceiptEvidence, markMergeReceiptReconciled,
|
|
8
|
+
import { inspectMergeReceiptEvidence, markMergeReceiptReconciled, summarizeMergeReceipt, } from "./receipts.js";
|
|
9
9
|
import { findGitWorkspaceRoot } from "./install.js";
|
|
10
10
|
import { mapWithFixedConcurrency } from "../extension/concurrency.js";
|
|
11
11
|
const RECEIPT_ONLY_REPAIR_CONCURRENCY = 4;
|
|
@@ -95,19 +95,6 @@ export async function runMergeReconcile(options, global) {
|
|
|
95
95
|
});
|
|
96
96
|
}
|
|
97
97
|
const pendingReceipts = receiptEvidence.receipts;
|
|
98
|
-
const { pendingDecisions: discardedReceipts } = partitionMergeReceipts(pendingReceipts);
|
|
99
|
-
if (!dryRun && options.force !== true && discardedReceipts.length > 0) {
|
|
100
|
-
throw new PmCliError(`Merge reconciliation would accept ${discardedReceipts.length} receipt(s) containing discarded scalar values. Review pm merge report and rerun with --force only after deciding which values to retain or re-apply.`, EXIT_CODE.CONFLICT, {
|
|
101
|
-
code: "merge_reconcile_discards_require_acceptance",
|
|
102
|
-
required: "Explicitly review every discarded field before accepting reconciliation.",
|
|
103
|
-
nextSteps: discardedReceipts.map((receipt) => `Review receipt ${receipt.id} for ${receipt.item_id}: ${receipt.decisions
|
|
104
|
-
.map((decision) => decision.field)
|
|
105
|
-
.join(", ")}.`),
|
|
106
|
-
recovery: {
|
|
107
|
-
suggested_retry: "pm merge reconcile --dry-run",
|
|
108
|
-
},
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
98
|
const receiptsByItem = new Map();
|
|
112
99
|
for (const receipt of pendingReceipts) {
|
|
113
100
|
const receipts = receiptsByItem.get(receipt.item_id) ?? [];
|
|
@@ -193,4 +180,4 @@ export async function runMergeReconcile(options, global) {
|
|
|
193
180
|
};
|
|
194
181
|
}
|
|
195
182
|
//# sourceMappingURL=reconcile.js.map
|
|
196
|
-
//# debugId=
|
|
183
|
+
//# debugId=8d85d220-dad2-5234-8819-65632275eb47
|
package/dist/sdk/query/next.d.ts
CHANGED
|
@@ -60,6 +60,12 @@ export interface NextBlockerRef {
|
|
|
60
60
|
title: string | null;
|
|
61
61
|
/** Lifecycle state reported for status. */
|
|
62
62
|
status: ItemStatus | null;
|
|
63
|
+
/** Whether this reference must be checked outside the local tracker. */
|
|
64
|
+
external?: boolean;
|
|
65
|
+
/** Conservative timestamp from which the unresolved external block may be stale. */
|
|
66
|
+
blocked_since?: string;
|
|
67
|
+
/** External resolver identity, or null when no integration verified the reference. */
|
|
68
|
+
resolver?: string | null;
|
|
63
69
|
}
|
|
64
70
|
/**
|
|
65
71
|
* A classified actionable item on the `pm next` output: a {@link ContextFocusItem}
|
package/dist/sdk/query/next.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* re-deriving readiness from a full `pm context` snapshot every turn.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
12
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="9b788e0a-f4ac-5c6e-bf52-0ef0519d6925")}catch(e){}}();
|
|
13
13
|
import { collectBlockedByIds, computeActionabilityReport, } from "../actionability.js";
|
|
14
14
|
import { resolveRuntimeStatusRegistry, EXIT_CODE, PmCliError, nowIso, normalizeStatusInput, normalizeStatusForRegistry, resolvePmRoot, readSettings, resolveAuthor, } from "../runtime-primitives.js";
|
|
15
15
|
import { parseIntegerLimit } from "./parsers.js";
|
|
@@ -60,6 +60,11 @@ function toNextActionableItem(entry, statusRegistry, childrenByParent, rank) {
|
|
|
60
60
|
id: blocker.id,
|
|
61
61
|
title: blocker.title,
|
|
62
62
|
status: blocker.status,
|
|
63
|
+
...(blocker.external ? { external: true } : {}),
|
|
64
|
+
...(blocker.blocked_since
|
|
65
|
+
? { blocked_since: blocker.blocked_since }
|
|
66
|
+
: {}),
|
|
67
|
+
...(blocker.external ? { resolver: blocker.resolver ?? null } : {}),
|
|
63
68
|
})),
|
|
64
69
|
unblocks: entry.unblocks,
|
|
65
70
|
rank,
|
|
@@ -474,4 +479,4 @@ function formatNextLine(item) {
|
|
|
474
479
|
return `${rankToken}${item.id} p${item.priority} ${item.status} ${item.type} deadline:${deadlineToken}${parentToken}${unblocksToken} ${item.title}`;
|
|
475
480
|
}
|
|
476
481
|
//# sourceMappingURL=next.js.map
|
|
477
|
-
//# debugId=
|
|
482
|
+
//# debugId=9b788e0a-f4ac-5c6e-bf52-0ef0519d6925
|
package/dist/sdk/test/entry.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Public contract for structured linked test keys, shared by SDK and presentation-layer consumers. */
|
|
2
2
|
export declare const STRUCTURED_LINKED_TEST_KEYS: readonly ["command", "cmd", "path", "scope", "timeout", "timeout_seconds", "pm_context_mode", "workspace_context_mode", "env_set", "env_clear", "shared_host_safe", "assert_stdout_contains", "assert_stdout_regex", "assert_stderr_contains", "assert_stderr_regex", "assert_stdout_min_lines", "assert_json_field_equals", "assert_json_field_gte", "note"];
|
|
3
|
-
/**
|
|
3
|
+
/** Classify linked-test plaintext through the shared structured-entry grammar. */
|
|
4
4
|
export declare function looksLikeStructuredLinkedTestEntry(raw: string): boolean;
|
|
5
5
|
/** Implements normalize structured linked test entry for the public runtime surface of this module. */
|
|
6
6
|
export declare function normalizeStructuredLinkedTestEntry(kv: Record<string, string>, optionName: "--add" | "--test"): Record<string, string>;
|
package/dist/sdk/test/entry.js
CHANGED
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
* Implements the pm linked test entry command surface and its agent-facing runtime behavior.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
7
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="b29e4416-82ab-585c-9456-718e4699566d")}catch(e){}}();
|
|
8
8
|
import { EXIT_CODE } from "../../core/shared/constants.js";
|
|
9
9
|
import { PmCliError } from "../../core/shared/errors.js";
|
|
10
|
+
import { looksLikeStructuredKeyValueEntry } from "../../core/item/parse.js";
|
|
10
11
|
// Keys accepted inside a structured linked-test entry. `cmd` is an alias for
|
|
11
12
|
// `command`; everything else maps 1:1 to a LinkedTest field.
|
|
12
13
|
/** Public contract for structured linked test keys, shared by SDK and presentation-layer consumers. */
|
|
@@ -32,14 +33,9 @@ export const STRUCTURED_LINKED_TEST_KEYS = [
|
|
|
32
33
|
"note",
|
|
33
34
|
];
|
|
34
35
|
const STRUCTURED_LINKED_TEST_KEY_SET = new Set(STRUCTURED_LINKED_TEST_KEYS);
|
|
35
|
-
|
|
36
|
-
const STRUCTURED_LINKED_TEST_ENTRY_PATTERN = new RegExp(`^(?:[-*+]\\s+)?(?:${STRUCTURED_LINKED_TEST_KEY_PATTERN})\\s*[:=]`, "i");
|
|
37
|
-
/** Implements looks like structured linked test entry for the public runtime surface of this module. */
|
|
36
|
+
/** Classify linked-test plaintext through the shared structured-entry grammar. */
|
|
38
37
|
export function looksLikeStructuredLinkedTestEntry(raw) {
|
|
39
|
-
|
|
40
|
-
return true;
|
|
41
|
-
}
|
|
42
|
-
return STRUCTURED_LINKED_TEST_ENTRY_PATTERN.test(raw);
|
|
38
|
+
return looksLikeStructuredKeyValueEntry(raw, STRUCTURED_LINKED_TEST_KEYS);
|
|
43
39
|
}
|
|
44
40
|
/** Implements normalize structured linked test entry for the public runtime surface of this module. */
|
|
45
41
|
export function normalizeStructuredLinkedTestEntry(kv, optionName) {
|
|
@@ -72,4 +68,4 @@ export function normalizeStructuredLinkedTestEntry(kv, optionName) {
|
|
|
72
68
|
return normalizedKv;
|
|
73
69
|
}
|
|
74
70
|
//# sourceMappingURL=entry.js.map
|
|
75
|
-
//# debugId=
|
|
71
|
+
//# debugId=b29e4416-82ab-585c-9456-718e4699566d
|
|
@@ -234,6 +234,8 @@ declare function readJsonPathValue(root: unknown, fieldPath: string): {
|
|
|
234
234
|
value: unknown;
|
|
235
235
|
};
|
|
236
236
|
declare function evaluateLinkedTestAssertions(linkedTest: LinkedTest, stdout: string, stderr: string): string[];
|
|
237
|
+
/** Return whether a linked command applies a runner-level test-name filter that must match at least one test. */
|
|
238
|
+
declare function commandUsesTestNameFilter(command: string): boolean;
|
|
237
239
|
interface RunLinkedTestsOptions {
|
|
238
240
|
progress?: boolean;
|
|
239
241
|
sourceRoots?: LinkedTestSandboxSourceRoots;
|
|
@@ -303,6 +305,7 @@ export declare const _testOnlyTestCommand: {
|
|
|
303
305
|
buildPmContextMismatchHint: typeof buildPmContextMismatchHint;
|
|
304
306
|
commandInvokesPmCli: typeof commandInvokesPmCli;
|
|
305
307
|
commandInvokesPmTrackerReadCommand: typeof commandInvokesPmTrackerReadCommand;
|
|
308
|
+
commandUsesTestNameFilter: typeof commandUsesTestNameFilter;
|
|
306
309
|
copyIntoSandboxIfPresent: typeof copyIntoSandboxIfPresent;
|
|
307
310
|
countLinkedTestItemFiles: typeof countLinkedTestItemFiles;
|
|
308
311
|
ensureScope: typeof ensureScope;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Implements the pm test command surface and its agent-facing runtime behavior.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
7
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="de5bc6bf-fe31-5643-a809-ee9ca52aac48")}catch(e){}}();
|
|
8
8
|
import { assertInitializedTracker } from "../environment/tracker-preflight.js";
|
|
9
9
|
import { spawn } from "node:child_process";
|
|
10
10
|
import { cp, mkdir, mkdtemp, open, readdir, rm, symlink, } from "node:fs/promises";
|
|
@@ -817,9 +817,17 @@ async function runLinkedTestCommand(command, timeoutMs, env, progressContext, pr
|
|
|
817
817
|
try {
|
|
818
818
|
child = createLinkedTestChild(command, env, stdoutHandle.fd, stderrHandle.fd, cwd);
|
|
819
819
|
}
|
|
820
|
-
|
|
820
|
+
catch (error) {
|
|
821
821
|
await Promise.all([stdoutHandle.close(), stderrHandle.close()]);
|
|
822
|
+
throw error;
|
|
822
823
|
}
|
|
824
|
+
const childClose = waitForLinkedTestChildClose(child);
|
|
825
|
+
let spawnError;
|
|
826
|
+
/* c8 ignore next 5 -- shell spawn error callbacks are non-deterministic across platforms. */
|
|
827
|
+
child.on("error", (error) => {
|
|
828
|
+
spawnError = error.message;
|
|
829
|
+
});
|
|
830
|
+
await Promise.all([stdoutHandle.close(), stderrHandle.close()]);
|
|
823
831
|
closeLinkedTestStdin(child);
|
|
824
832
|
const timers = {
|
|
825
833
|
heartbeat: beginLinkedTestProgress(progressContext, progressMode),
|
|
@@ -828,18 +836,13 @@ async function runLinkedTestCommand(command, timeoutMs, env, progressContext, pr
|
|
|
828
836
|
};
|
|
829
837
|
const requestTermination = createLinkedTestTerminationRequester(child, timers);
|
|
830
838
|
let timedOut = false;
|
|
831
|
-
let spawnError;
|
|
832
|
-
/* c8 ignore next 5 -- shell spawn error callbacks are non-deterministic across platforms. */
|
|
833
|
-
child.on("error", (error) => {
|
|
834
|
-
spawnError = error.message;
|
|
835
|
-
});
|
|
836
839
|
/* c8 ignore next 4 -- callback scheduling timing is non-deterministic under coverage instrumentation. */
|
|
837
840
|
timers.timedOutTimer = setTimeout(() => {
|
|
838
841
|
timedOut = true;
|
|
839
842
|
void requestTermination();
|
|
840
843
|
}, timeoutMs);
|
|
841
844
|
timers.timedOutTimer.unref?.();
|
|
842
|
-
const { code, signal } = await
|
|
845
|
+
const { code, signal } = await childClose;
|
|
843
846
|
clearLinkedTestTimers(timers);
|
|
844
847
|
const [stdout, stderr] = await Promise.all([
|
|
845
848
|
readLinkedTestCapture(stdoutPath),
|
|
@@ -1285,16 +1288,45 @@ const EMPTY_LINKED_TEST_RUN_PATTERNS = [
|
|
|
1285
1288
|
{ code: "no_tests_found", regex: /\bNo tests found\b/i },
|
|
1286
1289
|
{ code: "no_matching_tests", regex: /\bNo matching tests?\b/i },
|
|
1287
1290
|
{ code: "collected_zero_items", regex: /\bcollected 0 items?\b/i },
|
|
1291
|
+
{
|
|
1292
|
+
code: "reported_zero_passes",
|
|
1293
|
+
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,
|
|
1294
|
+
},
|
|
1295
|
+
{
|
|
1296
|
+
code: "reported_zero_tests",
|
|
1297
|
+
regex: /^\s*(?:#|ℹ)?\s*tests\s+0\s*$/imu,
|
|
1298
|
+
},
|
|
1299
|
+
];
|
|
1300
|
+
const POSITIVE_LINKED_TEST_RUN_PATTERNS = [
|
|
1301
|
+
/^\s*(?:#|ℹ)?\s*tests\s+[1-9]\d*\s*$/imu,
|
|
1302
|
+
/^\s*(?:#|ℹ)?\s*pass\s+[1-9]\d*\s*$/imu,
|
|
1303
|
+
/\bTests?\s+[1-9]\d*\s+passed\b/iu,
|
|
1304
|
+
/\b[1-9]\d*\s+passed\b/iu,
|
|
1305
|
+
/\bTests:\s+(?:.*\b)?[1-9]\d*\s+passed\b/iu,
|
|
1288
1306
|
];
|
|
1289
1307
|
function detectEmptyLinkedTestRun(stdout, stderr) {
|
|
1290
1308
|
const combined = `${stdout}\n${stderr}`;
|
|
1291
1309
|
for (const pattern of EMPTY_LINKED_TEST_RUN_PATTERNS) {
|
|
1292
1310
|
if (pattern.regex.test(combined)) {
|
|
1293
|
-
return pattern.code;
|
|
1311
|
+
return { code: pattern.code };
|
|
1294
1312
|
}
|
|
1295
1313
|
}
|
|
1296
1314
|
return null;
|
|
1297
1315
|
}
|
|
1316
|
+
/** Return whether runner output contains a recognized positive executed-test receipt. */
|
|
1317
|
+
function hasPositiveLinkedTestRunReceipt(stdout, stderr) {
|
|
1318
|
+
const combined = `${stdout}\n${stderr}`;
|
|
1319
|
+
return POSITIVE_LINKED_TEST_RUN_PATTERNS.some((pattern) => pattern.test(combined));
|
|
1320
|
+
}
|
|
1321
|
+
/** Return whether a linked command applies a runner-level test-name filter that must match at least one test. */
|
|
1322
|
+
function commandUsesTestNameFilter(command) {
|
|
1323
|
+
const normalized = normalizeCommandForValidation(command);
|
|
1324
|
+
if (/(?:^|\s)(?:--test-name-pattern|--testnamepattern)(?:=|\s)/u.test(normalized)) {
|
|
1325
|
+
return true;
|
|
1326
|
+
}
|
|
1327
|
+
return splitNormalizedCommandSegments(normalized).some((segment) => /(?:^|\s)-t(?:=|\s)/u.test(segment) &&
|
|
1328
|
+
segmentInvokesUnsafeDirectTestRunner(segment));
|
|
1329
|
+
}
|
|
1298
1330
|
function createLinkedTestSandboxLayout(sandboxRoot) {
|
|
1299
1331
|
return {
|
|
1300
1332
|
root: sandboxRoot,
|
|
@@ -1625,8 +1657,8 @@ function buildLinkedTestEmptyRunResult(params) {
|
|
|
1625
1657
|
execution_context: params.executionContext,
|
|
1626
1658
|
stdout: params.execution.stdout,
|
|
1627
1659
|
stderr: params.execution.stderr,
|
|
1628
|
-
error: `Linked test reported an empty test run (${params.emptyRunSignal})
|
|
1629
|
-
"Update test selection
|
|
1660
|
+
error: `Linked test reported an empty test run (${params.emptyRunSignal.code}). ` +
|
|
1661
|
+
"Update the test selection so at least one test executes; unfiltered commands may opt out by omitting --fail-on-empty-test-run.",
|
|
1630
1662
|
};
|
|
1631
1663
|
}
|
|
1632
1664
|
function buildLinkedTestCommandFailureResult(params) {
|
|
@@ -1643,11 +1675,19 @@ function buildLinkedTestCommandFailureResult(params) {
|
|
|
1643
1675
|
};
|
|
1644
1676
|
}
|
|
1645
1677
|
function buildLinkedTestPassedExecutionResult(params) {
|
|
1646
|
-
if (params.options?.failOnEmptyTestRun === true
|
|
1678
|
+
if (params.options?.failOnEmptyTestRun === true ||
|
|
1679
|
+
commandUsesTestNameFilter(params.linkedTest.command ?? "")) {
|
|
1647
1680
|
const emptyRunSignal = detectEmptyLinkedTestRun(params.execution.stdout, params.execution.stderr);
|
|
1648
1681
|
if (emptyRunSignal) {
|
|
1649
1682
|
return buildLinkedTestEmptyRunResult({ ...params, emptyRunSignal });
|
|
1650
1683
|
}
|
|
1684
|
+
if (commandUsesTestNameFilter(params.linkedTest.command ?? "") &&
|
|
1685
|
+
!hasPositiveLinkedTestRunReceipt(params.execution.stdout, params.execution.stderr)) {
|
|
1686
|
+
return buildLinkedTestEmptyRunResult({
|
|
1687
|
+
...params,
|
|
1688
|
+
emptyRunSignal: { code: "missing_positive_execution_receipt" },
|
|
1689
|
+
});
|
|
1690
|
+
}
|
|
1651
1691
|
}
|
|
1652
1692
|
const assertionFailures = evaluateLinkedTestAssertions(params.linkedTest, params.execution.stdout, params.execution.stderr);
|
|
1653
1693
|
if (assertionFailures.length > 0) {
|
|
@@ -2129,6 +2169,7 @@ export const _testOnlyTestCommand = {
|
|
|
2129
2169
|
buildPmContextMismatchHint,
|
|
2130
2170
|
commandInvokesPmCli,
|
|
2131
2171
|
commandInvokesPmTrackerReadCommand,
|
|
2172
|
+
commandUsesTestNameFilter,
|
|
2132
2173
|
copyIntoSandboxIfPresent,
|
|
2133
2174
|
countLinkedTestItemFiles,
|
|
2134
2175
|
ensureScope,
|
|
@@ -2160,4 +2201,4 @@ export const _testOnlyTestCommand = {
|
|
|
2160
2201
|
summarizeRunResultStatuses,
|
|
2161
2202
|
};
|
|
2162
2203
|
//# sourceMappingURL=execution.js.map
|
|
2163
|
-
//# debugId=
|
|
2204
|
+
//# debugId=de5bc6bf-fe31-5643-a809-ee9ca52aac48
|