@unbrained/pm-cli 2026.8.16 → 2026.8.18

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 (225) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/AGENTS.md +4 -3
  3. package/CHANGELOG.md +65 -6
  4. package/README.md +16 -16
  5. package/dist/cli/error-guidance.js +7 -4
  6. package/dist/cli/public.d.ts +2 -0
  7. package/dist/cli/public.js +21 -0
  8. package/dist/cli/register-list-query.js +132 -51
  9. package/dist/cli/register-mutation.js +4 -4
  10. package/dist/cli-bundle/bundle-manifest.json +148 -148
  11. package/dist/cli-bundle/chunks/chunk-5UZZAJKR.js +3 -0
  12. package/dist/cli-bundle/chunks/chunk-ALDJGKAK.js +2 -0
  13. package/dist/cli-bundle/chunks/chunk-BSK2IN3C.js +8 -0
  14. package/dist/cli-bundle/chunks/chunk-DY4DMUMC.js +197 -0
  15. package/dist/cli-bundle/chunks/{chunk-73UGEBRS.js → chunk-GNFAFIJI.js} +10 -10
  16. package/dist/cli-bundle/chunks/chunk-L5Q2CLPE.js +35 -0
  17. package/dist/cli-bundle/chunks/{chunk-RIS565OA.js → chunk-PVRUN5ZS.js} +5 -5
  18. package/dist/cli-bundle/chunks/chunk-RZQTVMRQ.js +2 -0
  19. package/dist/cli-bundle/chunks/chunk-SARFF5H5.js +3 -0
  20. package/dist/cli-bundle/chunks/chunk-UYCLQVL2.js +13 -0
  21. package/dist/cli-bundle/chunks/{chunk-URW4QRMI.js → chunk-YRGOLZA7.js} +59 -55
  22. package/dist/cli-bundle/chunks/register-list-query-I23LALEE.js +11 -0
  23. package/dist/cli-bundle/chunks/register-mutation-6YGU3GPH.js +20 -0
  24. package/dist/cli-bundle/chunks/{register-operations-KGDNRMCL.js → register-operations-4HTFC6J7.js} +2 -2
  25. package/dist/cli-bundle/chunks/{register-setup-NVXBOD5I.js → register-setup-PPPEF3SN.js} +2 -2
  26. package/dist/cli-bundle/focused-chunks/chunk-2PN4TJXH.js +2 -0
  27. package/dist/cli-bundle/focused-chunks/{chunk-C7JUBRFP.js → chunk-3TKWMNKF.js} +3 -3
  28. package/dist/cli-bundle/focused-chunks/chunk-5AF3ZBNB.js +155 -0
  29. package/dist/cli-bundle/focused-chunks/chunk-C5IW2NDA.js +2 -0
  30. package/dist/cli-bundle/focused-chunks/{chunk-CODV5LUT.js → chunk-EUEGXZ2A.js} +3 -3
  31. package/dist/cli-bundle/focused-chunks/{chunk-4JPEBFFC.js → chunk-EX6MKP2X.js} +2 -2
  32. package/dist/cli-bundle/focused-chunks/{chunk-SSNDYZTM.js → chunk-K43KKAFS.js} +2 -2
  33. package/dist/cli-bundle/focused-chunks/{chunk-6ROKKT4X.js → chunk-M62NO7EI.js} +2 -2
  34. package/dist/cli-bundle/focused-chunks/{chunk-T46KLOIB.js → chunk-ODOVCP45.js} +47 -47
  35. package/dist/cli-bundle/focused-chunks/chunk-RNDNMARJ.js +16 -0
  36. package/dist/cli-bundle/focused-chunks/chunk-TN4AE665.js +26 -0
  37. package/dist/cli-bundle/focused-chunks/chunk-TVV2DONO.js +2 -0
  38. package/dist/cli-bundle/focused-chunks/chunk-UI6AJ5TD.js +4 -0
  39. package/dist/cli-bundle/focused-chunks/chunk-V5XTGLK7.js +2 -0
  40. package/dist/cli-bundle/focused-chunks/chunk-WGHQVU6P.js +2 -0
  41. package/dist/cli-bundle/focused-chunks/chunk-XYNBXTDQ.js +2 -0
  42. package/dist/cli-bundle/focused-chunks/chunk-Z56ECT7I.js +2 -0
  43. package/dist/cli-bundle/main.js +7 -7
  44. package/dist/cli-bundle/sdk-authoring.js +1 -1
  45. package/dist/cli-bundle/sdk-contracts.js +1 -1
  46. package/dist/cli-bundle/sdk-core.js +31 -31
  47. package/dist/cli-bundle/sdk-governance.js +1 -1
  48. package/dist/cli-bundle/sdk-graph.js +1 -1
  49. package/dist/cli-bundle/sdk-merge.js +32 -32
  50. package/dist/cli-bundle/sdk-query.js +1 -1
  51. package/dist/cli-bundle/sdk-runtime.js +1 -1
  52. package/dist/cli-bundle/sdk-testing.js +1 -1
  53. package/dist/cli-bundle/sdk.js +4 -2
  54. package/dist/core/config/nested-settings.js +8 -2
  55. package/dist/core/extensions/exporter-output-contract.d.ts +12 -0
  56. package/dist/core/extensions/exporter-output-contract.js +67 -0
  57. package/dist/core/extensions/extension-types.d.ts +36 -1
  58. package/dist/core/extensions/extension-types.js +2 -2
  59. package/dist/core/extensions/loader.js +43 -48
  60. package/dist/core/extensions/manifest-schema.d.ts +20 -0
  61. package/dist/core/extensions/manifest-schema.js +52 -0
  62. package/dist/core/history/event-index.d.ts +5 -0
  63. package/dist/core/history/event-index.js +133 -27
  64. package/dist/core/history/replay.js +32 -14
  65. package/dist/core/schema/fields-file.d.ts +1 -1
  66. package/dist/core/schema/fields-file.js +2 -2
  67. package/dist/core/schema/runtime-field-values.js +115 -9
  68. package/dist/core/schema/runtime-schema.d.ts +3 -1
  69. package/dist/core/schema/runtime-schema.js +124 -3
  70. package/dist/core/sentry/helpers.js +3 -3
  71. package/dist/core/shared/constants.js +5 -2
  72. package/dist/core/shared/errors.d.ts +10 -0
  73. package/dist/core/shared/errors.js +2 -2
  74. package/dist/core/shared/time.d.ts +2 -0
  75. package/dist/core/shared/time.js +27 -2
  76. package/dist/core/store/settings-validator.d.ts +4 -0
  77. package/dist/core/store/settings-validator.js +3 -2
  78. package/dist/core/store/settings.js +8 -2
  79. package/dist/mcp/tool-definitions.js +12 -7
  80. package/dist/sdk/agent/refusal-reachability.d.ts +46 -6
  81. package/dist/sdk/agent/refusal-reachability.js +350 -8
  82. package/dist/sdk/authoring.d.ts +1 -1
  83. package/dist/sdk/authoring.js +2 -2
  84. package/dist/sdk/cli-bootstrap.d.ts +2 -2
  85. package/dist/sdk/cli-bootstrap.js +4 -4
  86. package/dist/sdk/cli-contracts/command-aliases.d.ts +34 -2
  87. package/dist/sdk/cli-contracts/command-aliases.js +64 -13
  88. package/dist/sdk/cli-contracts/enum-contracts.d.ts +4 -0
  89. package/dist/sdk/cli-contracts/enum-contracts.js +9 -2
  90. package/dist/sdk/cli-contracts/flag-contracts.js +11 -7
  91. package/dist/sdk/cli-contracts/grammar-contracts.d.ts +70 -0
  92. package/dist/sdk/cli-contracts/grammar-contracts.js +345 -0
  93. package/dist/sdk/cli-contracts/runtime-contracts.d.ts +19 -2
  94. package/dist/sdk/cli-contracts/runtime-contracts.js +61 -14
  95. package/dist/sdk/cli-contracts/tool-parameter-tables.js +10 -4
  96. package/dist/sdk/cli-contracts/tool-schema.d.ts +2 -2
  97. package/dist/sdk/cli-contracts/tool-schema.js +20 -11
  98. package/dist/sdk/cli-contracts.d.ts +2 -2
  99. package/dist/sdk/cli-contracts.js +4 -4
  100. package/dist/sdk/completion.js +13 -15
  101. package/dist/sdk/compose.d.ts +9 -5
  102. package/dist/sdk/compose.js +33 -3
  103. package/dist/sdk/context-intent-contracts.d.ts +8 -4
  104. package/dist/sdk/context-intent-contracts.js +76 -31
  105. package/dist/sdk/contracts.d.ts +1 -0
  106. package/dist/sdk/contracts.js +3 -2
  107. package/dist/sdk/core.d.ts +1 -1
  108. package/dist/sdk/core.js +3 -3
  109. package/dist/sdk/dependency-flag-validation.d.ts +9 -2
  110. package/dist/sdk/dependency-flag-validation.js +101 -35
  111. package/dist/sdk/environment/host-environment-errors.js +25 -7
  112. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +78 -14
  113. package/dist/sdk/generated/generated-error-code-catalog-part-2.js +62 -14
  114. package/dist/sdk/governance/assurance-action.d.ts +3 -7
  115. package/dist/sdk/governance/assurance-action.js +97 -2
  116. package/dist/sdk/governance/boundary-fixtures.d.ts +86 -0
  117. package/dist/sdk/governance/boundary-fixtures.js +201 -0
  118. package/dist/sdk/governance/defect-recurrence-signals.d.ts +10 -0
  119. package/dist/sdk/governance/defect-recurrence-signals.js +25 -0
  120. package/dist/sdk/governance/defect-recurrence.d.ts +242 -0
  121. package/dist/sdk/governance/defect-recurrence.js +523 -0
  122. package/dist/sdk/governance.d.ts +2 -0
  123. package/dist/sdk/governance.js +4 -2
  124. package/dist/sdk/graph/governance.js +3 -2
  125. package/dist/sdk/guide-topics.js +193 -4
  126. package/dist/sdk/index.d.ts +7 -3
  127. package/dist/sdk/index.js +8 -5
  128. package/dist/sdk/init-agent-guidance.js +3 -3
  129. package/dist/sdk/lifecycle/create.js +4 -2
  130. package/dist/sdk/lifecycle/update-many.js +15 -2
  131. package/dist/sdk/lifecycle/update.js +5 -2
  132. package/dist/sdk/linked-artifacts.js +63 -14
  133. package/dist/sdk/mutation-events.d.ts +18 -2
  134. package/dist/sdk/mutation-events.js +59 -16
  135. package/dist/sdk/output.d.ts +44 -0
  136. package/dist/sdk/output.js +28 -2
  137. package/dist/sdk/query/complete-list.d.ts +94 -0
  138. package/dist/sdk/query/complete-list.js +173 -0
  139. package/dist/sdk/query/get.d.ts +2 -0
  140. package/dist/sdk/query/get.js +32 -7
  141. package/dist/sdk/query/list.js +5 -2
  142. package/dist/sdk/query.d.ts +1 -0
  143. package/dist/sdk/query.js +3 -2
  144. package/dist/sdk/read-output-contracts.d.ts +2 -2
  145. package/dist/sdk/read-output-contracts.js +36 -9
  146. package/dist/sdk/relationship-analytics.js +90 -14
  147. package/dist/sdk/relationship-kinds/contract.d.ts +4 -0
  148. package/dist/sdk/relationship-kinds/contract.js +3 -2
  149. package/dist/sdk/relationships.d.ts +1 -1
  150. package/dist/sdk/relationships.js +11 -2
  151. package/dist/sdk/runtime-extended-actions.js +10 -2
  152. package/dist/sdk/runtime.d.ts +8 -2
  153. package/dist/sdk/runtime.js +11 -8
  154. package/dist/sdk/schema.d.ts +1 -0
  155. package/dist/sdk/schema.js +2 -2
  156. package/dist/sdk/test/execution.d.ts +6 -2
  157. package/dist/sdk/test/execution.js +39 -13
  158. package/dist/types.d.ts +34 -2
  159. package/dist/types.js +4 -2
  160. package/docs/AGENT_GUIDE.md +25 -9
  161. package/docs/CLI_GRAMMAR.md +106 -0
  162. package/docs/COMMANDS.md +23 -23
  163. package/docs/CONFIGURATION.md +58 -1
  164. package/docs/CONTEXT_INTEGRITY_CONTRACTS.md +58 -0
  165. package/docs/DEFECT_RECURRENCE.md +134 -0
  166. package/docs/DEPENDENCY_KIND_CONTRACT.md +12 -2
  167. package/docs/ONBOARDING.md +4 -4
  168. package/docs/QUICKSTART.md +1 -1
  169. package/docs/README.md +4 -0
  170. package/docs/RELATIONSHIP_GRAPH.md +16 -2
  171. package/docs/RELEASING.md +30 -6
  172. package/docs/SDK.md +74 -7
  173. package/docs/SDK_ARTIFACT_OUTPUT.md +72 -0
  174. package/docs/SDK_CONTEXT_COORDINATION.md +46 -12
  175. package/docs/SDK_RUNTIME_BOUNDARIES.md +9 -1
  176. package/docs/TESTING.md +18 -1
  177. package/docs/TRUSTWORTHY_CONTEXT_EVIDENCE.md +1 -1
  178. package/docs/agent-task-token-baseline.json +5 -5
  179. package/marketplace.json +2 -2
  180. package/package.json +8 -5
  181. package/packages/pm-beads/package.json +1 -1
  182. package/packages/pm-calendar/package.json +1 -1
  183. package/packages/pm-command-kit/package.json +1 -1
  184. package/packages/pm-digital-twin/package.json +1 -1
  185. package/packages/pm-governance-audit/package.json +1 -1
  186. package/packages/pm-guide-shell/package.json +1 -1
  187. package/packages/pm-kanban/package.json +1 -1
  188. package/packages/pm-lifecycle-hooks/package.json +1 -1
  189. package/packages/pm-linked-test-adapters/package.json +1 -1
  190. package/packages/pm-search-advanced/package.json +1 -1
  191. package/packages/pm-templates/package.json +1 -1
  192. package/packages/pm-todos/package.json +1 -1
  193. package/packages/pm-vcs/package.json +1 -1
  194. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  195. package/plugins/pm-claude/skills/pm-audit/SKILL.md +26 -0
  196. package/plugins/pm-claude/skills/pm-developer/SKILL.md +26 -0
  197. package/plugins/pm-claude/skills/pm-planner/SKILL.md +26 -0
  198. package/plugins/pm-claude/skills/pm-release/SKILL.md +26 -0
  199. package/plugins/pm-claude/skills/pm-workflow/SKILL.md +26 -0
  200. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  201. package/plugins/pm-codex/skills/pm-auditor/SKILL.md +26 -0
  202. package/plugins/pm-codex/skills/pm-native/SKILL.md +26 -0
  203. package/plugins/pm-codex/skills/pm-release/SKILL.md +26 -0
  204. package/sdk/public-surface.json +1243 -54
  205. package/dist/cli-bundle/chunks/chunk-47OQEOQB.js +0 -2
  206. package/dist/cli-bundle/chunks/chunk-HCIYWD6M.js +0 -35
  207. package/dist/cli-bundle/chunks/chunk-I2LUWWR5.js +0 -3
  208. package/dist/cli-bundle/chunks/chunk-IC5W2T34.js +0 -2
  209. package/dist/cli-bundle/chunks/chunk-OAC5NIV4.js +0 -3
  210. package/dist/cli-bundle/chunks/chunk-PANOOBIS.js +0 -13
  211. package/dist/cli-bundle/chunks/chunk-TK6N7HGM.js +0 -8
  212. package/dist/cli-bundle/chunks/chunk-TOFG2URD.js +0 -197
  213. package/dist/cli-bundle/chunks/register-list-query-EYCXHZAG.js +0 -10
  214. package/dist/cli-bundle/chunks/register-mutation-7YVIG54J.js +0 -20
  215. package/dist/cli-bundle/focused-chunks/chunk-5NMFSX26.js +0 -155
  216. package/dist/cli-bundle/focused-chunks/chunk-725JSCMP.js +0 -2
  217. package/dist/cli-bundle/focused-chunks/chunk-DQ6SMTBF.js +0 -2
  218. package/dist/cli-bundle/focused-chunks/chunk-HAACPSUW.js +0 -2
  219. package/dist/cli-bundle/focused-chunks/chunk-J4UFYHOD.js +0 -2
  220. package/dist/cli-bundle/focused-chunks/chunk-JX5Z344Q.js +0 -2
  221. package/dist/cli-bundle/focused-chunks/chunk-L4RDYBIQ.js +0 -2
  222. package/dist/cli-bundle/focused-chunks/chunk-NJ5RLEFD.js +0 -26
  223. package/dist/cli-bundle/focused-chunks/chunk-QYZERYR5.js +0 -4
  224. package/dist/cli-bundle/focused-chunks/chunk-TZATVC4G.js +0 -16
  225. package/dist/cli-bundle/focused-chunks/chunk-WY3WRPVN.js +0 -2
@@ -0,0 +1,523 @@
1
+ /**
2
+ * @module sdk/defect-recurrence
3
+ *
4
+ * Compiles versioned defect-recurrence policy into an incremental index and
5
+ * explains which local and hosted checks a proposed change must run.
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]="59f5918f-108e-5cad-905b-7f690c3c4794")}catch(e){}}();
9
+ import { createHash } from "node:crypto";
10
+ import { stableStringify } from "../../core/shared/serialization.js";
11
+ import { defectRecurrenceItemSignals } from "./defect-recurrence-signals.js";
12
+ /** Stable defect escape taxonomy used by project policy and PM metadata. */
13
+ export const DEFECT_ESCAPE_CLASSES = [
14
+ "production_defect",
15
+ "nightly_regression",
16
+ "scanner_finding",
17
+ "review_caught_late",
18
+ ];
19
+ /** Evidence dispositions accepted by the fail-closed defect gate. */
20
+ export const DEFECT_GATE_EVIDENCE_DISPOSITIONS = [
21
+ "gate_added",
22
+ "gate_strengthened",
23
+ "explicit_waiver",
24
+ ];
25
+ const MAX_FILE_PATTERN_LENGTH = 256;
26
+ const MAX_COMPILED_PATH_PATTERNS = 1_024;
27
+ const compiledPathPatterns = new Map();
28
+ const CHANGE_INPUT_FIELDS = [
29
+ "files",
30
+ "package_names",
31
+ "item_ids",
32
+ "tags",
33
+ "error_codes",
34
+ ];
35
+ function uniqueSorted(values) {
36
+ return [...new Set(values.map((value) => value.trim()).filter(Boolean))].sort((left, right) => (left < right ? -1 : 1));
37
+ }
38
+ function fingerprint(value) {
39
+ return createHash("sha256").update(stableStringify(value)).digest("hex");
40
+ }
41
+ function assertNonEmpty(value, field) {
42
+ if (typeof value !== "string" || !value.trim()) {
43
+ throw new TypeError(`${field} must be a non-empty string`);
44
+ }
45
+ }
46
+ function isRecord(value) {
47
+ return typeof value === "object" && value !== null && !Array.isArray(value);
48
+ }
49
+ function isStringArray(value) {
50
+ return (Array.isArray(value) && value.every((entry) => typeof entry === "string"));
51
+ }
52
+ function assertChangeInput(value, field) {
53
+ if (!isRecord(value))
54
+ throw new TypeError(`${field} must be an object`);
55
+ for (const key of CHANGE_INPUT_FIELDS) {
56
+ if (value[key] !== undefined && !isStringArray(value[key])) {
57
+ throw new TypeError(`${field}.${key} must be an array of strings`);
58
+ }
59
+ }
60
+ return value;
61
+ }
62
+ function assertTriggers(value, familyId) {
63
+ if (!isRecord(value)) {
64
+ throw new TypeError(`defect recurrence family ${familyId} triggers must be an object`);
65
+ }
66
+ for (const key of [
67
+ "package_names",
68
+ "item_ids",
69
+ "tags",
70
+ "error_codes",
71
+ ]) {
72
+ if (value[key] !== undefined && !isStringArray(value[key])) {
73
+ throw new TypeError(`defect recurrence family ${familyId} triggers.${key} must be an array of strings`);
74
+ }
75
+ }
76
+ const filePatterns = value.file_patterns;
77
+ if (filePatterns !== undefined && !isStringArray(filePatterns)) {
78
+ throw new TypeError(`defect recurrence family ${familyId} triggers.file_patterns must be an array of strings`);
79
+ }
80
+ for (const pattern of filePatterns ?? []) {
81
+ if (pattern.length > MAX_FILE_PATTERN_LENGTH) {
82
+ throw new TypeError(`defect recurrence family ${familyId} file pattern exceeds ${MAX_FILE_PATTERN_LENGTH} characters`);
83
+ }
84
+ }
85
+ }
86
+ /** Validate stable identity fields and reject duplicate family ids. */
87
+ function assertFamilyIdentity(family, ids) {
88
+ assertNonEmpty(family.id, "defect recurrence family id");
89
+ if (ids.has(family.id)) {
90
+ throw new TypeError(`Duplicate defect recurrence family ${family.id}`);
91
+ }
92
+ ids.add(family.id);
93
+ if (!Number.isInteger(family.version) || family.version < 1) {
94
+ throw new TypeError(`defect recurrence family ${family.id} version must be a positive integer`);
95
+ }
96
+ if (!DEFECT_ESCAPE_CLASSES.includes(family.escape_class)) {
97
+ throw new TypeError(`defect recurrence family ${family.id} has an invalid escape class`);
98
+ }
99
+ assertNonEmpty(family.title, `defect recurrence family ${family.id} title`);
100
+ assertNonEmpty(family.owner_item_id, `defect recurrence family ${family.id} owner_item_id`);
101
+ }
102
+ /** Validate historical lineage and executable local and hosted check references. */
103
+ function assertFamilyEvidenceContracts(family) {
104
+ if (!isStringArray(family.historical_item_ids) ||
105
+ family.historical_item_ids.length === 0 ||
106
+ family.historical_item_ids.some((itemId) => !itemId.trim())) {
107
+ throw new TypeError(`defect recurrence family ${family.id} requires historical_item_ids`);
108
+ }
109
+ assertTriggers(family.triggers, family.id);
110
+ if (!isRecord(family.checks) ||
111
+ !isStringArray(family.checks.local) ||
112
+ !isStringArray(family.checks.hosted) ||
113
+ family.checks.local.length === 0 ||
114
+ family.checks.hosted.length === 0 ||
115
+ family.checks.local.some((check) => !check.trim()) ||
116
+ family.checks.hosted.some((check) => !check.trim())) {
117
+ throw new TypeError(`defect recurrence family ${family.id} checks must contain nonempty local and hosted string arrays`);
118
+ }
119
+ }
120
+ /** Validate finite normalized escape and false-positive budgets. */
121
+ function assertFamilyBudget(family) {
122
+ if (!isRecord(family.budget) ||
123
+ typeof family.budget.max_escape_rate !== "number" ||
124
+ !Number.isFinite(family.budget.max_escape_rate) ||
125
+ typeof family.budget.max_false_positive_rate !== "number" ||
126
+ !Number.isFinite(family.budget.max_false_positive_rate)) {
127
+ throw new TypeError(`defect recurrence family ${family.id} requires numeric budgets`);
128
+ }
129
+ if (family.budget.max_escape_rate < 0 ||
130
+ family.budget.max_escape_rate > 1 ||
131
+ family.budget.max_false_positive_rate < 0 ||
132
+ family.budget.max_false_positive_rate > 1) {
133
+ throw new TypeError(`defect recurrence family ${family.id} budgets must be between 0 and 1`);
134
+ }
135
+ }
136
+ /** Validate that a structurally safe negative control selects its own family. */
137
+ function assertFamilyNegativeControl(family) {
138
+ const negativeControl = assertChangeInput(family.negative_control, `defect recurrence family ${family.id} negative_control`);
139
+ if (familyReasons(family, negativeControl, {}).length === 0) {
140
+ throw new TypeError(`defect recurrence family ${family.id} negative_control must select its own family`);
141
+ }
142
+ }
143
+ /** Validate the complete recurrence policy before indexing or analysis. */
144
+ function validatePolicy(policy) {
145
+ if (policy.version !== 1) {
146
+ throw new TypeError(`Unsupported defect recurrence policy version ${policy.version}`);
147
+ }
148
+ if (typeof policy.evidence_epoch !== "string" ||
149
+ !Number.isFinite(Date.parse(policy.evidence_epoch))) {
150
+ throw new TypeError("defect recurrence evidence_epoch must be an ISO timestamp");
151
+ }
152
+ const ids = new Set();
153
+ for (const family of policy.families) {
154
+ if (!isRecord(family))
155
+ throw new TypeError("defect recurrence family must be an object");
156
+ assertFamilyIdentity(family, ids);
157
+ assertFamilyEvidenceContracts(family);
158
+ assertFamilyBudget(family);
159
+ assertFamilyNegativeControl(family);
160
+ }
161
+ }
162
+ function pathMatches(pattern, value) {
163
+ let compiled = compiledPathPatterns.get(pattern);
164
+ if (compiled === undefined) {
165
+ if (compiledPathPatterns.size >= MAX_COMPILED_PATH_PATTERNS) {
166
+ const oldest = compiledPathPatterns.keys().next().value;
167
+ compiledPathPatterns.delete(oldest);
168
+ }
169
+ const escaped = pattern
170
+ .replaceAll("**", "\u0000")
171
+ .replaceAll(/[|\\{}()[\]^$+?./]/gu, "\\$&")
172
+ .replaceAll("*", "[^/]*")
173
+ .replaceAll("\u0000", ".*");
174
+ compiled = new RegExp(`^${escaped}$`, "u");
175
+ compiledPathPatterns.set(pattern, compiled);
176
+ }
177
+ return compiled.test(value);
178
+ }
179
+ function fileReasons(family, input) {
180
+ const reasons = [];
181
+ const patterns = uniqueSorted(family.triggers.file_patterns ?? []);
182
+ for (const file of uniqueSorted(input.files ?? [])) {
183
+ for (const pattern of patterns) {
184
+ if (pathMatches(pattern, file))
185
+ reasons.push({ signal: "file", value: file, matched: pattern });
186
+ }
187
+ }
188
+ return reasons;
189
+ }
190
+ function exactReasons(family, input) {
191
+ const exactSignals = [
192
+ {
193
+ signal: "package",
194
+ values: uniqueSorted(input.package_names ?? []),
195
+ triggers: uniqueSorted(family.triggers.package_names ?? []),
196
+ },
197
+ {
198
+ signal: "tag",
199
+ values: uniqueSorted(input.tags ?? []),
200
+ triggers: uniqueSorted(family.triggers.tags ?? []),
201
+ },
202
+ {
203
+ signal: "error_code",
204
+ values: uniqueSorted(input.error_codes ?? []),
205
+ triggers: uniqueSorted(family.triggers.error_codes ?? []),
206
+ },
207
+ ];
208
+ const reasons = [];
209
+ for (const exact of exactSignals) {
210
+ const triggerSet = new Set(exact.triggers);
211
+ for (const value of exact.values) {
212
+ if (triggerSet.has(value))
213
+ reasons.push({ signal: exact.signal, value, matched: value });
214
+ }
215
+ }
216
+ return reasons;
217
+ }
218
+ function itemReasons(family, input, itemFamilies) {
219
+ const directItemIds = new Set(family.triggers.item_ids ?? []);
220
+ return uniqueSorted(input.item_ids ?? []).flatMap((itemId) => directItemIds.has(itemId) || itemFamilies[itemId]?.includes(family.id)
221
+ ? [{ signal: "item", value: itemId, matched: family.id }]
222
+ : []);
223
+ }
224
+ function familyReasons(family, input, itemFamilies) {
225
+ const reasons = [
226
+ ...fileReasons(family, input),
227
+ ...exactReasons(family, input),
228
+ ...itemReasons(family, input, itemFamilies),
229
+ ];
230
+ return reasons.sort((left, right) => left.signal !== right.signal
231
+ ? left.signal.localeCompare(right.signal)
232
+ : left.value !== right.value
233
+ ? left.value.localeCompare(right.value)
234
+ : left.matched.localeCompare(right.matched));
235
+ }
236
+ function readRiskCursorOffset(cursor, indexFingerprint) {
237
+ if (!cursor)
238
+ return 0;
239
+ let parsed;
240
+ try {
241
+ parsed = JSON.parse(Buffer.from(cursor, "base64url").toString("utf8"));
242
+ }
243
+ catch {
244
+ throw new TypeError("defect change-risk cursor is invalid");
245
+ }
246
+ if (typeof parsed !== "object" ||
247
+ parsed === null ||
248
+ Array.isArray(parsed) ||
249
+ parsed.index_fingerprint !==
250
+ indexFingerprint ||
251
+ !Number.isInteger(parsed.offset) ||
252
+ parsed.offset < 0) {
253
+ throw new TypeError("defect change-risk cursor does not match this index");
254
+ }
255
+ return parsed.offset;
256
+ }
257
+ function changeSignalCount(input) {
258
+ return [
259
+ input.files,
260
+ input.package_names,
261
+ input.item_ids,
262
+ input.tags,
263
+ input.error_codes,
264
+ ].reduce((total, values) => total + (values?.length ?? 0), 0);
265
+ }
266
+ /** Compile deterministic policy and PM metadata into a sparse incremental index. */
267
+ export function buildDefectRecurrenceIndex(policy, items, options = {}) {
268
+ validatePolicy(policy);
269
+ const families = [...policy.families].sort((left, right) => left.id < right.id ? -1 : 1);
270
+ const policyFingerprint = fingerprint({ ...policy, families });
271
+ const changed = new Set(options.changed_item_ids ?? []);
272
+ const canReuse = options.previous_index?.policy_fingerprint === policyFingerprint;
273
+ const itemFamilies = canReuse
274
+ ? Object.fromEntries(Object.entries(options.previous_index?.item_families ?? {}).filter(([itemId]) => !changed.has(itemId)))
275
+ : {};
276
+ const itemsReused = canReuse ? Object.keys(itemFamilies).length : 0;
277
+ for (const item of items) {
278
+ const signals = defectRecurrenceItemSignals(item);
279
+ const matched = families
280
+ .filter((family) => family.historical_item_ids.includes(item.id) ||
281
+ familyReasons(family, signals, {}).length > 0)
282
+ .map((family) => family.id);
283
+ if (matched.length > 0)
284
+ itemFamilies[item.id] = matched;
285
+ else
286
+ delete itemFamilies[item.id];
287
+ }
288
+ const sortedItemFamilies = Object.fromEntries(Object.entries(itemFamilies)
289
+ .sort(([left], [right]) => (left < right ? -1 : 1))
290
+ .map(([itemId, familyIds]) => [itemId, uniqueSorted(familyIds)]));
291
+ return {
292
+ version: 1,
293
+ policy_fingerprint: policyFingerprint,
294
+ index_fingerprint: fingerprint({
295
+ policy_fingerprint: policyFingerprint,
296
+ item_families: sortedItemFamilies,
297
+ }),
298
+ families,
299
+ item_families: sortedItemFamilies,
300
+ build: {
301
+ items_scanned: items.length,
302
+ items_reused: itemsReused,
303
+ items_indexed: Object.keys(sortedItemFamilies).length,
304
+ },
305
+ };
306
+ }
307
+ /** Explain recurrence families and shared local/hosted checks for one change. */
308
+ export function analyzeDefectChangeRisk(index, input, options = {}) {
309
+ const limit = options.limit ?? 25;
310
+ if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
311
+ throw new TypeError("defect change-risk limit must be an integer from 1 through 100");
312
+ }
313
+ const offset = readRiskCursorOffset(options.cursor, index.index_fingerprint);
314
+ const matches = index.families.flatMap((family) => {
315
+ const reasons = familyReasons(family, input, index.item_families);
316
+ return reasons.length === 0
317
+ ? []
318
+ : [
319
+ {
320
+ family_id: family.id,
321
+ family_version: family.version,
322
+ owner_item_id: family.owner_item_id,
323
+ escape_class: family.escape_class,
324
+ reasons,
325
+ local_checks: uniqueSorted(family.checks.local),
326
+ hosted_checks: uniqueSorted(family.checks.hosted),
327
+ budget: family.budget,
328
+ },
329
+ ];
330
+ });
331
+ const page = matches.slice(offset, offset + limit);
332
+ const nextOffset = offset + page.length;
333
+ const nextCursor = nextOffset < matches.length
334
+ ? Buffer.from(JSON.stringify({
335
+ index_fingerprint: index.index_fingerprint,
336
+ offset: nextOffset,
337
+ })).toString("base64url")
338
+ : undefined;
339
+ const reportWithoutCost = {
340
+ risk_detected: matches.length > 0,
341
+ index_fingerprint: index.index_fingerprint,
342
+ items: page,
343
+ total: matches.length,
344
+ ...(nextCursor ? { next_cursor: nextCursor } : {}),
345
+ required_local_checks: uniqueSorted(matches.flatMap((match) => match.local_checks)),
346
+ required_hosted_checks: uniqueSorted(matches.flatMap((match) => match.hosted_checks)),
347
+ row_contract: {
348
+ row_keys: ["items"],
349
+ jq_selector: ".items[]",
350
+ },
351
+ };
352
+ return {
353
+ ...reportWithoutCost,
354
+ cost: {
355
+ families_evaluated: index.families.length,
356
+ signals_evaluated: changeSignalCount(input),
357
+ families_selected: matches.length,
358
+ estimated_output_tokens: Math.ceil(JSON.stringify(reportWithoutCost).length / 4),
359
+ },
360
+ };
361
+ }
362
+ function isDefectItem(item) {
363
+ return ((typeof item.type === "string" && item.type.toLowerCase() === "issue") ||
364
+ (Array.isArray(item.tags) ? item.tags : []).some((tag) => typeof tag === "string" &&
365
+ ["bug", "defect", "security"].includes(tag.toLowerCase())));
366
+ }
367
+ function nonEmptyString(value) {
368
+ return typeof value === "string" && value.trim().length > 0;
369
+ }
370
+ function nonEmptyStringArray(value) {
371
+ return (Array.isArray(value) && value.length > 0 && value.every(nonEmptyString));
372
+ }
373
+ function validEvidence(value, nowMs) {
374
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
375
+ return { valid: false, expired: false };
376
+ }
377
+ const evidence = value;
378
+ if (!DEFECT_GATE_EVIDENCE_DISPOSITIONS.includes(evidence.disposition) ||
379
+ typeof evidence.owner !== "string" ||
380
+ !evidence.owner.trim()) {
381
+ return { valid: false, expired: false };
382
+ }
383
+ if (evidence.disposition === "explicit_waiver") {
384
+ const expiry = Date.parse(evidence.waiver_expires_at ?? "");
385
+ return {
386
+ valid: nonEmptyString(evidence.waiver_reason) &&
387
+ Number.isFinite(expiry) &&
388
+ expiry >= nowMs,
389
+ expired: Number.isFinite(expiry) && expiry < nowMs,
390
+ };
391
+ }
392
+ return {
393
+ valid: nonEmptyString(evidence.gate_id) &&
394
+ nonEmptyString(evidence.negative_control) &&
395
+ nonEmptyStringArray(evidence.local_checks) &&
396
+ nonEmptyStringArray(evidence.hosted_checks),
397
+ expired: false,
398
+ };
399
+ }
400
+ function evidenceFindings(item, nowMs) {
401
+ const findings = [];
402
+ if (!Number.isFinite(Date.parse(typeof item.completed_at === "string" ? item.completed_at : "")) &&
403
+ !Number.isFinite(Date.parse(typeof item.closed_at === "string" ? item.closed_at : ""))) {
404
+ findings.push({
405
+ item_id: item.id,
406
+ kind: "invalid_completion_timestamp",
407
+ detail: `${item.id} requires a valid completed_at or closed_at timestamp after the defect-evidence epoch.`,
408
+ });
409
+ }
410
+ if (typeof item.escape_class !== "string") {
411
+ findings.push({
412
+ item_id: item.id,
413
+ kind: "missing_escape_class",
414
+ detail: `${item.id} requires escape_class after the defect-evidence epoch.`,
415
+ });
416
+ }
417
+ else if (!DEFECT_ESCAPE_CLASSES.includes(item.escape_class)) {
418
+ findings.push({
419
+ item_id: item.id,
420
+ kind: "invalid_escape_class",
421
+ detail: `${item.id} has unsupported escape_class ${item.escape_class}.`,
422
+ });
423
+ }
424
+ if (item.gate_evidence === undefined) {
425
+ findings.push({
426
+ item_id: item.id,
427
+ kind: "missing_gate_evidence",
428
+ detail: `${item.id} requires structured gate_evidence after the defect-evidence epoch.`,
429
+ });
430
+ return findings;
431
+ }
432
+ const result = validEvidence(item.gate_evidence, nowMs);
433
+ if (!result.valid) {
434
+ findings.push({
435
+ item_id: item.id,
436
+ kind: result.expired ? "expired_waiver" : "invalid_gate_evidence",
437
+ detail: result.expired
438
+ ? `${item.id} carries an expired gate-evidence waiver.`
439
+ : `${item.id} gate_evidence does not prove a gate or explicit waiver.`,
440
+ });
441
+ }
442
+ return findings;
443
+ }
444
+ /** Enforce structured escape classification and gate evidence on new terminal defects. */
445
+ export function evaluateDefectGateEvidence(items, policy, terminalStatuses, now = new Date()) {
446
+ validatePolicy(policy);
447
+ const epochMs = Date.parse(policy.evidence_epoch);
448
+ const terminal = new Set(terminalStatuses);
449
+ const terminalDefects = items.filter((item) => terminal.has(item.status) && isDefectItem(item));
450
+ const governed = terminalDefects.filter((item) => {
451
+ const completedAt = Date.parse(typeof item.completed_at === "string" ? item.completed_at : "");
452
+ if (Number.isFinite(completedAt))
453
+ return completedAt >= epochMs;
454
+ const closedAt = Date.parse(typeof item.closed_at === "string" ? item.closed_at : "");
455
+ if (Number.isFinite(closedAt))
456
+ return closedAt >= epochMs;
457
+ const createdAt = Date.parse(typeof item.created_at === "string" ? item.created_at : "");
458
+ return !Number.isFinite(createdAt) || createdAt >= epochMs;
459
+ });
460
+ const classCounts = Object.fromEntries(DEFECT_ESCAPE_CLASSES.map((escapeClass) => [escapeClass, 0]));
461
+ const evidenceDispositionCounts = Object.fromEntries(DEFECT_GATE_EVIDENCE_DISPOSITIONS.map((disposition) => [disposition, 0]));
462
+ const classified = terminalDefects.filter((item) => typeof item.escape_class === "string" &&
463
+ DEFECT_ESCAPE_CLASSES.includes(item.escape_class));
464
+ for (const item of classified) {
465
+ classCounts[item.escape_class] += 1;
466
+ if (typeof item.gate_evidence === "object" &&
467
+ item.gate_evidence !== null &&
468
+ !Array.isArray(item.gate_evidence)) {
469
+ const disposition = item.gate_evidence
470
+ .disposition;
471
+ if (typeof disposition === "string" &&
472
+ DEFECT_GATE_EVIDENCE_DISPOSITIONS.includes(disposition)) {
473
+ evidenceDispositionCounts[disposition] += 1;
474
+ }
475
+ }
476
+ }
477
+ const findings = governed.flatMap((item) => evidenceFindings(item, now.getTime()));
478
+ findings.sort((left, right) => left.item_id !== right.item_id
479
+ ? left.item_id.localeCompare(right.item_id)
480
+ : left.kind.localeCompare(right.kind));
481
+ return {
482
+ ok: findings.length === 0,
483
+ governed_item_count: governed.length,
484
+ classified_item_count: classified.length,
485
+ class_counts: classCounts,
486
+ evidence_disposition_counts: evidenceDispositionCounts,
487
+ findings,
488
+ };
489
+ }
490
+ /** Parse an untrusted serialized policy without weakening runtime validation. */
491
+ export function parseDefectRecurrencePolicy(value) {
492
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
493
+ throw new TypeError("defect recurrence policy must be an object");
494
+ }
495
+ const policy = value;
496
+ if (!Array.isArray(policy.families)) {
497
+ throw new TypeError("defect recurrence policy families must be an array");
498
+ }
499
+ validatePolicy(policy);
500
+ return policy;
501
+ }
502
+ /** Parse an untrusted cross-transport change-risk request. */
503
+ export function parseDefectChangeRiskRequest(value) {
504
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
505
+ throw new TypeError("defect change-risk request must be an object");
506
+ }
507
+ const request = value;
508
+ const change = assertChangeInput(request.change, "defect change-risk request.change");
509
+ if (request.cursor !== undefined && typeof request.cursor !== "string") {
510
+ throw new TypeError("defect change-risk request.cursor must be a string");
511
+ }
512
+ if (request.limit !== undefined && !Number.isInteger(request.limit)) {
513
+ throw new TypeError("defect change-risk request.limit must be an integer");
514
+ }
515
+ return {
516
+ policy: parseDefectRecurrencePolicy(request.policy),
517
+ change,
518
+ ...(request.cursor === undefined ? {} : { cursor: request.cursor }),
519
+ ...(request.limit === undefined ? {} : { limit: request.limit }),
520
+ };
521
+ }
522
+ //# sourceMappingURL=defect-recurrence.js.map
523
+ //# debugId=59f5918f-108e-5cad-905b-7f690c3c4794
@@ -11,6 +11,8 @@ export * from "./governance/assurance-action.js";
11
11
  export * from "./governance/assurance-mutation-error.js";
12
12
  export * from "./governance/assurance-presets.js";
13
13
  export * from "./governance/assurance-runtime.js";
14
+ export * from "./governance/boundary-fixtures.js";
15
+ export * from "./governance/defect-recurrence.js";
14
16
  export { runReindex, type ReindexOptions, type ReindexResult, } from "./governance/reindex.js";
15
17
  export * from "./governance/stale-work.js";
16
18
  export * from "./governance/storage-integrity.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]="0baaaf38-2a32-5d6e-92bc-9919d2393f86")}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]="c2063b82-58b6-54c9-baff-ca252cadf463")}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";
@@ -13,6 +13,8 @@ export * from "./governance/assurance-action.js";
13
13
  export * from "./governance/assurance-mutation-error.js";
14
14
  export * from "./governance/assurance-presets.js";
15
15
  export * from "./governance/assurance-runtime.js";
16
+ export * from "./governance/boundary-fixtures.js";
17
+ export * from "./governance/defect-recurrence.js";
16
18
  export { runReindex, } from "./governance/reindex.js";
17
19
  export * from "./governance/stale-work.js";
18
20
  export * from "./governance/storage-integrity.js";
@@ -23,4 +25,4 @@ export * from "./mutation-guard.js";
23
25
  export * from "./governance/provenance-health.js";
24
26
  export * from "./similarity.js";
25
27
  //# sourceMappingURL=governance.js.map
26
- //# debugId=0baaaf38-2a32-5d6e-92bc-9919d2393f86
28
+ //# debugId=c2063b82-58b6-54c9-baff-ca252cadf463
@@ -1,5 +1,5 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="1955b7a4-385d-5a5d-8856-6db9f94814aa")}catch(e){}}();
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="b63fba34-8ca1-5e3a-905d-772ea8ce0f11")}catch(e){}}();
3
3
  import { findRedundantRelationshipEdges, isTransitiveKind, orientTransitiveEdge, transitiveFamilyKey, } from "./analytics.js";
4
4
  import { findRelationshipCutStructure } from "./centrality.js";
5
5
  import { RELATIONSHIP_AUDIT_FINDING_CODES, } from "./governance-contracts.js";
@@ -12,6 +12,7 @@ const OUTCOME_MILESTONE_TITLE_PREFIX = "outcome milestone:";
12
12
  const SEMANTIC_CONTEXT_KINDS = new Set([
13
13
  "discovered_from",
14
14
  "incident_from",
15
+ "recurs_from",
15
16
  "supersedes",
16
17
  "verifies",
17
18
  ]);
@@ -672,4 +673,4 @@ export function diffRelationshipAuditSnapshots(baseline, current) {
672
673
  };
673
674
  }
674
675
  //# sourceMappingURL=governance.js.map
675
- //# debugId=1955b7a4-385d-5a5d-8856-6db9f94814aa
676
+ //# debugId=b63fba34-8ca1-5e3a-905d-772ea8ce0f11