Compare commits

16 Commits

Author SHA1 Message Date
Svrnty 5cc5d225c7 Record local VLM provider proof 2026-06-19 16:18:45 -04:00
Svrnty 8502bd9f17 Record BTE provider gateway refresh proof 2026-06-19 16:10:15 -04:00
Svrnty b262e9fe80 Normalize Svrnty Vision pickup posture 2026-06-19 08:31:07 -04:00
Svrnty 7c188dd248 Reject Svrnty Vision generated cache residue 2026-06-18 14:19:26 -04:00
Svrnty 480f6dcd34 Normalize AGENTS pickup field 2026-06-18 05:15:50 -04:00
Svrnty 59a51787ed Add Svrnty Vision legacy ingest overview 2026-06-18 03:15:36 -04:00
Svrnty 74ff840d3d Compact Svrnty Vision agent pickup 2026-06-18 01:30:10 -04:00
Svrnty 2611f79e67 Add Svrnty Vision navigation index 2026-06-17 23:38:20 -04:00
Svrnty 10fdb18204 Normalize AGENTS authority pickup 2026-06-17 22:15:24 -04:00
Svrnty 903de12f2c Add Svrnty Vision agent contract blocks 2026-06-17 19:22:27 -04:00
Svrnty b4f97bc2c6 docs: add svrnty vision pickup block 2026-06-16 17:04:01 -04:00
Svrnty 846f9662e2 Align vision board with registry rules 2026-06-16 13:36:17 -04:00
Svrnty c3f6793ce1 docs: reconcile vision workboard readiness 2026-06-12 08:50:44 -04:00
Svrnty 2da8a6d390 CC: add Vision package Docker context 2026-06-06 12:30:02 -04:00
Svrnty d62c5eb744 CC: prepare generic VISION package candidate 2026-06-06 08:25:14 -04:00
Svrnty 8045f46b06 chore: add Cortex child governance 2026-06-01 09:54:26 -04:00
37 changed files with 2230 additions and 7 deletions
+12
View File
@@ -0,0 +1,12 @@
.git
.pytest_cache
.mypy_cache
.ruff_cache
.venv
__pycache__
*.pyc
outputs
worktrees
tests
docs
candidate-manifests
+50
View File
@@ -0,0 +1,50 @@
# Svrnty Vision Endgoal
Endgoal: keep `svrnty-vision` as the child-local sovereign vision HTTP gateway and Visual Perception Package Candidate for VLM analysis, FLUX rendering, palette extraction, and cutout work without Core authority, Seed installation authority, Runtime startup authority, Profile Exposure authority, provider admission, wildcard tool access, or product-readiness claims.
Route: `svrnty-vision`.
Stage: CLEAN.
Clean score: 100.
Validator: `python3 tools/validate_svrnty_vision_child.py`.
Current Pickup: use this workspace for vision gateway source, BTE-shaped route adapters, package-candidate docs, visual-evidence contracts, host-adapter candidates, manifests, Docker package context, validators, fixtures, and proof packets; do not start runtimes, admit providers, broaden exposure, or claim readiness.
Authority Boundary: child-local vision gateway and package-candidate workspace only; not Cortex OS Core authority, Runtime authority, Host Runtime authority, Profile Exposure authority, provider authority, Docker/runtime lifecycle authority, release authority, production authority, or readiness authority.
Legacy-work relation: older BTE-embedded vision, cloud-provider SDK, VLM, FLUX, palette, rembg, Visual Evidence, and package-candidate work is tracked in `docs/LEGACY-INGEST.md` and distilled into this standalone gateway only when current docs and validators admit it. Do not import provider credentials, backend state, runtime behavior, or mass because it exists.
## Universal Cortex OS Agent Contract
- Follow parent `AGENTS.md`; this file is route-local instruction before chat memory.
- For broad work, run `cortex graph context` as Derived State, then read local files.
- Before edits, read `AGENTS.md`, `README.md`, and `WORKBOARD.yaml`; keep writes route-local unless Core authorizes promotion.
- Use Karpathy rules, small gateway seams, real evals, and cartesian/pragmatic/efficient/elegant no-live execution.
- Run the validator before handoff or done claims; raise proof only for runtime, provider, secret, memory, profile exposure, release, or readiness.
- Keep proof and handoffs refs-only. Do not write Hindsight memory, Core SOT, sibling repos, runtime state, provider state, external systems, or organization payloads without route approval.
## Repo-Custom Agent Contract
Svrnty Vision is a child-local sovereign vision HTTP gateway and Visual Perception Package Candidate workspace. It owns VLM/FLUX/palette/cutout gateway source, BTE-shaped HTTP route adapters, package-candidate docs, visual-evidence contracts, host-adapter candidates, tool-grant manifests, Docker package context, validators, fixtures, and proof packets.
Do not start Runtime, start Docker, build or run containers, install packages, start FastAPI/Uvicorn, call VLM/FLUX/palette/rembg endpoints, call Spark/ComfyUI/vLLM services, call cloud vision/image providers, read credentials or env values, mutate BTE/Core/Seed/sibling/OpenDesign repos, grant wildcard tools, grant Profile Exposure, write durable Hindsight memory, or claim readiness without governed approval.
## Current Pickup
Use this workspace for vision gateway source, BTE-shaped route adapters, package-candidate docs, visual-evidence contracts, host-adapter candidates, manifests, Docker package context, validators, fixtures, and proof packets.
## Allowed Writes
Write inside this repo only: source, docs, manifests, Docker package context, validators, fixtures, proof packets, workboard entries, and handoffs.
## Forbidden Effects
Do not mutate `../core/`, sibling repos, BTE, Seed, OpenDesign, provider state, credentials, runtime state, Docker lifecycle, Profile Exposure, wildcard tool grants, Hindsight live memory, or readiness/release claims without governed approval.
## Validation
After editing, run:
```bash
python3 tools/validate_svrnty_vision_child.py
```
## Handoff
Handoffs are compact and refs-only: changed files, validator output, forbidden effects avoided, and legacy intentions left for later.
+37
View File
@@ -0,0 +1,37 @@
# Svrnty Vision Context
`svrnty-vision` is a child-local Cortex OS VISION package candidate for visual
perception and visual production tools. It is not Core authority, not Seed
installed, not Runtime, not Profile Exposure, and not provider admission.
## Terms
Visual Perception Package Candidate
: A child-local candidate package that owns pixel/media perception surfaces under
canonical sense `VISION`.
VISION Sense Family
: The canonical sense family for seeing. It can include textual/source reading
through `/research` and pixel/media perception through `/vision`.
Visual Evidence
: A normalized output record produced by a VISION tool. It discloses package,
tool, source, provider mode, retention, observed content, extracted claims,
confidence, caveats, timestamp, and validation status.
Vision Tool Candidate
: A granular tool inside the visual perception package. Granting the package does
not grant every tool.
Current Route Adapter
: An existing HTTP route that can be mapped to a future Cortex OS tool id without
changing the implementation stack.
Research Handoff
: Research may cite or synthesize Visual Evidence that `/vision` already
produced, but `/vision` does not perform research synthesis, web search, page
fetch, PDF extraction, or deep research workflows.
BTE Compatibility Surface
: Existing route names and BTE-shaped behavior stay usable while the package
candidate is documented for Cortex OS.
+25
View File
@@ -0,0 +1,25 @@
FROM python:3.12-slim
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
SVRNTY_VISION_HOST=0.0.0.0 \
SVRNTY_VISION_PORT=8094
WORKDIR /app
RUN useradd --create-home --shell /usr/sbin/nologin vision
COPY pyproject.toml README.md ./
COPY src ./src
RUN python -m pip install --no-cache-dir --upgrade pip \
&& python -m pip install --no-cache-dir .
USER vision
EXPOSE 8094
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8094/healthz', timeout=3).read()"
CMD ["python", "-m", "svrnty_vision.server"]
+31
View File
@@ -0,0 +1,31 @@
# Svrnty Vision Index
Route: `svrnty-vision`.
Path: `/home/svrnty/workspaces/cortex-os/svrnty-vision`.
Category: child-local sovereign vision HTTP gateway and Visual Perception Package Candidate workspace.
Validator: `python3 tools/validate_svrnty_vision_child.py`.
## Read Order
1. `AGENTS.md` for route-local authority, forbidden effects, and validator rules.
2. `README.md` for gateway posture, operator/developer examples, and cleanup-route boundary.
3. `docs/LEGACY-INGEST.md` for legacy intention coverage and rejection gates.
4. `docs/VISION-PACKAGE-CANDIDATE.md`, `docs/VISUAL-EVIDENCE-CONTRACT.md`, and `docs/VISION-HOST-ADAPTER-CANDIDATES.md` for package-candidate work.
5. `candidate-manifests/` for tool grants and manifest contracts.
6. `WORKBOARD.yaml` for child-local work state.
## Local Authority
Svrnty Vision owns child-local VLM, FLUX, palette, and cutout gateway source; BTE-shaped HTTP route adapters; package-candidate docs; visual-evidence contracts; host-adapter candidates; tool-grant manifests; Docker package context; validators; fixtures; and proof packets.
Svrnty Vision is not Core authority, Seed installation authority, Runtime startup authority, Host Runtime authority, Docker lifecycle authority, Profile Exposure authority, provider admission authority, wildcard tool authority, product-readiness authority, release authority, or production authority.
## Legacy Relation
Older BTE-embedded vision, cloud-provider SDK, VLM, FLUX, palette, rembg, Visual Evidence, and package-candidate work is distilled into this standalone gateway only when current docs and validators admit the intention. Do not import provider credentials, backend state, runtime behavior, cloud-provider coupling, wildcard tool access, or implementation mass because it exists.
## Completion State
Stage: CLEAN.
Clean score: 100.
Current next pass: keep this route static unless a governed route authorizes Docker, FastAPI/Uvicorn, endpoint calls, Spark/ComfyUI/vLLM calls, provider calls, package installation, Profile Exposure, Seed installation, Runtime startup, or readiness claims.
+30 -5
View File
@@ -31,12 +31,37 @@ libraries — Pillow/colorthief and rembg respectively. They land in 4b.
## Status ## Status
**Phase 4a (this commit):** scaffold only. All four endpoints return Current implementation: all four listed endpoints exist. `/vlm/analyze` proxies
HTTP 501 Not Implemented. `/healthz` returns 200. to Qwen3-VL through the configured VLM endpoint, `/flux/render` proxies to
ComfyUI FLUX, `/palette/extract` runs in process, `/rembg/cutout` runs in
process, and `/healthz` returns 200.
Phase 4b will port the real logic. Phase 4c deletes the corresponding ## Cortex OS Package Candidate
.NET code from BTE. Phase 4d wires BTE to call svrnty-vision over HTTP.
See `/home/svrnty/workspaces/hermes/sot/01-ROADMAP/BTE-REFACTOR-EXECUTION-PLAN.md`. `svrnty-vision` is also documented as a child-local Visual Perception Package
Candidate under canonical sense `VISION`. Existing BTE-shaped HTTP routes remain
current route adapters; exact Cortex OS candidate tool ids, grant rules, host
adapter candidates, and Visual Evidence contract live in `docs/` and
`candidate-manifests/`.
This does not grant Core promotion, Seed installation, Runtime startup, Profile
Exposure, provider admission, or wildcard tool access.
Legacy intention coverage is tracked in `docs/LEGACY-INGEST.md`.
## Cortex OS Boundary
This repo owns child-local sovereign vision gateway source, BTE-shaped HTTP route
adapters, package-candidate docs, visual-evidence contracts, host-adapter
candidates, tool-grant manifests, Docker package context, validators, fixtures,
and proof packets only. Cleanup agents must not start Runtime, start Docker,
build or run containers, install packages, start FastAPI/Uvicorn, call VLM,
FLUX, palette, or rembg endpoints, call Spark/ComfyUI/vLLM services, call cloud
vision/image providers, read credentials or env values, mutate
BTE/Core/Seed/sibling/OpenDesign repos, grant wildcard tools, grant Profile
Exposure, write durable Hindsight memory, or claim readiness without explicit
governed approval. Run, test, Docker, endpoint, provider, and configuration
examples below are operator/developer docs, not cleanup-route authorization.
## Run ## Run
+31
View File
@@ -0,0 +1,31 @@
items:
- id: SVRNTY-VISION-WORK-001
title: Svrnty Vision Child Workspace Governance
status: complete
source: outputs/2026-06-12-svrnty-vision-workboard-readiness-reconciliation.md
owner: ""
- id: SVRNTY-VISION-WORK-002
title: Generic VISION Package Candidate And Visual Evidence Proof
status: complete
source: outputs/2026-06-06-svrnty-vision-work-002-package-candidate-validation.md
owner: ""
- id: SVRNTY-VISION-WORK-003
title: Vision Package Candidate Sandcastle
status: complete
source: outputs/2026-06-12-svrnty-vision-workboard-readiness-reconciliation.md
owner: ""
- id: SVRNTY-VISION-WORK-004
title: Package Docker Build Context
status: complete
source: Dockerfile
owner: ""
- id: SVRNTY-VISION-WORK-005
title: Svrnty Vision Agent Contract Blocks
status: complete
source: AGENTS.md
owner: ""
- id: SVRNTY-VISION-WORK-006
title: Svrnty Vision Navigation Index
status: complete
source: INDEX.md
owner: ""
@@ -0,0 +1,60 @@
{
"schema_version": "vision.host_adapter_candidate.v1",
"adapter_id": "vision-claude-code-adapter",
"authority": "child-local",
"status": "candidate_only_not_seed_installed",
"host_runtime": "claude-code",
"host_runtime_class": "claude-code",
"package_id": "visual-perception-package-candidate",
"adapter_role": "thin_access_adapter",
"seed_artifact_target": "host-adapters/vision-claude-code-adapter.json",
"wildcard_grant_allowed": false,
"current_tool_candidates": [
"vision.image_analyze",
"vision.image_generate",
"vision.palette_extract",
"vision.background_cutout"
],
"planned_tool_candidates": [
"vision.ocr_read",
"vision.screenshot_observe",
"vision.browser_observe",
"vision.document_layout_read",
"vision.chart_read",
"vision.table_read",
"vision.diagram_read",
"vision.object_detect",
"vision.visual_ground",
"vision.segment",
"vision.video_read",
"vision.image_edit"
],
"disclosure_contract": {
"provider_mode_required": true,
"retention_required": true,
"visual_evidence_required": true
},
"allowed_effects": [
"analyze_image_input",
"generate_image_output",
"extract_palette",
"remove_background",
"return_visual_evidence"
],
"forbidden_effects": [
"research_synthesis",
"textual_web_search",
"textual_page_fetch",
"profile_exposure",
"runtime_start",
"provider_admission",
"wildcard_tool_exposure"
],
"non_authorization": {
"core_promotion": false,
"seed_installation": false,
"runtime_start": false,
"profile_exposure": false,
"provider_admission": false
}
}
@@ -0,0 +1,60 @@
{
"schema_version": "vision.host_adapter_candidate.v1",
"adapter_id": "vision-codex-cli-adapter",
"authority": "child-local",
"status": "candidate_only_not_seed_installed",
"host_runtime": "codex-cli",
"host_runtime_class": "codex-cli",
"package_id": "visual-perception-package-candidate",
"adapter_role": "thin_access_adapter",
"seed_artifact_target": "host-adapters/vision-codex-cli-adapter.json",
"wildcard_grant_allowed": false,
"current_tool_candidates": [
"vision.image_analyze",
"vision.image_generate",
"vision.palette_extract",
"vision.background_cutout"
],
"planned_tool_candidates": [
"vision.ocr_read",
"vision.screenshot_observe",
"vision.browser_observe",
"vision.document_layout_read",
"vision.chart_read",
"vision.table_read",
"vision.diagram_read",
"vision.object_detect",
"vision.visual_ground",
"vision.segment",
"vision.video_read",
"vision.image_edit"
],
"disclosure_contract": {
"provider_mode_required": true,
"retention_required": true,
"visual_evidence_required": true
},
"allowed_effects": [
"analyze_image_input",
"generate_image_output",
"extract_palette",
"remove_background",
"return_visual_evidence"
],
"forbidden_effects": [
"research_synthesis",
"textual_web_search",
"textual_page_fetch",
"profile_exposure",
"runtime_start",
"provider_admission",
"wildcard_tool_exposure"
],
"non_authorization": {
"core_promotion": false,
"seed_installation": false,
"runtime_start": false,
"profile_exposure": false,
"provider_admission": false
}
}
@@ -0,0 +1,60 @@
{
"schema_version": "vision.host_adapter_candidate.v1",
"adapter_id": "vision-pi-code-adapter",
"authority": "child-local",
"status": "candidate_only_not_seed_installed",
"host_runtime": "pi-code",
"host_runtime_class": "pi-code",
"package_id": "visual-perception-package-candidate",
"adapter_role": "thin_access_adapter",
"seed_artifact_target": "host-adapters/vision-pi-code-adapter.json",
"wildcard_grant_allowed": false,
"current_tool_candidates": [
"vision.image_analyze",
"vision.image_generate",
"vision.palette_extract",
"vision.background_cutout"
],
"planned_tool_candidates": [
"vision.ocr_read",
"vision.screenshot_observe",
"vision.browser_observe",
"vision.document_layout_read",
"vision.chart_read",
"vision.table_read",
"vision.diagram_read",
"vision.object_detect",
"vision.visual_ground",
"vision.segment",
"vision.video_read",
"vision.image_edit"
],
"disclosure_contract": {
"provider_mode_required": true,
"retention_required": true,
"visual_evidence_required": true
},
"allowed_effects": [
"analyze_image_input",
"generate_image_output",
"extract_palette",
"remove_background",
"return_visual_evidence"
],
"forbidden_effects": [
"research_synthesis",
"textual_web_search",
"textual_page_fetch",
"profile_exposure",
"runtime_start",
"provider_admission",
"wildcard_tool_exposure"
],
"non_authorization": {
"core_promotion": false,
"seed_installation": false,
"runtime_start": false,
"profile_exposure": false,
"provider_admission": false
}
}
@@ -0,0 +1,84 @@
{
"schema_version": "vision.package_candidate.v1",
"package_id": "visual-perception-package-candidate",
"workspace": "svrnty-vision",
"authority": "child-local",
"status": "candidate_only_not_seed_installed",
"canonical_sense": "VISION",
"package_role": "generic_visual_perception_and_production",
"current_route_adapters": [
{
"route": "POST /vlm/analyze",
"tool_id": "vision.image_analyze",
"capability": "vlm_image_analysis"
},
{
"route": "POST /flux/render",
"tool_id": "vision.image_generate",
"capability": "image_generation"
},
{
"route": "POST /palette/extract",
"tool_id": "vision.palette_extract",
"capability": "palette_extraction"
},
{
"route": "POST /rembg/cutout",
"tool_id": "vision.background_cutout",
"capability": "background_cutout"
}
],
"current_tool_candidates": [
"vision.image_analyze",
"vision.image_generate",
"vision.palette_extract",
"vision.background_cutout"
],
"planned_tool_candidates": [
"vision.ocr_read",
"vision.screenshot_observe",
"vision.browser_observe",
"vision.document_layout_read",
"vision.chart_read",
"vision.table_read",
"vision.diagram_read",
"vision.object_detect",
"vision.visual_ground",
"vision.segment",
"vision.video_read",
"vision.image_edit"
],
"owned_capability_classes": [
"pixel_media_perception",
"visual_evidence_production",
"image_generation",
"image_editing",
"screenshot_and_browser_visual_observation",
"layout_chart_table_diagram_visual_reading"
],
"forbidden_research_owned_capabilities": [
"web_search",
"fetch_page",
"extract_pdf",
"deep_research",
"research_synthesis",
"capsule_writing"
],
"research_handoff": {
"may_produce_visual_evidence_for_research": true,
"may_perform_research_synthesis": false,
"handoff_contract": "candidate-manifests/visual-evidence-contract.json"
},
"grant_policy": {
"wildcard_grant_allowed": false,
"package_default_grants_all_tools": false,
"tool_grants_are_granular": true
},
"non_authorization": {
"core_promotion": false,
"seed_installation": false,
"runtime_start": false,
"profile_exposure": false,
"provider_admission": false
}
}
@@ -0,0 +1,69 @@
{
"schema_version": "vision.tool_grants.v1",
"package_id": "visual-perception-package-candidate",
"authority": "child-local",
"status": "candidate_only_not_seed_installed",
"canonical_sense": "VISION",
"wildcard_grant_allowed": false,
"package_default_grants_all_tools": false,
"current_tool_candidates": [
{
"tool_id": "vision.image_analyze",
"route": "POST /vlm/analyze",
"grant_default": false,
"requires_visual_evidence": true
},
{
"tool_id": "vision.image_generate",
"route": "POST /flux/render",
"grant_default": false,
"requires_visual_evidence": false
},
{
"tool_id": "vision.palette_extract",
"route": "POST /palette/extract",
"grant_default": false,
"requires_visual_evidence": true
},
{
"tool_id": "vision.background_cutout",
"route": "POST /rembg/cutout",
"grant_default": false,
"requires_visual_evidence": true
}
],
"planned_tool_candidates": [
"vision.ocr_read",
"vision.screenshot_observe",
"vision.browser_observe",
"vision.document_layout_read",
"vision.chart_read",
"vision.table_read",
"vision.diagram_read",
"vision.object_detect",
"vision.visual_ground",
"vision.segment",
"vision.video_read",
"vision.image_edit"
],
"required_disclosures": {
"producing_package_id": true,
"producing_tool_id": true,
"capability_surface": true,
"provider_mode": true,
"retention_disclosure": true,
"validation_status": true
},
"forbidden_effects": [
"research_synthesis",
"textual_web_search",
"textual_page_fetch",
"pdf_text_extraction",
"deep_research_workflow",
"capsule_writing",
"runtime_start",
"profile_exposure",
"provider_admission",
"wildcard_tool_exposure"
]
}
@@ -0,0 +1,37 @@
{
"schema_version": "vision.visual_evidence_contract.v1",
"package_id": "visual-perception-package-candidate",
"authority": "child-local",
"status": "candidate_only_not_seed_installed",
"required_fields": [
"producing_package_id",
"producing_tool_id",
"capability_surface",
"source_reference",
"provider_mode",
"retention_disclosure",
"observed_content_summary",
"extracted_claims",
"confidence",
"caveats",
"timestamp",
"validation_status"
],
"first_vertical_proof": {
"source_route": "POST /vlm/analyze",
"source_tool_id": "vision.image_analyze",
"module": "src/svrnty_vision/visual_evidence.py",
"test": "tests/test_visual_evidence.py",
"live_provider_call_required": false
},
"research_handoff": {
"research_may_consume_visual_evidence": true,
"vision_may_write_research_capsules": false,
"vision_may_perform_research_synthesis": false
},
"disclosure_contract": {
"provider_mode_required": true,
"retention_required": true,
"validation_status_required": true
}
}
+75
View File
@@ -0,0 +1,75 @@
# Svrnty Vision Legacy Ingest
Schema: `cortex.svrnty-vision.legacy-ingest.v1`
Last reviewed: `2026-06-18`
This file is Svrnty Vision child-local operator state. It is not Cortex OS Core
SOT, not Seed installation authority, not Runtime startup authority, not Docker
lifecycle authority, not Profile Exposure authority, not provider authority, not
credential access, and not readiness or release approval.
Legacy vision work is assessed by intention first. Old work is kept, ported,
archived outside the umbrella, deferred, or rejected only after the useful
intention is identified and compared against the current Svrnty Vision route.
## Rules
- Do not import BTE service coupling, cloud-provider credentials, backend state,
runtime behavior, Docker lifecycle effects, or wildcard tool grants.
- Do not start FastAPI/Uvicorn, Docker, Runtime, VLM, FLUX, Spark, ComfyUI,
vLLM, rembg, palette extraction, or cloud image providers from cleanup work.
- Keep records compact: refs, intention, current coverage, decision, closure
gate, and forbidden effects.
## Current Gates
### BTE-Embedded Vision Extraction
- Source refs: `src/svrnty_vision/`, `README.md`, and BTE-shaped route-adapter
docs.
- Intention: move VLM, FLUX, palette, and cutout capability out of BTE so BTE
remains narrow and sovereign-first.
- Current coverage: met as standalone FastAPI gateway source with local
validator and documented BTE-shaped HTTP adapters.
- Decision: keep as active route-local source; do not re-import BTE coupling.
- Closure gate: BTE reintegration, endpoint calls, or Runtime startup requires
governed approval and redacted proof.
### Visual Perception Package Candidate
- Source refs: `docs/VISION-PACKAGE-CANDIDATE.md`,
`docs/VISUAL-EVIDENCE-CONTRACT.md`,
`docs/VISION-HOST-ADAPTER-CANDIDATES.md`, and `candidate-manifests/`.
- Intention: preserve a Cortex OS candidate package for canonical sense
`VISION` without granting Seed installation, Runtime startup, or wildcard
tool authority.
- Current coverage: met as package-candidate docs, host-adapter candidates,
tool-grant manifests, and visual-evidence contract.
- Decision: keep as candidate material only.
- Closure gate: Core promotion, Seed installation, Profile Exposure, or
wildcard grants require separate governed route approval.
### Provider And Heavy-Model Boundaries
- Source refs: `README.md`, `.env.example`, `src/svrnty_vision/settings.py`,
and route docs.
- Intention: document Spark-hosted heavy model dependencies while keeping the
child route from owning those services or credentials.
- Current coverage: met as operator/developer docs and settings shape.
- Decision: keep only configuration shape and boundary docs.
- Closure gate: provider calls, Spark/ComfyUI/vLLM calls, credential reads, or
Docker/server startup are outside cleanup authority.
### Generated Cache Custody
- Source refs:
`/home/svrnty/workspaces/cortex-os-retired/2026-06-18/svrnty-vision-python-cache-preserved-141800/`.
- Intention: remove interpreter-generated cache residue from the active child
route without deleting custody proof.
- Current coverage: met by preserving the cache outside the umbrella and adding
a route-local validator guard against recurrence.
- Decision: reject generated cache material as archive-only, not source,
evidence, authority, or package candidate material.
- Closure gate: future cache recurrence fails
`python3 tools/validate_svrnty_vision_child.py`; no runtime, Docker, provider,
credential, Core, Seed, Hindsight, or sibling-route effects are implied.
+29
View File
@@ -0,0 +1,29 @@
# VISION Host Adapter Candidates
Status: candidate only. These adapter manifests do not install into Seed and do
not grant host access.
## Intent
Claude Code, Codex CLI, and Pi-Code should expose the same VISION package
capabilities when Seed accepts the package. Each host adapter is thin: it maps
host-specific command shape to the same package tool ids and the same disclosure
contract.
## Host Targets
- `claude-code`
- `codex-cli`
- `pi-code`
## Parity Rule
Each host adapter candidate must expose the same current tool ids, the same
planned tool ids, no wildcard grant, and the same Visual Evidence disclosure
contract.
## Candidate Manifests
- `candidate-manifests/host-adapters/vision-claude-code-adapter.json`
- `candidate-manifests/host-adapters/vision-codex-cli-adapter.json`
- `candidate-manifests/host-adapters/vision-pi-code-adapter.json`
+70
View File
@@ -0,0 +1,70 @@
# VISION Package Candidate
Status: child-local candidate only. No Core promotion, Seed installation,
Runtime start, Profile Exposure, or provider admission is authorized. No
wildcard grant is authorized by this document.
## Intent
`svrnty-vision` is the generic visual-perception package candidate for the
canonical Cortex OS sense `VISION`. It owns tools that inspect or produce pixels,
images, screenshots, browser observations, layouts, charts, diagrams, grounded
regions, segmentations, video frames, or generated/edited images.
`research` is also under the `VISION` sense family, but it owns textual/source
reading and research workflows. The boundary is by capability, not by sense name:
Research reads sources; Vision sees media.
## Current Route Adapters
| Current route | Candidate tool id | Capability |
| --- | --- | --- |
| `POST /vlm/analyze` | `vision.image_analyze` | Analyze image input with a VLM and return a normalized observation. |
| `POST /flux/render` | `vision.image_generate` | Generate image output through the existing FLUX route. |
| `POST /palette/extract` | `vision.palette_extract` | Extract dominant colors from image input. |
| `POST /rembg/cutout` | `vision.background_cutout` | Remove image background and return cutout output. |
## Planned Tool Candidates
The complete VISION visual-perception package should cover:
- `vision.ocr_read`
- `vision.screenshot_observe`
- `vision.browser_observe`
- `vision.document_layout_read`
- `vision.chart_read`
- `vision.table_read`
- `vision.diagram_read`
- `vision.object_detect`
- `vision.visual_ground`
- `vision.segment`
- `vision.video_read`
- `vision.image_edit`
These are not implemented or granted by this slice. They are named so future
work has a canonical target and does not duplicate Research capabilities.
## Boundary
Owned here:
- Pixel/media perception.
- Visual evidence production.
- Image generation or editing.
- Visual extraction from screenshots, browser views, image files, video frames,
charts, diagrams, and layouts.
Not owned here:
- Web search.
- Page fetch.
- PDF text extraction.
- Research synthesis.
- Deep research planning.
- Capsule writing.
- Profile Exposure.
- Runtime startup.
- Provider admission.
Research can consume Visual Evidence only through an explicit handoff contract.
Vision never becomes a research synthesizer by returning evidence.
+42
View File
@@ -0,0 +1,42 @@
# Visual Evidence Contract
Status: candidate contract. This is route-only evidence for Cortex OS and Seed
review. It does not grant tools or promote the package.
## Required Fields
Every Visual Evidence record must include:
- `producing_package_id`
- `producing_tool_id`
- `capability_surface`
- `source_reference`
- `provider_mode`
- `retention_disclosure`
- `observed_content_summary`
- `extracted_claims`
- `confidence`
- `caveats`
- `timestamp`
- `validation_status`
## First Vertical Proof
The first proof adapts a raw-mode `POST /vlm/analyze` response into Visual
Evidence through a pure Python adapter. It does not call a live provider.
Proof module: `src/svrnty_vision/visual_evidence.py`
Proof test: `tests/test_visual_evidence.py`
## Research Handoff Rule
Research may cite Visual Evidence as an input source if the record includes the
required fields and validation status. Research owns synthesis and capsule writing.
Vision owns the visual observation record only.
## Provider And Retention Disclosure
Provider mode and retention are mandatory because host agents must be able to
disclose how the visual observation was produced. Missing disclosure invalidates
the evidence record.
@@ -0,0 +1,69 @@
{
"schema": "svrnty-vision.bte-product-ready-local-vlm-provider-proof.v1",
"timestamp": "2026-06-19T20:16:43Z",
"work_item_id": "SVRNTY-VISION-WORK-008",
"route": "svrnty-vision",
"goal": "Prove the configured local VLM provider is reachable for the BTE Product Ready provider-call chain without claiming readiness.",
"approval": {
"name": "BTE Product Ready local VLM provider proof refresh",
"expires_after_report": true
},
"provider_route_resolution": {
"registered_ollama_provider_route_found": false,
"adapter_owner_route_used_for_proof": "svrnty-vision",
"reason": "svrnty-vision owns the configured VLM adapter endpoint used by BTE through /vlm/analyze"
},
"configured_provider": {
"kind": "local_ollama_openai_compatible",
"base_url": "http://100.88.167.87:11434",
"expected_model": "qwen3-vl:32b"
},
"health_proof": {
"ollama_tags": {
"url": "http://100.88.167.87:11434/api/tags",
"http_status": 200,
"duration_seconds": 0.004933,
"expected_model_present": true
},
"openai_models": {
"url": "http://100.88.167.87:11434/v1/models",
"http_status": 200,
"duration_seconds": 0.001439,
"expected_model_present": true
}
},
"gateway_recheck": {
"host_healthz": {
"url": "http://localhost:8092/healthz",
"http_status": 200,
"duration_seconds": 0.001575,
"body_status": "ok",
"version": "0.1.0"
},
"bte_runtime_namespace_healthz": {
"url": "http://localhost:8092/healthz",
"http_status": 200,
"duration_seconds": 0.000823,
"body_status": "ok",
"version": "0.1.0"
}
},
"downstream_bte_result_ref": "../bte/docs/goal-runs/bte-product-ready-local-vlm-provider-refresh/bte-work-020-successful-provider-call.json",
"route_validator": {
"command": "python3 tools/validate_svrnty_vision_child.py",
"status": "pass"
},
"tool_effects": {
"provider_health_probe_performed": true,
"provider_start_or_refresh_performed": false,
"direct_model_inference_performed": false,
"gateway_runtime_started": false,
"bte_provider_call_performed_by_bte_route": true,
"profile_exposure_changed": false,
"mcp_registered": false,
"archive_delete_performed": false,
"raw_payload_storage_created_by_this_route": false,
"product_ready_claim_made": false
},
"status": "local_vlm_provider_health_proven"
}
@@ -0,0 +1,25 @@
# SVRNTY-VISION-WORK-008 Local VLM Provider Proof
Status: local VLM provider health proven.
Approval: `BTE Product Ready local VLM provider proof refresh`.
Proof:
- No separate registered Ollama provider route was found; `svrnty-vision` owns the adapter endpoint used by BTE through `/vlm/analyze`.
- `GET http://100.88.167.87:11434/api/tags`: HTTP `200`; `qwen3-vl:32b` present.
- `GET http://100.88.167.87:11434/v1/models`: HTTP `200`; `qwen3-vl:32b` present.
- `GET http://localhost:8092/healthz`: HTTP `200`, status `ok`, version `0.1.0`.
- From the BTE runtime namespace, `GET http://localhost:8092/healthz`: HTTP `200`, status `ok`, version `0.1.0`.
Boundary:
- No provider start or refresh was needed.
- No direct model inference was performed from this route.
- No credentials, secrets, raw model payloads, or image payloads were recorded.
- No Profile Exposure, MCP registration, archive/delete, raw payload storage, release claim, or Product Ready claim happened.
Validators:
- `python3 tools/validate_svrnty_vision_child.py`: PASS.
- `python3 tools/validate_svrnty_vision_bte_local_vlm_provider_proof.py`: PASS.
@@ -0,0 +1,64 @@
{
"schema": "svrnty-vision.bte-product-ready-provider-gateway-refresh-proof.v1",
"timestamp": "2026-06-19T20:08:14Z",
"work_item_id": "SVRNTY-VISION-WORK-007",
"route": "svrnty-vision",
"goal": "Provide bounded provider-gateway health proof for the BTE Product Ready live-effect chain without claiming readiness.",
"approval": {
"name": "BTE Product Ready provider gateway proof refresh",
"expired": true,
"expiry_reason": "first_failed_blocked_effect:bte_provider_call_timeout"
},
"precondition": {
"bte_expected_url": "http://localhost:8092/healthz",
"initial_host_health_status": "connection_refused"
},
"gateway_refresh": {
"image": "cortex-os/vision:seed",
"host_port_container": {
"name": "svrnty-vision-bte-proof",
"container_id_prefix": "b25509d4d640",
"mapping": "0.0.0.0:8092->8094/tcp",
"healthz": {
"url": "http://localhost:8092/healthz",
"http_status": 200,
"body_status": "ok",
"version": "0.1.0"
}
},
"bte_network_namespace_container": {
"name": "svrnty-vision-bte-net-proof",
"container_id_prefix": "a9a916ad2f5c",
"network": "container:bte-rest-mcp-readiness-api",
"env_override": "SVRNTY_VISION_PORT=8092",
"healthz_from_bte_runtime_namespace": {
"url": "http://localhost:8092/healthz",
"http_status": 200,
"body_status": "ok",
"version": "0.1.0"
}
}
},
"downstream_bte_recheck": {
"readyz": {
"url": "http://localhost:6001/readyz",
"http_status": 200,
"body_status": "Healthy"
},
"provider_call_result_ref": "../bte/docs/goal-runs/bte-product-ready-provider-gateway-refresh/bte-work-019-provider-gateway-refresh-and-provider-call.json"
},
"route_validator": {
"command": "python3 tools/validate_svrnty_vision_child.py",
"status": "pass"
},
"tool_effects": {
"gateway_runtime_started": true,
"provider_call_from_this_route": false,
"profile_exposure_changed": false,
"mcp_registered": false,
"archive_delete_performed": false,
"raw_payload_storage_created": false,
"product_ready_claim_made": false
},
"status": "gateway_health_proven_provider_call_blocked_downstream"
}
@@ -0,0 +1,24 @@
# SVRNTY-VISION-WORK-007 Provider Gateway Refresh
Status: gateway health proven; downstream BTE provider call blocked.
Approval: `BTE Product Ready provider gateway proof refresh`. The approval expired after the first failed blocked effect: the bounded BTE provider call timed out at the local VLM backend.
Proof:
- `svrnty-vision-bte-proof` started from `cortex-os/vision:seed` and mapped host `8092` to container `8094`.
- `GET http://localhost:8092/healthz`: HTTP `200`, status `ok`, version `0.1.0`.
- `svrnty-vision-bte-net-proof` started from `cortex-os/vision:seed` in the BTE runtime network namespace with `SVRNTY_VISION_PORT=8092`.
- From `bte-rest-mcp-readiness-api`, `GET http://localhost:8092/healthz`: HTTP `200`, status `ok`, version `0.1.0`.
- BTE `GET http://localhost:6001/readyz`: HTTP `200`, status `Healthy`.
Boundary:
- No credentials or secrets were read or recorded.
- No Profile Exposure, MCP registration, archive/delete, raw payload storage, release claim, or Product Ready claim happened.
- This route did not call the VLM provider directly. The downstream bounded BTE provider-call result is recorded in BTE proof `bte-work-019`.
Validator:
- `python3 tools/validate_svrnty_vision_child.py`: PASS.
- `python3 tools/validate_svrnty_vision_bte_provider_gateway_refresh.py`: PASS.
@@ -0,0 +1,55 @@
# SVRNTY-VISION-WORK-002 Validation Evidence
Date: 2026-06-06
Scope: generic Cortex OS VISION package candidate and Visual Evidence proof for
`svrnty-vision`.
## What Changed
- Added child-local VISION package candidate docs and manifests.
- Mapped current HTTP routes to candidate tool ids.
- Added granular tool-grant candidate manifest with no wildcard grants.
- Added Visual Evidence contract and a pure VLM response adapter proof.
- Added host adapter candidate manifests for Claude Code, Codex CLI, and
Pi-Code.
- Added a local sandcastle handoff for the next Seed acceptance route.
- Fixed package dependency declaration from `rembg` to `rembg[cpu]` so the
background cutout route has the required CPU backend on fresh installs.
## Gates Passed
- `python3 tools/validate_vision_package_candidate.py`
- `python3 tools/validate_vision_tool_grants.py`
- `python3 tools/validate_visual_evidence_contract.py`
- `python3 tools/validate_vision_host_adapter_candidates.py`
- `python3 tools/validate_svrnty_vision_child.py`
- `.venv/bin/python tools/validate_svrnty_vision_child.py`
- `pytest --noconftest tests/test_visual_evidence.py`
- `.venv/bin/pytest tests/ -m 'not integration'`
- `python3 -m py_compile src/svrnty_vision/visual_evidence.py tools/validate_vision_package_candidate.py tools/validate_vision_tool_grants.py tools/validate_visual_evidence_contract.py tools/validate_vision_host_adapter_candidates.py tools/validate_svrnty_vision_child.py`
- `.venv/bin/python -m py_compile src/svrnty_vision/visual_evidence.py tools/validate_vision_package_candidate.py tools/validate_vision_tool_grants.py tools/validate_visual_evidence_contract.py tools/validate_vision_host_adapter_candidates.py tools/validate_svrnty_vision_child.py`
- `git diff --check`
## Test Result
Non-integration suite after `rembg[cpu]` dependency fix:
- 30 passed.
- 10 integration tests deselected.
- 1 Starlette/httpx deprecation warning.
## Boundaries Preserved
- No Core mutation.
- No Seed installation.
- No Runtime start.
- No Profile Exposure.
- No provider admission.
- No wildcard grant.
- No Research synthesis in Vision.
## Follow-Up
Follow-up, optional: update the FastAPI/TestClient dependency path when the
Starlette/httpx deprecation becomes a compatibility issue.
@@ -0,0 +1,45 @@
{
"schema_version": "svrnty-vision-workboard-readiness-reconciliation.v1",
"status": "complete",
"ok": true,
"cc": "core-compliant",
"date": "2026-06-12",
"workspace": "svrnty-vision",
"reconciled_rows": [
{
"id": "SVRNTY-VISION-WORK-001",
"from": "candidate",
"to": "complete",
"reason": "Workspace governance, rules, docs, board, and validator are present."
},
{
"id": "SVRNTY-VISION-WORK-003",
"from": "ready-for-agent",
"to": "complete",
"reason": "The sandcastle handoff exists; successor consumption belongs to governed Seed/Core routes."
}
],
"evidence": [
"AGENTS.md",
"README.md",
"WORKBOARD.yaml",
"CONTEXT.md",
"docs/VISION-PACKAGE-CANDIDATE.md",
"docs/VISUAL-EVIDENCE-CONTRACT.md",
"docs/VISION-HOST-ADAPTER-CANDIDATES.md",
"candidate-manifests/vision-package-candidate.json",
"candidate-manifests/vision-tool-grants.json",
"candidate-manifests/visual-evidence-contract.json",
"tools/validate_svrnty_vision_child.py"
],
"false_claims": {
"core_promotion": false,
"seed_installation": false,
"runtime_started": false,
"profile_exposure_changed": false,
"provider_admitted": false,
"wildcard_grant": false,
"sibling_mutation": false
},
"next_roi": "Consume this validated package candidate through governed Seed/Core routes when package release and Runtime approval gates allow it."
}
@@ -0,0 +1,40 @@
---
title: Svrnty Vision Workboard Readiness Reconciliation
date: 2026-06-12
source: output
cc: core-compliant
status: complete
---
# Svrnty Vision Workboard Readiness Reconciliation
`svrnty-vision` is child-local validated as the Cortex OS VISION package
candidate. Its remaining candidate and ready-for-agent workboard rows were stale
workspace status, not missing local package evidence.
## Reconciled Rows
- `SVRNTY-VISION-WORK-001`: `candidate` -> `complete`; workspace governance,
local rules, docs, board, and validator are present.
- `SVRNTY-VISION-WORK-003`: `ready-for-agent` -> `complete`; the sandcastle
handoff exists and its next ROI belongs to Seed consumption, not more local
Vision package work.
## Evidence
- `python3 tools/validate_svrnty_vision_child.py`: green.
- `docs/VISION-PACKAGE-CANDIDATE.md`: package boundary.
- `docs/VISUAL-EVIDENCE-CONTRACT.md`: Visual Evidence contract.
- `docs/VISION-HOST-ADAPTER-CANDIDATES.md`: host adapter candidates.
- `candidate-manifests/`: package, grants, evidence, and host adapter
manifests.
## Boundaries
No Core promotion, Seed installation, Runtime start, Profile Exposure, provider
admission, wildcard grant, or sibling mutation is claimed here.
## Next ROI
Consume this validated package candidate through governed Seed/Core routes when
package release and Runtime approval gates allow it.
@@ -0,0 +1,34 @@
# Vision Package Candidate Sandcastle
Status: ready for the next agent. This sandcastle belongs inside
`svrnty-vision`; it is not an umbrella repo and not a Core route.
## End State
Make `svrnty-vision` Seed-acceptable as the generic Cortex OS VISION
visual-perception package candidate, with host parity for Claude Code, Codex CLI,
and Pi-Code, granular grants, and Visual Evidence disclosure.
## Current Vertical Slice
- Package boundary documented.
- Current BTE routes mapped to Cortex OS candidate tool ids.
- Planned SOTA visual tool inventory named without implementing all tools.
- Visual Evidence contract documented.
- VLM raw-mode output adapted into Visual Evidence through a pure proof.
- Host adapter candidate parity documented.
## Stop Conditions
- Do not mutate Core from this workspace.
- Do not install into Seed from this workspace.
- Do not start Runtime.
- Do not grant Profile Exposure.
- Do not admit providers.
- Do not wildcard expose all tools.
## Next ROI
Build the first Seed route outside this workspace that can accept these manifests
as candidate artifacts, then prove one host adapter can expose exactly one tool
grant without wildcard access.
+1 -1
View File
@@ -20,7 +20,7 @@ dependencies = [
"httpx>=0.27,<1.0", "httpx>=0.27,<1.0",
"Pillow>=11,<13", "Pillow>=11,<13",
"colorthief>=0.2.1", "colorthief>=0.2.1",
"rembg>=2.0,<3.0", "rembg[cpu]>=2.0,<3.0",
] ]
[project.optional-dependencies] [project.optional-dependencies]
+1 -1
View File
@@ -5,7 +5,7 @@ pydantic-settings>=2.6,<3.0
httpx>=0.27,<1.0 httpx>=0.27,<1.0
Pillow>=11,<13 Pillow>=11,<13
colorthief>=0.2.1 colorthief>=0.2.1
rembg>=2.0,<3.0 rembg[cpu]>=2.0,<3.0
# Test deps # Test deps
pytest>=8.3,<9.0 pytest>=8.3,<9.0
+148
View File
@@ -0,0 +1,148 @@
"""Visual Evidence adapter for Cortex OS VISION package candidates."""
from __future__ import annotations
import json
from typing import Any, Protocol
from pydantic import BaseModel, Field
PACKAGE_ID = "visual-perception-package-candidate"
DEFAULT_VALIDATION_STATUS = "candidate_validated"
class VlmAnalysisResponse(Protocol):
justification: str
model_id: str
raw_scores_json: str
class VisualEvidence(BaseModel):
"""Normalized visual observation record for host disclosure and handoff."""
producing_package_id: str = PACKAGE_ID
producing_tool_id: str
capability_surface: str
source_reference: str
provider_mode: str
retention_disclosure: str
observed_content_summary: str
extracted_claims: list[str] = Field(default_factory=list)
confidence: str
caveats: list[str] = Field(default_factory=list)
timestamp: str
validation_status: str
model_id: str | None = None
raw_observation_ref: str | None = None
def visual_evidence_from_vlm_response(
response: VlmAnalysisResponse,
*,
source_reference: str,
provider_mode: str,
retention_disclosure: str,
timestamp: str,
producing_tool_id: str = "vision.image_analyze",
capability_surface: str = "vision.image_analyze",
confidence: str = "medium",
caveats: list[str] | None = None,
validation_status: str = DEFAULT_VALIDATION_STATUS,
) -> VisualEvidence:
"""Adapt the current VLM response into a Cortex OS Visual Evidence record."""
_require_non_empty("source_reference", source_reference)
_require_non_empty("provider_mode", provider_mode)
_require_non_empty("retention_disclosure", retention_disclosure)
_require_non_empty("timestamp", timestamp)
raw_text = response.raw_scores_json.strip()
parsed = _parse_json_object(raw_text)
summary = _summary_from_response(parsed, response.justification, raw_text)
claims = _claims_from_raw(parsed, raw_text)
if not claims and summary:
claims = [f"summary: {summary}"]
return VisualEvidence(
producing_tool_id=producing_tool_id,
capability_surface=capability_surface,
source_reference=source_reference,
provider_mode=provider_mode,
retention_disclosure=retention_disclosure,
observed_content_summary=summary,
extracted_claims=claims,
confidence=confidence,
caveats=caveats or [],
timestamp=timestamp,
validation_status=validation_status,
model_id=response.model_id,
raw_observation_ref="AnalyzeResponse.raw_scores_json",
)
def _require_non_empty(field_name: str, value: str) -> None:
if not value or not value.strip():
raise ValueError(f"{field_name} is required for Visual Evidence")
def _parse_json_object(raw_text: str) -> dict[str, Any] | None:
if not raw_text:
return None
try:
parsed = json.loads(raw_text)
except json.JSONDecodeError:
return None
return parsed if isinstance(parsed, dict) else None
def _summary_from_response(
parsed: dict[str, Any] | None, justification: str, raw_text: str
) -> str:
if parsed:
for key in (
"observed_content_summary",
"summary",
"description",
"caption",
"text",
):
value = parsed.get(key)
if isinstance(value, str) and value.strip():
return _compact(value)
if justification.strip():
return _compact(justification)
return _compact(raw_text)
def _claims_from_raw(parsed: dict[str, Any] | None, raw_text: str) -> list[str]:
if not parsed:
return [f"raw: {_compact(raw_text)}"] if raw_text else []
claims: list[str] = []
for key, value in parsed.items():
claims.append(f"{key}: {_format_claim_value(value)}")
return claims
def _format_claim_value(value: Any) -> str:
if isinstance(value, list):
if not value:
return "[]"
if all(
isinstance(item, str | int | float | bool) or item is None
for item in value
):
return ", ".join(str(item) for item in value)
return json.dumps(value, sort_keys=True, separators=(",", ":"))
if isinstance(value, dict):
return json.dumps(value, sort_keys=True, separators=(",", ":"))
if value is None:
return "null"
return str(value)
def _compact(text: str, limit: int = 500) -> str:
compacted = " ".join(text.split())
if len(compacted) <= limit:
return compacted
return compacted[: limit - 3].rstrip() + "..."
+57
View File
@@ -0,0 +1,57 @@
from __future__ import annotations
from types import SimpleNamespace
from svrnty_vision.visual_evidence import visual_evidence_from_vlm_response
def test_vlm_raw_mode_response_becomes_visual_evidence() -> None:
response = SimpleNamespace(
rubric_mode="raw",
justification="",
model_id="qwen-test",
raw_scores_json='{"description":"solid red square","objects":["red square"],"detected_text":[]}',
)
evidence = visual_evidence_from_vlm_response(
response,
source_reference="fixture://red-square.png",
provider_mode="sovereign",
retention_disclosure="synchronous_no_async_persistence",
timestamp="2026-06-06T00:00:00Z",
)
assert evidence.producing_package_id == "visual-perception-package-candidate"
assert evidence.producing_tool_id == "vision.image_analyze"
assert evidence.capability_surface == "vision.image_analyze"
assert evidence.source_reference == "fixture://red-square.png"
assert evidence.provider_mode == "sovereign"
assert evidence.retention_disclosure == "synchronous_no_async_persistence"
assert evidence.observed_content_summary == "solid red square"
assert "description: solid red square" in evidence.extracted_claims
assert "objects: red square" in evidence.extracted_claims
assert "detected_text: []" in evidence.extracted_claims
assert evidence.validation_status == "candidate_validated"
assert "research_synthesis" not in evidence.model_dump()
def test_non_json_vlm_response_gets_safe_raw_fallback() -> None:
response = SimpleNamespace(
rubric_mode="raw",
justification="",
model_id="qwen-test",
raw_scores_json="a screenshot of a pricing page with two columns",
)
evidence = visual_evidence_from_vlm_response(
response,
source_reference="fixture://pricing.png",
provider_mode="sovereign",
retention_disclosure="synchronous_no_async_persistence",
timestamp="2026-06-06T00:00:00Z",
)
assert evidence.observed_content_summary == "a screenshot of a pricing page with two columns"
assert evidence.extracted_claims == [
"raw: a screenshot of a pricing page with two columns"
]
@@ -0,0 +1,82 @@
#!/usr/bin/env python3
"""Validate the Svrnty Vision local VLM provider proof packet."""
from __future__ import annotations
import json
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
PROOF = ROOT / "docs/goal-runs/bte-product-ready-local-vlm-provider-refresh/svrnty-vision-work-008-local-vlm-provider-proof.json"
MD = ROOT / "docs/goal-runs/bte-product-ready-local-vlm-provider-refresh/svrnty-vision-work-008-local-vlm-provider-proof.md"
def require(condition: bool, errors: list[str], message: str) -> None:
if not condition:
errors.append(message)
def main() -> int:
errors: list[str] = []
proof = json.loads(PROOF.read_text(encoding="utf-8"))
md = MD.read_text(encoding="utf-8")
require(proof.get("schema") == "svrnty-vision.bte-product-ready-local-vlm-provider-proof.v1", errors, "schema")
require(proof.get("work_item_id") == "SVRNTY-VISION-WORK-008", errors, "work_item_id")
require(proof.get("route") == "svrnty-vision", errors, "route")
route = proof.get("provider_route_resolution", {})
require(route.get("registered_ollama_provider_route_found") is False, errors, "registered_route")
require(route.get("adapter_owner_route_used_for_proof") == "svrnty-vision", errors, "adapter_owner")
provider = proof.get("configured_provider", {})
require(provider.get("expected_model") == "qwen3-vl:32b", errors, "expected_model")
health = proof.get("health_proof", {})
for key in ("ollama_tags", "openai_models"):
probe = health.get(key, {})
require(probe.get("http_status") == 200, errors, f"{key}:http_status")
require(probe.get("expected_model_present") is True, errors, f"{key}:model_present")
gateway = proof.get("gateway_recheck", {})
for key in ("host_healthz", "bte_runtime_namespace_healthz"):
probe = gateway.get(key, {})
require(probe.get("http_status") == 200 and probe.get("body_status") == "ok", errors, f"{key}:healthz")
effects = proof.get("tool_effects", {})
require(effects.get("provider_health_probe_performed") is True, errors, "provider_health_probe")
require(effects.get("bte_provider_call_performed_by_bte_route") is True, errors, "bte_provider_call")
for key in (
"provider_start_or_refresh_performed",
"direct_model_inference_performed",
"gateway_runtime_started",
"profile_exposure_changed",
"mcp_registered",
"archive_delete_performed",
"raw_payload_storage_created_by_this_route",
"product_ready_claim_made",
):
require(effects.get(key) is False, errors, f"forbidden_effect:{key}")
for snippet in (
"local VLM provider health proven",
"qwen3-vl:32b",
"No direct model inference",
"No credentials",
"Product Ready claim",
):
require(snippet in md, errors, f"md_missing:{snippet}")
result = {
"ok": not errors,
"validator": "svrnty-vision-bte-local-vlm-provider-proof-v1",
"checked": [str(PROOF.relative_to(ROOT)), str(MD.relative_to(ROOT))],
"errors": errors,
}
print(json.dumps(result, indent=2))
return 0 if not errors else 1
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,71 @@
#!/usr/bin/env python3
"""Validate the Svrnty Vision BTE provider-gateway refresh proof."""
from __future__ import annotations
import json
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
PROOF = ROOT / "docs/goal-runs/bte-product-ready-provider-gateway-refresh/svrnty-vision-work-007-provider-gateway-refresh.json"
MD = ROOT / "docs/goal-runs/bte-product-ready-provider-gateway-refresh/svrnty-vision-work-007-provider-gateway-refresh.md"
def require(condition: bool, errors: list[str], message: str) -> None:
if not condition:
errors.append(message)
def main() -> int:
errors: list[str] = []
proof = json.loads(PROOF.read_text(encoding="utf-8"))
md = MD.read_text(encoding="utf-8")
require(proof.get("schema") == "svrnty-vision.bte-product-ready-provider-gateway-refresh-proof.v1", errors, "schema")
require(proof.get("work_item_id") == "SVRNTY-VISION-WORK-007", errors, "work_item_id")
require(proof.get("route") == "svrnty-vision", errors, "route")
require(proof.get("approval", {}).get("expired") is True, errors, "approval_expired")
refresh = proof.get("gateway_refresh", {})
host = refresh.get("host_port_container", {}).get("healthz", {})
bte_ns = refresh.get("bte_network_namespace_container", {}).get("healthz_from_bte_runtime_namespace", {})
require(host.get("http_status") == 200 and host.get("body_status") == "ok", errors, "host_healthz")
require(bte_ns.get("http_status") == 200 and bte_ns.get("body_status") == "ok", errors, "bte_namespace_healthz")
readyz = proof.get("downstream_bte_recheck", {}).get("readyz", {})
require(readyz.get("http_status") == 200 and readyz.get("body_status") == "Healthy", errors, "bte_readyz")
effects = proof.get("tool_effects", {})
require(effects.get("gateway_runtime_started") is True, errors, "gateway_runtime_started")
for key in (
"provider_call_from_this_route",
"profile_exposure_changed",
"mcp_registered",
"archive_delete_performed",
"raw_payload_storage_created",
"product_ready_claim_made",
):
require(effects.get(key) is False, errors, f"forbidden_effect:{key}")
for snippet in (
"No credentials or secrets",
"No Profile Exposure",
"raw payload storage",
"Product Ready claim",
"bte-work-019",
):
require(snippet in md, errors, f"md_missing:{snippet}")
result = {
"ok": not errors,
"validator": "svrnty-vision-bte-provider-gateway-refresh-v1",
"checked": [str(PROOF.relative_to(ROOT)), str(MD.relative_to(ROOT))],
"errors": errors,
}
print(json.dumps(result, indent=2))
return 0 if not errors else 1
if __name__ == "__main__":
raise SystemExit(main())
+140
View File
@@ -0,0 +1,140 @@
#!/usr/bin/env python3
"""Validate Svrnty Vision child workspace governance shell."""
from __future__ import annotations
import json
import os
import subprocess
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
sys.dont_write_bytecode = True
GENERATED_ARTIFACT_DIRS = [
"src/svrnty_vision/__pycache__",
"tools/__pycache__",
".pytest_cache",
]
REQUIRED = [
"AGENTS.md",
"INDEX.md",
"README.md",
"WORKBOARD.yaml",
"CONTEXT.md",
"docs/VISION-PACKAGE-CANDIDATE.md",
"docs/VISUAL-EVIDENCE-CONTRACT.md",
"docs/VISION-HOST-ADAPTER-CANDIDATES.md",
"candidate-manifests/vision-package-candidate.json",
"candidate-manifests/vision-tool-grants.json",
"candidate-manifests/visual-evidence-contract.json",
"outputs/2026-06-12-svrnty-vision-workboard-readiness-reconciliation.md",
"outputs/2026-06-12-svrnty-vision-workboard-readiness-reconciliation.json",
"Dockerfile",
".dockerignore",
"tools/validate_vision_package_docker_context.py",
]
VALIDATORS = [
"tools/validate_vision_package_candidate.py",
"tools/validate_vision_tool_grants.py",
"tools/validate_visual_evidence_contract.py",
"tools/validate_vision_host_adapter_candidates.py",
"tools/validate_vision_package_docker_context.py",
]
def main() -> int:
errors: list[str] = []
validator_outputs: dict[str, str] = {}
for rel in REQUIRED:
if not (ROOT / rel).exists():
errors.append(f"missing:{rel}")
checks = {
"AGENTS.md": [
"## Universal Cortex OS Agent Contract",
"## Repo-Custom Agent Contract",
"Svrnty Vision is a child-local sovereign vision HTTP gateway",
"Do not start Runtime, start Docker, build or run containers",
"child-local",
"not Cortex OS Core authority",
"python3 tools/validate_svrnty_vision_child.py",
],
"README.md": [
"## Cortex OS Boundary",
"child-local sovereign vision gateway source",
"not start Runtime, start Docker",
"call VLM",
"call Spark/ComfyUI/vLLM services",
"write durable Hindsight memory",
"operator/developer docs, not cleanup-route authorization",
],
"INDEX.md": [
"Route: `svrnty-vision`.",
"Category: child-local sovereign vision HTTP gateway and Visual Perception Package Candidate workspace.",
"Svrnty Vision owns child-local VLM, FLUX, palette, and cutout gateway source",
"Svrnty Vision is not Core authority, Seed installation authority, Runtime startup authority",
"Stage: CLEAN.",
"Clean score: 100.",
"Docker, FastAPI/Uvicorn, endpoint calls",
],
"WORKBOARD.yaml": [
"SVRNTY-VISION-WORK-001",
"SVRNTY-VISION-WORK-003",
"SVRNTY-VISION-WORK-005",
"SVRNTY-VISION-WORK-006",
"Svrnty Vision Navigation Index",
"source: INDEX.md",
"status: complete",
'owner: ""',
],
"CONTEXT.md": ["Visual Perception Package Candidate", "Research Handoff"],
"docs/VISION-PACKAGE-CANDIDATE.md": ["Research reads sources; Vision sees media", "wildcard grant"],
"docs/LEGACY-INGEST.md": ["Generated Cache Custody", "svrnty-vision-python-cache-preserved-141800"],
"outputs/2026-06-12-svrnty-vision-workboard-readiness-reconciliation.md": [
"No Core promotion",
"Seed consumption",
"Runtime start",
],
}
for rel, snippets in checks.items():
path = ROOT / rel
if path.exists():
text = path.read_text(encoding="utf-8")
for snippet in snippets:
if snippet not in text:
errors.append(f"{rel}:missing:{snippet}")
for rel in VALIDATORS:
path = ROOT / rel
if not path.exists():
errors.append(f"missing:{rel}")
continue
env = os.environ.copy()
env["PYTHONDONTWRITEBYTECODE"] = "1"
completed = subprocess.run(
[sys.executable, str(path)],
cwd=ROOT,
env=env,
text=True,
capture_output=True,
check=False,
)
validator_outputs[rel] = completed.stdout.strip()
if completed.returncode != 0:
errors.append(f"validator_failed:{rel}:{completed.stderr.strip()}")
for rel in GENERATED_ARTIFACT_DIRS:
if (ROOT / rel).exists():
errors.append(f"generated_artifact_residue:{rel}")
result = {
"ok": not errors,
"validator": "svrnty-vision-child-v1",
"checked": REQUIRED + VALIDATORS,
"errors": errors,
"warnings": [],
"validator_outputs": validator_outputs,
}
print(json.dumps(result, indent=2, sort_keys=True))
return 0 if result["ok"] else 1
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,119 @@
#!/usr/bin/env python3
"""Validate VISION host adapter candidate parity."""
from __future__ import annotations
import json
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
HOSTS = {
"claude-code": "candidate-manifests/host-adapters/vision-claude-code-adapter.json",
"codex-cli": "candidate-manifests/host-adapters/vision-codex-cli-adapter.json",
"pi-code": "candidate-manifests/host-adapters/vision-pi-code-adapter.json",
}
CURRENT_TOOLS = [
"vision.image_analyze",
"vision.image_generate",
"vision.palette_extract",
"vision.background_cutout",
]
PLANNED_TOOLS = [
"vision.ocr_read",
"vision.screenshot_observe",
"vision.browser_observe",
"vision.document_layout_read",
"vision.chart_read",
"vision.table_read",
"vision.diagram_read",
"vision.object_detect",
"vision.visual_ground",
"vision.segment",
"vision.video_read",
"vision.image_edit",
]
def main() -> int:
errors: list[str] = []
checked = list(HOSTS.values()) + ["docs/VISION-HOST-ADAPTER-CANDIDATES.md"]
manifests: dict[str, dict] = {}
for host, rel in HOSTS.items():
manifest = _read_json(ROOT / rel, errors)
if manifest:
manifests[host] = manifest
for host, manifest in manifests.items():
_expect(manifest.get("host_runtime") == host, f"host_runtime_mismatch:{host}", errors)
_expect(manifest.get("package_id") == "visual-perception-package-candidate", f"package_mismatch:{host}", errors)
_expect(manifest.get("adapter_role") == "thin_access_adapter", f"adapter_role_mismatch:{host}", errors)
_expect(manifest.get("wildcard_grant_allowed") is False, f"wildcard_grant_allowed:{host}", errors)
_expect(manifest.get("current_tool_candidates") == CURRENT_TOOLS, f"current_tools_mismatch:{host}", errors)
_expect(manifest.get("planned_tool_candidates") == PLANNED_TOOLS, f"planned_tools_mismatch:{host}", errors)
disclosures = manifest.get("disclosure_contract", {})
for key in ("provider_mode_required", "retention_required", "visual_evidence_required"):
if disclosures.get(key) is not True:
errors.append(f"disclosure_missing:{host}:{key}")
forbidden = set(manifest.get("forbidden_effects", []))
for effect in ("research_synthesis", "textual_web_search", "runtime_start", "wildcard_tool_exposure"):
if effect not in forbidden:
errors.append(f"forbidden_effect_missing:{host}:{effect}")
for key, value in manifest.get("non_authorization", {}).items():
if value is not False:
errors.append(f"non_authorization_enabled:{host}:{key}")
if len(manifests) == len(HOSTS):
current_sets = {tuple(m["current_tool_candidates"]) for m in manifests.values()}
planned_sets = {tuple(m["planned_tool_candidates"]) for m in manifests.values()}
_expect(len(current_sets) == 1, "current_tool_parity_failed", errors)
_expect(len(planned_sets) == 1, "planned_tool_parity_failed", errors)
_check_snippets(
"docs/VISION-HOST-ADAPTER-CANDIDATES.md",
["Claude Code", "Codex CLI", "Pi-Code", "no wildcard grant"],
errors,
)
result = {
"ok": not errors,
"validator": "vision-host-adapter-candidates-v1",
"checked": checked,
"errors": errors,
"warnings": [],
}
print(json.dumps(result, indent=2, sort_keys=True))
return 0 if result["ok"] else 1
def _read_json(path: Path, errors: list[str]) -> dict | None:
if not path.exists():
errors.append(f"missing:{path.relative_to(ROOT)}")
return None
try:
return json.loads(path.read_text(encoding="utf-8"))
except json.JSONDecodeError as exc:
errors.append(f"{path.relative_to(ROOT)}:json:{exc}")
return None
def _check_snippets(rel: str, snippets: list[str], errors: list[str]) -> None:
path = ROOT / rel
if not path.exists():
errors.append(f"missing:{rel}")
return
text = path.read_text(encoding="utf-8")
for snippet in snippets:
if snippet not in text:
errors.append(f"{rel}:missing:{snippet}")
def _expect(condition: bool, error: str, errors: list[str]) -> None:
if not condition:
errors.append(error)
if __name__ == "__main__":
raise SystemExit(main())
+138
View File
@@ -0,0 +1,138 @@
#!/usr/bin/env python3
"""Validate the child-local VISION package candidate manifest and docs."""
from __future__ import annotations
import json
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
CURRENT_TOOLS = [
"vision.image_analyze",
"vision.image_generate",
"vision.palette_extract",
"vision.background_cutout",
]
PLANNED_TOOLS = [
"vision.ocr_read",
"vision.screenshot_observe",
"vision.browser_observe",
"vision.document_layout_read",
"vision.chart_read",
"vision.table_read",
"vision.diagram_read",
"vision.object_detect",
"vision.visual_ground",
"vision.segment",
"vision.video_read",
"vision.image_edit",
]
ROUTE_TOOLS = {
"POST /vlm/analyze": "vision.image_analyze",
"POST /flux/render": "vision.image_generate",
"POST /palette/extract": "vision.palette_extract",
"POST /rembg/cutout": "vision.background_cutout",
}
def main() -> int:
errors: list[str] = []
required = [
"CONTEXT.md",
"docs/VISION-PACKAGE-CANDIDATE.md",
"candidate-manifests/vision-package-candidate.json",
]
for rel in required:
if not (ROOT / rel).exists():
errors.append(f"missing:{rel}")
manifest_path = ROOT / "candidate-manifests/vision-package-candidate.json"
manifest = _read_json(manifest_path, errors)
if manifest:
_expect(manifest.get("canonical_sense") == "VISION", "canonical_sense_not_vision", errors)
_expect(
manifest.get("status") == "candidate_only_not_seed_installed",
"status_not_candidate_only",
errors,
)
_expect(manifest.get("current_tool_candidates") == CURRENT_TOOLS, "current_tools_mismatch", errors)
_expect(manifest.get("planned_tool_candidates") == PLANNED_TOOLS, "planned_tools_mismatch", errors)
route_map = {
item.get("route"): item.get("tool_id")
for item in manifest.get("current_route_adapters", [])
}
_expect(route_map == ROUTE_TOOLS, "route_tool_map_mismatch", errors)
grant_policy = manifest.get("grant_policy", {})
_expect(grant_policy.get("wildcard_grant_allowed") is False, "wildcard_grant_allowed", errors)
_expect(
grant_policy.get("package_default_grants_all_tools") is False,
"package_default_grants_all_tools",
errors,
)
_expect(grant_policy.get("tool_grants_are_granular") is True, "tool_grants_not_granular", errors)
for key, value in manifest.get("non_authorization", {}).items():
if value is not False:
errors.append(f"non_authorization_enabled:{key}")
forbidden = set(manifest.get("forbidden_research_owned_capabilities", []))
for capability in ("web_search", "fetch_page", "extract_pdf", "deep_research", "research_synthesis"):
if capability not in forbidden:
errors.append(f"missing_forbidden_research_capability:{capability}")
_check_snippets(
"docs/VISION-PACKAGE-CANDIDATE.md",
[
"Research reads sources; Vision sees media",
"No Core promotion",
"wildcard grant",
"`vision.image_analyze`",
"`vision.video_read`",
],
errors,
)
_check_snippets(
"CONTEXT.md",
["Visual Evidence", "Research Handoff", "BTE Compatibility Surface"],
errors,
)
result = {
"ok": not errors,
"validator": "vision-package-candidate-v1",
"checked": required,
"errors": errors,
"warnings": [],
}
print(json.dumps(result, indent=2, sort_keys=True))
return 0 if result["ok"] else 1
def _read_json(path: Path, errors: list[str]) -> dict | None:
if not path.exists():
return None
try:
return json.loads(path.read_text(encoding="utf-8"))
except json.JSONDecodeError as exc:
errors.append(f"{path.relative_to(ROOT)}:json:{exc}")
return None
def _check_snippets(rel: str, snippets: list[str], errors: list[str]) -> None:
path = ROOT / rel
if not path.exists():
return
text = path.read_text(encoding="utf-8")
for snippet in snippets:
if snippet not in text:
errors.append(f"{rel}:missing:{snippet}")
def _expect(condition: bool, error: str, errors: list[str]) -> None:
if not condition:
errors.append(error)
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,80 @@
#!/usr/bin/env python3
"""Validate the no-live Vision package Docker build context."""
from __future__ import annotations
import json
import re
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
DOCKERFILE = ROOT / "Dockerfile"
DOCKERIGNORE = ROOT / ".dockerignore"
WORKBOARD = ROOT / "WORKBOARD.yaml"
REQUIRED_DOCKER_SNIPPETS = [
"FROM python:3.12-slim",
"SVRNTY_VISION_HOST=0.0.0.0",
"SVRNTY_VISION_PORT=8094",
"COPY pyproject.toml README.md ./",
"COPY src ./src",
"python -m pip install --no-cache-dir .",
"USER vision",
"EXPOSE 8094",
"HEALTHCHECK",
"http://127.0.0.1:8094/healthz",
'CMD ["python", "-m", "svrnty_vision.server"]',
]
REQUIRED_IGNORE = {
".git",
".venv",
"__pycache__",
"outputs",
"worktrees",
"tests",
"candidate-manifests",
}
FORBIDDEN_PATTERNS = [
re.compile(r"sk-[A-Za-z0-9_-]{20,}"),
re.compile(r"AIza[0-9A-Za-z_-]{20,}"),
re.compile(r"\b(?:ck|cs)_[0-9A-Za-z]{20,}"),
re.compile(r"(?i)\b[A-Z0-9_]*(?:PASS|PASSWORD|SECRET|TOKEN|KEY)[A-Z0-9_]*\s*=\s*[^`\s#]{8,}"),
]
def main() -> int:
errors: list[str] = []
dockerfile = DOCKERFILE.read_text(encoding="utf-8") if DOCKERFILE.is_file() else ""
dockerignore = DOCKERIGNORE.read_text(encoding="utf-8") if DOCKERIGNORE.is_file() else ""
workboard = WORKBOARD.read_text(encoding="utf-8") if WORKBOARD.is_file() else ""
if not dockerfile:
errors.append("missing:Dockerfile")
if not dockerignore:
errors.append("missing:.dockerignore")
for snippet in REQUIRED_DOCKER_SNIPPETS:
if snippet not in dockerfile:
errors.append(f"dockerfile_missing:{snippet}")
ignore_rows = {line.strip() for line in dockerignore.splitlines() if line.strip() and not line.startswith("#")}
for row in sorted(REQUIRED_IGNORE - ignore_rows):
errors.append(f"dockerignore_missing:{row}")
if "SVRNTY-VISION-WORK-004" not in workboard:
errors.append("workboard_missing:SVRNTY-VISION-WORK-004")
if "Package Docker Build Context" not in workboard:
errors.append("workboard_missing:Package Docker Build Context")
combined = "\n".join([dockerfile, dockerignore, workboard])
for pattern in FORBIDDEN_PATTERNS:
if pattern.search(combined):
errors.append(f"value_pattern_found:{pattern.pattern}")
result = {
"ok": not errors,
"validator": "vision-package-docker-context-no-live",
"checked": ["Dockerfile", ".dockerignore", "WORKBOARD.yaml"],
"errors": errors,
"warnings": [],
}
print(json.dumps(result, indent=2))
return 0 if result["ok"] else 1
if __name__ == "__main__":
raise SystemExit(main())
+103
View File
@@ -0,0 +1,103 @@
#!/usr/bin/env python3
"""Validate granular VISION tool grant candidate manifest."""
from __future__ import annotations
import json
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
CURRENT_TOOLS = [
"vision.image_analyze",
"vision.image_generate",
"vision.palette_extract",
"vision.background_cutout",
]
PLANNED_TOOLS = [
"vision.ocr_read",
"vision.screenshot_observe",
"vision.browser_observe",
"vision.document_layout_read",
"vision.chart_read",
"vision.table_read",
"vision.diagram_read",
"vision.object_detect",
"vision.visual_ground",
"vision.segment",
"vision.video_read",
"vision.image_edit",
]
def main() -> int:
errors: list[str] = []
path = ROOT / "candidate-manifests/vision-tool-grants.json"
manifest = _read_json(path, errors)
if manifest:
_expect(manifest.get("canonical_sense") == "VISION", "canonical_sense_not_vision", errors)
_expect(manifest.get("wildcard_grant_allowed") is False, "wildcard_grant_allowed", errors)
_expect(
manifest.get("package_default_grants_all_tools") is False,
"package_default_grants_all_tools",
errors,
)
current = manifest.get("current_tool_candidates", [])
current_tool_ids = [item.get("tool_id") for item in current]
_expect(current_tool_ids == CURRENT_TOOLS, "current_tool_ids_mismatch", errors)
_expect(manifest.get("planned_tool_candidates") == PLANNED_TOOLS, "planned_tools_mismatch", errors)
for item in current:
if item.get("grant_default") is not False:
errors.append(f"grant_default_enabled:{item.get('tool_id')}")
disclosures = manifest.get("required_disclosures", {})
for field in (
"producing_package_id",
"producing_tool_id",
"capability_surface",
"provider_mode",
"retention_disclosure",
"validation_status",
):
if disclosures.get(field) is not True:
errors.append(f"required_disclosure_missing:{field}")
forbidden = set(manifest.get("forbidden_effects", []))
for effect in (
"research_synthesis",
"textual_web_search",
"textual_page_fetch",
"deep_research_workflow",
"wildcard_tool_exposure",
):
if effect not in forbidden:
errors.append(f"forbidden_effect_missing:{effect}")
result = {
"ok": not errors,
"validator": "vision-tool-grants-v1",
"checked": ["candidate-manifests/vision-tool-grants.json"],
"errors": errors,
"warnings": [],
}
print(json.dumps(result, indent=2, sort_keys=True))
return 0 if result["ok"] else 1
def _read_json(path: Path, errors: list[str]) -> dict | None:
if not path.exists():
errors.append(f"missing:{path.relative_to(ROOT)}")
return None
try:
return json.loads(path.read_text(encoding="utf-8"))
except json.JSONDecodeError as exc:
errors.append(f"{path.relative_to(ROOT)}:json:{exc}")
return None
def _expect(condition: bool, error: str, errors: list[str]) -> None:
if not condition:
errors.append(error)
if __name__ == "__main__":
raise SystemExit(main())
+132
View File
@@ -0,0 +1,132 @@
#!/usr/bin/env python3
"""Validate the Visual Evidence contract and pure adapter proof."""
from __future__ import annotations
import json
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "src"))
REQUIRED_FIELDS = [
"producing_package_id",
"producing_tool_id",
"capability_surface",
"source_reference",
"provider_mode",
"retention_disclosure",
"observed_content_summary",
"extracted_claims",
"confidence",
"caveats",
"timestamp",
"validation_status",
]
def main() -> int:
errors: list[str] = []
required = [
"docs/VISUAL-EVIDENCE-CONTRACT.md",
"candidate-manifests/visual-evidence-contract.json",
"src/svrnty_vision/visual_evidence.py",
"tests/test_visual_evidence.py",
]
for rel in required:
if not (ROOT / rel).exists():
errors.append(f"missing:{rel}")
manifest = _read_json(ROOT / "candidate-manifests/visual-evidence-contract.json", errors)
if manifest:
_expect(manifest.get("required_fields") == REQUIRED_FIELDS, "required_fields_mismatch", errors)
proof = manifest.get("first_vertical_proof", {})
_expect(proof.get("source_tool_id") == "vision.image_analyze", "proof_tool_mismatch", errors)
_expect(proof.get("live_provider_call_required") is False, "live_provider_required", errors)
handoff = manifest.get("research_handoff", {})
_expect(handoff.get("research_may_consume_visual_evidence") is True, "research_handoff_missing", errors)
_expect(handoff.get("vision_may_write_research_capsules") is False, "vision_writes_capsules", errors)
_expect(handoff.get("vision_may_perform_research_synthesis") is False, "vision_research_synthesis", errors)
_check_snippets(
"docs/VISUAL-EVIDENCE-CONTRACT.md",
[
"`producing_package_id`",
"Research owns synthesis and capsule writing",
"does not call a live provider",
],
errors,
)
_validate_adapter(errors)
result = {
"ok": not errors,
"validator": "visual-evidence-contract-v1",
"checked": required,
"errors": errors,
"warnings": [],
}
print(json.dumps(result, indent=2, sort_keys=True))
return 0 if result["ok"] else 1
def _validate_adapter(errors: list[str]) -> None:
try:
from types import SimpleNamespace
from svrnty_vision.visual_evidence import visual_evidence_from_vlm_response
except Exception as exc: # pragma: no cover - validator import report
errors.append(f"adapter_import_failed:{type(exc).__name__}:{exc}")
return
response = SimpleNamespace(
rubric_mode="raw",
justification="",
model_id="qwen-test",
raw_scores_json='{"description":"solid red square","objects":["red square"],"detected_text":[]}',
)
evidence = visual_evidence_from_vlm_response(
response,
source_reference="fixture://red-square.png",
provider_mode="sovereign",
retention_disclosure="synchronous_no_async_persistence",
timestamp="2026-06-06T00:00:00Z",
)
dumped = evidence.model_dump()
for field in REQUIRED_FIELDS:
if field not in dumped:
errors.append(f"adapter_missing_field:{field}")
_expect(dumped.get("producing_package_id") == "visual-perception-package-candidate", "adapter_package_mismatch", errors)
_expect(dumped.get("producing_tool_id") == "vision.image_analyze", "adapter_tool_mismatch", errors)
_expect(dumped.get("observed_content_summary") == "solid red square", "adapter_summary_mismatch", errors)
_expect("description: solid red square" in dumped.get("extracted_claims", []), "adapter_claim_missing", errors)
def _read_json(path: Path, errors: list[str]) -> dict | None:
if not path.exists():
return None
try:
return json.loads(path.read_text(encoding="utf-8"))
except json.JSONDecodeError as exc:
errors.append(f"{path.relative_to(ROOT)}:json:{exc}")
return None
def _check_snippets(rel: str, snippets: list[str], errors: list[str]) -> None:
path = ROOT / rel
if not path.exists():
return
text = path.read_text(encoding="utf-8")
for snippet in snippets:
if snippet not in text:
errors.append(f"{rel}:missing:{snippet}")
def _expect(condition: bool, error: str, errors: list[str]) -> None:
if not condition:
errors.append(error)
if __name__ == "__main__":
raise SystemExit(main())