` | **Required.** Which device this is; picks the results directory |
+| `--purpose ` | `answer` (default), `task`, or `both`. The two prompts in `core/imageRecognition` |
+| `--images ` | Fixture ids, comma separated. Default: all six |
+| `--repeat ` | Samples per image, default 1. These models are not stable across samples |
+| `--cold` | Unload the model between samples. Slower, and the only way the timings mean anything |
+| `--notes "..."` | Free text stored in the record |
+| `--out ` | Results root, default `benchmarks/results` |
+
+A model that does not report Ollama's `vision` capability is refused **before** the run
+rather than scored zero, because a zero there would look like a capability result and is
+actually a configuration mistake.
+
+### The 1.1.0 baseline
+
+Machine B, `--purpose both --repeat 2 --cold`, so 24 graded descriptions per model.
+
+| Model | Params | Subject | Detail | Text read | Confused | Cold |
+|---|---|---|---|---|---|---|
+| `minicpm-v4.6` | 752M | **24/24** | 42/48 | 4/8 | 0 | 11s |
+| `qwen3.5:0.8b` | 873M | 23/24 | **46/48** | **8/8** | 0 | 13s |
+
+Two findings worth carrying forward, both of which changed the shipped code:
+
+- **`minicpm-v4.6` missed the same text every time.** All four of its text failures are
+ `car.jpg`, whose only text is a small BMW badge on the grille. It read `cebu pacific`
+ off the aeroplane, in a large clear typeface, on all four attempts. So the axis is not
+ measuring "can it read" but "how small is the text", which is exactly the limitation
+ [doc/IMAGE-RECOGNITION.md](../doc/IMAGE-RECOGNITION.md) now warns about.
+- **`qwen3.5:0.8b` denied having vision once in twenty-four.** Sent the image, reporting
+ the capability, having described the same photograph correctly one sample earlier, it
+ replied *"I cannot see this image. I am an AI model designed to process text…"*. Left
+ alone that paragraph becomes the description. `core/imageRecognition` now recognises
+ the shape and reports a failed read instead, which is how a benchmark is supposed to
+ earn its keep.
+
## The machines
The letters match the ones used throughout [doc/MODELS.md](../doc/MODELS.md):
diff --git a/benchmarks/results/B/vision__minicpm-v4.6-latest__2026-08-20T17-24-28.json b/benchmarks/results/B/vision__minicpm-v4.6-latest__2026-08-20T17-24-28.json
new file mode 100644
index 0000000..e31cba1
--- /dev/null
+++ b/benchmarks/results/B/vision__minicpm-v4.6-latest__2026-08-20T17-24-28.json
@@ -0,0 +1,617 @@
+{
+ "schemaVersion": 1,
+ "benchmark": "vision",
+ "model": "minicpm-v4.6:latest",
+ "machine": "B",
+ "params": "752.16M",
+ "capabilities": [
+ "completion",
+ "vision"
+ ],
+ "purposes": [
+ "answer",
+ "task"
+ ],
+ "repeat": 2,
+ "coldStart": true,
+ "startedAt": "2026-08-20T17:24:28.867Z",
+ "durationMs": 264238,
+ "notes": "Machine B, CPU-only, model unloaded between samples for honest timings.",
+ "summary": {
+ "graded": 24,
+ "passed": 24,
+ "failed": 0,
+ "confused": 0,
+ "detailScore": 0.875,
+ "detailGot": 42,
+ "detailMax": 48,
+ "textImages": 8,
+ "textRead": 4,
+ "errored": 0,
+ "skipped": 0,
+ "meanMs": 10809,
+ "coldMeanMs": 11039,
+ "coldSamples": 12,
+ "totalMs": 259414
+ },
+ "results": [
+ {
+ "id": "cat",
+ "file": "cat.jpg",
+ "purpose": "answer",
+ "attempt": 1,
+ "durationMs": 10644,
+ "ok": true,
+ "description": "A small tabby kitten with blue eyes lies on a green surface. It is arranged looking directly at the camera. The setting is colorful and soft, suggesting a cozy indoor or outdoor area. There is no visible text or numbers.",
+ "expected": "A tabby kitten lying down, head on a green surface, facing the camera.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "kitten"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "tabby",
+ "lies"
+ ],
+ "readText": null,
+ "textMatched": [],
+ "confused": []
+ },
+ {
+ "id": "cat",
+ "file": "cat.jpg",
+ "purpose": "answer",
+ "attempt": 2,
+ "durationMs": 10937,
+ "ok": true,
+ "description": "A small tabby kitten with blue eyes is lying on a green surface. It is arranged as if resting. The setting is colorful and soft, with a blurred background. There is no visible text or numbers.",
+ "expected": "A tabby kitten lying down, head on a green surface, facing the camera.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "kitten"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "tabby",
+ "lying"
+ ],
+ "readText": null,
+ "textMatched": [],
+ "confused": []
+ },
+ {
+ "id": "dog",
+ "file": "dog.jpg",
+ "purpose": "answer",
+ "attempt": 1,
+ "durationMs": 13393,
+ "ok": true,
+ "description": "Two fluffy puppies sit in a grassy field. They appear happy, with tongues out. The setting is colorful, with orange flowers and green grass. There is no visible text or numbers. The scene is bright and cheerful.",
+ "expected": "Two golden retriever puppies sitting in grass strewn with orange petals.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "puppies"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "two",
+ "grass"
+ ],
+ "readText": null,
+ "textMatched": [],
+ "confused": []
+ },
+ {
+ "id": "dog",
+ "file": "dog.jpg",
+ "purpose": "answer",
+ "attempt": 2,
+ "durationMs": 13444,
+ "ok": true,
+ "description": "Two fluffy puppies sit in a grassy field. They appear happy, with tongues out. The setting is colorful, with orange and yellow flowers. There is no visible text or numbers. The scene is bright and natural.",
+ "expected": "Two golden retriever puppies sitting in grass strewn with orange petals.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "puppies"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "two",
+ "field"
+ ],
+ "readText": null,
+ "textMatched": [],
+ "confused": []
+ },
+ {
+ "id": "dog-1",
+ "file": "dog-1.jpg",
+ "purpose": "answer",
+ "attempt": 1,
+ "durationMs": 10633,
+ "ok": true,
+ "description": "A happy dog lies in grass. It is arranged as if posing for a photo. The setting is colorful, with green and yellow hues. There is no visible text or numbers. The scene is bright and cheerful.",
+ "expected": "A single corgi lying in sunlit grass, wearing a green collar.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "dog"
+ ],
+ "detail": 1,
+ "detailTotal": 2,
+ "detailMatched": [
+ "grass"
+ ],
+ "readText": null,
+ "textMatched": [],
+ "confused": []
+ },
+ {
+ "id": "dog-1",
+ "file": "dog-1.jpg",
+ "purpose": "answer",
+ "attempt": 2,
+ "durationMs": 10691,
+ "ok": true,
+ "description": "A happy dog lies in grass. It is arranged as if posing for a photo. The setting is colorful, with green and yellow tones. There is no visible text or numbers. The scene is bright and natural.",
+ "expected": "A single corgi lying in sunlit grass, wearing a green collar.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "dog"
+ ],
+ "detail": 1,
+ "detailTotal": 2,
+ "detailMatched": [
+ "grass"
+ ],
+ "readText": null,
+ "textMatched": [],
+ "confused": []
+ },
+ {
+ "id": "car",
+ "file": "car.jpg",
+ "purpose": "answer",
+ "attempt": 1,
+ "durationMs": 12905,
+ "ok": true,
+ "description": "A yellow car is moving on a road. The background shows trees and a clear sky. The setting is outdoors with natural colors. There is no visible text or numbers.",
+ "expected": "A yellow BMW M4 coupe on a road, panning shot, bare trees behind.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "car"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "yellow",
+ "road"
+ ],
+ "readText": false,
+ "textMatched": [],
+ "confused": []
+ },
+ {
+ "id": "car",
+ "file": "car.jpg",
+ "purpose": "answer",
+ "attempt": 2,
+ "durationMs": 12699,
+ "ok": true,
+ "description": "A yellow car is moving on a road. The background has trees and a clear sky. The setting is outdoors with natural colors. There is no visible text or numbers.",
+ "expected": "A yellow BMW M4 coupe on a road, panning shot, bare trees behind.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "car"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "yellow",
+ "road"
+ ],
+ "readText": false,
+ "textMatched": [],
+ "confused": []
+ },
+ {
+ "id": "plane",
+ "file": "plane.jpg",
+ "purpose": "answer",
+ "attempt": 1,
+ "durationMs": 7033,
+ "ok": true,
+ "description": "A cebu pacific airplane is shown in flight. It is colorful with yellow, blue, and teal designs. The setting is a clear sky. There is no visible text or numbers. The image is credited to Jetphotos.",
+ "expected": "A Cebu Pacific Airbus A320 on approach, gear down, against grey sky.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "airplane"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "yellow",
+ "sky"
+ ],
+ "readText": true,
+ "textMatched": [
+ "cebu"
+ ],
+ "confused": []
+ },
+ {
+ "id": "plane",
+ "file": "plane.jpg",
+ "purpose": "answer",
+ "attempt": 2,
+ "durationMs": 7084,
+ "ok": true,
+ "description": "A cebu pacific airplane is shown in flight. It is colorful with yellow, blue, and teal designs. The setting is a clear sky. There is no visible text or numbers. The image is credited to Jetphotos and Berian Spotters.",
+ "expected": "A Cebu Pacific Airbus A320 on approach, gear down, against grey sky.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "airplane"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "yellow",
+ "sky"
+ ],
+ "readText": true,
+ "textMatched": [
+ "cebu"
+ ],
+ "confused": []
+ },
+ {
+ "id": "tree",
+ "file": "tree.jpg",
+ "purpose": "answer",
+ "attempt": 1,
+ "durationMs": 10697,
+ "ok": true,
+ "description": "A large tree stands in a grassy field. It is surrounded by soft colors. The setting is bright and natural. There is no visible text or numbers. The scene is vibrant with green and purple hues.",
+ "expected": "One large green tree alone in a grassy field at sunset, blue sky above.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "tree"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "field",
+ "green"
+ ],
+ "readText": null,
+ "textMatched": [],
+ "confused": []
+ },
+ {
+ "id": "tree",
+ "file": "tree.jpg",
+ "purpose": "answer",
+ "attempt": 2,
+ "durationMs": 10874,
+ "ok": true,
+ "description": "A large tree stands in a green field. It is surrounded by grass and small purple flowers. The setting is colorful with blue sky and golden light. There is no visible text or numbers.",
+ "expected": "One large green tree alone in a grassy field at sunset, blue sky above.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "tree"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "field",
+ "sky"
+ ],
+ "readText": null,
+ "textMatched": [],
+ "confused": []
+ },
+ {
+ "id": "cat",
+ "file": "cat.jpg",
+ "purpose": "task",
+ "attempt": 1,
+ "durationMs": 10995,
+ "ok": true,
+ "description": "A close-up photo of a tabby kitten with blue eyes on green fabric. Text is unreadable. Layout is natural, with focus on the kitten's face. Colors are soft and natural. No code or error messages present.",
+ "expected": "A tabby kitten lying down, head on a green surface, facing the camera.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "kitten"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "tabby",
+ "green"
+ ],
+ "readText": null,
+ "textMatched": [],
+ "confused": []
+ },
+ {
+ "id": "cat",
+ "file": "cat.jpg",
+ "purpose": "task",
+ "attempt": 2,
+ "durationMs": 10865,
+ "ok": true,
+ "description": "A close-up photo of a tabby kitten with blue eyes on green fabric. Text is unreadable. Layout is natural, with focus on the kitten's face. Colors are soft, with clear contrast between fur and background.",
+ "expected": "A tabby kitten lying down, head on a green surface, facing the camera.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "kitten"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "tabby",
+ "green"
+ ],
+ "readText": null,
+ "textMatched": [],
+ "confused": []
+ },
+ {
+ "id": "dog",
+ "file": "dog.jpg",
+ "purpose": "task",
+ "attempt": 1,
+ "durationMs": 13751,
+ "ok": true,
+ "description": "A screenshot of two playful puppies in a grassy field with orange flowers. Text is unreadable. Layout is natural, with puppies centered, flowers scattered. Colors are soft, with green and orange tones. No code or labels present.",
+ "expected": "Two golden retriever puppies sitting in grass strewn with orange petals.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "puppies"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "two",
+ "field"
+ ],
+ "readText": null,
+ "textMatched": [],
+ "confused": []
+ },
+ {
+ "id": "dog",
+ "file": "dog.jpg",
+ "purpose": "task",
+ "attempt": 2,
+ "durationMs": 13812,
+ "ok": true,
+ "description": "A photo of two playful puppies in a grassy field with orange flowers. Text is unreadable. Layout is natural, with puppies and flowers. Colors are soft, with green grass and bright orange petals. Spacing is deliberate, emphasizing the subjects.",
+ "expected": "Two golden retriever puppies sitting in grass strewn with orange petals.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "puppies"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "two",
+ "grass"
+ ],
+ "readText": null,
+ "textMatched": [],
+ "confused": []
+ },
+ {
+ "id": "dog-1",
+ "file": "dog-1.jpg",
+ "purpose": "task",
+ "attempt": 1,
+ "durationMs": 10928,
+ "ok": true,
+ "description": "A screenshot of a cheerful corgi in grass. The image is natural lighting. Text is unreadable. Layout is focused on the dog. Colors are warm and soft. Spacing is deliberate. No code or error present.",
+ "expected": "A single corgi lying in sunlit grass, wearing a green collar.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "dog"
+ ],
+ "detail": 1,
+ "detailTotal": 2,
+ "detailMatched": [
+ "grass"
+ ],
+ "readText": null,
+ "textMatched": [],
+ "confused": []
+ },
+ {
+ "id": "dog-1",
+ "file": "dog-1.jpg",
+ "purpose": "task",
+ "attempt": 2,
+ "durationMs": 914,
+ "ok": true,
+ "description": "A screenshot of a cheerful corgi in grass. The image is natural lighting. Text is unreadable. Layout is focused on the dog. Colors are warm and soft. Spacing is deliberate. No code or labels visible.",
+ "expected": "A single corgi lying in sunlit grass, wearing a green collar.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "dog"
+ ],
+ "detail": 1,
+ "detailTotal": 2,
+ "detailMatched": [
+ "grass"
+ ],
+ "readText": null,
+ "textMatched": [],
+ "confused": []
+ },
+ {
+ "id": "car",
+ "file": "car.jpg",
+ "purpose": "task",
+ "attempt": 1,
+ "durationMs": 10406,
+ "ok": true,
+ "description": "Screenshot of a yellow car in motion. Visible text and numbers are unreadable. Layout includes car, road, trees. Colors are distinct, sizes vary for emphasis. Spacing is deliberate.",
+ "expected": "A yellow BMW M4 coupe on a road, panning shot, bare trees behind.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "car"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "yellow",
+ "road"
+ ],
+ "readText": false,
+ "textMatched": [],
+ "confused": []
+ },
+ {
+ "id": "car",
+ "file": "car.jpg",
+ "purpose": "task",
+ "attempt": 2,
+ "durationMs": 14314,
+ "ok": true,
+ "description": "A screenshot of a yellow sports car in motion. The image is dynamic, with blurred background trees. Text and numbers are unreadable. Layout includes car, road, and forest. Colours are vibrant, sizes varied for emphasis. Spacing is deliberate. No code or error messages present.",
+ "expected": "A yellow BMW M4 coupe on a road, panning shot, bare trees behind.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "car"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "yellow",
+ "road"
+ ],
+ "readText": false,
+ "textMatched": [],
+ "confused": []
+ },
+ {
+ "id": "plane",
+ "file": "plane.jpg",
+ "purpose": "task",
+ "attempt": 1,
+ "durationMs": 8668,
+ "ok": true,
+ "description": "A screenshot of a cebu pacific airplane. Text \"cebu pacific\" is visible. Numbers like \"RP-C407\" are present. Layout includes panels and buttons. Colors are deliberate, with yellow, blue, and white. Spacing is clear and structured.",
+ "expected": "A Cebu Pacific Airbus A320 on approach, gear down, against grey sky.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "airplane"
+ ],
+ "detail": 1,
+ "detailTotal": 2,
+ "detailMatched": [
+ "yellow"
+ ],
+ "readText": true,
+ "textMatched": [
+ "cebu"
+ ],
+ "confused": []
+ },
+ {
+ "id": "plane",
+ "file": "plane.jpg",
+ "purpose": "task",
+ "attempt": 2,
+ "durationMs": 8936,
+ "ok": true,
+ "description": "Screenshot of an aircraft. Diagram showing layout. Text \"cebu pacific\" visible. Number \"RP-C407\" on tail. Colors yellow, blue, white, with wave patterns. Button not present. Field not visible. Panel not shown. Spacing deliberate. No code or error.",
+ "expected": "A Cebu Pacific Airbus A320 on approach, gear down, against grey sky.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "aircraft"
+ ],
+ "detail": 1,
+ "detailTotal": 2,
+ "detailMatched": [
+ "yellow"
+ ],
+ "readText": true,
+ "textMatched": [
+ "cebu"
+ ],
+ "confused": []
+ },
+ {
+ "id": "tree",
+ "file": "tree.jpg",
+ "purpose": "task",
+ "attempt": 1,
+ "durationMs": 12416,
+ "ok": true,
+ "description": "A landscape screenshot of a tree in a field. Text is unreadable. Layout includes tree, grass, sky, and field. Colors are natural, with green, blue, and yellow tones. Spacing is deliberate, emphasizing natural elements.",
+ "expected": "One large green tree alone in a grassy field at sunset, blue sky above.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "tree"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "field",
+ "sky"
+ ],
+ "readText": null,
+ "textMatched": [],
+ "confused": []
+ },
+ {
+ "id": "tree",
+ "file": "tree.jpg",
+ "purpose": "task",
+ "attempt": 2,
+ "durationMs": 12375,
+ "ok": true,
+ "description": "A landscape screenshot of a tree in a field. Text is unreadable. Layout includes tree, grass, sky, and horizon. Colors are natural, with green, blue, and yellow tones. Spacing is deliberate, emphasizing depth and contrast.",
+ "expected": "One large green tree alone in a grassy field at sunset, blue sky above.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "tree"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "field",
+ "sky"
+ ],
+ "readText": null,
+ "textMatched": [],
+ "confused": []
+ }
+ ]
+}
diff --git a/benchmarks/results/B/vision__qwen3.5-0.8b__2026-08-20T17-28-53.json b/benchmarks/results/B/vision__qwen3.5-0.8b__2026-08-20T17-28-53.json
new file mode 100644
index 0000000..429dba2
--- /dev/null
+++ b/benchmarks/results/B/vision__qwen3.5-0.8b__2026-08-20T17-28-53.json
@@ -0,0 +1,628 @@
+{
+ "schemaVersion": 1,
+ "benchmark": "vision",
+ "model": "qwen3.5:0.8b",
+ "machine": "B",
+ "params": "873.44M",
+ "capabilities": [
+ "vision",
+ "completion",
+ "tools",
+ "thinking"
+ ],
+ "purposes": [
+ "answer",
+ "task"
+ ],
+ "repeat": 2,
+ "coldStart": true,
+ "startedAt": "2026-08-20T17:28:53.238Z",
+ "durationMs": 309927,
+ "notes": "Machine B, CPU-only, model unloaded between samples for honest timings.",
+ "summary": {
+ "graded": 24,
+ "passed": 23,
+ "failed": 1,
+ "confused": 0,
+ "detailScore": 0.958,
+ "detailGot": 46,
+ "detailMax": 48,
+ "textImages": 8,
+ "textRead": 8,
+ "errored": 0,
+ "skipped": 0,
+ "meanMs": 12837,
+ "coldMeanMs": 12853,
+ "coldSamples": 12,
+ "totalMs": 308091
+ },
+ "results": [
+ {
+ "id": "cat",
+ "file": "cat.jpg",
+ "purpose": "answer",
+ "attempt": 1,
+ "durationMs": 8017,
+ "ok": true,
+ "description": "The main subject is a small kitten with striking blue eyes and tabby markings on its fur. It has long white whiskers and pink nose, giving it a curious expression as it lies down on a soft green surface. The kitten’s ears are perked up, suggesting attentiveness or alertness. Its body stretches out to the right side of the frame, while its head rests prominently in the foreground.\n\nThe setting appears to be indoors, with a blurred background that suggests an indoor environment like a living room or bedroom. The colors are muted but vibrant: greens dominate the lower portion of the image, contrasting with the brownish-gray tones of the kitten’s fur. There is no text visible in the frame.",
+ "expected": "A tabby kitten lying down, head on a green surface, facing the camera.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "kitten"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "tabby",
+ "rest"
+ ],
+ "readText": null,
+ "textMatched": [],
+ "confused": []
+ },
+ {
+ "id": "cat",
+ "file": "cat.jpg",
+ "purpose": "answer",
+ "attempt": 2,
+ "durationMs": 7827,
+ "ok": true,
+ "description": "The main subject is a curious kitten with striking blue eyes and tabby markings on its fur. It lies down on a soft green surface, possibly a blanket or mat, with its head resting gently against the edge. Its ears are perked up, and it appears to be looking directly at the viewer with an attentive expression. The background is softly blurred, drawing attention to the kitten's face and whiskers.\n\nThe kitten is lying down on a green surface, likely a blanket or mat. It has blue eyes and tabby markings on its fur. Its ears are perked up, and it appears to be looking directly at the viewer with an attentive expression. The background is softly blurred, drawing attention to the kitten's face and whiskers.",
+ "expected": "A tabby kitten lying down, head on a green surface, facing the camera.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "kitten"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "tabby",
+ "lying"
+ ],
+ "readText": null,
+ "textMatched": [],
+ "confused": []
+ },
+ {
+ "id": "dog",
+ "file": "dog.jpg",
+ "purpose": "answer",
+ "attempt": 1,
+ "durationMs": 23385,
+ "ok": true,
+ "description": "The main subject consists of two golden retriever puppies sitting side by side on a grassy field scattered with fallen autumn leaves. The puppy on the left has its tongue hanging out in a happy expression, while the one on the right is panting with its mouth open wide. Both dogs appear joyful and energetic, embodying innocence and playfulness.\n\nThey are arranged closely together, facing forward toward the viewer, creating an intimate and friendly composition. The background is softly blurred, emphasizing the subjects and adding depth to the image. The colors are warm and natural, dominated by greens from the grass and leaves, with golden hues reflecting the puppies' fur tones. There are no visible text or numbers in the frame.",
+ "expected": "Two golden retriever puppies sitting in grass strewn with orange petals.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "dog"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "two",
+ "grass"
+ ],
+ "readText": null,
+ "textMatched": [],
+ "confused": []
+ },
+ {
+ "id": "dog",
+ "file": "dog.jpg",
+ "purpose": "answer",
+ "attempt": 2,
+ "durationMs": 23090,
+ "ok": true,
+ "description": "The image features two golden retriever puppies sitting side by side on a grassy field scattered with fallen autumn leaves. The puppy on the left has its tongue out in a happy expression, while the one on the right also panting happily. Both dogs look joyful and energetic, embodying innocence and playfulness. The background is softly blurred green, suggesting an open outdoor space like a park or meadow during fall season. Colors dominate with warm yellows, oranges, and soft greens from the grass and foliage. There are no visible text or numbers in the frame.",
+ "expected": "Two golden retriever puppies sitting in grass strewn with orange petals.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "dog"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "two",
+ "grass"
+ ],
+ "readText": null,
+ "textMatched": [],
+ "confused": []
+ },
+ {
+ "id": "dog-1",
+ "file": "dog-1.jpg",
+ "purpose": "answer",
+ "attempt": 1,
+ "durationMs": 7542,
+ "ok": true,
+ "description": "The main subject is a cheerful Corgi lying on the grass, its ears perked up and mouth open in a happy expression. It wears a green collar with a buckle visible. The setting appears to be an outdoor park or field during the day, bathed in warm sunlight that creates a soft glow around the dog. The background features blurred greenery, suggesting trees or bushes, enhancing the peaceful atmosphere. Colors dominate the scene: vibrant greens from the grass and foliage, warm yellows and oranges from the sunlight, and the white fur of the Corgi. There are no text, numbers, or labels visible in the image.",
+ "expected": "A single corgi lying in sunlit grass, wearing a green collar.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "dog"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "grass",
+ "collar"
+ ],
+ "readText": null,
+ "textMatched": [],
+ "confused": []
+ },
+ {
+ "id": "dog-1",
+ "file": "dog-1.jpg",
+ "purpose": "answer",
+ "attempt": 2,
+ "durationMs": 7267,
+ "ok": true,
+ "description": "The main subject is a cheerful Corgi dog lying on green grass with its tongue out in a happy expression. The setting appears to be outdoors during the day, bathed in warm sunlight that creates a soft glow around the scene. The background features blurred trees and foliage, suggesting a natural park or garden environment. The colors are vibrant greens from the grass, tan and white fur on the dog, and golden hues from the sunlight. There is no visible text or numbers within the image itself.",
+ "expected": "A single corgi lying in sunlit grass, wearing a green collar.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "dog"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "grass",
+ "white"
+ ],
+ "readText": null,
+ "textMatched": [],
+ "confused": []
+ },
+ {
+ "id": "car",
+ "file": "car.jpg",
+ "purpose": "answer",
+ "attempt": 1,
+ "durationMs": 24097,
+ "ok": true,
+ "description": "The main subject is a vibrant yellow BMW M4 coupe in motion on a paved road. It features sleek headlights, distinctive kidney grilles, and aerodynamic design elements like side skirts and a front splitter. The car's glossy paint reflects sunlight, emphasizing its sporty appeal.\n\nIt appears to be driving at speed, as indicated by the blurred background trees and road markings. This creates a sense of dynamism and performance.\n\nThe setting is an open road with autumnal trees in the distance, suggesting a rural or suburban environment. The colors are dominated by the car's bright yellow, contrasting with the muted greens and browns of the foliage. A clear blue sky adds to the serene yet dynamic atmosphere.\n\nThere is no visible text or numbers within the image itself.",
+ "expected": "A yellow BMW M4 coupe on a road, panning shot, bare trees behind.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "car"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "yellow",
+ "road"
+ ],
+ "readText": true,
+ "textMatched": [
+ "bmw"
+ ],
+ "confused": []
+ },
+ {
+ "id": "car",
+ "file": "car.jpg",
+ "purpose": "answer",
+ "attempt": 2,
+ "durationMs": 23611,
+ "ok": true,
+ "description": "The main subject is a vibrant yellow BMW M4 coupe in motion on a paved road. It features sleek headlights, distinctive kidney grilles, and sporty alloy wheels. The car appears to be driving at speed, as indicated by the blurred background trees and road surface. This setup suggests an action shot capturing the vehicle's dynamic movement.\n\nThe setting is outdoors with a backdrop of tall, leafless trees under a clear sky. The colors are dominated by the bright yellow of the car against the muted greens and browns of the forest. There are no visible text or numbers in the image.",
+ "expected": "A yellow BMW M4 coupe on a road, panning shot, bare trees behind.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "car"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "yellow",
+ "road"
+ ],
+ "readText": true,
+ "textMatched": [
+ "bmw"
+ ],
+ "confused": []
+ },
+ {
+ "id": "plane",
+ "file": "plane.jpg",
+ "purpose": "answer",
+ "attempt": 1,
+ "durationMs": 5359,
+ "ok": true,
+ "description": "The main subject is a Cebu Pacific airplane in flight against a clear sky backdrop. The aircraft features distinctive yellow and blue livery with the airline's name prominently displayed on its fuselage. Its wings are extended, suggesting it may be preparing for landing or has just taken off. The tail fin displays a vibrant gradient of yellow to blue, complemented by teal accents along the side. Text visible includes \"CP-4407\" near the rear of the plane and \"Image Copyright Javier Rodriguez - BERIAN SPOTTERS\" at the bottom right corner.",
+ "expected": "A Cebu Pacific Airbus A320 on approach, gear down, against grey sky.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "plane"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "yellow",
+ "sky"
+ ],
+ "readText": true,
+ "textMatched": [
+ "cebu"
+ ],
+ "confused": []
+ },
+ {
+ "id": "plane",
+ "file": "plane.jpg",
+ "purpose": "answer",
+ "attempt": 2,
+ "durationMs": 5558,
+ "ok": true,
+ "description": "The main subject is a Cebu Pacific airplane in flight against a clear sky backdrop. It appears to be flying at low altitude with landing gear deployed. The aircraft features distinctive branding with the airline name prominently displayed on its fuselage in blue lettering. The livery includes yellow and teal accents along the tail fin, wings, and engine area. Text visible on the side reads \"RP-C407\". The overall composition highlights the plane's design and branding while emphasizing its operational status within the aviation industry.",
+ "expected": "A Cebu Pacific Airbus A320 on approach, gear down, against grey sky.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "plane"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "yellow",
+ "sky"
+ ],
+ "readText": true,
+ "textMatched": [
+ "cebu"
+ ],
+ "confused": []
+ },
+ {
+ "id": "tree",
+ "file": "tree.jpg",
+ "purpose": "answer",
+ "attempt": 1,
+ "durationMs": 7959,
+ "ok": true,
+ "description": "The main subject is a majestic oak tree with sprawling branches and lush green foliage, standing prominently in the foreground of a serene landscape. It stands alone amidst vibrant fields of wildflowers, creating a striking contrast between its ancient form and the natural beauty surrounding it. The tree casts long shadows across the grassy field, indicating that the sun is low on the horizon, suggesting either sunrise or sunset.\n\nThe setting features expansive open fields bathed in warm sunlight, with distant hills faintly visible under a clear blue sky. The colors are vivid: deep blues of the sky, rich greens of the tree and grass, golden hues from the sunlight, and delicate purple blooms scattered throughout the meadow. There is no text or numbers present in the image.",
+ "expected": "One large green tree alone in a grassy field at sunset, blue sky above.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "tree"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "field",
+ "sky"
+ ],
+ "readText": null,
+ "textMatched": [],
+ "confused": []
+ },
+ {
+ "id": "tree",
+ "file": "tree.jpg",
+ "purpose": "answer",
+ "attempt": 2,
+ "durationMs": 8064,
+ "ok": true,
+ "description": "The main subject is a majestic oak tree with sprawling branches, standing alone in a vibrant field bathed in golden light. It appears to be thriving, its leaves glistening under the sun's rays. The tree dominates the foreground, while the expansive grassy landscape stretches out behind it, dotted with wildflowers and other small plants.\n\nThe setting is a serene countryside scene, characterized by lush greenery and open fields. The sky above is a brilliant blue, transitioning to a soft yellow near the horizon where the sun appears to be setting or rising. This warm light casts long shadows across the field, enhancing the tranquil atmosphere of the moment.\n\nThere are no visible text, numbers, or labels in the image. The colors are vivid: deep blues, lush greens, and golden yellows create a harmonious and peaceful composition.",
+ "expected": "One large green tree alone in a grassy field at sunset, blue sky above.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "tree"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "field",
+ "sky"
+ ],
+ "readText": null,
+ "textMatched": [],
+ "confused": []
+ },
+ {
+ "id": "cat",
+ "file": "cat.jpg",
+ "purpose": "task",
+ "attempt": 1,
+ "durationMs": 9362,
+ "ok": true,
+ "description": "The image captures a close-up portrait of a young tabby kitten with striking blue eyes and white markings on its muzzle. The kitten is lying down, resting its head on a soft, textured green surface that appears to be a rug or blanket. Its ears are perked up, and it gazes directly at the camera with an alert expression.\n\nThe background is softly blurred, drawing focus to the kitten's face. This shallow depth of field creates a sense of intimacy and immediacy. The lighting is natural and diffused, highlighting the texture of the fur and the vibrant blue of the eyes.\n\nIn terms of layout, the kitten dominates the frame from left to right. Its head occupies the upper portion, while its body extends diagonally into the lower right corner. The green surface serves as a base for the composition, anchoring the subject below. There are no buttons or panels visible; instead, the focus is entirely on the animal's features and expression.\n\nThe color palette consists primarily of earthy tones—browns, greys, and greens—with a pop of blue from the kitten’s eyes. The green surface provides a contrasting base that makes the kitten stand out. The overall composition feels calm and serene, capturing a quiet moment in nature.",
+ "expected": "A tabby kitten lying down, head on a green surface, facing the camera.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "kitten"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "tabby",
+ "lying"
+ ],
+ "readText": null,
+ "textMatched": [],
+ "confused": []
+ },
+ {
+ "id": "cat",
+ "file": "cat.jpg",
+ "purpose": "task",
+ "attempt": 2,
+ "durationMs": 7480,
+ "ok": true,
+ "description": "I cannot see this image. I am an AI model designed to process text and provide information, but I do not have the ability to view or interpret visual content like screenshots, photos, or UI elements. Therefore, I cannot identify what kind of image this is, extract any text or code from it, describe its layout, or analyze its colors and spacing.\n\nIf you can provide the text describing this image (e.g., a screenshot description), I would be happy to help you with that.",
+ "expected": "A tabby kitten lying down, head on a green surface, facing the camera.",
+ "passed": false,
+ "subject": false,
+ "subjectMatched": [],
+ "detail": 0,
+ "detailTotal": 2,
+ "detailMatched": [],
+ "readText": null,
+ "textMatched": [],
+ "confused": []
+ },
+ {
+ "id": "dog",
+ "file": "dog.jpg",
+ "purpose": "task",
+ "attempt": 1,
+ "durationMs": 24525,
+ "ok": true,
+ "description": "The image is a photograph of two golden retriever puppies sitting on grass scattered with fallen autumn leaves. The puppy on the left has its tongue hanging out, while the one on the right is panting with its mouth open. Both dogs are looking towards the camera. The background is a soft-focus green field, and the foreground features vibrant orange and yellow flowers.\n\nThe layout places the two puppies in the center of the frame, flanking each other. They occupy the middle ground, while the grass and flowers fill the foreground. The background extends to the edges of the image, creating depth through blur. There are no buttons, fields, panels, or UI elements visible. No text, code, numbers, or labels appear in the image.\n\nThe dominant colors are shades of green (background), golden yellow (puppies and flowers), and orange (leaves). The puppies' fur is a light golden color, contrasting with the darker brown leaves. The overall composition suggests a peaceful outdoor scene, likely taken during fall.",
+ "expected": "Two golden retriever puppies sitting in grass strewn with orange petals.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "dog"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "two",
+ "grass"
+ ],
+ "readText": null,
+ "textMatched": [],
+ "confused": []
+ },
+ {
+ "id": "dog",
+ "file": "dog.jpg",
+ "purpose": "task",
+ "attempt": 2,
+ "durationMs": 24630,
+ "ok": true,
+ "description": "The image is a photograph of two golden retriever puppies sitting on the grass in a field with scattered orange and yellow flowers. The background is a soft, out-of-focus green. The dogs are positioned side by side, facing forward but looking slightly to the right. Both have their tongues hanging out, appearing happy or panting. The lighting is bright and natural, suggesting daytime.\n\nThe layout consists of two main subjects (the puppies) in the foreground, with a blurred background behind them. There are no buttons, fields, panels, or UI elements visible. No text, code, numbers, or labels appear anywhere in the image. The colors are primarily green, yellow, orange, and brown tones.\n\nThe image is a photo of two golden retriever puppies sitting on the grass in a field with scattered flowers.",
+ "expected": "Two golden retriever puppies sitting in grass strewn with orange petals.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "dog"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "two",
+ "grass"
+ ],
+ "readText": null,
+ "textMatched": [],
+ "confused": []
+ },
+ {
+ "id": "dog-1",
+ "file": "dog-1.jpg",
+ "purpose": "task",
+ "attempt": 1,
+ "durationMs": 7391,
+ "ok": true,
+ "description": "The image is a screenshot of a photo of a Corgi dog lying on grass in the foreground, with a blurred background of trees and foliage. The text \"Corgi\" appears above the dog's head. There are no code snippets or UI elements visible. The layout shows the dog centered in the frame, with its ears perked up and mouth slightly open, conveying a happy expression. The lighting is soft and warm, suggesting it was taken during the day.",
+ "expected": "A single corgi lying in sunlit grass, wearing a green collar.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "dog"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "grass",
+ "ears"
+ ],
+ "readText": null,
+ "textMatched": [],
+ "confused": []
+ },
+ {
+ "id": "dog-1",
+ "file": "dog-1.jpg",
+ "purpose": "task",
+ "attempt": 2,
+ "durationMs": 7428,
+ "ok": true,
+ "description": "The image shows a Corgi lying on green grass with its tongue out in a happy expression. The background is blurred, suggesting depth of field. The dog has a collar and large ears. There are no buttons, fields, panels, or UI elements visible. No text, code, numbers, or labels appear anywhere in the image.",
+ "expected": "A single corgi lying in sunlit grass, wearing a green collar.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "dog"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "grass",
+ "collar"
+ ],
+ "readText": null,
+ "textMatched": [],
+ "confused": []
+ },
+ {
+ "id": "car",
+ "file": "car.jpg",
+ "purpose": "task",
+ "attempt": 1,
+ "durationMs": 23860,
+ "ok": true,
+ "description": "The image is a photograph of a yellow BMW M4 coupe in motion on a paved road, captured with a panning shutter effect to emphasize speed. The car features a prominent front grille, sleek headlights, and aerodynamic bodywork. Trees and foliage are blurred in the background, indicating movement. The scene is well-lit, likely during daylight hours.\n\nThe layout centers the car prominently in the frame, with the road stretching into the distance. The composition uses motion blur to convey speed, while the car's details remain sharp. No text, code, or labels are present.",
+ "expected": "A yellow BMW M4 coupe on a road, panning shot, bare trees behind.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "car"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "yellow",
+ "road"
+ ],
+ "readText": true,
+ "textMatched": [
+ "bmw"
+ ],
+ "confused": []
+ },
+ {
+ "id": "car",
+ "file": "car.jpg",
+ "purpose": "task",
+ "attempt": 2,
+ "durationMs": 24606,
+ "ok": true,
+ "description": "The image is a photograph of a yellow BMW M4 Coupe in motion on a paved road, with blurred trees and sky in the background indicating speed. The car features a black front grille, sleek headlights, and silver multi-spoke wheels. It appears to be a screenshot from a video or photo album capturing the vehicle's dynamic performance.\n\nThe layout places the car centrally in the frame, angled slightly towards the left. The road occupies the lower portion of the image, while the background consists of out-of-focus trees and sky. There are no buttons, fields, panels, or UI elements visible. No text, code, numbers, or labels appear anywhere in the image.\n\nThe colors are predominantly yellow for the car, black for the grille and mirrors, silver for wheels, and muted tones for the background foliage and sky. The spacing is natural, with the road stretching horizontally across the bottom and the tree line forming a backdrop behind the vehicle. The overall composition emphasizes motion and speed through the use of blur in the background elements.",
+ "expected": "A yellow BMW M4 coupe on a road, panning shot, bare trees behind.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "car"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "yellow",
+ "road"
+ ],
+ "readText": true,
+ "textMatched": [
+ "bmw"
+ ],
+ "confused": []
+ },
+ {
+ "id": "plane",
+ "file": "plane.jpg",
+ "purpose": "task",
+ "attempt": 1,
+ "durationMs": 5188,
+ "ok": true,
+ "description": "The image is a screenshot of a Cebu Pacific airplane in flight against a gray sky. The text on the fuselage reads \"cebu pacific\" in blue letters. The aircraft has landing gear deployed, and its registration number appears to be RP-C4407. The layout groups the text on the fuselage with the plane's body and wings. The colors are yellow, white, and blue. The image is a photo of an airplane.",
+ "expected": "A Cebu Pacific Airbus A320 on approach, gear down, against grey sky.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "plane"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "yellow",
+ "sky"
+ ],
+ "readText": true,
+ "textMatched": [
+ "cebu"
+ ],
+ "confused": []
+ },
+ {
+ "id": "plane",
+ "file": "plane.jpg",
+ "purpose": "task",
+ "attempt": 2,
+ "durationMs": 5313,
+ "ok": true,
+ "description": "The image is a screenshot of a commercial airplane in flight against a gray sky. The text on the fuselage reads \"cebu-pacific\" in blue letters. The aircraft has yellow and blue livery with a wave design. The landing gear is deployed, indicating the plane is either taking off or landing. The photo captures the entire aircraft from front to back.",
+ "expected": "A Cebu Pacific Airbus A320 on approach, gear down, against grey sky.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "plane"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "yellow",
+ "sky"
+ ],
+ "readText": true,
+ "textMatched": [
+ "cebu"
+ ],
+ "confused": []
+ },
+ {
+ "id": "tree",
+ "file": "tree.jpg",
+ "purpose": "task",
+ "attempt": 1,
+ "durationMs": 7550,
+ "ok": true,
+ "description": "1. A photo of a large tree in a field under a blue sky.\n2. \"large tree\", \"field\", \"blue sky\", \"tree trunk\".\n3. The tree is the main subject, with branches spreading out to the left and right.\n4. The tree is positioned on the left side of the image.\n5. The grassy field extends from the bottom to the horizon line.\n6. The sky takes up the top half of the image.",
+ "expected": "One large green tree alone in a grassy field at sunset, blue sky above.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "tree"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "field",
+ "sky"
+ ],
+ "readText": null,
+ "textMatched": [],
+ "confused": []
+ },
+ {
+ "id": "tree",
+ "file": "tree.jpg",
+ "purpose": "task",
+ "attempt": 2,
+ "durationMs": 8982,
+ "ok": true,
+ "description": "1. A photograph of a solitary tree in a field under a clear blue sky.\n2. The tree has green leaves and a thick trunk.\n3. The image is a screenshot of a landscape scene.\n4. Colors are vibrant greens, blues, and yellows. Spacing is natural with no deliberate borders or buttons.",
+ "expected": "One large green tree alone in a grassy field at sunset, blue sky above.",
+ "passed": true,
+ "subject": true,
+ "subjectMatched": [
+ "tree"
+ ],
+ "detail": 2,
+ "detailTotal": 2,
+ "detailMatched": [
+ "field",
+ "sky"
+ ],
+ "readText": null,
+ "textMatched": [],
+ "confused": []
+ }
+ ]
+}
diff --git a/doc/CHOOSING-A-MODEL.md b/doc/CHOOSING-A-MODEL.md
new file mode 100644
index 0000000..605b2ae
--- /dev/null
+++ b/doc/CHOOSING-A-MODEL.md
@@ -0,0 +1,60 @@
+# Choosing a model
+
+
+Model names are confusing. Here is a plain-language ranking, based on
+[real measurements](https://github.com/jaymar921/HirayaCoder/blob/main/doc/MODELS.md)
+rather than on the descriptions.
+
+| Model | Download | Needs | Verdict |
+|---|---|---|---|
+| `gemma4:e2b` | 7.2 GB | 16 GB RAM | **Best starting point.** Fastest to a correct answer |
+| `qwen3.5:4b` | 3.4 GB | 8–16 GB RAM | Good, smaller download |
+| `llama3.2:1b` | 1.3 GB | 8 GB RAM | For low-spec machines. Simple single-file jobs only |
+| `gemma4:e4b` | 9.6 GB | 32 GB RAM or a Mac | The strongest, if you have room |
+| `qwen3.5:0.8b` | 1.0 GB | — | Last resort. Writes files correctly; does not finish an app |
+
+## What a very small model will and will not do
+
+Worth setting expectations, because the honest answer is more useful than the
+encouraging one. Measured on a 98-line brief for a complete React app:
+
+- **A 0.8B model now scaffolds the project, writes every file the request named, and
+ installs the dependencies.** In the previous release the same model spent its whole
+ session listing the directory and wrote nothing.
+- **It does not get the app working.** The build still fails, and no model at this size
+ finished the app in any of our runs.
+
+So a very small model is genuinely useful for *"write me this file"* and for the parts of
+a big request that are mechanical. Handing it a whole application and walking away is not
+something we can recommend yet, and the numbers behind that are in
+[the evaluation notes](https://github.com/jaymar921/HirayaCoder/blob/main/doc/SESSION-ANALYSIS-0.9.0.md).
+
+Switch models any time from the dropdown at the top of the chat — no reinstall needed.
+
+**One thing that surprises people:** a graphics card makes it *faster*, not *smarter*.
+A bigger model gives better answers; a better GPU gives the same answer sooner.
+
+---
+
+---
+
+## If you want to attach images
+
+Reading a picture needs a model with the **vision** capability, and it does not have to
+be the model you code with. See
+[IMAGE-RECOGNITION.md](IMAGE-RECOGNITION.md#which-model-does-the-looking).
+
+| Model | Download | Reads images | Notes |
+|---|---|---|---|
+| `minicpm-v4.6` | 1.6 GB | Yes | Built for this. The recommended second model |
+| `qwen3.5:2b` | 2.7 GB | Yes | Codes and reads images |
+| `qwen3.5:4b` | 3.4 GB | Yes | Same, larger |
+| `gemma4:e2b` | 7.2 GB | Yes | The all-round recommendation already reads images |
+| `llama3.2` | 2.0 GB | No | Pair it with `minicpm-v4.6` |
+
+---
+
+## Next
+
+- [The measurements behind the ranking](MODELS.md)
+- [What a small model cannot do](LIMITATIONS.md)
diff --git a/doc/DEVELOPING.md b/doc/DEVELOPING.md
new file mode 100644
index 0000000..5667e7f
--- /dev/null
+++ b/doc/DEVELOPING.md
@@ -0,0 +1,122 @@
+# Developing HirayaCoder
+
+Everything the README's beginner path leaves out: what is interesting about the design,
+how it is measured, and how to build and contribute to it.
+
+---
+
+
+Everything above is the beginner's path. The rest is the engineering, and it is
+documented properly elsewhere.
+
+**Features and settings** — [FEATURES.md](https://github.com/jaymar921/HirayaCoder/blob/main/doc/FEATURES.md)
+· **How it is built** — [ARCHITECTURE.md](https://github.com/jaymar921/HirayaCoder/blob/main/doc/ARCHITECTURE.md)
+· **Measurements** — [MODELS.md](https://github.com/jaymar921/HirayaCoder/blob/main/doc/MODELS.md)
+· **Security model** — [SECURITY.md](https://github.com/jaymar921/HirayaCoder/blob/main/doc/SECURITY.md)
+
+## What is interesting about it technically
+
+- **Agentic on every model, down to 1B.** It plans, reads, edits, deletes, and runs
+ scripts across multiple files on its own. Two loop strategies — native tool-calling for
+ capable models, a constrained one-action-per-turn JSON loop for small ones — behind one
+ driver, so the mechanism changes with the model but the reach never does.
+- **Three layers of local memory.** A plain-text session log, the conversation itself,
+ and typed facts about the project that persist across sessions, so the second session
+ does not rediscover what the first one paid for.
+- **"Done" has to be true.** A run that reports success having written nothing, or having
+ left `// Implement this here` inside a function it just wrote, gets sent back once with
+ the specific problem named. Completion is judged from what changed on disk, never from
+ what the model says about itself.
+- **It learns from what actually happened.** Outcomes are recorded locally — counts and
+ guard codes, never your code — and a model that trips the same guard three times gets
+ the matching correction added to its prompt. It adapts what the model is *told*, never
+ what it is *allowed to do*.
+- **Every guard names a real failure.** The write guards exist because four different
+ models produced six damaged files in one seventeen-run sweep: deleted exports, a
+ CommonJS module silently rewritten as ESM, an implementation replaced by an empty
+ object. Every one of them parsed cleanly.
+
+## Benchmarks
+
+Measured on three named machines, with the delete declined at the prompt on purpose — a
+model that claims it deleted the file has failed the task whatever else it got right.
+There are four harnesses: editing an existing project, building one from an empty folder,
+wiring an existing project together, and — new in 0.9.0 — building a whole application
+from one brief and then **driving the finished thing**, either by clicking every control
+in a headless browser or by calling its service layer directly.
+
+That last harness runs four briefs, so a model's trouble with the work can be told apart
+from its trouble with the language it was asked to work in:
+
+| Brief | Stack | Graded by |
+|---|---|---|
+| TODO app | React + Vite + Tailwind | 12 features, clicked in a browser |
+| Contact manager | React + Vite + Vitest | 12 features, clicked in a browser |
+| Point of sale | Java + Swing + Maven | 8 features, through the service layer |
+| Point of sale | Python + Tkinter, stdlib only | 8 features, through the service layer |
+
+It exists because of a result worth stating plainly: a model passed the scaffold,
+structure, install and build checks, and shipped an app whose only button incremented
+Vite's demo counter. Four green gates and nothing that worked. The full
+tables, including what each model broke and how, are in
+[MODELS.md](https://github.com/jaymar921/HirayaCoder/blob/main/doc/MODELS.md) and
+[benchmarks/](https://github.com/jaymar921/HirayaCoder/blob/main/benchmarks/README.md).
+
+The finding worth repeating here: **the mocked test suite passes clean while a real model
+destroys a real file.** Nearly every serious bug in this project was found by running an
+actual model, never by the unit tests.
+
+## Building from source
+
+Needs Node.js 18 or newer.
+
+```bash
+npm install
+npm run test:all # lint + unit + integration, against a real VS Code
+npm run package # builds builds/v/hirayacoder-.vsix
+```
+
+## Contributing
+
+Contributions are welcome, with **one hard rule: pull requests only — never push
+directly to `main`.** CI runs the suite on Ubuntu, macOS, and Windows, and that matrix is
+the only evidence this project has that anything works on the two platforms the
+maintainer does not own.
+
+Read [CONTRIBUTING.md](https://github.com/jaymar921/HirayaCoder/blob/main/CONTRIBUTING.md)
+first. The short version:
+
+- Branch and commit as `feat/…`, `fix/…`, `docs/…`.
+- `npm run test:all` must pass.
+- **If you touched the agent loop, prompts, translator, or tools, run a real model**
+ (`node tools/bench-agent.js gemma4:e2b agent auto full`) and put the outcome in the PR.
+- Don't weaken a guard or a permission prompt to make something pass.
+- Comments explain *why*, not *what*.
+
+Security issues: please contact [jaymar921](https://github.com/jaymar921) directly rather
+than opening a public issue.
+
+## Repository layout
+
+```
+HirayaCoder/
+├── app/ # Extension source — agent loops, tools, security layer, webview
+├── test/ # Unit + integration tests
+├── doc/ # Architecture, features, models, tutorial, security, publishing
+├── setup/ # AI build prompt + versioned model/translator system prompts
+├── security/ # Threat model, SAST reports
+├── scripts/ # Packaging
+├── tools/ # Live-model benchmark harnesses
+└── builds/ # Packaged .vsix output, per version (gitignored)
+```
+
+---
+
+---
+
+## Next
+
+- [How it is built](ARCHITECTURE.md)
+- [Every feature and setting](FEATURES.md)
+- [The security model and threat model](SECURITY.md)
+- [Publishing a release](PUBLISHING.md)
diff --git a/doc/FEATURES.md b/doc/FEATURES.md
index 2b743f1..ec2fca6 100644
--- a/doc/FEATURES.md
+++ b/doc/FEATURES.md
@@ -99,9 +99,19 @@ models are GPU-resident this is worth re-trying.
- **`+` attaches reference files.** They are scanned for secrets and redacted *at
ingestion*, before truncation, so a credential cannot survive by sitting past the cut
and reappearing when the budget changes.
-- **Images** for vision-capable models — magic-number checked, 4 MB cap, first message
- only. The attach button is disabled for models without the capability, since such a
- model does not error on an image, it ignores it and answers from the text.
+- **Images**, on any model — magic-number checked, 4 MB cap. The raw picture rides on the
+ first message when the selected model has the `vision` capability, and never when it
+ does not: such a model would not error, it would ignore the image and answer from the
+ text alone.
+- **Image recognition** (`core/imageRecognition`, 1.1.0) is what makes the above work on
+ a text-only model. A vision model writes down what is in the picture and *that* goes
+ into the context, so a screenshot is usable on `llama3.2`. It is produced even for a
+ model that can see, because an agent loop outlives the picture: images ride on turn one
+ only, and the words are small enough to carry on all eight. The describer is the
+ selected model when it can see (no extra load) and otherwise the smallest installed
+ vision model. The attach button is gated on the *machine* having a vision model, not on
+ the selected one being it. Every attachment comes with a panel showing exactly what was
+ read. Full detail: [IMAGE-RECOGNITION.md](IMAGE-RECOGNITION.md).
- **Session memory** persists across turns as plain text you can read and clear
(**Show Session Memory**, **Clear Session Memory**). Notes are composed by the
extension from what actually happened, not written by the model, and a failed write or
@@ -388,6 +398,8 @@ where bare JSON mode managed **0 out of 6**.
| `hirayacoder.scripts.allowedBinaries` | `[]` | Extends the built-in allow-list. |
| `hirayacoder.scripts.timeoutMs` | `120000` | |
| `hirayacoder.security.protectedPaths` | `[".git", ".hirayacoder"]` | |
+| `hirayacoder.vision.enabled` | `true` | Read attached images into a written description. Off still sends images to a model that can see them. |
+| `hirayacoder.vision.describeModel` | *(automatic)* | Which model reads images. Honoured only if installed **and** reporting `vision`. |
| `hirayacoder.experimental.stepSessions` | `false` | Run each TODO item as a briefed, verified step. See above. |
| `hirayacoder.inlineCompletion.enabled` | `false` | |
| `hirayacoder.statusBar.enabled` | `true` | Connection, model, tier. |
diff --git a/doc/GETTING-STARTED.md b/doc/GETTING-STARTED.md
new file mode 100644
index 0000000..9f56d3c
--- /dev/null
+++ b/doc/GETTING-STARTED.md
@@ -0,0 +1,134 @@
+# Getting started with HirayaCoder
+
+The complete first-run walkthrough: what the extension is, whether your computer can
+run it, and the four steps to a working install. The
+[README](../README.md#quick-start) has the short version of the same four steps.
+
+---
+
+## New here? Start with this
+
+**You do not need to know how to code to try it.** You do need about twenty minutes and
+a computer with a bit of room to spare. Here is the honest version of what you are
+signing up for.
+
+### What it actually does
+
+You open a folder on your computer, type something like *"make me a webpage with a
+to-do list"*, and HirayaCoder creates the files, writes the code, and shows you what it
+changed before saving anything. You can then ask it to change things: *"make the buttons
+blue"*, *"add a delete button"*.
+
+### What makes it different from ChatGPT or Copilot
+
+| | HirayaCoder | ChatGPT / Copilot |
+|---|---|---|
+| Cost | Free, forever | Usually a monthly fee |
+| Internet | Not needed after setup | Required |
+| Your code | Never leaves your computer | Sent to a company's servers |
+| Quality | Good, not great — see below | Better |
+| Speed | Seconds to minutes, depends on your PC | Fast |
+
+**The trade is real and you should know it before you start.** HirayaCoder runs a small
+AI model on your own hardware, and small models are not as clever as the big paid ones.
+It handles ordinary tasks well. It struggles with big, vague requests. If you ask for
+"a full social media app" you will be disappointed; if you ask for one page, one feature,
+or one fix at a time, it does a decent job.
+
+### Will it run on my computer?
+
+The main question is **how much RAM (memory) you have**. Here's a quick guide — you do
+not need a fancy graphics card.
+
+| Your computer | Will it work? | What to expect |
+|---|---|---|
+| 8 GB RAM | Yes, just barely | Slow, and only simple single-file tasks |
+| 16 GB RAM, no graphics card | **Yes — this is what it was built for** | A task takes 1–5 minutes. Very usable |
+| 16 GB+ with a gaming graphics card | Yes, comfortably | A task takes 20–60 seconds |
+| Mac with Apple Silicon (M1–M4) | Yes, very well | A task takes 10–30 seconds |
+
+To check on Windows: press `Ctrl+Shift+Esc`, click **Performance**, then **Memory**.
+On a Mac: Apple menu → **About This Mac**.
+
+---
+
+## Getting started
+
+Four steps. Copy and paste the commands exactly.
+
+### Step 1 — Install VS Code
+
+If you do not already have it, download it free from
+[code.visualstudio.com](https://code.visualstudio.com). This is the program you will
+actually be working in. You need **version 1.85 or newer** — any download from this year
+is fine.
+
+### Step 2 — Install Ollama
+
+[Ollama](https://ollama.com) is the free program that runs the AI on your computer.
+Download it, install it, and leave it running in the background. It has no window — it
+just sits in your system tray or menu bar, and that is normal.
+
+### Step 3 — Download an AI model
+
+Open a terminal and paste one line. (On Windows press the Start button, type
+`PowerShell`, and hit Enter. On a Mac press `Cmd+Space`, type `Terminal`, and hit Enter.)
+
+**Pick the line that matches your computer:**
+
+```bash
+# 16 GB RAM or more — the best all-round choice, start here
+ollama pull gemma4:e2b
+
+# 8 GB RAM, or if the one above is too slow — smaller and faster, but more limited
+ollama pull llama3.2:1b
+```
+
+This downloads a few gigabytes, so it takes a while on a slow connection. You only ever
+do it once.
+
+### Step 4 — Install HirayaCoder
+
+Download the `.vsix` file from the
+[Releases page](https://github.com/jaymar921/HirayaCoder/releases), then run:
+
+```bash
+code --install-extension hirayacoder-.vsix
+```
+
+Replace `` with the number in the filename you downloaded.
+
+### You're ready
+
+1. Open VS Code.
+2. Go to **File → Open Folder** and pick a folder — an empty new one is perfect for a
+ first try. **This step is required**: HirayaCoder refuses to do anything without a
+ folder open, so that it can never touch files outside it.
+3. Press `Ctrl+Shift+H` (`Cmd+Shift+H` on a Mac).
+4. Type something and press Enter.
+
+Good first things to type:
+
+- `make a webpage that says hello with a big blue button`
+- `create a simple to-do list app in one HTML file`
+- `explain what this project does` *(in a folder that already has code)*
+
+**If anything above is unclear once you are in there, press *Guide* in the chat header.**
+It opens the same four setup steps and — more usefully — what to expect: how long a task
+takes, why a refusal is usually the checks working, and what a small model is and is not
+good at.
+
+There is a longer, friendlier walkthrough in
+[TUTORIAL.md](https://github.com/jaymar921/HirayaCoder/blob/main/doc/TUTORIAL.md).
+
+---
+
+---
+
+## Next
+
+- [Using it day to day](USING-IT.md) — the three modes, watching a run, and the habits that make small models work
+- [What it cannot do](LIMITATIONS.md) — read this before you decide whether it fits your work
+- [When something goes wrong](TROUBLESHOOTING.md)
+- [Choosing a model](CHOOSING-A-MODEL.md)
+- [A longer, friendlier walkthrough](TUTORIAL.md)
diff --git a/doc/IMAGE-RECOGNITION.md b/doc/IMAGE-RECOGNITION.md
new file mode 100644
index 0000000..d1cba06
--- /dev/null
+++ b/doc/IMAGE-RECOGNITION.md
@@ -0,0 +1,200 @@
+# Reading images
+
+*New in 1.1.0.*
+
+You can attach a picture to a message. HirayaCoder looks at it, writes down what is in
+it, and then uses that as part of the conversation. It works for photos, screenshots,
+error messages, and rough sketches of a screen you want built.
+
+This page explains how it works, what it is good at, and where it falls over.
+
+---
+
+## The short version
+
+1. Click the image button next to the message box.
+2. Pick a PNG, JPEG, WEBP, or GIF file.
+3. Type your message and send it.
+
+In **Ask** mode you get an answer about the picture. In **Agent** mode the picture
+becomes part of the job, so "build this screen" works with a mockup attached.
+
+---
+
+## What is actually happening
+
+This is worth understanding, because it explains most of the behaviour you will notice.
+
+An AI model is a program that predicts text. Most of them only handle text, in and out.
+A few of them, called **vision models**, can also take an image as input. The ones you
+have installed are listed by `ollama list`, and whether a given one can see is something
+Ollama reports about it.
+
+So there are two situations.
+
+**Your model can see.** The picture goes to it directly, along with your message. This
+is the simple case and costs nothing extra.
+
+**Your model cannot see.** This used to mean the image button was greyed out. Since
+1.1.0 it does not. Instead HirayaCoder sends the picture to a different model, one that
+*can* see, and asks it a single question: what is in this image? That answer comes back
+as a paragraph of ordinary text, and the paragraph is what your coding model receives.
+
+The second case is the interesting one. It means you can run `llama3.2` for the coding,
+which has no vision at all, and still hand it a screenshot. You do not have to switch
+models, and you do not have to give up the model you like.
+
+### Why a written description, even when the model can see
+
+There is a second reason this exists, and it applies even to a model that has vision.
+
+In **Agent** mode the model works in a loop. It reads a file, then decides what to do
+next, then writes a file, then decides again. That can be eight or ten rounds. The
+picture is only attached to the first one, because attaching it to all of them would
+mean uploading several megabytes over and over, and on a normal laptop that is slower
+than the actual thinking.
+
+So by round four, the model is working on your screenshot without being able to see it
+any more. The written description is small enough to carry on every single round, so it
+does not disappear. That is why Agent mode always produces one, even on a vision model.
+
+---
+
+## Which model does the looking
+
+By default HirayaCoder picks for you:
+
+1. If the model you selected can see, it does the looking itself. Nothing else loads.
+2. Otherwise, the **smallest** installed model that can see does it.
+
+Smallest, not best, and there is a good reason. Ollama keeps one model in memory at a
+time. Using a second model means unloading the first one, loading the second, then
+loading the first one back. On a 16 GB laptop with no graphics card that is thirty to
+sixty seconds of nothing visibly happening. Describing a picture is a simple job, so the
+smallest model that can do it is the one that gets you back to work soonest.
+
+If you want to choose yourself, set `hirayacoder.vision.describeModel` to a model name.
+It is honoured only if that model is installed and actually reports vision. If it does
+not, HirayaCoder picks automatically instead and writes the reason to the log, rather
+than silently turning images off on you.
+
+You can turn the whole thing off with `hirayacoder.vision.enabled`. A model that can see
+for itself still receives images; only the extra description step stops.
+
+### Getting a model that can see
+
+If nothing you have installed can read images, the button tells you so. One command
+fixes it:
+
+```bash
+ollama pull minicpm-v4.6
+```
+
+It is 1.6 GB, which is small, and it is built for exactly this. It sits alongside
+whatever you already use for coding.
+
+---
+
+## Seeing what it read
+
+Under your message there is a collapsed panel labelled **What *model* saw in *file***.
+Open it and you get the exact text the vision model produced.
+
+Please use it. It is the difference between two problems that look identical:
+
+- The vision model misread your picture, and everything after that was doomed.
+- The vision model read it correctly and the coding model went wrong afterwards.
+
+Those need opposite fixes, and without the panel you cannot tell them apart. If the
+description is wrong, the fastest repair is usually to say so in your next message:
+"that is a delete button, not a save button".
+
+---
+
+## What it is good at
+
+Measured against six photographs, on the two smallest vision models that exist. The
+numbers are in [MODELS.md](MODELS.md#image-recognition-110) and the raw runs are in
+[benchmarks/](../benchmarks/README.md).
+
+- **Naming the main thing.** Cat, dog, car, tree, aeroplane. This is reliable, including
+ on a 752M model, which is very small.
+- **Colour, setting, and count.** "Two puppies in grass", "a yellow car on a road".
+ Mostly right.
+- **Reading large text.** Signs, headings, big labels. Works more often than not.
+
+Applied to actual work, that translates to:
+
+- A screenshot of an error, so you do not have to retype it.
+- A mockup or a sketch of a screen, as the basis for building it.
+- A photo of a whiteboard, for the shape of a data model.
+- A screenshot of a UI you want changed, so you can point rather than describe.
+
+---
+
+## What it is not good at
+
+Be honest with yourself about this before you rely on it.
+
+- **Small text.** A 752M model reading a stack trace at normal screenshot resolution
+ will get some of it wrong, and it will not tell you which parts. Crop and zoom first,
+ or paste the text.
+- **Exact numbers.** Line numbers, hex colours, version strings, anything where being
+ one character off matters. Check them.
+- **Precise layout.** It knows a button is near the top. It does not know it is 24
+ pixels from the edge.
+- **Anything it has not seen.** It describes what is in front of it. It does not know
+ your design system or what your icons mean.
+
+The failure that costs you most is the quiet one. A model that misreads a picture does
+not say "I am unsure". It writes a confident, well formed paragraph about the wrong
+thing. This is why the description is shown to you rather than hidden.
+
+---
+
+## Limits and rules
+
+- **Formats:** PNG, JPEG, WEBP, GIF.
+- **Size:** 4 MB per image. The file is checked before it is read, so a huge one is
+ rejected rather than loaded into memory. Images get about a third larger when they are
+ encoded for sending, and a big one can take minutes on a CPU.
+- **The file type is verified.** A `.png` that is really something else is spotted by
+ looking at the actual bytes, not by trusting the name.
+- **Images belong to one message.** They are sent with the message you attached them to
+ and are not carried into the next one. Attached files, which are different, do stay.
+- **It is still completely offline.** The picture goes to Ollama on `127.0.0.1` and
+ nowhere else, exactly like every other thing this extension sends. Nothing about
+ images changes the privacy story.
+
+---
+
+## Speed
+
+Reading a picture is a full model call, so it takes about as long as an ordinary reply.
+On a CPU-only laptop that is roughly five to twenty seconds for a small vision model.
+The status line tells you which model is reading which file while it happens.
+
+If your coding model cannot see, add the time to load the second model on top. That is
+the one part of this that feels slow, and it is why the automatic choice prefers a model
+that is already loaded.
+
+Attaching the same picture twice in a row costs nothing the second time. The description
+is kept for the session.
+
+---
+
+## Settings
+
+| Setting | Default | What it does |
+|---|---|---|
+| `hirayacoder.vision.enabled` | `true` | Turn the description step off entirely |
+| `hirayacoder.vision.describeModel` | `""` | Name a model to do the looking. Empty means pick automatically |
+
+---
+
+## Next
+
+- [Using it day to day](USING-IT.md)
+- [Choosing a model](CHOOSING-A-MODEL.md)
+- [What HirayaCoder cannot do](LIMITATIONS.md)
+- [The measurements](MODELS.md#image-recognition-110)
diff --git a/doc/LIMITATIONS.md b/doc/LIMITATIONS.md
new file mode 100644
index 0000000..f2fff8b
--- /dev/null
+++ b/doc/LIMITATIONS.md
@@ -0,0 +1,199 @@
+# What HirayaCoder cannot do
+
+Every tool has a shape. This page is the honest description of this one's, so you can
+decide whether it fits your work before you spend an evening finding out.
+
+Two kinds of limit are listed here and it is worth keeping them apart:
+
+- **Limits by design.** Things HirayaCoder refuses to do because doing them would make
+ it a different and worse tool. These are not going to change.
+- **Limits of the technology.** Things a small model running on your own laptop is not
+ currently good at. Some of these will improve. Most of them will improve slowly.
+
+---
+
+## Limits by design
+
+These are choices. They are the reason the tool is worth using, and they cost real
+convenience.
+
+### It never talks to the internet
+
+The only address it will connect to is `127.0.0.1`, which is your own machine. A
+non-loopback address is rejected in code before any connection opens. This means:
+
+- No cloud models. No GPT, no Claude, no Gemini, whatever your subscription is.
+- No looking up documentation, no fetching a package from the web, no searching.
+- No sharing a session with a teammate.
+
+If your workflow depends on a frontier model, this is not the tool. That is a real
+answer, not a soft one.
+
+### It cannot leave the folder you opened
+
+Every read, write, and delete is confined to your open workspace folder. It cannot touch
+your home directory, a sibling project, or anything outside. There is no setting that
+turns this off.
+
+It also refuses to run in an untrusted workspace, and it does nothing at all until you
+have a folder open.
+
+### It runs only a fixed list of commands
+
+`node`, `npm`, `git`, `pytest`, `mvn`, and about twenty more. Everything else is
+refused. Notably:
+
+- **No shell.** Commands run directly, so `&&`, `|`, `>` and `$(...)` are refused
+ rather than interpreted. One command at a time.
+- **No `rm`, `ls`, `mkdir`, `curl`, `wget`.** File work goes through the safe file
+ tools instead.
+- **Anything that reaches the network or publishes code** (`git push`, `npm publish`)
+ asks for confirmation regardless of your settings.
+
+You can add binaries to the list in settings. The model cannot add to it.
+
+### It asks before it writes
+
+By default every file write shows you a diff and waits. You can turn that off with Auto
+Edit, and deletes still ask even then. If you want an agent that works unattended for an
+hour and shows you the result, that is not what this is.
+
+### It cannot see your running program
+
+There is no browser, no debugger attached, no screenshot of your app. It can run your
+build and read the output. It cannot click a button and see what happened.
+
+Since 1.1.0 you can [attach a screenshot yourself](IMAGE-RECOGNITION.md), which covers
+some of the same ground manually.
+
+### There is no telemetry, so there is no crash reporting
+
+Nobody sees your errors but you. If something breaks, the log in the output channel is
+the whole record, and an issue on GitHub is the only way we learn about it.
+
+---
+
+## Limits of small models
+
+This is the part people underestimate. HirayaCoder runs whatever model your laptop can
+hold, and that model is small. Small models are genuinely useful and genuinely limited,
+and the limits are specific rather than general.
+
+### It does not finish a whole application
+
+Measured, not guessed. On a 98-line brief for a complete React app:
+
+- A 0.8B model scaffolds the project, writes every file the request named, and installs
+ the dependencies.
+- It does not get the app working. The build fails, and **no model at this size finished
+ the app in any of our runs.**
+
+One model passed the scaffold, structure, install and build checks and shipped an app
+whose only button incremented the demo counter it started with. Four green gates and
+nothing that worked.
+
+So: one file, one feature, one fix at a time is where this tool earns its keep. A whole
+application handed over and walked away from is not something we can recommend yet. The
+numbers are in [SESSION-ANALYSIS-0.9.0.md](SESSION-ANALYSIS-0.9.0.md) and
+[MODELS.md](MODELS.md).
+
+### It is slow
+
+On a 16 GB laptop with no graphics card, expect one to five minutes for an ordinary
+task. Each step in an agent run is a separate model call, and a run can be eight steps.
+
+A graphics card makes it **faster, not smarter**. A bigger model makes it smarter, not
+faster. These are separate knobs and people mix them up constantly.
+
+### It repeats itself
+
+The classic small-model failure is not bad code, it is the same correct-looking action
+forever. HirayaCoder keeps a record of everything the agent has already done and puts it
+back in front of the model each turn, which fixes most of it. It does not fix all of it.
+
+### It reports success it did not achieve
+
+Small models say "I have created and tested the app" having written nothing. This is
+common enough that completion is checked against what actually changed on disk rather
+than against what the model says. When the check fires you are told plainly. When it
+does not fire, and the model was wrong anyway, you find out later.
+
+### It writes damaged files
+
+Four different models produced six damaged files in one seventeen-run sweep: deleted
+exports, a CommonJS module silently rewritten as ESM, an implementation replaced by an
+empty object. Every one of them parsed cleanly. There are guards for each of these
+specific shapes, and a guard only catches the shape it knows.
+
+### It does not know your codebase
+
+It sees what fits in the context window, which on a 1B model at medium settings is about
+1800 tokens for everything: the system prompt, your message, the file listing, and the
+result of its last action. It is not holding your architecture in its head. Naming the
+file you mean is worth more here than it would be with a large model.
+
+### It forgets between sessions, mostly
+
+There is memory, in three layers, and it is genuinely useful. It is not the same as a
+model that has read your whole repository.
+
+---
+
+## Limits of image recognition
+
+New in 1.1.0, and [documented in full](IMAGE-RECOGNITION.md). The short version:
+
+- **Small text is unreliable.** A stack trace in a normal screenshot will be partly
+ misread, and you will not be told which parts.
+- **Exact values need checking.** Line numbers, hex colours, version strings.
+- **Precise layout is out of reach.** It knows a button is near the top. It does not
+ know it is 24 pixels from the edge.
+- **A misread is silent.** The model does not hedge. It writes a confident paragraph
+ about the wrong thing, which is why the description is shown to you rather than
+ hidden.
+
+Formats are PNG, JPEG, WEBP, and GIF, at up to 4 MB each.
+
+---
+
+## Things that are not limitations, though they look like one
+
+Worth listing, because these get reported as bugs.
+
+**"It refused to write the file."** Usually the write guards working. A half-written
+file, unbalanced brackets, or a rewrite that deletes an export other files depend on.
+Ask again.
+
+**"It said the tool was not available."** Plan mode and read-only turns remove the
+writing tools structurally. The model is not being difficult, the tool genuinely does
+not exist for that turn.
+
+**"It asked me before running the build."** That is the permission gate. Turn on
+auto-approve for scripts if you want it not to.
+
+---
+
+## Where it does not compete
+
+| | HirayaCoder | A cloud assistant |
+|---|---|---|
+| Cost | Free, forever | Usually a monthly fee |
+| Internet | Not needed | Required |
+| Your code | Never leaves your computer | Sent to a company's servers |
+| Quality | Good on small tasks | Better on everything |
+| Speed | Seconds to minutes | Fast |
+| Whole applications | No | Sometimes |
+| Works on a plane | Yes | No |
+
+The trade is real. Nobody should choose this because they think it is secretly as good.
+Choose it because the code not leaving your machine is worth something to you, or
+because there is no budget, or because there is no connection.
+
+---
+
+## Next
+
+- [Choosing a model](CHOOSING-A-MODEL.md) — the ranking, and what each size actually manages
+- [The measurements](MODELS.md) — the full tables behind everything claimed here
+- [When something goes wrong](TROUBLESHOOTING.md)
+- [The security model](SECURITY.md)
diff --git a/doc/MODELS.md b/doc/MODELS.md
index cdad47b..1f8b739 100644
--- a/doc/MODELS.md
+++ b/doc/MODELS.md
@@ -125,10 +125,16 @@ Tier comes from `core/modelCapability.js`: ≤ 3B **or** no tool support → Tie
`gemma4:e2b` is named for its ~2B *effective* parameters, but Ollama reports 5.1B raw,
which is what the threshold sees.
-**Images** need the `vision` capability. `qwen3.5:*` and `gemma4:*` report it;
-`llama3.2:*` and `stable-code` do not. A model without it does not error on an image —
-it ignores it and answers from the text alone, which is why the attach button is
-disabled rather than left to fail quietly.
+**Images** need the `vision` capability. `qwen3.5:*`, `gemma4:*` and `minicpm-v4.6`
+report it; `llama3.2:*` and `stable-code` do not. A model without it does not error on
+an image — it ignores it and answers from the text alone, which is why the raw picture
+is never put on a message to one.
+
+**Since 1.1.0 that no longer disables the attach button.** The image reaches a text-only
+model as a written description produced by whichever installed model *can* see, so the
+button is gated on the machine having a vision model at all rather than on the selected
+one being it. Measurements are in [Image recognition](#image-recognition-110);
+the mechanism is in [IMAGE-RECOGNITION.md](IMAGE-RECOGNITION.md).
**TODO lists** require the `thinking` capability *and* ≥ 2B parameters
(`hirayacoder.model.todoMinParams`). `qwen3.5:0.8b` reports `thinking` and is excluded
@@ -645,6 +651,76 @@ unproductive rather than destructive.
---
+## Image recognition (1.1.0)
+
+From `tools/bench-vision.js`. Six photographs in `docs/test-images/`, both prompts
+(`answer` and `task`), two samples each, so **24 graded descriptions per model**. Machine
+B, `--cold`, which unloads the model between samples — without that the timings are
+Ollama's prompt cache rather than the model.
+
+| Model | Params | Subject | Detail | Text read | Confused | Cold |
+|---|---|---|---|---|---|---|
+| `minicpm-v4.6` | 752M | **24/24** | 42/48 | 4/8 | 0 | 11s |
+| `qwen3.5:0.8b` | 873M | 23/24 | **46/48** | **8/8** | 0 | 13s |
+
+`Subject` is naming the right thing. `Detail` is colour, setting, and count. `Text read`
+is only scored on the two fixtures that have words in them. `Confused` counts
+descriptions asserting something the photograph contradicts, and it can only be lost —
+a cat called a dog fails the fixture whatever else it got right.
+
+### What the numbers mean in practice
+
+**Naming the subject is solved at this size.** 47 of 48 across two sub-1B models, and
+zero confusions. If you attach a photo and ask what is in it, both of these answer
+correctly. That is a stronger result than expected for 752M parameters.
+
+**Reading text is not, and it depends on size of the text rather than size of the
+model.** Every one of `minicpm-v4.6`'s four text failures is `car.jpg`, whose only text
+is a small BMW badge on a grille. It read `cebu pacific` off the aeroplane — large,
+clear, high-contrast — on all four attempts. So the honest guidance is not "small models
+cannot read" but "crop and zoom before you rely on it", which is what
+[IMAGE-RECOGNITION.md](IMAGE-RECOGNITION.md) now says.
+
+**The two models differ more than their sizes suggest.** 121M parameters apart, and one
+read every word while the other read half. There is no way to predict which you have
+from the model card, which is the argument for the panel that shows you what was read.
+
+### The failure that changed the code
+
+`qwen3.5:0.8b`, one sample in twenty-four, replied:
+
+> I cannot see this image. I am an AI model designed to process text and provide
+> information, but I do not have the ability to view or interpret visual content like
+> screenshots, photos, or UI elements.
+
+It reports the `vision` capability. It was sent the image. It had described the same
+photograph correctly on the previous sample. This is a small model's text-only training
+reasserting itself over what it was actually handed.
+
+Left alone, that paragraph *is* the description: stored, put in front of the coding model
+as "what is in this picture", and shown to the user. The coding model then reasons from a
+statement that no image exists, which is worse than being told the read failed.
+`core/imageRecognition` now recognises the shape and reports a failed read.
+
+The detector is narrow on purpose. The recognition prompt asks the describer to say when
+it cannot make something out, so *"I cannot see the licence plate clearly"* is the
+instruction working, and a detector that ate those would punish the behaviour we want. It
+requires a first-person inability **plus** a visual object, and only looks at the opening
+400 characters, so a real description that ends with a boilerplate disclaimer survives.
+
+### Reproducing it
+
+```bash
+node tools/bench-vision.js minicpm-v4.6:latest --machine B --purpose both --repeat 2 --cold
+node tools/bench-vision.js qwen3.5:0.8b --machine B --purpose both --repeat 2 --cold
+```
+
+Raw runs: `benchmarks/results/B/vision__*.json`. Every record keeps the full description
+text, because when a score looks wrong the only way to tell a bad grader from a bad model
+is to read what the model actually wrote.
+
+---
+
## What each model exposed
Every item here was invisible to the mocked test suite.
diff --git a/doc/TROUBLESHOOTING.md b/doc/TROUBLESHOOTING.md
new file mode 100644
index 0000000..ecfba95
--- /dev/null
+++ b/doc/TROUBLESHOOTING.md
@@ -0,0 +1,89 @@
+# When something goes wrong
+
+Small AI models make mistakes. HirayaCoder has built-in checks that catch the common
+ones, so several of the messages below are the system **working**, not breaking.
+
+---
+
+
+Small AI models make mistakes. HirayaCoder has built-in checks that catch the common
+ones, so a few of the messages below are the system **working**, not breaking.
+
+## "It refused to write the file"
+
+**This is usually a good thing.** HirayaCoder checks the AI's work before saving and
+blocks writes that would damage your files — a half-written file, code with a missing
+bracket, or a rewrite that quietly deletes something other files depend on.
+
+Just ask again. It usually gets it right the second time.
+
+## "It said it was done but nothing changed"
+
+A known habit of small models: reporting success without doing the work. HirayaCoder
+checks and will tell you plainly when this happens. Ask again, and include the exact
+file name this time.
+
+## "It's taking forever"
+
+Normal on a laptop with no graphics card — a few minutes per task is expected. If it is
+much worse than that, the model is probably too big for your RAM. Try `llama3.2:1b`.
+
+## "The code it wrote doesn't work"
+
+Try, in order:
+
+1. Paste the error message into the chat. It is quite good at fixing errors it can see.
+2. Ask for a smaller piece of the problem.
+3. Switch to a bigger model if your RAM allows it.
+
+## "It can't run my program"
+
+HirayaCoder can only run programs already installed on your computer. If you ask it to
+run a Python script, you need Python installed. It will still *write* the code — it just
+cannot run it for you.
+
+It also only runs a fixed list of well-known commands. **This is the whole list** —
+anything else is refused on purpose, so a mistake by the AI cannot damage your system:
+
+| For | It may run |
+|---|---|
+| JavaScript / Node | `node`, `npm`, `npx`, `yarn`, `pnpm` |
+| Python | `python`, `python3`, `pip`, `pip3`, `pytest` |
+| Java | `java`, `javac`, `mvn`, `gradle` |
+| Go, Rust, .NET | `go`, `cargo`, `dotnet` |
+| Testing and formatting | `jest`, `mocha`, `vitest`, `ava`, `tsc`, `eslint`, `prettier` |
+| Other | `git`, `make`, `ollama` |
+
+Two things worth knowing before they surprise you:
+
+- **Everyday shell commands are not on the list** — `rm`, `ls`, `mkdir`, `curl` are all
+ refused. Creating files and folders happens through the safe file tools instead, so
+ `mkdir` is never needed: writing a file creates the folders above it.
+- **No shell is involved.** Commands run directly, so `&&`, `|`, and `>` are refused
+ rather than interpreted. One command at a time.
+
+You can add to the list in HirayaCoder's settings. The AI cannot add to it itself.
+
+---
+
+---
+
+## It cannot read my image
+
+Two different causes, and the message tells you which:
+
+- **"None of your installed models can read images"** — nothing you have installed has
+ vision. Pull one: `ollama pull minicpm-v4.6`. It is 1.6 GB and it is the smallest
+ thing that does the job.
+- **The description is wrong** — the vision model misread the picture. Open the
+ *What … saw* panel under your message to see exactly what it read, then say what it
+ got wrong in your next message. Full detail in
+ [IMAGE-RECOGNITION.md](IMAGE-RECOGNITION.md).
+
+---
+
+## Next
+
+- [What it cannot do, on purpose and otherwise](LIMITATIONS.md)
+- [Choosing a model](CHOOSING-A-MODEL.md)
+- [The security model, if a refusal surprised you](SECURITY.md)
diff --git a/doc/USING-IT.md b/doc/USING-IT.md
new file mode 100644
index 0000000..336152f
--- /dev/null
+++ b/doc/USING-IT.md
@@ -0,0 +1,159 @@
+# Using HirayaCoder day to day
+
+What the extension does once it is installed and you have a folder open: the three
+modes, what the panel is showing you while a run happens, and the request habits that
+make the difference between a small model finishing and a small model spinning.
+
+New in 1.1.0: [attaching images](IMAGE-RECOGNITION.md).
+
+---
+
+
+## Watching a run happen
+
+
+
+
+
+A local model can take the better part of a minute per step, so the panel shows you each
+one as it happens: what it is doing, which file, and the reason the model gave for it.
+It opens when the first step arrives and folds away when the turn ends — and if you open
+or close it yourself, it stays how you left it.
+
+That matters most when a run is going wrong. Six steps in, you can see it re-reading the
+same file or editing something you never asked about, and stop it — rather than finding
+out from the summary ten minutes later.
+
+## Small models that finish
+
+
+
+
+
+The classic failure of a very small model is not bad code — it is the same correct-looking
+action forever. HirayaCoder keeps its own record of every file the agent has read, written
+and deleted, every folder it has listed and every command it has run, and puts that record
+in front of the model on each turn.
+
+A repeated read is no longer fatal either. Asking twice for a directory listing used to end
+the run; now the agent is handed back what it already had, told what to do next, and only
+stopped if it asks a third time.
+
+## Long requests, one step at a time
+
+
+
+
+
+If you paste in a long, structured request — headings, numbered steps, a folder tree —
+HirayaCoder works through it **one section at a time**, in your order, using your words.
+It does not ask the model to summarise your request first: your headings already are the
+plan, and reading them takes no guesswork, so this works the same on a 0.8B model as on a
+large one.
+
+Two details worth knowing, because they let you steer it:
+
+- **Draw the folder structure you want** and it will be read as real paths. `src/` plus
+ `components/` plus `TodoItem.jsx` becomes `src/components/TodoItem.jsx`.
+- **Put a comment next to a file** — `TodoItem.jsx # one todo row, with edit and
+ delete` — and that comment becomes the instruction for writing it. A file with a
+ comment beside it may be rewritten if it already exists; a file without one is left
+ alone. That is how you say "this one is yours to write" and "this one came from the
+ scaffolding tool, don't touch it".
+
+A section that only states rules — "React functional components only, no UI libraries" —
+is not treated as a step. It is carried underneath every step instead.
+
+Short requests are unaffected. "Fix the typo in the heading" runs exactly as it always
+did.
+
+## Asked the wrong way
+
+
+
+
+
+The smallest models have a problem that looks like incompetence and is not. Asked to
+choose a tool and fill in its arguments as JSON, a 1B model will reliably answer with the
+simplest thing that satisfies the format — often just "done". Asked in plain English to
+write a file, the same model writes it correctly.
+
+So for a file **your request named**, HirayaCoder stops asking the model what to do. The
+decision is already made: the action is a file write, the path came from your own
+request, and the only question left is what goes in the file.
+
+It is worth being precise about what that does and does not allow, because it is the one
+place the model is not choosing:
+
+- It can only ever write a file **you named** in your request.
+- It never touches `package.json`, lockfiles, `.env`, or anything inside `node_modules`,
+ `dist` or `.git`.
+- It never replaces a file that already exists unless you put a comment next to it.
+- Every write still shows you the diff and waits for approval, exactly as before, and
+ still goes in the audit log.
+
+When it writes a file, it first reads what the files around it actually export, so the
+imports line up rather than being guessed at.
+
+## The three modes
+
+There is a row of buttons at the top of the chat. You can ignore them at first —
+**Agent** is the default and is what you want most of the time.
+
+| Mode | Use it when |
+|---|---|
+| **Agent** | You want it to actually write or change files. The normal choice. |
+| **Plan** | You want to see what it *would* do first, without it touching anything. |
+| **Ask** | You just have a question. It will not change any files. |
+
+You do not have to switch to Ask to ask a question — if you say "hello" or "what does
+this file do", Agent mode notices and just answers you.
+
+## Attaching a picture
+
+Click the image button beside the message box and pick a PNG, JPEG, WEBP, or GIF. In Ask
+mode you get an answer about the picture; in Agent mode the picture becomes part of the
+job, so "build the screen in this mockup" works.
+
+This is available even if the model you selected cannot see images. In that case
+HirayaCoder passes the picture to whichever installed model can, gets a written
+description back, and gives that to your coding model. Under your message, a panel
+labelled *What … saw* shows exactly what was read, which is how you tell a misread
+picture from a bad answer.
+
+If nothing installed can read images, `ollama pull minicpm-v4.6` is the smallest fix.
+
+Full detail, including what it gets wrong: [IMAGE-RECOGNITION.md](IMAGE-RECOGNITION.md).
+
+## Nothing changes without your say-so
+
+By default, every time it wants to write to a file, you get a prompt with a **Review
+diff** button showing exactly what changes. Nothing is saved until you approve.
+
+Once you trust it, you can turn on **Auto Edit** to skip those prompts. Deleting a file
+always asks, even then.
+
+## Tips that make a real difference
+
+- **Ask for one thing at a time — or structure the big ask.** "Add a delete button" works
+ far better than "add delete, edit, sorting, and dark mode" thrown in as one sentence.
+ If you do want the big one, give it headings, numbered steps or a folder tree, and it
+ will work through them one at a time — see
+ [Long requests, one step at a time](#long-requests-one-step-at-a-time).
+- **Name the file** if you know it. "Change the title in index.html" beats "change the
+ title".
+- **It remembers this conversation**, so you can say "make it bigger" and it knows what
+ "it" is.
+- **Be specific about what is wrong.** "The button doesn't do anything when I click it"
+ is much more useful than "it's broken".
+
+---
+
+---
+
+## Next
+
+- [Reading images](IMAGE-RECOGNITION.md) — attaching a screenshot or a photo to a message
+- [What it cannot do](LIMITATIONS.md)
+- [When something goes wrong](TROUBLESHOOTING.md)
+- [Every setting, in detail](FEATURES.md)
diff --git a/docs/images/ADS-1.1.0.md b/docs/images/ADS-1.1.0.md
new file mode 100644
index 0000000..3f07ede
--- /dev/null
+++ b/docs/images/ADS-1.1.0.md
@@ -0,0 +1,125 @@
+# HirayaCoder 1.1.0 — image recognition ad set
+
+Two square (1:1) ads for the 1.1.0 feature release, with the caption each one ships
+with, plus a single caption for posting the pair together.
+
+Rendered at **2160×2160** from the sources in `src/` (1080×1080 CSS pixels at
+`--force-device-scale-factor=2`), same as the four 1.0.0 launch ads in
+[`ADS-1.0.0.md`](ADS-1.0.0.md). 1:1 is the safe ratio everywhere: it is the native
+LinkedIn/Instagram feed shape, and X and Facebook centre-crop it without losing content.
+
+| # | Image | Source | The one thing it argues |
+|---|---|---|---|
+| 5 | `ad-5-vision.png` | `src/ad-5-vision.html` | A text-only model can now use your screenshot |
+| 6 | `ad-6-what-it-read.png` | `src/ad-6-what-it-read.html` | You always see what the model actually read |
+
+Order matters if you post them as a pair: 5 is the capability, 6 is the objection
+("can I trust what a 750M model says it saw?"). Posting 6 alone works; posting 5 alone
+oversells.
+
+Regenerating them is the same command as the README images — see
+[`src/README.md`](src/README.md).
+
+---
+
+## The numbers in ad 6 are real, and they expire
+
+`ad-6-what-it-read.html` carries three measurements on its face. They come from
+`tools/bench-vision.js` on machine B, and the raw runs are in
+`benchmarks/results/B/vision__*.json`:
+
+| On the ad | Where it comes from |
+|---|---|
+| **47/48** named the subject correctly | `minicpm-v4.6` 24/24 plus `qwen3.5:0.8b` 23/24, both prompts, two samples each |
+| **0** described something not in the photo | the `confused` axis, zero on both models |
+| **12/16** read the text printed in the picture | `minicpm-v4.6` 4/8 plus `qwen3.5:0.8b` 8/8 |
+
+**Re-run the sweep before re-rendering this ad for any later release.** An ad quoting a
+score the harness no longer produces is worse than one quoting none, and these are
+small-sample numbers on models that get replaced.
+
+The one that will move is the third. It is also the one the ad is built around, so if a
+future sweep makes it flattering, the ad needs rewriting rather than renumbering: the
+argument is "you cannot tell which model you have without looking", and it stops working
+if both models read everything.
+
+---
+
+## Ad 5 — `ad-5-vision.png`
+
+> Your AI model probably cannot see. That stopped mattering.
+>
+> HirayaCoder 1.1.0 reads images. Attach a screenshot, a photo, or a sketch of a screen
+> you want built, and it uses what is in the picture.
+>
+> The part worth knowing: **this works even when the model you code with is text-only.**
+> Most local models are. HirayaCoder hands the picture to whichever installed model has
+> vision, gets back a written description, and passes that to your coding model. You do
+> not switch models, and you do not give up the one you like.
+>
+> It also keeps working past the first turn. An agent run is eight or ten rounds and the
+> picture only rides on the first one, so a written description is the only form of it
+> that survives to the end.
+>
+> Both models run on your own machine. The image goes to 127.0.0.1 and nowhere else,
+> like everything else this extension sends.
+>
+> `ollama pull minicpm-v4.6` is 1.6 GB and it is all you need.
+
+## Ad 6 — `ad-6-what-it-read.png`
+
+> A small model that misreads your screenshot does not tell you.
+>
+> That is the actual problem with image recognition on local hardware. It does not
+> hedge, it does not say "I am not sure". It writes a fluent, confident paragraph about
+> the wrong thing, and every answer after that is built on it.
+>
+> So HirayaCoder shows you the paragraph. Every image you attach comes with a panel
+> holding the exact text the vision model produced. When an answer looks wrong you can
+> tell, in one click, whether the picture was misread or the reasoning was. Those need
+> opposite fixes and they are otherwise indistinguishable.
+>
+> Measured on six photographs across two models under 1B parameters: 47 of 48 named the
+> subject correctly, and none of them described something that was not there.
+>
+> But only 12 of 16 read the text printed in the picture, and that number was not evenly
+> split. One model read every word. The other, almost the same size, read half.
+>
+> We published the number we came off worst on, because it is the one that tells you to
+> open the panel.
+
+---
+
+## The one caption for both
+
+Use this when posting them together.
+
+> **HirayaCoder 1.1.0 can look at pictures, and it shows you what it saw.**
+>
+> HirayaCoder is a free, fully offline AI coding agent for VS Code. It runs on your own
+> machine through Ollama, and nothing you write ever leaves your laptop. This release
+> teaches it to read images.
+>
+> **1. It works on a model that cannot see.** Most local models are text-only. Attach a
+> screenshot anyway: the picture goes to whichever installed model has vision, comes
+> back as a written description, and that goes to your coding model. No switching, no
+> giving up the model you like. `ollama pull minicpm-v4.6` is 1.6 GB.
+>
+> **2. It survives the whole run.** An agent turn is eight or ten rounds, and the image
+> only rides on the first one. The description is small enough to carry on every round,
+> which is why it is produced even for a model that can see perfectly well.
+>
+> **3. You always see what it read.** A vision model that misreads your screenshot does
+> not hedge, it writes a confident paragraph about the wrong thing. Every attachment
+> comes with a panel showing the exact description, so you can tell a misread picture
+> from a bad answer.
+>
+> **4. The numbers, including the bad one.** Six photographs, two models under 1B: 47 of
+> 48 named the subject, 0 invented something that was not there, and 12 of 16 read the
+> printed text. That last one split unevenly. One model read every word and the other
+> read half, which is precisely why the panel exists.
+>
+> Still free, still MIT, still zero production dependencies, still refuses any address
+> that is not 127.0.0.1. github.com/jaymar921/HirayaCoder
+>
+> #OfflineAI #LocalLLM #VSCode #PrivacyFirst #OpenSource #VisionModels
diff --git a/docs/images/ad-5-vision.png b/docs/images/ad-5-vision.png
new file mode 100644
index 0000000000000000000000000000000000000000..aa21e6b009b3966962064e25c270529f8145e649
GIT binary patch
literal 399954
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