@oscharko-dev/keiko 0.2.0-beta.7 → 0.2.0-beta.9

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 (314) hide show
  1. package/dist/ui/csp-hashes.json +14 -14
  2. package/dist/ui/static/404.html +1 -1
  3. package/dist/ui/static/__next.__PAGE__.txt +2 -2
  4. package/dist/ui/static/__next._full.txt +3 -3
  5. package/dist/ui/static/__next._head.txt +1 -1
  6. package/dist/ui/static/__next._index.txt +2 -2
  7. package/dist/ui/static/__next._tree.txt +2 -2
  8. package/dist/ui/static/_next/static/chunks/06-q1ntr51v8r.js +1 -0
  9. package/dist/ui/static/_next/static/chunks/0y9qlvrxsyign.js +109 -0
  10. package/dist/ui/static/_next/static/chunks/1h0v66-p9cx19.css +1 -0
  11. package/dist/ui/static/_next/static/chunks/3ewv4mguqzooj.js +1 -0
  12. package/dist/ui/static/_not-found/__next._full.txt +2 -2
  13. package/dist/ui/static/_not-found/__next._head.txt +1 -1
  14. package/dist/ui/static/_not-found/__next._index.txt +2 -2
  15. package/dist/ui/static/_not-found/__next._not-found.__PAGE__.txt +1 -1
  16. package/dist/ui/static/_not-found/__next._not-found.txt +1 -1
  17. package/dist/ui/static/_not-found/__next._tree.txt +2 -2
  18. package/dist/ui/static/_not-found.html +1 -1
  19. package/dist/ui/static/_not-found.txt +2 -2
  20. package/dist/ui/static/fonts/OFL.txt +93 -0
  21. package/dist/ui/static/fonts/jetbrains-mono-latin-wght-normal.woff2 +0 -0
  22. package/dist/ui/static/index.html +1 -1
  23. package/dist/ui/static/index.txt +3 -3
  24. package/dist/ui/static/launch/__next._full.txt +3 -3
  25. package/dist/ui/static/launch/__next._head.txt +1 -1
  26. package/dist/ui/static/launch/__next._index.txt +2 -2
  27. package/dist/ui/static/launch/__next._tree.txt +2 -2
  28. package/dist/ui/static/launch/__next.launch.__PAGE__.txt +2 -2
  29. package/dist/ui/static/launch/__next.launch.txt +1 -1
  30. package/dist/ui/static/launch.html +1 -1
  31. package/dist/ui/static/launch.txt +3 -3
  32. package/dist/ui/static/local-knowledge/__next._full.txt +3 -3
  33. package/dist/ui/static/local-knowledge/__next._head.txt +1 -1
  34. package/dist/ui/static/local-knowledge/__next._index.txt +2 -2
  35. package/dist/ui/static/local-knowledge/__next._tree.txt +2 -2
  36. package/dist/ui/static/local-knowledge/__next.local-knowledge.__PAGE__.txt +2 -2
  37. package/dist/ui/static/local-knowledge/__next.local-knowledge.txt +1 -1
  38. package/dist/ui/static/local-knowledge/capsule/__next._full.txt +3 -3
  39. package/dist/ui/static/local-knowledge/capsule/__next._head.txt +1 -1
  40. package/dist/ui/static/local-knowledge/capsule/__next._index.txt +2 -2
  41. package/dist/ui/static/local-knowledge/capsule/__next._tree.txt +2 -2
  42. package/dist/ui/static/local-knowledge/capsule/__next.local-knowledge.capsule.__PAGE__.txt +2 -2
  43. package/dist/ui/static/local-knowledge/capsule/__next.local-knowledge.capsule.txt +1 -1
  44. package/dist/ui/static/local-knowledge/capsule/__next.local-knowledge.txt +1 -1
  45. package/dist/ui/static/local-knowledge/capsule.html +1 -1
  46. package/dist/ui/static/local-knowledge/capsule.txt +3 -3
  47. package/dist/ui/static/local-knowledge.html +1 -1
  48. package/dist/ui/static/local-knowledge.txt +3 -3
  49. package/dist/ui/static/memoriaviva/__next._full.txt +2 -2
  50. package/dist/ui/static/memoriaviva/__next._head.txt +1 -1
  51. package/dist/ui/static/memoriaviva/__next._index.txt +2 -2
  52. package/dist/ui/static/memoriaviva/__next._tree.txt +2 -2
  53. package/dist/ui/static/memoriaviva/__next.memoriaviva.__PAGE__.txt +1 -1
  54. package/dist/ui/static/memoriaviva/__next.memoriaviva.txt +1 -1
  55. package/dist/ui/static/memoriaviva/consolidation/__next._full.txt +2 -2
  56. package/dist/ui/static/memoriaviva/consolidation/__next._head.txt +1 -1
  57. package/dist/ui/static/memoriaviva/consolidation/__next._index.txt +2 -2
  58. package/dist/ui/static/memoriaviva/consolidation/__next._tree.txt +2 -2
  59. package/dist/ui/static/memoriaviva/consolidation/__next.memoriaviva.consolidation.__PAGE__.txt +1 -1
  60. package/dist/ui/static/memoriaviva/consolidation/__next.memoriaviva.consolidation.txt +1 -1
  61. package/dist/ui/static/memoriaviva/consolidation/__next.memoriaviva.txt +1 -1
  62. package/dist/ui/static/memoriaviva/consolidation.html +1 -1
  63. package/dist/ui/static/memoriaviva/consolidation.txt +2 -2
  64. package/dist/ui/static/memoriaviva/detail/__next._full.txt +2 -2
  65. package/dist/ui/static/memoriaviva/detail/__next._head.txt +1 -1
  66. package/dist/ui/static/memoriaviva/detail/__next._index.txt +2 -2
  67. package/dist/ui/static/memoriaviva/detail/__next._tree.txt +2 -2
  68. package/dist/ui/static/memoriaviva/detail/__next.memoriaviva.detail.__PAGE__.txt +1 -1
  69. package/dist/ui/static/memoriaviva/detail/__next.memoriaviva.detail.txt +1 -1
  70. package/dist/ui/static/memoriaviva/detail/__next.memoriaviva.txt +1 -1
  71. package/dist/ui/static/memoriaviva/detail.html +1 -1
  72. package/dist/ui/static/memoriaviva/detail.txt +2 -2
  73. package/dist/ui/static/memoriaviva/review-queue/__next._full.txt +2 -2
  74. package/dist/ui/static/memoriaviva/review-queue/__next._head.txt +1 -1
  75. package/dist/ui/static/memoriaviva/review-queue/__next._index.txt +2 -2
  76. package/dist/ui/static/memoriaviva/review-queue/__next._tree.txt +2 -2
  77. package/dist/ui/static/memoriaviva/review-queue/__next.memoriaviva.review-queue.__PAGE__.txt +1 -1
  78. package/dist/ui/static/memoriaviva/review-queue/__next.memoriaviva.review-queue.txt +1 -1
  79. package/dist/ui/static/memoriaviva/review-queue/__next.memoriaviva.txt +1 -1
  80. package/dist/ui/static/memoriaviva/review-queue.html +1 -1
  81. package/dist/ui/static/memoriaviva/review-queue.txt +2 -2
  82. package/dist/ui/static/memoriaviva.html +1 -1
  83. package/dist/ui/static/memoriaviva.txt +2 -2
  84. package/dist/ui/static/sw.js +7 -3
  85. package/node_modules/@oscharko-dev/keiko-cli/dist/.tsbuildinfo +1 -1
  86. package/node_modules/@oscharko-dev/keiko-cli/dist/memory.d.ts.map +1 -1
  87. package/node_modules/@oscharko-dev/keiko-cli/dist/memory.js +4 -5
  88. package/node_modules/@oscharko-dev/keiko-cli/package.json +1 -1
  89. package/node_modules/@oscharko-dev/keiko-contracts/dist/.tsbuildinfo +1 -1
  90. package/node_modules/@oscharko-dev/keiko-contracts/dist/index.d.ts +2 -2
  91. package/node_modules/@oscharko-dev/keiko-contracts/dist/index.d.ts.map +1 -1
  92. package/node_modules/@oscharko-dev/keiko-contracts/dist/index.js +1 -1
  93. package/node_modules/@oscharko-dev/keiko-contracts/dist/qualityIntelligence/bffWire.d.ts +36 -4
  94. package/node_modules/@oscharko-dev/keiko-contracts/dist/qualityIntelligence/bffWire.d.ts.map +1 -1
  95. package/node_modules/@oscharko-dev/keiko-contracts/dist/qualityIntelligence/index.d.ts +1 -1
  96. package/node_modules/@oscharko-dev/keiko-contracts/dist/qualityIntelligence/index.d.ts.map +1 -1
  97. package/node_modules/@oscharko-dev/keiko-contracts/package.json +1 -1
  98. package/node_modules/@oscharko-dev/keiko-evaluations/dist/.tsbuildinfo +1 -1
  99. package/node_modules/@oscharko-dev/keiko-evaluations/package.json +1 -1
  100. package/node_modules/@oscharko-dev/keiko-evidence/dist/.tsbuildinfo +1 -1
  101. package/node_modules/@oscharko-dev/keiko-evidence/dist/index.d.ts +1 -1
  102. package/node_modules/@oscharko-dev/keiko-evidence/dist/index.d.ts.map +1 -1
  103. package/node_modules/@oscharko-dev/keiko-evidence/dist/qualityIntelligence/figmaSnapshot/schema.d.ts +15 -0
  104. package/node_modules/@oscharko-dev/keiko-evidence/dist/qualityIntelligence/figmaSnapshot/schema.d.ts.map +1 -1
  105. package/node_modules/@oscharko-dev/keiko-evidence/dist/qualityIntelligence/figmaSnapshot/schema.js +13 -3
  106. package/node_modules/@oscharko-dev/keiko-evidence/dist/qualityIntelligence/figmaSnapshot/store.d.ts +13 -0
  107. package/node_modules/@oscharko-dev/keiko-evidence/dist/qualityIntelligence/figmaSnapshot/store.d.ts.map +1 -1
  108. package/node_modules/@oscharko-dev/keiko-evidence/dist/qualityIntelligence/figmaSnapshot/store.js +46 -2
  109. package/node_modules/@oscharko-dev/keiko-evidence/dist/qualityIntelligence/index.d.ts +2 -2
  110. package/node_modules/@oscharko-dev/keiko-evidence/dist/qualityIntelligence/index.d.ts.map +1 -1
  111. package/node_modules/@oscharko-dev/keiko-evidence/package.json +1 -1
  112. package/node_modules/@oscharko-dev/keiko-harness/dist/.tsbuildinfo +1 -1
  113. package/node_modules/@oscharko-dev/keiko-harness/package.json +1 -1
  114. package/node_modules/@oscharko-dev/keiko-local-knowledge/dist/.tsbuildinfo +1 -1
  115. package/node_modules/@oscharko-dev/keiko-local-knowledge/dist/parsers/pdf-parser.d.ts.map +1 -1
  116. package/node_modules/@oscharko-dev/keiko-local-knowledge/dist/parsers/pdf-parser.js +0 -10
  117. package/node_modules/@oscharko-dev/keiko-local-knowledge/dist/parsers/types.d.ts +2 -2
  118. package/node_modules/@oscharko-dev/keiko-local-knowledge/dist/parsers/types.d.ts.map +1 -1
  119. package/node_modules/@oscharko-dev/keiko-local-knowledge/dist/parsers/types.js +3 -3
  120. package/node_modules/@oscharko-dev/keiko-local-knowledge/package.json +1 -1
  121. package/node_modules/@oscharko-dev/keiko-memory-capture/package.json +1 -1
  122. package/node_modules/@oscharko-dev/keiko-memory-consolidation/package.json +1 -1
  123. package/node_modules/@oscharko-dev/keiko-memory-governance/dist/.tsbuildinfo +1 -1
  124. package/node_modules/@oscharko-dev/keiko-memory-governance/dist/index.d.ts +1 -1
  125. package/node_modules/@oscharko-dev/keiko-memory-governance/dist/index.d.ts.map +1 -1
  126. package/node_modules/@oscharko-dev/keiko-memory-governance/dist/index.js +1 -1
  127. package/node_modules/@oscharko-dev/keiko-memory-governance/dist/maintenance.d.ts +2 -16
  128. package/node_modules/@oscharko-dev/keiko-memory-governance/dist/maintenance.d.ts.map +1 -1
  129. package/node_modules/@oscharko-dev/keiko-memory-governance/dist/maintenance.js +49 -48
  130. package/node_modules/@oscharko-dev/keiko-memory-governance/dist/retention.d.ts +2 -1
  131. package/node_modules/@oscharko-dev/keiko-memory-governance/dist/retention.d.ts.map +1 -1
  132. package/node_modules/@oscharko-dev/keiko-memory-governance/dist/retention.js +15 -0
  133. package/node_modules/@oscharko-dev/keiko-memory-governance/package.json +1 -1
  134. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/.tsbuildinfo +1 -1
  135. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/decay.d.ts +2 -0
  136. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/decay.d.ts.map +1 -0
  137. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/decay.js +22 -0
  138. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/diversity.d.ts +8 -0
  139. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/diversity.d.ts.map +1 -0
  140. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/diversity.js +87 -0
  141. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/index.d.ts +4 -2
  142. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/index.d.ts.map +1 -1
  143. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/index.js +4 -1
  144. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/ranking.d.ts +5 -1
  145. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/ranking.d.ts.map +1 -1
  146. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/ranking.js +140 -21
  147. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/recency.d.ts.map +1 -1
  148. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/recency.js +4 -10
  149. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/retrieve.d.ts.map +1 -1
  150. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/retrieve.js +11 -1
  151. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/strength.d.ts +9 -0
  152. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/strength.d.ts.map +1 -0
  153. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/strength.js +51 -0
  154. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/types.d.ts +11 -0
  155. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/types.d.ts.map +1 -1
  156. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/types.js +7 -0
  157. package/node_modules/@oscharko-dev/keiko-memory-retrieval/package.json +1 -1
  158. package/node_modules/@oscharko-dev/keiko-memory-vault/dist/.tsbuildinfo +1 -1
  159. package/node_modules/@oscharko-dev/keiko-memory-vault/dist/access.d.ts +3 -0
  160. package/node_modules/@oscharko-dev/keiko-memory-vault/dist/access.d.ts.map +1 -1
  161. package/node_modules/@oscharko-dev/keiko-memory-vault/dist/access.js +31 -1
  162. package/node_modules/@oscharko-dev/keiko-memory-vault/dist/schema.d.ts +1 -1
  163. package/node_modules/@oscharko-dev/keiko-memory-vault/dist/schema.d.ts.map +1 -1
  164. package/node_modules/@oscharko-dev/keiko-memory-vault/dist/schema.js +16 -1
  165. package/node_modules/@oscharko-dev/keiko-memory-vault/dist/types.d.ts +1 -0
  166. package/node_modules/@oscharko-dev/keiko-memory-vault/dist/types.d.ts.map +1 -1
  167. package/node_modules/@oscharko-dev/keiko-memory-vault/dist/vault.d.ts.map +1 -1
  168. package/node_modules/@oscharko-dev/keiko-memory-vault/dist/vault.js +4 -1
  169. package/node_modules/@oscharko-dev/keiko-memory-vault/package.json +1 -1
  170. package/node_modules/@oscharko-dev/keiko-model-gateway/dist/.tsbuildinfo +1 -1
  171. package/node_modules/@oscharko-dev/keiko-model-gateway/package.json +1 -1
  172. package/node_modules/@oscharko-dev/keiko-quality-intelligence/dist/.tsbuildinfo +1 -1
  173. package/node_modules/@oscharko-dev/keiko-quality-intelligence/dist/domain/figma/screenIrTestBaseline.d.ts +8 -0
  174. package/node_modules/@oscharko-dev/keiko-quality-intelligence/dist/domain/figma/screenIrTestBaseline.d.ts.map +1 -1
  175. package/node_modules/@oscharko-dev/keiko-quality-intelligence/dist/domain/figma/screenIrTestBaseline.js +9 -5
  176. package/node_modules/@oscharko-dev/keiko-quality-intelligence/dist/domain/testDesignModel.d.ts +6 -0
  177. package/node_modules/@oscharko-dev/keiko-quality-intelligence/dist/domain/testDesignModel.d.ts.map +1 -1
  178. package/node_modules/@oscharko-dev/keiko-quality-intelligence/dist/domain/testDesignModel.js +116 -21
  179. package/node_modules/@oscharko-dev/keiko-quality-intelligence/dist/domain/testQualityRubric.d.ts +6 -0
  180. package/node_modules/@oscharko-dev/keiko-quality-intelligence/dist/domain/testQualityRubric.d.ts.map +1 -1
  181. package/node_modules/@oscharko-dev/keiko-quality-intelligence/dist/domain/testQualityRubric.js +12 -0
  182. package/node_modules/@oscharko-dev/keiko-quality-intelligence/dist/domain/validation.d.ts.map +1 -1
  183. package/node_modules/@oscharko-dev/keiko-quality-intelligence/dist/domain/validation.js +8 -7
  184. package/node_modules/@oscharko-dev/keiko-quality-intelligence/dist/generation/parseGeneratedCandidates.d.ts.map +1 -1
  185. package/node_modules/@oscharko-dev/keiko-quality-intelligence/dist/generation/parseGeneratedCandidates.js +29 -7
  186. package/node_modules/@oscharko-dev/keiko-quality-intelligence/dist/generation/prompt.d.ts.map +1 -1
  187. package/node_modules/@oscharko-dev/keiko-quality-intelligence/dist/generation/prompt.js +61 -24
  188. package/node_modules/@oscharko-dev/keiko-quality-intelligence/dist/index.d.ts +1 -1
  189. package/node_modules/@oscharko-dev/keiko-quality-intelligence/dist/index.d.ts.map +1 -1
  190. package/node_modules/@oscharko-dev/keiko-quality-intelligence/dist/index.js +1 -1
  191. package/node_modules/@oscharko-dev/keiko-quality-intelligence/package.json +1 -1
  192. package/node_modules/@oscharko-dev/keiko-sdk/dist/.tsbuildinfo +1 -1
  193. package/node_modules/@oscharko-dev/keiko-sdk/package.json +1 -1
  194. package/node_modules/@oscharko-dev/keiko-security/dist/.tsbuildinfo +1 -1
  195. package/node_modules/@oscharko-dev/keiko-security/package.json +1 -1
  196. package/node_modules/@oscharko-dev/keiko-server/dist/.tsbuildinfo +1 -1
  197. package/node_modules/@oscharko-dev/keiko-server/dist/chat-handlers.d.ts.map +1 -1
  198. package/node_modules/@oscharko-dev/keiko-server/dist/chat-handlers.js +44 -65
  199. package/node_modules/@oscharko-dev/keiko-server/dist/conversation-prompt.d.ts.map +1 -1
  200. package/node_modules/@oscharko-dev/keiko-server/dist/conversation-prompt.js +2 -0
  201. package/node_modules/@oscharko-dev/keiko-server/dist/deps.d.ts +2 -0
  202. package/node_modules/@oscharko-dev/keiko-server/dist/deps.d.ts.map +1 -1
  203. package/node_modules/@oscharko-dev/keiko-server/dist/deps.js +1 -0
  204. package/node_modules/@oscharko-dev/keiko-server/dist/gateway-setup.d.ts.map +1 -1
  205. package/node_modules/@oscharko-dev/keiko-server/dist/gateway-setup.js +333 -70
  206. package/node_modules/@oscharko-dev/keiko-server/dist/grounded-orchestrator.d.ts +1 -0
  207. package/node_modules/@oscharko-dev/keiko-server/dist/grounded-orchestrator.d.ts.map +1 -1
  208. package/node_modules/@oscharko-dev/keiko-server/dist/grounded-orchestrator.js +410 -18
  209. package/node_modules/@oscharko-dev/keiko-server/dist/grounded-prompt.d.ts.map +1 -1
  210. package/node_modules/@oscharko-dev/keiko-server/dist/grounded-prompt.js +1 -0
  211. package/node_modules/@oscharko-dev/keiko-server/dist/grounded-qa-hybrid.d.ts.map +1 -1
  212. package/node_modules/@oscharko-dev/keiko-server/dist/grounded-qa-hybrid.js +7 -5
  213. package/node_modules/@oscharko-dev/keiko-server/dist/grounded-qa-multi-source.js +4 -3
  214. package/node_modules/@oscharko-dev/keiko-server/dist/grounded-qa.d.ts +1 -0
  215. package/node_modules/@oscharko-dev/keiko-server/dist/grounded-qa.d.ts.map +1 -1
  216. package/node_modules/@oscharko-dev/keiko-server/dist/grounded-qa.js +4 -1
  217. package/node_modules/@oscharko-dev/keiko-server/dist/local-knowledge-grounded-qa.d.ts +1 -0
  218. package/node_modules/@oscharko-dev/keiko-server/dist/local-knowledge-grounded-qa.d.ts.map +1 -1
  219. package/node_modules/@oscharko-dev/keiko-server/dist/local-knowledge-grounded-qa.js +8 -3
  220. package/node_modules/@oscharko-dev/keiko-server/dist/memory-conv-handlers.d.ts.map +1 -1
  221. package/node_modules/@oscharko-dev/keiko-server/dist/memory-conv-handlers.js +34 -5
  222. package/node_modules/@oscharko-dev/keiko-server/dist/memory-embedding.d.ts +12 -2
  223. package/node_modules/@oscharko-dev/keiko-server/dist/memory-embedding.d.ts.map +1 -1
  224. package/node_modules/@oscharko-dev/keiko-server/dist/memory-embedding.js +127 -0
  225. package/node_modules/@oscharko-dev/keiko-server/dist/memory-handlers.d.ts.map +1 -1
  226. package/node_modules/@oscharko-dev/keiko-server/dist/memory-handlers.js +40 -11
  227. package/node_modules/@oscharko-dev/keiko-server/dist/memory-maintenance-handlers.d.ts +16 -3
  228. package/node_modules/@oscharko-dev/keiko-server/dist/memory-maintenance-handlers.d.ts.map +1 -1
  229. package/node_modules/@oscharko-dev/keiko-server/dist/memory-maintenance-handlers.js +72 -50
  230. package/node_modules/@oscharko-dev/keiko-server/dist/memory-retrieval-signals.d.ts +12 -0
  231. package/node_modules/@oscharko-dev/keiko-server/dist/memory-retrieval-signals.d.ts.map +1 -0
  232. package/node_modules/@oscharko-dev/keiko-server/dist/memory-retrieval-signals.js +84 -0
  233. package/node_modules/@oscharko-dev/keiko-server/dist/memory-salience.d.ts.map +1 -1
  234. package/node_modules/@oscharko-dev/keiko-server/dist/memory-salience.js +11 -6
  235. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/figma/figmaConnector.d.ts.map +1 -1
  236. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/figma/figmaConnector.js +9 -4
  237. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/figma/figmaScopedPagination.d.ts +6 -5
  238. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/figma/figmaScopedPagination.d.ts.map +1 -1
  239. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/figma/figmaScopedPagination.js +10 -9
  240. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/figma/figmaSnapshotBuilder.d.ts +2 -1
  241. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/figma/figmaSnapshotBuilder.d.ts.map +1 -1
  242. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/figma/figmaSnapshotBuilder.js +62 -26
  243. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/figma/figmaSnapshotHash.d.ts +2 -0
  244. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/figma/figmaSnapshotHash.d.ts.map +1 -1
  245. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/figma/figmaSnapshotHash.js +6 -3
  246. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/figma/figmaSnapshotTypes.d.ts +14 -0
  247. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/figma/figmaSnapshotTypes.d.ts.map +1 -1
  248. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/figmaSnapshotRoutes.d.ts +33 -0
  249. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/figmaSnapshotRoutes.d.ts.map +1 -1
  250. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/figmaSnapshotRoutes.js +263 -5
  251. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/figmaSnapshotScreenIds.d.ts +19 -0
  252. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/figmaSnapshotScreenIds.d.ts.map +1 -0
  253. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/figmaSnapshotScreenIds.js +75 -0
  254. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/judgePort.d.ts.map +1 -1
  255. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/judgePort.js +13 -12
  256. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/modelSelection.d.ts +6 -6
  257. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/modelSelection.d.ts.map +1 -1
  258. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/modelSelection.js +12 -7
  259. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/reCheckRoutes.d.ts.map +1 -1
  260. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/reCheckRoutes.js +34 -3
  261. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/runIngestion.d.ts +4 -2
  262. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/runIngestion.d.ts.map +1 -1
  263. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/runIngestion.js +222 -35
  264. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/runRoutes.d.ts +7 -0
  265. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/runRoutes.d.ts.map +1 -1
  266. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/runRoutes.js +68 -16
  267. package/node_modules/@oscharko-dev/keiko-server/dist/routes.d.ts.map +1 -1
  268. package/node_modules/@oscharko-dev/keiko-server/dist/routes.js +7 -1
  269. package/node_modules/@oscharko-dev/keiko-server/package.json +1 -1
  270. package/node_modules/@oscharko-dev/keiko-tools/dist/.tsbuildinfo +1 -1
  271. package/node_modules/@oscharko-dev/keiko-tools/package.json +1 -1
  272. package/node_modules/@oscharko-dev/keiko-verification/dist/.tsbuildinfo +1 -1
  273. package/node_modules/@oscharko-dev/keiko-verification/package.json +1 -1
  274. package/node_modules/@oscharko-dev/keiko-workflows/dist/.tsbuildinfo +1 -1
  275. package/node_modules/@oscharko-dev/keiko-workflows/dist/planner/anchors.d.ts.map +1 -1
  276. package/node_modules/@oscharko-dev/keiko-workflows/dist/planner/anchors.js +45 -0
  277. package/node_modules/@oscharko-dev/keiko-workflows/dist/planner/index.d.ts +2 -0
  278. package/node_modules/@oscharko-dev/keiko-workflows/dist/planner/index.d.ts.map +1 -1
  279. package/node_modules/@oscharko-dev/keiko-workflows/dist/planner/index.js +1 -0
  280. package/node_modules/@oscharko-dev/keiko-workflows/dist/planner/intent.d.ts +8 -0
  281. package/node_modules/@oscharko-dev/keiko-workflows/dist/planner/intent.d.ts.map +1 -0
  282. package/node_modules/@oscharko-dev/keiko-workflows/dist/planner/intent.js +140 -0
  283. package/node_modules/@oscharko-dev/keiko-workflows/dist/planner/plan.d.ts +2 -0
  284. package/node_modules/@oscharko-dev/keiko-workflows/dist/planner/plan.d.ts.map +1 -1
  285. package/node_modules/@oscharko-dev/keiko-workflows/dist/planner/plan.js +21 -5
  286. package/node_modules/@oscharko-dev/keiko-workflows/dist/qualityIntelligence/modelRoutedTestDesign.d.ts.map +1 -1
  287. package/node_modules/@oscharko-dev/keiko-workflows/dist/qualityIntelligence/modelRoutedTestDesign.js +86 -40
  288. package/node_modules/@oscharko-dev/keiko-workflows/dist/ranking/rank.d.ts.map +1 -1
  289. package/node_modules/@oscharko-dev/keiko-workflows/dist/ranking/rank.js +9 -0
  290. package/node_modules/@oscharko-dev/keiko-workflows/package.json +1 -1
  291. package/node_modules/@oscharko-dev/keiko-workspace/dist/.tsbuildinfo +1 -1
  292. package/node_modules/@oscharko-dev/keiko-workspace/dist/index.d.ts +1 -0
  293. package/node_modules/@oscharko-dev/keiko-workspace/dist/index.d.ts.map +1 -1
  294. package/node_modules/@oscharko-dev/keiko-workspace/dist/repoSearch.d.ts +3 -0
  295. package/node_modules/@oscharko-dev/keiko-workspace/dist/repoSearch.d.ts.map +1 -1
  296. package/node_modules/@oscharko-dev/keiko-workspace/dist/repoSearch.js +23 -6
  297. package/node_modules/@oscharko-dev/keiko-workspace/dist/repoSearchMatchers.d.ts +1 -1
  298. package/node_modules/@oscharko-dev/keiko-workspace/dist/repoSearchMatchers.d.ts.map +1 -1
  299. package/node_modules/@oscharko-dev/keiko-workspace/dist/repoSearchMatchers.js +24 -3
  300. package/node_modules/@oscharko-dev/keiko-workspace/dist/repoSearchPolicy.d.ts +34 -0
  301. package/node_modules/@oscharko-dev/keiko-workspace/dist/repoSearchPolicy.d.ts.map +1 -0
  302. package/node_modules/@oscharko-dev/keiko-workspace/dist/repoSearchPolicy.js +342 -0
  303. package/node_modules/@oscharko-dev/keiko-workspace/dist/repoSearchScan.d.ts +6 -1
  304. package/node_modules/@oscharko-dev/keiko-workspace/dist/repoSearchScan.d.ts.map +1 -1
  305. package/node_modules/@oscharko-dev/keiko-workspace/dist/repoSearchScan.js +57 -9
  306. package/node_modules/@oscharko-dev/keiko-workspace/package.json +1 -1
  307. package/package.json +2 -1
  308. package/dist/ui/static/_next/static/chunks/0-qhhdvxg2j_y.js +0 -1
  309. package/dist/ui/static/_next/static/chunks/0ke4ratkgvcxo.css +0 -1
  310. package/dist/ui/static/_next/static/chunks/3vf3oh2-sl2nc.js +0 -1
  311. package/dist/ui/static/_next/static/chunks/3wmd4-2vznp2g.js +0 -106
  312. /package/dist/ui/static/_next/static/{frhs0YcUqCPLHal-wHjDP → a5sUbXeTgQ9A2LRTMu_Q_}/_buildManifest.js +0 -0
  313. /package/dist/ui/static/_next/static/{frhs0YcUqCPLHal-wHjDP → a5sUbXeTgQ9A2LRTMu_Q_}/_clientMiddlewareManifest.js +0 -0
  314. /package/dist/ui/static/_next/static/{frhs0YcUqCPLHal-wHjDP → a5sUbXeTgQ9A2LRTMu_Q_}/_ssgManifest.js +0 -0
@@ -7,7 +7,8 @@
7
7
  // 2. downloads the PNG bytes from that ephemeral url via the injectable render port (no auth
8
8
  // header — the url is pre-signed);
9
9
  // 3. assembles the immutable Snapshot value: per-screen IR + image + deterministic integrity
10
- // hashes + provenance, with a `skippedScreens` list for any screen that failed to render.
10
+ // hashes + provenance, with a `skippedScreens` list plus structural-only IR rows for any
11
+ // screen that failed to render.
11
12
  //
12
13
  // Once this returns, the Snapshot is the communication boundary: nothing downstream contacts
13
14
  // Figma. The token flows ONLY into the images-call header; it never reaches the snapshot value,
@@ -16,7 +17,7 @@ import { FigmaConnectorError } from "./figmaConnectorErrors.js";
16
17
  import { mapWithConcurrency } from "./figmaConcurrency.js";
17
18
  import { classifyTokenFailure } from "./figmaTokenSource.js";
18
19
  import { DEFAULT_FIGMA_RETRY_POLICY, fetchWithBackoff, realFigmaRetrySleep, } from "./figmaRetry.js";
19
- import { hashBytes, hashScreen, hashSnapshot } from "./figmaSnapshotHash.js";
20
+ import { hashBytes, hashScreen, hashSnapshot, hashStructuralScreen } from "./figmaSnapshotHash.js";
20
21
  import { DEFAULT_SCOPED_PAGINATION_LIMITS, SCOPED_PAGINATION_LIMIT_CEILINGS, } from "./figmaScopedPagination.js";
21
22
  const FIGMA_API_ORIGIN = "https://api.figma.com";
22
23
  const SNAPSHOT_SCHEMA_VERSION = 1;
@@ -130,15 +131,24 @@ const extractImageUrls = (json) => {
130
131
  }
131
132
  return out;
132
133
  };
134
+ // Figma's render endpoint can reject a subset of otherwise readable screen node ids with a client
135
+ // status (observed on large boards where some detected screen frames cannot be rendered). That is a
136
+ // renderability problem, not an auth/egress/build integrity problem: keep the snapshot usable by
137
+ // returning an empty render-url map for that batch so the affected screens become structural-only.
138
+ // Auth/scope/proxy/rate-limit/upstream failures remain hard coded errors below.
139
+ const isRenderEndpointSoftFailure = (status) => status === 400 || status === 404 || status === 422;
133
140
  // Resolve one `/v1/images` batch to its ephemeral-url map. The fetch is wrapped in the deterministic
134
- // 429 backoff; a non-2xx is a hard coded error (auth/scope/upstream); a 2xx with a malformed body is a
135
- // transient render-overload signal that is retried up to MAX_RENDER_BODY_RETRIES, then degrades to an
136
- // empty map so the affected screens skip (render-url-missing) instead of aborting the build (F8).
141
+ // 429 backoff; auth/scope/upstream non-2xx responses are hard coded errors, while renderability
142
+ // client failures degrade this batch to structural-only. A 2xx with a malformed body is a transient
143
+ // render-overload signal that is retried up to MAX_RENDER_BODY_RETRIES, then degrades to an empty map
144
+ // so the affected screens skip (render-url-missing) instead of aborting the build (F8).
137
145
  const resolveRenderBatch = async (input, batch, policy, sleep) => {
138
146
  const requestUrl = buildImagesUrl(input.provenance.fileKey, batch, input.provenance.version);
139
147
  for (let attempt = 0;; attempt += 1) {
140
148
  const response = await fetchWithBackoff(() => input.imagesPort({ url: requestUrl, headers: { "X-Figma-Token": input.token } }), policy, sleep);
141
149
  if (response.status < 200 || response.status >= 300) {
150
+ if (isRenderEndpointSoftFailure(response.status))
151
+ return {};
142
152
  throw statusToError(response.status, response.json);
143
153
  }
144
154
  const map = extractImageUrls(response.json);
@@ -164,8 +174,14 @@ const requestRenderUrls = async (input, screenIds) => {
164
174
  }
165
175
  return urls;
166
176
  };
167
- const skip = (screenId, reason) => ({
168
- skipped: { screenId, reason },
177
+ const structuralOnly = (ir, reason) => ({
178
+ skipped: { screenId: ir.id, reason },
179
+ structuralScreen: {
180
+ screenId: ir.id,
181
+ ir,
182
+ reason,
183
+ integrityHash: hashStructuralScreen(ir.id, ir),
184
+ },
169
185
  });
170
186
  // Classifies a finished render download into a skip reason, or null when the bytes are usable.
171
187
  const renderSkipReason = (response, maxBytes) => {
@@ -201,18 +217,18 @@ const classifyRenderError = (screenId, err) => {
201
217
  if (err instanceof FigmaConnectorError) {
202
218
  if (RENDER_EGRESS_ABORT_CODES.has(err.code))
203
219
  throw err;
204
- return skip(screenId, `render-fetch-failed:${err.code}`);
220
+ return { skipped: { screenId, reason: `render-fetch-failed:${err.code}` } };
205
221
  }
206
- return skip(screenId, "render-fetch-failed");
222
+ return { skipped: { screenId, reason: "render-fetch-failed" } };
207
223
  };
208
224
  // Downloads one screen's render bytes and classifies the result into a kept screen or a skip.
209
225
  // A single screen failing after retries degrades to a skip (partial render), never aborting.
210
226
  // Exception: hard egress errors (TLS/proxy misconfiguration) abort via classifyRenderError.
211
227
  const resolveScreen = async (input, ir, renderUrl) => {
212
228
  if (renderUrl === null)
213
- return skip(ir.id, "render-url-missing");
229
+ return structuralOnly(ir, "render-url-missing");
214
230
  if (!isRenderUrlSafe(renderUrl))
215
- return skip(ir.id, "render-url-blocked");
231
+ return structuralOnly(ir, "render-url-blocked");
216
232
  const maxBytes = input.maxImageBytes ?? DEFAULT_MAX_IMAGE_BYTES;
217
233
  const policy = input.retryPolicy ?? DEFAULT_FIGMA_RETRY_POLICY;
218
234
  const sleep = input.sleep ?? realFigmaRetrySleep;
@@ -221,11 +237,15 @@ const resolveScreen = async (input, ir, renderUrl) => {
221
237
  response = await fetchWithBackoff(() => input.renderPort({ url: renderUrl, headers: {} }), policy, sleep);
222
238
  }
223
239
  catch (err) {
224
- return classifyRenderError(ir.id, err);
240
+ const classified = classifyRenderError(ir.id, err);
241
+ if (classified.skipped !== undefined) {
242
+ return structuralOnly(ir, classified.skipped.reason);
243
+ }
244
+ return classified;
225
245
  }
226
246
  const reason = renderSkipReason(response, maxBytes);
227
247
  if (reason !== null)
228
- return skip(ir.id, reason);
248
+ return structuralOnly(ir, reason);
229
249
  const sha256 = hashBytes(response.bytes);
230
250
  const screen = {
231
251
  screenId: ir.id,
@@ -240,9 +260,31 @@ const resolveScreen = async (input, ir, renderUrl) => {
240
260
  };
241
261
  return { screen };
242
262
  };
263
+ function collectSnapshotRows(outcomes, cappedScreens) {
264
+ const screens = [];
265
+ const skippedScreens = [];
266
+ const structuralScreens = [];
267
+ for (const outcome of outcomes) {
268
+ if (outcome.screen !== undefined)
269
+ screens.push(outcome.screen);
270
+ if (outcome.skipped !== undefined)
271
+ skippedScreens.push(outcome.skipped);
272
+ if (outcome.structuralScreen !== undefined)
273
+ structuralScreens.push(outcome.structuralScreen);
274
+ }
275
+ for (const ir of cappedScreens) {
276
+ const outcome = structuralOnly(ir, "render-screen-cap-exceeded");
277
+ if (outcome.skipped !== undefined)
278
+ skippedScreens.push(outcome.skipped);
279
+ if (outcome.structuralScreen !== undefined)
280
+ structuralScreens.push(outcome.structuralScreen);
281
+ }
282
+ return { screens, skippedScreens, structuralScreens };
283
+ }
243
284
  /**
244
285
  * Render the screens and assemble the immutable Figma Snapshot. Render failures degrade to a
245
- * `skippedScreens` entry (partial render); a non-2xx `/v1/images` call is a hard coded error.
286
+ * `skippedScreens` entry plus structural-only IR (partial render); a non-2xx `/v1/images` call is
287
+ * a hard coded error.
246
288
  */
247
289
  export const buildFigmaSnapshot = async (input) => {
248
290
  const maxScreensRendered = boundedPositiveInt(input.maxScreensRendered, DEFAULT_SCOPED_PAGINATION_LIMITS.maxScreensDeep, SCOPED_PAGINATION_LIMIT_CEILINGS.maxScreensDeep);
@@ -254,25 +296,19 @@ export const buildFigmaSnapshot = async (input) => {
254
296
  : await requestRenderUrls(input, screenIds);
255
297
  const concurrency = boundedPositiveInt(input.downloadConcurrency, DEFAULT_DOWNLOAD_CONCURRENCY, Number.MAX_SAFE_INTEGER);
256
298
  const outcomes = await mapWithConcurrency(renderableScreens, concurrency, (ir) => resolveScreen(input, ir, renderUrls.get(ir.id) ?? null));
257
- const screens = [];
258
- const skippedScreens = [];
259
- for (const outcome of outcomes) {
260
- if (outcome.screen !== undefined)
261
- screens.push(outcome.screen);
262
- if (outcome.skipped !== undefined)
263
- skippedScreens.push(outcome.skipped);
264
- }
265
- for (const ir of cappedScreens) {
266
- skippedScreens.push({ screenId: ir.id, reason: "render-screen-cap-exceeded" });
267
- }
299
+ const { screens, skippedScreens, structuralScreens } = collectSnapshotRows(outcomes, cappedScreens);
268
300
  return {
269
301
  snapshotSchemaVersion: SNAPSHOT_SCHEMA_VERSION,
270
302
  provenance: input.provenance,
271
303
  screens,
272
304
  skippedScreens,
305
+ structuralScreens,
273
306
  // Carried verbatim from the Screen-IR (#752) for the navigation/flow graph (#811). NOT folded
274
307
  // into the integrity hash below — `links` is non-identity metadata, so drift (#735) is stable.
275
308
  links: input.ir.links,
276
- integrityHash: hashSnapshot(SNAPSHOT_SCHEMA_VERSION, input.provenance.version, screens),
309
+ integrityHash: hashSnapshot(SNAPSHOT_SCHEMA_VERSION, input.provenance.version, [
310
+ ...screens,
311
+ ...structuralScreens,
312
+ ]),
277
313
  };
278
314
  };
@@ -2,6 +2,8 @@ import type { QualityIntelligenceFigma } from "@oscharko-dev/keiko-quality-intel
2
2
  type ScreenIr = QualityIntelligenceFigma.ScreenIr;
3
3
  /** sha256 over the canonical per-screen identity: {screenId, ir, imageSha256}. */
4
4
  export declare const hashScreen: (screenId: string, ir: ScreenIr, imageSha256: string) => string;
5
+ /** sha256 over a structural-only screen identity: {screenId, ir, structuralOnly:true}. */
6
+ export declare const hashStructuralScreen: (screenId: string, ir: ScreenIr) => string;
5
7
  /**
6
8
  * sha256 over the canonical snapshot identity: schema version + pinned Figma version + the
7
9
  * per-screen hashes sorted by screenId. EXCLUDES `fetchedAt` so the hash is drift-stable across
@@ -1 +1 @@
1
- {"version":3,"file":"figmaSnapshotHash.d.ts","sourceRoot":"","sources":["../../../src/qualityIntelligence/figma/figmaSnapshotHash.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,0CAA0C,CAAC;AAEzF,KAAK,QAAQ,GAAG,wBAAwB,CAAC,QAAQ,CAAC;AAwDlD,kFAAkF;AAClF,eAAO,MAAM,UAAU,GAAI,UAAU,MAAM,EAAE,IAAI,QAAQ,EAAE,aAAa,MAAM,KAAG,MACV,CAAC;AAExE;;;;GAIG;AACH,eAAO,MAAM,YAAY,GACvB,uBAAuB,MAAM,EAC7B,SAAS,MAAM,GAAG,SAAS,EAC3B,WAAW,SAAS;IAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;CAAE,EAAE,KAClF,MAKF,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,OAAO,UAAU,KAAG,MACI,CAAC"}
1
+ {"version":3,"file":"figmaSnapshotHash.d.ts","sourceRoot":"","sources":["../../../src/qualityIntelligence/figma/figmaSnapshotHash.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,0CAA0C,CAAC;AAEzF,KAAK,QAAQ,GAAG,wBAAwB,CAAC,QAAQ,CAAC;AAwDlD,kFAAkF;AAClF,eAAO,MAAM,UAAU,GAAI,UAAU,MAAM,EAAE,IAAI,QAAQ,EAAE,aAAa,MAAM,KAAG,MACV,CAAC;AAExE,0FAA0F;AAC1F,eAAO,MAAM,oBAAoB,GAAI,UAAU,MAAM,EAAE,IAAI,QAAQ,KAAG,MACU,CAAC;AAEjF;;;;GAIG;AACH,eAAO,MAAM,YAAY,GACvB,uBAAuB,MAAM,EAC7B,SAAS,MAAM,GAAG,SAAS,EAC3B,WAAW,SAAS;IAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;CAAE,EAAE,KAClF,MAKF,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,OAAO,UAAU,KAAG,MACI,CAAC"}
@@ -3,9 +3,10 @@
3
3
  // The hash is the drift identity: same unchanged design ⇒ byte-identical hash, regardless of when
4
4
  // it was fetched. We therefore canonicalise to a stable key order and EXCLUDE the wall-clock
5
5
  // `fetchedAt`. The snapshot identity is the pinned Figma `version` + the sorted per-screen
6
- // identities (each = screenId + structural IR + image content sha256). `version` and the IR are
7
- // deterministic outputs of #751/#752; the image sha is included so an unexpected render-byte change
8
- // surfaces as drift too.
6
+ // identities (each = screenId + structural IR + image content sha256, or a structural-only marker
7
+ // for screens whose PNG render was deliberately skipped/could not be fetched). `version` and the IR
8
+ // are deterministic outputs of #751/#752; the image sha is included so an unexpected render-byte
9
+ // change surfaces as drift too, while structural-only screens still participate in drift detection.
9
10
  import { createHash } from "node:crypto";
10
11
  const sha256Hex = (input) => createHash("sha256").update(input, "utf8").digest("hex");
11
12
  // Stable stringify: object keys are emitted in sorted order at every depth so the serialisation is
@@ -46,6 +47,8 @@ const hashStableIr = (ir) => ({
46
47
  });
47
48
  /** sha256 over the canonical per-screen identity: {screenId, ir, imageSha256}. */
48
49
  export const hashScreen = (screenId, ir, imageSha256) => sha256Hex(canonical({ imageSha256, ir: hashStableIr(ir), screenId }));
50
+ /** sha256 over a structural-only screen identity: {screenId, ir, structuralOnly:true}. */
51
+ export const hashStructuralScreen = (screenId, ir) => sha256Hex(canonical({ ir: hashStableIr(ir), screenId, structuralOnly: true }));
49
52
  /**
50
53
  * sha256 over the canonical snapshot identity: schema version + pinned Figma version + the
51
54
  * per-screen hashes sorted by screenId. EXCLUDES `fetchedAt` so the hash is drift-stable across
@@ -16,6 +16,14 @@ export interface FigmaSnapshotScreen {
16
16
  /** sha256 over the canonical {screenId, ir, imageSha256} body — the per-screen drift identity. */
17
17
  readonly integrityHash: string;
18
18
  }
19
+ /** One screen whose structural IR is captured but no PNG side-file exists. */
20
+ export interface FigmaSnapshotStructuralScreen {
21
+ readonly screenId: string;
22
+ readonly ir: ScreenIr;
23
+ readonly reason: FigmaSkippedScreenReason;
24
+ /** sha256 over the canonical {screenId, ir, structuralOnly:true} body. */
25
+ readonly integrityHash: string;
26
+ }
19
27
  /** Why a detected screen produced no render and was excluded from `screens` (partial-render).
20
28
  * The `render-fetch-failed` member also appears as `render-fetch-failed:<CODE>` when the fetch
21
29
  * threw a FigmaConnectorError — the code suffix lets metrics distinguish an egress
@@ -37,6 +45,12 @@ export interface FigmaSnapshot {
37
45
  readonly provenance: FigmaProvenance;
38
46
  readonly screens: readonly FigmaSnapshotScreen[];
39
47
  readonly skippedScreens: readonly FigmaSkippedScreen[];
48
+ /**
49
+ * Structural IR for screens that did not produce a PNG render. This keeps QI/code consumers from
50
+ * losing JSON evidence when render fan-out is capped or a single render fetch degrades. Optional
51
+ * for older snapshots/builders; new builds emit it for every skipped screen that has IR.
52
+ */
53
+ readonly structuralScreens?: readonly FigmaSnapshotStructuralScreen[];
40
54
  /**
41
55
  * Raw inter-screen transitions carried from the Screen-IR (#752) for the navigation/flow graph
42
56
  * (#811). OPTIONAL and additive — a constructor that omits it (e.g. an older builder or a test
@@ -1 +1 @@
1
- {"version":3,"file":"figmaSnapshotTypes.d.ts","sourceRoot":"","sources":["../../../src/qualityIntelligence/figma/figmaSnapshotTypes.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,0CAA0C,CAAC;AACzF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3D,KAAK,QAAQ,GAAG,wBAAwB,CAAC,QAAQ,CAAC;AAElD,+FAA+F;AAC/F,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,+DAA+D;AAC/D,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,kBAAkB,CAAC;IACnC,kGAAkG;IAClG,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED;;;;GAIG;AACH,MAAM,MAAM,wBAAwB,GAChC,oBAAoB,GACpB,oBAAoB,GACpB,4BAA4B,GAC5B,qBAAqB,GACrB,uBAAuB,MAAM,EAAE,GAC/B,cAAc,GACd,kBAAkB,CAAC;AAEvB,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,wBAAwB,CAAC;CAC3C;AAED;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,qBAAqB,EAAE,CAAC,CAAC;IAClC,QAAQ,CAAC,UAAU,EAAE,eAAe,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACjD,QAAQ,CAAC,cAAc,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACvD;;;;;;OAMG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,wBAAwB,CAAC,eAAe,EAAE,CAAC;IACrE,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC"}
1
+ {"version":3,"file":"figmaSnapshotTypes.d.ts","sourceRoot":"","sources":["../../../src/qualityIntelligence/figma/figmaSnapshotTypes.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,0CAA0C,CAAC;AACzF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3D,KAAK,QAAQ,GAAG,wBAAwB,CAAC,QAAQ,CAAC;AAElD,+FAA+F;AAC/F,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,+DAA+D;AAC/D,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,kBAAkB,CAAC;IACnC,kGAAkG;IAClG,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED,8EAA8E;AAC9E,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,wBAAwB,CAAC;IAC1C,0EAA0E;IAC1E,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED;;;;GAIG;AACH,MAAM,MAAM,wBAAwB,GAChC,oBAAoB,GACpB,oBAAoB,GACpB,4BAA4B,GAC5B,qBAAqB,GACrB,uBAAuB,MAAM,EAAE,GAC/B,cAAc,GACd,kBAAkB,CAAC;AAEvB,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,wBAAwB,CAAC;CAC3C;AAED;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,qBAAqB,EAAE,CAAC,CAAC;IAClC,QAAQ,CAAC,UAAU,EAAE,eAAe,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACjD,QAAQ,CAAC,cAAc,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACvD;;;;OAIG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,SAAS,6BAA6B,EAAE,CAAC;IACtE;;;;;;OAMG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,wBAAwB,CAAC,eAAe,EAAE,CAAC;IACrE,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC"}
@@ -17,6 +17,8 @@ export interface FigmaSnapshotSummary {
17
17
  * "3 screens from 1 section (2 skipped)".
18
18
  */
19
19
  readonly reductionHint: string;
20
+ /** Skipped/non-rendered screens that still carry persisted structural Screen-IR JSON. */
21
+ readonly structuralOnlyCount: number;
20
22
  /** Integrity hash over the snapshot content — deterministic for drift detection (#735). */
21
23
  readonly integrityHash: string;
22
24
  /**
@@ -37,6 +39,11 @@ export interface FigmaSnapshotSummary {
37
39
  * the token-free PNG side-file through /api/figma/snapshots/:runId/screens/:screenIndex/image.
38
40
  */
39
41
  readonly screens: readonly FigmaScreenSummary[];
42
+ /**
43
+ * Screen-IR summaries for detected screens that have JSON evidence but no rendered PNG side-file.
44
+ * These are selectable as QI sources, but the browser cannot request an image for them.
45
+ */
46
+ readonly structuralScreens: readonly FigmaStructuralScreenSummary[];
40
47
  }
41
48
  export interface FigmaScreenSummary {
42
49
  readonly screenId: string;
@@ -51,6 +58,30 @@ export interface FigmaScreenSummary {
51
58
  /** Byte size of the rendered PNG. */
52
59
  readonly imageByteLength: number;
53
60
  }
61
+ export interface FigmaStructuralScreenSummary {
62
+ readonly screenId: string;
63
+ /** Display name derived from the IR (ir.name). */
64
+ readonly name: string;
65
+ /** A brief structural description (field count, control count) for the gallery card. */
66
+ readonly irSummary: string;
67
+ /** Why no rendered PNG side-file exists for this screen. */
68
+ readonly reason: string;
69
+ }
70
+ export interface FigmaSnapshotListEntry {
71
+ readonly runId: string;
72
+ readonly fileKey: string;
73
+ readonly nodeId: string;
74
+ readonly version: string | undefined;
75
+ readonly fetchedAt: string;
76
+ readonly screenCount: number;
77
+ readonly skippedCount: number;
78
+ readonly structuralOnlyCount: number;
79
+ readonly reductionHint: string;
80
+ readonly integrityHash: string;
81
+ }
82
+ export interface FigmaSnapshotListResponse {
83
+ readonly snapshots: readonly FigmaSnapshotListEntry[];
84
+ }
54
85
  export declare function figmaPaginationFromEnv(env: EnvSource): ScopedPaginationLimits;
55
86
  interface CoalescedBuildEntry {
56
87
  readonly promise: Promise<RouteResult>;
@@ -61,7 +92,9 @@ export declare function makeInFlightMap(): Map<string, CoalescedBuildEntry>;
61
92
  export declare function resetInFlightMap(): void;
62
93
  export declare function handleFigmaTriggerSnapshot(ctx: RouteContext, deps: UiHandlerDeps, inFlight?: Map<string, CoalescedBuildEntry>): Promise<RouteResult>;
63
94
  export declare function handleFigmaRevokeToken(ctx: RouteContext, deps: UiHandlerDeps): RouteResult;
95
+ export declare function handleFigmaListSnapshots(ctx: RouteContext, deps: UiHandlerDeps): RouteResult;
64
96
  export declare function handleFigmaLoadSnapshot(ctx: RouteContext, deps: UiHandlerDeps): RouteResult;
97
+ export declare function handleFigmaInspectSnapshotScreenJson(ctx: RouteContext, deps: UiHandlerDeps): RouteResult;
65
98
  export declare function handleFigmaLoadSnapshotImage(ctx: RouteContext, deps: UiHandlerDeps): HandlerOutcome;
66
99
  export {};
67
100
  //# sourceMappingURL=figmaSnapshotRoutes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"figmaSnapshotRoutes.d.ts","sourceRoot":"","sources":["../../src/qualityIntelligence/figmaSnapshotRoutes.ts"],"names":[],"mappings":"AAgDA,OAAO,EAAa,KAAK,cAAc,EAAE,KAAK,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AACnG,OAAO,EAIL,KAAK,aAAa,EACnB,MAAM,YAAY,CAAC;AAEpB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EASL,KAAK,qBAAqB,EAE1B,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,EAC5B,MAAM,kBAAkB,CAAC;AA2I1B,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,8CAA8C;IAC9C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,0DAA0D;IAC1D,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B;;;OAGG;IACH,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,2FAA2F;IAC3F,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,kBAAkB,CAAC;IACvC;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,qBAAqB,CAAC;IACzC;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,SAAS,kBAAkB,EAAE,CAAC;CACjD;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,kDAAkD;IAClD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,wFAAwF;IACxF,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,sDAAsD;IACtD,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,kCAAkC;IAClC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,qCAAqC;IACrC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;CAClC;AA+MD,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,SAAS,GAAG,sBAAsB,CAiB7E;AA8GD,UAAU,mBAAmB;IAC3B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;CACxC;AAMD,sEAAsE;AACtE,wBAAgB,eAAe,IAAI,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAElE;AAED,gFAAgF;AAChF,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC;AA6FD,wBAAsB,0BAA0B,CAC9C,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,aAAa,EACnB,QAAQ,GAAE,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAsB,GAC9D,OAAO,CAAC,WAAW,CAAC,CAiDtB;AAID,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,GAAG,WAAW,CA4B1F;AAID,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,GAAG,WAAW,CAwB3F;AAiCD,wBAAgB,4BAA4B,CAC1C,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,aAAa,GAClB,cAAc,CAgChB"}
1
+ {"version":3,"file":"figmaSnapshotRoutes.d.ts","sourceRoot":"","sources":["../../src/qualityIntelligence/figmaSnapshotRoutes.ts"],"names":[],"mappings":"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"}
@@ -44,6 +44,8 @@
44
44
  // The coalesced build continues — other waiters (if any) are unaffected and will
45
45
  // receive the result when the build settles.
46
46
  import { randomUUID } from "node:crypto";
47
+ import { lstatSync, readdirSync, readFileSync } from "node:fs";
48
+ import { join } from "node:path";
47
49
  import { STREAMING } from "../routes.js";
48
50
  import { currentGatewayConfig, currentGatewayEgressConfig, currentRedactionSecrets, } from "../deps.js";
49
51
  import { redact } from "@oscharko-dev/keiko-security";
@@ -155,12 +157,19 @@ function readBody(req) {
155
157
  req.on("error", reject);
156
158
  });
157
159
  }
158
- function buildReductionHint(screenCount, skippedCount) {
160
+ function buildReductionHint(screenCount, skippedCount, structuralOnlyCount = 0) {
159
161
  const total = screenCount + skippedCount;
160
- const skippedClause = skippedCount > 0
161
- ? ` (${skippedCount.toString()} render${skippedCount !== 1 ? "s" : ""} skipped)`
162
+ if (skippedCount === 0) {
163
+ return `${screenCount.toString()} screen${screenCount !== 1 ? "s" : ""} from ${total.toString()} detected`;
164
+ }
165
+ const structuralClause = structuralOnlyCount > 0
166
+ ? `${structuralOnlyCount.toString()} structural-only`
167
+ : `${skippedCount.toString()} render${skippedCount !== 1 ? "s" : ""} skipped`;
168
+ const missingIrCount = Math.max(0, skippedCount - structuralOnlyCount);
169
+ const missingClause = structuralOnlyCount > 0 && missingIrCount > 0
170
+ ? `; ${missingIrCount.toString()} without structural IR`
162
171
  : "";
163
- return `${screenCount.toString()} screen${screenCount !== 1 ? "s" : ""} from ${total.toString()} detected${skippedClause}`;
172
+ return `${screenCount.toString()} rendered screen${screenCount !== 1 ? "s" : ""} from ${total.toString()} detected (${structuralClause}${missingClause})`;
164
173
  }
165
174
  // Counts interaction-hint roles over a stored ScreenIr node tree (`{ root: { interactionHint,
166
175
  // children } }`). Duck-typed: it does NOT import the IR domain, only walks the serialised shape the
@@ -212,6 +221,7 @@ function screenNameFromIrJson(irJson) {
212
221
  function recordToSummary(record, coverage, metrics) {
213
222
  const screenCount = record.screens.length;
214
223
  const skippedCount = record.skippedScreens.length;
224
+ const structuralOnlyCount = record.structuralScreens?.length ?? 0;
215
225
  const truncatedClause = coverage !== undefined && (coverage.screensTruncated > 0 || coverage.capped)
216
226
  ? `; ${coverage.screensTruncated.toString()} partially captured (deep content bounded)`
217
227
  : "";
@@ -223,7 +233,8 @@ function recordToSummary(record, coverage, metrics) {
223
233
  fetchedAt: record.provenance.fetchedAt,
224
234
  screenCount,
225
235
  skippedCount,
226
- reductionHint: `${buildReductionHint(screenCount, skippedCount)}${truncatedClause}`,
236
+ structuralOnlyCount,
237
+ reductionHint: `${buildReductionHint(screenCount, skippedCount, structuralOnlyCount)}${truncatedClause}`,
227
238
  integrityHash: record.integrityHash,
228
239
  ...(coverage !== undefined ? { coverage } : {}),
229
240
  ...(metrics !== undefined ? { metrics } : {}),
@@ -235,6 +246,126 @@ function recordToSummary(record, coverage, metrics) {
235
246
  imageSha256: s.image.sha256,
236
247
  imageByteLength: s.image.byteLength,
237
248
  })),
249
+ structuralScreens: (record.structuralScreens ?? []).map((s) => ({
250
+ screenId: s.screenId,
251
+ name: screenNameFromIrJson(s.irJson),
252
+ irSummary: irSummaryFromJson(s.irJson),
253
+ reason: s.reason,
254
+ })),
255
+ };
256
+ }
257
+ function decodeRouteParam(raw) {
258
+ if (raw === undefined || raw.length === 0)
259
+ return "";
260
+ try {
261
+ return decodeURIComponent(raw);
262
+ }
263
+ catch {
264
+ return "";
265
+ }
266
+ }
267
+ function collectIrIds(value, out = new Set()) {
268
+ if (typeof value !== "object" || value === null)
269
+ return out;
270
+ if (Array.isArray(value)) {
271
+ for (const entry of value)
272
+ collectIrIds(entry, out);
273
+ return out;
274
+ }
275
+ const record = value;
276
+ if (typeof record.id === "string" && record.id.length > 0)
277
+ out.add(record.id);
278
+ for (const entry of Object.values(record))
279
+ collectIrIds(entry, out);
280
+ return out;
281
+ }
282
+ function relatedLinksForScreen(irJson, links) {
283
+ if (links === undefined || links.length === 0)
284
+ return [];
285
+ const ids = collectIrIds(irJson);
286
+ return links.filter((link) => ids.has(link.sourceNodeId) || ids.has(link.targetNodeId));
287
+ }
288
+ function screenJsonSnapshot(record) {
289
+ return {
290
+ screenCount: record.screens.length,
291
+ skippedCount: record.skippedScreens.length,
292
+ structuralOnlyCount: record.structuralScreens?.length ?? 0,
293
+ integrityHash: record.integrityHash,
294
+ redactionSummary: record.redactionSummary,
295
+ ...(record.metrics !== undefined ? { metrics: record.metrics } : {}),
296
+ ...(record.tokens !== undefined ? { tokens: record.tokens } : {}),
297
+ };
298
+ }
299
+ function renderedScreenJsonResponse(record, rendered) {
300
+ return {
301
+ runId: record.runId,
302
+ fileKey: record.provenance.fileKey,
303
+ nodeId: record.provenance.nodeId,
304
+ version: record.provenance.version,
305
+ fetchedAt: record.provenance.fetchedAt,
306
+ source: {
307
+ kind: "figma-snapshot",
308
+ snapshotRunId: record.runId,
309
+ screenIds: [rendered.screenId],
310
+ },
311
+ snapshot: screenJsonSnapshot(record),
312
+ screen: {
313
+ kind: "rendered",
314
+ screenId: rendered.screenId,
315
+ name: screenNameFromIrJson(rendered.irJson),
316
+ irSummary: irSummaryFromJson(rendered.irJson),
317
+ integrityHash: rendered.integrityHash,
318
+ irJson: rendered.irJson,
319
+ image: rendered.image,
320
+ },
321
+ relatedLinks: relatedLinksForScreen(rendered.irJson, record.links),
322
+ };
323
+ }
324
+ function structuralScreenJsonResponse(record, structural) {
325
+ return {
326
+ runId: record.runId,
327
+ fileKey: record.provenance.fileKey,
328
+ nodeId: record.provenance.nodeId,
329
+ version: record.provenance.version,
330
+ fetchedAt: record.provenance.fetchedAt,
331
+ source: {
332
+ kind: "figma-snapshot",
333
+ snapshotRunId: record.runId,
334
+ screenIds: [structural.screenId],
335
+ },
336
+ snapshot: screenJsonSnapshot(record),
337
+ screen: {
338
+ kind: "structural",
339
+ screenId: structural.screenId,
340
+ name: screenNameFromIrJson(structural.irJson),
341
+ irSummary: irSummaryFromJson(structural.irJson),
342
+ integrityHash: structural.integrityHash,
343
+ irJson: structural.irJson,
344
+ structuralReason: structural.reason,
345
+ },
346
+ relatedLinks: relatedLinksForScreen(structural.irJson, record.links),
347
+ };
348
+ }
349
+ function screenJsonResponse(record, screenId) {
350
+ const rendered = record.screens.find((screen) => screen.screenId === screenId);
351
+ if (rendered !== undefined)
352
+ return renderedScreenJsonResponse(record, rendered);
353
+ const structural = record.structuralScreens?.find((screen) => screen.screenId === screenId);
354
+ return structural === undefined ? undefined : structuralScreenJsonResponse(record, structural);
355
+ }
356
+ function recordToListEntry(record) {
357
+ const structuralOnlyCount = record.structuralScreens?.length ?? 0;
358
+ return {
359
+ runId: record.runId,
360
+ fileKey: record.provenance.fileKey,
361
+ nodeId: record.provenance.nodeId,
362
+ version: record.provenance.version,
363
+ fetchedAt: record.provenance.fetchedAt,
364
+ screenCount: record.screens.length,
365
+ skippedCount: record.skippedScreens.length,
366
+ structuralOnlyCount,
367
+ reductionHint: buildReductionHint(record.screens.length, record.skippedScreens.length, structuralOnlyCount),
368
+ integrityHash: record.integrityHash,
238
369
  };
239
370
  }
240
371
  function persistedAuditCounts(record, metrics) {
@@ -410,6 +541,16 @@ function persistSnapshot(evidenceDir, runId, result, deps) {
410
541
  integrityHash: s.integrityHash,
411
542
  image: { mimeType: "image/png", bytes: s.image.bytes },
412
543
  })),
544
+ ...(result.snapshot.structuralScreens !== undefined
545
+ ? {
546
+ structuralScreens: result.snapshot.structuralScreens.map((s) => ({
547
+ screenId: s.screenId,
548
+ reason: s.reason,
549
+ irJson: s.ir,
550
+ integrityHash: s.integrityHash,
551
+ })),
552
+ }
553
+ : {}),
413
554
  skippedScreens: result.snapshot.skippedScreens.map((ss) => ({
414
555
  screenId: ss.screenId,
415
556
  reason: ss.reason,
@@ -583,6 +724,96 @@ export function handleFigmaRevokeToken(ctx, deps) {
583
724
  },
584
725
  };
585
726
  }
727
+ // ─── GET /api/figma/snapshots — list stored snapshots for dashboard/history ────────────────
728
+ const DEFAULT_FIGMA_SNAPSHOT_LIST_LIMIT = 12;
729
+ const MAX_FIGMA_SNAPSHOT_LIST_LIMIT = 50;
730
+ const FIGMA_SNAPSHOT_RECORD_SUFFIX = ".figma-snapshot.json";
731
+ const FIGMA_EVIDENCE_SUBDIR = "qi";
732
+ function parseSnapshotListLimit(url) {
733
+ const raw = url.searchParams.get("limit");
734
+ if (raw === null || raw.length === 0)
735
+ return DEFAULT_FIGMA_SNAPSHOT_LIST_LIMIT;
736
+ const parsed = Number(raw);
737
+ if (!Number.isInteger(parsed) || parsed <= 0)
738
+ return DEFAULT_FIGMA_SNAPSHOT_LIST_LIMIT;
739
+ return Math.min(parsed, MAX_FIGMA_SNAPSHOT_LIST_LIMIT);
740
+ }
741
+ function parseSnapshotListScope(url) {
742
+ const fileKey = (url.searchParams.get("fileKey") ?? "").trim();
743
+ const nodeId = (url.searchParams.get("nodeId") ?? "").trim();
744
+ return fileKey.length > 0 && nodeId.length > 0 ? { fileKey, nodeId } : undefined;
745
+ }
746
+ function loadSnapshotListEntries(store, runIds) {
747
+ const entries = [];
748
+ for (const runId of runIds) {
749
+ let record;
750
+ try {
751
+ record = store.loadMetadata(runId);
752
+ }
753
+ catch {
754
+ continue;
755
+ }
756
+ if (record !== undefined)
757
+ entries.push(recordToListEntry(record));
758
+ }
759
+ return entries;
760
+ }
761
+ function readSnapshotFetchedAt(qiDir, fileName) {
762
+ try {
763
+ const parsed = JSON.parse(readFileSync(join(qiDir, fileName), "utf8"));
764
+ const provenance = typeof parsed.provenance === "object" && parsed.provenance !== null
765
+ ? parsed.provenance
766
+ : undefined;
767
+ const fetchedAt = provenance?.fetchedAt;
768
+ return typeof fetchedAt === "string" && fetchedAt.length > 0 ? fetchedAt : undefined;
769
+ }
770
+ catch {
771
+ return undefined;
772
+ }
773
+ }
774
+ function listRecentSnapshotRunIds(evidenceDir, limit) {
775
+ const qiDir = join(evidenceDir, FIGMA_EVIDENCE_SUBDIR);
776
+ const stat = lstatSync(qiDir, { throwIfNoEntry: false });
777
+ if (stat?.isDirectory() !== true)
778
+ return [];
779
+ const records = [];
780
+ for (const entry of readdirSync(qiDir, { withFileTypes: true })) {
781
+ if (!entry.isFile() || !entry.name.endsWith(FIGMA_SNAPSHOT_RECORD_SUFFIX))
782
+ continue;
783
+ const runId = entry.name.slice(0, -FIGMA_SNAPSHOT_RECORD_SUFFIX.length);
784
+ const fetchedAt = readSnapshotFetchedAt(qiDir, entry.name);
785
+ if (fetchedAt !== undefined)
786
+ records.push({ runId, fetchedAt });
787
+ }
788
+ records.sort((a, b) => (a.fetchedAt > b.fetchedAt ? -1 : a.fetchedAt < b.fetchedAt ? 1 : 0));
789
+ return records.slice(0, limit).map((record) => record.runId);
790
+ }
791
+ export function handleFigmaListSnapshots(ctx, deps) {
792
+ const evidenceDir = deps.evidenceDir;
793
+ if (evidenceDir === undefined || evidenceDir.length === 0) {
794
+ return { status: 503, body: figmaErrorBody("FIGMA_NO_EVIDENCE_DIR") };
795
+ }
796
+ const limit = parseSnapshotListLimit(ctx.url);
797
+ const scope = parseSnapshotListScope(ctx.url);
798
+ const store = createNodeFigmaSnapshotStore(evidenceDir);
799
+ try {
800
+ if (scope !== undefined) {
801
+ const snapshots = store
802
+ .listByScope(scope.fileKey, scope.nodeId)
803
+ .slice(0, limit)
804
+ .flatMap((entry) => {
805
+ const record = store.loadMetadata(entry.runId);
806
+ return record === undefined ? [] : [recordToListEntry(record)];
807
+ });
808
+ return { status: 200, body: { snapshots } };
809
+ }
810
+ const snapshots = loadSnapshotListEntries(store, listRecentSnapshotRunIds(evidenceDir, limit));
811
+ return { status: 200, body: { snapshots } };
812
+ }
813
+ catch {
814
+ return { status: 500, body: figmaErrorBody("FIGMA_INTERNAL") };
815
+ }
816
+ }
586
817
  // ─── GET /api/figma/snapshots/:runId ──────────────────────────────────────────
587
818
  export function handleFigmaLoadSnapshot(ctx, deps) {
588
819
  const evidenceDir = deps.evidenceDir;
@@ -606,6 +837,33 @@ export function handleFigmaLoadSnapshot(ctx, deps) {
606
837
  }
607
838
  return { status: 200, body: recordToSummary(record, undefined, record.metrics) };
608
839
  }
840
+ // ─── GET /api/figma/snapshots/:runId/screens/:screenId/json ───────────────────
841
+ export function handleFigmaInspectSnapshotScreenJson(ctx, deps) {
842
+ const evidenceDir = deps.evidenceDir;
843
+ if (evidenceDir === undefined || evidenceDir.length === 0) {
844
+ return { status: 503, body: figmaErrorBody("FIGMA_NO_EVIDENCE_DIR") };
845
+ }
846
+ const runId = decodeRouteParam(ctx.params.runId);
847
+ const screenId = decodeRouteParam(ctx.params.screenId);
848
+ if (runId.length === 0 || screenId.length === 0) {
849
+ return { status: 404, body: figmaErrorBody("FIGMA_SCREEN_NOT_FOUND") };
850
+ }
851
+ const store = createNodeFigmaSnapshotStore(evidenceDir);
852
+ let record;
853
+ try {
854
+ record = store.loadMetadata(runId);
855
+ }
856
+ catch {
857
+ return { status: 500, body: figmaErrorBody("FIGMA_INTERNAL") };
858
+ }
859
+ if (record === undefined) {
860
+ return { status: 404, body: figmaErrorBody("FIGMA_SNAPSHOT_NOT_FOUND") };
861
+ }
862
+ const body = screenJsonResponse(record, screenId);
863
+ return body === undefined
864
+ ? { status: 404, body: figmaErrorBody("FIGMA_SCREEN_NOT_FOUND") }
865
+ : { status: 200, body };
866
+ }
609
867
  // ─── GET /api/figma/snapshots/:runId/screens/:screenIndex/image ──────────────
610
868
  function parseScreenIndex(raw) {
611
869
  if (raw === undefined || raw.length === 0)