zorya-classifier 0.1.0 → 0.2.1

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 (311) hide show
  1. package/NOTICE.md +2 -0
  2. package/README.md +37 -9
  3. package/dist/app/cli-arguments.d.ts +40 -0
  4. package/dist/app/cli-arguments.js +67 -0
  5. package/dist/app/create-classifier.d.ts +24 -0
  6. package/dist/app/create-classifier.js +226 -14
  7. package/dist/app/extraction-files.d.ts +9 -0
  8. package/dist/app/extraction-files.js +28 -0
  9. package/dist/cli.js +58 -14
  10. package/dist/entities/boundary-view.d.ts +29 -0
  11. package/dist/entities/boundary-view.js +1 -0
  12. package/dist/entities/contracts.d.ts +81 -1
  13. package/dist/entities/contracts.js +7 -0
  14. package/dist/entities/cutout-raster.d.ts +7 -0
  15. package/dist/entities/cutout-raster.js +1 -0
  16. package/dist/entities/object-proposals.d.ts +56 -0
  17. package/dist/entities/object-proposals.js +8 -0
  18. package/dist/entities/representation.d.ts +225 -0
  19. package/dist/entities/representation.js +33 -0
  20. package/dist/entities/segmentation-metadata.d.ts +11 -0
  21. package/dist/entities/segmentation-metadata.js +1 -0
  22. package/dist/entities/segmentation-verification.d.ts +31 -0
  23. package/dist/entities/segmentation-verification.js +1 -0
  24. package/dist/entities/segmentation.d.ts +164 -0
  25. package/dist/entities/segmentation.js +5 -0
  26. package/dist/entities/whole-object.d.ts +132 -0
  27. package/dist/entities/whole-object.js +7 -0
  28. package/dist/features/choice.d.ts +1 -1
  29. package/dist/features/choice.js +2 -2
  30. package/dist/features/segmentation/box-check.d.ts +10 -0
  31. package/dist/features/segmentation/box-check.js +299 -0
  32. package/dist/features/segmentation/mask.d.ts +6 -0
  33. package/dist/features/segmentation/mask.js +89 -0
  34. package/dist/features/segmentation/packed-mask.d.ts +5 -0
  35. package/dist/features/segmentation/packed-mask.js +53 -0
  36. package/dist/index.d.ts +6 -1
  37. package/dist/infrastructure/boundary-view.d.ts +2 -0
  38. package/dist/infrastructure/boundary-view.js +263 -0
  39. package/dist/infrastructure/openrouter-common.d.ts +8 -8
  40. package/dist/infrastructure/openrouter-vision.d.ts +3 -0
  41. package/dist/infrastructure/openrouter-vision.js +5 -0
  42. package/dist/infrastructure/raster.d.ts +10 -1
  43. package/dist/infrastructure/raster.js +99 -4
  44. package/dist/infrastructure/segmentation/box-presentation.d.ts +2 -0
  45. package/dist/infrastructure/segmentation/box-presentation.js +136 -0
  46. package/dist/infrastructure/segmentation/cutout-raster.d.ts +3 -0
  47. package/dist/infrastructure/segmentation/cutout-raster.js +79 -0
  48. package/dist/infrastructure/segmentation/grabcut/index.d.ts +17 -0
  49. package/dist/infrastructure/segmentation/grabcut/index.js +121 -0
  50. package/dist/infrastructure/segmentation/grabcut/worker-inference.d.ts +47 -0
  51. package/dist/infrastructure/segmentation/grabcut/worker-inference.js +260 -0
  52. package/dist/infrastructure/segmentation/index.d.ts +2 -0
  53. package/dist/infrastructure/segmentation/index.js +2 -0
  54. package/dist/infrastructure/segmentation/mobile-sam/index.d.ts +66 -0
  55. package/dist/infrastructure/segmentation/mobile-sam/index.js +478 -0
  56. package/dist/infrastructure/segmentation/mobile-sam/worker-inference.d.ts +149 -0
  57. package/dist/infrastructure/segmentation/mobile-sam/worker-inference.js +845 -0
  58. package/dist/infrastructure/segmentation/native-child.d.ts +23 -0
  59. package/dist/infrastructure/segmentation/native-child.js +297 -0
  60. package/dist/infrastructure/segmentation/native-protocol.d.ts +923 -0
  61. package/dist/infrastructure/segmentation/native-protocol.js +242 -0
  62. package/dist/infrastructure/segmentation/native-worker.d.ts +1 -0
  63. package/dist/infrastructure/segmentation/native-worker.js +93 -0
  64. package/dist/infrastructure/segmentation/probe-backend.d.ts +1 -0
  65. package/dist/infrastructure/segmentation/probe-backend.js +31 -0
  66. package/dist/infrastructure/segmentation/proposal-presentation.d.ts +3 -0
  67. package/dist/infrastructure/segmentation/proposal-presentation.js +371 -0
  68. package/dist/infrastructure/segmentation/verify-asset.d.ts +4 -0
  69. package/dist/infrastructure/segmentation/verify-asset.js +32 -0
  70. package/dist/infrastructure/text-representation.d.ts +23 -0
  71. package/dist/infrastructure/text-representation.js +226 -0
  72. package/dist/infrastructure/vision-chat.d.ts +1 -1
  73. package/dist/infrastructure/vision-chat.js +13 -4
  74. package/dist/openai-compatible.d.ts +3 -0
  75. package/dist/openai-compatible.js +4 -0
  76. package/dist/openrouter.d.ts +2 -2
  77. package/dist/openrouter.js +1 -1
  78. package/dist/processes/boundary-state.d.ts +12 -0
  79. package/dist/processes/boundary-state.js +196 -0
  80. package/dist/processes/classify.d.ts +2 -1
  81. package/dist/processes/classify.js +21 -3
  82. package/dist/processes/extract-from-region.d.ts +19 -0
  83. package/dist/processes/extract-from-region.js +415 -0
  84. package/dist/processes/extract.d.ts +25 -0
  85. package/dist/processes/extract.js +223 -0
  86. package/dist/processes/locate.d.ts +2 -1
  87. package/dist/processes/locate.js +84 -11
  88. package/dist/processes/proposal-location.d.ts +19 -0
  89. package/dist/processes/proposal-location.js +396 -0
  90. package/dist/processes/whole-object.d.ts +7 -0
  91. package/dist/processes/whole-object.js +272 -0
  92. package/dist/segmentation.d.ts +10 -0
  93. package/dist/segmentation.js +4 -0
  94. package/dist/shared/input-budget.d.ts +7 -0
  95. package/dist/shared/input-budget.js +21 -0
  96. package/docs/0.2.0-plan.md +190 -0
  97. package/docs/README.ru.md +43 -14
  98. package/docs/acceptance.md +116 -3
  99. package/docs/api.md +12 -6
  100. package/docs/architecture.md +22 -7
  101. package/docs/benchmarks.md +10 -0
  102. package/docs/categorical-presence-port.md +26 -0
  103. package/docs/evidence/v02/baseline.json +1043 -0
  104. package/docs/evidence/v02/boundary-variants.md +17 -0
  105. package/docs/evidence/v02/box-confirmation-dev40/README.md +15 -0
  106. package/docs/evidence/v02/box-confirmation-dev40/component-refinement.json +246 -0
  107. package/docs/evidence/v02/box-confirmation-dev40/evaluation.json +1663 -0
  108. package/docs/evidence/v02/box-confirmation-dev40/provenance.json +130 -0
  109. package/docs/evidence/v02/box-confirmation-dev40/report.json +127 -0
  110. package/docs/evidence/v02/box-confirmation-dev40-freeze-20261020-v1.json +230 -0
  111. package/docs/evidence/v02/box-confirmation-method-selection-20261020-v1.json +85 -0
  112. package/docs/evidence/v02/box-heldout-cancelled-before-dispatch-20261021-v1.json +32 -0
  113. package/docs/evidence/v02/box-heldout-combined-seal-20261021-v1.json +89 -0
  114. package/docs/evidence/v02/box-heldout-evaluator-truth-seal-20261021-v1.json +61 -0
  115. package/docs/evidence/v02/box-heldout-exclusion-audit-20261020-v1.json +81 -0
  116. package/docs/evidence/v02/box-heldout-map-seal-20261021-v1.json +48 -0
  117. package/docs/evidence/v02/box-heldout-photo-seal-20261020-v1.json +62 -0
  118. package/docs/evidence/v02/box-heldout-run-freeze-20261021-v1.json +61 -0
  119. package/docs/evidence/v02/box-heldout-source-review-20261020-v1.json +134 -0
  120. package/docs/evidence/v02/box-heldout-ui-seal-20261020-v1.json +44 -0
  121. package/docs/evidence/v02/calculator-proof-root-review-20261020-v1.json +63 -0
  122. package/docs/evidence/v02/categorical-dev40-freeze.json +94 -0
  123. package/docs/evidence/v02/categorical-dev40-preserve.json +460 -0
  124. package/docs/evidence/v02/categorical-dev40-target-only.json +460 -0
  125. package/docs/evidence/v02/categorical-heldout-admission-20261020-v3.json +215 -0
  126. package/docs/evidence/v02/categorical-heldout-admission-20261020-v4.json +270 -0
  127. package/docs/evidence/v02/categorical-heldout-dispatch-authorization-20261020-v4.json +79 -0
  128. package/docs/evidence/v02/categorical-heldout-predispatch-20261020-v3.json +57 -0
  129. package/docs/evidence/v02/categorical-heldout-producer-source-map-v4.json +262 -0
  130. package/docs/evidence/v02/categorical-heldout-producer-source-map.json +263 -0
  131. package/docs/evidence/v02/categorical-heldout-run-freeze-20261020-v3.json +35 -0
  132. package/docs/evidence/v02/categorical-heldout-run-freeze-20261020-v4.json +35 -0
  133. package/docs/evidence/v02/categorical-heldout-v4/candidate-evaluation.json +2812 -0
  134. package/docs/evidence/v02/categorical-heldout-v4/candidate-physical-receipts.json +15261 -0
  135. package/docs/evidence/v02/categorical-heldout-v4/candidate-provenance.json +131 -0
  136. package/docs/evidence/v02/categorical-heldout-v4/control-evaluation.json +855 -0
  137. package/docs/evidence/v02/categorical-heldout-v4/control-physical-receipts.json +4402 -0
  138. package/docs/evidence/v02/categorical-heldout-v4/control-provenance.json +31 -0
  139. package/docs/evidence/v02/categorical-heldout-v4/mask-normalized-exploratory.json +682 -0
  140. package/docs/evidence/v02/categorical-heldout-v4/paired-qualification.json +21 -0
  141. package/docs/evidence/v02/categorical-heldout-v4/report.json +258 -0
  142. package/docs/evidence/v02/categorical-presence-dev6.json +144 -0
  143. package/docs/evidence/v02/categorical-presence-dev6.md +18 -0
  144. package/docs/evidence/v02/categorical-vision-method-selection.json +90 -0
  145. package/docs/evidence/v02/checked-region-component-v1/README.md +7 -0
  146. package/docs/evidence/v02/checked-region-component-v1/evidence.json +311 -0
  147. package/docs/evidence/v02/checked-region-resource-interrupted-2b0d09f.json +102 -0
  148. package/docs/evidence/v02/checked-region-segmentation-resource-4df544c-receipt.json +167 -0
  149. package/docs/evidence/v02/checked-region-segmentation-resource-4df544c-root-review.json +58 -0
  150. package/docs/evidence/v02/checked-region-segmentation-resource-987ac32-receipt.json +158 -0
  151. package/docs/evidence/v02/ci-012b935.json +17 -0
  152. package/docs/evidence/v02/ci-0c365cb.json +372 -0
  153. package/docs/evidence/v02/ci-185bde8.json +32 -0
  154. package/docs/evidence/v02/ci-24f849f.json +95 -0
  155. package/docs/evidence/v02/ci-25c19cd.json +375 -0
  156. package/docs/evidence/v02/ci-2922caf.json +375 -0
  157. package/docs/evidence/v02/ci-29357d7.json +29 -0
  158. package/docs/evidence/v02/ci-32aa90d.json +24 -0
  159. package/docs/evidence/v02/ci-35e3064.json +376 -0
  160. package/docs/evidence/v02/ci-4331b1b.json +384 -0
  161. package/docs/evidence/v02/ci-43b7426.json +376 -0
  162. package/docs/evidence/v02/ci-450439c.json +373 -0
  163. package/docs/evidence/v02/ci-46283fe.json +29 -0
  164. package/docs/evidence/v02/ci-4c9c09f.json +34 -0
  165. package/docs/evidence/v02/ci-5579646.json +27 -0
  166. package/docs/evidence/v02/ci-6f9cc15.json +127 -0
  167. package/docs/evidence/v02/ci-7c1051f.json +27 -0
  168. package/docs/evidence/v02/ci-9837888.json +34 -0
  169. package/docs/evidence/v02/ci-987ac32.json +29 -0
  170. package/docs/evidence/v02/ci-9d00fe5.json +25 -0
  171. package/docs/evidence/v02/ci-9f85acc.json +127 -0
  172. package/docs/evidence/v02/ci-be275a6.json +201 -0
  173. package/docs/evidence/v02/ci-c107ddf.json +372 -0
  174. package/docs/evidence/v02/ci-c35f67d.json +127 -0
  175. package/docs/evidence/v02/ci-c5eddd8.json +32 -0
  176. package/docs/evidence/v02/ci-d2042e0.json +372 -0
  177. package/docs/evidence/v02/ci-d684259.json +375 -0
  178. package/docs/evidence/v02/ci-e0c190b.json +127 -0
  179. package/docs/evidence/v02/ci-e551253.json +372 -0
  180. package/docs/evidence/v02/ci-f9d4633.json +29 -0
  181. package/docs/evidence/v02/dev1-proposal-evaluation.md +13 -0
  182. package/docs/evidence/v02/dev2-mobile-sam-proposal-oracle.md +21 -0
  183. package/docs/evidence/v02/dev2-proposal-evaluation.md +21 -0
  184. package/docs/evidence/v02/fresh-development.md +16 -0
  185. package/docs/evidence/v02/fresh-native-numeric/artifact-hashes.json +9 -0
  186. package/docs/evidence/v02/fresh-native-numeric/evaluation-root-v1.json +568 -0
  187. package/docs/evidence/v02/fresh-native-numeric/run-manifest.json +342 -0
  188. package/docs/evidence/v02/fresh-native-numeric/summary.json +5654 -0
  189. package/docs/evidence/v02/fresh-native-outline-axis/artifact-hashes.json +9 -0
  190. package/docs/evidence/v02/fresh-native-outline-axis/evaluation-root-v1.json +568 -0
  191. package/docs/evidence/v02/fresh-native-outline-axis/run-manifest.json +378 -0
  192. package/docs/evidence/v02/fresh-native-outline-axis/summary.json +5605 -0
  193. package/docs/evidence/v02/fresh-text/README.md +23 -0
  194. package/docs/evidence/v02/fresh-text/ascii-contours-v1/classify-followup-run-manifest.json +405 -0
  195. package/docs/evidence/v02/fresh-text/ascii-contours-v1/classify-followup-summary.json +1646 -0
  196. package/docs/evidence/v02/fresh-text/ascii-contours-v1/evaluation.json +568 -0
  197. package/docs/evidence/v02/fresh-text/ascii-contours-v1/original-run-manifest.json +404 -0
  198. package/docs/evidence/v02/fresh-text/ascii-contours-v1/original-summary.json +4416 -0
  199. package/docs/evidence/v02/fresh-text/ascii-luma-v1/classify-followup-run-manifest.json +405 -0
  200. package/docs/evidence/v02/fresh-text/ascii-luma-v1/classify-followup-summary.json +1646 -0
  201. package/docs/evidence/v02/fresh-text/ascii-luma-v1/evaluation.json +568 -0
  202. package/docs/evidence/v02/fresh-text/ascii-luma-v1/original-run-manifest.json +404 -0
  203. package/docs/evidence/v02/fresh-text/ascii-luma-v1/original-summary.json +4647 -0
  204. package/docs/evidence/v02/fresh-text/ascii-svg-layered-v1/classify-followup-run-manifest.json +405 -0
  205. package/docs/evidence/v02/fresh-text/ascii-svg-layered-v1/classify-followup-summary.json +846 -0
  206. package/docs/evidence/v02/fresh-text/ascii-svg-layered-v1/evaluation.json +568 -0
  207. package/docs/evidence/v02/fresh-text/ascii-svg-layered-v1/original-run-manifest.json +404 -0
  208. package/docs/evidence/v02/fresh-text/ascii-svg-layered-v1/original-summary.json +1789 -0
  209. package/docs/evidence/v02/fresh-text/no-image-control/evaluation.json +568 -0
  210. package/docs/evidence/v02/fresh-text/no-image-control/original-run-manifest.json +404 -0
  211. package/docs/evidence/v02/fresh-text/no-image-control/original-summary.json +3953 -0
  212. package/docs/evidence/v02/fresh-text/palette-grid-v1/classify-followup-run-manifest.json +405 -0
  213. package/docs/evidence/v02/fresh-text/palette-grid-v1/classify-followup-summary.json +1646 -0
  214. package/docs/evidence/v02/fresh-text/palette-grid-v1/evaluation.json +568 -0
  215. package/docs/evidence/v02/fresh-text/palette-grid-v1/original-run-manifest.json +404 -0
  216. package/docs/evidence/v02/fresh-text/palette-grid-v1/original-summary.json +3974 -0
  217. package/docs/evidence/v02/fresh-text/pixel-rle-v1/classify-followup-run-manifest.json +405 -0
  218. package/docs/evidence/v02/fresh-text/pixel-rle-v1/classify-followup-summary.json +1646 -0
  219. package/docs/evidence/v02/fresh-text/pixel-rle-v1/evaluation.json +568 -0
  220. package/docs/evidence/v02/fresh-text/pixel-rle-v1/original-run-manifest.json +404 -0
  221. package/docs/evidence/v02/fresh-text/pixel-rle-v1/original-summary.json +4945 -0
  222. package/docs/evidence/v02/fresh-text/report.json +284 -0
  223. package/docs/evidence/v02/fresh-text/svg-contours-v1/classify-followup-run-manifest.json +405 -0
  224. package/docs/evidence/v02/fresh-text/svg-contours-v1/classify-followup-summary.json +846 -0
  225. package/docs/evidence/v02/fresh-text/svg-contours-v1/evaluation.json +568 -0
  226. package/docs/evidence/v02/fresh-text/svg-contours-v1/original-run-manifest.json +404 -0
  227. package/docs/evidence/v02/fresh-text/svg-contours-v1/original-summary.json +2189 -0
  228. package/docs/evidence/v02/heldout-evaluation-20261020.md +37 -0
  229. package/docs/evidence/v02/heldout-input-derivation.md +18 -0
  230. package/docs/evidence/v02/integration-0c365cb.json +90 -0
  231. package/docs/evidence/v02/integration-25c19cd.json +52 -0
  232. package/docs/evidence/v02/integration-35e3064.json +41 -0
  233. package/docs/evidence/v02/integration-4331b1b.json +42 -0
  234. package/docs/evidence/v02/integration-4c9c09f.json +46 -0
  235. package/docs/evidence/v02/integration-8010aad.json +48 -0
  236. package/docs/evidence/v02/integration-c1aa640.json +41 -0
  237. package/docs/evidence/v02/integration-eea1790.json +46 -0
  238. package/docs/evidence/v02/integration-fa82e65.json +42 -0
  239. package/docs/evidence/v02/jpeg-transport-offline.json +268 -0
  240. package/docs/evidence/v02/jpeg-transport-offline.md +7 -0
  241. package/docs/evidence/v02/local-archive-cleanup.json +59 -0
  242. package/docs/evidence/v02/native-base64-after.json +30 -0
  243. package/docs/evidence/v02/native-base64-before.json +32 -0
  244. package/docs/evidence/v02/native-checked-region-windows-46283fe.json +248 -0
  245. package/docs/evidence/v02/native-checked-region-windows-987ac32.json +141 -0
  246. package/docs/evidence/v02/native-grabcut-windows-4c494a3-run2.json +148 -0
  247. package/docs/evidence/v02/native-grabcut-windows-4c494a3-run3.json +148 -0
  248. package/docs/evidence/v02/native-grabcut-windows-4c494a3.json +56 -0
  249. package/docs/evidence/v02/native-proposal-windows-0c365cb.json +148 -0
  250. package/docs/evidence/v02/native-proposal-windows-450439c.json +156 -0
  251. package/docs/evidence/v02/native-proposal-windows-4c9c09f-retry1.json +147 -0
  252. package/docs/evidence/v02/native-proposal-windows-4c9c09f.json +52 -0
  253. package/docs/evidence/v02/native-region-windows-efc9318.json +163 -0
  254. package/docs/evidence/v02/native-windows-eea1790-baseline.json +198 -0
  255. package/docs/evidence/v02/native-windows-eea1790.json +228 -0
  256. package/docs/evidence/v02/ordinary-luna-axis/run-manifest.json +99 -0
  257. package/docs/evidence/v02/ordinary-luna-axis/summary.json +811 -0
  258. package/docs/evidence/v02/owned-temp-cleanup-20261020-v1.json +28 -0
  259. package/docs/evidence/v02/presentation-paired-20261020.json +1438 -0
  260. package/docs/evidence/v02/proposal-resource-soak-261dc23.json +201 -0
  261. package/docs/evidence/v02/proposal-resource-soak-450439c-receipt.md +13 -0
  262. package/docs/evidence/v02/reconstructed-c03-box.json +41 -0
  263. package/docs/evidence/v02/region-seeded-dev-v1/README.md +17 -0
  264. package/docs/evidence/v02/region-seeded-dev-v1/evaluation.json +260 -0
  265. package/docs/evidence/v02/region-seeded-dev-v2/README.md +15 -0
  266. package/docs/evidence/v02/region-seeded-dev-v2/evaluation.json +232 -0
  267. package/docs/evidence/v02/region-seeded-dev-v3-candidate-only/README.md +9 -0
  268. package/docs/evidence/v02/region-seeded-dev-v3-candidate-only/control-identity-failure.json +22 -0
  269. package/docs/evidence/v02/region-seeded-dev-v3-candidate-only/evaluation.json +160 -0
  270. package/docs/evidence/v02/region-seeded-dev-v3-control-r2/README.md +9 -0
  271. package/docs/evidence/v02/region-seeded-dev-v3-control-r2/attempt-accounting.json +55 -0
  272. package/docs/evidence/v02/region-seeded-dev-v3-control-r2/evaluation.json +136 -0
  273. package/docs/evidence/v02/region-seeded-development-control-r2-freeze-20261021.json +132 -0
  274. package/docs/evidence/v02/region-seeded-development-freeze-20261021-v1.json +167 -0
  275. package/docs/evidence/v02/region-seeded-development-freeze-20261021-v2.json +186 -0
  276. package/docs/evidence/v02/region-seeded-development-freeze-20261021-v3.json +205 -0
  277. package/docs/evidence/v02/region-seeded-heldout-cancelled-before-dispatch-20261021-v2.json +20 -0
  278. package/docs/evidence/v02/region-seeded-heldout-dispatch-authorization-20261021-v3.json +47 -0
  279. package/docs/evidence/v02/region-seeded-heldout-readiness-audit-20261021-v3.json +125 -0
  280. package/docs/evidence/v02/region-seeded-heldout-run-freeze-20261021-v2.json +234 -0
  281. package/docs/evidence/v02/region-seeded-heldout-run-freeze-20261021-v3.json +357 -0
  282. package/docs/evidence/v02/region-seeded-heldout-v3/README.md +19 -0
  283. package/docs/evidence/v02/region-seeded-heldout-v3/evaluation-summary.json +445 -0
  284. package/docs/evidence/v02/region-seeded-method-selection-20261021-v2.json +66 -0
  285. package/docs/evidence/v02/region-seeded-method-selection-20261021-v3.json +120 -0
  286. package/docs/evidence/v02/region-segmentation-resource-0ba4c87-receipt.json +156 -0
  287. package/docs/evidence/v02/resource-soak-linux-8010aad.json +125 -0
  288. package/docs/evidence/v02/screen-v2-terminal-evaluation.md +22 -0
  289. package/docs/evidence/v02/seed-outline/run-manifest.json +59 -0
  290. package/docs/evidence/v02/seed-outline/summary.json +487 -0
  291. package/docs/evidence/v02/seed-unbounded/run-manifest.json +80 -0
  292. package/docs/evidence/v02/seed-unbounded/summary.json +730 -0
  293. package/docs/evidence/v02/segmentation/README.md +21 -0
  294. package/docs/evidence/v02/segmentation/component-comparison-summary.json +240 -0
  295. package/docs/evidence/v02/segmentation/provenance/MOBILE-SAM-LICENSE.txt +201 -0
  296. package/docs/evidence/v02/segmentation/provenance/OPENCV-LICENSE.txt +202 -0
  297. package/docs/evidence/v02/segmentation/provenance/mobile-sam-download-receipt.json +29 -0
  298. package/docs/evidence/v02/segmentation/provenance/mobile-sam-graph-audit.json +214 -0
  299. package/docs/evidence/v02/segmentation/provenance/mobile-sam-model-metadata.json +166 -0
  300. package/docs/evidence/v02/split-choice-noul-dev6.json +72 -0
  301. package/docs/evidence/v02/split-choice-noul-dev6.md +11 -0
  302. package/docs/evidence/v02/supported-vision-screen-20261020.md +65 -0
  303. package/docs/evidence/v02/wesnoth-1.16.11-minimap-projection-source-audit.md +56 -0
  304. package/docs/evidence/v02/wesnoth-proof-root-review-20261020-v1.json +76 -0
  305. package/docs/operations.md +8 -2
  306. package/docs/plan.md +2 -0
  307. package/docs/publishing.md +16 -1
  308. package/docs/research/text-transport-audit.md +66 -0
  309. package/docs/v02-usage.md +164 -0
  310. package/docs/vision-results.md +31 -2
  311. package/package.json +30 -5
@@ -0,0 +1,272 @@
1
+ import { z } from 'zod';
2
+ import { parse } from '../entities/contracts.js';
3
+ import { assertActive, ClassifierError } from '../entities/errors.js';
4
+ import { wholeObjectSchema } from '../entities/whole-object.js';
5
+ import { validateDecision } from '../features/choice.js';
6
+ import { targetState } from '../features/prompts.js';
7
+ import { prepareBoundaryState } from './boundary-state.js';
8
+ import { locateInput } from './locate.js';
9
+ const axisOrder = ['left', 'right', 'top', 'bottom'];
10
+ const boundaryResponseSchema = z
11
+ .object({
12
+ status: z.literal('known'),
13
+ interval: z.object({ start: z.number().int(), endExclusive: z.number().int() }).strict(),
14
+ })
15
+ .strict();
16
+ function partition(interval, tolerancePx, policy) {
17
+ const length = interval.endExclusive - interval.start;
18
+ if (!Number.isSafeInteger(length) || length < 1)
19
+ return [];
20
+ const count = policy === 'tolerance-aware-v1'
21
+ ? Math.min(16, Math.max(1, Math.ceil(length / tolerancePx)))
22
+ : Math.min(16, length);
23
+ return Array.from({ length: count }, (_, i) => {
24
+ const start = interval.start + Math.floor((i * length) / count);
25
+ const endExclusive = interval.start + Math.floor(((i + 1) * length) / count);
26
+ return { start, endExclusive };
27
+ });
28
+ }
29
+ function boundsFor(region) {
30
+ return {
31
+ left: { start: region.left, endExclusive: region.left + region.width },
32
+ right: { start: region.left, endExclusive: region.left + region.width },
33
+ top: { start: region.top, endExclusive: region.top + region.height },
34
+ bottom: { start: region.top, endExclusive: region.top + region.height },
35
+ };
36
+ }
37
+ function boundaryInstruction(axis) {
38
+ const edge = axis === 'left' ? 'left' : axis === 'right' ? 'right' : axis === 'top' ? 'top' : 'bottom';
39
+ return `Select the interval containing the ${edge}most visible pixel coordinate INSIDE HARD_ROI of the SAME physical instance identified by SEED. If the object continues outside HARD_ROI, locate only the visible intersection with that ROI; do not infer hidden parts. Coordinates use the original oriented source pixels. Choose only a supplied interval or unknown; do not infer free coordinates. The interval limits are half-open integer coordinate ranges. Current boundary intervals are candidate ranges, not new ROI limits.`;
40
+ }
41
+ function choicesFor(axis, current, tolerancePx, policy, showBoundaryView = false) {
42
+ const intervals = partition(current, tolerancePx, policy);
43
+ const labels = intervals.map((_, i) => `${axis}${String(i).padStart(2, '0')}`);
44
+ return {
45
+ intervals,
46
+ labels,
47
+ question: {
48
+ type: 'choice',
49
+ rankingSize: 1,
50
+ instructions: showBoundaryView
51
+ ? `${boundaryInstruction(axis)} A separate boundary view uses X/Y display labels. Use the exact display-label-to-choice mapping in the request state; still select only one supplied closed interval or unknown.`
52
+ : boundaryInstruction(axis),
53
+ criteria: {
54
+ ...Object.fromEntries(intervals.map((range, i) => [
55
+ labels[i],
56
+ `Integer boundary coordinates in [${range.start}, ${range.endExclusive}).`,
57
+ ])),
58
+ unknown: 'The same seeded instance or its boundary cannot be identified reliably.',
59
+ },
60
+ },
61
+ };
62
+ }
63
+ function sameInstanceQuestion() {
64
+ return {
65
+ type: 'choice',
66
+ rankingSize: 1,
67
+ instructions: 'Using ORIGINAL and SEED, determine whether extent answers can track the same single visible physical instance represented by SEED. If multiple indistinguishable instances could be the seed, or identity cannot be resolved, choose ambiguous. Select only one supplied answer.',
68
+ criteria: {
69
+ same: 'The visible extent can be tracked to the exact same physical instance represented by SEED.',
70
+ different: 'The apparent extent may belong to a different matching instance than SEED.',
71
+ ambiguous: 'The seeded instance is not uniquely identifiable in the scene.',
72
+ },
73
+ };
74
+ }
75
+ async function prepareState(raster, image, seed, target, exclusions, signal, representation) {
76
+ const seedImage = await raster.crop(image, seed, signal);
77
+ if (!representation) {
78
+ const original = await raster.imageState(image, signal);
79
+ const seedState = await raster.imageState(seedImage, signal);
80
+ return [...targetState(target, exclusions), 'ORIGINAL', ...original, 'SEED', ...seedState];
81
+ }
82
+ const views = [
83
+ {
84
+ id: 'source',
85
+ role: 'source',
86
+ image,
87
+ sourceRegion: { left: 0, top: 0, width: image.width, height: image.height },
88
+ },
89
+ { id: 'seed', role: 'crop', image: seedImage, sourceRegion: seed },
90
+ ];
91
+ const prepared = await representation.prepare({ source: image, views, signal });
92
+ return [
93
+ ...targetState(target, exclusions),
94
+ 'ORIGINAL AND SEED GEOMETRY',
95
+ JSON.stringify({ seedRegion: seed }),
96
+ ...prepared.state,
97
+ ];
98
+ }
99
+ function resultMetadata(image, stages, durationMs) {
100
+ return {
101
+ image: { width: image.width, height: image.height, coordinateSpace: 'oriented-source-pixels' },
102
+ stages,
103
+ durationMs,
104
+ knownCostUsd: stages.reduce((sum, stage) => sum + (stage.costUsd ?? 0), 0),
105
+ unknownCostCount: stages.filter((stage) => stage.costUsd === null).length,
106
+ };
107
+ }
108
+ function unknown(reason, metadata) {
109
+ return { status: 'unknown', reason, metadata };
110
+ }
111
+ function intervalWidth(interval) {
112
+ return interval.endExclusive - interval.start;
113
+ }
114
+ function selectedChoice(result, question, keys) {
115
+ const answer = result.answers[question];
116
+ if (!answer || (answer.type !== 'choice' && answer.type !== 'ranked-choice'))
117
+ throw new ClassifierError('INVALID_RESPONSE', `Missing closed Choice answer for ${question}`);
118
+ if (answer.type === 'ranked-choice') {
119
+ const choice = answer.ranking[0];
120
+ if (!choice || !keys.includes(choice))
121
+ throw new ClassifierError('INVALID_RESPONSE', 'Unknown ranked interval');
122
+ return choice;
123
+ }
124
+ const maximum = Math.max(...keys.map((key) => answer.probabilities[key] ?? -Infinity));
125
+ const choice = keys.find((key) => answer.probabilities[key] === maximum);
126
+ if (!choice)
127
+ throw new ClassifierError('INVALID_RESPONSE', 'Closed Choice has no winner');
128
+ return choice;
129
+ }
130
+ function finalRegion(bounds, roi, source, seed) {
131
+ const { left, right, top, bottom } = bounds;
132
+ const x1 = left.start, x2 = right.endExclusive;
133
+ const y1 = top.start, y2 = bottom.endExclusive;
134
+ if (x1 >= x2 || y1 >= y2)
135
+ return undefined;
136
+ const region = { left: x1, top: y1, width: x2 - x1, height: y2 - y1 };
137
+ if (x1 < roi.left ||
138
+ y1 < roi.top ||
139
+ x2 > roi.left + roi.width ||
140
+ y2 > roi.top + roi.height ||
141
+ x2 > source.width ||
142
+ y2 > source.height ||
143
+ x1 >= seed.left + seed.width ||
144
+ y1 >= seed.top + seed.height ||
145
+ x2 <= seed.left ||
146
+ y2 <= seed.top)
147
+ return undefined;
148
+ return region;
149
+ }
150
+ /** Seed a physical instance, then narrow its four visible source-space boundaries with closed interval Choices. */
151
+ export async function locateWholeObjectInput(input, decision, raster, signal, representation, boundaryView, intervalPolicy = 'fixed-16-v1') {
152
+ const started = performance.now();
153
+ const args = parse(wholeObjectSchema, input);
154
+ const normalized = await raster.normalize(args.image, signal);
155
+ const roi = args.roi ?? { left: 0, top: 0, width: normalized.width, height: normalized.height };
156
+ const seedInput = {
157
+ image: args.image,
158
+ target: args.target,
159
+ exclusions: args.exclusions,
160
+ roi,
161
+ mode: 'region',
162
+ ...(args.representation === undefined ? {} : { representation: args.representation }),
163
+ };
164
+ const stages = [];
165
+ const trackedDecision = {
166
+ async decide(request, callSignal) {
167
+ const result = await decision.decide(request, callSignal);
168
+ stages.push(result.metadata);
169
+ return result;
170
+ },
171
+ };
172
+ const metadata = () => resultMetadata(normalized, stages, performance.now() - started);
173
+ try {
174
+ const seedResult = await locateInput(seedInput, trackedDecision, raster, signal, undefined, representation);
175
+ if (seedResult.status !== 'located')
176
+ return { ...seedResult, metadata: metadata() };
177
+ const seed = seedResult.region;
178
+ let current = boundsFor(roi);
179
+ for (let pass = 0; pass < args.maxExtentPasses; pass++) {
180
+ assertActive(signal);
181
+ const specs = Object.fromEntries(axisOrder.map((axis) => [
182
+ axis,
183
+ choicesFor(axis, current[axis], args.boxTolerancePx, intervalPolicy, !!boundaryView && !representation),
184
+ ]));
185
+ const state = boundaryView && !representation
186
+ ? await prepareBoundaryState(raster, normalized, seed, args.target, args.exclusions, signal, boundaryView, roi, current, specs)
187
+ : await prepareState(raster, normalized, seed, args.target, args.exclusions, signal, representation);
188
+ const questions = Object.fromEntries(axisOrder.map((axis) => [axis, specs[axis].question]));
189
+ if (pass === 0)
190
+ questions['instance'] = sameInstanceQuestion();
191
+ const request = {
192
+ state: [
193
+ ...state,
194
+ JSON.stringify({
195
+ pass: pass + 1,
196
+ hardRoi: roi,
197
+ seedRegion: seed,
198
+ currentBounds: current,
199
+ }),
200
+ ],
201
+ questions,
202
+ };
203
+ const answer = validateDecision(request, await trackedDecision.decide(request, signal));
204
+ assertActive(signal);
205
+ if (pass === 0) {
206
+ const identity = selectedChoice(answer, 'instance', ['same', 'different', 'ambiguous']);
207
+ if (identity !== 'same')
208
+ return unknown('instance-ambiguous', metadata());
209
+ }
210
+ const next = { ...current };
211
+ for (const axis of axisOrder) {
212
+ const spec = specs[axis];
213
+ const selected = selectedChoice(answer, axis, [...spec.labels, 'unknown']);
214
+ if (selected === 'unknown')
215
+ return unknown('extent-uncertain', metadata());
216
+ const chosen = spec.intervals[spec.labels.indexOf(selected)];
217
+ if (!chosen)
218
+ throw new ClassifierError('INVALID_RESPONSE', 'Selected boundary interval is missing');
219
+ next[axis] = parse(boundaryResponseSchema, { status: 'known', interval: chosen }, 'INVALID_RESPONSE').interval;
220
+ }
221
+ if (boundaryView && !representation) {
222
+ const x1 = next.left.start;
223
+ const x2 = next.right.endExclusive;
224
+ const y1 = next.top.start;
225
+ const y2 = next.bottom.endExclusive;
226
+ if (x1 >= x2 ||
227
+ y1 >= y2 ||
228
+ x1 >= seed.left + seed.width ||
229
+ x2 <= seed.left ||
230
+ y1 >= seed.top + seed.height ||
231
+ y2 <= seed.top)
232
+ return unknown('extent-inconsistent', metadata());
233
+ }
234
+ current = next;
235
+ if (axisOrder.every((axis) => intervalWidth(current[axis]) <= args.boxTolerancePx))
236
+ break;
237
+ }
238
+ if (axisOrder.some((axis) => intervalWidth(current[axis]) > args.boxTolerancePx))
239
+ return unknown('extent-uncertain', metadata());
240
+ const bounds = current;
241
+ const region = finalRegion(bounds, roi, normalized, seed);
242
+ if (!region)
243
+ return unknown('extent-inconsistent', metadata());
244
+ const edgeLimited = bounds.left.start === roi.left ||
245
+ bounds.top.start === roi.top ||
246
+ bounds.right.endExclusive === roi.left + roi.width ||
247
+ bounds.bottom.endExclusive === roi.top + roi.height;
248
+ return {
249
+ status: 'located',
250
+ kind: 'whole-object',
251
+ seedRegion: { ...seed },
252
+ region,
253
+ normalizedRegion: {
254
+ left: region.left / normalized.width,
255
+ top: region.top / normalized.height,
256
+ width: region.width / normalized.width,
257
+ height: region.height / normalized.height,
258
+ },
259
+ bounds,
260
+ completeness: edgeLimited ? 'boundary-limited' : 'visible-object',
261
+ method: 'grid-object-extent-v1',
262
+ metadata: metadata(),
263
+ };
264
+ }
265
+ catch (error) {
266
+ if (error instanceof ClassifierError &&
267
+ error.code === 'RESOURCE_LIMIT' &&
268
+ error.details['component'] === 'representation')
269
+ return unknown('representation-limit', metadata());
270
+ throw error;
271
+ }
272
+ }
@@ -0,0 +1,10 @@
1
+ /** Optional native adapters are composed here; importing types never starts native work. */
2
+ export type { BinaryMask, SegmentationInput, SegmentationMetadata, SegmentationResult, SegmentationPort, ExtractionResult, } from './entities/segmentation.js';
3
+ export { createMobileSamSegmenter } from './infrastructure/segmentation/mobile-sam/index.js';
4
+ export type { MobileSamSegmenterOptions } from './infrastructure/segmentation/mobile-sam/index.js';
5
+ export { createMobileSamObjectProposals } from './infrastructure/segmentation/mobile-sam/index.js';
6
+ export type { MobileSamObjectProposalsOptions } from './infrastructure/segmentation/mobile-sam/index.js';
7
+ export type { PackedBinaryMask, ObjectProposal, ObjectProposalInput, ObjectProposalBatch, ObjectProposalPort, } from './entities/object-proposals.js';
8
+ export { packBinaryMask, unpackBinaryMask } from './features/segmentation/packed-mask.js';
9
+ export { createGrabCutSegmenter } from './infrastructure/segmentation/grabcut/index.js';
10
+ export type { GrabCutSegmenterOptions } from './infrastructure/segmentation/grabcut/index.js';
@@ -0,0 +1,4 @@
1
+ export { createMobileSamSegmenter } from './infrastructure/segmentation/mobile-sam/index.js';
2
+ export { createMobileSamObjectProposals } from './infrastructure/segmentation/mobile-sam/index.js';
3
+ export { packBinaryMask, unpackBinaryMask } from './features/segmentation/packed-mask.js';
4
+ export { createGrabCutSegmenter } from './infrastructure/segmentation/grabcut/index.js';
@@ -0,0 +1,7 @@
1
+ /** Reserves active plus queued snapshots before allocating any owned byte copy. */
2
+ export declare class InputBudget {
3
+ private readonly limit;
4
+ private retained;
5
+ constructor(limit: number);
6
+ reserve(bytes: number): () => void;
7
+ }
@@ -0,0 +1,21 @@
1
+ import { ClassifierError } from '../entities/errors.js';
2
+ /** Reserves active plus queued snapshots before allocating any owned byte copy. */
3
+ export class InputBudget {
4
+ limit;
5
+ retained = 0;
6
+ constructor(limit) {
7
+ this.limit = limit;
8
+ }
9
+ reserve(bytes) {
10
+ if (!Number.isSafeInteger(bytes) || bytes < 0 || bytes > this.limit - this.retained)
11
+ throw new ClassifierError('CAPACITY', 'Owned input byte budget is full');
12
+ this.retained += bytes;
13
+ let released = false;
14
+ return () => {
15
+ if (released)
16
+ return;
17
+ released = true;
18
+ this.retained -= bytes;
19
+ };
20
+ }
21
+ }
@@ -0,0 +1,190 @@
1
+ # Accepted 0.2 implementation plan
2
+
3
+ Latest owner clarification, 2026-10-21: preserve the original three-Choice region search. Use its selected region only as a segmentation prompt on the **full oriented source image**, never as a crop limiting the object. Derive the cutout, bounding rectangle and its centre from actual foreground mask pixels. Then send the original image with that rectangle and a seed anchor to the caller's decision model, asking whether the entire visible seeded object fits inside. If not, request one actual enclosing mask from the segmenter and check its derived rectangle once more. No model-generated coordinates or separate extent search. The prepared v2 blind run is [cancelled before dispatch](evidence/v02/region-seeded-heldout-cancelled-before-dispatch-20261021-v2.json); its immutable freeze and earlier failures remain historical evidence. The sealed, undispatched cohort stays blind and can evaluate the corrected workflow after separately named development selection.
4
+
5
+ Status: implementation, the corrected checked-flow resource protocol and the frozen v3 heldout admission are complete. The [current acceptance](acceptance.md) records the measured limits: 8/60 positive complete boxes, eight passing mask references out of 30, and no false seeds/cutouts on 20 negatives. The qualified recipe remains caller-owned; no text-only encoder qualified. Final archive/security/source-CI and registry publication evidence belongs to the release manifest. This document preserves the execution history below. Publication requires the final gates, not merely a successful build. The library never chooses a model/provider: benchmark routes such as Luna are explicit recipes, not defaults, and greater model capability does not guarantee monotonic accuracy improvement.
6
+
7
+ The fresh text-only development comparison is now complete for the primary photo and independent real-UI strata: all six encoders have zero correct whole-object matches. The [preserved comparison](evidence/v02/fresh-text/README.md) includes the original classification harness defect, separate corrected classification follow-ups, representation-limit failures and false locations. No text candidate proceeds to blind qualification or public promotion.
8
+
9
+ ## Active completion goal and vision selection
10
+
11
+ The owner explicitly activated the completion goal on 2026-10-20: finish this plan, close its acceptance gates, and choose the best qualified vision approach. The goal covers the standalone library and its authorized GitHub/npm release. Vision localization and experimental text-only image encodings are separate comparisons; a result from one does not qualify the other.
12
+
13
+ The earlier qualification preparation used the box-confirmation recipe documented below. That preparation is historical and is superseded by the region-segmentation implementation described in the next section. The earlier [categorical neutral-instruction recipe](evidence/v02/categorical-vision-method-selection.json) used the [v4 frozen 80-case input/source closure](evidence/v02/categorical-heldout-run-freeze-20261020-v4.json). Its [dispatch authorization](evidence/v02/categorical-heldout-dispatch-authorization-20261020-v4.json) records 451 passing local tests, all four Windows/Linux CI jobs, installed-archive checks and the completed monotonic resource gate for that snapshot. Candidate80 and photo-only no-image40 completed serially before independent evaluation. The [v4 heldout result failed](https://github.com/zoomplus/zorya-classifier/blob/36818582091bfd19951631f358183bdb9ffd1e11/docs/evidence/v02/categorical-heldout-v4/README.md): one correct whole-object box on 60 positives, zero false locations on 20 negatives, and paired-photo interval [0, 0.10], whose lower bound must be positive. Classification is 69/80 and does not qualify localization. Initial mask coverage omitted the separately indexed references; differing annotation canvases remain explicit and cannot repair the box/paired result. This cohort is now exposed regression data. A changed method requires a separately named development comparison and a fresh blind cohort. Earlier experimental branches below are preserved history, not instructions to select a runner-up from the failed heldout.
14
+
15
+ ## Current checked region-segmentation implementation
16
+
17
+ The latest owner correction supersedes the earlier prohibition on a post-segmentation model confirmation. It does not restore interval searching. A closed `box_check` Choice uses the fixed ordered IDs `complete`, `needs-expansion`, `unknown`, selecting the categorical maximum with stable ties and preserving scores. The source presentation marks the actual mask-derived rectangle and the original seed, so a photograph containing several matching objects still refers to the same physical instance. The prompt asks about visible containment, not unseen geometry or pixel-mask purity. Caller-injected models/providers remain mandatory; no model is connected by the library.
18
+
19
+ Contracts: `SegmentationBoxCheckPort.check` receives full-source RGBA, actual `region`, original `seedRegion`, hard `roi`, target/exclusions and the operation signal. It returns a validated verdict, selection and actual request metadata. `SegmentationBoxPresentationPort` renders the bounded full-source presentation. `SegmentationPort.expand?` receives the original full-source segmentation input plus a full-source binary previous mask. Existing custom segmenters remain compatible: an initial complete result needs no expansion; an incomplete result without expansion support returns an explicit unknown. `SegmentationVerification` records both checks and whether expansion was unnecessary, unsupported, unavailable, rejected or applied.
20
+
21
+ Runtime: retain one absolute deadline across region search, segmentation, verification, optional expansion and final verification. Permit at most two containment checks and one expansion. A corrected mask must preserve all prior visible foreground, add foreground and grow at least one actual box edge while respecting source/ROI/alpha. MobileSAM selects the smallest eligible native enclosing mask, then native quality and token order for deterministic ties. Native quality is not semantic confidence. Missing or unresolved correction yields `segmentation-uncertain`; never stretch a box or publish a partial cutout as complete. Derive bounds, centre, mask and PNG only after a complete check. Record verification costs with the region stages. Region-only localization remains unchanged.
22
+
23
+ Transport preparation is mandatory after full-source box rendering. Inject the existing `RasterPort.imageState`, preserving all source content and recording scale/padding rather than sending raw arbitrary-size PNGs. Built-in transports validate prepared sides 280..1568; keep that validation. The added native/ordinary public-adapter regression found the first implementation's missing preparation on 64×48 and 1920×1080 inputs. Preserve the interrupted checked-flow resource attempt; repeat affected gates on corrected runtime bytes. Mask geometry remains in the full oriented source coordinate space.
24
+
25
+ Implementation wave: root first commits shared contracts, rules and cancellation receipt; A5 then owns native request/worker/adapter expansion and focused tests in its own worktree; A6 owns the generic closed checker, full-source box/seed presentation and their tests in another worktree; root implements process/factory integration and operation/ownership tests. A3 keeps the existing resource run untouched as historical baseline and prepares checked-flow native/resource coverage separately. Merge focused tested commits before global checks. New DEV recipes count up to five model calls for extraction (three region plus two checks), or six with the benchmark's caller presence guard. Freeze source, prompt, routes, scheduling and budget before a new complete DEV comparison; do not relabel the old v2 results or open the unused blind cohort before selection.
26
+
27
+ The integrated checked-flow runtime at `987ac32` passed installed-archive checks and [native Windows execution](evidence/v02/native-checked-region-windows-987ac32.json), with all ten owned children closed. The merged producer/evaluator fixture and freeze guard bring the offline suite to 563 passing tests at `29357d7`, including [all four hosted Windows/Linux × Node 22/24 jobs](evidence/v02/ci-29357d7.json). The [v3 development freeze](evidence/v02/region-seeded-development-freeze-20261021-v3.json) pins all 68 producer sources, the explicit route, prompts, native assets and budgets before the complete candidate/control comparison. Corrected-runtime two-hour resources and independent quality qualification remain pending; these platform checks do not qualify accuracy or publication. The [completed checked resource attempt](evidence/v02/checked-region-segmentation-resource-987ac32-receipt.json) retains valid measurements but failed its strict idle-before deadline by 0.377521 ms. Preserve that failure, repair the harness to await an absolute monotonic deadline without tolerance, test early timer resolution and cancellation, then repeat the full resource protocol on unchanged runtime bytes before blind dispatch.
28
+
29
+ The complete v3 candidate is [scored independently](evidence/v02/region-seeded-dev-v3-candidate-only/README.md). The original paired evaluation stopped because the freeze reused the candidate prompt text when calculating the control identity. Preserve that failure and the unscored original control. A new [prospective control-R2 freeze](evidence/v02/region-seeded-development-control-r2-freeze-20261021.json) is verified from explicit per-profile prompt policies before repeating the complete control40. Reuse only the already frozen immutable candidate; do not rerun candidates to choose a better outcome. This corrects freeze authoring without changing the 68-file producer, runtime, model prompts or the sealed blind cohort.
30
+
31
+ The complete R2 [paired seed comparison](evidence/v02/region-seeded-dev-v3-control-r2/README.md) passes development admission. The [conditional v3 method selection](evidence/v02/region-seeded-method-selection-20261021-v3.json) freezes that exact recipe before new blind pixels or per-case truth are opened. All 40 candidate cases, 40 R2 controls, provider failures, unknown charges and the first failed control identity remain preserved. Whole-object box and mask quality remain separately reported and low; no universal accuracy or release qualification is inferred from the seed-search statistic.
32
+
33
+ The [new v3 blind freeze](evidence/v02/region-seeded-heldout-run-freeze-20261021-v3.json) retains all 80 unused cases and the unchanged truth/source seals. Candidate and control have separately reconstructed ordered JSON identities; an [independent metadata audit](evidence/v02/region-seeded-heldout-readiness-audit-20261021-v3.json) confirms them and preserves the first rejected zero-request draft. Both offline preflights verify 80/80 images. Model dispatch remains disabled until checked-flow two-hour resource acceptance and a separately recorded integrator GO. Evaluate truth only after both producer profiles are terminal, preserving every failed, unknown and unavailable result.
34
+
35
+ The previous unverified region-segmentation version below remains baseline evidence; its source/platform/resource checks cannot qualify newly changed runtime bytes.
36
+
37
+ The previous primary flow used the original three-Choice region locator followed by one explicitly supplied full-source segmenter. `extract` returned a cutout, mask-derived bounding rectangle and its centre; `locate` with a configured segmenter obtained the same mask-derived geometry. No separate extent search or model confirmation ran in that baseline. Default region localization stayed compatible, and the caller selected its model and segmenter. All 486 integrated offline tests and strict source/build/format checks passed at `c6b8069`. These are baseline checks, not evidence for the latest correction.
38
+
39
+ The old box-confirmation dispatch was [cancelled before any provider call](evidence/v02/box-heldout-cancelled-before-dispatch-20261021-v1.json). Its undispatched 80-case cohort remains sealed for qualification after new development selection. In the replacement benchmark, original region hits and mask-derived object geometry are separate metrics. A genuine image-free control can compare region localization without a native segmenter; it must never claim a cell is a whole-object box. All previous failed variants and their unchanged scoring remain historical evidence below. Earlier statements about preparing or selecting the box-confirmation flow are superseded by this section and the owner's clarification above.
40
+
41
+ The [first complete region-seeded DEV40](evidence/v02/region-seeded-dev-v1/README.md) failed its negative-case admission gate: one absent target produced a seed and a cutout. Its separately recorded classification correctly rejected that target. The separately named [presence-first v2 caller recipe](evidence/v02/region-seeded-development-freeze-20261021-v2.json) reuses the existing classification result and starts the unchanged region/segmentation workflow only after a successful `present` result. Absence and classification errors remain explicit, with no localization calls. This adds no model call and does not change the library's public `extract` or model selection. It supersedes the earlier classification-independent benchmark policy only for these explicitly named v2 profiles. Repeat all DEV40 candidate/control cases before selection; do not qualify from a post-hoc filter. The same heldout executable is prepared but remains unauthorized until the new development gate and independent freeze pass.
42
+
43
+ The [complete v2 rerun passed development admission](evidence/v02/region-seeded-dev-v2/README.md), while its mask-derived box score remained 5/30. The [conditional development selection](evidence/v02/region-seeded-method-selection-20261021-v2.json) freezes that exact caller composition before opening the unused blind cohort. Its [new run freeze](evidence/v02/region-seeded-heldout-run-freeze-20261021-v2.json) measures the owner's region-first workflow: actual foreground in a seed is distinct from a complete object mask. The paired positive-photo gate compares exact reference-mask seed hits and separately labeled coarse seed-box hits against the identical image-free seed task. Mask-derived boxes retain IoU >= 0.5 and coverage >= 0.9; exact masks retain IoU >= 0.8. Neither whole-object success nor pixel-mask quality can be inferred from the seed statistic. All unknowns/errors, verified negatives and available reference masks remain in their respective denominators. Source/CI/resource gates remain mandatory, and a frozen recipe is not publication qualification.
44
+
45
+ ## Historical box-confirmation hypothesis (superseded before dispatch)
46
+
47
+ The failed v4 positive funnel motivates a new implementation, not selection of another recipe from its revealed answers. The explicit proposal workflow will judge the actual foreground-derived bounding rectangle separately from pixel-mask completeness, and final confirmation will receive the one selected candidate at source resolution. No inferred coordinates, padding to annotations, color heuristics, model default or lower scoring threshold are added. The compatible region/interval workflow is unchanged.
48
+
49
+ The separately named `native-luna-box-confirmation-sam-target-plus-neutral-v1` development profile retains the full original 20-photo/10-UI/10-map inputs, target-plus-neutral policy, explicit Luna/OpenAI route, pinned CPU MobileSAM assets, 120-second operation deadline, two case workers, one active/two waiting native jobs, two native threads, 8-pixel tolerance, three extent passes, at most six physical calls per case, and 300 calls / $5 / $0.05 unknown-cost reservation. It has no retry or fallback. Freeze the actual executable closure before dispatch and preserve the new identity in its independent evaluator. The old two-profile comparison and old heldout identities are not relabeled.
50
+
51
+ A successful DEV gate precedes a new source-group-disjoint blind freeze; exposed v4 cases remain regression data. The two changed runtime files also require fresh resource and native installed-archive acceptance. The `450439c` monotonic two-hour run is active with explicit two native threads and no terminal result. Its interrupted one-thread setup attempt is retained and is not a pass. Fresh native Windows installed-archive checks and all four hosted CI jobs have passed for the changed runtime; final source checks remain mandatory. The [complete new DEV40 report](evidence/v02/box-confirmation-dev40/report.json) records 5/30 positive whole-object matches, photo 4/16, classification 35/40 and zero false locations on ten negatives. Mask evidence remains insufficient: one annotated case returned a prediction, neither of its two references passes IoU 0.8. The native-only refinement component test preserved all eight actual model-predicted boxes/seeds and original outputs. Its one annotated returned case still has zero passing mask references; no additional refiner is promoted. The [new development selection](evidence/v02/box-confirmation-method-selection-20261020-v1.json) freezes the box-confirmation recipe before acquisition of a fresh source-group-disjoint blind cohort. The native resource gate and new blind qualification remain pending.
52
+
53
+ The [separate HELDOUT runner review](evidence/v02/box-heldout-source-review-20261020-v1.json) pins the complete 82-file dispatcher closure, including its indirect encoder imports and three metadata files. An independent TypeScript AST traversal confirms coverage; all 53 runtime sources remain byte-identical to `b03210a`. New candidate/control profiles have strict, separate identities, freeze validation and successful synthetic normalization checks. Historical plans/evaluators remain unchanged. [The sealed photo acquisition](evidence/v02/box-heldout-photo-seal-20261020-v1.json) has 30 positives with exact-canvas official masks and ten verified negatives, disjoint from all 160 exposed photo/source groups. The [controlled Calculator proof](evidence/v02/calculator-proof-root-review-20261020-v1.json) has been independently checked, and [its sealed UI20 cohort](evidence/v02/box-heldout-ui-seal-20261020-v1.json) has 15 positives and five verified negatives. The [controlled map20 seal](evidence/v02/box-heldout-map-seal-20261021-v1.json) and [combined corpus/asset seal](evidence/v02/box-heldout-combined-seal-20261021-v1.json) now pin the complete cohort. The [strict input/source freeze](evidence/v02/box-heldout-run-freeze-20261021-v1.json), SHA-256 `31e6b96b485b1dccbbe10b2c5e222ca0c20f3adbe25ee027a682595a136d8650`, is validated against all 82 producer files and the unchanged development selection. Resource completion, source-oracle integration and final source/CI review still precede any blind dispatch.
54
+
55
+ The [excluded Wesnoth minimap proof](evidence/v02/wesnoth-proof-root-review-20261020-v1.json) independently verifies the source-derived placement: all 1,297 changed pixels fall inside the projected support of 51 markers, and repeated same-state minimap pixels are identical. Source-faithful float32 accumulator sampling resolves the three edge pixels missed by ideal-rational scaling. The projection remains an upper bound because later terrain can overpaint it; per-marker visible boxes require isolated A/B evidence. The controlled map20 now contains 15 isolated A/B/A positives and five source-verified negatives across 20 official map sources; original and canonical input pins are sealed. Its per-case oracle remains the independent curator's evidence, separate from this excluded proof. This is one actual rendered game/editor family, not diverse natural-game validation.
56
+
57
+ ## Historical vision investigation
58
+
59
+ The earlier vision development wave compared the existing numeric boundary-interval baseline with visually labeled axis bands using the same closed intervals, source coordinates, instance grounding and geometric validity rules. Consider splitting axis questions only if the band presentation remains insufficient. Preserve the failed baseline, add allowlisted decision/geometry traces for new runs, and freeze each presentation/prompt variant before dispatch. The Luna Decisions route is one explicitly pinned benchmark candidate, not a library default.
60
+
61
+ The historical source snapshot `a6c2683` passed 376 tests and hosted CI. Later integration passed 393 Linux tests, source/format/installed-archive checks, and [2922caf passed every Windows/Linux × Node 22/24 job](evidence/v02/ci-2922caf.json). Earlier archive-path and Windows test-path failures remain in evidence; neither repair weakened a scanner or changed runtime behavior. At that historical snapshot, all 53 runtime source files were unchanged from frozen snapshot `0c`; the replacement resource harness is separately pinned below. The supported 20-photo [screen report](evidence/v02/supported-vision-screen-20261020.md) does not replace the full DEV40 evaluation or authorize a winner. Do not declare qualification or publication from a screen.
62
+
63
+ The [fresh interval comparison](evidence/v02/fresh-development.md) remains unqualified. A subsequent private diagnostic freezes MobileSAM point proposals from actual model-selected seed regions, renders every actual mask as an alias-only candidate, and lets Luna make one closed Choice plus a separate whole-target binary check. Source pixels produce candidate boxes; a language model never supplies coordinates. Raw native quality is not semantic confidence. Producer requests contain no case identifiers, source filenames, native quality, split or truth; early identifier-labeled sheets were rejected before any paid use and retained. Candidate replay and oracle upper bounds do not qualify an end-to-end implementation. Any promoted alternative still requires the same source/ROI ownership, one absolute operation deadline, final method contract, independent metrics, blind freeze and lifecycle gates.
64
+
65
+ The axis-band candidate is opt-in through the internal benchmark composition, not the public default. Its rendering port receives only oriented source pixels, a source rectangle and closed candidate intervals; it never identifies objects. First-pass left/right share one X view and top/bottom share one Y view. Later passes zoom each boundary interval independently with 25% surrounding context, clipped to the hard ROI, while retaining original and seed images (at most six images per extent request). Display labels `X0…XF` / `Y0…YF` map explicitly to exact Choice IDs. The process validates returned PNG metadata, source transforms and candidate identity before dispatch. Text-only encodings ignore this renderer and continue to send zero image parts. Allowlisted traces retain selected IDs, raw native scores, actual seed/boundary geometry and transforms without recording images, request text or credentials.
66
+
67
+ Whole-object variants share the existing IoU/visible-coverage criteria, independent annotations, errors/unknown denominators and false-location checks. The default region mode retains its fragment-localization semantics and is reported separately; a cell is not compared as if it were an object box. Compare MobileSAM and GrabCut on the same independently labeled masks, distinguishing oracle-box component tests from end-to-end extraction.
68
+
69
+ Select eligible vision approaches by whole-object accuracy and reliability first, then p95 latency, actual request count and cost. Timings from concurrent native stress are diagnostic only. Keep the public tolerance contract and quality thresholds; parameter experiments must be explicitly named and frozen during development. Select and freeze the winning implementation before heldout evaluation, without choosing a new winner from revealed heldout outcomes.
70
+
71
+ The original native resource run used frozen native/extraction/raster code from `8010aad`; that run is historical. New diagnostics and resource work proceed in separate worktrees. Final completion requires the source/archive/platform/resource gates and documented benchmark evidence. A build or a short smoke run alone does not complete the goal.
72
+
73
+ The `8010aad` resource report is historical coverage of the earlier pipeline. The optional public packed-proposal path requires a new frozen two-hour run. Its first complete 40-case DEV run exposed a native input-validation defect: a repeated-group base64 regular expression throws `RangeError` on the admitted 1280×800 RGBA payload. This is independent of decision-model accuracy. The [before](evidence/v02/native-base64-before.json) and [after](evidence/v02/native-base64-after.json) offline reproductions pin the protocol source, sizes and zero provider/native calls. Native protocol validation now uses a bounded linear canonical-base64 scan, with large-payload, malformed-padding and nonzero-pad-bit regressions across the request/reply schemas. Preserve the failed DEV run; repeat the complete DEV pass on the repaired source before winner freeze, rather than repairing individual outcome rows. Then perform the new resource, archive and platform gates on the final runtime bytes.
74
+
75
+ The repaired [DEV2 run](evidence/v02/dev2-proposal-evaluation.md) completed without recurrence of the protocol overflow. Its complete 40-case recipe was frozen before blind evaluation. The [80-case heldout evaluation](evidence/v02/heldout-evaluation-20261020.md) failed: 3/60 positive boxes, 2/20 false locations, and a paired photo bootstrap lower bound of zero. Preserve that failure and its original source, truth and output hashes. Do not choose a runner-up from its revealed outcomes. All 53 runtime source files remain unchanged from `0c365cb`; native Windows installed-archive checks apply to those same runtime bytes.
76
+
77
+ Additional ordinary-model screens must distinguish unsupported API configurations from visual errors. Preserve the incomplete JSON-Schema Qwen screen and the ordinary-Luna 45-second timeout results. Probe actual multi-image route compatibility before an expanded run, without automatic format/model/provider fallback. A differently configured deadline or response format is a separately named and frozen development recipe; it does not repair earlier rows. The public default deadline remains 45 seconds. Timings collected while native stress is running are diagnostic and cannot rank latency. Keep per-case native metadata and physical request accounting independent when overlapping operations.
78
+
79
+ The first proposal resource report used wall-clock phase accounting and could not prove the full physical duration. Its measurements remain historical. The replacement resource harness is frozen at `261dc23`: monotonic deadlines, at least 600 seconds warmup, 6,540 busy seconds, two 30-second idle windows, five-second samples, and an independent process/kernel-uptime witness. The complete replacement run passed its monotonic-duration, memory and owned-child gates; see [the pinned receipt](https://github.com/zoomplus/zorya-classifier/blob/36818582091bfd19951631f358183bdb9ffd1e11/docs/evidence/v02/proposal-resource-soak-261dc23.md). The earlier incomplete duration proof remains preserved. The public runtime itself already uses monotonic operation durations and owned deadline timers.
80
+
81
+ The first split-port vision experiment assigned closed spatial Choices and independent Noul predicates to separate caller-supplied adapters. Its [DEV6 pilot failed](evidence/v02/split-choice-noul-dev6.md): classification 4/6, positive whole-object matches 0/2, zero false locations on four negatives and one failed negative localization. It is ineligible for full DEV40 dispatch; preserve its source, outputs, failures and unknown cost.
82
+
83
+ The next offline composition assigns each binary predicate to a native closed Choice over `present`, `absent`, and `unknown`, while spatial Choice uses its separately configured adapter. Validate native scores and select a maximum with existing stable-tie rules, then return categorical `presence`; never fabricate a NoUL score or probabilities. Preserve the exact predicate/state/images and journal the actual transformed Choice request before conversion. A mixed request awaits both physical branches under one absolute deadline; failures cancel and join the sibling branch. Each route has its own physical/logical recorder and truthful receipts under one hard budget. Composite metadata must not invent a single model identity or duplicate costs. Use no fallback, game/color heuristics, or free model coordinates.
84
+
85
+ These split-port experiments belong to examples and benchmark composition, with no built-in model/provider or modification of the frozen 53-file runtime. Their frozen six-case pilot gate requires at least one correct positive whole-object result and no false locations on four verified negatives; failed split profiles do not proceed to full DEV40. Freeze every controller, model, prompt, deadline, scheduling policy and native limit before dispatch.
86
+
87
+ The next, distinct comparison uses one injected native DecisionPort for both original spatial Choices and converted categorical binary Choices in the same request. It preserves original spatial answers, maps only converted binary answers to categorical presence, and preserves the single actual request's metadata and receipt. This reduces the physical upper bound to six calls per case, including classification. Predeclare two full DEV40 profiles: (A) the original target plus neutral instruction suffix, and (B) the manifest target alone. The only removable suffix is the exact neutral procedural instruction present on the 20 photo inputs; reject any other nonempty instruction rather than discard task constraints. UI/map descriptions stay unchanged. This paired ablation tests the complete original DEV corpus rather than filtering a new native batch method on two difficult photos. Each profile has 300 physical calls / $5 / $0.05 unknown reserve, a 120-second operation budget, two case workers and the same pinned native limits/assets. Profiles run serially; runtime defaults stay unchanged. This protocol is recorded before either profile dispatches.
88
+
89
+ Mandatory full DEV40 eligibility remains unchanged: all cases and failures count, and no false locations are allowed on its ten verified negatives. Qualification additionally requires the unchanged positive-box/mask criteria and a new source-group-disjoint blind 80-case cohort; no revealed heldout case may become fresh evidence. Keep new heldout truth and pixels sealed from producers until a new DEV candidate is frozen. Before a larger run, derive its worst-case physical-call bound and freeze sufficient explicit admission caps without weakening legacy budget tiers. Preserve all failed variants and never replace a revealed heldout failure with a runner-up.
90
+
91
+ Both native categorical DEV40 profiles have now completed on frozen producer `e4f55ba`. Preserving the neutral instruction reached 3/16 photo whole-object matches; target alone reached 0/16. Both classify 35/40, with zero false locations on ten negatives. All eight and four respective 502 failures and unknown charges remain counted. [The development method selection](evidence/v02/categorical-vision-method-selection.json) freezes the first profile before any new heldout pixels or per-case truth are opened; concurrent timings are not used. Carry its 120-second deadline, two case workers, one active/two waiting native jobs, CPU threads, prompts and source transforms unchanged into blind qualification. The new run freeze must additionally bind the canonical 80 input pins, this method-selection digest and the actual executable producer closure. Source/CI, monotonic resource acceptance and independent oracle integrity remain prerequisites to blind dispatch. Development selection does not authorize publication or install a model/provider default.
92
+
93
+ ## Results and compatibility
94
+
95
+ 1. Preserve the default three-Choice region locator and its result shape. A grid cell is evidence of a fragment, never an object mask.
96
+ 2. Add explicit `mode: 'whole-object'`: seed search followed by closed boundary interval selection for one visible physical instance.
97
+ 3. Add `extract`: transparent PNG, binary mask, source-space box and offset. Segmenters are optional, explicit, caller-owned adapters; no rectangle fallback.
98
+ 4. Compare six text-only image encodings. Promote exactly one tested winner as `experimental-text` / `text-image-v1`; do not expose losing research implementations in the published archive.
99
+ 5. Support custom ports and generic OpenAI-compatible text transports. No provider/model fallback, fabricated probabilities or automatic native asset downloads.
100
+
101
+ ## Frozen S1 contracts
102
+
103
+ Contracts live in `src/entities/{contracts,whole-object,representation,segmentation}.ts`. Public operation bytes are snapshotted before the first await. Validation uses Zod. Default timeout is one absolute 45-second deadline including queue time; cancellation never releases a physical slot early. Default admitted input-byte budget is 64 MiB and is reserved before copying.
104
+
105
+ Whole-object input adds `boxTolerancePx` (1–64, default 8), `maxExtentPasses` (1–3, default 3). Success contains `kind: 'whole-object'`, `seedRegion`, conservative `region`, normalized region, four half-open `bounds` intervals, completeness (`visible-object` or `boundary-limited`), method and all stage receipts. Unknowns distinguish instance ambiguity, uncertain extent, inconsistent extent and representation limits. Old failures retain their meaning.
106
+
107
+ Each extent API request asks four closed Choices: leftmost, rightmost, topmost and bottommost visible pixel of the same seeded instance. Partition each axis into at most 16 actual integer intervals plus `unknown`; recursively partition the selected interval. Never generate free coordinates. Stop within three passes when interval widths meet tolerance. If l∈[aL,bL) and r∈[aR,bR), the conservative horizontal extent is [aL,bR). Left/right intervals may coincide for a small object. Validate existence, source/ROI containment and seed intersection; do not sort, clamp or average invalid answers. Hard ROI boundaries limit visible completeness. Multiple indistinguishable seeded instances yield unknown.
108
+
109
+ `ImageRepresentationPort.prepare` receives all views for one decision request at once. Every view has source region, role and optional code-generated grid metadata. Text encoders operate on clean pixels; rendered grid labels are metadata, not extra encoded pixel views. Deduplicate identical clean/grid regions. Bound all encoded views together to 64 KiB UTF-8, each string to 64,000 UTF-16 units, and ASCII/RLE resolution to at most 64×64. Allocate resolution before encoding; never truncate a final encoded string. Record dimensions, transforms and bytes. If distinct candidate geometry collapses at that resolution, report unknown rather than fabricate precision.
110
+
111
+ `SegmentationPort.segment` receives canonical RGBA8, seed region, whole bounds, hard ROI, target/exclusions and signal. It returns a source-size binary-u8 mask plus actual backend/method/time/input dimensions or a named unknown. `settle` joins physical work; `dispose` closes only the adapter's own resources. Classifier joins its segment tasks but does not close an externally shared segmenter. Extraction crops by the mask box, zeros RGBA outside the mask, preserves source color/alpha inside it, and reports source offset. Validate binary values, dimensions, pixel cap, ROI and seed intersection. No fictitious confidence from native quality scores.
112
+
113
+ ## Encoders and strict codec
114
+
115
+ Research candidates: `ascii-luma-v1`, `palette-grid-v1`, `svg-contours-v1`, `pixel-rle-v1`, `ascii-contours-v1`, `ascii-svg-layered-v1`.
116
+
117
+ RLE grammar is versioned and deterministic:
118
+
119
+ ```text
120
+ PIXEL_RLE_V1
121
+ width=8 height=2
122
+ color=RGBA8 order=row-major
123
+
124
+ y0: #FF0000FF*5 #000000FF*3
125
+ y1: #FFFFFFFF*8
126
+ ```
127
+
128
+ Uppercase eight-hex RGBA, positive decimal run count, no crossing rows, exact row widths, ascending unique row numbers and exact height. Validate caps before allocating; no eval, exponent/fraction counts, duplicate rows or overflow. Roundtrip equality applies to the normalized raster, not the original photo before lossy resizing. Compare raw/PNG/text bytes and tokens; do not claim RLE always compresses photographs. Prompt ablation: no grammar, grammar only, grammar plus two synthetic examples. Examples contain no benchmark ground truth.
129
+
130
+ Contour candidate: luma → p1/p99 contrast stretch → Otsu binary; independently Sobel → gradient threshold. Export BINARY and EDGES ASCII planes. Constant images must not divide by zero or invent edges. Replicate borders; distinguish transparency. Do not assume dark pixels are the target, discard small components or add morphological fitting. SVG derives from the original color raster, not thresholded pixels: at most 512 contours/2,048 vertices, no scripts, external URLs or foreignObject. Exceeding representable limits is explicit. Layered candidate sends both representations in one call, with common source geometry. Compare ASCII, SVG and layered evidence independently.
131
+
132
+ Text-only localization uses no OCR, semantic model, hidden segmentation or image input. Neural `extract` is a separate explicitly reported workflow. Text factories reject image parts before fetch; they do not silently discard them.
133
+
134
+ ## Native segmentation and ownership
135
+
136
+ Port universal utilities and algorithms from the game repository without `@element` imports or game DTOs. Add optional peer `onnxruntime-node@1.23.2`; core and segmentation entry imports do not initialize native sessions. Explicit CPU required; DML only when requested and available, with no CPU fallback. Default one owned child, at most two waiting jobs. IDs fence late results; parent loss stops owned children. A job has a 10-second native limit; abort gives 250 ms grace before killing only the owned child. Capacity releases after confirmed exit/settlement; poison the adapter if exit cannot be confirmed.
137
+
138
+ Pinned Acly/MobileSAM revision `0d3b403339b4674a82493d5e97964dd78089ddc8`:
139
+
140
+ | Asset | Bytes | SHA256 |
141
+ | ----------------------------- | -------: | ---------------------------------------------------------------- |
142
+ | mobile_sam_image_encoder.onnx | 28157093 | 580f5fb648ea1062c0aabc26217aed56921985f03f0cbbd852bba81d760cc749 |
143
+ | sam_mask_decoder_multi.onnx | 16496559 | 8976b90a87ba50a6a72217a5ff994f7d25ce16f2229fcc1ed259e1294c622ffe |
144
+
145
+ Encoder takes HWC RGB float32 values 0–255 and performs normalization/padding itself. Prompt the whole box, preserve the seed separately for grounding. Preserve model/asset license and graph validation. OpenCV 4.13.0 JS (10,964,323 bytes, SHA256 `63366510248adf3a7eddf3e793dd825404efb7df3749f4d6f8557c7fa4ca8aa0`) is an explicit alternate backend, never silent fallback.
146
+
147
+ ## Work ownership and waves
148
+
149
+ Root orchestrator: GPT-6.1 Sol / max. New implementation agents: GPT-6 Luna / high. At most three executors plus root. Each executor uses its own worktree, commits focused changes, reports SHA/files/tests/evidence/limitations and removes only its own temporary files/processes. Root alone changes shared contracts, factory, public exports, CLI, package/lock/config and root documentation. Coordinate shared files before edits. Global checks run on a stable merged tree.
150
+
151
+ | Wave | Root | Parallel executors | Gate |
152
+ | ---- | -------------------------------------------------- | -------------------------------------------------------------------------------------- | ---------------------------------- |
153
+ | 0 | baseline, plan, S1 contracts | A3 native audit, A5 protocol audit, A6 independent acceptance/corpus | S1 SHA |
154
+ | 1 | review/contracts/byte ownership | A1 canonical pixels/transforms/RLE; A2 whole-object intervals; A3 segmentation/extract | focused checks, merge A1→A2→A3 |
155
+ | 2 | integrate M1 | A4 other encoders; A5 text transports/requests; A6 independent corpus/evaluation | M2 SHA |
156
+ | 3 | public composition, CLI, clean packaging | A2 integration fixes; A3 native fixes; A6 independent API/archive audit | BENCH SHA |
157
+ | 4 | freeze routes/prompts/budgets | A5 sole paid dev runner; A6 evaluator; A3 native fixtures outside latency measurements | winner freeze |
158
+ | 5 | promote only winner, byte-parity check, clean dist | scoped fixes only | RC1 SHA |
159
+ | 6A | freeze implementation | A5 heldout API run; A6 blind evaluation; A3 light prep | public experiment gate |
160
+ | 6B | native/resource integration | A3 Windows/Linux native; A6 resource audit; A5 docs | resource gate |
161
+ | 7 | classify fixes and rerun affected gates | relevant owners | fresh holdout if algorithm changes |
162
+ | 8 | documentation, archive, GitHub/npm release | independent final review | all mandatory checks |
163
+
164
+ A1 owns canonical normalization, transforms, representation budgets/RLE and tests. A2 owns extent geometry, prompts, orchestration and tests, using frozen ports/fakes. A3 owns explicit native backend workers, mask/PNG and extraction process, injected whole-locator port (no dependency on an unmerged implementation). A4 owns other research encoders and normalization ablations. A5 owns text protocol and exactly one paid dispatcher. A6 owns independent annotated corpus, oracle/evaluator, no-image payload checks and installed-consumer acceptance. An audit role can finish before a new executor takes the same concurrency slot; old inherited-model agents do not silently become Luna.
165
+
166
+ ## Evaluation and selection
167
+
168
+ Existing exposed images are regression/development evidence only. Acquire source-group-disjoint licensed dev40 and heldout80, with independent labels/masks. Dev: photos16 positive/4 negative, maps7/3, UI7/3. Heldout: photos30/10, maps15/5, UI15/5. Synthetic UI cannot be reported as real UI. A missing annotation is not a negative. Keep labels outside producer inputs and hash/freeze manifests before paid calls.
169
+
170
+ The controlled UI source oracle uses JSONL and measured pixel allocations, whereas the shared evaluator consumes normalized JSON arrays. The evaluator-only `canonical-ui-truth.ts` converter validates source identities, painted-edge confirmation, allocation agreement and scoped negative inventory evidence before conversion. Its tests use synthetic records; the HELDOUT oracle itself must not be opened or converted until the vision recipe is frozen. Preserve original oracle hashes and record the derived evaluator array separately. Never import this converter into a producer.
171
+
172
+ Primary whole-object match requires IoU≥0.5 AND visible GT coverage≥0.9 for the same GT instance. Mask IoU≥0.8. Include unknown/errors/timeouts in denominators. Photo classification is a separate closed-label request and cannot feed localization. Text control receives no image or encoding. Pin exact DeepSeek Flash/Together text and Luna Decisions/OpenAI vision control routes; unavailable routes are not auto-replaced.
173
+
174
+ All offline/security/privacy/resource/protocol gates and zero false locations on ten dev negatives are eligibility conditions. Rank eligible candidates lexicographically by photo localization, photo classification, lower p95, lower cost, then representation bytes. Freeze winner before heldout. Heldout gate: all mandatory checks, at most one false location on twenty negatives, and positive lower 95% paired-bootstrap bound versus no-image control on photo localization (10,000 resamples, fixed seed). Do not choose a runner-up on revealed holdout. If none qualifies, retain research evidence and leave public experimental release unqualified.
175
+
176
+ One dispatcher counts physical API admissions separately from receipts; failed/missing receipts retain unknown cost. Use explicit hard run budgets despite broad owner authorization. Preserve failed/incomplete runs. Never log keys, raw private media or user sessions.
177
+
178
+ ## Final acceptance and documentation
179
+
180
+ Run `npm run check`, `npm run pack:check`, `npm run format:check`; installed archive tests cover old/default behavior and new modes. Source/pack secret scanning remains strict. Clean dist before build; losing candidates/research corpora/native weights are absent from npm. Core import works without ONNX. Compare public promoted encoder byte-for-byte with the frozen benchmark winner.
181
+
182
+ Offline cases: large/tiny/thin/edge/transparent/negative/multiple-instance objects, ROI/EXIF transforms, invalid intervals, stable ties, malicious RLE, constant normalization, overflow and byte budgets, cancellation at awaits, queue pressure, duplicate/late worker messages, worker exit/parent loss and caller-owned segmenter reuse. Timers, listeners, ports and owned processes reach zero after dispose. No early native-slot reuse.
183
+
184
+ Resource run: two hours, ten-minute warmup, five-second snapshots, no forced GC; compare corresponding idle windows. Heap/external/arrayBuffers increase ≤max(10%,16MiB), RSS >max(20%,128MiB) requires investigation and rerun. Record actual host limits and platforms. Configured CI is not execution. Windows user game/install checks remain owned by the user and separate from this library.
185
+
186
+ Update README/Russian README, API, architecture, operations, provenance/licenses, examples/CLI, benchmark/results, contributor rules and publication checklist. Preserve old reports. Release 0.2 only after mandatory qualification, not as an alias for incomplete work.
187
+
188
+ The owner reconfirmed npm publication and discoverability on 2026-10-20. At release, update npm keywords and GitHub repository topics to match the capabilities actually qualified and shipped, create the version tag and release notes, publish the reviewed archive, and verify the registry version/dist-tag and GitHub/npm links. Update usage examples and measured-results documentation before publication. Do not tag an unqualified research method as a supported public feature.
189
+
190
+ The replacement monotonic resource gate completed on frozen source `261dc23`: [duration, memory and owned-process evidence](https://github.com/zoomplus/zorya-classifier/blob/36818582091bfd19951631f358183bdb9ffd1e11/docs/evidence/v02/proposal-resource-soak-261dc23.md). The repaired `d684259` CI run passed all four Windows/Linux × Node 22/24 jobs. The categorical heldout producers and independent evaluator use separate explicit profile identities; a new freeze must pin their actual executable source and canonical inputs before any dispatch.