For the complete end-to-end state machine and a full real task139 optimization example,
read AGENTUSE.md. Use the repository skills in skills/neurogolf-researcher/ and
skills/neurogolf-optimizer/ rather than reconstructing the workflow from chat history.
You are a Researcher agent in the NeuroGolf 2026 competition harness. Your job: take ARC-AGI transformation tasks and produce ONNX neural-network files that solve them with a lower cost (params + memory bytes) than the current best solution.
Competition URL: https://www.kaggle.com/competitions/neurogolf-2026
Python (EXACT Kaggle match):
/Users/yeyang/miniconda3/envs/neurogolf/bin/python
Project root:
/Users/yeyang/Desktop/uzh26s/golf/
Key locations:
data/task{001..400}.json β task definitions (train/test/arc-gen pairs)
knowledge/task_{001..400}/ β your persistent memory per task
submissions/best/onnx/ β canonical deployable best ONNX files (400 files)
submissions/best/submission.zip β canonical deployable bundle
submissions/best-unvarified/ β local best bundle with not-yet-LB-verified wins
tools/ β your tools (see Β§4)
strategy.md β competition strategies and ONNX patterns
competition_description.md β full competition rules and pitfalls
local_score.py β official local scorer
Kaggle credentials:
~/.kaggle/access_token β bearer token for API calls
~/.kaggle/kaggle.json β API key (username: yyleaves)
import math
# cost = total_params + total_memory_bytes (intermediates only, NOT input/output)
score = max(1.0, 25.0 - math.log(max(1.0, cost)))
# Max: 25.0/task (cost=0), total 400Γ25=10000
# Current deployable best is tracked by submissions/best/submission.zip.
# Not-yet-verified local wins are tracked by submissions/best-unvarified/submission.zip.Target scores (what you're aiming for):
- 22+ pts β cost < 110 (very tight, but achievable on simple tasks)
- 20+ pts β cost < 800
- 18+ pts β cost < 6,000
- 16+ pts β cost < 45,000
Before optimizing, distinguish deployable best from local-only experiments.
cat knowledge/task_233/best_score.txt
python tools/score_task.py 233 submissions/best/onnx/task233.onnxknowledge/task_XXX/best_score.txt must match the deployable ONNX in
submissions/best/onnx/taskXXX.onnx. If a local lookup/fingerprint candidate scores higher
but fails or is unverified on hidden tests, record it in insights.md as a local ceiling,
not as best_score.txt.
python tools/list_tasks.py --bottom 30Pick the lowest-scoring deployable task you haven't worked on this session. Focus on tasks
where score < 14.6 first. Hidden-zero tasks are allowed only if your method is structural
and you keep a deploy-safe baseline until LB confirms the replacement.
python tools/task_brief.py 233 --pending # compact score, cost, top tensors, risk, rephrase hints
python tools/show_task.py 233 # shows grids, current score, cost breakdown
cat knowledge/task_233/description.md # read description + examples
cat knowledge/task_233/insights.md # read attempt history β critical!NEVER skip reading insights.md for the task you are actually editing. The brief tells you which tensor/representation to focus on first, and insights.md tells you what has already been tried and failed. Read only the relevant strategy slices to save tokens:
sed -n '1,140p' strategy.md # current focus + search order
rg -n "task233|Trick 36|Trick 40|Bool output|Sentinel-label|Kaggle Submission" strategy.mdBefore coding, write a compact cost ledger for the current ONNX. Do not skip this, even for small cleanup tasks:
Top tensor:
Shape/dtype/bytes:
Producer and consumers:
Semantic payload:
Cheaper representation:
Expected cost removed:
New tensors/params introduced:
Net score-gain estimate:
Hidden-test risk:
Every candidate must state which counted tensor it deletes. If a proposal cannot name the removed tensor and the added cost, it is only an idea, not an optimization round.
grep -r "color_replace" knowledge/*/description.md | head -10
python tools/list_tasks.py --similar 233Look for tasks with similar transformations that already have good scores. Their ONNX patterns are your starting point.
Read the train/test examples carefully. Ask yourself:
- What is the exact mapping from input to output?
- Does the output size differ from input? (many do)
- Which colors matter? Which are background?
- Is the rule deterministic (same rule for all examples)?
- What edge cases exist (empty grid, single pixel, large grid)?
Read the ARC-GEN generator if available to understand the exact rule.
Search order:
- Representation rephrase first: can the rule move from full one-hot/full grid to scalar label, row/column vector, bbox descriptor, candidate selector, patch writer, direct output writer, tiny classifier, or static relation table?
- Method next: can the transformation be expressed in fewer conceptual steps?
- Memory next: can the largest counted tensor be removed by time-for-space, recomputation, or replacing a full 2D/10-channel canvas with scalars/vectors/descriptors?
- Architecture next: can you crop earlier, shrink dtype, reorder ops, avoid full 30x30 intermediates?
- Detail last: only shave individual nodes after representation/method/architecture have stalled.
For tasks under 20 points, always try both:
- patch the existing best,
- build a fresh rule-specific baseline from scratch.
Fresh baselines regularly beat incremental patches by more than one point.
Write a Python script candidate_N.py that generates candidate_N.onnx. Follow the patterns in strategy.md. Key rules:
Network I/O (mandatory):
- Input:
[1, 10, 30, 30]float32 β one-hot encoded colors - Output:
[1, 10, 30, 30]with a scorer-supported numeric or BOOL dtype. The scorer decodes every output withpred > 0; BOOL, UINT8, INT32, FLOAT16, and FLOAT32 outputs are valid when ONNX Runtime supports the graph. The declared output dtype must match the runtime dtype exactly; never rely on misleading metadata. - Input tensor name:
"input", output tensor name:"output"
Banned ops / representations: If and every node carrying a GRAPH/GRAPHS
subgraph attribute, Loop, Scan, NonZero, Unique, Script, Function, Compress, any
Sequence op, and TopK. Sparse ONNX initializers are also forbidden;
every initializer must use a dense tensor representation.
Kaggle crash hazards (corrected 2026-06-07 by binary search):
Sqrt is banned on Kaggle and causes "non-zero exit code". Also guard any
data-derived divisor before Div/Mod; hidden cases with Mod(x, 0) or division
by zero can crash even if local ARC-GEN passes. Einsum, OneHot, Mod, and
GreaterOrEqual are allowed when otherwise well-formed and have LB-verified uses.
All tensor shapes must be static (no symbolic dims).
Start with the simplest possible solution β often just a few Conv/Where/Pad nodes. Complexity is the enemy. See strategy.md Β§ONNX Patterns.
High-value patterns from recent practice:
- Slice/crop first, then compute. A 6x6 intermediate can be 25x cheaper than full 30x30.
- Prefer uint8/bool label pipelines and make
Equal(label, arange10)or bool Pad the final output. - Use sentinel labels outside the active output so the scorer sees zero-hot padding.
- Collapse color detection to scalar/vector reductions before slicing many channels.
- Select the winning candidate in descriptor space first, then render only that branch. Avoid
building
[K,H,W]or[10,H,W]branch stacks and choosing afterward. - Use free input/output aggressively: unchanged regions should often be preserved with
Where(edit_mask, edit_value, input), and one-hot expansion/Pad/Equal should be the final graph output whenever possible. - Treat tiny MLP/classifier/LUT selectors as time-for-space only after a collision audit:
feature vectors must map to a unique structural action over train/test/ARC-GEN. Otherwise
the candidate is
LOCAL_ONLYorLB_PROBE, not a deploy-safe win. - Defer visible-set fingerprint/LUT work until conventional structural methods have been applied broadly. Use fingerprinting only as a temporary diagnostic/local ceiling unless the key-space coverage is proven or LB confirms it.
python tools/score_task.py 233 candidate_233.onnxThis gives you local score and cost breakdown. Only proceed if the score is strictly better than the current best (read from knowledge/task_233/best_score.txt).
# Save candidate
cp candidate_233.onnx knowledge/task_233/candidate.onnx
# Update insights with this version
python tools/update_insights.py 233 --score 14.5 --desc "Replaced Where with direct Conv, saved 50k bytes"
# If deploy-safe locally but not LB-confirmed yet: update best-unvarified only.
python tools/update_best.py 233 knowledge/task_233/candidate.onnx --score 14.5000
# Upload submissions/best-unvarified/submission.zip to test the accumulated unverified wins.If the win is local-only, visible-fingerprint based, or hidden-risk:
- do not run
update_best.pyunless the user explicitly wants an LB probe, - do not change
best_score.txt, - write the attempt and score in
insights.md, - keep the ONNX under
candidates/orknowledge/task_XXX/candidate.onnx.
Don't wait for LB confirmation for clearly structural wins. Queue the full-bundle pending
submission via best-unvarified and move to the next task. For hidden-zero/overfit-risk tasks, expect an LB probe
before promoting the candidate to canonical deployable best.
python tools/confirm_lb.py --pending task233_14.5000 6418.23
# Or, if the human submitted submissions/best-unvarified/submission.zip successfully:
python tools/best_unvarified.py promote --all --lb-total 6418.23This:
- Updates knowledge/task_233/best_score.txt
- Updates knowledge/task_233/insights.md
- Copies candidate.onnx into the new
submissions/s_<LB>/onnx/task233.onnx - Marks the task verified in submissions/best/manifest.json
If the human reports a hidden-test failure or 0.00, immediately restore deployable best for
that task to the last LB-safe ONNX and score; keep the failed model only as a diagnostic in
insights.md.
| Tool | Usage | What it does |
|---|---|---|
list_tasks.py |
python tools/list_tasks.py --bottom 30 |
Priority queue, worst scores first |
bucket_brief.py |
python tools/bucket_brief.py --start 1 --end 20 --pending |
Bucket sum + optimization-potential ranking by removable tensors |
task_brief.py |
python tools/task_brief.py N --pending [--json] |
Compact score/cost/top-tensor/risk/rephrase brief for one task |
show_task.py |
python tools/show_task.py N |
ASCII grid + current score + cost |
score_task.py |
python tools/score_task.py N file.onnx |
Local score for one task |
analyze_onnx.py |
python tools/analyze_onnx.py file.onnx |
Per-node cost breakdown |
update_best.py |
python tools/update_best.py N file.onnx --score X [--verified] |
Without --verified, update best-unvarified; with --verified, update canonical best |
best_unvarified.py |
python tools/best_unvarified.py promote --all --lb-total LB |
Manage/prove/promote accumulated unverified local wins |
record_submission.py |
python tools/record_submission.py N file.onnx --base best --new-score X |
Build one-off full 400-file pending bundle |
stage_submission.py |
python tools/stage_submission.py N file.onnx |
Temporary full-bundle staging; prefer record_submission for manual LB queue |
submit_task.py |
python tools/submit_task.py staged/ |
Submit to Kaggle + poll LB |
update_insights.py |
python tools/update_insights.py N --score X --desc Y |
Log attempt to insights.md |
update_strategy.py |
python tools/update_strategy.py N WIN old new "lesson" [--reusable] |
Log chronology to logs/strategy_history.md; --reusable also updates strategy.md |
confirm_lb.py |
python tools/confirm_lb.py --pending taskNNN_X.YYYY LB_TOTAL |
After LB confirm, update all state |
import onnx
from onnx import helper, TensorProto
import numpy as np
# Network constants
SHAPE = [1, 10, 30, 30] # always fixed
dtype = TensorProto.FLOAT
# Define I/O
x = helper.make_tensor_value_info("input", dtype, SHAPE)
y = helper.make_tensor_value_info("output", dtype, SHAPE)
# --- YOUR LOGIC HERE ---
# Add initializers (weights) and nodes
graph = helper.make_graph(nodes, "graph", [x], [y], initializers)
model = helper.make_model(graph, ir_version=10,
opset_imports=[helper.make_opsetid("", 10)])
onnx.save(model, "candidate_N.onnx")- Input
[1,10,30,30]= 36,000 float32 β excluded from cost (free!) - Output
[1,10,30,30]β excluded from cost (free), regardless of its supported dtype - Every intermediate tensor [1,10,30,30] float32 costs 144,000 bytes
- Shrink intermediates! Work at the actual grid size, not 30Γ30.
- 3Γ3 intermediate = only 10Γ3Γ3Γ4 = 360 bytes vs 144,000 bytes
- Pad back to 30Γ30 at the very end (the padding IS the output β free)
Reshape/Identity/SqueezeDO count toward costExpandis zero-copy broadcast β prefer over Tile
# Pattern: direct color mapping (e.g. "change color 3 to color 7")
# Use a 1Γ1 Conv to remix channels β cheap!
# Pattern: spatial shift (move everything N pixels)
# Use Pad + Slice at the input scale, NOT 30Γ30 scale
# Pattern: crop β process small β pad back
# Slice to bounding box β cheap ops β Pad to 30Γ30
# Pattern: identity + small modification
# Copy input β modify a few specific pixels with Where
# Pattern: tiling
# Slice one tile β Expand/Tile to fill 30Γ30
# Pattern: logical ops on channels (AND, OR, MAX)
# Cast to uint8 β bitwise β Cast back
# Pattern: zero-parameter solution (pure hardcoded transform)
# Use only Constant + Slice + Concat + Pad β 0 initializers!bool= 1 byte vsfloat32= 4 bytes β 4Γ cheaper for boolean masksuint8= 1 byte for color indices- Cast:
output.astype(float)at the end is cheap
-
Conv bias must have
dims[0] == output_channelsβ otherwise it poisons all other tasks in the bundle (they score 0 silently). -
All tensor shapes must be fully static β no
dim_param, no symbolic dims. After adding value_info, every dim must havedim_value > 0. -
Never use banned ops/representations:
Ifor anyGRAPH/GRAPHSsubgraph, Loop, Scan, NonZero, Unique, Script, Function, Compress, Sequence*, orTopK. The official local scoring path returns no score/memory for subgraphs, so branch activations cannot be hidden from the ledger. Never emit sparse initializers; all ONNX initializers must be dense. Also avoidSqrtfor Kaggle, and guard data-derived divisors beforeDiv/Mod. -
Don't memorize visible examples β the LB uses hidden private tests. A lookup table that hardcodes each train output will score 0 on LB. Look for the GENERAL rule.
-
Score locally first, ALWAYS β never stage a candidate without confirming it beats the deployable current best.
-
Reshape/Identity/Squeezecost memory β they're not free. Minimize their use. -
Profile traces β
local_score.pycleans them up. If you run ORT sessions manually, delete the*.jsontrace files after. -
Full-bundle packaging only for LB probes β Kaggle debugging packages should contain all 400 task ONNX files. Single-ONNX zips produced misleading 0.00 results in practice.
-
One task per session focus β don't try to improve 5 tasks at once. Go deep on one, then move.
-
Do not let dashboard cache drive decisions β
knowledge/total_score.txtis a cache. The authoritative deployable total is a full local score ofsubmissions/best/submission.zip.
Codex does not have persistent memory across sessions. Manage it this way:
- Your persistent memory = knowledge/task_XXX/insights.md β always read it first.
- Cross-task patterns = strategy.md β read relevant sections, not the whole file every time.
- Avoid loading multiple task JSONs β only load the task you're currently working on.
- Keep candidate scripts focused β one
candidate_N.pyper task, delete after confirmed. - Session discipline: One task β study β build β validate β stage β next task. Don't drift.
Use subagents when they can run in parallel on a bounded side task:
- explorer: infer exact rule/invariants, find similar solved tasks, summarize failure history;
- worker: implement one candidate file with a clearly owned path;
- verifier: run scoring and check hidden-risk packaging while the main agent continues coding.
Do not paste the whole repository context into subagents. Pass a small prompt with:
- constant competition constraints,
- task id and current score,
description.mdand the relevant parts ofinsights.md,- the specific file path they own,
- the expected output format.
Keep invariant boilerplate at the top of reusable skill/prompt text and task-specific material below it, so cached KV is reusable across tasks.
At the start of each session, run:
python tools/bucket_brief.py --start 1 --end 20 --pending
python tools/list_tasks.py --bottom 30Pick your task from the brief/ranking and commit to it for the session. Pass
task_brief.py --json output to subagents instead of pasting full strategy or long insights files.
After the human confirms LB improvement, they will:
git add -A
git commit -m "task{N}: {old_score:.2f} -> {new_score:.2f} (+{delta:.2f}) | LB {new_lb_total:.2f}"
git push origin mainThe knowledge base, solution files, and submission zip all get committed together.
You do NOT need to run git commands. Just stage your candidate correctly and log the attempt.
| Metric | Current | Target |
|---|---|---|
| Total LB score | 7,350.529365 | 7,500+ |
| Local metadata sum | 7,350.364949 | align with LB cache after sync |
| Tasks scoring β₯ 20 pts | 67 | 100+ |
| Tasks scoring β₯ 18 pts | 221 | 280+ |
| Worst task score | 14.5542 (t233) | 16+ |
The gap from the current LB cache to 7,500 is about 149.47 points across 400 tasks. The cheapest gains are now mostly representation/memory rewrites on mid-tier tasks, not simple baseline recovery.
Priority order: bottom 30 tasks β tasks below 16 β memory-heavy tasks below 18.5 β then bucket-level campaigns where cross-task tricks can transfer.
10. Overfit-Risk and Hidden-Zero Tasks
These tasks are known to fail on LB despite passing all local arc-gen examples:
Overfit-risk (aggressive hidden-test cases exist):
018, 048, 096, 118, 192, 219, 285, 319, 355, 359
Known hidden-zero (Andrey's solutions scored 0 on hidden tests):
076, 157, 209, 219, 255, 366
For these tasks, your solution must implement the GENERAL rule, not a pattern that only works on visible examples. After building, test on 50+ arc-gen examples manually to check robustness.
Recent hidden-test lesson:
task157andtask158visible-set fingerprint local wins hidden-scored 0; deployable best must remain the recovered baseline until a structural solver passes LB.task159was fixed by replacing visible-set fingerprinting with a structural renderer and was LB-confirmed.
Current priority:
- Optimize with conventional methods first across the task set: understand the rule, extract parameters structurally, crop early, shrink dtype/intermediates, and build fresh baselines.
- Revisit fingerprint/key-space coverage later, after normal structural optimization is exhausted.