@unbrained/pm-cli 2026.8.14 → 2026.8.15

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 (145) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +25 -2
  3. package/dist/cli/commander-usage.js +17 -4
  4. package/dist/cli/error-guidance.d.ts +2 -0
  5. package/dist/cli/error-guidance.js +52 -25
  6. package/dist/cli/main.js +3 -2
  7. package/dist/cli/register-mutation.js +4 -2
  8. package/dist/cli/register-operations.js +5 -5
  9. package/dist/cli-bundle/bundle-manifest.json +150 -150
  10. package/dist/cli-bundle/chunks/{chunk-TXMEUUUY.js → chunk-24GAV7XL.js} +2 -2
  11. package/dist/cli-bundle/chunks/{chunk-TMJ7URWS.js → chunk-AIM6VT2N.js} +9 -9
  12. package/dist/cli-bundle/chunks/chunk-DSWDLTDR.js +197 -0
  13. package/dist/cli-bundle/chunks/{chunk-FIW4P64F.js → chunk-J42EGKDK.js} +2 -2
  14. package/dist/cli-bundle/chunks/chunk-KIEGUBYQ.js +2 -0
  15. package/dist/cli-bundle/chunks/{chunk-SBRKH6OR.js → chunk-KLKYLSDX.js} +2 -2
  16. package/dist/cli-bundle/chunks/{chunk-G5JUB66K.js → chunk-LJHOAVGI.js} +59 -59
  17. package/dist/cli-bundle/chunks/{chunk-BFALV3YP.js → chunk-MEM2ZS3O.js} +2 -2
  18. package/dist/cli-bundle/chunks/chunk-QHPCJWEJ.js +35 -0
  19. package/dist/cli-bundle/chunks/{chunk-XAVMOK6B.js → chunk-WLPDLNEZ.js} +2 -2
  20. package/dist/cli-bundle/chunks/{register-list-query-T3EHHNNV.js → register-list-query-CT3LFDDN.js} +2 -2
  21. package/dist/cli-bundle/chunks/{register-mutation-E27WCLSZ.js → register-mutation-NWEVA5YS.js} +3 -3
  22. package/dist/cli-bundle/chunks/register-operations-AHIAG5KM.js +2 -0
  23. package/dist/cli-bundle/chunks/{register-setup-3YXZ444E.js → register-setup-ZJZFVAKE.js} +2 -2
  24. package/dist/cli-bundle/focused-chunks/chunk-4X57RE4U.js +2 -0
  25. package/dist/cli-bundle/focused-chunks/{chunk-XDNAW5OI.js → chunk-6XKF3PGV.js} +2 -2
  26. package/dist/cli-bundle/focused-chunks/chunk-7NJ4QYQ2.js +2 -0
  27. package/dist/cli-bundle/focused-chunks/{chunk-WCVE2NIG.js → chunk-EXZA4257.js} +2 -2
  28. package/dist/cli-bundle/focused-chunks/chunk-ITAUHSO6.js +50 -0
  29. package/dist/cli-bundle/focused-chunks/{chunk-4DQ2YERC.js → chunk-KLVUMNXJ.js} +2 -2
  30. package/dist/cli-bundle/focused-chunks/{chunk-K7RWWOAG.js → chunk-KT5XMYSX.js} +2 -2
  31. package/dist/cli-bundle/focused-chunks/{chunk-PPFGQDSI.js → chunk-KTKH4IMG.js} +2 -2
  32. package/dist/cli-bundle/focused-chunks/chunk-M67F5AIN.js +8 -0
  33. package/dist/cli-bundle/focused-chunks/{chunk-KSEDUWVZ.js → chunk-NSPYTG6G.js} +2 -2
  34. package/dist/cli-bundle/focused-chunks/chunk-OKA6ELTL.js +2 -0
  35. package/dist/cli-bundle/focused-chunks/chunk-PUISBOPP.js +2 -0
  36. package/dist/cli-bundle/focused-chunks/chunk-QLNMCESQ.js +155 -0
  37. package/dist/cli-bundle/focused-chunks/{chunk-LZ4MV3RH.js → chunk-SLKC6QLJ.js} +2 -2
  38. package/dist/cli-bundle/focused-chunks/chunk-U65UC4G3.js +2 -0
  39. package/dist/cli-bundle/focused-chunks/{chunk-KFSZ3BP6.js → chunk-WTSER4WJ.js} +6 -6
  40. package/dist/cli-bundle/focused-chunks/{chunk-YOOI3UZX.js → chunk-ZMFXQ7VF.js} +2 -2
  41. package/dist/cli-bundle/main.js +13 -13
  42. package/dist/cli-bundle/sdk-authoring.js +1 -1
  43. package/dist/cli-bundle/sdk-contracts.js +1 -1
  44. package/dist/cli-bundle/sdk-core.js +32 -35
  45. package/dist/cli-bundle/sdk-governance.js +1 -1
  46. package/dist/cli-bundle/sdk-graph.js +1 -1
  47. package/dist/cli-bundle/sdk-merge.js +32 -31
  48. package/dist/cli-bundle/sdk-query.js +1 -1
  49. package/dist/cli-bundle/sdk-runtime.js +1 -1
  50. package/dist/cli-bundle/sdk-testing.js +1 -1
  51. package/dist/cli-bundle/sdk.js +2 -2
  52. package/dist/core/diagnostics/remediation.js +7 -2
  53. package/dist/core/extensions/reserved-host-flags.js +3 -2
  54. package/dist/core/history/replay.d.ts +14 -1
  55. package/dist/core/history/replay.js +45 -8
  56. package/dist/core/output/output.d.ts +2 -0
  57. package/dist/core/output/output.js +3 -2
  58. package/dist/core/shared/command-types.d.ts +2 -0
  59. package/dist/core/shared/command-types.js +2 -2
  60. package/dist/core/shared/errors.d.ts +12 -0
  61. package/dist/core/shared/errors.js +2 -2
  62. package/dist/mcp/tool-definitions.js +9 -5
  63. package/dist/sdk/agent/refusal-reachability.d.ts +61 -0
  64. package/dist/sdk/agent/refusal-reachability.js +91 -3
  65. package/dist/sdk/cli-bootstrap.d.ts +2 -0
  66. package/dist/sdk/cli-bootstrap.js +7 -2
  67. package/dist/sdk/cli-contracts/completeness.js +3 -2
  68. package/dist/sdk/cli-contracts/flag-contracts.js +3 -2
  69. package/dist/sdk/cli-contracts/registration-helpers.js +3 -2
  70. package/dist/sdk/cli-contracts/runtime-contracts.d.ts +5 -0
  71. package/dist/sdk/cli-contracts/runtime-contracts.js +23 -7
  72. package/dist/sdk/cli-contracts/tool-parameter-tables.js +15 -5
  73. package/dist/sdk/cli-contracts/tool-schema.js +10 -3
  74. package/dist/sdk/cli-program.js +3 -2
  75. package/dist/sdk/core.d.ts +2 -2
  76. package/dist/sdk/core.js +3 -3
  77. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +26 -2
  78. package/dist/sdk/generated/generated-error-code-catalog-part-2.js +50 -26
  79. package/dist/sdk/governance/assurance-action.d.ts +7 -1
  80. package/dist/sdk/governance/assurance-action.js +7 -6
  81. package/dist/sdk/governance/assurance-mutation-error.d.ts +42 -1
  82. package/dist/sdk/governance/assurance-mutation-error.js +27 -3
  83. package/dist/sdk/governance/assurance-runtime.js +12 -11
  84. package/dist/sdk/governance/assurance.d.ts +9 -0
  85. package/dist/sdk/governance/assurance.js +80 -14
  86. package/dist/sdk/governance/extension-host-version.d.ts +25 -0
  87. package/dist/sdk/governance/extension-host-version.js +105 -0
  88. package/dist/sdk/governance/health.d.ts +32 -1
  89. package/dist/sdk/governance/health.js +119 -27
  90. package/dist/sdk/governance/provenance-health.d.ts +27 -0
  91. package/dist/sdk/governance/provenance-health.js +95 -3
  92. package/dist/sdk/governance/validate.js +7 -8
  93. package/dist/sdk/governance.d.ts +6 -0
  94. package/dist/sdk/governance.js +8 -2
  95. package/dist/sdk/history-repair.d.ts +13 -1
  96. package/dist/sdk/history-repair.js +62 -18
  97. package/dist/sdk/linked-artifacts.js +16 -5
  98. package/dist/sdk/pagination.d.ts +15 -0
  99. package/dist/sdk/pagination.js +9 -5
  100. package/dist/sdk/query/context.d.ts +5 -1
  101. package/dist/sdk/query/context.js +14 -5
  102. package/dist/sdk/read-output/continuation.d.ts +11 -0
  103. package/dist/sdk/read-output/continuation.js +132 -0
  104. package/dist/sdk/read-output-budget.d.ts +1 -1
  105. package/dist/sdk/read-output-budget.js +14 -10
  106. package/dist/sdk/read-output-contracts.d.ts +66 -2
  107. package/dist/sdk/read-output-contracts.js +183 -19
  108. package/dist/sdk/read-output-rows.d.ts +2 -0
  109. package/dist/sdk/read-output-rows.js +15 -2
  110. package/dist/sdk/runtime-input.js +4 -2
  111. package/docs/ASSURANCE.md +59 -2
  112. package/docs/COMMANDS.md +8 -2
  113. package/docs/READ_OUTPUT_CONTRACTS.md +49 -8
  114. package/docs/RELEASING.md +42 -5
  115. package/docs/SDK_CONTEXT_INTEGRITY.md +64 -2
  116. package/marketplace.json +2 -2
  117. package/package.json +3 -3
  118. package/packages/pm-beads/package.json +1 -1
  119. package/packages/pm-calendar/package.json +1 -1
  120. package/packages/pm-command-kit/package.json +1 -1
  121. package/packages/pm-digital-twin/package.json +1 -1
  122. package/packages/pm-governance-audit/package.json +1 -1
  123. package/packages/pm-guide-shell/package.json +1 -1
  124. package/packages/pm-kanban/package.json +1 -1
  125. package/packages/pm-lifecycle-hooks/package.json +1 -1
  126. package/packages/pm-linked-test-adapters/package.json +1 -1
  127. package/packages/pm-search-advanced/package.json +1 -1
  128. package/packages/pm-templates/package.json +1 -1
  129. package/packages/pm-todos/package.json +1 -1
  130. package/packages/pm-vcs/package.json +1 -1
  131. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  132. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  133. package/sdk/public-surface.json +1133 -187
  134. package/dist/cli-bundle/chunks/chunk-BBOFSVAK.js +0 -197
  135. package/dist/cli-bundle/chunks/chunk-YAMTJL6Y.js +0 -34
  136. package/dist/cli-bundle/chunks/chunk-YSVSMJ2B.js +0 -2
  137. package/dist/cli-bundle/chunks/register-operations-4R6I7CPY.js +0 -2
  138. package/dist/cli-bundle/focused-chunks/chunk-F43TT2QQ.js +0 -8
  139. package/dist/cli-bundle/focused-chunks/chunk-KYUQGTSB.js +0 -2
  140. package/dist/cli-bundle/focused-chunks/chunk-PGXJQLCT.js +0 -155
  141. package/dist/cli-bundle/focused-chunks/chunk-Q4YY3XNF.js +0 -2
  142. package/dist/cli-bundle/focused-chunks/chunk-QK753U3U.js +0 -46
  143. package/dist/cli-bundle/focused-chunks/chunk-QUMB2J27.js +0 -2
  144. package/dist/cli-bundle/focused-chunks/chunk-SM7MTG27.js +0 -2
  145. package/dist/cli-bundle/focused-chunks/chunk-ZM6TZVHC.js +0 -2
@@ -7,7 +7,7 @@
7
7
  * one deterministic SDK path.
8
8
  */
9
9
 
10
- !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]="07889857-2176-5659-96f7-8f5591f4c692")}catch(e){}}();
10
+ !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]="2bf52d6d-7e4f-5ec5-86bd-3e817ef521bb")}catch(e){}}();
11
11
  import { createHash } from "node:crypto";
12
12
  import path from "node:path";
13
13
  import { EXIT_CODE } from "../../core/shared/constants.js";
@@ -15,7 +15,7 @@ import { PmCliError } from "../../core/shared/errors.js";
15
15
  import { appendWorkspaceAuditEvent, mutateWorkspaceJsonWithHistory, readFileIfExists, readHistoryEntries, readSettings, resolveAuthor, stableStringify, WORKSPACE_HISTORY_ID, getWorkspaceHistoryPath, } from "../runtime-primitives.js";
16
16
  import { resolveCanonicalRelationshipKind } from "../relationships.js";
17
17
  import { MAX_ASSURANCE_VERDICT_LIMIT } from "./assurance-limits.js";
18
- import { AssuranceEvaluationRefusalError, AssuranceMutationRefusalError, } from "./assurance-mutation-error.js";
18
+ import { AssuranceEvaluationRefusalError, AssuranceMutationRefusalError, AssuranceSourceResolutionError, } from "./assurance-mutation-error.js";
19
19
  /** Current serialized assurance registry format. */
20
20
  export const ASSURANCE_DOCUMENT_VERSION = 1;
21
21
  /** Supported lifecycle points that can evaluate a gate. */
@@ -30,6 +30,18 @@ export const ASSURANCE_GATE_TRIGGERS = [
30
30
  "on-claim",
31
31
  "on-close",
32
32
  ];
33
+ /** Supported discriminants for built-in, delegated, and derived measurements. */
34
+ export const ASSURANCE_MEASUREMENT_SOURCE_KINDS = [
35
+ "items",
36
+ "dependency_kind",
37
+ "graph",
38
+ "validate",
39
+ "health",
40
+ "history",
41
+ "links",
42
+ "provider",
43
+ "derived",
44
+ ];
33
45
  const POLARITY_KEYS = [
34
46
  "ceiling",
35
47
  "floor",
@@ -161,7 +173,15 @@ export function validateMeasurementDefinition(definition) {
161
173
  if (definition.max_cost !== undefined) {
162
174
  requireFiniteNonNegative(definition.max_cost, "measurement.max_cost");
163
175
  }
164
- const source = definition.source;
176
+ const candidateSource = definition.source;
177
+ if (Object.prototype.toString.call(candidateSource) !== "[object Object]") {
178
+ throw new AssuranceMutationRefusalError(`measurement.source.kind must be one of ${ASSURANCE_MEASUREMENT_SOURCE_KINDS.join(", ")}`);
179
+ }
180
+ const sourceKind = Reflect.get(candidateSource, "kind");
181
+ if (!ASSURANCE_MEASUREMENT_SOURCE_KINDS.includes(sourceKind)) {
182
+ throw new AssuranceMutationRefusalError(`measurement.source.kind must be one of ${ASSURANCE_MEASUREMENT_SOURCE_KINDS.join(", ")}`);
183
+ }
184
+ const source = candidateSource;
165
185
  if (source.kind === "items") {
166
186
  validateItemsMeasurementSource(source);
167
187
  }
@@ -240,14 +260,25 @@ function validateGateProviderPolicy(policy) {
240
260
  /** Validate and return one gate declaration. */
241
261
  export function validateGateDefinition(definition) {
242
262
  requireStableId(definition.id, "gate.id");
263
+ if (!Array.isArray(definition.assertion_ids)) {
264
+ throw new AssuranceMutationRefusalError("gate.assertion_ids is required and must be an array");
265
+ }
243
266
  if (definition.assertion_ids.length === 0) {
244
267
  throw new AssuranceMutationRefusalError("gate requires at least one assertion");
245
268
  }
246
269
  for (const id of definition.assertion_ids)
247
270
  requireStableId(id, "gate.assertion_id");
271
+ if (!Array.isArray(definition.triggers)) {
272
+ throw new AssuranceMutationRefusalError(`gate.triggers is required and must be an array of ${ASSURANCE_GATE_TRIGGERS.join(", ")}`);
273
+ }
248
274
  if (definition.triggers.length === 0) {
249
275
  throw new AssuranceMutationRefusalError("gate requires at least one trigger");
250
276
  }
277
+ for (const trigger of definition.triggers) {
278
+ if (!ASSURANCE_GATE_TRIGGERS.includes(trigger)) {
279
+ throw new AssuranceMutationRefusalError(`gate trigger must be one of ${ASSURANCE_GATE_TRIGGERS.join(", ")}`);
280
+ }
281
+ }
251
282
  if (definition.provider_policy) {
252
283
  validateGateProviderPolicy(definition.provider_policy);
253
284
  }
@@ -543,16 +574,10 @@ async function evaluateMeasurementInternal(definition, context, definitions, sta
543
574
  if (builtin !== null) {
544
575
  result = builtin;
545
576
  }
546
- else if (definition.source.kind === "graph" ||
547
- definition.source.kind === "validate" ||
548
- definition.source.kind === "health" ||
549
- definition.source.kind === "provider") {
577
+ else {
550
578
  result = await context.external(definition.source);
551
579
  providerCalls = 1;
552
580
  }
553
- else {
554
- throw new TypeError(`unsupported assurance source ${definition.source.kind}`);
555
- }
556
581
  }
557
582
  const ownCost = {
558
583
  units: result.cost,
@@ -587,7 +612,21 @@ function evaluateMeasurementCached(definition, context, definitions, stack, stat
587
612
  const cached = state.cache.get(definition.id);
588
613
  if (cached)
589
614
  return cached;
590
- const evaluation = evaluateMeasurementInternal(definition, context, definitions, stack, state);
615
+ const evaluation = evaluateMeasurementInternal(definition, context, definitions, stack, state).catch((error) => {
616
+ if (error instanceof AssuranceSourceResolutionError &&
617
+ error.measurement_id === undefined) {
618
+ const sourceContext = {
619
+ measurement_id: definition.id,
620
+ source_kind: error.source_kind,
621
+ field: error.field,
622
+ ...(error.check ? { check: error.check } : {}),
623
+ };
624
+ const sourceError = new AssuranceSourceResolutionError(error.message, sourceContext);
625
+ sourceError.cause = error;
626
+ throw sourceError;
627
+ }
628
+ throw error;
629
+ });
591
630
  state.cache.set(definition.id, evaluation);
592
631
  return evaluation;
593
632
  }
@@ -664,6 +703,28 @@ function enforceGateProviderPolicy(gate, document, context, trigger) {
664
703
  }
665
704
  }
666
705
  }
706
+ async function evaluateGateMeasurement(gateId, assertionId, definition, context, definitions) {
707
+ try {
708
+ return await evaluateMeasurement(definition, context, definitions);
709
+ }
710
+ catch (error) {
711
+ if (error instanceof AssuranceSourceResolutionError) {
712
+ const checkLocation = error.check ? ` check ${error.check}` : "";
713
+ const measurementId = error.measurement_id ?? definition.id;
714
+ const refusal = new AssuranceEvaluationRefusalError(`assurance gate ${gateId} assertion ${assertionId} measurement ${measurementId} source ${error.source_kind}${checkLocation} field ${error.field} could not resolve: ${error.message}`, {
715
+ gate_id: gateId,
716
+ assertion_id: assertionId,
717
+ measurement_id: measurementId,
718
+ source_kind: error.source_kind,
719
+ field: error.field,
720
+ ...(error.check ? { check: error.check } : {}),
721
+ });
722
+ refusal.cause = error;
723
+ throw refusal;
724
+ }
725
+ throw error;
726
+ }
727
+ }
667
728
  /** Evaluate a named gate into one presentation-independent verdict document. */
668
729
  export async function evaluateAssuranceGate(gateId, document, context, options) {
669
730
  validateAssuranceDocument(document);
@@ -688,7 +749,7 @@ export async function evaluateAssuranceGate(gateId, document, context, options)
688
749
  else if (assertionDefinition.scope.kind === "filter") {
689
750
  const scopeMeasurementId = assertionDefinition.scope.measurement_id;
690
751
  const scopeDefinition = document.measurements.find((entry) => entry.id === scopeMeasurementId);
691
- const scope = await evaluateMeasurement(scopeDefinition, context, document.measurements);
752
+ const scope = await evaluateGateMeasurement(gateDefinition.id, assertionDefinition.id, scopeDefinition, context, document.measurements);
692
753
  const contributorIds = new Set(scope.contributors);
693
754
  scopedContext = {
694
755
  ...context,
@@ -698,7 +759,7 @@ export async function evaluateAssuranceGate(gateId, document, context, options)
698
759
  const owner = context.items.find((item) => item.id === assertionDefinition.owner_item_id);
699
760
  const ownerTerminal = owner !== undefined &&
700
761
  new Set(context.terminal_statuses ?? ["closed", "canceled"]).has(owner.status);
701
- return evaluateAssuranceAssertion(assertionDefinition, await evaluateMeasurement(measurementDefinition, scopedContext, document.measurements), { owner_terminal: ownerTerminal });
762
+ return evaluateAssuranceAssertion(assertionDefinition, await evaluateGateMeasurement(gateDefinition.id, assertionDefinition.id, measurementDefinition, scopedContext, document.measurements), { owner_terminal: ownerTerminal });
702
763
  });
703
764
  const blocking = assertions.some((entry) => entry.verdict === "fail" && entry.enforcement === "block");
704
765
  const warning = assertions.some((entry) => entry.verdict === "fail" && entry.enforcement === "warn");
@@ -711,6 +772,11 @@ export async function evaluateAssuranceGate(gateId, document, context, options)
711
772
  verdict: blocking ? "block" : warning ? "warn" : "pass",
712
773
  exit_code: blocking ? 1 : 0,
713
774
  assertions,
775
+ assertions_total: assertions.length,
776
+ row_contract: {
777
+ row_keys: ["assertions"],
778
+ jq_selector: ".assertions[]",
779
+ },
714
780
  cost: sumCosts(assertions.map((entry) => entry.cost), Math.max(0, ...assertions.map((entry) => entry.cost.duration_ms))),
715
781
  };
716
782
  }
@@ -940,4 +1006,4 @@ export async function listAssuranceVerdicts(pmRoot, options = {}) {
940
1006
  .slice(0, limit);
941
1007
  }
942
1008
  //# sourceMappingURL=assurance.js.map
943
- //# debugId=07889857-2176-5659-96f7-8f5591f4c692
1009
+ //# debugId=2bf52d6d-7e4f-5ec5-86bd-3e817ef521bb
@@ -0,0 +1,25 @@
1
+ import type { LoadedExtension } from "../../core/extensions/loader.js";
2
+ /** One distinct resolvable pm-cli installation and its extension consumers. */
3
+ export interface ExtensionHostVersionCopy {
4
+ /** Installed pm-cli version. */
5
+ version: string;
6
+ /** Privacy-safe workspace-relative package manifest location. */
7
+ path: string;
8
+ /** Dependency layout that exposed this copy. */
9
+ layout: "host" | "npm" | "pnpm" | "other";
10
+ /** Loaded extensions resolving this copy. */
11
+ consumers: string[];
12
+ }
13
+ /** Extension/host version census returned by the health SDK. */
14
+ export interface ExtensionHostVersionCensus {
15
+ /** Version of the process-hosting pm-cli package. */
16
+ host_version: string | null;
17
+ /** Every distinct relevant copy resolved by the host or an extension. */
18
+ copies: ExtensionHostVersionCopy[];
19
+ /** Copies whose version differs from the process host. */
20
+ mismatches: ExtensionHostVersionCopy[];
21
+ /** Stable health warning tokens for mismatched copies. */
22
+ warnings: string[];
23
+ }
24
+ /** Scan the host plus the nearest pm-cli package resolvable by every loaded extension. */
25
+ export declare function scanExtensionHostVersions(loadedExtensions: readonly LoadedExtension[], workspaceRoot: string, hostPackageRoot?: string): Promise<ExtensionHostVersionCensus>;
@@ -0,0 +1,105 @@
1
+ /**
2
+ * @module sdk/governance/extension-host-version
3
+ *
4
+ * Discovers the pm-cli copy that each loaded extension resolves at runtime so
5
+ * package-manager layout skew is visible before SDK singleton state diverges.
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]="b55cafdc-9a3b-5386-bd85-cbd2311a5933")}catch(e){}}();
9
+ import fs from "node:fs/promises";
10
+ import { createRequire } from "node:module";
11
+ import path from "node:path";
12
+ import { resolvePmPackageRootFromModule } from "../../core/packages/root.js";
13
+ function displayPackagePath(packageJsonPath, workspaceRoot) {
14
+ const relative = path.relative(workspaceRoot, packageJsonPath);
15
+ if (relative.length > 0 &&
16
+ relative !== ".." &&
17
+ !relative.startsWith(`..${path.sep}`) &&
18
+ !path.isAbsolute(relative)) {
19
+ return relative.split(path.sep).join("/");
20
+ }
21
+ const externalPath = path.resolve(packageJsonPath);
22
+ let fingerprint = 0xcbf29ce484222325n;
23
+ for (let index = 0; index < externalPath.length; index += 1) {
24
+ fingerprint ^= BigInt(externalPath.charCodeAt(index));
25
+ fingerprint = BigInt.asUintN(64, fingerprint * 0x100000001b3n);
26
+ }
27
+ const installationId = fingerprint.toString(16).padStart(16, "0");
28
+ return `<external>/${installationId}/${path.basename(path.dirname(packageJsonPath))}/package.json`;
29
+ }
30
+ function dependencyLayout(packageJsonPath) {
31
+ if (packageJsonPath.includes(`${path.sep}.pnpm${path.sep}`))
32
+ return "pnpm";
33
+ if (packageJsonPath.includes(`${path.sep}node_modules${path.sep}`)) {
34
+ return "npm";
35
+ }
36
+ return "other";
37
+ }
38
+ async function readPackageVersion(packageJsonPath) {
39
+ try {
40
+ const parsed = JSON.parse(await fs.readFile(packageJsonPath, "utf8"));
41
+ return parsed.name === "@unbrained/pm-cli" &&
42
+ typeof parsed.version === "string" &&
43
+ parsed.version.trim().length > 0
44
+ ? parsed.version.trim()
45
+ : undefined;
46
+ }
47
+ catch {
48
+ return undefined;
49
+ }
50
+ }
51
+ /** Scan the host plus the nearest pm-cli package resolvable by every loaded extension. */
52
+ export async function scanExtensionHostVersions(loadedExtensions, workspaceRoot, hostPackageRoot = resolvePmPackageRootFromModule(import.meta.url, [
53
+ "../../..",
54
+ ])) {
55
+ const hostPackageJson = path.join(hostPackageRoot, "package.json");
56
+ const hostVersion = await readPackageVersion(hostPackageJson);
57
+ const copies = new Map();
58
+ if (hostVersion !== undefined) {
59
+ copies.set(path.resolve(hostPackageJson), {
60
+ version: hostVersion,
61
+ path: displayPackagePath(hostPackageJson, workspaceRoot),
62
+ layout: "host",
63
+ consumers: ["pm-cli-host"],
64
+ });
65
+ }
66
+ for (const extension of loadedExtensions) {
67
+ try {
68
+ const resolved = createRequire(extension.entry_path).resolve("@unbrained/pm-cli/package.json");
69
+ const version = await readPackageVersion(resolved);
70
+ if (version === undefined)
71
+ continue;
72
+ const key = path.resolve(resolved);
73
+ const existing = copies.get(key);
74
+ if (existing) {
75
+ if (!existing.consumers.includes(extension.name)) {
76
+ existing.consumers.push(extension.name);
77
+ existing.consumers.sort((left, right) => left.localeCompare(right));
78
+ }
79
+ continue;
80
+ }
81
+ copies.set(key, {
82
+ version,
83
+ path: displayPackagePath(resolved, workspaceRoot),
84
+ layout: dependencyLayout(resolved),
85
+ consumers: [extension.name],
86
+ });
87
+ }
88
+ catch {
89
+ // Extensions that do not resolve pm-cli use the host surface they were
90
+ // handed. Load/activation diagnostics own unrelated resolution failures.
91
+ }
92
+ }
93
+ const sortedCopies = [...copies.values()].sort((left, right) => left.path.localeCompare(right.path));
94
+ const mismatches = hostVersion === undefined
95
+ ? []
96
+ : sortedCopies.filter((copy) => copy.version !== hostVersion);
97
+ return {
98
+ host_version: hostVersion ?? null,
99
+ copies: sortedCopies,
100
+ mismatches,
101
+ warnings: mismatches.map((copy) => `extension_host_pm_cli_version_skew:${hostVersion}:${copy.version}:${copy.consumers.join("+")}`),
102
+ };
103
+ }
104
+ //# sourceMappingURL=extension-host-version.js.map
105
+ //# debugId=b55cafdc-9a3b-5386-bd85-cbd2311a5933
@@ -20,6 +20,21 @@ export interface HealthCheck {
20
20
  /** Value that configures or reports details for this contract. */
21
21
  details: Record<string, unknown>;
22
22
  }
23
+ /** One warning indexed to the check, severity, and safe next action that decide health. */
24
+ export interface HealthFinding {
25
+ /** Original stable warning token. */
26
+ warning: string;
27
+ /** Stable colon-delimited warning code. */
28
+ code: string;
29
+ /** Health check that owns the finding. */
30
+ check: HealthCheck["name"];
31
+ /** Whether the finding blocks the top-level health verdict. */
32
+ severity: "advisory" | "gate_failing";
33
+ /** Executable remediation when one is safe and fully specified. */
34
+ remediation?: string;
35
+ /** Explicit refusal when automatic remediation would require operator choices. */
36
+ disposition?: "no_safe_automatic_remediation";
37
+ }
23
38
  /** Documents the health result payload exchanged by command, SDK, and package integrations. */
24
39
  export interface HealthResult {
25
40
  /** Whether the operation completed without a blocking failure. */
@@ -28,8 +43,16 @@ export interface HealthResult {
28
43
  checks: HealthCheck[];
29
44
  /** Number of warning entries represented by this result. */
30
45
  warning_count?: number;
46
+ /** Maximum warning rows retained in every projection. */
47
+ warning_limit?: number;
48
+ /** Whether warning rows were omitted after the declared limit. */
49
+ warnings_truncated?: boolean;
31
50
  /** Value that configures or reports warnings for this contract. */
32
51
  warnings: string[];
52
+ /** Every retained warning indexed to its owning check and severity. */
53
+ findings: HealthFinding[];
54
+ /** Exact gate-failing warning tokens that make `ok` false. */
55
+ failed_because: string[];
33
56
  /** Value that configures or reports projection for this contract. */
34
57
  projection?: {
35
58
  mode: "brief" | "summary" | "full";
@@ -115,7 +138,7 @@ interface ExtensionHealthTriageSummary {
115
138
  top_warnings: string[];
116
139
  remediation: string[];
117
140
  }
118
- /** Advisory warnings are surfaced for visibility but never flip overall health to not-ok. Telemetry is opt-out, non-critical observability: a queued/unreachable telemetry endpoint or corrupt local telemetry state is not a project-health failure and must not block agents that gate on `pm health` `ok`. History over-compaction-threshold warnings are likewise advisory maintenance hints — a deep stream is healthy, just a candidate for `pm history-compact`. Invalid legacy provenance values remain immutable diagnostic evidence: write-time resolvers reject new invalid values, while health reports the bounded aggregate without requiring truthful history to be rewritten. */
141
+ /** Advisory warnings are surfaced for visibility but never flip overall health to not-ok. Telemetry is opt-out, non-critical observability: a queued/unreachable telemetry endpoint or corrupt local telemetry state is not a project-health failure and must not block agents that gate on `pm health` `ok`. History over-compaction-threshold warnings are likewise advisory maintenance hints — a deep stream is healthy, just a candidate for `pm history-compact`. Invalid legacy provenance remains advisory until an operator explicitly applies the privacy-safe history normalizer. */
119
142
  declare function isAdvisoryHealthWarning(warning: string, requireMergeDrivers?: boolean): boolean;
120
143
  declare function warningCode(value: string): string;
121
144
  declare function normalizeExtensionNameForMatch(value: string): string;
@@ -183,12 +206,20 @@ declare function buildStaleInProgressHealthSummary(scan: StaleInProgressScan): {
183
206
  scan: StaleInProgressScan;
184
207
  warnings: string[];
185
208
  };
209
+ /** Index every warning to exactly one health check, severity, and safe next action. */
210
+ declare function buildHealthFindings(params: {
211
+ warnings: string[];
212
+ checks: HealthCheck[];
213
+ remediationSources: Record<HealthCheck["name"], string[]>;
214
+ requireMergeDrivers: boolean;
215
+ }): HealthFinding[];
186
216
  declare function projectHealthResult(result: HealthResult, options: RunHealthOptions, summaryMode: boolean): HealthResult;
187
217
  /** Implements run health for the public runtime surface of this module. */
188
218
  export declare function runHealth(global: GlobalOptions, options?: RunHealthOptions): Promise<HealthResult>;
189
219
  /** Public contract for test only health command, shared by SDK and presentation-layer consumers. */
190
220
  export declare const _testOnlyHealthCommand: {
191
221
  buildExtensionHealthTriageSummary: typeof buildExtensionHealthTriageSummary;
222
+ buildHealthFindings: typeof buildHealthFindings;
192
223
  buildCapabilityContractMetadata: typeof buildCapabilityContractMetadata;
193
224
  buildStaleInProgressHealthSummary: typeof buildStaleInProgressHealthSummary;
194
225
  buildVectorizationProviderDetails: typeof buildVectorizationProviderDetails;
@@ -4,11 +4,11 @@
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]="97fd3651-47de-5505-be2b-bc466313e91f")}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]="e7be701e-085c-5903-96da-61149452c673")}catch(e){}}();
8
8
  import fs from "node:fs/promises";
9
9
  import path from "node:path";
10
10
  import { resolveItemTypeRegistry } from "../../core/item/type-registry.js";
11
- import { isFileMissingError, pathExists, readFileIfExists } from "../../core/fs/fs-utils.js";
11
+ import { isFileMissingError, pathExists, readFileIfExists, } from "../../core/fs/fs-utils.js";
12
12
  import { activateExtensions, getActiveExtensionRegistrations, loadExtensions, runActiveOnReadHooks, } from "../../core/extensions/index.js";
13
13
  import { collectRegisteredItemFieldNames } from "../../core/extensions/item-fields.js";
14
14
  import { KNOWN_EXTENSION_CAPABILITIES, } from "../../core/extensions/loader.js";
@@ -30,9 +30,10 @@ import { parseItemDocument } from "../../core/item/item-format.js";
30
30
  import { effectiveItemFormatVersion, scanItemFormatVersions, } from "../../core/item/item-format-version.js";
31
31
  import { listAllItemMetadataLight, listAllItemMetadataWithBody, } from "../../core/store/item-store.js";
32
32
  import { PM_TELEMETRY_SOURCE_CONTEXT_VALUES, TELEMETRY_MAX_QUEUE_ENTRY_ATTEMPTS, TELEMETRY_SCHEMA_VERSION, } from "../../core/telemetry/runtime.js";
33
- import { getItemFormatFromPath, getSettingsPath, ITEM_FILE_EXTENSIONS, resolveGlobalPmRoot, resolvePmRoot, } from "../../core/store/paths.js";
33
+ import { getItemFormatFromPath, getSettingsPath, ITEM_FILE_EXTENSIONS, resolveGlobalPmRoot, resolvePmRoot, resolveWorkspaceRoot, } from "../../core/store/paths.js";
34
34
  import { readSettingsWithMetadata } from "../../core/store/settings.js";
35
- import { buildRemediationMap } from "../../core/diagnostics/remediation.js";
35
+ import { buildRemediationMap, resolveRemediation, } from "../../core/diagnostics/remediation.js";
36
+ import { scanExtensionHostVersions } from "./extension-host-version.js";
36
37
  import { scanHistoryAuthorAttribution, } from "../author-attribution.js";
37
38
  import { readManagedExtensionState } from "../extension.js";
38
39
  import { scanProvenanceResolverHealth } from "./provenance-health.js";
@@ -45,6 +46,7 @@ import { listMergeReceipts, partitionMergeReceipts, } from "../merge/receipts.js
45
46
  const PM_TELEMETRY_SOURCE_CONTEXT_SET = new Set(PM_TELEMETRY_SOURCE_CONTEXT_VALUES);
46
47
  const STALE_VECTORIZATION_SUMMARY_LIMIT = 25;
47
48
  const BRIEF_HEALTH_DETAIL_LIMIT = 8;
49
+ const HEALTH_WARNING_LIMIT = 100;
48
50
  const TELEMETRY_QUEUE_RELATIVE_PATH = path.join("runtime", "telemetry", "events.jsonl");
49
51
  const TELEMETRY_STATE_RELATIVE_PATH = path.join("runtime", "telemetry", "state.json");
50
52
  const TELEMETRY_ENDPOINT_PROBE_TIMEOUT_MS = 2_500;
@@ -54,7 +56,7 @@ const TELEMETRY_SERVER_MAX_SCHEMA_VERSION_HEADERS = [
54
56
  "x-pm-telemetry-max-schema-version",
55
57
  "x-pm-telemetry-max-version",
56
58
  ];
57
- /** Advisory warnings are surfaced for visibility but never flip overall health to not-ok. Telemetry is opt-out, non-critical observability: a queued/unreachable telemetry endpoint or corrupt local telemetry state is not a project-health failure and must not block agents that gate on `pm health` `ok`. History over-compaction-threshold warnings are likewise advisory maintenance hints — a deep stream is healthy, just a candidate for `pm history-compact`. Invalid legacy provenance values remain immutable diagnostic evidence: write-time resolvers reject new invalid values, while health reports the bounded aggregate without requiring truthful history to be rewritten. */
59
+ /** Advisory warnings are surfaced for visibility but never flip overall health to not-ok. Telemetry is opt-out, non-critical observability: a queued/unreachable telemetry endpoint or corrupt local telemetry state is not a project-health failure and must not block agents that gate on `pm health` `ok`. History over-compaction-threshold warnings are likewise advisory maintenance hints — a deep stream is healthy, just a candidate for `pm history-compact`. Invalid legacy provenance remains advisory until an operator explicitly applies the privacy-safe history normalizer. */
58
60
  function isAdvisoryHealthWarning(warning, requireMergeDrivers = false) {
59
61
  return (warning.startsWith("telemetry_") ||
60
62
  warning.startsWith("history_stream_over_compact_threshold:") ||
@@ -612,6 +614,7 @@ async function buildExtensionCheck(pmRoot, settings, noExtensionsFlag) {
612
614
  noExtensions: noExtensionsFlag,
613
615
  });
614
616
  const loadedSummaries = loadResult.loaded.map((extension) => summarizeLoadedExtension(extension));
617
+ const hostVersionCensus = await scanExtensionHostVersions(loadResult.loaded, resolveWorkspaceRoot(pmRoot));
615
618
  const activationResult = await activateExtensions({
616
619
  ...loadResult,
617
620
  loaded: loadResult.loaded,
@@ -696,6 +699,7 @@ async function buildExtensionCheck(pmRoot, settings, noExtensionsFlag) {
696
699
  ...projectManagedState.warnings,
697
700
  ...globalManagedState.warnings,
698
701
  ...updateCoverageWarnings,
702
+ ...hostVersionCensus.warnings,
699
703
  ];
700
704
  const capabilityGuidance = collectUnknownCapabilityGuidance(extensionWarnings);
701
705
  const capabilityContract = buildCapabilityContractMetadata();
@@ -721,6 +725,7 @@ async function buildExtensionCheck(pmRoot, settings, noExtensionsFlag) {
721
725
  : {}),
722
726
  capability_contract: capabilityContract,
723
727
  capability_guidance: capabilityGuidance,
728
+ host_version_census: hostVersionCensus,
724
729
  },
725
730
  },
726
731
  warnings: extensionWarnings,
@@ -858,6 +863,7 @@ const HEALTH_DETAIL_SUMMARIZERS = {
858
863
  triage: details.triage,
859
864
  capability_contract: details.capability_contract,
860
865
  capability_guidance: summarizeRecordList(details.capability_guidance, limit),
866
+ host_version_census: details.host_version_census,
861
867
  };
862
868
  },
863
869
  storage: (details, limit) => {
@@ -961,6 +967,9 @@ function applyBriefHealthProjection(result) {
961
967
  details: summarizeHealthCheckDetails(check, BRIEF_HEALTH_DETAIL_LIMIT),
962
968
  })),
963
969
  warnings: warningsSummary.sample,
970
+ findings: result.findings.filter((finding) => warningsSummary.sample.includes(finding.warning) ||
971
+ result.failed_because.includes(finding.warning)),
972
+ failed_because: result.failed_because,
964
973
  projection: {
965
974
  mode: "brief",
966
975
  warning_count: warningsSummary.count,
@@ -990,6 +999,9 @@ function applySummaryHealthProjection(result) {
990
999
  })),
991
1000
  warning_count: warningsSummary.count,
992
1001
  warnings: warningsSummary.sample,
1002
+ findings: result.findings.filter((finding) => warningsSummary.sample.includes(finding.warning) ||
1003
+ result.failed_because.includes(finding.warning)),
1004
+ failed_because: result.failed_because,
993
1005
  projection: {
994
1006
  mode: "summary",
995
1007
  warning_count: warningsSummary.count,
@@ -1819,22 +1831,62 @@ function extractHistoryDriftedCount(historyDriftCheck) {
1819
1831
  function buildHealthRemediationSources(params) {
1820
1832
  return {
1821
1833
  settings: params.normalizedSettingsReadWarnings,
1822
- directories: params.directoryState.missingDirs.map((dir) => `missing_directory:${dir}`),
1834
+ directories: params.directoryState.missingDirs.map((dir) => `missing_directory:${dir}`).concat(params.directoryState.hookWarnings),
1823
1835
  settings_values: params.settingWarnings,
1824
1836
  telemetry: params.telemetryCheck.warnings,
1825
1837
  extensions: params.extensionCheck.warnings,
1826
1838
  storage: [
1839
+ ...params.historyPolicyWarnings,
1827
1840
  ...params.historySummary.over_threshold.map((id) => `history_stream_over_compact_threshold:${id}`),
1828
1841
  ...params.authorAttributionWarnings,
1829
1842
  ...params.staleInProgressWarnings,
1830
1843
  ...params.provenanceWarnings,
1831
1844
  ],
1832
1845
  locks: params.locksCheck.warnings,
1833
- integrity: params.integrityCheck.warnings,
1846
+ integrity: [
1847
+ ...params.normalizedItemReadWarnings,
1848
+ ...params.integrityCheck.warnings,
1849
+ ],
1834
1850
  history_drift: params.historyDriftCheck.warnings,
1835
1851
  vectorization: params.vectorizationCheck.warnings,
1836
1852
  };
1837
1853
  }
1854
+ /** Index every warning to exactly one health check, severity, and safe next action. */
1855
+ function buildHealthFindings(params) {
1856
+ const owners = new Map();
1857
+ for (const [check, warnings] of Object.entries(params.remediationSources)) {
1858
+ for (const warning of warnings) {
1859
+ if (!owners.has(warning))
1860
+ owners.set(warning, check);
1861
+ }
1862
+ }
1863
+ const checksByName = new Map(params.checks.map((check) => [check.name, check]));
1864
+ const findings = params.warnings.map((warning) => {
1865
+ const check = owners.get(warning) ?? "integrity";
1866
+ const code = resolveRemediation(warning)?.code ?? warningCode(warning);
1867
+ const details = checksByName.get(check)?.details;
1868
+ const remediationMap = typeof details?.remediation_map === "object" &&
1869
+ details.remediation_map !== null
1870
+ ? details.remediation_map
1871
+ : {};
1872
+ const remediation = remediationMap[code];
1873
+ const severity = isAdvisoryHealthWarning(warning, params.requireMergeDrivers)
1874
+ ? "advisory"
1875
+ : "gate_failing";
1876
+ return {
1877
+ warning,
1878
+ code,
1879
+ check,
1880
+ severity,
1881
+ ...(typeof remediation === "string"
1882
+ ? { remediation }
1883
+ : severity === "gate_failing"
1884
+ ? { disposition: "no_safe_automatic_remediation" }
1885
+ : {}),
1886
+ };
1887
+ });
1888
+ return findings;
1889
+ }
1838
1890
  function rewriteBulkHealthRemediation(params) {
1839
1891
  if (params.check.name === "history_drift" && params.historyDriftedCount > 1) {
1840
1892
  for (const code of Object.keys(params.remediationMap)) {
@@ -1862,10 +1914,38 @@ function attachHealthRemediationMaps(params) {
1862
1914
  }
1863
1915
  }
1864
1916
  function projectHealthResult(result, options, summaryMode) {
1865
- if (summaryMode) {
1866
- return applySummaryHealthProjection(result);
1867
- }
1868
- return options.brief === true ? applyBriefHealthProjection(result) : result;
1917
+ const warningCount = result.warning_count ?? result.warnings.length;
1918
+ const boundedResult = {
1919
+ ...result,
1920
+ warning_count: warningCount,
1921
+ warning_limit: HEALTH_WARNING_LIMIT,
1922
+ warnings_truncated: warningCount > HEALTH_WARNING_LIMIT,
1923
+ warnings: result.warnings.slice(0, HEALTH_WARNING_LIMIT),
1924
+ findings: (result.findings ?? []).filter((finding) => result.warnings
1925
+ .slice(0, HEALTH_WARNING_LIMIT)
1926
+ .includes(finding.warning) ||
1927
+ result.failed_because.includes(finding.warning)),
1928
+ };
1929
+ const projected = summaryMode
1930
+ ? applySummaryHealthProjection(boundedResult)
1931
+ : options.brief === true
1932
+ ? applyBriefHealthProjection(boundedResult)
1933
+ : boundedResult;
1934
+ return {
1935
+ ...projected,
1936
+ warning_count: warningCount,
1937
+ warning_limit: HEALTH_WARNING_LIMIT,
1938
+ warnings_truncated: warningCount > projected.warnings.length,
1939
+ ...(projected.projection
1940
+ ? {
1941
+ projection: {
1942
+ ...projected.projection,
1943
+ warning_count: warningCount,
1944
+ warnings_truncated: warningCount > projected.warnings.length,
1945
+ },
1946
+ }
1947
+ : {}),
1948
+ };
1869
1949
  }
1870
1950
  /** Implements run health for the public runtime surface of this module. */
1871
1951
  export async function runHealth(global, options = {}) {
@@ -1957,23 +2037,26 @@ export async function runHealth(global, options = {}) {
1957
2037
  historyDriftCheck,
1958
2038
  vectorizationCheck,
1959
2039
  });
2040
+ const remediationSources = buildHealthRemediationSources({
2041
+ directoryState,
2042
+ normalizedSettingsReadWarnings,
2043
+ settingWarnings,
2044
+ normalizedItemReadWarnings,
2045
+ telemetryCheck,
2046
+ extensionCheck,
2047
+ historyPolicyWarnings: historyPolicy.warnings,
2048
+ historySummary,
2049
+ authorAttributionWarnings,
2050
+ staleInProgressWarnings: staleInProgress.warnings,
2051
+ provenanceWarnings,
2052
+ locksCheck,
2053
+ integrityCheck,
2054
+ historyDriftCheck,
2055
+ vectorizationCheck,
2056
+ });
1960
2057
  attachHealthRemediationMaps({
1961
2058
  checks,
1962
- remediationSources: buildHealthRemediationSources({
1963
- directoryState,
1964
- normalizedSettingsReadWarnings,
1965
- settingWarnings,
1966
- telemetryCheck,
1967
- extensionCheck,
1968
- historySummary,
1969
- authorAttributionWarnings,
1970
- staleInProgressWarnings: staleInProgress.warnings,
1971
- provenanceWarnings,
1972
- locksCheck,
1973
- integrityCheck,
1974
- historyDriftCheck,
1975
- vectorizationCheck,
1976
- }),
2059
+ remediationSources,
1977
2060
  historyDriftedCount: extractHistoryDriftedCount(historyDriftCheck),
1978
2061
  overThresholdCount: historySummary.over_threshold.length,
1979
2062
  });
@@ -1982,10 +2065,18 @@ export async function runHealth(global, options = {}) {
1982
2065
  // it must never flip overall project health to not-ok. Such warnings are still
1983
2066
  // surfaced in `warnings` and the telemetry check's own `warn` status.
1984
2067
  const blockingWarnings = normalizedWarnings.filter((warning) => !isAdvisoryHealthWarning(warning, options.requireMergeDrivers === true));
2068
+ const findings = buildHealthFindings({
2069
+ warnings: normalizedWarnings,
2070
+ checks,
2071
+ remediationSources,
2072
+ requireMergeDrivers: options.requireMergeDrivers === true,
2073
+ });
1985
2074
  const result = {
1986
2075
  ok: blockingWarnings.length === 0,
1987
2076
  checks,
1988
2077
  warnings: normalizedWarnings,
2078
+ findings,
2079
+ failed_because: blockingWarnings,
1989
2080
  generated_at: nowIso(),
1990
2081
  };
1991
2082
  return projectHealthResult(result, options, skipPolicy.summaryMode);
@@ -1993,6 +2084,7 @@ export async function runHealth(global, options = {}) {
1993
2084
  /** Public contract for test only health command, shared by SDK and presentation-layer consumers. */
1994
2085
  export const _testOnlyHealthCommand = {
1995
2086
  buildExtensionHealthTriageSummary,
2087
+ buildHealthFindings,
1996
2088
  buildCapabilityContractMetadata,
1997
2089
  buildStaleInProgressHealthSummary,
1998
2090
  buildVectorizationProviderDetails,
@@ -2019,4 +2111,4 @@ export const _testOnlyHealthCommand = {
2019
2111
  warningCode,
2020
2112
  };
2021
2113
  //# sourceMappingURL=health.js.map
2022
- //# debugId=97fd3651-47de-5505-be2b-bc466313e91f
2114
+ //# debugId=e7be701e-085c-5903-96da-61149452c673