visionset 0.0.1b2__tar.gz

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 (444) hide show
  1. visionset-0.0.1b2/.agents/skills/backend/kernel-architecture/SKILL.md +95 -0
  2. visionset-0.0.1b2/.agents/skills/backend/python-reviewer/SKILL.md +110 -0
  3. visionset-0.0.1b2/.agents/skills/backend/python-setup/SKILL.md +87 -0
  4. visionset-0.0.1b2/.agents/skills/frontend/annotator-core/SKILL.md +121 -0
  5. visionset-0.0.1b2/.agents/skills/frontend/nodejs-setup/SKILL.md +66 -0
  6. visionset-0.0.1b2/.agents/skills/frontend/react-19/SKILL.md +93 -0
  7. visionset-0.0.1b2/.agents/skills/frontend/typescript/SKILL.md +121 -0
  8. visionset-0.0.1b2/.agents/skills/infra/docker-dev/SKILL.md +101 -0
  9. visionset-0.0.1b2/.dockerignore +46 -0
  10. visionset-0.0.1b2/.editorconfig +15 -0
  11. visionset-0.0.1b2/.github/dependabot.yml +90 -0
  12. visionset-0.0.1b2/.github/workflows/ci.yml +567 -0
  13. visionset-0.0.1b2/.github/workflows/dependabot-auto-merge.yml +33 -0
  14. visionset-0.0.1b2/.github/workflows/publish-pypi.yml +66 -0
  15. visionset-0.0.1b2/.gitignore +66 -0
  16. visionset-0.0.1b2/AGENTS.md +116 -0
  17. visionset-0.0.1b2/CHANGELOG.md +204 -0
  18. visionset-0.0.1b2/CONTRIBUTING.md +214 -0
  19. visionset-0.0.1b2/DESIGN.md +497 -0
  20. visionset-0.0.1b2/LICENSE +202 -0
  21. visionset-0.0.1b2/PKG-INFO +221 -0
  22. visionset-0.0.1b2/README.md +194 -0
  23. visionset-0.0.1b2/VERSION +1 -0
  24. visionset-0.0.1b2/docker/api-dev.sh +56 -0
  25. visionset-0.0.1b2/docker/api.Dockerfile +81 -0
  26. visionset-0.0.1b2/docker/app-dev.sh +30 -0
  27. visionset-0.0.1b2/docker/app.Dockerfile +37 -0
  28. visionset-0.0.1b2/docker/compose.yaml +230 -0
  29. visionset-0.0.1b2/docker/nginx.conf +103 -0
  30. visionset-0.0.1b2/docs/README.md +39 -0
  31. visionset-0.0.1b2/docs/annotations.md +911 -0
  32. visionset-0.0.1b2/docs/api.md +587 -0
  33. visionset-0.0.1b2/docs/auth.md +283 -0
  34. visionset-0.0.1b2/docs/batches.md +270 -0
  35. visionset-0.0.1b2/docs/cli.md +459 -0
  36. visionset-0.0.1b2/docs/datasets.md +206 -0
  37. visionset-0.0.1b2/docs/events.md +151 -0
  38. visionset-0.0.1b2/docs/examples.md +365 -0
  39. visionset-0.0.1b2/docs/ingest.md +377 -0
  40. visionset-0.0.1b2/docs/install.md +104 -0
  41. visionset-0.0.1b2/docs/jobs.md +210 -0
  42. visionset-0.0.1b2/docs/mcp-tools.md +60 -0
  43. visionset-0.0.1b2/docs/mcp-walkthrough.md +380 -0
  44. visionset-0.0.1b2/docs/mcp.md +328 -0
  45. visionset-0.0.1b2/docs/media.md +368 -0
  46. visionset-0.0.1b2/docs/persistence.md +249 -0
  47. visionset-0.0.1b2/docs/projects.md +121 -0
  48. visionset-0.0.1b2/docs/releases.md +603 -0
  49. visionset-0.0.1b2/docs/releasing.md +142 -0
  50. visionset-0.0.1b2/docs/schemas.md +316 -0
  51. visionset-0.0.1b2/docs/sources.md +160 -0
  52. visionset-0.0.1b2/docs/tutorial.md +253 -0
  53. visionset-0.0.1b2/docs/ui.md +713 -0
  54. visionset-0.0.1b2/docs/workspaces.md +380 -0
  55. visionset-0.0.1b2/examples/README.md +161 -0
  56. visionset-0.0.1b2/examples/cli_end_to_end.sh +151 -0
  57. visionset-0.0.1b2/examples/http_end_to_end.py +583 -0
  58. visionset-0.0.1b2/examples/ingest_end_to_end.py +402 -0
  59. visionset-0.0.1b2/examples/mcp_end_to_end.py +445 -0
  60. visionset-0.0.1b2/examples/sdk_end_to_end.py +476 -0
  61. visionset-0.0.1b2/examples/thirty_minute_flow.py +415 -0
  62. visionset-0.0.1b2/frontend/annotator/eslint.config.js +91 -0
  63. visionset-0.0.1b2/frontend/annotator/package.json +44 -0
  64. visionset-0.0.1b2/frontend/annotator/src/adapters/ids.ts +21 -0
  65. visionset-0.0.1b2/frontend/annotator/src/adapters/react/AnnotationLayer.tsx +68 -0
  66. visionset-0.0.1b2/frontend/annotator/src/adapters/react/AnnotatorCanvas.tsx +732 -0
  67. visionset-0.0.1b2/frontend/annotator/src/adapters/react/Shapes.tsx +340 -0
  68. visionset-0.0.1b2/frontend/annotator/src/adapters/react/TransientLayer.tsx +237 -0
  69. visionset-0.0.1b2/frontend/annotator/src/adapters/react/hooks.ts +57 -0
  70. visionset-0.0.1b2/frontend/annotator/src/adapters/react/index.ts +48 -0
  71. visionset-0.0.1b2/frontend/annotator/src/adapters/react/keyboard.test.ts +95 -0
  72. visionset-0.0.1b2/frontend/annotator/src/adapters/react/keyboard.ts +99 -0
  73. visionset-0.0.1b2/frontend/annotator/src/adapters/react/paint.test.ts +220 -0
  74. visionset-0.0.1b2/frontend/annotator/src/adapters/react/paint.ts +220 -0
  75. visionset-0.0.1b2/frontend/annotator/src/adapters/react/visibility.test.ts +80 -0
  76. visionset-0.0.1b2/frontend/annotator/src/adapters/react/visibility.ts +49 -0
  77. visionset-0.0.1b2/frontend/annotator/src/adapters/viewport.test.ts +200 -0
  78. visionset-0.0.1b2/frontend/annotator/src/adapters/viewport.ts +172 -0
  79. visionset-0.0.1b2/frontend/annotator/src/core/_fixture.ts +45 -0
  80. visionset-0.0.1b2/frontend/annotator/src/core/_random.ts +55 -0
  81. visionset-0.0.1b2/frontend/annotator/src/core/geometry/bbox.test.ts +340 -0
  82. visionset-0.0.1b2/frontend/annotator/src/core/geometry/bbox.ts +324 -0
  83. visionset-0.0.1b2/frontend/annotator/src/core/geometry/clamp.test.ts +18 -0
  84. visionset-0.0.1b2/frontend/annotator/src/core/geometry/clamp.ts +5 -0
  85. visionset-0.0.1b2/frontend/annotator/src/core/geometry/hitTest.test.ts +273 -0
  86. visionset-0.0.1b2/frontend/annotator/src/core/geometry/hitTest.ts +243 -0
  87. visionset-0.0.1b2/frontend/annotator/src/core/geometry/polygon.test.ts +280 -0
  88. visionset-0.0.1b2/frontend/annotator/src/core/geometry/polygon.ts +214 -0
  89. visionset-0.0.1b2/frontend/annotator/src/core/geometry/primitives.test.ts +69 -0
  90. visionset-0.0.1b2/frontend/annotator/src/core/geometry/primitives.ts +81 -0
  91. visionset-0.0.1b2/frontend/annotator/src/core/geometry/tolerance.test.ts +129 -0
  92. visionset-0.0.1b2/frontend/annotator/src/core/geometry/tolerance.ts +183 -0
  93. visionset-0.0.1b2/frontend/annotator/src/core/geometry/transforms.property.test.ts +372 -0
  94. visionset-0.0.1b2/frontend/annotator/src/core/ids.ts +26 -0
  95. visionset-0.0.1b2/frontend/annotator/src/core/input/_palette.ts +212 -0
  96. visionset-0.0.1b2/frontend/annotator/src/core/input/actions.ts +113 -0
  97. visionset-0.0.1b2/frontend/annotator/src/core/input/bindings.test.ts +322 -0
  98. visionset-0.0.1b2/frontend/annotator/src/core/input/bindings.ts +239 -0
  99. visionset-0.0.1b2/frontend/annotator/src/core/input/index.ts +106 -0
  100. visionset-0.0.1b2/frontend/annotator/src/core/input/keys.test.ts +121 -0
  101. visionset-0.0.1b2/frontend/annotator/src/core/input/keys.ts +183 -0
  102. visionset-0.0.1b2/frontend/annotator/src/core/input/pointer.test.ts +51 -0
  103. visionset-0.0.1b2/frontend/annotator/src/core/input/pointer.ts +78 -0
  104. visionset-0.0.1b2/frontend/annotator/src/core/input/runAction.test.ts +292 -0
  105. visionset-0.0.1b2/frontend/annotator/src/core/input/runAction.ts +211 -0
  106. visionset-0.0.1b2/frontend/annotator/src/core/interaction/_scene.ts +276 -0
  107. visionset-0.0.1b2/frontend/annotator/src/core/interaction/affordance.test.ts +441 -0
  108. visionset-0.0.1b2/frontend/annotator/src/core/interaction/affordance.ts +245 -0
  109. visionset-0.0.1b2/frontend/annotator/src/core/interaction/bboxTool.test.ts +359 -0
  110. visionset-0.0.1b2/frontend/annotator/src/core/interaction/draft.test.ts +148 -0
  111. visionset-0.0.1b2/frontend/annotator/src/core/interaction/draft.ts +99 -0
  112. visionset-0.0.1b2/frontend/annotator/src/core/interaction/effects.ts +82 -0
  113. visionset-0.0.1b2/frontend/annotator/src/core/interaction/events.ts +163 -0
  114. visionset-0.0.1b2/frontend/annotator/src/core/interaction/gestures.test.ts +468 -0
  115. visionset-0.0.1b2/frontend/annotator/src/core/interaction/machine.property.test.ts +208 -0
  116. visionset-0.0.1b2/frontend/annotator/src/core/interaction/machine.test.ts +546 -0
  117. visionset-0.0.1b2/frontend/annotator/src/core/interaction/machine.ts +735 -0
  118. visionset-0.0.1b2/frontend/annotator/src/core/interaction/polygonTool.test.ts +355 -0
  119. visionset-0.0.1b2/frontend/annotator/src/core/interaction/runEffects.test.ts +184 -0
  120. visionset-0.0.1b2/frontend/annotator/src/core/interaction/runEffects.ts +103 -0
  121. visionset-0.0.1b2/frontend/annotator/src/core/interaction/state.ts +184 -0
  122. visionset-0.0.1b2/frontend/annotator/src/core/interaction/tags.test.ts +521 -0
  123. visionset-0.0.1b2/frontend/annotator/src/core/interaction/tags.ts +255 -0
  124. visionset-0.0.1b2/frontend/annotator/src/core/interaction/target.test.ts +230 -0
  125. visionset-0.0.1b2/frontend/annotator/src/core/interaction/target.ts +187 -0
  126. visionset-0.0.1b2/frontend/annotator/src/core/interaction/tool.ts +99 -0
  127. visionset-0.0.1b2/frontend/annotator/src/core/state/_random.ts +114 -0
  128. visionset-0.0.1b2/frontend/annotator/src/core/state/_sample.ts +50 -0
  129. visionset-0.0.1b2/frontend/annotator/src/core/state/commandLog.test.ts +208 -0
  130. visionset-0.0.1b2/frontend/annotator/src/core/state/commandLog.ts +152 -0
  131. visionset-0.0.1b2/frontend/annotator/src/core/state/commands.ts +98 -0
  132. visionset-0.0.1b2/frontend/annotator/src/core/state/document.test.ts +264 -0
  133. visionset-0.0.1b2/frontend/annotator/src/core/state/document.ts +244 -0
  134. visionset-0.0.1b2/frontend/annotator/src/core/state/selection.test.ts +211 -0
  135. visionset-0.0.1b2/frontend/annotator/src/core/state/selection.ts +139 -0
  136. visionset-0.0.1b2/frontend/annotator/src/core/state/store.property.test.ts +124 -0
  137. visionset-0.0.1b2/frontend/annotator/src/core/state/store.test.ts +302 -0
  138. visionset-0.0.1b2/frontend/annotator/src/core/state/store.ts +266 -0
  139. visionset-0.0.1b2/frontend/annotator/src/core/types.ts +247 -0
  140. visionset-0.0.1b2/frontend/annotator/src/core/wire.test.ts +496 -0
  141. visionset-0.0.1b2/frontend/annotator/src/core/wire.ts +547 -0
  142. visionset-0.0.1b2/frontend/annotator/src/index.ts +249 -0
  143. visionset-0.0.1b2/frontend/annotator/tsconfig.build.json +8 -0
  144. visionset-0.0.1b2/frontend/annotator/tsconfig.core.json +39 -0
  145. visionset-0.0.1b2/frontend/annotator/tsconfig.json +29 -0
  146. visionset-0.0.1b2/frontend/app/bench/_gestures.ts +61 -0
  147. visionset-0.0.1b2/frontend/app/bench/_sampler.ts +192 -0
  148. visionset-0.0.1b2/frontend/app/bench/annotator.bench.ts +324 -0
  149. visionset-0.0.1b2/frontend/app/cycle/cycle.spec.ts +514 -0
  150. visionset-0.0.1b2/frontend/app/e2e/_bench.ts +136 -0
  151. visionset-0.0.1b2/frontend/app/e2e/_frame.ts +246 -0
  152. visionset-0.0.1b2/frontend/app/e2e/adapter-gaps.spec.ts +133 -0
  153. visionset-0.0.1b2/frontend/app/e2e/annotate.spec.ts +831 -0
  154. visionset-0.0.1b2/frontend/app/e2e/bbox.spec.ts +185 -0
  155. visionset-0.0.1b2/frontend/app/e2e/demo.spec.ts +150 -0
  156. visionset-0.0.1b2/frontend/app/e2e/keyboard.spec.ts +276 -0
  157. visionset-0.0.1b2/frontend/app/e2e/navigation.spec.ts +251 -0
  158. visionset-0.0.1b2/frontend/app/e2e/panel.spec.ts +151 -0
  159. visionset-0.0.1b2/frontend/app/e2e/perf.spec.ts +286 -0
  160. visionset-0.0.1b2/frontend/app/e2e/polygon.spec.ts +372 -0
  161. visionset-0.0.1b2/frontend/app/e2e/shell.spec.ts +312 -0
  162. visionset-0.0.1b2/frontend/app/e2e/showcase.spec.ts +184 -0
  163. visionset-0.0.1b2/frontend/app/e2e/styleguide.spec.ts +167 -0
  164. visionset-0.0.1b2/frontend/app/e2e/surround.spec.ts +263 -0
  165. visionset-0.0.1b2/frontend/app/e2e/tags.spec.ts +127 -0
  166. visionset-0.0.1b2/frontend/app/e2e/viewport.spec.ts +178 -0
  167. visionset-0.0.1b2/frontend/app/eslint.config.js +8 -0
  168. visionset-0.0.1b2/frontend/app/index.html +26 -0
  169. visionset-0.0.1b2/frontend/app/package.json +40 -0
  170. visionset-0.0.1b2/frontend/app/playwright.bench.config.ts +92 -0
  171. visionset-0.0.1b2/frontend/app/playwright.config.ts +94 -0
  172. visionset-0.0.1b2/frontend/app/playwright.cycle.config.ts +101 -0
  173. visionset-0.0.1b2/frontend/app/public/favicon.svg +24 -0
  174. visionset-0.0.1b2/frontend/app/src/demo/AnnotatorDemo.tsx +469 -0
  175. visionset-0.0.1b2/frontend/app/src/demo/BenchmarkHost.tsx +159 -0
  176. visionset-0.0.1b2/frontend/app/src/demo/ShowcaseFrame.tsx +72 -0
  177. visionset-0.0.1b2/frontend/app/src/demo/ToolStrip.tsx +262 -0
  178. visionset-0.0.1b2/frontend/app/src/demo/benchScene.ts +271 -0
  179. visionset-0.0.1b2/frontend/app/src/demo/sampleAsset.ts +72 -0
  180. visionset-0.0.1b2/frontend/app/src/demo/sampleSchema.ts +41 -0
  181. visionset-0.0.1b2/frontend/app/src/demo/theme.ts +103 -0
  182. visionset-0.0.1b2/frontend/app/src/main.tsx +60 -0
  183. visionset-0.0.1b2/frontend/app/src/routes.tsx +296 -0
  184. visionset-0.0.1b2/frontend/app/src/shell/AppShell.tsx +253 -0
  185. visionset-0.0.1b2/frontend/app/src/shell/Gated.tsx +20 -0
  186. visionset-0.0.1b2/frontend/app/src/shell/NotFound.tsx +31 -0
  187. visionset-0.0.1b2/frontend/app/src/styleguide/Styleguide.tsx +485 -0
  188. visionset-0.0.1b2/frontend/app/src/styles.css +15 -0
  189. visionset-0.0.1b2/frontend/app/tsconfig.e2e.json +26 -0
  190. visionset-0.0.1b2/frontend/app/tsconfig.json +24 -0
  191. visionset-0.0.1b2/frontend/app/vite.config.ts +41 -0
  192. visionset-0.0.1b2/openapi.json +8117 -0
  193. visionset-0.0.1b2/package.json +26 -0
  194. visionset-0.0.1b2/pnpm-lock.yaml +4317 -0
  195. visionset-0.0.1b2/pnpm-workspace.yaml +2 -0
  196. visionset-0.0.1b2/pyproject.toml +164 -0
  197. visionset-0.0.1b2/scripts/build_dist.sh +49 -0
  198. visionset-0.0.1b2/scripts/bundle_static.mjs +22 -0
  199. visionset-0.0.1b2/scripts/check.sh +127 -0
  200. visionset-0.0.1b2/scripts/cycle_server.sh +47 -0
  201. visionset-0.0.1b2/scripts/export_mcp_tools.py +129 -0
  202. visionset-0.0.1b2/scripts/export_openapi.py +27 -0
  203. visionset-0.0.1b2/scripts/export_wire_fixtures.py +177 -0
  204. visionset-0.0.1b2/scripts/generate_client.mjs +465 -0
  205. visionset-0.0.1b2/scripts/setup_agents.sh +66 -0
  206. visionset-0.0.1b2/scripts/sync_versions.mjs +84 -0
  207. visionset-0.0.1b2/src/visionset/__init__.py +10 -0
  208. visionset-0.0.1b2/src/visionset/_static/.gitkeep +0 -0
  209. visionset-0.0.1b2/src/visionset/_static/README.md +22 -0
  210. visionset-0.0.1b2/src/visionset/_static/assets/index-B0OgIhau.css +2 -0
  211. visionset-0.0.1b2/src/visionset/_static/assets/index-Cq3n2nuZ.js +55 -0
  212. visionset-0.0.1b2/src/visionset/_static/favicon.svg +24 -0
  213. visionset-0.0.1b2/src/visionset/_static/index.html +27 -0
  214. visionset-0.0.1b2/src/visionset/cli/__init__.py +1 -0
  215. visionset-0.0.1b2/src/visionset/cli/_errors.py +108 -0
  216. visionset-0.0.1b2/src/visionset/cli/_output.py +105 -0
  217. visionset-0.0.1b2/src/visionset/cli/_resolve.py +78 -0
  218. visionset-0.0.1b2/src/visionset/cli/_workspace.py +76 -0
  219. visionset-0.0.1b2/src/visionset/cli/batches.py +191 -0
  220. visionset-0.0.1b2/src/visionset/cli/export.py +220 -0
  221. visionset-0.0.1b2/src/visionset/cli/formats.py +44 -0
  222. visionset-0.0.1b2/src/visionset/cli/ingest.py +179 -0
  223. visionset-0.0.1b2/src/visionset/cli/init.py +62 -0
  224. visionset-0.0.1b2/src/visionset/cli/jobs.py +190 -0
  225. visionset-0.0.1b2/src/visionset/cli/main.py +76 -0
  226. visionset-0.0.1b2/src/visionset/cli/mcp.py +109 -0
  227. visionset-0.0.1b2/src/visionset/cli/projects.py +72 -0
  228. visionset-0.0.1b2/src/visionset/cli/releases.py +175 -0
  229. visionset-0.0.1b2/src/visionset/cli/schemas.py +145 -0
  230. visionset-0.0.1b2/src/visionset/cli/tokens.py +115 -0
  231. visionset-0.0.1b2/src/visionset/cli/ui.py +135 -0
  232. visionset-0.0.1b2/src/visionset/formats/__init__.py +7 -0
  233. visionset-0.0.1b2/src/visionset/formats/_dummy.py +51 -0
  234. visionset-0.0.1b2/src/visionset/formats/_layout.py +141 -0
  235. visionset-0.0.1b2/src/visionset/formats/coco/__init__.py +327 -0
  236. visionset-0.0.1b2/src/visionset/formats/lanes/README.md +9 -0
  237. visionset-0.0.1b2/src/visionset/formats/lanes/__init__.py +1 -0
  238. visionset-0.0.1b2/src/visionset/formats/registry.py +78 -0
  239. visionset-0.0.1b2/src/visionset/formats/voc/__init__.py +269 -0
  240. visionset-0.0.1b2/src/visionset/formats/yolo/__init__.py +270 -0
  241. visionset-0.0.1b2/src/visionset/kernel/__init__.py +127 -0
  242. visionset-0.0.1b2/src/visionset/kernel/adapters/__init__.py +17 -0
  243. visionset-0.0.1b2/src/visionset/kernel/adapters/_mappers.py +498 -0
  244. visionset-0.0.1b2/src/visionset/kernel/adapters/_tables.py +550 -0
  245. visionset-0.0.1b2/src/visionset/kernel/adapters/ffmpeg_video_processor.py +521 -0
  246. visionset-0.0.1b2/src/visionset/kernel/adapters/filesystem_blob_store.py +51 -0
  247. visionset-0.0.1b2/src/visionset/kernel/adapters/in_process_event_bus.py +75 -0
  248. visionset-0.0.1b2/src/visionset/kernel/adapters/migrations.py +88 -0
  249. visionset-0.0.1b2/src/visionset/kernel/adapters/pillow_image_processor.py +267 -0
  250. visionset-0.0.1b2/src/visionset/kernel/adapters/sqlite_metadata_store.py +329 -0
  251. visionset-0.0.1b2/src/visionset/kernel/adapters/stored_token_auth_provider.py +69 -0
  252. visionset-0.0.1b2/src/visionset/kernel/domain/__init__.py +214 -0
  253. visionset-0.0.1b2/src/visionset/kernel/domain/annotation.py +53 -0
  254. visionset-0.0.1b2/src/visionset/kernel/domain/asset.py +139 -0
  255. visionset-0.0.1b2/src/visionset/kernel/domain/batch.py +80 -0
  256. visionset-0.0.1b2/src/visionset/kernel/domain/dataset.py +145 -0
  257. visionset-0.0.1b2/src/visionset/kernel/domain/events.py +162 -0
  258. visionset-0.0.1b2/src/visionset/kernel/domain/geometry.py +95 -0
  259. visionset-0.0.1b2/src/visionset/kernel/domain/ingest.py +226 -0
  260. visionset-0.0.1b2/src/visionset/kernel/domain/media.py +153 -0
  261. visionset-0.0.1b2/src/visionset/kernel/domain/names.py +33 -0
  262. visionset-0.0.1b2/src/visionset/kernel/domain/partition.py +109 -0
  263. visionset-0.0.1b2/src/visionset/kernel/domain/project.py +97 -0
  264. visionset-0.0.1b2/src/visionset/kernel/domain/release.py +620 -0
  265. visionset-0.0.1b2/src/visionset/kernel/domain/schema.py +230 -0
  266. visionset-0.0.1b2/src/visionset/kernel/domain/schema_diff.py +218 -0
  267. visionset-0.0.1b2/src/visionset/kernel/domain/source.py +186 -0
  268. visionset-0.0.1b2/src/visionset/kernel/domain/task.py +174 -0
  269. visionset-0.0.1b2/src/visionset/kernel/domain/token.py +157 -0
  270. visionset-0.0.1b2/src/visionset/kernel/domain/transitions.py +41 -0
  271. visionset-0.0.1b2/src/visionset/kernel/domain/workspace.py +14 -0
  272. visionset-0.0.1b2/src/visionset/kernel/errors.py +735 -0
  273. visionset-0.0.1b2/src/visionset/kernel/ports/__init__.py +48 -0
  274. visionset-0.0.1b2/src/visionset/kernel/ports/auth_provider.py +31 -0
  275. visionset-0.0.1b2/src/visionset/kernel/ports/blob_store.py +12 -0
  276. visionset-0.0.1b2/src/visionset/kernel/ports/event_bus.py +34 -0
  277. visionset-0.0.1b2/src/visionset/kernel/ports/exporter.py +147 -0
  278. visionset-0.0.1b2/src/visionset/kernel/ports/image_processor.py +72 -0
  279. visionset-0.0.1b2/src/visionset/kernel/ports/importer.py +18 -0
  280. visionset-0.0.1b2/src/visionset/kernel/ports/metadata_store.py +178 -0
  281. visionset-0.0.1b2/src/visionset/kernel/ports/model_provider.py +14 -0
  282. visionset-0.0.1b2/src/visionset/kernel/ports/video_processor.py +106 -0
  283. visionset-0.0.1b2/src/visionset/kernel/services/__init__.py +45 -0
  284. visionset-0.0.1b2/src/visionset/kernel/services/annotation_service.py +501 -0
  285. visionset-0.0.1b2/src/visionset/kernel/services/batch_service.py +572 -0
  286. visionset-0.0.1b2/src/visionset/kernel/services/dataset_service.py +367 -0
  287. visionset-0.0.1b2/src/visionset/kernel/services/ingest_service.py +984 -0
  288. visionset-0.0.1b2/src/visionset/kernel/services/job_service.py +366 -0
  289. visionset-0.0.1b2/src/visionset/kernel/services/project_service.py +316 -0
  290. visionset-0.0.1b2/src/visionset/kernel/services/release_service.py +859 -0
  291. visionset-0.0.1b2/src/visionset/kernel/services/schema_service.py +378 -0
  292. visionset-0.0.1b2/src/visionset/kernel/services/source_service.py +264 -0
  293. visionset-0.0.1b2/src/visionset/kernel/services/token_service.py +223 -0
  294. visionset-0.0.1b2/src/visionset/kernel/services/workspace_service.py +578 -0
  295. visionset-0.0.1b2/src/visionset/mcp/__init__.py +1 -0
  296. visionset-0.0.1b2/src/visionset/mcp/_errors.py +179 -0
  297. visionset-0.0.1b2/src/visionset/mcp/_resolve.py +110 -0
  298. visionset-0.0.1b2/src/visionset/mcp/_workspace.py +55 -0
  299. visionset-0.0.1b2/src/visionset/mcp/annotations.py +213 -0
  300. visionset-0.0.1b2/src/visionset/mcp/assets.py +175 -0
  301. visionset-0.0.1b2/src/visionset/mcp/batches.py +238 -0
  302. visionset-0.0.1b2/src/visionset/mcp/datasets.py +45 -0
  303. visionset-0.0.1b2/src/visionset/mcp/formats.py +30 -0
  304. visionset-0.0.1b2/src/visionset/mcp/jobs.py +152 -0
  305. visionset-0.0.1b2/src/visionset/mcp/main.py +228 -0
  306. visionset-0.0.1b2/src/visionset/mcp/projects.py +111 -0
  307. visionset-0.0.1b2/src/visionset/mcp/releases.py +221 -0
  308. visionset-0.0.1b2/src/visionset/mcp/schemas.py +195 -0
  309. visionset-0.0.1b2/src/visionset/mcp/sources.py +163 -0
  310. visionset-0.0.1b2/src/visionset/py.typed +0 -0
  311. visionset-0.0.1b2/src/visionset/server/__init__.py +10 -0
  312. visionset-0.0.1b2/src/visionset/server/dependencies.py +296 -0
  313. visionset-0.0.1b2/src/visionset/server/errors.py +486 -0
  314. visionset-0.0.1b2/src/visionset/server/main.py +302 -0
  315. visionset-0.0.1b2/src/visionset/server/models.py +1340 -0
  316. visionset-0.0.1b2/src/visionset/server/routes/__init__.py +78 -0
  317. visionset-0.0.1b2/src/visionset/server/routes/annotations.py +149 -0
  318. visionset-0.0.1b2/src/visionset/server/routes/assets.py +206 -0
  319. visionset-0.0.1b2/src/visionset/server/routes/batches.py +223 -0
  320. visionset-0.0.1b2/src/visionset/server/routes/datasets.py +150 -0
  321. visionset-0.0.1b2/src/visionset/server/routes/formats.py +42 -0
  322. visionset-0.0.1b2/src/visionset/server/routes/ingest.py +70 -0
  323. visionset-0.0.1b2/src/visionset/server/routes/jobs.py +137 -0
  324. visionset-0.0.1b2/src/visionset/server/routes/projects.py +95 -0
  325. visionset-0.0.1b2/src/visionset/server/routes/releases.py +291 -0
  326. visionset-0.0.1b2/src/visionset/server/routes/schemas.py +147 -0
  327. visionset-0.0.1b2/src/visionset/server/routes/sources.py +180 -0
  328. visionset-0.0.1b2/src/visionset/server/runner.py +77 -0
  329. visionset-0.0.1b2/src/visionset/server/session.py +248 -0
  330. visionset-0.0.1b2/src/visionset/server/uploads.py +160 -0
  331. visionset-0.0.1b2/src/visionset/wire/__init__.py +506 -0
  332. visionset-0.0.1b2/tests/architecture/test_kernel_purity.py +26 -0
  333. visionset-0.0.1b2/tests/architecture/test_tracked_file_sizes.py +106 -0
  334. visionset-0.0.1b2/tests/cli/_flow.py +157 -0
  335. visionset-0.0.1b2/tests/cli/test_batch_commands.py +190 -0
  336. visionset-0.0.1b2/tests/cli/test_export_commands.py +588 -0
  337. visionset-0.0.1b2/tests/cli/test_full_cycle.py +130 -0
  338. visionset-0.0.1b2/tests/cli/test_ingest_commands.py +178 -0
  339. visionset-0.0.1b2/tests/cli/test_init.py +120 -0
  340. visionset-0.0.1b2/tests/cli/test_job_commands.py +179 -0
  341. visionset-0.0.1b2/tests/cli/test_json_contract.py +197 -0
  342. visionset-0.0.1b2/tests/cli/test_mcp_command.py +249 -0
  343. visionset-0.0.1b2/tests/cli/test_output.py +52 -0
  344. visionset-0.0.1b2/tests/cli/test_project_commands.py +130 -0
  345. visionset-0.0.1b2/tests/cli/test_release_commands.py +199 -0
  346. visionset-0.0.1b2/tests/cli/test_schema_commands.py +159 -0
  347. visionset-0.0.1b2/tests/cli/test_tokens.py +267 -0
  348. visionset-0.0.1b2/tests/cli/test_ui.py +249 -0
  349. visionset-0.0.1b2/tests/cli/test_workspace_option.py +151 -0
  350. visionset-0.0.1b2/tests/examples/test_cli_end_to_end.py +113 -0
  351. visionset-0.0.1b2/tests/examples/test_http_end_to_end.py +112 -0
  352. visionset-0.0.1b2/tests/examples/test_ingest_end_to_end.py +140 -0
  353. visionset-0.0.1b2/tests/examples/test_mcp_end_to_end.py +125 -0
  354. visionset-0.0.1b2/tests/examples/test_sdk_end_to_end.py +142 -0
  355. visionset-0.0.1b2/tests/examples/test_thirty_minute_flow.py +110 -0
  356. visionset-0.0.1b2/tests/fixtures/media.py +343 -0
  357. visionset-0.0.1b2/tests/fixtures/samples.py +289 -0
  358. visionset-0.0.1b2/tests/fixtures/test_media.py +222 -0
  359. visionset-0.0.1b2/tests/fixtures/wire_annotations.json +163 -0
  360. visionset-0.0.1b2/tests/formats/test_coco.py +363 -0
  361. visionset-0.0.1b2/tests/formats/test_coco_smoke.py +259 -0
  362. visionset-0.0.1b2/tests/formats/test_entry_points.py +20 -0
  363. visionset-0.0.1b2/tests/formats/test_registry.py +133 -0
  364. visionset-0.0.1b2/tests/formats/test_report_agreement.py +307 -0
  365. visionset-0.0.1b2/tests/formats/test_voc.py +378 -0
  366. visionset-0.0.1b2/tests/formats/test_yolo.py +493 -0
  367. visionset-0.0.1b2/tests/formats/test_yolo_smoke.py +220 -0
  368. visionset-0.0.1b2/tests/kernel/test_annotation.py +51 -0
  369. visionset-0.0.1b2/tests/kernel/test_annotation_service.py +871 -0
  370. visionset-0.0.1b2/tests/kernel/test_auth_provider.py +217 -0
  371. visionset-0.0.1b2/tests/kernel/test_batch_service.py +676 -0
  372. visionset-0.0.1b2/tests/kernel/test_blob_store.py +39 -0
  373. visionset-0.0.1b2/tests/kernel/test_concurrency.py +205 -0
  374. visionset-0.0.1b2/tests/kernel/test_dataset_service.py +710 -0
  375. visionset-0.0.1b2/tests/kernel/test_domain_invariants.py +52 -0
  376. visionset-0.0.1b2/tests/kernel/test_events.py +601 -0
  377. visionset-0.0.1b2/tests/kernel/test_geometry.py +140 -0
  378. visionset-0.0.1b2/tests/kernel/test_image_processor.py +564 -0
  379. visionset-0.0.1b2/tests/kernel/test_ingest_service.py +2305 -0
  380. visionset-0.0.1b2/tests/kernel/test_job_service.py +509 -0
  381. visionset-0.0.1b2/tests/kernel/test_metadata_store.py +641 -0
  382. visionset-0.0.1b2/tests/kernel/test_migrations.py +210 -0
  383. visionset-0.0.1b2/tests/kernel/test_partition.py +135 -0
  384. visionset-0.0.1b2/tests/kernel/test_project_service.py +742 -0
  385. visionset-0.0.1b2/tests/kernel/test_release.py +250 -0
  386. visionset-0.0.1b2/tests/kernel/test_release_service.py +1158 -0
  387. visionset-0.0.1b2/tests/kernel/test_schema_diff.py +165 -0
  388. visionset-0.0.1b2/tests/kernel/test_schema_service.py +648 -0
  389. visionset-0.0.1b2/tests/kernel/test_source_service.py +432 -0
  390. visionset-0.0.1b2/tests/kernel/test_token.py +161 -0
  391. visionset-0.0.1b2/tests/kernel/test_token_service.py +220 -0
  392. visionset-0.0.1b2/tests/kernel/test_video_processor.py +565 -0
  393. visionset-0.0.1b2/tests/kernel/test_workspace_service.py +655 -0
  394. visionset-0.0.1b2/tests/mcp/_flow.py +180 -0
  395. visionset-0.0.1b2/tests/mcp/test_agent_walk.py +217 -0
  396. visionset-0.0.1b2/tests/mcp/test_annotation_tools.py +219 -0
  397. visionset-0.0.1b2/tests/mcp/test_asset_tools.py +177 -0
  398. visionset-0.0.1b2/tests/mcp/test_batch_tools.py +271 -0
  399. visionset-0.0.1b2/tests/mcp/test_ingest_tools.py +192 -0
  400. visionset-0.0.1b2/tests/mcp/test_job_tools.py +196 -0
  401. visionset-0.0.1b2/tests/mcp/test_project_tools.py +139 -0
  402. visionset-0.0.1b2/tests/mcp/test_registration.py +247 -0
  403. visionset-0.0.1b2/tests/mcp/test_release_tools.py +422 -0
  404. visionset-0.0.1b2/tests/mcp/test_schema_tools.py +270 -0
  405. visionset-0.0.1b2/tests/mcp/test_tool_errors.py +185 -0
  406. visionset-0.0.1b2/tests/mcp/test_tool_reference.py +69 -0
  407. visionset-0.0.1b2/tests/packaging/test_wheel.py +381 -0
  408. visionset-0.0.1b2/tests/scripts/annotator_boundary.test.mjs +208 -0
  409. visionset-0.0.1b2/tests/scripts/checks_wiring.test.mjs +112 -0
  410. visionset-0.0.1b2/tests/scripts/design_tokens.test.mjs +130 -0
  411. visionset-0.0.1b2/tests/scripts/e2e_discipline.test.mjs +103 -0
  412. visionset-0.0.1b2/tests/scripts/favicon.test.mjs +81 -0
  413. visionset-0.0.1b2/tests/scripts/generate_client.test.mjs +146 -0
  414. visionset-0.0.1b2/tests/scripts/sync_versions.test.mjs +23 -0
  415. visionset-0.0.1b2/tests/server/_api.py +64 -0
  416. visionset-0.0.1b2/tests/server/_exports.py +142 -0
  417. visionset-0.0.1b2/tests/server/_flow.py +149 -0
  418. visionset-0.0.1b2/tests/server/_openapi.py +52 -0
  419. visionset-0.0.1b2/tests/server/_probe.py +99 -0
  420. visionset-0.0.1b2/tests/server/_runner.py +63 -0
  421. visionset-0.0.1b2/tests/server/test_annotations.py +454 -0
  422. visionset-0.0.1b2/tests/server/test_auth.py +198 -0
  423. visionset-0.0.1b2/tests/server/test_batches.py +548 -0
  424. visionset-0.0.1b2/tests/server/test_datasets.py +442 -0
  425. visionset-0.0.1b2/tests/server/test_downloads.py +396 -0
  426. visionset-0.0.1b2/tests/server/test_errors.py +402 -0
  427. visionset-0.0.1b2/tests/server/test_external_client.py +301 -0
  428. visionset-0.0.1b2/tests/server/test_formats.py +81 -0
  429. visionset-0.0.1b2/tests/server/test_health.py +33 -0
  430. visionset-0.0.1b2/tests/server/test_ingest.py +402 -0
  431. visionset-0.0.1b2/tests/server/test_jobs.py +301 -0
  432. visionset-0.0.1b2/tests/server/test_openapi_contract.py +192 -0
  433. visionset-0.0.1b2/tests/server/test_projects.py +415 -0
  434. visionset-0.0.1b2/tests/server/test_releases.py +569 -0
  435. visionset-0.0.1b2/tests/server/test_schemas.py +479 -0
  436. visionset-0.0.1b2/tests/server/test_session.py +307 -0
  437. visionset-0.0.1b2/tests/server/test_sources.py +339 -0
  438. visionset-0.0.1b2/tests/server/test_static_ui.py +292 -0
  439. visionset-0.0.1b2/tests/server/test_uploads.py +162 -0
  440. visionset-0.0.1b2/tests/server/test_wire_fixtures.py +155 -0
  441. visionset-0.0.1b2/tests/server/test_wire_models.py +313 -0
  442. visionset-0.0.1b2/tests/server/test_workspace_dependency.py +214 -0
  443. visionset-0.0.1b2/tests/test_versioning.py +43 -0
  444. visionset-0.0.1b2/uv.lock +2367 -0
@@ -0,0 +1,95 @@
1
+ ---
2
+ name: kernel-architecture
3
+ description: >
4
+ VisionSet hexagonal architecture: what belongs in kernel/ vs server/, cli/, mcp/, formats/,
5
+ the machine-enforced import boundaries, and how to add a port, an adapter, or a format plugin.
6
+ Trigger: When adding or moving Python modules, designing domain models or ports, writing a
7
+ FastAPI route / Typer command / MCP tool, or when an import contract fails.
8
+ license: Apache-2.0
9
+ metadata:
10
+ author: robomous
11
+ version: "1.0"
12
+ scope: [root, backend]
13
+ auto_invoke:
14
+ - "Adding or moving modules under src/visionset/"
15
+ - "Writing a FastAPI route, Typer command, or MCP tool"
16
+ - "Fixing a lint-imports / architecture test failure"
17
+ allowed-tools: Read, Edit, Write, Glob, Grep, Bash, WebFetch, WebSearch, Task
18
+ ---
19
+
20
+ ## The shape
21
+
22
+ ```
23
+ src/visionset/
24
+ kernel/ domain models + ports (Protocols) + default adapters — framework-free
25
+ server/ FastAPI. Thin: parse -> call SDK -> serialize
26
+ cli/ Typer. Thin: parse -> call SDK -> print
27
+ mcp/ MCP stdio server. Thin: tool -> SDK call
28
+ formats/ importer/exporter plugins, discovered via the `visionset.formats` entry point
29
+ _static/ compiled UI bundle, injected at build time
30
+ ```
31
+
32
+ Every surface (UI, CLI, MCP, REST) is a **thin client of the same SDK**. If a behavior exists
33
+ in only one of them, it is in the wrong place.
34
+
35
+ ## Two rules the machine enforces
36
+
37
+ 1. **Kernel purity** — `visionset.kernel` must never import `visionset.server`,
38
+ `visionset.cli`, `visionset.mcp`, `visionset.formats`, nor `fastapi` / `typer` / `mcp` /
39
+ `uvicorn`. Enforced by import-linter contracts in `pyproject.toml` **and** a fresh-process
40
+ test under `tests/architecture/`.
41
+ 2. **Delivery clients are siblings** — `server`, `cli`, and `mcp` never import each other.
42
+ Shared logic moves down into the kernel, never sideways.
43
+
44
+ ```bash
45
+ uv run lint-imports # both contracts
46
+ uv run pytest tests/architecture
47
+ ```
48
+
49
+ **If a change fights either boundary, the change is wrong — not the boundary.** Never relax a
50
+ contract in `pyproject.toml` to make a build pass; restructure instead.
51
+
52
+ ## Where does this code go?
53
+
54
+ | You are writing… | It goes in |
55
+ | --- | --- |
56
+ | A concept with invariants (Dataset, Annotation, Label) | `kernel/domain/` as a pydantic v2 model |
57
+ | A use case / orchestration ("import a dataset") | `kernel/` service — callable from every surface |
58
+ | An interface to the outside world (storage, repo, clock, ids) | `kernel/ports/` as a `Protocol` |
59
+ | A concrete implementation of a port (filesystem, sqlite, uuid4) | `kernel/adapters/` |
60
+ | HTTP shape: routes, request/response models, status codes | `server/` |
61
+ | A `visionset ...` subcommand, flags, human output | `cli/` |
62
+ | An MCP tool declaration and its mapping | `mcp/` |
63
+ | Reading/writing COCO, YOLO, … | `formats/` + an entry point |
64
+
65
+ Rule of thumb: **if it would still make sense with no HTTP, no terminal, and no LLM, it belongs
66
+ in the kernel.**
67
+
68
+ ## Adding a port
69
+
70
+ 1. Define the `Protocol` in `kernel/ports/` — narrow, domain-worded (`ImageStore`, not
71
+ `S3Client`), fully typed, no framework types in the signature.
72
+ 2. Ship a default adapter in `kernel/adapters/` so local-first works with zero configuration.
73
+ 3. Inject it — constructor parameter with a default, never a module-level singleton and never
74
+ an import from a delivery module.
75
+ 4. Test against the Protocol with a fake; the default adapter gets its own test.
76
+
77
+ ## Adding a format plugin
78
+
79
+ - Implement the importer/exporter under `formats/`.
80
+ - Register it in `pyproject.toml` under `[project.entry-points."visionset.formats"]`.
81
+ - Discovery goes through `importlib.metadata` — never a hardcoded registry, never an
82
+ `if fmt == "coco"` chain. Third-party distributions (`visionset-format-x`) register into the
83
+ same group; anything that breaks that breaks the plugin promise.
84
+ - A change to the plugin surface ships with a test proving discoverability via
85
+ `importlib.metadata`.
86
+
87
+ ## Delivery-layer discipline
88
+
89
+ - A route/command/tool body should read as: validate input → call one SDK function → shape the
90
+ output. Business logic in a route is a bug.
91
+ - `openapi.json` at the repo root is a **committed contract** consumed by the generated TS
92
+ client. After changing routes or response models:
93
+ `uv run python scripts/export_openapi.py` and commit the diff.
94
+ - Errors: raise domain errors from the kernel; translate them to HTTP status / exit codes at
95
+ the boundary. The kernel never raises `HTTPException`.
@@ -0,0 +1,110 @@
1
+ ---
2
+ name: python-reviewer
3
+ description: >
4
+ Reviews and refines VisionSet Python (kernel, FastAPI, Typer, MCP, pydantic v2) for clarity,
5
+ consistency, and architectural fit while preserving behavior exactly. Focuses on recently
6
+ modified code unless told otherwise. Use proactively after Python changes.
7
+ license: Apache-2.0
8
+ metadata:
9
+ author: robomous
10
+ version: "1.0"
11
+ scope: [root, backend]
12
+ auto_invoke: "Reviewing or refactoring Python code"
13
+ model: opus
14
+ allowed-tools: Read, Edit, Write, Glob, Grep, Bash, Task
15
+ ---
16
+
17
+ You are an expert Python reviewer for a library-first, hexagonal codebase. Your job is to make
18
+ recently modified code clearer, more consistent, and architecturally correct **without changing
19
+ what it does**. You prefer explicit, readable code over clever compression.
20
+
21
+ ## 1. Preserve functionality
22
+
23
+ Never change behavior — only how it is expressed. Same outputs, same errors, same side effects.
24
+ Non-negotiable. If you believe behavior is wrong, report it separately; do not silently "fix" it
25
+ inside a refactor.
26
+
27
+ ## 2. Enforce the architecture first
28
+
29
+ Architecture violations outrank style. Check, in order:
30
+
31
+ - Does anything in `visionset/kernel/` import `fastapi`, `typer`, `mcp`, `uvicorn`, or a
32
+ sibling delivery module? → must move.
33
+ - Do `server`, `cli`, or `mcp` import each other? → the shared part moves down into the kernel.
34
+ - Is there business logic inside a route handler, CLI command, or MCP tool? → it belongs in a
35
+ kernel service so every surface gets it.
36
+ - Is a concrete dependency (filesystem, DB, clock, uuid) used directly instead of through a
37
+ port `Protocol`? → introduce/use the port.
38
+ - Is a format handled by a hardcoded branch instead of the `visionset.formats` entry-point
39
+ group? → use discovery.
40
+
41
+ See the `kernel-architecture` skill for where each kind of code belongs.
42
+
43
+ ## 3. Apply project standards
44
+
45
+ - **ruff** owns formatting and import order (line length 100, rules `E, F, I, UP, B, SIM`).
46
+ Never introduce black/isort/flake8. Remove `# noqa` that hides a real fix.
47
+ - Kernel code is fully typed under strict mypy: no untyped defs, no bare `Any`, no
48
+ `disallow_any_generics` escapes. Annotate new public functions outside the kernel too.
49
+ - Naming: `snake_case` functions/variables, `PascalCase` classes, `UPPER_CASE` constants.
50
+ Name for the domain (`ImageStore`), not the technology (`S3Client`).
51
+ - Imports at the top of the file; a function-local import needs a one-line reason.
52
+ - f-strings over `%`/`format()`. `pathlib` over `os.path`. `enum.StrEnum` over string literals
53
+ scattered across modules.
54
+
55
+ ## 4. pydantic v2 and domain modelling
56
+
57
+ - Invariants live in the model (`Field` constraints, `@field_validator`, `@model_validator`),
58
+ not re-checked at every call site.
59
+ - Prefer `model_validate` / `model_dump` (v2 API); flag leftover v1 idioms (`parse_obj`,
60
+ `.dict()`, `class Config`, `@validator`).
61
+ - Domain models are immutable where it costs nothing (`model_config = ConfigDict(frozen=True)`)
62
+ and never carry transport concerns (HTTP status, CLI flags, MCP schemas).
63
+ - Value objects over primitive soup: an id type beats a bare `str` threaded through ten calls.
64
+
65
+ ## 5. Delivery layers
66
+
67
+ **FastAPI (`server/`)**
68
+ - Handlers stay thin: validate → one SDK call → shape response. Explicit `response_model` and
69
+ status codes.
70
+ - Dependencies via `Depends`, not module-level globals.
71
+ - Domain errors are translated to HTTP at the boundary; the kernel never raises
72
+ `HTTPException`.
73
+ - Any route or response-model change requires re-exporting `openapi.json`
74
+ (`uv run python scripts/export_openapi.py`) — it is a committed contract.
75
+
76
+ **Typer (`cli/`)**
77
+ - One command = one SDK call plus presentation. Exit codes are meaningful.
78
+ - Human-readable output by default; keep machine-readable output an explicit flag.
79
+
80
+ **MCP (`mcp/`)**
81
+ - A tool is a typed mapping onto an SDK function. Tool descriptions state what the tool does
82
+ and when to use it — they are the model's only documentation.
83
+
84
+ ## 6. Enhance clarity
85
+
86
+ - Early returns over deep nesting; delete dead code and premature abstractions.
87
+ - Remove comments that restate the code; keep comments that record *why* (a constraint, a
88
+ boundary, a decision).
89
+ - Consolidate related logic; extract a well-named function instead of a comment-delimited block.
90
+ - Narrow exception handling — no bare `except:`, no `except Exception` that swallows.
91
+ - Prefer the stdlib over a new dependency; a new runtime dependency ships to every wheel user.
92
+
93
+ ## 7. Tests
94
+
95
+ - New kernel behavior ships with tests under `tests/kernel/`; architecture rules under
96
+ `tests/architecture/`; plugin-surface changes prove discoverability via `importlib.metadata`.
97
+ - Test behavior through public entry points, not private internals.
98
+ - Ports are tested with fakes; each default adapter has its own test.
99
+ - Never commit fixture media — `**/workspace-data/` stays ignored.
100
+
101
+ ## Output format
102
+
103
+ Report as:
104
+
105
+ 1. **Architecture** — boundary violations (blocking).
106
+ 2. **Correctness risks** — behavior you suspect is wrong, stated as a question, not silently
107
+ changed.
108
+ 3. **Refinements applied** — file:line, one line each.
109
+ 4. **Checks run** — the actual commands and their result (`ruff`, `pytest`, `lint-imports`,
110
+ `mypy` when the kernel changed). Report failures verbatim; never claim green without running.
@@ -0,0 +1,87 @@
1
+ ---
2
+ name: python-setup
3
+ description: >
4
+ Python environment and conventions for VisionSet. uv workspace, ruff (lint + format),
5
+ mypy (strict on the kernel), import-linter, pytest.
6
+ Trigger: When running Python scripts, formatting/linting Python code, adding dependencies,
7
+ or setting up the backend environment.
8
+ license: Apache-2.0
9
+ metadata:
10
+ author: robomous
11
+ version: "1.0"
12
+ scope: [root, backend]
13
+ auto_invoke: "Running Python scripts or setting up the Python environment"
14
+ allowed-tools: Read, Edit, Write, Glob, Grep, Bash, WebFetch, WebSearch, Task
15
+ ---
16
+
17
+ ## Environment
18
+
19
+ - One Python distribution: `visionset`, living in `src/visionset/`. There is no `backend/`
20
+ folder and no `visionset_*` sibling packages.
21
+ - Requires **Python 3.12+**. Package manager: **uv** (never pip, never poetry).
22
+ - All commands run from the **repo root** — there is no per-component venv.
23
+
24
+ ```bash
25
+ uv sync # create/refresh .venv with the package (editable) + dev group
26
+ uv run python -c "..." # run anything inside the env
27
+ uv run pytest # tests
28
+ uv add <pkg> # runtime dependency -> [project].dependencies
29
+ uv add --dev <pkg> # dev dependency -> [dependency-groups].dev
30
+ ```
31
+
32
+ Never edit `uv.lock` by hand; never `pip install` into `.venv`.
33
+
34
+ ## Checks that must stay green
35
+
36
+ | Check | Command |
37
+ | --- | --- |
38
+ | Tests | `uv run pytest` |
39
+ | Import contracts (architecture) | `uv run lint-imports` |
40
+ | Kernel type-safety (strict) | `uv run mypy src/visionset/kernel` |
41
+ | Lint | `uv run ruff check .` |
42
+ | Format | `uv run ruff format .` |
43
+ | OpenAPI contract | `uv run python scripts/export_openapi.py` (commit the diff) |
44
+
45
+ Run at minimum `ruff check`, `ruff format`, `pytest`, and `lint-imports` after any Python
46
+ change. If you touched the kernel, add `mypy`. If you touched FastAPI routes or response
47
+ models, re-export `openapi.json` — it is a committed contract, a stale one is a bug.
48
+
49
+ ## Formatting and lint
50
+
51
+ **ruff is both the formatter and the linter** — do not introduce black, isort, flake8, or
52
+ autopep8. Line length is 100. Rule set: `E, F, I, UP, B, SIM` (configured in `pyproject.toml`;
53
+ change it there, never with scattered `# noqa`).
54
+
55
+ ```bash
56
+ uv run ruff format <path> # format
57
+ uv run ruff check --fix <path>
58
+ ```
59
+
60
+ ## Imports (REQUIRED)
61
+
62
+ - Imports go at the top of the file. `ruff`'s `I` rules own the ordering — let the tool sort;
63
+ do not hand-order.
64
+ - Import inside a function **only** to break a genuine cycle or to keep an optional dependency
65
+ optional, and say why in a one-line comment.
66
+ - Nothing in `visionset.kernel` may import a framework or a delivery module — see the
67
+ `kernel-architecture` skill. That rule beats convenience, always.
68
+
69
+ ## Typing
70
+
71
+ - The kernel is under strict mypy (`disallow_untyped_defs`, `disallow_any_generics`,
72
+ `warn_return_any`, …). Every kernel function is fully annotated; no bare `Any`.
73
+ - Outside the kernel, follow the local style — annotate new public functions.
74
+ - Domain invariants belong in the pydantic v2 models, not in ad-hoc `assert`s at call sites.
75
+
76
+ ## Versioning
77
+
78
+ The repo-root `VERSION` file is the single source of truth. `pyproject.toml` reads it via
79
+ hatchling's regex version source; `pnpm version:sync` propagates it to the npm packages.
80
+ Never hand-edit a version anywhere else.
81
+
82
+ ## Dependency rules
83
+
84
+ - A new runtime dependency in `[project].dependencies` ships to every user of the wheel —
85
+ justify it. The kernel must stay framework-free; tooling-only deps go to the dev group.
86
+ - Importer/exporter plugins register under the `visionset.formats` entry-point group instead
87
+ of being imported directly.
@@ -0,0 +1,121 @@
1
+ ---
2
+ name: annotator-core
3
+ description: >
4
+ The headless boundary of @visionset/annotator: pure-TS interaction state machine, geometry and
5
+ input in src/core/, React (and any other renderer) confined to src/adapters/.
6
+ Trigger: When adding or changing annotation behavior, canvas interaction, geometry, undo/redo,
7
+ or an annotator render adapter — and when the "no React in core" ESLint rule fires.
8
+ license: Apache-2.0
9
+ metadata:
10
+ author: robomous
11
+ version: "1.0"
12
+ scope: [frontend]
13
+ auto_invoke:
14
+ - "Changing annotation/canvas interaction behavior"
15
+ - "Adding or modifying an annotator render adapter"
16
+ allowed-tools: Read, Edit, Write, Glob, Grep, Bash, WebFetch, WebSearch, Task
17
+ ---
18
+
19
+ ## The shape
20
+
21
+ ```
22
+ frontend/annotator/src/
23
+ core/ pure TypeScript — no React, no DOM globals
24
+ types.ts the wire contract, mirrored exactly (annotations, schema, asset)
25
+ wire.ts unknown in, typed out — plus the outbound create/update projections
26
+ ids.ts the IdFactory port; core cannot mint a uuid, `crypto` is a host global
27
+ _fixture.ts test harness (kernel-written fixture loader) — excluded from the build
28
+ geometry/ math: clamp, hit-testing, transforms
29
+ input/ normalized pointer/keyboard events -> intents
30
+ state/ document.ts + selection.ts + commandLog.ts (undo/redo)
31
+ adapters/
32
+ ids.ts randomUuid — a host capability, not a renderer
33
+ react/ AnnotatorCanvas and friends — the only place React exists
34
+ index.ts public entry point
35
+ ```
36
+
37
+ **The document** (`state/document.ts`) is `AnnotationDocument`: an asset, a schema, and
38
+ `ReadonlyMap<string, Annotation>`. Immutable — every operation returns a new one. Never named
39
+ `Document` (a host global). Annotations are addressed by UUID, never by index.
40
+
41
+ **Selection** (`state/selection.ts`) is a `ReadonlySet<string>` held *beside* the document and
42
+ **filtered on read**, never pruned on write. That is what makes it survive undo/redo with no
43
+ coordination: undoing a delete makes the annotation resolve again, still selected. Do not move
44
+ selection into the document, and do not add an eager prune.
45
+
46
+ The document refuses only its own invariants — duplicate id, unknown id, foreign asset. Kernel
47
+ rules (class↔geometry agreement, required attributes, bounds) stay the kernel's; the tools refuse at
48
+ draw time, where a user can be told.
49
+
50
+ ## The rules the machine enforces
51
+
52
+ `src/core/**` must not import React and must not reach the DOM. Three gates, the frontend mirror
53
+ of the backend's import-linter kernel contracts, all run by
54
+ `pnpm --filter @visionset/annotator lint`:
55
+
56
+ | Gate | Where | Scope | Catches |
57
+ | --- | --- | --- | --- |
58
+ | `no-restricted-imports` | `eslint.config.js` | `src/core/**` | `react`, `react-dom`, `react-*` |
59
+ | `no-restricted-globals` | `eslint.config.js` | `src/core/**`, **tests included** | `document`, `window`, `navigator`, `requestAnimationFrame`, … as **values** |
60
+ | `tsconfig.core.json` | its own file | `src/core/**` **minus** `*.test.ts` | DOM **types in signatures** (`KeyboardEvent`, `SVGSVGElement`), plus `setTimeout` / `console` / `fetch` in shipping code |
61
+
62
+ Two ESLint rules and a compiler pass because each reaches where the others cannot. A lint rule
63
+ reports *value* references, so `getSvgSize(svg: SVGSVGElement)` passes it — that is what the
64
+ DOM-free `lib` is for. Conversely the type gate excludes the vitest harness, which legitimately
65
+ runs under Node and reads a kernel-written fixture through `node:fs`; admitting it would take
66
+ `types: ["node"]`, and @types/node re-declares `setTimeout`, `console` and `fetch`, so buying the
67
+ tests would cost the timer ban on the engine. The ESLint half covers those files instead.
68
+
69
+ `tests/scripts/annotator_boundary.test.mjs` proves all three fire, and pins the two traps: `jsx:
70
+ "preserve"` in `tsconfig.core.json` is load-bearing (`jsx: "react-jsx"` imports
71
+ `react/jsx-runtime`'s types into every file, dragging in @types/react's empty `interface
72
+ KeyboardEvent {}` DOM stand-ins), and a type-gate probe must live inside the package, because from
73
+ `os.tmpdir()` nothing resolves `node_modules` and the probe fails for the wrong reason.
74
+
75
+ **If a change fights the boundary, the change is wrong — not the boundary.** Do not add an
76
+ eslint-disable, do not widen a rule's file scope, do not put `DOM` back in the `lib`.
77
+
78
+ When a gate fires, the fix is one of:
79
+
80
+ - The logic is renderer-agnostic → keep it in `core/`, drop the React import or the DOM reference
81
+ (take values as arguments, return plain data).
82
+ - The logic is genuinely about rendering → move it to `adapters/react/`.
83
+ - The signature wanted a DOM type for its *shape* → define the shape in `core/` as a plain
84
+ interface. That is what the input layer's normalized events are for; a DOM type in a core
85
+ signature is the leak this gate exists to stop.
86
+
87
+ ## Writing core logic
88
+
89
+ - Pure functions and explicit state transitions. No hidden globals, no `window`/`document`
90
+ access, no timers owned by core — the adapter supplies those.
91
+ - Input arrives as normalized events; core returns intents/state, never DOM mutations.
92
+ - Every mutating action goes through the command log so undo/redo stays a core capability, not
93
+ an adapter feature.
94
+ - Deterministic: same input sequence → same state. That is what makes it testable and what will
95
+ let 3D/point-cloud modes reuse it later.
96
+
97
+ ## Writing an adapter
98
+
99
+ An adapter only: subscribes to core state, paints it, and forwards normalized input back. It
100
+ holds no annotation logic of its own. React is the first adapter, not the only intended one —
101
+ anything it "knows" that core doesn't will not survive the second renderer.
102
+
103
+ ## Tests
104
+
105
+ Core is unit-tested with **vitest** and needs no DOM:
106
+
107
+ ```bash
108
+ pnpm --filter @visionset/annotator test
109
+ pnpm --filter @visionset/annotator lint # all three headless boundary gates
110
+ ```
111
+
112
+ The wire mirror is proved against `tests/fixtures/wire_annotations.json`, which
113
+ `scripts/export_wire_fixtures.py` writes from the same pydantic models `openapi.json` comes from —
114
+ it carries the asset, the schema and the annotations a document is built from. Never hand-write a
115
+ TypeScript copy of a kernel shape: extend the exporter and regenerate
116
+ (`uv run python scripts/export_wire_fixtures.py`), which both gates then hold in step.
117
+
118
+ `lint` is `eslint src` plus two compiler passes — `typecheck` (the whole package, tests included)
119
+ and `typecheck:core` (the boundary). Either is runnable on its own by name.
120
+
121
+ New interaction behavior ships with a core test that drives the state machine directly.
@@ -0,0 +1,66 @@
1
+ ---
2
+ name: nodejs-setup
3
+ description: >
4
+ Frontend environment for the VisionSet pnpm workspace. Node.js 24, corepack/pnpm, per-package
5
+ and recursive scripts.
6
+ Trigger: When setting up the frontend environment, installing packages, adding a workspace
7
+ package, or running frontend build/test/lint scripts.
8
+ license: Apache-2.0
9
+ metadata:
10
+ author: robomous
11
+ version: "1.0"
12
+ scope: [frontend]
13
+ auto_invoke: "Setting up frontend environment or installing packages"
14
+ allowed-tools: Read, Edit, Write, Glob, Grep, Bash, WebFetch, WebSearch, Task
15
+ ---
16
+
17
+ ## Environment
18
+
19
+ - **Node.js 24**. With nvm on the host: `nvm use 24`.
20
+ - **pnpm** only (never npm, never yarn). Pinned via `packageManager` in the root
21
+ `package.json` — enable it with `corepack enable`.
22
+ - Single **pnpm workspace** rooted at the repo root; members are `frontend/*`
23
+ (`@visionset/annotator`, `@visionset/ui-core`, `@visionset/app`).
24
+ - Run commands **from the repo root**.
25
+
26
+ ## Commands
27
+
28
+ ```bash
29
+ pnpm install # install the whole workspace
30
+
31
+ pnpm -r build # build every package (topological order)
32
+ pnpm -r test # vitest where defined
33
+ pnpm -r lint # eslint
34
+
35
+ pnpm --filter @visionset/app dev # Vite dev server
36
+ pnpm --filter @visionset/annotator test # one package
37
+ pnpm --filter @visionset/ui-core build
38
+
39
+ pnpm version:sync # propagate repo-root VERSION into frontend/*/package.json
40
+ pnpm bundle:static # place the built app into src/visionset/_static/
41
+ ```
42
+
43
+ ## Dependencies
44
+
45
+ ```bash
46
+ pnpm --filter @visionset/app add <pkg> # runtime dep of one package
47
+ pnpm --filter @visionset/ui-core add -D <pkg> # dev dep of one package
48
+ pnpm add -w -D <pkg> # root tooling only
49
+ ```
50
+
51
+ - Cross-package references use the workspace protocol: `"@visionset/annotator": "workspace:*"`.
52
+ - `@visionset/annotator` keeps `react` as an **optional peer dependency** — never promote it to
53
+ a hard dependency; the core must stay usable without React.
54
+ - `@visionset/ui-core` UI primitives are **Radix + lucide only**. Do not add another component
55
+ library.
56
+ - `@visionset/app` is `private: true` — never published to npm; its bundle ships inside the
57
+ Python wheel.
58
+
59
+ ## Versions
60
+
61
+ Never hand-edit a `version` field. The repo-root `VERSION` file is the source of truth;
62
+ `pnpm version:sync` converts it to npm semver (`0.1.0.dev0` → `0.1.0-dev.0`).
63
+
64
+ ## Before you say it works
65
+
66
+ `pnpm -r build && pnpm -r test && pnpm -r lint` from the root. Report failures verbatim.
@@ -0,0 +1,93 @@
1
+ ---
2
+ name: react-19
3
+ description: >
4
+ React 19 patterns with React Compiler.
5
+ Trigger: When writing React 19 components/hooks in .tsx (React Compiler rules, hook patterns,
6
+ refs as props).
7
+ license: Apache-2.0
8
+ metadata:
9
+ author: robomous
10
+ version: "1.0"
11
+ scope: [root, frontend]
12
+ auto_invoke: "Writing React components"
13
+ allowed-tools: Read, Edit, Write, Glob, Grep, Bash, WebFetch, WebSearch, Task
14
+ ---
15
+
16
+ ## Where React is allowed (VisionSet)
17
+
18
+ | Package | React? |
19
+ | --- | --- |
20
+ | `frontend/annotator/src/core/` | **Never** — pure TS, ESLint-enforced |
21
+ | `frontend/annotator/src/adapters/react/` | Yes — the render adapter |
22
+ | `frontend/ui-core/` | Yes — domain components (Radix + lucide only) |
23
+ | `frontend/app/` | Yes — the product shell |
24
+
25
+ ## No Manual Memoization (REQUIRED)
26
+
27
+ ```typescript
28
+ // ✅ React Compiler handles optimization automatically
29
+ function Component({ items }) {
30
+ const filtered = items.filter(x => x.active);
31
+ const sorted = filtered.sort((a, b) => a.name.localeCompare(b.name));
32
+
33
+ const handleClick = (id) => {
34
+ console.log(id);
35
+ };
36
+
37
+ return <List items={sorted} onClick={handleClick} />;
38
+ }
39
+
40
+ // ❌ NEVER: Manual memoization
41
+ const filtered = useMemo(() => items.filter((x) => x.active), [items]);
42
+ const handleClick = useCallback((id) => console.log(id), []);
43
+ ```
44
+
45
+ ## Imports (REQUIRED)
46
+
47
+ ```typescript
48
+ // ✅ ALWAYS: Named imports
49
+ import { useState, useEffect, useRef } from "react";
50
+
51
+ // ❌ NEVER
52
+ import React from "react";
53
+ import * as React from "react";
54
+ ```
55
+
56
+ ## use() Hook
57
+
58
+ ```typescript
59
+ import { use } from "react";
60
+
61
+ // Read promises (suspends until resolved)
62
+ function Comments({ promise }) {
63
+ const comments = use(promise);
64
+ return comments.map(c => <div key={c.id}>{c.text}</div>);
65
+ }
66
+
67
+ // Conditional context (not possible with useContext!)
68
+ function Theme({ showTheme }) {
69
+ if (showTheme) {
70
+ const theme = use(ThemeContext);
71
+ return <div style={{ color: theme.primary }}>Themed</div>;
72
+ }
73
+ return <div>Plain</div>;
74
+ }
75
+ ```
76
+
77
+ ## ref as Prop (No forwardRef)
78
+
79
+ ```typescript
80
+ // ✅ React 19: ref is just a prop
81
+ function Input({ ref, ...props }) {
82
+ return <input ref={ref} {...props} />;
83
+ }
84
+
85
+ // ❌ Old way (unnecessary now)
86
+ const Input = forwardRef((props, ref) => <input ref={ref} {...props} />);
87
+ ```
88
+
89
+ ## Keep components thin
90
+
91
+ Annotation behavior (hit-testing, geometry, undo/redo, interaction state) belongs in
92
+ `@visionset/annotator` core, not in a component. A React component subscribes to core state and
93
+ renders it. If deleting React would delete the logic, the logic is in the wrong package.