Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c3f6793ce1 | |||
| 2da8a6d390 | |||
| d62c5eb744 | |||
| 8045f46b06 |
@@ -0,0 +1,12 @@
|
|||||||
|
.git
|
||||||
|
.pytest_cache
|
||||||
|
.mypy_cache
|
||||||
|
.ruff_cache
|
||||||
|
.venv
|
||||||
|
__pycache__
|
||||||
|
*.pyc
|
||||||
|
outputs
|
||||||
|
worktrees
|
||||||
|
tests
|
||||||
|
docs
|
||||||
|
candidate-manifests
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# Svrnty Vision Child Workspace Rules
|
||||||
|
|
||||||
|
This workspace is child-local under the Cortex OS umbrella.
|
||||||
|
|
||||||
|
It is not Cortex OS Core authority. Promotion into Core requires a governed Core route.
|
||||||
|
|
||||||
|
## Authority Order
|
||||||
|
|
||||||
|
1. `/home/svrnty/workspaces/cortex-os/core` active SOT.
|
||||||
|
2. `/home/svrnty/workspaces/cortex-os/core/AGENTS.md`.
|
||||||
|
3. This file.
|
||||||
|
4. `README.md`, `WORKBOARD.yaml`, and local tools.
|
||||||
|
|
||||||
|
## Editing Rule
|
||||||
|
|
||||||
|
Keep work inside this workspace unless Core explicitly routes promotion.
|
||||||
|
|
||||||
|
After editing, run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 tools/validate_svrnty_vision_child.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## Protected Boundaries
|
||||||
|
|
||||||
|
- Do not mutate `../core/` from this workspace.
|
||||||
|
- Do not mutate sibling repositories.
|
||||||
|
- Do not import this workspace into Core source.
|
||||||
+37
@@ -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
@@ -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,12 +31,21 @@ 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.
|
||||||
|
|
||||||
## Run
|
## Run
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
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: jp
|
||||||
|
- 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: jp
|
||||||
|
- id: SVRNTY-VISION-WORK-003
|
||||||
|
title: Vision Package Candidate Sandcastle
|
||||||
|
status: complete
|
||||||
|
source: outputs/2026-06-12-svrnty-vision-workboard-readiness-reconciliation.md
|
||||||
|
owner: jp
|
||||||
|
- id: SVRNTY-VISION-WORK-004
|
||||||
|
title: Package Docker Build Context
|
||||||
|
status: complete
|
||||||
|
source: Dockerfile
|
||||||
|
owner: jp
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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`
|
||||||
@@ -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.
|
||||||
@@ -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,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
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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() + "..."
|
||||||
@@ -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"
|
||||||
|
]
|
||||||
Executable
+89
@@ -0,0 +1,89 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Validate Svrnty Vision child workspace governance shell."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
REQUIRED = [
|
||||||
|
"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",
|
||||||
|
"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": ["child-local", "not Cortex OS Core authority", "python3 tools/validate_svrnty_vision_child.py"],
|
||||||
|
"WORKBOARD.yaml": ["SVRNTY-VISION-WORK-001", "SVRNTY-VISION-WORK-003", "status: complete", "owner: jp"],
|
||||||
|
"CONTEXT.md": ["Visual Perception Package Candidate", "Research Handoff"],
|
||||||
|
"docs/VISION-PACKAGE-CANDIDATE.md": ["Research reads sources; Vision sees media", "wildcard grant"],
|
||||||
|
"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
|
||||||
|
completed = subprocess.run(
|
||||||
|
[sys.executable, str(path)],
|
||||||
|
cwd=ROOT,
|
||||||
|
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()}")
|
||||||
|
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())
|
||||||
@@ -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())
|
||||||
@@ -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())
|
||||||
@@ -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())
|
||||||
Reference in New Issue
Block a user