@unbrained/pm-cli 2026.8.14 → 2026.8.16

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 (160) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +51 -11
  3. package/dist/cli/commander-usage.js +17 -4
  4. package/dist/cli/error-guidance.d.ts +7 -0
  5. package/dist/cli/error-guidance.js +65 -25
  6. package/dist/cli/main.js +3 -2
  7. package/dist/cli/register-history-author.js +16 -11
  8. package/dist/cli/register-mutation.js +4 -2
  9. package/dist/cli/register-operations.js +5 -5
  10. package/dist/cli-bundle/bundle-manifest.json +148 -148
  11. package/dist/cli-bundle/chunks/chunk-47OQEOQB.js +2 -0
  12. package/dist/cli-bundle/chunks/{chunk-XAVMOK6B.js → chunk-73UGEBRS.js} +2 -2
  13. package/dist/cli-bundle/chunks/chunk-HCIYWD6M.js +35 -0
  14. package/dist/cli-bundle/chunks/{chunk-FIW4P64F.js → chunk-I2LUWWR5.js} +2 -2
  15. package/dist/cli-bundle/chunks/{chunk-SBRKH6OR.js → chunk-IC5W2T34.js} +2 -2
  16. package/dist/cli-bundle/chunks/{chunk-TMJ7URWS.js → chunk-PANOOBIS.js} +9 -9
  17. package/dist/cli-bundle/chunks/{chunk-BFALV3YP.js → chunk-RIS565OA.js} +2 -2
  18. package/dist/cli-bundle/chunks/{chunk-TXMEUUUY.js → chunk-TK6N7HGM.js} +2 -2
  19. package/dist/cli-bundle/chunks/chunk-TOFG2URD.js +197 -0
  20. package/dist/cli-bundle/chunks/{chunk-G5JUB66K.js → chunk-URW4QRMI.js} +65 -59
  21. package/dist/cli-bundle/chunks/{register-list-query-T3EHHNNV.js → register-list-query-EYCXHZAG.js} +2 -2
  22. package/dist/cli-bundle/chunks/register-mutation-7YVIG54J.js +20 -0
  23. package/dist/cli-bundle/chunks/register-operations-KGDNRMCL.js +2 -0
  24. package/dist/cli-bundle/chunks/{register-setup-3YXZ444E.js → register-setup-NVXBOD5I.js} +2 -2
  25. package/dist/cli-bundle/focused-chunks/{chunk-PPFGQDSI.js → chunk-4JPEBFFC.js} +2 -2
  26. package/dist/cli-bundle/focused-chunks/chunk-5NMFSX26.js +155 -0
  27. package/dist/cli-bundle/focused-chunks/{chunk-YOOI3UZX.js → chunk-6ROKKT4X.js} +2 -2
  28. package/dist/cli-bundle/focused-chunks/chunk-725JSCMP.js +2 -0
  29. package/dist/cli-bundle/focused-chunks/{chunk-XDNAW5OI.js → chunk-C7JUBRFP.js} +2 -2
  30. package/dist/cli-bundle/focused-chunks/{chunk-4DQ2YERC.js → chunk-CODV5LUT.js} +2 -2
  31. package/dist/cli-bundle/focused-chunks/chunk-DQ6SMTBF.js +2 -0
  32. package/dist/cli-bundle/focused-chunks/chunk-HAACPSUW.js +2 -0
  33. package/dist/cli-bundle/focused-chunks/{chunk-WCVE2NIG.js → chunk-J4UFYHOD.js} +2 -2
  34. package/dist/cli-bundle/focused-chunks/chunk-JX5Z344Q.js +2 -0
  35. package/dist/cli-bundle/focused-chunks/chunk-L4RDYBIQ.js +2 -0
  36. package/dist/cli-bundle/focused-chunks/{chunk-KSEDUWVZ.js → chunk-NJ5RLEFD.js} +2 -2
  37. package/dist/cli-bundle/focused-chunks/{chunk-K7RWWOAG.js → chunk-QYZERYR5.js} +2 -2
  38. package/dist/cli-bundle/focused-chunks/chunk-SSNDYZTM.js +8 -0
  39. package/dist/cli-bundle/focused-chunks/chunk-T46KLOIB.js +50 -0
  40. package/dist/cli-bundle/focused-chunks/{chunk-KFSZ3BP6.js → chunk-TZATVC4G.js} +6 -6
  41. package/dist/cli-bundle/focused-chunks/chunk-WY3WRPVN.js +2 -0
  42. package/dist/cli-bundle/main.js +13 -13
  43. package/dist/cli-bundle/sdk-authoring.js +1 -1
  44. package/dist/cli-bundle/sdk-contracts.js +1 -1
  45. package/dist/cli-bundle/sdk-core.js +32 -35
  46. package/dist/cli-bundle/sdk-governance.js +1 -1
  47. package/dist/cli-bundle/sdk-graph.js +1 -1
  48. package/dist/cli-bundle/sdk-merge.js +32 -31
  49. package/dist/cli-bundle/sdk-query.js +1 -1
  50. package/dist/cli-bundle/sdk-runtime.js +1 -1
  51. package/dist/cli-bundle/sdk-testing.js +1 -1
  52. package/dist/cli-bundle/sdk.js +2 -2
  53. package/dist/core/diagnostics/remediation.js +7 -2
  54. package/dist/core/extensions/reserved-host-flags.js +3 -2
  55. package/dist/core/history/replay.d.ts +14 -1
  56. package/dist/core/history/replay.js +45 -8
  57. package/dist/core/history/workspace-history.js +32 -10
  58. package/dist/core/output/output.d.ts +2 -0
  59. package/dist/core/output/output.js +7 -3
  60. package/dist/core/shared/command-types.d.ts +2 -0
  61. package/dist/core/shared/command-types.js +2 -2
  62. package/dist/core/shared/errors.d.ts +14 -0
  63. package/dist/core/shared/errors.js +2 -2
  64. package/dist/core/store/item-store.js +28 -2
  65. package/dist/mcp/tool-definitions.js +9 -5
  66. package/dist/sdk/agent/refusal-reachability.d.ts +69 -0
  67. package/dist/sdk/agent/refusal-reachability.js +159 -3
  68. package/dist/sdk/author-attribution.d.ts +56 -11
  69. package/dist/sdk/author-attribution.js +174 -50
  70. package/dist/sdk/cli-bootstrap.d.ts +2 -0
  71. package/dist/sdk/cli-bootstrap.js +7 -2
  72. package/dist/sdk/cli-contracts/completeness.js +3 -2
  73. package/dist/sdk/cli-contracts/flag-contracts.js +6 -2
  74. package/dist/sdk/cli-contracts/registration-helpers.js +3 -2
  75. package/dist/sdk/cli-contracts/runtime-contracts.d.ts +5 -0
  76. package/dist/sdk/cli-contracts/runtime-contracts.js +23 -7
  77. package/dist/sdk/cli-contracts/tool-parameter-tables.js +22 -5
  78. package/dist/sdk/cli-contracts/tool-schema.js +20 -5
  79. package/dist/sdk/cli-program.js +3 -2
  80. package/dist/sdk/completion.js +11 -4
  81. package/dist/sdk/core.d.ts +2 -2
  82. package/dist/sdk/core.js +3 -3
  83. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +50 -2
  84. package/dist/sdk/generated/generated-error-code-catalog-part-2.js +74 -14
  85. package/dist/sdk/governance/assurance-action.d.ts +7 -1
  86. package/dist/sdk/governance/assurance-action.js +7 -6
  87. package/dist/sdk/governance/assurance-mutation-error.d.ts +42 -1
  88. package/dist/sdk/governance/assurance-mutation-error.js +27 -3
  89. package/dist/sdk/governance/assurance-runtime.js +12 -11
  90. package/dist/sdk/governance/assurance.d.ts +9 -0
  91. package/dist/sdk/governance/assurance.js +80 -14
  92. package/dist/sdk/governance/coverage-assurance.d.ts +24 -0
  93. package/dist/sdk/governance/coverage-assurance.js +61 -0
  94. package/dist/sdk/governance/extension-host-version.d.ts +25 -0
  95. package/dist/sdk/governance/extension-host-version.js +116 -0
  96. package/dist/sdk/governance/health.d.ts +32 -1
  97. package/dist/sdk/governance/health.js +119 -27
  98. package/dist/sdk/governance/provenance-health.d.ts +27 -0
  99. package/dist/sdk/governance/provenance-health.js +95 -3
  100. package/dist/sdk/governance/validate.js +7 -8
  101. package/dist/sdk/governance.d.ts +6 -0
  102. package/dist/sdk/governance.js +8 -2
  103. package/dist/sdk/history-repair.d.ts +13 -1
  104. package/dist/sdk/history-repair.js +62 -18
  105. package/dist/sdk/index.d.ts +2 -1
  106. package/dist/sdk/index.js +4 -3
  107. package/dist/sdk/linked-artifacts.js +16 -5
  108. package/dist/sdk/pagination.d.ts +15 -0
  109. package/dist/sdk/pagination.js +9 -5
  110. package/dist/sdk/query/context.d.ts +5 -1
  111. package/dist/sdk/query/context.js +14 -5
  112. package/dist/sdk/read-output/continuation.d.ts +11 -0
  113. package/dist/sdk/read-output/continuation.js +132 -0
  114. package/dist/sdk/read-output-budget.d.ts +1 -1
  115. package/dist/sdk/read-output-budget.js +14 -10
  116. package/dist/sdk/read-output-contracts.d.ts +66 -2
  117. package/dist/sdk/read-output-contracts.js +185 -19
  118. package/dist/sdk/read-output-rows.d.ts +2 -0
  119. package/dist/sdk/read-output-rows.js +15 -2
  120. package/dist/sdk/runtime-input.js +4 -2
  121. package/dist/sdk/runtime.d.ts +2 -5
  122. package/dist/sdk/runtime.js +3 -2
  123. package/docs/ASSURANCE.md +59 -2
  124. package/docs/COMMANDS.md +8 -2
  125. package/docs/READ_OUTPUT_CONTRACTS.md +55 -8
  126. package/docs/RELEASING.md +54 -11
  127. package/docs/SDK_CONTEXT_INTEGRITY.md +94 -4
  128. package/docs/SDK_CONTEXT_TRUTH_CONTRACTS.md +13 -5
  129. package/marketplace.json +2 -2
  130. package/package.json +3 -3
  131. package/packages/pm-beads/package.json +1 -1
  132. package/packages/pm-calendar/package.json +1 -1
  133. package/packages/pm-command-kit/package.json +1 -1
  134. package/packages/pm-digital-twin/package.json +1 -1
  135. package/packages/pm-governance-audit/package.json +1 -1
  136. package/packages/pm-guide-shell/package.json +1 -1
  137. package/packages/pm-kanban/package.json +1 -1
  138. package/packages/pm-lifecycle-hooks/package.json +1 -1
  139. package/packages/pm-linked-test-adapters/package.json +1 -1
  140. package/packages/pm-search-advanced/package.json +1 -1
  141. package/packages/pm-templates/package.json +1 -1
  142. package/packages/pm-todos/package.json +1 -1
  143. package/packages/pm-vcs/package.json +1 -1
  144. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  145. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  146. package/sdk/public-surface.json +1241 -180
  147. package/dist/cli-bundle/chunks/chunk-BBOFSVAK.js +0 -197
  148. package/dist/cli-bundle/chunks/chunk-YAMTJL6Y.js +0 -34
  149. package/dist/cli-bundle/chunks/chunk-YSVSMJ2B.js +0 -2
  150. package/dist/cli-bundle/chunks/register-mutation-E27WCLSZ.js +0 -20
  151. package/dist/cli-bundle/chunks/register-operations-4R6I7CPY.js +0 -2
  152. package/dist/cli-bundle/focused-chunks/chunk-F43TT2QQ.js +0 -8
  153. package/dist/cli-bundle/focused-chunks/chunk-KYUQGTSB.js +0 -2
  154. package/dist/cli-bundle/focused-chunks/chunk-LZ4MV3RH.js +0 -2
  155. package/dist/cli-bundle/focused-chunks/chunk-PGXJQLCT.js +0 -155
  156. package/dist/cli-bundle/focused-chunks/chunk-Q4YY3XNF.js +0 -2
  157. package/dist/cli-bundle/focused-chunks/chunk-QK753U3U.js +0 -46
  158. package/dist/cli-bundle/focused-chunks/chunk-QUMB2J27.js +0 -2
  159. package/dist/cli-bundle/focused-chunks/chunk-SM7MTG27.js +0 -2
  160. package/dist/cli-bundle/focused-chunks/chunk-ZM6TZVHC.js +0 -2
@@ -0,0 +1,116 @@
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]="c21b1a94-1d80-57c0-b090-c4831fc505a2")}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
+ /** Resolve symlink and platform alias segments without failing soft scans for absent hosts. */
52
+ async function canonicalPath(candidate) {
53
+ try {
54
+ return await fs.realpath(candidate);
55
+ }
56
+ catch {
57
+ return path.resolve(candidate);
58
+ }
59
+ }
60
+ /** Scan the host plus the nearest pm-cli package resolvable by every loaded extension. */
61
+ export async function scanExtensionHostVersions(loadedExtensions, workspaceRoot, hostPackageRoot = resolvePmPackageRootFromModule(import.meta.url, [
62
+ "../../..",
63
+ ])) {
64
+ const canonicalWorkspaceRoot = await canonicalPath(workspaceRoot);
65
+ const hostPackageJson = await canonicalPath(path.join(hostPackageRoot, "package.json"));
66
+ const hostVersion = await readPackageVersion(hostPackageJson);
67
+ const copies = new Map();
68
+ if (hostVersion !== undefined) {
69
+ copies.set(hostPackageJson, {
70
+ version: hostVersion,
71
+ path: displayPackagePath(hostPackageJson, canonicalWorkspaceRoot),
72
+ layout: "host",
73
+ consumers: ["pm-cli-host"],
74
+ });
75
+ }
76
+ for (const extension of loadedExtensions) {
77
+ try {
78
+ const resolved = createRequire(extension.entry_path).resolve("@unbrained/pm-cli/package.json");
79
+ const canonicalPackageJson = await canonicalPath(resolved);
80
+ const version = await readPackageVersion(canonicalPackageJson);
81
+ if (version === undefined)
82
+ continue;
83
+ const key = canonicalPackageJson;
84
+ const existing = copies.get(key);
85
+ if (existing) {
86
+ if (!existing.consumers.includes(extension.name)) {
87
+ existing.consumers.push(extension.name);
88
+ existing.consumers.sort((left, right) => left.localeCompare(right));
89
+ }
90
+ continue;
91
+ }
92
+ copies.set(key, {
93
+ version,
94
+ path: displayPackagePath(canonicalPackageJson, canonicalWorkspaceRoot),
95
+ layout: dependencyLayout(canonicalPackageJson),
96
+ consumers: [extension.name],
97
+ });
98
+ }
99
+ catch {
100
+ // Extensions that do not resolve pm-cli use the host surface they were
101
+ // handed. Load/activation diagnostics own unrelated resolution failures.
102
+ }
103
+ }
104
+ const sortedCopies = [...copies.values()].sort((left, right) => left.path.localeCompare(right.path));
105
+ const mismatches = hostVersion === undefined
106
+ ? []
107
+ : sortedCopies.filter((copy) => copy.version !== hostVersion);
108
+ return {
109
+ host_version: hostVersion ?? null,
110
+ copies: sortedCopies,
111
+ mismatches,
112
+ warnings: mismatches.map((copy) => `extension_host_pm_cli_version_skew:${hostVersion}:${copy.version}:${copy.consumers.join("+")}`),
113
+ };
114
+ }
115
+ //# sourceMappingURL=extension-host-version.js.map
116
+ //# debugId=c21b1a94-1d80-57c0-b090-c4831fc505a2
@@ -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
@@ -1,3 +1,4 @@
1
+ import type { HistoryEntry } from "../../types/index.js";
1
2
  /** Aggregated resolver attempts for one harness provenance dimension. */
2
3
  export interface ProvenanceResolverHealthOutcome {
3
4
  /** Detected harness namespace. */
@@ -35,5 +36,31 @@ export interface ProvenanceResolverHealthScan {
35
36
  /** Whether an event or byte ceiling omitted history input. */
36
37
  truncated: boolean;
37
38
  }
39
+ /** Privacy-safe receipt for an explicit provenance normalization pass. */
40
+ export interface ProvenanceNormalizationReceipt {
41
+ /** Whether at least one immutable event required normalization. */
42
+ changed: boolean;
43
+ /** Immutable events whose invalid provenance observations were removed. */
44
+ events_changed: number;
45
+ /** Total invalid observations removed without retaining their values. */
46
+ observations_removed: number;
47
+ /** Aggregate invalid-value classes removed by the pass. */
48
+ invalid_values: ProvenanceValueHealthFinding[];
49
+ }
50
+ /** Normalized entries plus the privacy-safe mutation receipt. */
51
+ export interface ProvenanceNormalizationResult {
52
+ /** History entries with invalid bounded observations removed. */
53
+ entries: HistoryEntry[];
54
+ /** Aggregate mutation receipt; never includes an observed value. */
55
+ receipt: ProvenanceNormalizationReceipt;
56
+ }
57
+ /**
58
+ * Remove only provenance observations whose values violate the bounded domain.
59
+ * History operations, patches, timestamps, authors, and hashes are retained;
60
+ * the receipt exposes aggregate classes and counts but never the removed value.
61
+ */
62
+ export declare function normalizeInvalidHistoryProvenance(entries: HistoryEntry[]): ProvenanceNormalizationResult;
63
+ /** Find every history stream requiring provenance normalization without returning raw values. */
64
+ export declare function listInvalidProvenanceHistoryStreamIds(pmRoot: string): Promise<string[]>;
38
65
  /** Scan immutable history without failing over malformed streams owned by integrity checks. */
39
66
  export declare function scanProvenanceResolverHealth(pmRoot: string, eventLimit?: number): Promise<ProvenanceResolverHealthScan>;
@@ -4,7 +4,7 @@
4
4
  * Reads bounded, privacy-safe provenance resolver outcomes for health checks.
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]="60154e1b-8348-5b6b-badf-e76bba43bef8")}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]="6efed404-acd2-5c3a-a008-856df67c8b77")}catch(e){}}();
8
8
  import fs from "node:fs/promises";
9
9
  import path from "node:path";
10
10
  const DEFAULT_PROVENANCE_HISTORY_BYTE_LIMIT = 8_388_608;
@@ -27,6 +27,98 @@ const INVALID_PROVENANCE_VALUE_CLASSIFIERS = [
27
27
  function isRecord(value) {
28
28
  return typeof value === "object" && value !== null && !Array.isArray(value);
29
29
  }
30
+ function invalidProvenanceValueKind(value) {
31
+ return INVALID_PROVENANCE_VALUE_CLASSIFIERS.find((classifier) => classifier.matches(value))?.kind;
32
+ }
33
+ /**
34
+ * Remove only provenance observations whose values violate the bounded domain.
35
+ * History operations, patches, timestamps, authors, and hashes are retained;
36
+ * the receipt exposes aggregate classes and counts but never the removed value.
37
+ */
38
+ export function normalizeInvalidHistoryProvenance(entries) {
39
+ const invalidValues = new Map();
40
+ let eventsChanged = 0;
41
+ let observationsRemoved = 0;
42
+ const normalized = entries.map((entry) => {
43
+ const provenance = isRecord(entry.agent_provenance)
44
+ ? entry.agent_provenance
45
+ : undefined;
46
+ if (provenance === undefined)
47
+ return entry;
48
+ const retained = {};
49
+ let eventChanged = false;
50
+ for (const [dimension, observation] of Object.entries(provenance)) {
51
+ const kind = isRecord(observation)
52
+ ? invalidProvenanceValueKind(observation.value)
53
+ : undefined;
54
+ if (kind === undefined) {
55
+ retained[dimension] = observation;
56
+ continue;
57
+ }
58
+ eventChanged = true;
59
+ observationsRemoved += 1;
60
+ const harness = entry.agent_harness ?? "unknown";
61
+ const key = `${harness}\0${dimension}\0${kind}`;
62
+ const aggregate = invalidValues.get(key) ?? {
63
+ harness,
64
+ dimension,
65
+ kind,
66
+ count: 0,
67
+ };
68
+ aggregate.count += 1;
69
+ invalidValues.set(key, aggregate);
70
+ }
71
+ if (!eventChanged)
72
+ return entry;
73
+ eventsChanged += 1;
74
+ const next = { ...entry };
75
+ if (Object.keys(retained).length === 0) {
76
+ delete next.agent_provenance;
77
+ }
78
+ else {
79
+ next.agent_provenance = retained;
80
+ }
81
+ return next;
82
+ });
83
+ return {
84
+ entries: normalized,
85
+ receipt: {
86
+ changed: eventsChanged > 0,
87
+ events_changed: eventsChanged,
88
+ observations_removed: observationsRemoved,
89
+ invalid_values: [...invalidValues.values()].sort((left, right) => left.harness.localeCompare(right.harness) ||
90
+ left.dimension.localeCompare(right.dimension) ||
91
+ left.kind.localeCompare(right.kind)),
92
+ },
93
+ };
94
+ }
95
+ /** Find every history stream requiring provenance normalization without returning raw values. */
96
+ export async function listInvalidProvenanceHistoryStreamIds(pmRoot) {
97
+ const ids = [];
98
+ for (const file of await listHistoryFiles(pmRoot)) {
99
+ try {
100
+ const content = await fs.readFile(path.join(pmRoot, "history", file), "utf8");
101
+ for (const line of content.split("\n")) {
102
+ if (line.trim().length === 0)
103
+ continue;
104
+ const entry = parseHistoryEntry(line);
105
+ if (entry === null || !isRecord(entry.agent_provenance))
106
+ continue;
107
+ const invalid = Object.values(entry.agent_provenance).some((observation) => isRecord(observation) &&
108
+ invalidProvenanceValueKind(observation.value) !== undefined);
109
+ if (invalid) {
110
+ ids.push(file.slice(0, -".jsonl".length));
111
+ break;
112
+ }
113
+ }
114
+ }
115
+ catch {
116
+ // Integrity diagnostics own unreadable or malformed streams. This census
117
+ // only selects safely parseable streams for the explicit normalizer.
118
+ }
119
+ }
120
+ return ids;
121
+ }
30
122
  async function listHistoryFiles(pmRoot) {
31
123
  try {
32
124
  return (await fs.readdir(path.join(pmRoot, "history")))
@@ -84,7 +176,7 @@ function collectResolverOutcomes(entry, aggregates, invalidValues) {
84
176
  Object.entries(provenance).forEach(([dimension, observation]) => {
85
177
  if (!isRecord(observation))
86
178
  return;
87
- const kind = INVALID_PROVENANCE_VALUE_CLASSIFIERS.find((classifier) => classifier.matches(observation.value))?.kind;
179
+ const kind = invalidProvenanceValueKind(observation.value);
88
180
  if (!kind)
89
181
  return;
90
182
  const key = `${harness}\0${dimension}\0${kind}`;
@@ -182,4 +274,4 @@ export async function scanProvenanceResolverHealth(pmRoot, eventLimit = 10_000)
182
274
  };
183
275
  }
184
276
  //# sourceMappingURL=provenance-health.js.map
185
- //# debugId=60154e1b-8348-5b6b-badf-e76bba43bef8
277
+ //# debugId=6efed404-acd2-5c3a-a008-856df67c8b77
@@ -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]="7b47a375-e72c-5108-bd93-75dd8cf93218")}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]="b8c4707f-5445-5079-96e5-dc0dbd649b34")}catch(e){}}();
8
8
  import fs from "node:fs/promises";
9
9
  import { realpathSync } from "node:fs";
10
10
  import { execFile } from "node:child_process";
@@ -778,9 +778,7 @@ function buildMetadataCounts(metadataPolicy, missingByField) {
778
778
  const missing = missingByField[field];
779
779
  const value = missing ? missing.length : 0;
780
780
  const countKey = METADATA_COUNT_KEY_BY_FIELD[field];
781
- if (value > 0 && countKey) {
782
- counts[countKey] = value;
783
- }
781
+ counts[countKey] = value;
784
782
  }
785
783
  return counts;
786
784
  }
@@ -858,9 +856,10 @@ function buildMetadataCheck(items, metadataPolicy, statusRegistry, verboseDiagno
858
856
  const duplicateIssueCodes = findDuplicateIssueCodes(items);
859
857
  const duplicateIssueCodeSummary = summarizeDuplicateIssueCodes(duplicateIssueCodes, verboseDiagnostics);
860
858
  const warningTokens = buildMetadataWarningTokens(metadataPolicy, missingByField, duplicateIssueCodeSummary.warnings);
861
- // Zero-suppress counts to reduce agent token cost (telemetry pm-tylj).
862
- // Only emit counts for the ACTIVE required fields of the resolved profile so a
863
- // looser profile (e.g. core) never reports missing reviewer/risk/sprint/etc.
859
+ // Emit an explicit numeric predicate for every ACTIVE required field. Stable
860
+ // zeroes let assurance consumers distinguish a passing metric from an absent
861
+ // contract key without exposing any per-item diagnostic rows.
862
+ // A looser profile (e.g. core) still never reports reviewer/risk/sprint/etc.
864
863
  // Defensive guards (Gemini high #1, PR #78 follow-up): a future settings
865
864
  // shape could include an unsupported field in required_fields — fall back
866
865
  // to 0 instead of throwing TypeError, and skip writing when the count-key
@@ -2314,4 +2313,4 @@ export async function runValidate(options, global, services = {}) {
2314
2313
  }
2315
2314
  /* c8 ignore stop */
2316
2315
  //# sourceMappingURL=validate.js.map
2317
- //# debugId=7b47a375-e72c-5108-bd93-75dd8cf93218
2316
+ //# debugId=b8c4707f-5445-5079-96e5-dc0dbd649b34