You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Recorded by Armando in the 2026-08-07 design session on the AI-assist substrate issue (#81),
which covered ModelProvider pre-annotation, the review loop in the UI, and importers:
ModelProvider is designed and declared now as a kernel port with no implementations,
shaped so that a local runner and a cloud service are both ordinary adapters later: MCP stays
a native first-class path; autolabeling will run locally and in the cloud eventually; nothing
ships yet, and nothing may be designed to preclude the cloud half. Local-first,
cloud-compatible after.
Nothing in this issue ships an implementation. The deliverable is a port whose shape
survives both futures.
Correction to the premise: the port already exists, as a placeholder
src/visionset/kernel/ports/model_provider.py is at HEAD (904b37a) and is exported from kernel/ports/__init__.py:24. It is not a designed port and its own docstring says so — "Phase 3 — declared now to fix the port surface". Its entire surface is:
That shape fails the dual test in three ways, which is precisely the work:
Per-asset, so a provider that batches — which every hosted inference service does — pays
one round trip per image.
Synchronous, returning a materialised Sequence, so a remote provider that takes minutes
has nowhere to be except blocking its caller.
Discovered by nothing.Importer and Exporter name the visionset.formats entry-point
group; this port names no group at all, so there is no way to install a provider even in
principle.
So this issue re-shapes a declared placeholder. It does not add a new file to empty space,
and a PR that only creates one has not done the work.
The pattern to follow
kernel/ports/importer.py — a @runtime_checkable Protocol, discovered through an entry-point
group, zero implementations, documented as awaiting its producers. formats/registry.py
already filters one group by port (isinstance on an instance, because issubclass against a
protocol with data members raises), so reusing that group or declaring a new one both have
precedent — but the choice has to be made and argued rather than inherited.
Design questions the implementer answers in the PR, not in advance here
Every answer must hold for a hypothetical local ONNX runner and a hypothetical HTTP service
equally. That dual test is the acceptance criterion.
The invocation unit — asset, or batch? A per-asset call is simplest locally and worst
remotely, which is exactly the tension the port has to resolve rather than pick a side of by
accident.
The result shape — annotations carrying provenance='model', confidence, and model
identity/version. The domain already requires model_ref whenever provenance is model
(domain/annotation.py:69-71); the open question is whether that one string is the whole of
model identity or whether the port owes more. These fields align with the work that surfaces
provenance and confidence in the review loop (Model-produced annotations: provenance and confidence surfaced in the review loop #417), which is the rendering half of the same
split.
How a slow or asynchronous provider reports progress without the port assuming a
transport. The JobQueue port in kernel/ports/job_queue.py, which came out of the embedded
job system with its SQLite adapter and in-process dispatcher (Embedded job system: a JobQueue port, a SQLite adapter, and an in-process spawn dispatcher #328), already exists and is
explicitly not an executor — its separation of "where rows live" from "how the product asks
for work" is the shape to lean on, not to duplicate.
Out of scope
Scope updated 2026-08-08: the port and the local adapter shipped as slice 2 (#434),
including cross-box NMS, the fp16 shims, and the weight-download job. This issue remains open
for the hosted adapter and the HTTP endpoint contract. The original scope text is preserved in
this issue's edit history.
The decision this encodes
Recorded by Armando in the 2026-08-07 design session on the AI-assist substrate issue (#81),
which covered
ModelProviderpre-annotation, the review loop in the UI, and importers:Nothing in this issue ships an implementation. The deliverable is a port whose shape
survives both futures.
Correction to the premise: the port already exists, as a placeholder
src/visionset/kernel/ports/model_provider.pyis at HEAD (904b37a) and is exported fromkernel/ports/__init__.py:24. It is not a designed port and its own docstring says so —"Phase 3 — declared now to fix the port surface". Its entire surface is:
That shape fails the dual test in three ways, which is precisely the work:
one round trip per image.
Sequence, so a remote provider that takes minuteshas nowhere to be except blocking its caller.
ImporterandExportername thevisionset.formatsentry-pointgroup; this port names no group at all, so there is no way to install a provider even in
principle.
So this issue re-shapes a declared placeholder. It does not add a new file to empty space,
and a PR that only creates one has not done the work.
The pattern to follow
kernel/ports/importer.py— a@runtime_checkableProtocol, discovered through an entry-pointgroup, zero implementations, documented as awaiting its producers.
formats/registry.pyalready filters one group by port (
isinstanceon an instance, becauseissubclassagainst aprotocol with data members raises), so reusing that group or declaring a new one both have
precedent — but the choice has to be made and argued rather than inherited.
Design questions the implementer answers in the PR, not in advance here
Every answer must hold for a hypothetical local ONNX runner and a hypothetical HTTP service
equally. That dual test is the acceptance criterion.
remotely, which is exactly the tension the port has to resolve rather than pick a side of by
accident.
provenance='model',confidence, and modelidentity/version. The domain already requires
model_refwhenever provenance ismodel(
domain/annotation.py:69-71); the open question is whether that one string is the whole ofmodel identity or whether the port owes more. These fields align with the work that surfaces
provenance and confidence in the review loop (Model-produced annotations: provenance and confidence surfaced in the review loop #417), which is the rendering half of the same
split.
transport. The
JobQueueport inkernel/ports/job_queue.py, which came out of the embeddedjob system with its SQLite adapter and in-process dispatcher (Embedded job system: a JobQueue port, a SQLite adapter, and an in-process spawn dispatcher #328), already exists and is
explicitly not an executor — its separation of "where rows live" from "how the product asks
for work" is the shape to lean on, not to duplicate.
Out of scope
Scope updated 2026-08-08: the port and the local adapter shipped as slice 2 (#434),
including cross-box NMS, the fp16 shims, and the weight-download job. This issue remains open
for the hosted adapter and the HTTP endpoint contract. The original scope text is preserved in
this issue's edit history.
recorded on exercise the AI-assist substrate — ModelProvider pre-annotation, review loop in the UI, importers #81 and preserved here so it does not die with that thread: predictions are
written into
review_pending, never silentlyannotated, and schema validation isnot relaxed for them.
it rather than build it — that is a separate decision.
Acceptance criteria
ModelProvideris a designed port rather than a placeholder: every element of its shapejustified against both a local runner and a hosted service
docstring
importer.py's andjob_queue.py's register