Skip to content

Commit 58bafc0

Browse files
Bryan Chaskoclaude
andcommitted
feat: add snap pipeline recipes (ingest/classify/reconstruct/orchestrator); correct bytedance model IDs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 5a1a95e commit 58bafc0

6 files changed

Lines changed: 452 additions & 4 deletions

File tree

.goose/GOOSE-project.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,15 @@ use qwen-reason as your lead model for orchestration, planning, architecture dec
1010

1111
use llama3.1:8b as worker for file operations, formatting, simple tasks
1212

13-
use mistral-nemo for mid-tier work: code review, test writing, structured output
14-
1513
do NOT use llama3.1:8b for orchestration or complex reasoning — it will hallucinate the architecture
1614

15+
for pipeline recipe runs:
16+
- snap-ingest runs on llama3.1:8b (local, free)
17+
- snap-classify runs on bytedance-vision (bytedance-seed/seed-2.0-mini via openrouter)
18+
- snap-reconstruct runs on bytedance-reason (bytedance-seed/seed-2.0-lite via openrouter)
19+
- snap-pipeline orchestrator runs on qwen-reason
20+
- daily cost cap: $0.01 enforced via valkey key "snap:daily_cost"
21+
1722
### privacy zones — inviolable
1823

1924
- LOCAL: google creds, raw screenshots, EXIF data → local models only

GOOSE.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ all external model calls go through goose-proxy at localhost:4000
3030

3131
| Alias | Model | Use Case |
3232
|-------|-------|----------|
33-
| `bytedance-vision` | bytedance/ui-tars-1.5-7b | screenshot classification, UI recognition |
34-
| `bytedance-reason` | bytedance/seed-oss-36b-instruct | game reconstruction, analysis reasoning |
33+
| `bytedance-vision` | bytedance-seed/seed-2.0-mini | screenshot classification, image input support |
34+
| `bytedance-reason` | bytedance-seed/seed-2.0-lite | game reconstruction, analysis reasoning |
3535

3636
daily cost cap: $0.01 — enforced by goose-proxy via valkey counter
3737

recipes/snap-classify.yaml

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
version: "1.0.0"
2+
title: "Snap Classify — vision classification + metadata extraction"
3+
description: |
4+
For each queued screenshot: strip EXIF + crop (LOCAL zone → HYBRID zone),
5+
send sanitized image to bytedance-vision for snap/not-snap classification,
6+
extract game metadata (turn, locations, cards) from confirmed snap screenshots,
7+
and write classification results to staging/classified/.
8+
9+
instructions: |
10+
# Role
11+
You are the classification agent for the Marvel Snap pipeline. You operate at
12+
the LOCAL→HYBRID boundary. You preprocess raw images (strip EXIF, crop to game
13+
region) before any external model call. You NEVER send raw images to external
14+
models. After preprocessing, you call bytedance-vision via goose-proxy for
15+
classification and metadata extraction.
16+
17+
# Instructions
18+
For each file in {{ queue_dir }}/manifest.jsonl:
19+
1. Read the source file from {{ queue_dir }}.
20+
2. Preprocess: strip EXIF metadata, crop to game region (top 80% of image width,
21+
centered). Write sanitized image to a temp path in staging/sanitized/.
22+
3. Send the sanitized image to bytedance-vision with the classification prompt below.
23+
4. Parse the response — check is_snap and confidence fields.
24+
5. If is_snap=true AND confidence >= {{ confidence_threshold }}:
25+
- Send sanitized image to bytedance-vision for metadata extraction.
26+
- Parse metadata response — turn_number, locations[], cards_played[].
27+
6. Write a result JSON to {{ output_dir }}/<filename>.json conforming to
28+
fixtures/sample_classification_snap.json or sample_classification_not_snap.json.
29+
7. Append result summary to {{ output_dir }}/results.jsonl.
30+
31+
Classification prompt (send with image):
32+
"Is this a Marvel Snap screenshot showing an active game? Answer in JSON:
33+
{\"is_snap\": true/false, \"confidence\": 0.0-1.0, \"reason\": \"brief explanation\"}"
34+
35+
Metadata extraction prompt (send with image, only if is_snap=true):
36+
"Extract game state from this Marvel Snap screenshot. Answer in JSON following this schema:
37+
{\"turn_number\": int, \"locations\": [{\"name\": str, \"player_power\": int, \"opponent_power\": int}],
38+
\"cards_played\": [{\"card_name\": str, \"location_index\": int, \"energy_cost\": int, \"by_player\": bool}],
39+
\"confidence_scores\": {\"turn_number\": float, \"location_names\": float, \"card_names\": float}}"
40+
41+
For any field where confidence < 0.6: set value to null. Never fabricate data.
42+
43+
# End goal
44+
All queued screenshots have a classification result JSON in {{ output_dir }}.
45+
Snap screenshots have metadata extracted. results.jsonl has one entry per file.
46+
No raw image data sent to external models.
47+
48+
# Narrowing
49+
Never send raw unprocessed images to bytedance-vision.
50+
If goose-proxy is unreachable, halt and report — do not skip preprocessing.
51+
If confidence < {{ confidence_threshold }}: classify as not-snap, do not extract metadata.
52+
Do not infer or fill missing fields — use null with a low confidence score.
53+
Check daily cost cap: if valkey key "snap:daily_cost" >= 0.01, halt classification
54+
and report the cap hit. Do not exceed $0.01/day in external model calls.
55+
56+
parameters:
57+
- key: queue_dir
58+
input_type: string
59+
requirement: optional
60+
default: "staging/queued"
61+
description: "Directory containing queued screenshots from snap-ingest"
62+
- key: output_dir
63+
input_type: string
64+
requirement: optional
65+
default: "staging/classified"
66+
description: "Output directory for classification results"
67+
- key: confidence_threshold
68+
input_type: string
69+
requirement: optional
70+
default: "0.7"
71+
description: "Minimum confidence to accept a snap classification (0.0-1.0)"
72+
73+
extensions:
74+
developer:
75+
type: builtin
76+
name: developer
77+
timeout: 600
78+
valkey:
79+
name: valkey
80+
type: streamable_http
81+
uri: http://host.docker.internal:8110/mcp
82+
timeout: 120
83+
84+
settings:
85+
goose_provider: "openai"
86+
goose_model: "bytedance-vision"
87+
temperature: 0.1
88+
89+
prompt: |
90+
Begin snap classification.
91+
92+
Queue dir: {{ queue_dir }}
93+
Output dir: {{ output_dir }}
94+
Confidence threshold: {{ confidence_threshold }}
95+
96+
Read the manifest, preprocess each image (strip EXIF + crop), classify with bytedance-vision,
97+
extract metadata from confirmed snap screenshots. Write results. Report counts.
98+
99+
Before any external model call: check valkey key "snap:daily_cost".
100+
If cost >= 0.01, halt and report the cap hit.

recipes/snap-ingest.yaml

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
version: "1.0.0"
2+
title: "Snap Ingest — local screenshot queue"
3+
description: |
4+
Scans staging/raw/ for new Marvel Snap screenshots, validates each file
5+
(image format, non-empty), and queues them to staging/queued/ with a
6+
manifest entry. Runs entirely local — no external model calls. Zero cost.
7+
8+
instructions: |
9+
# Role
10+
You are the ingest agent for the Marvel Snap pipeline. You handle the LOCAL
11+
zone only: raw screenshots on disk. No network, no external models. You read
12+
from staging/raw/, validate, and move to staging/queued/. You never send raw
13+
images outside this step.
14+
15+
# Instructions
16+
Scan {{ staging_dir }} for image files (.jpg, .jpeg, .png). For each file:
17+
- confirm it is a valid image (non-zero size, readable)
18+
- check it is not already present in {{ queue_dir }} (skip duplicates)
19+
- copy it to {{ queue_dir }} preserving the original filename
20+
- append an entry to {{ queue_dir }}/manifest.jsonl
21+
22+
Manifest entry format (one JSON object per line):
23+
{"filename": "<name>", "source_path": "<original>", "queued_at": "<ISO timestamp>", "size_bytes": <n>}
24+
25+
# Steps
26+
1. List all .jpg/.jpeg/.png files in {{ staging_dir }} using the developer extension.
27+
2. Read {{ queue_dir }}/manifest.jsonl if it exists — extract already-queued filenames.
28+
3. For each new file: read its size, copy to {{ queue_dir }}, append to manifest.
29+
4. Report: total found, already queued (skipped), newly queued, any errors.
30+
31+
# End goal
32+
All new screenshots from {{ staging_dir }} are present in {{ queue_dir }} with
33+
manifest entries. No file is processed twice. No network calls made.
34+
35+
# Narrowing
36+
Do not modify or delete files in {{ staging_dir }}.
37+
Do not call any external model or make HTTP requests.
38+
Do not process files other than .jpg, .jpeg, .png.
39+
If {{ staging_dir }} is empty, report 0 files found and exit cleanly.
40+
41+
parameters:
42+
- key: staging_dir
43+
input_type: string
44+
requirement: optional
45+
default: "staging/raw"
46+
description: "Directory containing raw screenshots to ingest"
47+
- key: queue_dir
48+
input_type: string
49+
requirement: optional
50+
default: "staging/queued"
51+
description: "Output directory for queued screenshots"
52+
53+
extensions:
54+
developer:
55+
type: builtin
56+
name: developer
57+
timeout: 600
58+
59+
settings:
60+
goose_provider: "ollama"
61+
goose_model: "llama3.1:8b"
62+
temperature: 0.1
63+
64+
prompt: |
65+
Begin snap ingest.
66+
67+
Staging dir: {{ staging_dir }}
68+
Queue dir: {{ queue_dir }}
69+
70+
Scan for new screenshots, skip duplicates, queue all new files, update manifest.
71+
Report counts at the end.

recipes/snap-pipeline.yaml

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
version: "1.0.0"
2+
title: "Snap Pipeline — full orchestrator"
3+
description: |
4+
Orchestrates the full Marvel Snap Cybernetic Loop: ingest → classify → reconstruct.
5+
Runs snap-ingest, then snap-classify, then snap-reconstruct in sequence.
6+
Uses qwen-reason for orchestration decisions. Sub-recipes handle their own models.
7+
Reports a pipeline summary on completion.
8+
9+
instructions: |
10+
# Role
11+
You are the snap pipeline orchestrator. You coordinate three sub-recipe phases in
12+
sequence: ingest, classify, reconstruct. You do not process images or build game
13+
records yourself — you dispatch to sub-recipes, collect results, and surface the
14+
pipeline summary. You run on qwen-reason for decision-making.
15+
16+
# Instructions
17+
Run the three pipeline phases in sequence. After each phase: check the result.
18+
If a phase reports 0 items processed (and the previous phase produced output),
19+
halt and surface the issue — do not proceed with empty data.
20+
21+
Before classify phase: check valkey "snap:daily_cost". If >= 0.01, halt and report
22+
cost cap hit — do not run classify or reconstruct.
23+
24+
# Steps
25+
1. Invoke ingest sub-recipe with staging_dir={{ staging_dir }}, queue_dir={{ queue_dir }}.
26+
Collect: queued_count, skipped_count, errors.
27+
If queued_count == 0: report "nothing to process" and exit cleanly.
28+
29+
2. Check daily cost: read valkey key "snap:daily_cost". If >= 0.01, report cap and halt.
30+
31+
3. Invoke classify sub-recipe with queue_dir={{ queue_dir }}, output_dir={{ classified_dir }},
32+
confidence_threshold={{ confidence_threshold }}.
33+
Collect: classified_count, snap_count, not_snap_count, cost_incurred.
34+
Update valkey "snap:daily_cost" += cost_incurred.
35+
If snap_count == 0: report "no snap screenshots found" and exit cleanly.
36+
37+
4. Invoke reconstruct sub-recipe with classified_dir={{ classified_dir }},
38+
output_dir={{ output_dir }}, session_window_minutes={{ session_window_minutes }}.
39+
Collect: sessions_found, records_built, records_stored, validation_failures.
40+
41+
5. Emit pipeline summary JSON.
42+
43+
# End goal
44+
Full pipeline run complete. Pipeline summary JSON emitted with counts for all phases.
45+
Game records stored in qdrant snap-game-records.
46+
47+
# Narrowing
48+
Do not skip phases or short-circuit the sequence.
49+
Do not call external models directly — all model calls happen inside sub-recipes.
50+
If any sub-recipe returns an error: surface the error with the phase name and halt.
51+
Daily cost cap is enforced at step 2 — do not bypass it.
52+
53+
parameters:
54+
- key: staging_dir
55+
input_type: string
56+
requirement: optional
57+
default: "staging/raw"
58+
description: "Raw screenshots directory"
59+
- key: queue_dir
60+
input_type: string
61+
requirement: optional
62+
default: "staging/queued"
63+
description: "Ingest output / classify input directory"
64+
- key: classified_dir
65+
input_type: string
66+
requirement: optional
67+
default: "staging/classified"
68+
description: "Classify output / reconstruct input directory"
69+
- key: output_dir
70+
input_type: string
71+
requirement: optional
72+
default: "staging/reconstructed"
73+
description: "Reconstruction output directory for GameRecord JSON files"
74+
- key: confidence_threshold
75+
input_type: string
76+
requirement: optional
77+
default: "0.7"
78+
description: "Vision classification confidence threshold"
79+
- key: session_window_minutes
80+
input_type: string
81+
requirement: optional
82+
default: "10"
83+
description: "Screenshot grouping window in minutes"
84+
85+
sub_recipes:
86+
- name: "ingest"
87+
path: "recipes/snap-ingest.yaml"
88+
values: {}
89+
90+
- name: "classify"
91+
path: "recipes/snap-classify.yaml"
92+
values: {}
93+
94+
- name: "reconstruct"
95+
path: "recipes/snap-reconstruct.yaml"
96+
values: {}
97+
98+
extensions:
99+
developer:
100+
type: builtin
101+
name: developer
102+
timeout: 600
103+
valkey:
104+
name: valkey
105+
type: streamable_http
106+
uri: http://host.docker.internal:8110/mcp
107+
timeout: 120
108+
qdrant-shared:
109+
name: qdrant-shared
110+
type: streamable_http
111+
uri: http://host.docker.internal:8102/mcp/
112+
timeout: 300
113+
114+
settings:
115+
goose_provider: "openai"
116+
goose_model: "qwen-reason"
117+
temperature: 0.2
118+
119+
prompt: |
120+
Begin snap pipeline.
121+
122+
Staging dir: {{ staging_dir }}
123+
Queue dir: {{ queue_dir }}
124+
Classified dir: {{ classified_dir }}
125+
Output dir: {{ output_dir }}
126+
Confidence threshold: {{ confidence_threshold }}
127+
Session window: {{ session_window_minutes }} minutes
128+
129+
Run phases in sequence: ingest → classify → reconstruct.
130+
Check cost cap before classify. Halt if any phase errors.
131+
Emit pipeline summary JSON on completion.
132+
133+
response:
134+
json_schema:
135+
type: object
136+
properties:
137+
pipeline_run_id:
138+
type: string
139+
description: "Unique ID for this pipeline run (ISO timestamp)"
140+
phases:
141+
type: object
142+
properties:
143+
ingest:
144+
type: object
145+
properties:
146+
queued_count: {type: number}
147+
skipped_count: {type: number}
148+
errors: {type: number}
149+
classify:
150+
type: object
151+
properties:
152+
classified_count: {type: number}
153+
snap_count: {type: number}
154+
not_snap_count: {type: number}
155+
cost_incurred: {type: number}
156+
cost_cap_hit: {type: boolean}
157+
reconstruct:
158+
type: object
159+
properties:
160+
sessions_found: {type: number}
161+
records_built: {type: number}
162+
records_stored: {type: number}
163+
validation_failures: {type: number}
164+
total_cost_today:
165+
type: number
166+
description: "Running daily cost after this run (from valkey)"
167+
required:
168+
- pipeline_run_id
169+
- phases

0 commit comments

Comments
 (0)