@unbrained/pm-cli 2026.9.5 → 2026.9.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 (185) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +32 -2
  3. package/dist/cli/main.js +10 -8
  4. package/dist/cli/register-operations.js +13 -8
  5. package/dist/cli-bundle/bundle-manifest.json +150 -158
  6. package/dist/cli-bundle/chunks/chunk-AZDFJGH6.js +9 -0
  7. package/dist/cli-bundle/chunks/{chunk-WWAEGW4V.js → chunk-BAX2BYUW.js} +66 -66
  8. package/dist/cli-bundle/chunks/{chunk-IBYREGSP.js → chunk-CKOL5U7J.js} +51 -51
  9. package/dist/cli-bundle/chunks/{chunk-ZRAQS3UA.js → chunk-JT6XF6GE.js} +2 -2
  10. package/dist/cli-bundle/chunks/{chunk-HUAQITVW.js → chunk-JX4DH4WC.js} +2 -2
  11. package/dist/cli-bundle/chunks/{chunk-QPZHQY2O.js → chunk-NPN5I67J.js} +2 -2
  12. package/dist/cli-bundle/chunks/{chunk-66VPKFP2.js → chunk-OF76PSDQ.js} +2 -2
  13. package/dist/cli-bundle/chunks/chunk-QUWFABDM.js +232 -0
  14. package/dist/cli-bundle/chunks/{chunk-3HBDTJRK.js → chunk-RWWBOWJ5.js} +2 -2
  15. package/dist/cli-bundle/chunks/chunk-WGAOYSYH.js +40 -0
  16. package/dist/cli-bundle/chunks/{chunk-SF6SM6U5.js → chunk-WI2AYPA7.js} +2 -2
  17. package/dist/cli-bundle/chunks/{register-list-query-AYKGODWT.js → register-list-query-5JL52D33.js} +2 -2
  18. package/dist/cli-bundle/chunks/{register-mutation-YZYG53SQ.js → register-mutation-ZPG7KMVP.js} +2 -2
  19. package/dist/cli-bundle/chunks/register-operations-I5W3KMGB.js +2 -0
  20. package/dist/cli-bundle/chunks/{register-setup-UJCRVGBG.js → register-setup-H4SVAVX7.js} +2 -2
  21. package/dist/cli-bundle/focused-chunks/{chunk-QL66QNWG.js → chunk-3BZEOXLT.js} +2 -2
  22. package/dist/cli-bundle/focused-chunks/chunk-AL5ON6K6.js +2 -0
  23. package/dist/cli-bundle/focused-chunks/chunk-CHC2IDXA.js +6 -0
  24. package/dist/cli-bundle/focused-chunks/chunk-CK6QFAPZ.js +20 -0
  25. package/dist/cli-bundle/focused-chunks/{chunk-WOCIPMW7.js → chunk-EZR3GHFE.js} +2 -2
  26. package/dist/cli-bundle/focused-chunks/chunk-H2WJWK7L.js +56 -0
  27. package/dist/cli-bundle/focused-chunks/{chunk-3ASU6WGI.js → chunk-LIJIA4TY.js} +2 -2
  28. package/dist/cli-bundle/focused-chunks/{chunk-H5FOGAVD.js → chunk-M6PBRFSH.js} +2 -2
  29. package/dist/cli-bundle/focused-chunks/{chunk-B2TTQGFG.js → chunk-NTPY5GSO.js} +2 -2
  30. package/dist/cli-bundle/focused-chunks/chunk-P5O7MP5Y.js +2 -0
  31. package/dist/cli-bundle/focused-chunks/chunk-PDMW2E36.js +177 -0
  32. package/dist/cli-bundle/focused-chunks/{chunk-YEF5PQQ2.js → chunk-PEFABF7E.js} +2 -2
  33. package/dist/cli-bundle/focused-chunks/{chunk-KWWCBWFO.js → chunk-QTVCLMSZ.js} +2 -2
  34. package/dist/cli-bundle/focused-chunks/{chunk-4OSHLNUE.js → chunk-QTZRBUYA.js} +2 -2
  35. package/dist/cli-bundle/focused-chunks/chunk-SGQST3PQ.js +12 -0
  36. package/dist/cli-bundle/focused-chunks/chunk-UKHASMMP.js +10 -0
  37. package/dist/cli-bundle/focused-chunks/chunk-ZPEKQHOC.js +2 -0
  38. package/dist/cli-bundle/main.js +20 -20
  39. package/dist/cli-bundle/sdk-authoring.js +1 -1
  40. package/dist/cli-bundle/sdk-contracts.js +2 -2
  41. package/dist/cli-bundle/sdk-core.js +37 -37
  42. package/dist/cli-bundle/sdk-governance.js +1 -1
  43. package/dist/cli-bundle/sdk-graph.js +1 -1
  44. package/dist/cli-bundle/sdk-merge.js +42 -42
  45. package/dist/cli-bundle/sdk-query.js +1 -1
  46. package/dist/cli-bundle/sdk-runtime.js +1 -1
  47. package/dist/cli-bundle/sdk-testing.js +1 -1
  48. package/dist/cli-bundle/sdk.js +9 -9
  49. package/dist/core/extensions/module-graph-snapshot.js +5 -4
  50. package/dist/core/fs/host-environment-errors.d.ts +27 -0
  51. package/dist/core/fs/host-environment-errors.js +142 -0
  52. package/dist/core/output/mutation-projection.js +12 -5
  53. package/dist/core/store/item-metadata-query-index.js +13 -10
  54. package/dist/core/store/item-metadata-read-work.d.ts +19 -0
  55. package/dist/core/store/item-metadata-read-work.js +44 -0
  56. package/dist/core/store/item-store.js +6 -2
  57. package/dist/core/validate/fix-planning.d.ts +1 -1
  58. package/dist/core/validate/fix-planning.js +2 -2
  59. package/dist/core/validate/missing-link-owners.d.ts +1 -1
  60. package/dist/core/validate/missing-link-owners.js +5 -3
  61. package/dist/core/validate/stale-file-classification.d.ts +6 -4
  62. package/dist/core/validate/stale-file-classification.js +20 -5
  63. package/dist/mcp/server.js +12 -5
  64. package/dist/sdk/cli-contracts/flag-contracts.d.ts +2 -0
  65. package/dist/sdk/cli-contracts/flag-contracts.js +7 -5
  66. package/dist/sdk/cli-contracts/flag-lexicon-contracts.js +4 -3
  67. package/dist/sdk/cli-contracts/grammar-contracts.js +11 -2
  68. package/dist/sdk/cli-contracts/runtime-contracts.js +5 -2
  69. package/dist/sdk/cli-contracts/schemas/assurance-definition.d.ts +254 -5
  70. package/dist/sdk/cli-contracts/schemas/assurance-definition.js +52 -2
  71. package/dist/sdk/cli-contracts/tool-parameter-tables.js +18 -6
  72. package/dist/sdk/cli-contracts/tool-schema.d.ts +2 -2
  73. package/dist/sdk/cli-contracts/tool-schema.js +17 -5
  74. package/dist/sdk/context/usage-ledger.d.ts +41 -0
  75. package/dist/sdk/context/usage-ledger.js +186 -0
  76. package/dist/sdk/context-usage.d.ts +18 -9
  77. package/dist/sdk/context-usage.js +37 -112
  78. package/dist/sdk/dependency-provenance.d.ts +7 -0
  79. package/dist/sdk/dependency-provenance.js +14 -4
  80. package/dist/sdk/duplicates.d.ts +2 -0
  81. package/dist/sdk/duplicates.js +3 -2
  82. package/dist/sdk/environment/host-environment-errors.d.ts +5 -27
  83. package/dist/sdk/environment/host-environment-errors.js +4 -137
  84. package/dist/sdk/extension/install-runtime.js +9 -6
  85. package/dist/sdk/extension/install-snapshot.js +7 -6
  86. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +8 -8
  87. package/dist/sdk/governance/assurance-action.d.ts +3 -2
  88. package/dist/sdk/governance/assurance-action.js +23 -11
  89. package/dist/sdk/governance/assurance-runtime.d.ts +2 -0
  90. package/dist/sdk/governance/assurance-runtime.js +6 -3
  91. package/dist/sdk/governance/defect-recurrence-coverage.d.ts +93 -0
  92. package/dist/sdk/governance/defect-recurrence-coverage.js +189 -0
  93. package/dist/sdk/governance/defect-recurrence-graph.d.ts +33 -0
  94. package/dist/sdk/governance/defect-recurrence-graph.js +121 -0
  95. package/dist/sdk/governance/defect-recurrence.d.ts +8 -0
  96. package/dist/sdk/governance/defect-recurrence.js +118 -17
  97. package/dist/sdk/governance/health.d.ts +17 -2
  98. package/dist/sdk/governance/health.js +31 -10
  99. package/dist/sdk/governance/linked-file-report.d.ts +41 -0
  100. package/dist/sdk/governance/linked-file-report.js +72 -0
  101. package/dist/sdk/governance/validate.d.ts +5 -2
  102. package/dist/sdk/governance/validate.js +21 -37
  103. package/dist/sdk/governance.d.ts +2 -0
  104. package/dist/sdk/governance.js +4 -2
  105. package/dist/sdk/history-repair.d.ts +7 -0
  106. package/dist/sdk/history-repair.js +48 -13
  107. package/dist/sdk/lifecycle/close.d.ts +8 -0
  108. package/dist/sdk/lifecycle/close.js +16 -8
  109. package/dist/sdk/lifecycle/create.js +9 -12
  110. package/dist/sdk/lifecycle/update.js +12 -12
  111. package/dist/sdk/lifecycle-policy.d.ts +3 -0
  112. package/dist/sdk/lifecycle-policy.js +2 -2
  113. package/dist/sdk/merge/abandoned-receipts.d.ts +15 -0
  114. package/dist/sdk/merge/abandoned-receipts.js +47 -0
  115. package/dist/sdk/merge/driver.js +4 -2
  116. package/dist/sdk/merge/index.d.ts +1 -0
  117. package/dist/sdk/merge/index.js +2 -2
  118. package/dist/sdk/merge/receipt-operation.d.ts +15 -0
  119. package/dist/sdk/merge/receipt-operation.js +97 -0
  120. package/dist/sdk/merge/receipt-schema.d.ts +20 -0
  121. package/dist/sdk/merge/receipt-schema.js +398 -0
  122. package/dist/sdk/merge/receipts.d.ts +16 -7
  123. package/dist/sdk/merge/receipts.js +54 -340
  124. package/dist/sdk/merge/reconcile.d.ts +2 -0
  125. package/dist/sdk/merge/reconcile.js +11 -4
  126. package/dist/sdk/public-surface.json +1 -1
  127. package/dist/sdk/query/duplicate-candidates.d.ts +7 -0
  128. package/dist/sdk/query/duplicate-candidates.js +71 -0
  129. package/dist/sdk/query/get.js +7 -7
  130. package/dist/sdk/query.d.ts +2 -0
  131. package/dist/sdk/query.js +4 -2
  132. package/dist/sdk/read-output-session.js +5 -3
  133. package/dist/sdk/runtime.js +4 -2
  134. package/dist/sdk/similarity.d.ts +24 -3
  135. package/dist/sdk/similarity.js +50 -55
  136. package/docs/AGENT_GUIDE.md +23 -2
  137. package/docs/BOUNDED_CONTEXT_READS.md +185 -0
  138. package/docs/CLOSE_EVIDENCE_RECOVERY.md +35 -0
  139. package/docs/COMMANDS.md +30 -8
  140. package/docs/CONTEXT_RELEVANCE.md +16 -4
  141. package/docs/DEFECT_RECURRENCE.md +80 -6
  142. package/docs/EXTENSIONS.md +18 -19
  143. package/docs/MERGE_SAFETY.md +50 -0
  144. package/docs/OUTPUT_PROJECTION_CONTRACTS.md +66 -0
  145. package/docs/PR_REVIEW_LOOP.md +18 -3
  146. package/docs/README.md +18 -1
  147. package/docs/RELEASING.md +6 -2
  148. package/docs/SDK_LIFECYCLE.md +27 -0
  149. package/docs/SDK_RUNTIME_BOUNDARIES.md +11 -0
  150. package/docs/generated/FLAG_LEXICON_BUDGETS.md +1 -1
  151. package/marketplace.json +2 -2
  152. package/package.json +6 -3
  153. package/packages/pm-beads/package.json +1 -1
  154. package/packages/pm-calendar/package.json +1 -1
  155. package/packages/pm-command-kit/package.json +1 -1
  156. package/packages/pm-digital-twin/package.json +1 -1
  157. package/packages/pm-governance-audit/README.md +10 -1
  158. package/packages/pm-governance-audit/extensions/governance-audit/dedupe-audit.ts +50 -65
  159. package/packages/pm-governance-audit/extensions/governance-audit/index.ts +2 -2
  160. package/packages/pm-governance-audit/extensions/governance-audit/sdk.ts +1 -0
  161. package/packages/pm-governance-audit/package.json +1 -1
  162. package/packages/pm-guide-shell/package.json +1 -1
  163. package/packages/pm-kanban/package.json +1 -1
  164. package/packages/pm-lifecycle-hooks/package.json +1 -1
  165. package/packages/pm-linked-test-adapters/package.json +1 -1
  166. package/packages/pm-search-advanced/package.json +1 -1
  167. package/packages/pm-templates/package.json +1 -1
  168. package/packages/pm-todos/package.json +1 -1
  169. package/packages/pm-vcs/package.json +1 -1
  170. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  171. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  172. package/dist/cli-bundle/chunks/chunk-LUI4E32I.js +0 -231
  173. package/dist/cli-bundle/chunks/chunk-SVK5Y64D.js +0 -40
  174. package/dist/cli-bundle/chunks/chunk-V5GBD5KV.js +0 -9
  175. package/dist/cli-bundle/chunks/register-operations-KFSCC7QV.js +0 -2
  176. package/dist/cli-bundle/focused-chunks/chunk-24IG6ONG.js +0 -2
  177. package/dist/cli-bundle/focused-chunks/chunk-3ITLIBKS.js +0 -56
  178. package/dist/cli-bundle/focused-chunks/chunk-3NQD5YDG.js +0 -2
  179. package/dist/cli-bundle/focused-chunks/chunk-65N6JHAN.js +0 -2
  180. package/dist/cli-bundle/focused-chunks/chunk-GUBM3RGY.js +0 -20
  181. package/dist/cli-bundle/focused-chunks/chunk-JX3RCVCX.js +0 -2
  182. package/dist/cli-bundle/focused-chunks/chunk-MV752SUC.js +0 -12
  183. package/dist/cli-bundle/focused-chunks/chunk-PKIPMCFS.js +0 -6
  184. package/dist/cli-bundle/focused-chunks/chunk-RJMR2JIF.js +0 -9
  185. package/dist/cli-bundle/focused-chunks/chunk-WIUPWIZR.js +0 -177
@@ -4,7 +4,7 @@
4
4
  * Implements the pm health 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]="9f0077a4-4f0a-540f-9951-2e8a640629c9")}catch(e){}}();
7
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="cee8abad-51d2-5051-9cb8-81447bf2fd50")}catch(e){}}();
8
8
  import fs from "node:fs/promises";
9
9
  import path from "node:path";
10
10
  import { assertInitializedTracker } from "../environment/tracker-preflight.js";
@@ -1056,10 +1056,12 @@ function summarizeHealthCheckDetails(check, limit) {
1056
1056
  return summarize ? summarize(check.details, limit) : check.details;
1057
1057
  }
1058
1058
  /* c8 ignore stop */
1059
+ /** Bound diagnostic detail and warnings while retaining every blocking cause and the authoritative verdict. */
1059
1060
  function applyBriefHealthProjection(result) {
1060
1061
  const warningsSummary = summarizeStringList(result.warnings, BRIEF_HEALTH_DETAIL_LIMIT);
1061
1062
  return {
1062
1063
  ok: result.ok,
1064
+ ...(result.verdict ? { verdict: result.verdict } : {}),
1063
1065
  checks: result.checks.map((check) => ({
1064
1066
  name: check.name,
1065
1067
  status: check.status,
@@ -1082,6 +1084,7 @@ function applyBriefHealthProjection(result) {
1082
1084
  function isSkippedHealthCheck(check) {
1083
1085
  return check.details.skipped === true;
1084
1086
  }
1087
+ /** Emit verdicts for checks that ran, retain blocking causes, and name omitted checks for recovery. */
1085
1088
  function applySummaryHealthProjection(result) {
1086
1089
  const warningsSummary = summarizeStringList(result.warnings, BRIEF_HEALTH_DETAIL_LIMIT);
1087
1090
  const omittedChecks = result.checks
@@ -1089,6 +1092,7 @@ function applySummaryHealthProjection(result) {
1089
1092
  .map((check) => check.name);
1090
1093
  return {
1091
1094
  ok: result.ok,
1095
+ ...(result.verdict ? { verdict: result.verdict } : {}),
1092
1096
  checks: result.checks
1093
1097
  .filter((check) => !isSkippedHealthCheck(check))
1094
1098
  .map((check) => ({
@@ -1974,6 +1978,7 @@ function extractHistoryDriftedCount(historyDriftCheck) {
1974
1978
  const counts = historyDriftCheck.check.details.counts;
1975
1979
  return typeof counts?.drifted === "number" ? counts.drifted : 0;
1976
1980
  }
1981
+ /** Associate warning producers with their owning checks so findings inherit the correct remediation and severity. */
1977
1982
  function buildHealthRemediationSources(params) {
1978
1983
  return {
1979
1984
  settings: params.normalizedSettingsReadWarnings,
@@ -1985,7 +1990,7 @@ function buildHealthRemediationSources(params) {
1985
1990
  extensions: params.extensionCheck.warnings,
1986
1991
  storage: [
1987
1992
  ...params.historyPolicyWarnings,
1988
- ...params.historySummary.over_threshold.map((id) => `history_stream_over_compact_threshold:${id}`),
1993
+ ...params.historySummary.warnings,
1989
1994
  ...params.authorAttributionWarnings,
1990
1995
  ...params.staleInProgressWarnings,
1991
1996
  ...params.provenanceWarnings,
@@ -2064,6 +2069,7 @@ function attachHealthRemediationMaps(params) {
2064
2069
  }
2065
2070
  }
2066
2071
  }
2072
+ /** Attribute drift to merge receipts only when read-only history repair proves their exact current snapshot. */
2067
2073
  async function correlateMergeReceiptHistoryDriftItems(params) {
2068
2074
  if (params.gitWorkspaceRoot === null ||
2069
2075
  params.pendingMergeReceipts.length === 0) {
@@ -2094,15 +2100,15 @@ async function correlateMergeReceiptHistoryDriftItems(params) {
2094
2100
  if (!receipts || receipts.length === 0)
2095
2101
  continue;
2096
2102
  try {
2097
- const repair = await runHistoryRepair(id, {
2103
+ await runHistoryRepair(id, {
2098
2104
  dryRun: true,
2099
2105
  mergeReceiptProof: {
2100
2106
  gitWorkspaceRoot: params.gitWorkspaceRoot,
2101
2107
  receipts,
2102
2108
  },
2103
2109
  }, params.global);
2104
- if (repair.merge_receipt_proof?.trusted)
2105
- attributed.push(id);
2110
+ // An unforced proof-bearing preview rejects untrusted evidence before returning.
2111
+ attributed.push(id);
2106
2112
  }
2107
2113
  catch {
2108
2114
  // Fail closed: generic history-repair guidance remains authoritative.
@@ -2168,6 +2174,8 @@ export async function runHealth(global, options = {}) {
2168
2174
  const normalizedSettingsReadWarnings = [...new Set(settingsReadWarnings)];
2169
2175
  const typeRegistry = resolveItemTypeRegistry(settings, getActiveExtensionRegistrations());
2170
2176
  const strictDirectories = options.strictDirectories === true;
2177
+ const strictExit = options.strictExit === true || options.failOnWarn === true;
2178
+ const requireMergeDrivers = options.requireMergeDrivers === true || strictExit;
2171
2179
  const refreshPolicy = resolveVectorRefreshPolicy(options);
2172
2180
  const directoryState = await scanHealthDirectories(pmRoot, typeRegistry, strictDirectories);
2173
2181
  const settingsValueWarnings = validateSettingsValues(settings);
@@ -2213,13 +2221,13 @@ export async function runHealth(global, options = {}) {
2213
2221
  const provenanceInvalidValues = provenanceResolverHealth.invalid_values;
2214
2222
  const provenanceWarnings = provenanceResolverHealth.warnings;
2215
2223
  const locksCheck = await buildLocksCheck(pmRoot);
2216
- const integrityCheck = skipPolicy.skipIntegrity
2224
+ const integrityCheck = skipPolicy.skipIntegrity && !requireMergeDrivers
2217
2225
  ? {
2218
2226
  ...buildSkippedHealthCheck("integrity"),
2219
2227
  gitWorkspaceRoot: null,
2220
2228
  pendingMergeReceipts: [],
2221
2229
  }
2222
- : await buildIntegrityCheck(pmRoot, typeRegistry.type_to_folder, settings.schema, options.requireMergeDrivers === true, items);
2230
+ : await buildIntegrityCheck(pmRoot, typeRegistry.type_to_folder, settings.schema, requireMergeDrivers, items);
2223
2231
  const historyDriftCheck = skipPolicy.skipDrift
2224
2232
  ? buildSkippedHealthCheck("history_drift")
2225
2233
  : await buildHistoryDriftCheck({
@@ -2296,15 +2304,28 @@ export async function runHealth(global, options = {}) {
2296
2304
  // state (queue backlog, unreachable endpoint, corrupt local state) is advisory:
2297
2305
  // it must never flip overall project health to not-ok. Such warnings are still
2298
2306
  // surfaced in `warnings` and the telemetry check's own `warn` status.
2299
- const blockingWarnings = normalizedWarnings.filter((warning) => !isAdvisoryHealthWarning(warning, options.requireMergeDrivers === true));
2307
+ const blockingWarnings = normalizedWarnings.filter((warning) => !isAdvisoryHealthWarning(warning, requireMergeDrivers));
2300
2308
  const findings = buildHealthFindings({
2301
2309
  warnings: normalizedWarnings,
2302
2310
  checks,
2303
2311
  remediationSources,
2304
- requireMergeDrivers: options.requireMergeDrivers === true,
2312
+ requireMergeDrivers,
2305
2313
  });
2314
+ const failingChecks = new Set(findings.filter((finding) => finding.severity === "gate_failing").map((finding) => finding.check));
2315
+ const warningChecks = new Set(findings.map((finding) => finding.check));
2316
+ for (const check of checks) {
2317
+ check.ok = !failingChecks.has(check.name);
2318
+ if (warningChecks.has(check.name))
2319
+ check.status = "warn";
2320
+ }
2306
2321
  const result = {
2307
2322
  ok: blockingWarnings.length === 0,
2323
+ verdict: {
2324
+ authority: "ok",
2325
+ ...(strictExit ? { strict_exit: true } : {}),
2326
+ ...(requireMergeDrivers ? { require_merge_drivers: true } : {}),
2327
+ exit_code: strictExit && blockingWarnings.length > 0 ? EXIT_CODE.GENERIC_FAILURE : 0,
2328
+ },
2308
2329
  checks,
2309
2330
  warnings: normalizedWarnings,
2310
2331
  findings,
@@ -2344,4 +2365,4 @@ export const _testOnlyHealthCommand = {
2344
2365
  warningCode,
2345
2366
  };
2346
2367
  //# sourceMappingURL=health.js.map
2347
- //# debugId=9f0077a4-4f0a-540f-9951-2e8a640629c9
2368
+ //# debugId=cee8abad-51d2-5051-9cb8-81447bf2fd50
@@ -0,0 +1,41 @@
1
+ import type { RuntimeStatusRegistry } from "../../core/schema/runtime-schema.js";
2
+ import { type MissingLinkedPathRow, type OwnerItemMetadata, type StaleLinkOwnerInput } from "../../core/validate/missing-link-owners.js";
3
+ import type { ClassifiedStaleLinkedPath } from "../../core/validate/stale-file-classification.js";
4
+ /** Repair evidence for one path; nested holders use the same ceiling as paths. */
5
+ export interface LinkedFileRepairRow extends MissingLinkedPathRow {
6
+ /** Same-basename possibilities, not verified relocation instructions. */
7
+ candidates: string[];
8
+ /** Whether the classifier withheld additional possible destinations. */
9
+ candidates_truncated: boolean;
10
+ /** Unique holder and field pairs referencing this path before projection. */
11
+ link_count: number;
12
+ /** Whether the bounded holder list omits any link. */
13
+ items_truncated: boolean;
14
+ /** Number of links on nonterminal holders for this path. */
15
+ active_link_count: number;
16
+ }
17
+ /** Full-population counters and a bounded, consistently structured worklist. */
18
+ export interface LinkedFileRepairReport {
19
+ /** Distinct stale path count, also the unprojected worklist row count. */
20
+ missing_linked_path_rows_count: number;
21
+ /** Paths with owners and candidates joined before either is truncated. */
22
+ missing_linked_path_rows: LinkedFileRepairRow[];
23
+ /** Whether any complete path row is omitted. */
24
+ missing_linked_path_rows_truncated: boolean;
25
+ /** Unique path, holder and field triples across every lifecycle state. */
26
+ missing_linked_links_count: number;
27
+ /** Missing links whose holders are actionable under the workspace registry. */
28
+ active_missing_linked_links_count: number;
29
+ /** Missing links on holders with the configured close status. */
30
+ legacy_closed_missing_linked_links_count: number;
31
+ /** Missing links on all terminal holders, including the closed subset. */
32
+ legacy_terminal_missing_linked_links_count: number;
33
+ /** Distinct paths referenced by at least one active holder. */
34
+ active_missing_linked_paths_count: number;
35
+ }
36
+ /**
37
+ * Build an active-first repair worklist with deterministic path/holder sorting. Unknown
38
+ * statuses and absent holder metadata remain actionable. Infinity explicitly
39
+ * restores every path and holder; finite limits cap both dimensions equally.
40
+ */
41
+ export declare function buildLinkedFileRepairReport(links: readonly StaleLinkOwnerInput[], classifications: readonly ClassifiedStaleLinkedPath[], lookup: (id: string) => OwnerItemMetadata | undefined, registry: RuntimeStatusRegistry, limit?: number): LinkedFileRepairReport;
@@ -0,0 +1,72 @@
1
+ /**
2
+ * @module sdk/governance/linked-file-report
3
+ *
4
+ * Joins missing-file evidence with holder lifecycle before projecting a bounded
5
+ * repair worklist. Counts always describe the complete supplied evidence.
6
+ */
7
+
8
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="d62bc6ec-c345-5196-93b8-280b5484b18a")}catch(e){}}();
9
+ import { normalizeStatusInput } from "../../core/item/status.js";
10
+ import { buildMissingLinkedPathRows, } from "../../core/validate/missing-link-owners.js";
11
+ /**
12
+ * Build an active-first repair worklist with deterministic path/holder sorting. Unknown
13
+ * statuses and absent holder metadata remain actionable. Infinity explicitly
14
+ * restores every path and holder; finite limits cap both dimensions equally.
15
+ */
16
+ export function buildLinkedFileRepairReport(links, classifications, lookup, registry, limit = 40) {
17
+ const ceiling = limit === Infinity
18
+ ? Infinity
19
+ : Number.isFinite(limit)
20
+ ? Math.max(0, Math.floor(limit))
21
+ : 40;
22
+ const byPath = new Map(classifications.map((entry) => [entry.path, entry]));
23
+ const report = {
24
+ missing_linked_path_rows_count: 0,
25
+ missing_linked_path_rows: [],
26
+ missing_linked_path_rows_truncated: false,
27
+ missing_linked_links_count: 0,
28
+ active_missing_linked_links_count: 0,
29
+ legacy_closed_missing_linked_links_count: 0,
30
+ legacy_terminal_missing_linked_links_count: 0,
31
+ active_missing_linked_paths_count: 0,
32
+ };
33
+ const rows = buildMissingLinkedPathRows(links, lookup).map((row) => {
34
+ const active = [];
35
+ const terminal = [];
36
+ for (const owner of row.items) {
37
+ const status = normalizeStatusInput(owner.status, registry) ?? owner.status;
38
+ if (registry.terminal_statuses.has(status)) {
39
+ terminal.push(owner);
40
+ if (status === registry.close_status)
41
+ report.legacy_closed_missing_linked_links_count += 1;
42
+ }
43
+ else {
44
+ active.push(owner);
45
+ }
46
+ }
47
+ report.missing_linked_links_count += row.items.length;
48
+ report.active_missing_linked_links_count += active.length;
49
+ report.legacy_terminal_missing_linked_links_count += terminal.length;
50
+ if (active.length > 0)
51
+ report.active_missing_linked_paths_count += 1;
52
+ const classification = byPath.get(row.path);
53
+ return {
54
+ ...row,
55
+ candidates: classification?.candidates ?? [],
56
+ candidates_truncated: classification?.candidates_truncated ?? false,
57
+ link_count: row.items.length,
58
+ active_link_count: active.length,
59
+ items: [...active, ...terminal].slice(0, ceiling),
60
+ items_truncated: row.items.length > ceiling,
61
+ };
62
+ });
63
+ rows.sort((left, right) => Number(right.active_link_count > 0) -
64
+ Number(left.active_link_count > 0) ||
65
+ left.path.localeCompare(right.path));
66
+ report.missing_linked_path_rows_count = rows.length;
67
+ report.missing_linked_path_rows = rows.slice(0, ceiling);
68
+ report.missing_linked_path_rows_truncated = rows.length > ceiling;
69
+ return report;
70
+ }
71
+ //# sourceMappingURL=linked-file-report.js.map
72
+ //# debugId=d62bc6ec-c345-5196-93b8-280b5484b18a
@@ -151,7 +151,9 @@ export interface ValidateCountsResult extends Omit<ValidateResult, "fixes"> {
151
151
  * Build the counts-only validate projection without mutating the full result.
152
152
  * The check envelope and warning codes remain available for gates, while every
153
153
  * per-item diagnostic/fix array is removed recursively and scalar counts,
154
- * totals, statuses, and truncation markers are preserved.
154
+ * totals, statuses, and true truncation markers are preserved. False truncation
155
+ * markers are omitted; this sparse convention does not suppress other false or
156
+ * zero values, and the projection receipt still declares omitted diagnostic rows.
155
157
  */
156
158
  export declare function projectValidateCounts(result: ValidateResult): ValidateCountsResult;
157
159
  interface ValidateMetadataPolicy {
@@ -226,7 +228,8 @@ declare function buildLifecycleCheck(items: ItemWithBody[], includeStaleBlockers
226
228
  warnings: string[];
227
229
  terminalParentFixRows: TerminalParentFixRow[];
228
230
  };
229
- declare function buildFilesCheck(items: ItemWithBody[], workspaceRoot: string, pmRoot: string, fileScanMode: ValidateFileScanMode, includePmInternals: boolean, verboseFileLists: boolean): Promise<{
231
+ /** Join live filesystem evidence with holder lifecycle, retaining historical rows while warning only on active missing links. */
232
+ declare function buildFilesCheck(items: ItemWithBody[], workspaceRoot: string, pmRoot: string, fileScanMode: ValidateFileScanMode, includePmInternals: boolean, verboseFileLists: boolean, statusRegistry?: RuntimeStatusRegistry): Promise<{
230
233
  check: ValidateCheck;
231
234
  warnings: string[];
232
235
  staleLinkPruneRows: StaleLinkPruneRow[];
@@ -4,7 +4,7 @@
4
4
  * Implements the pm validate 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]="0d466293-869d-54bb-b07d-facb3be51e8c")}catch(e){}}();
7
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="898c4c05-70ab-5ea4-851e-9e224a85afc4")}catch(e){}}();
8
8
  import { assertInitializedTracker } from "../environment/tracker-preflight.js";
9
9
  import fs from "node:fs/promises";
10
10
  import { realpathSync } from "node:fs";
@@ -18,7 +18,7 @@ import { isFileMissingError } from "../../core/fs/fs-utils.js";
18
18
  import { normalizeStatusInput } from "../../core/item/status.js";
19
19
  import { resolveItemTypeRegistry } from "../../core/item/type-registry.js";
20
20
  import { resolveRuntimeStatusRegistry, } from "../../core/schema/runtime-schema.js";
21
- import { DEFAULT_VALIDATE_CLOSURE_LIKE_METADATA_FIELD_PATTERNS, DEFAULT_VALIDATE_STALE_BLOCKER_REASON_PATTERNS, EXIT_CODE, PM_DIRNAME, } from "../../core/shared/constants.js";
21
+ import { DEFAULT_VALIDATE_CLOSURE_LIKE_METADATA_FIELD_PATTERNS, DEFAULT_VALIDATE_STALE_BLOCKER_REASON_PATTERNS, EXIT_CODE, PM_DIRNAME, SETTINGS_DEFAULTS, } from "../../core/shared/constants.js";
22
22
  import { PmCliError } from "../../core/shared/errors.js";
23
23
  import { toNonEmptyStringOrUndefined } from "../../core/shared/primitives.js";
24
24
  import { nowIso } from "../../core/shared/time.js";
@@ -30,11 +30,11 @@ import { findDuplicateIssueCodes, } from "../../core/governance/issue-codes.js";
30
30
  import { buildMissingByTypeCounts, } from "../../core/validate/missing-by-type.js";
31
31
  import { classifyStaleLinkedPaths, summarizeStaleLinkedPathClassifications, } from "../../core/validate/stale-file-classification.js";
32
32
  import { isRemoteLinkedArtifactReference } from "../../core/validate/linked-artifact-reference.js";
33
- import { buildMissingLinkedPathRows, summarizeMissingLinkedPathRows, } from "../../core/validate/missing-link-owners.js";
34
33
  import { assertProjectionModeChoice, } from "../output-projection.js";
35
34
  import { collectDanglingDependencyReferences } from "../graph/assembly.js";
36
35
  import { auditMergeAttributeFence, auditMergeDriverConfiguration, findGitWorkspaceRoot, resolveProjectMergeTypeFolders, } from "../merge/install.js";
37
36
  import { inspectMergeReceiptEvidence, partitionMergeReceipts, } from "../merge/receipts.js";
37
+ import { buildLinkedFileRepairReport } from "./linked-file-report.js";
38
38
  import { scanStorageIntegrity } from "./storage-integrity.js";
39
39
  import { scanTrackedRuntimeCache } from "./tracked-runtime-cache.js";
40
40
  import { scanHistoryAuthorAttribution } from "../author-attribution.js";
@@ -166,10 +166,11 @@ const DIAGNOSTIC_LIST_SUMMARY_LIMIT = 5;
166
166
  const LINKED_ARTIFACT_MAX_PATH_LENGTH = 4096;
167
167
  const LINKED_ARTIFACT_MAX_SEGMENT_LENGTH = 255;
168
168
  const execFileAsync = promisify(execFile);
169
+ /** Recursively omit diagnostic arrays and false truncation markers while preserving zero counts and ordinary false values. */
169
170
  function projectValidateCountsRecord(record) {
170
171
  const projected = {};
171
172
  for (const [key, value] of Object.entries(record)) {
172
- if (Array.isArray(value)) {
173
+ if (Array.isArray(value) || (value === false && key.endsWith("_truncated"))) {
173
174
  continue;
174
175
  }
175
176
  projected[key] =
@@ -183,7 +184,9 @@ function projectValidateCountsRecord(record) {
183
184
  * Build the counts-only validate projection without mutating the full result.
184
185
  * The check envelope and warning codes remain available for gates, while every
185
186
  * per-item diagnostic/fix array is removed recursively and scalar counts,
186
- * totals, statuses, and truncation markers are preserved.
187
+ * totals, statuses, and true truncation markers are preserved. False truncation
188
+ * markers are omitted; this sparse convention does not suppress other false or
189
+ * zero values, and the projection receipt still declares omitted diagnostic rows.
187
190
  */
188
191
  export function projectValidateCounts(result) {
189
192
  return {
@@ -1573,7 +1576,8 @@ function buildLifecycleCheck(items, includeStaleBlockers, dependencyCycleSeverit
1573
1576
  }
1574
1577
  /* c8 ignore stop */
1575
1578
  /* c8 ignore start -- files-check candidate filtering/classification permutations are covered by file-audit integration suites */
1576
- async function buildFilesCheck(items, workspaceRoot, pmRoot, fileScanMode, includePmInternals, verboseFileLists) {
1579
+ /** Join live filesystem evidence with holder lifecycle, retaining historical rows while warning only on active missing links. */
1580
+ async function buildFilesCheck(items, workspaceRoot, pmRoot, fileScanMode, includePmInternals, verboseFileLists, statusRegistry = resolveRuntimeStatusRegistry(SETTINGS_DEFAULTS.schema)) {
1577
1581
  const linkedPathState = await collectLinkedPathScanState(items, workspaceRoot);
1578
1582
  const itemsById = new Map(items.map((item) => [item.id, item]));
1579
1583
  const uniqueMissingLinkedPaths = [
@@ -1601,9 +1605,6 @@ async function buildFilesCheck(items, workspaceRoot, pmRoot, fileScanMode, inclu
1601
1605
  if (partition.strictModeForcesPmInternals) {
1602
1606
  warnings.push("validate_files_tracked_all_strict_forces_pm_internals");
1603
1607
  }
1604
- if (uniqueMissingLinkedPaths.length > 0) {
1605
- warnings.push(`validate_files_missing_linked_paths:${uniqueMissingLinkedPaths.length}`);
1606
- }
1607
1608
  if (orphanedFiles.length > 0) {
1608
1609
  warnings.push(`validate_files_orphaned_paths:${orphanedFiles.length}`);
1609
1610
  }
@@ -1617,29 +1618,11 @@ async function buildFilesCheck(items, workspaceRoot, pmRoot, fileScanMode, inclu
1617
1618
  : summarizeFileList(summarizeOrphanedPathRows(orphanedPathRows), false)
1618
1619
  .values;
1619
1620
  const summarizedClassifications = summarizeFileList(summarizeStaleLinkedPathClassifications(classifiedStalePaths), verboseFileLists);
1620
- // Owner attribution for missing linked paths (GH-210): per-path rows naming
1621
- // the owning item(s) so cleanup is evidence-based instead of requiring a
1622
- // reverse lookup. Full structured objects under --verbose-file-lists; compact
1623
- // `path:classification owner=… field=…` one-liners (capped) otherwise — same
1624
- // full/summary split as the other file-check lists (file_list_detail_mode).
1625
- const missingLinkedPathRows = staleLinkPruneRows.map((row) => ({
1626
- item_id: row.item_id,
1627
- path: row.path,
1628
- link_kind: row.link_kind,
1629
- classification: row.classification,
1630
- }));
1631
- const ownerRows = buildMissingLinkedPathRows(missingLinkedPathRows, (id) => {
1632
- const owner = itemsById.get(id);
1633
- return owner
1634
- ? { type: owner.type, title: owner.title, status: owner.status }
1635
- : undefined;
1636
- });
1637
- // Default to token-efficient compact one-liners; expose the full structured
1638
- // rows (the GH-210 JSON shape) under --verbose-file-lists.
1639
- const ownerRowDetail = verboseFileLists
1640
- ? ownerRows
1641
- : summarizeFileList(summarizeMissingLinkedPathRows(ownerRows), false)
1642
- .values;
1621
+ const missingLinkedPathRows = staleLinkPruneRows;
1622
+ const repairReport = buildLinkedFileRepairReport(missingLinkedPathRows, classifiedStalePaths, (id) => itemsById.get(id), statusRegistry, verboseFileLists ? Infinity : FILE_LIST_SUMMARY_LIMIT);
1623
+ if (repairReport.active_missing_linked_paths_count > 0) {
1624
+ warnings.push(`validate_files_missing_linked_paths:${repairReport.active_missing_linked_paths_count}`);
1625
+ }
1643
1626
  return {
1644
1627
  check: {
1645
1628
  name: "files",
@@ -1678,11 +1661,11 @@ async function buildFilesCheck(items, workspaceRoot, pmRoot, fileScanMode, inclu
1678
1661
  missing_linked_paths: summarizedMissing.values,
1679
1662
  missing_linked_paths_truncated: summarizedMissing.truncated,
1680
1663
  missing_linked_paths_moved_count: movedStalePathCount,
1681
- missing_linked_paths_deleted_count: uniqueMissingLinkedPaths.length - movedStalePathCount,
1664
+ missing_linked_paths_deleted_count: classifiedStalePaths.filter((entry) => entry.classification === "deleted").length,
1665
+ missing_linked_paths_malformed_count: classifiedStalePaths.filter((entry) => entry.classification === "malformed").length,
1682
1666
  missing_linked_path_classifications: summarizedClassifications.values,
1683
1667
  missing_linked_path_classifications_truncated: summarizedClassifications.truncated,
1684
- missing_linked_path_rows_count: ownerRows.length,
1685
- missing_linked_path_rows: ownerRowDetail,
1668
+ ...repairReport,
1686
1669
  orphaned_paths_count: orphanedFiles.length,
1687
1670
  orphaned_paths_total: summarizedOrphaned.total,
1688
1671
  orphaned_paths: summarizedOrphaned.values,
@@ -2076,6 +2059,7 @@ function recordValidateCheck(state, built, fixHintsEnabled) {
2076
2059
  state.checks.push(built.check);
2077
2060
  state.warnings.push(...built.warnings);
2078
2061
  }
2062
+ /** Run only selected checks against one resolved workspace context and collect their typed remediation evidence. */
2079
2063
  async function executeRequestedValidateChecks(params) {
2080
2064
  const state = {
2081
2065
  checks: [],
@@ -2108,7 +2092,7 @@ async function executeRequestedValidateChecks(params) {
2108
2092
  recordValidateCheck(state, buildDependencyReferencesCheck(params.items, fullDiagnostics, params.statusRegistry), fixHintsEnabled);
2109
2093
  }
2110
2094
  if (params.requestedChecks.has("files")) {
2111
- const built = await buildFilesCheck(params.items, params.workspaceRoot, params.pmRoot, params.fileScanMode, Boolean(params.options.includePmInternals), Boolean(params.options.verboseFileLists));
2095
+ const built = await buildFilesCheck(params.items, params.workspaceRoot, params.pmRoot, params.fileScanMode, Boolean(params.options.includePmInternals), Boolean(params.options.verboseFileLists), params.statusRegistry);
2112
2096
  state.staleLinkPruneRows = built.staleLinkPruneRows;
2113
2097
  recordValidateCheck(state, built, fixHintsEnabled);
2114
2098
  }
@@ -2275,4 +2259,4 @@ export async function runValidate(options, global, services = {}) {
2275
2259
  }
2276
2260
  /* c8 ignore stop */
2277
2261
  //# sourceMappingURL=validate.js.map
2278
- //# debugId=0d466293-869d-54bb-b07d-facb3be51e8c
2262
+ //# debugId=898c4c05-70ab-5ea4-851e-9e224a85afc4
@@ -13,6 +13,7 @@ export * from "./governance/assurance-presets.js";
13
13
  export * from "./governance/assurance-runtime.js";
14
14
  export * from "./governance/boundary-fixtures.js";
15
15
  export * from "./governance/defect-recurrence.js";
16
+ export * from "./governance/defect-recurrence-coverage.js";
16
17
  export { runReindex, type ReindexOptions, type ReindexResult, } from "./governance/reindex.js";
17
18
  export * from "./governance/stale-work.js";
18
19
  export * from "./governance/storage-integrity.js";
@@ -20,6 +21,7 @@ export * from "./governance/status-role-diagnostics.js";
20
21
  export * from "./governance/workspace-position.js";
21
22
  export { runUpgrade, type UpgradeCliResult, type UpgradeCommandOptions, type UpgradeCommandRunner, type UpgradeCommandRunnerResult, type UpgradePackageResult, type UpgradeResult, } from "./governance/upgrade.js";
22
23
  export * from "./governance/validate.js";
24
+ export * from "./governance/linked-file-report.js";
23
25
  export * from "./author-attribution.js";
24
26
  export * from "./mutation-guard.js";
25
27
  export * from "./governance/provenance-health.js";
@@ -4,7 +4,7 @@
4
4
  * Provides validation, health, integrity, and mutation-policy primitives.
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]="ff251826-36f0-5b92-bee8-ca71d265986c")}catch(e){}}();
7
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="5e2aaa83-a109-5e44-b9b3-316501ffadf4")}catch(e){}}();
8
8
  export * from "./governance/gc.js";
9
9
  export * from "./governance/health.js";
10
10
  export * from "./governance/extension-host-version.js";
@@ -15,6 +15,7 @@ export * from "./governance/assurance-presets.js";
15
15
  export * from "./governance/assurance-runtime.js";
16
16
  export * from "./governance/boundary-fixtures.js";
17
17
  export * from "./governance/defect-recurrence.js";
18
+ export * from "./governance/defect-recurrence-coverage.js";
18
19
  export { runReindex, } from "./governance/reindex.js";
19
20
  export * from "./governance/stale-work.js";
20
21
  export * from "./governance/storage-integrity.js";
@@ -22,9 +23,10 @@ export * from "./governance/status-role-diagnostics.js";
22
23
  export * from "./governance/workspace-position.js";
23
24
  export { runUpgrade, } from "./governance/upgrade.js";
24
25
  export * from "./governance/validate.js";
26
+ export * from "./governance/linked-file-report.js";
25
27
  export * from "./author-attribution.js";
26
28
  export * from "./mutation-guard.js";
27
29
  export * from "./governance/provenance-health.js";
28
30
  export * from "./similarity.js";
29
31
  //# sourceMappingURL=governance.js.map
30
- //# debugId=ff251826-36f0-5b92-bee8-ca71d265986c
32
+ //# debugId=5e2aaa83-a109-5e44-b9b3-316501ffadf4
@@ -36,6 +36,13 @@ export interface HistoryRepairCommandOptions {
36
36
  gitWorkspaceRoot: string | null;
37
37
  receipts: MergeDecisionReceipt[];
38
38
  }>;
39
+ /** Restored-origin evidence checked against the same item snapshot protected by the rewrite transaction. */
40
+ mergeAbandonmentProof?: {
41
+ /** Git workspace containing the tracked item. */
42
+ gitWorkspaceRoot: string;
43
+ /** Authoritative receipt whose original rebase state must match. */
44
+ receipt: MergeDecisionReceipt;
45
+ };
39
46
  }
40
47
  /** Documents the history repair result payload exchanged by command, SDK, and package integrations. */
41
48
  export interface HistoryRepairResult {
@@ -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]="d397bc22-d0ae-5c3d-a1bf-7f4c38ca31be")}catch(e){}}();
7
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="9f91f2ee-cebe-592c-92ee-717301fa96ca")}catch(e){}}();
8
8
  import { assertInitializedTracker } from "./environment/tracker-preflight.js";
9
9
  import { salvageHistoryTail } from "./history/salvage.js";
10
10
  import { realpath } from "node:fs/promises";
@@ -30,6 +30,7 @@ import { listAllItemMetadataWithBody, readLocatedItem, } from "../core/store/ite
30
30
  import { resolvePmRoot } from "../core/store/paths.js";
31
31
  import { readSettings } from "../core/store/settings.js";
32
32
  import { resolveHistorySubject } from "./history-redact.js";
33
+ import { isOriginalGitStateRestored } from "./merge/receipt-operation.js";
33
34
  import { inspectMergeReceiptEvidence, summarizeMergeReceipt, } from "./merge/receipts.js";
34
35
  import { encodeItemScalarDecisionValue, hashItemScalarDecisionValue, } from "./merge/three-way.js";
35
36
  import { listInvalidProvenanceHistoryStreamIds, normalizeInvalidHistoryProvenance, } from "./governance/provenance-health.js";
@@ -229,6 +230,24 @@ async function verifyMergeReceiptProof(params) {
229
230
  receipt_ids: [],
230
231
  };
231
232
  }
233
+ /** Refuse a restored-origin audit when its exact item snapshot or clean history has changed. */
234
+ async function assertHistoryRepairAbandonmentProof(proof, context, chainOk, reanchor) {
235
+ if (proof === undefined)
236
+ return;
237
+ const { receipt, gitWorkspaceRoot } = proof;
238
+ const canonicalPath = context.currentItemPath === null ? null
239
+ : path.relative(gitWorkspaceRoot, context.currentItemPath).split(path.sep).join("/");
240
+ if (!chainOk || reanchor.entriesRehashed !== 0 || reanchor.entriesPatchRepaired !== 0 || context.matchedChainBefore !== true ||
241
+ context.currentItemRawBeforeLock === null || canonicalPath !== receipt.item_path ||
242
+ receipt.operation === undefined ||
243
+ !await isOriginalGitStateRestored(gitWorkspaceRoot, receipt.item_path, receipt.operation, context.currentItemRawBeforeLock)) {
244
+ throw new PmCliError("Restored-origin receipt evidence no longer proves the exact clean item snapshot.", EXIT_CODE.CONFLICT, {
245
+ code: "merge_reconcile_receipt_evidence_untrusted",
246
+ recovery: { suggested_retry: "pm merge reconcile --dry-run" },
247
+ });
248
+ }
249
+ }
250
+ /** Resolve and verify receipt candidates against loaded item evidence, refusing untrusted preview and apply requests alike. */
232
251
  async function resolveHistoryRepairMergeEvidence(params) {
233
252
  const requestedReceiptIds = new Set(params.options.mergeReceiptProof?.receipts
234
253
  .filter((receipt) => receipt.item_id === params.subjectId)
@@ -256,7 +275,6 @@ async function resolveHistoryRepairMergeEvidence(params) {
256
275
  : undefined;
257
276
  if (mergeReceiptProof &&
258
277
  !mergeReceiptProof.trusted &&
259
- params.options.dryRun !== true &&
260
278
  params.options.force !== true) {
261
279
  throw new PmCliError(`Merge receipt evidence for ${params.subjectId} does not prove the exact item snapshot (${mergeReceiptProof.reason}).`, EXIT_CODE.CONFLICT, {
262
280
  code: "merge_reconcile_receipt_evidence_untrusted",
@@ -359,6 +377,7 @@ function collectHistoryRepairWarnings(changed, skippedOps, reconciliation) {
359
377
  }
360
378
  return warnings;
361
379
  }
380
+ /** Revalidate item/history bytes and restored-origin Git proof under the rewrite lock before persisting. */
362
381
  async function applyHistoryRepairRewrite(params) {
363
382
  return executeHistoryRewrite({
364
383
  pmRoot: params.pmRoot,
@@ -366,16 +385,19 @@ async function applyHistoryRepairRewrite(params) {
366
385
  settings: params.settings,
367
386
  typeRegistry: params.typeRegistry,
368
387
  historyRawBeforeLock: params.historyRawBeforeLock,
369
- currentItemRawBeforeLock: params.currentItemRawBeforeLock,
388
+ currentItemRawBeforeLock: params.itemReplayContext.currentItemRawBeforeLock,
370
389
  operation: "history-repair",
371
390
  author: params.author,
372
391
  force: params.force,
373
- itemDocument: params.loadedItem?.document ?? null,
374
- applyRewrite: async ({ historyRawUnderLock }) => writeHistoryRawWithRollback({
375
- historyPath: params.historyPath,
376
- nextHistoryRaw: historyEntriesToRaw(params.rewrittenEntries),
377
- historyRawUnderLock,
378
- }),
392
+ itemDocument: params.itemReplayContext.loadedItem?.document ?? null,
393
+ applyRewrite: async ({ historyRawUnderLock }) => {
394
+ await assertHistoryRepairAbandonmentProof(params.mergeAbandonmentProof, params.itemReplayContext, params.chainOk, params.reanchor);
395
+ await writeHistoryRawWithRollback({
396
+ historyPath: params.historyPath,
397
+ nextHistoryRaw: historyEntriesToRaw(params.rewrittenEntries),
398
+ historyRawUnderLock,
399
+ });
400
+ },
379
401
  applyPostRewrite: async () => runActiveOnWriteHooks({
380
402
  path: params.historyPath,
381
403
  scope: "project",
@@ -395,6 +417,16 @@ export async function runHistoryRepair(id, options, global) {
395
417
  }
396
418
  return repairHistorySubject({ pmRoot, subject, settings, typeRegistry, options });
397
419
  }
420
+ /** Preserve explicit audit requests while reusing exact restored-origin dispositions in verified history. */
421
+ function shouldAppendForcedHistoryAudit(options, historyEntries) {
422
+ // Receipt persistence can fail after the audited history transaction commits.
423
+ // Only an identical disposition satisfies a retry; unrelated audits do not.
424
+ const alreadyRecorded = options.mergeAbandonmentProof !== undefined &&
425
+ options.auditContext !== undefined &&
426
+ historyEntries.some((entry) => entry.op === "merge_reconcile" &&
427
+ stableStringify(entry.context) === stableStringify(options.auditContext));
428
+ return options.forceAuditEntry === true && !alreadyRecorded;
429
+ }
398
430
  /** Reanchor or reconcile one resolved subject, separately from byte-preserving salvage. */
399
431
  async function repairHistorySubject(params) {
400
432
  const { pmRoot, subject, settings, typeRegistry, options } = params;
@@ -425,6 +457,7 @@ async function repairHistorySubject(params) {
425
457
  .filter((version) => version !== undefined));
426
458
  const reanchor = reanchorHistoryEntries(provenanceNormalization.entries, itemHashVersion);
427
459
  const itemReplayContext = await loadHistoryRepairItemReplay(subject, settings, historyEntries, itemHashVersion);
460
+ await assertHistoryRepairAbandonmentProof(options.mergeAbandonmentProof, itemReplayContext, chainBefore.ok, reanchor);
428
461
  const finalReplay = reanchor.finalDocument;
429
462
  const finalReplayHashes = replayHashVerificationCandidates(finalReplay, itemHashVersion);
430
463
  let reconcileNeeded = false;
@@ -449,7 +482,7 @@ async function repairHistorySubject(params) {
449
482
  reanchor.entriesRehashed > 0,
450
483
  reanchor.entriesPatchRepaired > 0,
451
484
  reconcileNeeded,
452
- options.forceAuditEntry === true,
485
+ shouldAppendForcedHistoryAudit(options, historyEntries),
453
486
  provenanceNormalization.receipt.changed,
454
487
  ].some(Boolean);
455
488
  const author = resolveAuthor(options.author, settings.author_default);
@@ -490,10 +523,12 @@ async function repairHistorySubject(params) {
490
523
  settings,
491
524
  typeRegistry,
492
525
  historyRawBeforeLock,
493
- currentItemRawBeforeLock: itemReplayContext.currentItemRawBeforeLock,
526
+ itemReplayContext,
527
+ mergeAbandonmentProof: options.mergeAbandonmentProof,
528
+ chainOk: chainBefore.ok,
529
+ reanchor,
494
530
  author,
495
531
  force: options.force,
496
- loadedItem: itemReplayContext.loadedItem,
497
532
  historyPath: subject.historyPath,
498
533
  rewrittenEntries,
499
534
  })));
@@ -620,4 +655,4 @@ export async function runHistoryRepairAll(options, global) {
620
655
  };
621
656
  }
622
657
  //# sourceMappingURL=history-repair.js.map
623
- //# debugId=d397bc22-d0ae-5c3d-a1bf-7f4c38ca31be
658
+ //# debugId=9f91f2ee-cebe-592c-92ee-717301fa96ca
@@ -1,3 +1,9 @@
1
+ /**
2
+ * @module sdk/lifecycle/close
3
+ *
4
+ * Implements the pm close command surface and its agent-facing runtime behavior.
5
+ */
6
+ import type { PmCliErrorRecoveryPayload } from "../../core/shared/errors.js";
1
7
  import { type GlobalOptions } from "../runtime-primitives.js";
2
8
  import { collectBlockedByIds } from "../actionability.js";
3
9
  import { type CloseOperationOptions, type CloseOperationResult } from "../lifecycle-policy.js";
@@ -28,6 +34,8 @@ export interface CloseResult {
28
34
  item: Record<string, unknown>;
29
35
  /** Changed metadata fields. */
30
36
  changed_fields: string[];
37
+ /** Evidence-only update guidance when acceptance fields are missing. */
38
+ recovery?: PmCliErrorRecoveryPayload;
31
39
  /** Validation and lifecycle receipts. */
32
40
  warnings: string[];
33
41
  }