swipium 1.0.0

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 (387) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +295 -0
  3. package/dist/appMap/automationLink.js +74 -0
  4. package/dist/appMap/automationLink.js.map +1 -0
  5. package/dist/appMap/build.js +193 -0
  6. package/dist/appMap/build.js.map +1 -0
  7. package/dist/appMap/codeIndex.js +73 -0
  8. package/dist/appMap/codeIndex.js.map +1 -0
  9. package/dist/appMap/featureModel.js +83 -0
  10. package/dist/appMap/featureModel.js.map +1 -0
  11. package/dist/appMap/firstRunApply.js +131 -0
  12. package/dist/appMap/firstRunApply.js.map +1 -0
  13. package/dist/appMap/fsWalk.js +75 -0
  14. package/dist/appMap/fsWalk.js.map +1 -0
  15. package/dist/appMap/issues.js +227 -0
  16. package/dist/appMap/issues.js.map +1 -0
  17. package/dist/appMap/migrations.js +87 -0
  18. package/dist/appMap/migrations.js.map +1 -0
  19. package/dist/appMap/prelaunch.js +66 -0
  20. package/dist/appMap/prelaunch.js.map +1 -0
  21. package/dist/appMap/projectRegistry.js +70 -0
  22. package/dist/appMap/projectRegistry.js.map +1 -0
  23. package/dist/appMap/provenance.js +52 -0
  24. package/dist/appMap/provenance.js.map +1 -0
  25. package/dist/appMap/query.js +118 -0
  26. package/dist/appMap/query.js.map +1 -0
  27. package/dist/appMap/runtimeMerge.js +207 -0
  28. package/dist/appMap/runtimeMerge.js.map +1 -0
  29. package/dist/appMap/schema.js +65 -0
  30. package/dist/appMap/schema.js.map +1 -0
  31. package/dist/appMap/screenMatch.js +75 -0
  32. package/dist/appMap/screenMatch.js.map +1 -0
  33. package/dist/appMap/staticScan.js +614 -0
  34. package/dist/appMap/staticScan.js.map +1 -0
  35. package/dist/appMap/store.js +173 -0
  36. package/dist/appMap/store.js.map +1 -0
  37. package/dist/appMap/tsAstScan.js +184 -0
  38. package/dist/appMap/tsAstScan.js.map +1 -0
  39. package/dist/artifacts/bundletool.js +199 -0
  40. package/dist/artifacts/bundletool.js.map +1 -0
  41. package/dist/artifacts/resolve.js +388 -0
  42. package/dist/artifacts/resolve.js.map +1 -0
  43. package/dist/automationGen/appiumModel.js +129 -0
  44. package/dist/automationGen/appiumModel.js.map +1 -0
  45. package/dist/automationGen/ciEmitter.js +80 -0
  46. package/dist/automationGen/ciEmitter.js.map +1 -0
  47. package/dist/automationGen/jsEmitter.js +440 -0
  48. package/dist/automationGen/jsEmitter.js.map +1 -0
  49. package/dist/automationGen/packagePatch.js +56 -0
  50. package/dist/automationGen/packagePatch.js.map +1 -0
  51. package/dist/automationGen/projectProfile.js +317 -0
  52. package/dist/automationGen/projectProfile.js.map +1 -0
  53. package/dist/automationGen/pythonEmitter.js +386 -0
  54. package/dist/automationGen/pythonEmitter.js.map +1 -0
  55. package/dist/automationGen/readmeEmitter.js +66 -0
  56. package/dist/automationGen/readmeEmitter.js.map +1 -0
  57. package/dist/automationGen/suitePlan.js +94 -0
  58. package/dist/automationGen/suitePlan.js.map +1 -0
  59. package/dist/automationGen/validation.js +150 -0
  60. package/dist/automationGen/validation.js.map +1 -0
  61. package/dist/build/parseBuildLog.js +119 -0
  62. package/dist/build/parseBuildLog.js.map +1 -0
  63. package/dist/build/plan.js +150 -0
  64. package/dist/build/plan.js.map +1 -0
  65. package/dist/ci/preflight.js +85 -0
  66. package/dist/ci/preflight.js.map +1 -0
  67. package/dist/cli/init.js +105 -0
  68. package/dist/cli/init.js.map +1 -0
  69. package/dist/cli/scan.js +85 -0
  70. package/dist/cli/scan.js.map +1 -0
  71. package/dist/cli/suite.js +85 -0
  72. package/dist/cli/suite.js.map +1 -0
  73. package/dist/cli/verify.js +62 -0
  74. package/dist/cli/verify.js.map +1 -0
  75. package/dist/consent/consent.js +43 -0
  76. package/dist/consent/consent.js.map +1 -0
  77. package/dist/context/detect.js +156 -0
  78. package/dist/context/detect.js.map +1 -0
  79. package/dist/context/findApps.js +198 -0
  80. package/dist/context/findApps.js.map +1 -0
  81. package/dist/context/projectRoot.js +41 -0
  82. package/dist/context/projectRoot.js.map +1 -0
  83. package/dist/context/scan.js +167 -0
  84. package/dist/context/scan.js.map +1 -0
  85. package/dist/core/target.js +115 -0
  86. package/dist/core/target.js.map +1 -0
  87. package/dist/core/targetPlan.js +140 -0
  88. package/dist/core/targetPlan.js.map +1 -0
  89. package/dist/drivers/DirectDriver.js +228 -0
  90. package/dist/drivers/DirectDriver.js.map +1 -0
  91. package/dist/drivers/Driver.js +4 -0
  92. package/dist/drivers/Driver.js.map +1 -0
  93. package/dist/drivers/SimctlDriver.js +127 -0
  94. package/dist/drivers/SimctlDriver.js.map +1 -0
  95. package/dist/drivers/WdaDriver.js +250 -0
  96. package/dist/drivers/WdaDriver.js.map +1 -0
  97. package/dist/explore/candidates.js +93 -0
  98. package/dist/explore/candidates.js.map +1 -0
  99. package/dist/explore/graph.js +116 -0
  100. package/dist/explore/graph.js.map +1 -0
  101. package/dist/explore/memory.js +35 -0
  102. package/dist/explore/memory.js.map +1 -0
  103. package/dist/explore/planner.js +105 -0
  104. package/dist/explore/planner.js.map +1 -0
  105. package/dist/explore/policy.js +85 -0
  106. package/dist/explore/policy.js.map +1 -0
  107. package/dist/explore/runner.js +475 -0
  108. package/dist/explore/runner.js.map +1 -0
  109. package/dist/explore/signatures.js +35 -0
  110. package/dist/explore/signatures.js.map +1 -0
  111. package/dist/explore/suite.js +55 -0
  112. package/dist/explore/suite.js.map +1 -0
  113. package/dist/featureTesting/executionBootstrap.js +140 -0
  114. package/dist/featureTesting/executionBootstrap.js.map +1 -0
  115. package/dist/firstRun/authStateMachine.js +83 -0
  116. package/dist/firstRun/authStateMachine.js.map +1 -0
  117. package/dist/firstRun/classifyScreen.js +201 -0
  118. package/dist/firstRun/classifyScreen.js.map +1 -0
  119. package/dist/firstRun/firstRunPlanner.js +207 -0
  120. package/dist/firstRun/firstRunPlanner.js.map +1 -0
  121. package/dist/firstRun/firstRunRunner.js +331 -0
  122. package/dist/firstRun/firstRunRunner.js.map +1 -0
  123. package/dist/firstRun/generatedDataPolicy.js +215 -0
  124. package/dist/firstRun/generatedDataPolicy.js.map +1 -0
  125. package/dist/firstRun/inputPlanner.js +67 -0
  126. package/dist/firstRun/inputPlanner.js.map +1 -0
  127. package/dist/firstRun/onboardingStateMachine.js +46 -0
  128. package/dist/firstRun/onboardingStateMachine.js.map +1 -0
  129. package/dist/firstRun/paywallPolicy.js +54 -0
  130. package/dist/firstRun/paywallPolicy.js.map +1 -0
  131. package/dist/firstRun/types.js +7 -0
  132. package/dist/firstRun/types.js.map +1 -0
  133. package/dist/fixtures/catalog.js +131 -0
  134. package/dist/fixtures/catalog.js.map +1 -0
  135. package/dist/flows/discover.js +18 -0
  136. package/dist/flows/discover.js.map +1 -0
  137. package/dist/flows/generate.js +211 -0
  138. package/dist/flows/generate.js.map +1 -0
  139. package/dist/flows/lint.js +97 -0
  140. package/dist/flows/lint.js.map +1 -0
  141. package/dist/flows/pack.js +68 -0
  142. package/dist/flows/pack.js.map +1 -0
  143. package/dist/flows/run.js +738 -0
  144. package/dist/flows/run.js.map +1 -0
  145. package/dist/flows/schema.js +240 -0
  146. package/dist/flows/schema.js.map +1 -0
  147. package/dist/flows/seedExec.js +108 -0
  148. package/dist/flows/seedExec.js.map +1 -0
  149. package/dist/flows/templates.js +155 -0
  150. package/dist/flows/templates.js.map +1 -0
  151. package/dist/flows/waitGuards.js +12 -0
  152. package/dist/flows/waitGuards.js.map +1 -0
  153. package/dist/index.js +33 -0
  154. package/dist/index.js.map +1 -0
  155. package/dist/ios/signing.js +182 -0
  156. package/dist/ios/signing.js.map +1 -0
  157. package/dist/issues/classify.js +186 -0
  158. package/dist/issues/classify.js.map +1 -0
  159. package/dist/issues/fingerprint.js +130 -0
  160. package/dist/issues/fingerprint.js.map +1 -0
  161. package/dist/issues/index.js +240 -0
  162. package/dist/issues/index.js.map +1 -0
  163. package/dist/issues/metrics.js +155 -0
  164. package/dist/issues/metrics.js.map +1 -0
  165. package/dist/issues/recurrence.js +193 -0
  166. package/dist/issues/recurrence.js.map +1 -0
  167. package/dist/issues/report.js +110 -0
  168. package/dist/issues/report.js.map +1 -0
  169. package/dist/issues/reportBridge.js +119 -0
  170. package/dist/issues/reportBridge.js.map +1 -0
  171. package/dist/issues/schema.js +90 -0
  172. package/dist/issues/schema.js.map +1 -0
  173. package/dist/issues/sourceRevision.js +108 -0
  174. package/dist/issues/sourceRevision.js.map +1 -0
  175. package/dist/issues/store.js +155 -0
  176. package/dist/issues/store.js.map +1 -0
  177. package/dist/lib/android.js +278 -0
  178. package/dist/lib/android.js.map +1 -0
  179. package/dist/lib/commandTemplate.js +55 -0
  180. package/dist/lib/commandTemplate.js.map +1 -0
  181. package/dist/lib/coordSpace.js +23 -0
  182. package/dist/lib/coordSpace.js.map +1 -0
  183. package/dist/lib/device.js +87 -0
  184. package/dist/lib/device.js.map +1 -0
  185. package/dist/lib/gitignore.js +22 -0
  186. package/dist/lib/gitignore.js.map +1 -0
  187. package/dist/lib/image.js +97 -0
  188. package/dist/lib/image.js.map +1 -0
  189. package/dist/lib/logger.js +8 -0
  190. package/dist/lib/logger.js.map +1 -0
  191. package/dist/lib/metroState.js +45 -0
  192. package/dist/lib/metroState.js.map +1 -0
  193. package/dist/lib/needsInput.js +201 -0
  194. package/dist/lib/needsInput.js.map +1 -0
  195. package/dist/lib/png.js +103 -0
  196. package/dist/lib/png.js.map +1 -0
  197. package/dist/lib/redact.js +21 -0
  198. package/dist/lib/redact.js.map +1 -0
  199. package/dist/lib/result.js +87 -0
  200. package/dist/lib/result.js.map +1 -0
  201. package/dist/lib/schemaHash.js +65 -0
  202. package/dist/lib/schemaHash.js.map +1 -0
  203. package/dist/lib/sensitive.js +13 -0
  204. package/dist/lib/sensitive.js.map +1 -0
  205. package/dist/lib/simctl.js +149 -0
  206. package/dist/lib/simctl.js.map +1 -0
  207. package/dist/lib/spawn.js +144 -0
  208. package/dist/lib/spawn.js.map +1 -0
  209. package/dist/lib/wda.js +347 -0
  210. package/dist/lib/wda.js.map +1 -0
  211. package/dist/lib/wdaConfig.js +69 -0
  212. package/dist/lib/wdaConfig.js.map +1 -0
  213. package/dist/lib/wdaTune.js +58 -0
  214. package/dist/lib/wdaTune.js.map +1 -0
  215. package/dist/oracle/auth.js +22 -0
  216. package/dist/oracle/auth.js.map +1 -0
  217. package/dist/oracle/failures.js +193 -0
  218. package/dist/oracle/failures.js.map +1 -0
  219. package/dist/oracle/health.js +123 -0
  220. package/dist/oracle/health.js.map +1 -0
  221. package/dist/oracle/record.js +36 -0
  222. package/dist/oracle/record.js.map +1 -0
  223. package/dist/orchestration/envelope.js +52 -0
  224. package/dist/orchestration/envelope.js.map +1 -0
  225. package/dist/orchestration/goal.js +98 -0
  226. package/dist/orchestration/goal.js.map +1 -0
  227. package/dist/plan/plan.js +124 -0
  228. package/dist/plan/plan.js.map +1 -0
  229. package/dist/prompts/index.js +63 -0
  230. package/dist/prompts/index.js.map +1 -0
  231. package/dist/report/artifactManifest.js +44 -0
  232. package/dist/report/artifactManifest.js.map +1 -0
  233. package/dist/report/coverage.js +60 -0
  234. package/dist/report/coverage.js.map +1 -0
  235. package/dist/report/evidence.js +84 -0
  236. package/dist/report/evidence.js.map +1 -0
  237. package/dist/report/export.js +447 -0
  238. package/dist/report/export.js.map +1 -0
  239. package/dist/report/flake.js +61 -0
  240. package/dist/report/flake.js.map +1 -0
  241. package/dist/report/history.js +564 -0
  242. package/dist/report/history.js.map +1 -0
  243. package/dist/report/policy.js +98 -0
  244. package/dist/report/policy.js.map +1 -0
  245. package/dist/report/qaLevel.js +96 -0
  246. package/dist/report/qaLevel.js.map +1 -0
  247. package/dist/report/readiness.js +213 -0
  248. package/dist/report/readiness.js.map +1 -0
  249. package/dist/report/summary.js +42 -0
  250. package/dist/report/summary.js.map +1 -0
  251. package/dist/report/testCatalog.js +93 -0
  252. package/dist/report/testCatalog.js.map +1 -0
  253. package/dist/report/timing.js +37 -0
  254. package/dist/report/timing.js.map +1 -0
  255. package/dist/server.js +180 -0
  256. package/dist/server.js.map +1 -0
  257. package/dist/services/automationGenerate.js +60 -0
  258. package/dist/services/automationGenerate.js.map +1 -0
  259. package/dist/services/build.js +66 -0
  260. package/dist/services/build.js.map +1 -0
  261. package/dist/services/preflight.js +51 -0
  262. package/dist/services/preflight.js.map +1 -0
  263. package/dist/services/prepareAndroid.js +183 -0
  264. package/dist/services/prepareAndroid.js.map +1 -0
  265. package/dist/services/prepareIos.js +162 -0
  266. package/dist/services/prepareIos.js.map +1 -0
  267. package/dist/services/report.js +537 -0
  268. package/dist/services/report.js.map +1 -0
  269. package/dist/services/smoke.js +120 -0
  270. package/dist/services/smoke.js.map +1 -0
  271. package/dist/services/suiteGenerate.js +123 -0
  272. package/dist/services/suiteGenerate.js.map +1 -0
  273. package/dist/services/testSuiteKnowledge.js +150 -0
  274. package/dist/services/testSuiteKnowledge.js.map +1 -0
  275. package/dist/session/attach.js +47 -0
  276. package/dist/session/attach.js.map +1 -0
  277. package/dist/session/progress.js +56 -0
  278. package/dist/session/progress.js.map +1 -0
  279. package/dist/session/store.js +329 -0
  280. package/dist/session/store.js.map +1 -0
  281. package/dist/snapshot/overlays.js +132 -0
  282. package/dist/snapshot/overlays.js.map +1 -0
  283. package/dist/snapshot/parse.js +204 -0
  284. package/dist/snapshot/parse.js.map +1 -0
  285. package/dist/snapshot/present.js +12 -0
  286. package/dist/snapshot/present.js.map +1 -0
  287. package/dist/snapshot/settle.js +36 -0
  288. package/dist/snapshot/settle.js.map +1 -0
  289. package/dist/state/profile.js +318 -0
  290. package/dist/state/profile.js.map +1 -0
  291. package/dist/suite/compile.js +176 -0
  292. package/dist/suite/compile.js.map +1 -0
  293. package/dist/suite/lint.js +45 -0
  294. package/dist/suite/lint.js.map +1 -0
  295. package/dist/suite/pom.js +285 -0
  296. package/dist/suite/pom.js.map +1 -0
  297. package/dist/suite/testcase.js +87 -0
  298. package/dist/suite/testcase.js.map +1 -0
  299. package/dist/testSuite/generator.js +222 -0
  300. package/dist/testSuite/generator.js.map +1 -0
  301. package/dist/testSuite/history.js +44 -0
  302. package/dist/testSuite/history.js.map +1 -0
  303. package/dist/testSuite/issueLinks.js +96 -0
  304. package/dist/testSuite/issueLinks.js.map +1 -0
  305. package/dist/testSuite/merge.js +190 -0
  306. package/dist/testSuite/merge.js.map +1 -0
  307. package/dist/testSuite/schema.js +86 -0
  308. package/dist/testSuite/schema.js.map +1 -0
  309. package/dist/testSuite/store.js +116 -0
  310. package/dist/testSuite/store.js.map +1 -0
  311. package/dist/testSuite/traceability.js +55 -0
  312. package/dist/testSuite/traceability.js.map +1 -0
  313. package/dist/tools/act.js +457 -0
  314. package/dist/tools/act.js.map +1 -0
  315. package/dist/tools/agent.js +225 -0
  316. package/dist/tools/agent.js.map +1 -0
  317. package/dist/tools/appMap.js +317 -0
  318. package/dist/tools/appMap.js.map +1 -0
  319. package/dist/tools/assertVisual.js +74 -0
  320. package/dist/tools/assertVisual.js.map +1 -0
  321. package/dist/tools/automationGenerate.js +394 -0
  322. package/dist/tools/automationGenerate.js.map +1 -0
  323. package/dist/tools/capabilities.js +114 -0
  324. package/dist/tools/capabilities.js.map +1 -0
  325. package/dist/tools/clearOverlay.js +150 -0
  326. package/dist/tools/clearOverlay.js.map +1 -0
  327. package/dist/tools/detectContext.js +41 -0
  328. package/dist/tools/detectContext.js.map +1 -0
  329. package/dist/tools/doctor.js +116 -0
  330. package/dist/tools/doctor.js.map +1 -0
  331. package/dist/tools/explore.js +289 -0
  332. package/dist/tools/explore.js.map +1 -0
  333. package/dist/tools/firstRun.js +136 -0
  334. package/dist/tools/firstRun.js.map +1 -0
  335. package/dist/tools/flow.js +331 -0
  336. package/dist/tools/flow.js.map +1 -0
  337. package/dist/tools/flowGenerate.js +55 -0
  338. package/dist/tools/flowGenerate.js.map +1 -0
  339. package/dist/tools/getArtifact.js +39 -0
  340. package/dist/tools/getArtifact.js.map +1 -0
  341. package/dist/tools/health.js +41 -0
  342. package/dist/tools/health.js.map +1 -0
  343. package/dist/tools/ios.js +492 -0
  344. package/dist/tools/ios.js.map +1 -0
  345. package/dist/tools/jobs.js +20 -0
  346. package/dist/tools/jobs.js.map +1 -0
  347. package/dist/tools/network.js +50 -0
  348. package/dist/tools/network.js.map +1 -0
  349. package/dist/tools/note.js +69 -0
  350. package/dist/tools/note.js.map +1 -0
  351. package/dist/tools/plan.js +49 -0
  352. package/dist/tools/plan.js.map +1 -0
  353. package/dist/tools/prepareIosTarget.js +99 -0
  354. package/dist/tools/prepareIosTarget.js.map +1 -0
  355. package/dist/tools/prepareTarget.js +257 -0
  356. package/dist/tools/prepareTarget.js.map +1 -0
  357. package/dist/tools/report.js +42 -0
  358. package/dist/tools/report.js.map +1 -0
  359. package/dist/tools/screenRecord.js +24 -0
  360. package/dist/tools/screenRecord.js.map +1 -0
  361. package/dist/tools/screenshot.js +63 -0
  362. package/dist/tools/screenshot.js.map +1 -0
  363. package/dist/tools/smoke.js +39 -0
  364. package/dist/tools/smoke.js.map +1 -0
  365. package/dist/tools/snapshot.js +129 -0
  366. package/dist/tools/snapshot.js.map +1 -0
  367. package/dist/tools/startSession.js +169 -0
  368. package/dist/tools/startSession.js.map +1 -0
  369. package/dist/tools/state.js +87 -0
  370. package/dist/tools/state.js.map +1 -0
  371. package/dist/tools/suite.js +412 -0
  372. package/dist/tools/suite.js.map +1 -0
  373. package/dist/tools/testThis.js +774 -0
  374. package/dist/tools/testThis.js.map +1 -0
  375. package/dist/tools/wda.js +477 -0
  376. package/dist/tools/wda.js.map +1 -0
  377. package/dist/version.js +62 -0
  378. package/dist/version.js.map +1 -0
  379. package/dist/visual/ocr.js +72 -0
  380. package/dist/visual/ocr.js.map +1 -0
  381. package/dist/visual/provider.js +75 -0
  382. package/dist/visual/provider.js.map +1 -0
  383. package/docs/README.md +19 -0
  384. package/docs/assets/swipium-lockup-ink-on-light.png +0 -0
  385. package/docs/mcp-server.md +135 -0
  386. package/docs/tools.md +130 -0
  387. package/package.json +70 -0
@@ -0,0 +1,240 @@
1
+ // SWIPIUM Issue Log — high-level service + query layer (SWIPIUM-REQ-07).
2
+ //
3
+ // This is the single entry point callers (health oracle, reports, explore, mobile audit, tools)
4
+ // use to record observations and mutate lifecycle. It ties together fingerprinting, classification,
5
+ // the append-only store, and the lifecycle reducer, then keeps the derived index in sync.
6
+ //
7
+ // The id helpers are deterministic given (issueId, createdAt, eventType, seq) so events are stable
8
+ // and tests are reproducible without a clock.
9
+ import { createHash } from 'node:crypto';
10
+ import { classifyObservation } from './classify.js';
11
+ import { fingerprint, issueIdFromFingerprint } from './fingerprint.js';
12
+ import { buildRecurrenceMessage, foldEvent } from './recurrence.js';
13
+ import { appendEvents, eventCountForIssue, findRecord, getIndex, loadPolicy, saveIndex, } from './store.js';
14
+ import { ISSUE_SCHEMA_VERSION } from './schema.js';
15
+ /** Deterministic, collision-resistant event id from its content. */
16
+ export function makeEventId(issueId, createdAt, eventType, seq = 0) {
17
+ const h = createHash('sha256').update(`${issueId}|${createdAt}|${eventType}|${seq}`).digest('hex').slice(0, 12);
18
+ return `evt_${h}`;
19
+ }
20
+ /**
21
+ * Record one observation: fingerprint → classify → decide lifecycle → append event(s) → update
22
+ * index. Reopens a fixed issue (with recurrence message) when its fingerprint is seen again.
23
+ */
24
+ export function recordObservation(root, observation, now, ctx = {}, meta = {}) {
25
+ const policy = loadPolicy(root);
26
+ const fp = fingerprint({
27
+ failureCode: observation.failureCode,
28
+ platform: meta.platform,
29
+ appId: meta.appId,
30
+ observation,
31
+ });
32
+ const issueId = issueIdFromFingerprint(fp);
33
+ const classification = classifyObservation(observation, { ...ctx, policy: mergePolicy(policy, ctx) });
34
+ const index = getIndex(root, now, meta.appId);
35
+ const prev = findRecord(index, { issueId, fingerprint: fp }) ?? null;
36
+ const wasFixed = prev?.state === 'fixed';
37
+ // The observation count is a monotonic per-issue sequence (each observation increments it), so it
38
+ // gives distinct event ids for two same-timestamp observations WITHOUT re-reading the whole log.
39
+ let seq = prev?.observationCount ?? 0;
40
+ const observed = {
41
+ schemaVersion: ISSUE_SCHEMA_VERSION,
42
+ eventId: makeEventId(issueId, now, 'observed', seq++),
43
+ issueId,
44
+ fingerprint: fp,
45
+ eventType: 'observed',
46
+ createdAt: now,
47
+ appId: meta.appId,
48
+ appName: meta.appName,
49
+ platform: meta.platform,
50
+ environment: meta.environment,
51
+ sourceRevision: meta.sourceRevision,
52
+ run: meta.run,
53
+ observation,
54
+ classification,
55
+ links: meta.links,
56
+ };
57
+ const events = [observed];
58
+ let record = foldEvent(prev, observed);
59
+ // Recurrence: append an explicit `reopened` event so the audit trail is honest (spec §Recurrence).
60
+ let reopened = false;
61
+ let recurrenceMessage;
62
+ if (wasFixed && record.state === 'reopened') {
63
+ reopened = true;
64
+ recurrenceMessage = record.lastRecurrenceMessage ?? buildRecurrenceMessage(record);
65
+ const reopenEvent = {
66
+ schemaVersion: ISSUE_SCHEMA_VERSION,
67
+ eventId: makeEventId(issueId, now, 'reopened', seq++),
68
+ issueId,
69
+ fingerprint: fp,
70
+ eventType: 'reopened',
71
+ createdAt: now,
72
+ platform: meta.platform,
73
+ environment: meta.environment,
74
+ lifecycle: { state: 'reopened', reopenedAt: now, recurrenceMessage },
75
+ };
76
+ events.push(reopenEvent);
77
+ record = foldEvent(record, reopenEvent);
78
+ }
79
+ appendEvents(root, events);
80
+ upsertRecord(index, record);
81
+ index.updatedAt = now;
82
+ saveIndex(root, index);
83
+ return { record, events, isNew: prev == null, reopened, recurrenceMessage, fingerprint: fp, issueId };
84
+ }
85
+ /** Append a `fixed` lifecycle event and update the index. */
86
+ export function markFixed(root, key, patch, now) {
87
+ return applyLifecycle(root, key, now, 'fixed', {
88
+ state: 'fixed',
89
+ fixedAt: now,
90
+ fixedInCommit: patch.fixedInCommit ?? patch.sourceRevision?.commit,
91
+ fixedInVersion: patch.fixedInVersion ?? patch.sourceRevision?.buildVersion,
92
+ howFixed: patch.howFixed,
93
+ fixedBy: patch.fixedBy,
94
+ }, patch.sourceRevision);
95
+ }
96
+ /** Append a `triaged` lifecycle event (change category/severity/owner/note). */
97
+ export function triageIssue(root, key, patch, now) {
98
+ return applyLifecycle(root, key, now, 'triaged', {
99
+ category: patch.category,
100
+ severity: patch.severity,
101
+ owner: patch.owner,
102
+ note: patch.note,
103
+ });
104
+ }
105
+ /** Append a `suppressed` lifecycle event. Suppressed issues stay visible under known-noise. */
106
+ export function suppressIssue(root, key, patch, now) {
107
+ return applyLifecycle(root, key, now, 'suppressed', {
108
+ state: 'suppressed',
109
+ suppressionReason: patch.reason,
110
+ suppressedUntil: patch.until,
111
+ suppressionScope: patch.scope ?? 'fingerprint',
112
+ });
113
+ }
114
+ /**
115
+ * Append a `linked_run` event tying a run (test case / audit check) to an issue with a relationship
116
+ * (observed / verified_fixed / regressed / suppressed) and optional evidence. The issue ledger stays
117
+ * the source of truth; this records HOW a run related to the issue (REQ-08).
118
+ */
119
+ export function linkRun(root, key, opts, now) {
120
+ const index = getIndex(root, now);
121
+ const existing = findRecord(index, key);
122
+ if (!existing)
123
+ return { ok: false, reason: `No issue found for ${key.issueId ?? key.fingerprint ?? '(no key)'}` };
124
+ const event = {
125
+ schemaVersion: ISSUE_SCHEMA_VERSION,
126
+ eventId: makeEventId(existing.issueId, now, 'linked_run', eventCountForIssue(root, existing.issueId)),
127
+ issueId: existing.issueId,
128
+ fingerprint: existing.fingerprint,
129
+ eventType: 'linked_run',
130
+ createdAt: now,
131
+ sourceRevision: opts.sourceRevision,
132
+ relationship: opts.relationship,
133
+ run: opts.reportUri || opts.reportPath || opts.testCaseId ? { reportUri: opts.reportUri, reportPath: opts.reportPath, testCaseId: opts.testCaseId } : undefined,
134
+ links: {
135
+ evidenceRefs: opts.evidenceUris?.map((uri) => ({ kind: 'evidence', uri })),
136
+ testRefs: opts.testCaseId ? [{ testCaseId: opts.testCaseId }] : undefined,
137
+ appMapRefs: opts.appMapRefs,
138
+ },
139
+ };
140
+ appendEvents(root, [event]);
141
+ const updated = foldEvent(existing, event);
142
+ upsertRecord(index, updated);
143
+ index.updatedAt = now;
144
+ saveIndex(root, index);
145
+ return { ok: true, record: updated };
146
+ }
147
+ /**
148
+ * Verify a FIXED issue with current-run evidence (REQ-08 `qa_issue_verify_fixed`). Requires the issue
149
+ * to be in state `fixed` and at least one evidence reference (report/test/audit). Appends a
150
+ * `verified_fixed` linked_run event so reports can honestly claim "verified this run".
151
+ */
152
+ export function verifyFixed(root, key, opts, now) {
153
+ const index = getIndex(root, now);
154
+ const existing = findRecord(index, key);
155
+ if (!existing)
156
+ return { ok: false, reason: `No issue found for ${key.issueId ?? key.fingerprint ?? '(no key)'}` };
157
+ if (existing.state !== 'fixed')
158
+ return { ok: false, reason: `Issue ${existing.issueId} is "${existing.state}", not "fixed" — only a fixed issue can be verified` };
159
+ const hasEvidence = Boolean(opts.reportUri || opts.testCaseId || opts.auditCheckId || (opts.evidenceUris && opts.evidenceUris.length));
160
+ if (!hasEvidence)
161
+ return { ok: false, reason: 'verify_fixed needs current-run evidence: a reportUri, testCaseId, auditCheckId, or evidenceUris' };
162
+ return linkRun(root, { issueId: existing.issueId }, {
163
+ relationship: 'verified_fixed',
164
+ reportUri: opts.reportUri,
165
+ testCaseId: opts.testCaseId ?? opts.auditCheckId,
166
+ evidenceUris: opts.evidenceUris,
167
+ sourceRevision: opts.sourceRevision,
168
+ }, now);
169
+ }
170
+ function applyLifecycle(root, key, now, eventType, lifecycle, sourceRevision) {
171
+ const index = getIndex(root, now);
172
+ const existing = findRecord(index, key);
173
+ if (!existing) {
174
+ return { ok: false, reason: `No issue found for ${key.issueId ?? key.fingerprint ?? '(no key)'}` };
175
+ }
176
+ const event = {
177
+ schemaVersion: ISSUE_SCHEMA_VERSION,
178
+ eventId: makeEventId(existing.issueId, now, eventType, eventCountForIssue(root, existing.issueId)),
179
+ issueId: existing.issueId,
180
+ fingerprint: existing.fingerprint,
181
+ eventType,
182
+ createdAt: now,
183
+ sourceRevision,
184
+ lifecycle,
185
+ };
186
+ appendEvents(root, [event]);
187
+ const updated = foldEvent(existing, event);
188
+ upsertRecord(index, updated);
189
+ index.updatedAt = now;
190
+ saveIndex(root, index);
191
+ return { ok: true, record: updated };
192
+ }
193
+ function upsertRecord(index, record) {
194
+ const i = index.records.findIndex((r) => r.issueId === record.issueId);
195
+ if (i >= 0)
196
+ index.records[i] = record;
197
+ else
198
+ index.records.push(record);
199
+ }
200
+ function mergePolicy(policy, ctx) {
201
+ // ctx.policy (in-memory) takes precedence; otherwise use the on-disk policy's classifiers.
202
+ if (ctx.policy?.classifiers)
203
+ return ctx.policy;
204
+ if (policy.classifiers)
205
+ return policy;
206
+ return undefined;
207
+ }
208
+ export function queryIssues(root, now, query = {}) {
209
+ const index = getIndex(root, now);
210
+ let records = index.records.slice();
211
+ if (!query.includeSuppressed)
212
+ records = records.filter((r) => r.state !== 'suppressed');
213
+ if (query.state)
214
+ records = records.filter((r) => r.state === query.state);
215
+ if (query.category)
216
+ records = records.filter((r) => r.category === query.category);
217
+ if (query.severity)
218
+ records = records.filter((r) => r.severity === query.severity);
219
+ if (query.platform)
220
+ records = records.filter((r) => r.platform === query.platform);
221
+ if (query.since)
222
+ records = records.filter((r) => r.lastSeenAt >= query.since);
223
+ const counts = {
224
+ byState: tally(records, (r) => r.state),
225
+ byCategory: tally(records, (r) => r.category),
226
+ bySeverity: tally(records, (r) => r.severity),
227
+ total: records.length,
228
+ };
229
+ const recurrenceCandidates = records.filter((r) => r.state === 'reopened' || r.lastRecurrenceMessage);
230
+ return { records, counts, recurrenceCandidates };
231
+ }
232
+ function tally(rows, key) {
233
+ return rows.reduce((a, r) => ((a[key(r)] = (a[key(r)] ?? 0) + 1), a), {});
234
+ }
235
+ export * from './schema.js';
236
+ export { fingerprint, issueIdFromFingerprint } from './fingerprint.js';
237
+ export { classifyObservation } from './classify.js';
238
+ export { resolveSourceRevision } from './sourceRevision.js';
239
+ export { buildRecurrenceMessage } from './recurrence.js';
240
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/issues/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,155 @@
1
+ // SWIPIUM-REQ-08 — issue quality metrics, derived from the append-only EVENT log (not just current
2
+ // index state) so trends are honest: opened / fixed / reopened / verified-fixed counts, issue aging,
3
+ // reopen rate (fixed issues that later reopened), fix-verification rate (fixed issues with a
4
+ // verified_fixed event), and a time/version/category series. PURE — `until` is the reference clock.
5
+ import { rebuildRecords } from './recurrence.js';
6
+ function inRange(at, since, until) {
7
+ if (since && at < since)
8
+ return false;
9
+ if (until && at > until)
10
+ return false;
11
+ return true;
12
+ }
13
+ function isoWeek(iso) {
14
+ const d = new Date(iso);
15
+ if (Number.isNaN(d.getTime()))
16
+ return iso.slice(0, 10);
17
+ const day = (d.getUTCDay() + 6) % 7; // Mon=0
18
+ const thursday = new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate() - day + 3));
19
+ const firstThursday = new Date(Date.UTC(thursday.getUTCFullYear(), 0, 4));
20
+ const week = 1 + Math.round(((thursday.getTime() - firstThursday.getTime()) / 86400000 - 3 + ((firstThursday.getUTCDay() + 6) % 7)) / 7);
21
+ return `${thursday.getUTCFullYear()}-W${String(week).padStart(2, '0')}`;
22
+ }
23
+ function bucketKey(ev, groupBy) {
24
+ switch (groupBy) {
25
+ case 'day':
26
+ return ev.createdAt.slice(0, 10);
27
+ case 'week':
28
+ return isoWeek(ev.createdAt);
29
+ case 'version':
30
+ return ev.sourceRevision?.buildVersion ?? 'unknown';
31
+ case 'commit':
32
+ return ev.sourceRevision?.commit ?? 'unknown';
33
+ case 'category':
34
+ return ev.classification?.category ?? 'unknown';
35
+ case 'owner':
36
+ return ev.classification?.owner ?? 'unknown';
37
+ case 'screen':
38
+ return ev.links?.appMapRefs?.[0]?.screenId ?? ev.observation?.screenId ?? 'unknown';
39
+ case 'feature':
40
+ return ev.links?.appMapRefs?.[0]?.featureId ?? 'unknown';
41
+ default:
42
+ return ev.createdAt.slice(0, 10);
43
+ }
44
+ }
45
+ function ageDays(fromIso, untilIso) {
46
+ const a = new Date(fromIso).getTime();
47
+ const b = new Date(untilIso).getTime();
48
+ if (Number.isNaN(a) || Number.isNaN(b))
49
+ return 0;
50
+ return Math.max(0, (b - a) / 86400000);
51
+ }
52
+ function percentile(sorted, p) {
53
+ if (sorted.length === 0)
54
+ return 0;
55
+ const idx = Math.min(sorted.length - 1, Math.ceil((p / 100) * sorted.length) - 1);
56
+ return sorted[Math.max(0, idx)];
57
+ }
58
+ /** Compute issue metrics from the full event stream. `until` defaults to the latest event time. */
59
+ export function computeIssueMetrics(events, opts = {}) {
60
+ const groupBy = opts.groupBy ?? 'week';
61
+ const until = opts.until ?? events.reduce((m, e) => (e.createdAt > m ? e.createdAt : m), '1970-01-01T00:00:00.000Z');
62
+ const records = rebuildRecords(events);
63
+ // First-observation EVENT per issue → "opened". Track the event id (not just the timestamp) so
64
+ // two same-timestamp observations of one fingerprint count as a single open (REQ-08 follow-up).
65
+ const firstObserved = new Map();
66
+ for (const e of events) {
67
+ if (e.eventType !== 'observed')
68
+ continue;
69
+ const prev = firstObserved.get(e.issueId);
70
+ if (!prev || e.createdAt < prev.at)
71
+ firstObserved.set(e.issueId, { at: e.createdAt, eventId: e.eventId });
72
+ }
73
+ let opened = 0;
74
+ for (const [, first] of firstObserved)
75
+ if (inRange(first.at, opts.since, opts.until))
76
+ opened++;
77
+ let fixed = 0;
78
+ let reopened = 0;
79
+ let verifiedFixed = 0;
80
+ let suppressed = 0;
81
+ const everFixed = new Set();
82
+ const fixedThenReopened = new Set();
83
+ const verifiedFixedIssues = new Set();
84
+ const fixedAtById = new Map();
85
+ const series = new Map();
86
+ const bump = (key, field) => {
87
+ const b = series.get(key) ?? { opened: 0, fixed: 0, reopened: 0, verifiedFixed: 0 };
88
+ b[field]++;
89
+ series.set(key, b);
90
+ };
91
+ for (const e of events) {
92
+ const within = inRange(e.createdAt, opts.since, opts.until);
93
+ if (e.eventType === 'observed' && firstObserved.get(e.issueId)?.eventId === e.eventId && within)
94
+ bump(bucketKey(e, groupBy), 'opened');
95
+ if (e.eventType === 'fixed') {
96
+ everFixed.add(e.issueId);
97
+ fixedAtById.set(e.issueId, e.createdAt);
98
+ if (within) {
99
+ fixed++;
100
+ bump(bucketKey(e, groupBy), 'fixed');
101
+ }
102
+ }
103
+ if (e.eventType === 'reopened') {
104
+ // a reopen counts toward reopen-rate only if it followed a fix.
105
+ if (everFixed.has(e.issueId))
106
+ fixedThenReopened.add(e.issueId);
107
+ if (within) {
108
+ reopened++;
109
+ bump(bucketKey(e, groupBy), 'reopened');
110
+ }
111
+ }
112
+ if (e.eventType === 'linked_run' && e.relationship === 'verified_fixed') {
113
+ verifiedFixedIssues.add(e.issueId);
114
+ if (within) {
115
+ verifiedFixed++;
116
+ bump(bucketKey(e, groupBy), 'verifiedFixed');
117
+ }
118
+ }
119
+ if (e.eventType === 'suppressed' && within)
120
+ suppressed++;
121
+ }
122
+ const noiseIssues = records.filter((r) => (opts.includeSuppressed || r.state !== 'suppressed') && (r.category === 'environment_noise' || r.state === 'expected_environment_noise'));
123
+ const openRecords = records.filter((r) => r.state === 'open' || r.state === 'observed_again' || r.state === 'reopened' || r.state === 'needs_triage');
124
+ const ages = openRecords.map((r) => ageDays(r.firstSeenAt, until)).sort((a, b) => a - b);
125
+ const avgAgeDays = ages.length ? Number((ages.reduce((s, v) => s + v, 0) / ages.length).toFixed(2)) : 0;
126
+ const p95AgeDays = Number(percentile(ages, 95).toFixed(2));
127
+ const reopenRatePct = everFixed.size ? Number(((fixedThenReopened.size / everFixed.size) * 100).toFixed(1)) : 0;
128
+ const fixVerificationRatePct = everFixed.size ? Number(((verifiedFixedIssues.size / everFixed.size) * 100).toFixed(1)) : 0;
129
+ const topRecurringIssues = records
130
+ .filter((r) => r.lastRecurrenceMessage || r.state === 'reopened')
131
+ .sort((a, b) => (b.lastSeenAt > a.lastSeenAt ? 1 : -1))
132
+ .slice(0, 5);
133
+ const topAgingIssues = openRecords
134
+ .slice()
135
+ .sort((a, b) => ageDays(b.firstSeenAt, until) - ageDays(a.firstSeenAt, until))
136
+ .slice(0, 5);
137
+ return {
138
+ opened,
139
+ fixed,
140
+ reopened,
141
+ verifiedFixed,
142
+ suppressed,
143
+ environmentNoise: noiseIssues.length,
144
+ avgAgeDays,
145
+ p95AgeDays,
146
+ reopenRatePct,
147
+ fixVerificationRatePct,
148
+ blockerOpenCount: openRecords.filter((r) => r.severity === 'blocker').length,
149
+ highOpenCount: openRecords.filter((r) => r.severity === 'high').length,
150
+ topRecurringIssues,
151
+ topAgingIssues,
152
+ series: [...series.entries()].sort((a, b) => (a[0] < b[0] ? -1 : 1)).map(([bucket, v]) => ({ bucket, ...v })),
153
+ };
154
+ }
155
+ //# sourceMappingURL=metrics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics.js","sourceRoot":"","sources":["../../src/issues/metrics.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,193 @@
1
+ // SWIPIUM Issue Log — lifecycle folding + recurrence messages (SWIPIUM-REQ-07 "Recurrence Behavior").
2
+ //
3
+ // PURE. `foldEvent` is the single reducer that turns the append-only event stream into the derived
4
+ // index record — so the index can always be rebuilt from `issues-log.jsonl`. `buildRecurrenceMessage`
5
+ // produces the product-facing text shown in reports when a fixed issue reappears.
6
+ import { ISSUE_SCHEMA_VERSION } from './schema.js';
7
+ /** Format an ISO timestamp as a YYYY-MM-DD date for human-facing recurrence text. */
8
+ export function isoDate(iso) {
9
+ if (!iso)
10
+ return 'an unknown date';
11
+ const m = iso.match(/^(\d{4}-\d{2}-\d{2})/);
12
+ return m ? m[1] : iso;
13
+ }
14
+ /**
15
+ * Build the report-ready recurrence message for an issue that was observed again.
16
+ * Three variants per the spec: fixed-with-commit, fixed-without-commit, never-fixed.
17
+ */
18
+ export function buildRecurrenceMessage(record) {
19
+ const summary = record.summary || record.title;
20
+ if (record.fixedAt && record.fixedInCommit) {
21
+ const note = record.howFixed ? ` Previous fix note: ${record.howFixed}.` : '';
22
+ return `This error ${summary} appeared again. It was first reported on ${isoDate(record.firstSeenAt)} and fixed on ${isoDate(record.fixedAt)} in ${record.fixedInCommit}.${note}`;
23
+ }
24
+ if (record.fixedAt) {
25
+ return `This error ${summary} appeared again. It was first reported on ${isoDate(record.firstSeenAt)} and fixed on ${isoDate(record.fixedAt)}. No fixed commit was recorded.`;
26
+ }
27
+ return `This error ${summary} was observed again. It was first reported on ${isoDate(record.firstSeenAt)} and has appeared ${record.observationCount} times.`;
28
+ }
29
+ function mergeRefs(existing, incoming, key) {
30
+ if (!existing && !incoming)
31
+ return undefined;
32
+ const seen = new Map();
33
+ for (const r of existing ?? [])
34
+ seen.set(key(r), r);
35
+ for (const r of incoming ?? [])
36
+ seen.set(key(r), r);
37
+ return [...seen.values()];
38
+ }
39
+ function mergeLinks(record, links) {
40
+ if (!links)
41
+ return;
42
+ record.appMapRefs = mergeRefs(record.appMapRefs, links.appMapRefs, (r) => `${r.screenId ?? ''}|${r.featureId ?? ''}`);
43
+ record.testRefs = mergeRefs(record.testRefs, links.testRefs, (r) => r.testCaseId);
44
+ record.reportRefs = mergeRefs(record.reportRefs, links.reportRefs, (r) => `${r.reportPath ?? ''}|${r.reportUri ?? ''}`);
45
+ record.evidenceRefs = mergeRefs(record.evidenceRefs, links.evidenceRefs, (r) => `${r.kind}|${r.uri ?? r.path ?? ''}`);
46
+ }
47
+ /**
48
+ * Fold a single event onto the running index record. Returns the updated record (a new object).
49
+ * `prev` is null when this is the first event for the issue. This reducer is the canonical
50
+ * definition of "what state is this issue in", and is replayed in order to rebuild the index.
51
+ */
52
+ export function foldEvent(prev, event) {
53
+ const obs = event.observation;
54
+ const cls = event.classification;
55
+ const lc = event.lifecycle;
56
+ const record = prev != null
57
+ ? { ...prev }
58
+ : {
59
+ schemaVersion: ISSUE_SCHEMA_VERSION,
60
+ issueId: event.issueId,
61
+ fingerprint: event.fingerprint,
62
+ title: obs?.title ?? 'Untitled issue',
63
+ summary: obs?.summary ?? obs?.title ?? 'issue',
64
+ state: 'needs_triage',
65
+ category: cls?.category ?? 'mcp_limitation',
66
+ severity: cls?.severity ?? 'low',
67
+ confidence: cls?.confidence ?? 0.5,
68
+ owner: cls?.owner,
69
+ platform: event.platform,
70
+ environment: event.environment,
71
+ firstSeenAt: event.createdAt,
72
+ lastSeenAt: event.createdAt,
73
+ observationCount: 0,
74
+ };
75
+ // Keep the most descriptive title/summary if a later observation has one.
76
+ if (obs?.title)
77
+ record.title = obs.title;
78
+ if (obs?.summary)
79
+ record.summary = obs.summary;
80
+ if (event.platform && record.platform == null)
81
+ record.platform = event.platform;
82
+ if (event.environment && record.environment == null)
83
+ record.environment = event.environment;
84
+ // Classification updates (latest classification wins for category/severity/owner/confidence).
85
+ if (cls) {
86
+ record.category = cls.category;
87
+ record.severity = cls.severity;
88
+ record.owner = cls.owner;
89
+ record.confidence = cls.confidence;
90
+ }
91
+ mergeLinks(record, event.links);
92
+ if (event.run) {
93
+ const reportRef = { reportPath: event.run.reportPath, reportUri: event.run.reportUri };
94
+ if (reportRef.reportPath || reportRef.reportUri) {
95
+ record.reportRefs = mergeRefs(record.reportRefs, [reportRef], (r) => `${r.reportPath ?? ''}|${r.reportUri ?? ''}`);
96
+ }
97
+ if (event.run.testCaseId) {
98
+ record.testRefs = mergeRefs(record.testRefs, [{ testCaseId: event.run.testCaseId }], (r) => r.testCaseId);
99
+ }
100
+ }
101
+ switch (event.eventType) {
102
+ case 'observed': {
103
+ record.observationCount += 1;
104
+ record.lastSeenAt = event.createdAt;
105
+ // Recurrence: an observation of a FIXED issue reopens it.
106
+ if (record.state === 'fixed') {
107
+ record.state = 'reopened';
108
+ record.reopenedAt = event.createdAt;
109
+ record.lastRecurrenceMessage = buildRecurrenceMessage(record);
110
+ }
111
+ else if (record.state === 'open' || record.state === 'observed_again') {
112
+ record.state = 'observed_again';
113
+ }
114
+ else if (record.state === 'needs_triage') {
115
+ // first observation of a noise/gate stays in its classified lane; otherwise open.
116
+ record.state = isNoiseCategory(record) ? 'expected_environment_noise' : 'open';
117
+ }
118
+ else if (record.state === 'reopened') {
119
+ // stays reopened, just bump counts
120
+ }
121
+ else if (record.state === 'suppressed' || record.state === 'expected_environment_noise') {
122
+ // suppressed/noise: keep the lane (still visible in known-noise), bump counts only.
123
+ }
124
+ break;
125
+ }
126
+ case 'classified': {
127
+ // pure re-classification of an existing record; lane derived from category.
128
+ if (record.state === 'needs_triage')
129
+ record.state = isNoiseCategory(record) ? 'expected_environment_noise' : 'open';
130
+ break;
131
+ }
132
+ case 'fixed': {
133
+ record.state = 'fixed';
134
+ record.fixedAt = lc?.fixedAt ?? event.createdAt;
135
+ record.fixedInCommit = lc?.fixedInCommit ?? event.sourceRevision?.commit ?? record.fixedInCommit;
136
+ record.fixedInVersion = lc?.fixedInVersion ?? event.sourceRevision?.buildVersion ?? record.fixedInVersion;
137
+ record.fixedBy = lc?.fixedBy ?? record.fixedBy;
138
+ record.howFixed = lc?.howFixed ?? record.howFixed;
139
+ break;
140
+ }
141
+ case 'reopened': {
142
+ record.state = 'reopened';
143
+ record.reopenedAt = lc?.reopenedAt ?? event.createdAt;
144
+ record.lastRecurrenceMessage = lc?.recurrenceMessage ?? buildRecurrenceMessage(record);
145
+ break;
146
+ }
147
+ case 'suppressed': {
148
+ record.state = 'suppressed';
149
+ record.suppressedUntil = lc?.suppressedUntil;
150
+ record.suppressionReason = lc?.suppressionReason;
151
+ record.suppressionScope = lc?.suppressionScope;
152
+ break;
153
+ }
154
+ case 'triaged': {
155
+ if (lc?.category)
156
+ record.category = lc.category;
157
+ if (lc?.severity)
158
+ record.severity = lc.severity;
159
+ if (lc?.owner)
160
+ record.owner = lc.owner;
161
+ break;
162
+ }
163
+ case 'linked_run': {
164
+ // Evidence from a run (test case / audit check), not a state change — links already merged.
165
+ // A `verified_fixed` link records that a passing run confirmed the fix still holds (REQ-08).
166
+ if (event.relationship === 'verified_fixed' && record.state === 'fixed') {
167
+ record.lastVerifiedFixedAt = event.createdAt;
168
+ }
169
+ break;
170
+ }
171
+ case 'note_added':
172
+ // links already merged above; no state change.
173
+ break;
174
+ }
175
+ // Apply any explicit state override carried on a lifecycle patch last.
176
+ if (lc?.state)
177
+ record.state = lc.state;
178
+ return record;
179
+ }
180
+ function isNoiseCategory(record) {
181
+ return record.category === 'environment_noise';
182
+ }
183
+ /** Replay an ordered event stream into the full set of index records (rebuild). */
184
+ export function rebuildRecords(events) {
185
+ const byId = new Map();
186
+ for (const ev of events) {
187
+ const prev = byId.get(ev.issueId) ?? null;
188
+ byId.set(ev.issueId, foldEvent(prev, ev));
189
+ }
190
+ return [...byId.values()];
191
+ }
192
+ export const ALL_STATES_FOR_TEST = ['open', 'fixed', 'reopened'];
193
+ //# sourceMappingURL=recurrence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recurrence.js","sourceRoot":"","sources":["../../src/issues/recurrence.ts"],"names":[],"mappings":"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"}