commit f8b4e5e09e14625dcca6fd4d2846c534a2115dd9 Author: Meta Vens Date: Mon May 4 12:20:23 2026 +0100 Scaffold VibeGeometry diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..81c9220 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +__pycache__/ +*.pyc +.venv/ +node_modules/ +state/scratch-compaction-*.md +experiments/renders/ +experiments/blender-cache/ +*.blend1 +*.blend2 diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..f36efd0 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,144 @@ +# VibeGeometry Instructions + +## Project Purpose + +VibeGeometry is a research and implementation home for agent-authored Blender +scene generation. + +The core problem is not "make a Python script that creates something shiny." +The core problem is building a pipeline where an agent can understand a visual +target, choose a procedural geometry strategy, construct inspectable Blender +5.0+ geometry-node graphs through Python, render the result, and revise from +evidence without accumulating clever junk. + +## Canonical State + +- Treat `state/map.yaml` as the canonical project map. +- Treat `state/scratch.md` as disposable working memory for one bounded + subgoal. +- Treat `state/evidence.jsonl` as the distilled durable ledger of what was + learned, verified, rejected, or accepted. +- Treat `notes/vibegeometry-implementation-plan.md` as the current + implementation plan. +- Update `state/map.yaml` when project understanding changes. +- Add evidence after meaningful research, implementation, verification, or + rejected paths, but keep it distilled. Routine proof belongs in git history, + renders, smoke artifacts, or targeted logs unless it changes what the next + session should believe. +- Do not turn notes, map, handoff, and evidence into four copies of the same + brain. Distinct jobs or bust. + +## Important Paths + +- Project root: `E:\Projects\VibeGeometry` +- Canonical map: `E:\Projects\VibeGeometry\state\map.yaml` +- Scratch surface: `E:\Projects\VibeGeometry\state\scratch.md` +- Distilled evidence ledger: `E:\Projects\VibeGeometry\state\evidence.jsonl` +- Branch ledger: `E:\Projects\VibeGeometry\state\branches.json` +- Handoff summary: `E:\Projects\VibeGeometry\notes\fresh-workspace-handoff.md` +- System map: `E:\Projects\VibeGeometry\notes\vibegeometry-current-system-map.md` +- Implementation plan: `E:\Projects\VibeGeometry\notes\vibegeometry-implementation-plan.md` +- Architecture rationale: `E:\Projects\VibeGeometry\notes\architecture-rationale.md` +- State CLI: `E:\Projects\VibeGeometry\tools\vibegeometry_state.py` +- Pre-compaction helper: `E:\Projects\VibeGeometry\tools\vibegeometry_prepare_compaction.py` + +## Useful Commands + +Use the bundled Python runtime if `python` is not on PATH: + +```powershell +npm run state:status +npm run state:prepare-compaction +& 'C:\Users\Meta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\python.exe' '.\tools\vibegeometry_state.py' add-evidence --type research --status ok --note '...' +& 'C:\Users\Meta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\python.exe' '.\tools\vibegeometry_state.py' add-branch --id branch-id --hypothesis '...' +``` + +## Session Bootstrap And Re-entry Protocol + +On fresh session load, do this before implementation: + +1. read: + - `state/map.yaml` + - `notes/fresh-workspace-handoff.md` + - `notes/vibegeometry-current-system-map.md` + - `notes/vibegeometry-implementation-plan.md` +2. run: + - `npm run state:status` +3. restate the current next action from the persisted state before starting + edits +4. if the user only asked to rehydrate or reorient, stop after orientation and + wait for an explicit continue instruction + +After compaction, resume, or suspicious continuity loss: + +1. rerun `npm run state:status` +2. reread `state/map.yaml` and `notes/fresh-workspace-handoff.md` +3. treat the persisted next action as authoritative unless fresh repo evidence + contradicts it + +When context pressure is clearly rising: + +1. stop broad exploration +2. narrow the active move to one bounded pipeline organ +3. persist map or handoff updates, plus distilled evidence only when the lesson + changes future belief, before the blackout lands + +When the user says to prepare for imminent compaction: + +1. immediately write hot live context from memory into a new + `state/scratch-compaction-.md` file before reading files, running + status checks, or doing tidy persistence work +2. run `tools/vibegeometry_prepare_compaction.py` before editing persistence + surfaces +3. use its warnings as the checklist for map, handoff, scratch, evidence, and + git hygiene +4. update only the state that actually needs to change +5. after hot context has been folded into canonical persisted state, delete the + `state/scratch-compaction-.md` file so stale emergency memory does not + linger +6. run `tools/vibegeometry_prepare_compaction.py` again after edits and scratch + cleanup +7. fix errors, address warnings, and commit the completed persistence pass + unless the work is deliberately mid-surgery + +## Operating Discipline + +- Before substantial edits, restate the current mechanism and intended change. +- Prefer one clear hypothesis per iteration. +- Verify with checks that reflect the visual and procedural goal, not just + proxy success. +- Revert or discard changes that do not clearly improve the target. +- If the diff grows while understanding shrinks, stop implementation and switch + to diagnosis. +- Keep maps and prose together; do not replace useful maps with prose-only + explanations. +- Commit completed work before it rots in the worktree unless the task is + deliberately mid-surgery or the user asked to leave changes uncommitted. +- Push after major completed passes when a remote exists, unless the user asked + not to or there is a concrete reason to keep the commit local for a moment. +- Before handoff, compaction, or phase boundaries, sync `state/map.yaml`, add + distilled evidence when the lesson changes future belief, refresh + `notes/fresh-workspace-handoff.md`, and make the next action explicit. + +## Blender And Geometry-Node Doctrine + +- Target Blender 5.0+ unless a task explicitly chooses another version. +- Prefer geometry nodes for procedural geometry and Python for construction, + orchestration, parameterization, render setup, and validation. +- Keep generated node groups named, inspectable, and reusable. A script that + creates an opaque graph is just a new kind of mess with sockets. +- Preserve attribute flow explicitly. Name important attributes, document where + they are created, transformed, consumed, and exposed. +- Prefer proven Blender node idioms and current API guidance over invented + graph tricks. +- Treat reference images as evidence-bearing inputs. Record what visual traits + are being targeted: silhouette, repetition, surface breakup, scale language, + material response, lighting, camera, and color. +- Render review is mandatory for visual claims. A script that runs is not a + scene that works. +- When a generated graph misses the target, revise one hypothesis at a time: + geometry structure, distribution logic, material, lighting, camera, or scale. +- Keep failed render attempts only when they teach a durable lesson. Otherwise + archive lightly or delete them; the repo is not a museum of almost. +- Do not bury visual intent inside Python constants. Scene briefs, graph + manifests, and review artifacts should expose what the agent meant to build. diff --git a/README.md b/README.md new file mode 100644 index 0000000..b75813c --- /dev/null +++ b/README.md @@ -0,0 +1,109 @@ +# VibeGeometry + +VibeGeometry is a research and implementation home for agent-authored Blender +scene generation. + +The project exists to build a pipeline where Codex, or any other sufficiently +useful local agent, can take reference images and visual instructions, plan a +procedural geometry strategy, generate Blender Python scripts, construct +complex geometry-node graphs, render the result, and revise from evidence +instead of shrugging confidently at a pile of decorative node spaghetti. + +The target is Blender 5.0 and later. The repo treats geometry nodes as the +main modeling surface, Python as the graph construction and orchestration +language, and rendered output as the evidence loop. + +## Core Idea + +VibeGeometry separates five things that are easy to smear together: + +1. **Visual target** + What the result should look like, grounded in reference images and explicit + instructions. + +2. **Geometry strategy** + The procedural plan: primitives, fields, instances, curves, surfaces, + simulation zones, attributes, materials, and camera composition. + +3. **Node graph contract** + The legible graph structure that should exist in Blender, including named + groups, exposed parameters, attribute flow, and reusable node idioms. + +4. **Python builder** + The script that creates the scene, builds node groups, links sockets, + assigns materials, places cameras and lights, and saves or renders the + `.blend` result. + +5. **Render review** + What the actual output proves: visual match, graph sanity, missing geometry, + material errors, performance pain, and rejected approaches. + +The script is not the design. The render is not the truth. The node graph is +the machine, and the repo exists to make that machine inspectable. + +## Current Status + +This is an initial scaffold. + +Currently present: + +- persistence surfaces for re-entry, evidence, planning, and compaction hygiene +- starter architecture notes for the Blender scene-generation pipeline +- a repo structure for schemas, prompts, examples, experiments, scripts, tools, + notes, and durable state +- small state inspection and pre-compaction helpers + +Not present yet: + +- Blender Python graph builder library +- scene brief schema +- node graph manifest schema +- render review schema +- reference-image ingestion helpers +- automated Blender render runner +- visual comparison or reviewer loop + +## Repo Tour + +- `AGENTS.md`: operating discipline for humans and agents +- `state/map.yaml`: canonical project map and current next action +- `state/scratch.md`: disposable working memory for one bounded subgoal +- `state/evidence.jsonl`: distilled belief-changing evidence +- `state/branches.json`: active or closed investigation branches +- `notes/fresh-workspace-handoff.md`: compact re-entry packet +- `notes/vibegeometry-current-system-map.md`: current repo machinery +- `notes/vibegeometry-implementation-plan.md`: implementation sequence +- `notes/architecture-rationale.md`: why the persistence surfaces exist +- `docs/architecture/`: pipeline contracts and design notes +- `docs/research/`: Blender geometry-node research notes +- `schemas/`: future JSON schema contracts +- `examples/`: future scene briefs, generated scripts, and review artifacts +- `experiments/`: disposable render attempts and captures +- `prompts/`: reusable agent prompts for scene planning and review +- `scripts/`: utility scripts that are not core persistence machinery +- `tools/vibegeometry_state.py`: state inspection and evidence helper +- `tools/vibegeometry_prepare_compaction.py`: pre-compaction audit helper + +## Useful Commands + +```powershell +npm run state:status +npm run state:prepare-compaction +``` + +To add distilled evidence: + +```powershell +& 'C:\Users\Meta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\python.exe' '.\tools\vibegeometry_state.py' add-evidence --type design --status accepted --note '...' +``` + +## Best Starting Points + +- `AGENTS.md` +- `state/map.yaml` +- `notes/vibegeometry-current-system-map.md` +- `notes/vibegeometry-implementation-plan.md` +- `docs/architecture/pipeline-overview.md` + +For Codex-driven work, read `AGENTS.md` before changing the repo. It contains +the rules that keep the project from turning into a confident procedural hairball. diff --git a/docs/architecture/pipeline-overview.md b/docs/architecture/pipeline-overview.md new file mode 100644 index 0000000..5bdc516 --- /dev/null +++ b/docs/architecture/pipeline-overview.md @@ -0,0 +1,64 @@ +# Pipeline Overview + +VibeGeometry's intended pipeline turns visual intent into Blender geometry-node +machinery, then uses rendered evidence to decide what actually worked. + +```text +reference images + instructions +-> scene brief +-> visual trait extraction +-> geometry strategy +-> node graph manifest +-> Blender Python builder +-> generated scene and render +-> render review +``` + +## Scene Brief + +The scene brief is the target contract. It should say what the scene needs to +look like before it says how to build it. + +Expected contents: + +- reference image paths or URLs +- user instructions +- target visual traits +- constraints and exclusions +- camera and composition notes +- material and lighting expectations +- acceptance criteria +- open questions + +## Geometry Strategy + +The geometry strategy is plain-language reasoning about how the target can be +made procedurally. It should explain primitives, distributions, curves, +surfaces, instances, attributes, material variation, and scale relationships. + +This is where the agent proves it knows what machine it is about to build. + +## Node Graph Manifest + +The node graph manifest is the inspectable design for Blender. It should name: + +- geometry node groups +- exposed sockets and default values +- attribute names and where they flow +- modifiers and object targets +- materials and shader parameters +- reusable graph idioms +- performance risks or version-sensitive API assumptions + +## Python Builder + +The Python builder turns the manifest into a Blender scene. Python owns +construction, orchestration, render setup, and file output. It should not hide +the procedural design in ad hoc mesh mutation unless the scene brief explicitly +requires that escape hatch. + +## Render Review + +Render review compares output to the target. It should identify visual hits, +misses, graph problems, material issues, camera/lighting errors, and the next +single hypothesis to test. diff --git a/docs/research/blender-5-geometry-nodes.md b/docs/research/blender-5-geometry-nodes.md new file mode 100644 index 0000000..4c26e49 --- /dev/null +++ b/docs/research/blender-5-geometry-nodes.md @@ -0,0 +1,15 @@ +# Blender 5 Geometry Nodes Research + +This file is the placeholder for current Blender 5.0+ API and geometry-node +best-practice notes. + +Use it for distilled findings only: + +- confirmed API changes +- node construction gotchas +- geometry-node idioms worth reusing +- rejected approaches that looked plausible and failed +- links to official Blender documentation or source material + +Do not store generic tutorial mush here. The internet already has enough of +that and seems proud of itself. diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..165f7e4 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,11 @@ +# Examples + +Reviewed example artifacts live here. + +Expected subfolders: + +- `scene-briefs/`: target descriptions and acceptance criteria +- `blender/`: generated scripts, manifests, and small `.blend` receipts when useful + +Keep disposable runs in `experiments/` until they teach something worth +promoting. diff --git a/experiments/README.md b/experiments/README.md new file mode 100644 index 0000000..af541f4 --- /dev/null +++ b/experiments/README.md @@ -0,0 +1,7 @@ +# Experiments + +Disposable render attempts, captures, and test scenes live here. + +Promote only reviewed examples or durable lessons. Failed visual attempts are +useful when they change future belief; otherwise they are just ballast with a +timestamp. diff --git a/notes/architecture-rationale.md b/notes/architecture-rationale.md new file mode 100644 index 0000000..abdca86 --- /dev/null +++ b/notes/architecture-rationale.md @@ -0,0 +1,86 @@ +# Architecture Rationale + +This note explains why VibeGeometry uses explicit state surfaces. It is not the +current implementation map and not the roadmap. For those, read: + +- `state/map.yaml` +- `notes/fresh-workspace-handoff.md` +- `notes/vibegeometry-implementation-plan.md` +- `notes/vibegeometry-current-system-map.md` + +## Core Failure + +The central failure mode is local plausibility without global coherence. + +An agent can write plausible Blender Python, create a large node graph, and +still lose the visual machine. Geometry-node work is especially good at this: +the script runs, the graph exists, the render has objects in it, and somehow +the target has been replaced by decorative procedural fog with a confident +variable name. + +The answer is explicit structure: + +- slow-changing map +- disposable scratch +- distilled evidence +- compact handoff +- implementation plan that is not pretending to be a second map +- ruthless deletion when code, graph, or memory stops serving the target + +## State Split + +VibeGeometry treats different kinds of cognition as different artifacts: + +- `map`: canonical system model, invariants, accepted design, rejected paths +- `scratch`: temporary local reasoning for one bounded subgoal +- `evidence`: distilled proof, decisions, rejected paths, and scars that change future belief +- `system map`: source-grounded explanation of the live repo machinery +- `handoff`: compact re-entry packet +- `plan`: distilled forward implementation direction +- `output`: user-visible replies, code edits, commits, renders, and verification artifacts + +The split matters because Blender scene generation has many seductive proxies: +valid Python, non-empty renders, high node counts, pretty materials, and +parameter sliders. None of those prove that the graph still serves the visual +target. + +## Pipeline Principle + +The working loop should be: + +```text +reference images and instructions +-> scene brief +-> visual trait extraction +-> geometry strategy +-> node graph manifest +-> Blender Python builder +-> render evidence +-> review against target +-> revise one hypothesis +``` + +The graph is allowed to be complex. The intent is not. If the agent cannot +describe what geometry flows through each stage and why that stage belongs, +the next move is explanation or simplification, not another compensating node +group with a tragic little name. + +## Compact-Rehydrate-Reorient + +Compaction is a real state transition, not a cosmetic cleanup. + +Before compaction, VibeGeometry should preserve: + +- current objective and active subgoal +- latest stable map +- distilled evidence or rejected paths that change future belief +- open questions, blockers, and next action + +After rehydrating, VibeGeometry should: + +- reread canonical state instead of trusting prompt residue +- restate the next action from persisted state +- continue only when instructed or when the task explicitly calls for it +- avoid broad implementation until the current pipeline organ is understood + +The aim is simple: the next waking thing should find coals instead of ash. diff --git a/notes/fresh-workspace-handoff.md b/notes/fresh-workspace-handoff.md new file mode 100644 index 0000000..8cf209e --- /dev/null +++ b/notes/fresh-workspace-handoff.md @@ -0,0 +1,64 @@ +# Fresh Workspace Handoff + +## Immediate Re-entry Instruction + +Read `state/map.yaml`, this file, `notes/vibegeometry-current-system-map.md`, +and `notes/vibegeometry-implementation-plan.md`, then run: + +```powershell +npm run state:status +``` + +Restate the persisted next action before editing. + +Do not continue implementation automatically from a rehydrate-only request. + +Do not trust this file for the exact live HEAD. Use git for volatile truth: + +```powershell +git status --short --branch +git log --oneline -5 +``` + +## Current State + +VibeGeometry is an initial scaffold. It has persistence machinery, project +orientation docs, and helper tools, but no Blender builder library or artifact +schemas yet. + +The current next action is to draft the scene brief and node graph manifest +schemas, then validate them against one tiny reference-driven example. + +## Important Invariants + +- Target Blender 5.0+ unless the task says otherwise. +- Geometry nodes are the primary procedural geometry surface. +- Python constructs and orchestrates graphs; it should not bury the design in + opaque imperative mesh generation. +- Visual target evidence must remain attached to scene briefs and render + review. +- A graph is not accepted just because it runs. Render evidence decides. +- Node groups, exposed sockets, and attributes need names clear enough for + future agents to inspect and modify. +- Add evidence only when it changes future belief. + +## Open Questions + +- What exact JSON shape should the scene brief use for references, + instructions, target traits, and acceptance criteria? +- What should the first node graph manifest schema require versus merely allow? +- Which Blender 5.0+ APIs and node construction helpers should become the + first stable builder layer? +- What is the smallest useful render-review artifact? + +## Compaction Notes + +When preparing for imminent compaction, write hot memory first to +`state/scratch-compaction-.md`, then run: + +```powershell +npm run state:prepare-compaction +``` + +Fold only durable lessons into canonical state and delete the temporary +scratch-compaction file after it has served its purpose. diff --git a/notes/vibegeometry-current-system-map.md b/notes/vibegeometry-current-system-map.md new file mode 100644 index 0000000..4ebedeb --- /dev/null +++ b/notes/vibegeometry-current-system-map.md @@ -0,0 +1,59 @@ +# VibeGeometry Current System Map + +VibeGeometry does not have a Blender runtime yet. The live system is a +persistence scaffold plus a planned artifact pipeline for agent-authored +geometry-node scenes. + +## Control Flow + +1. Rehydrate from `state/map.yaml`, `notes/fresh-workspace-handoff.md`, this + file, and `notes/vibegeometry-implementation-plan.md`. +2. Run `npm run state:status`. +3. Work one bounded pipeline organ. +4. Validate the seam that matters. +5. Persist only belief-changing evidence. +6. Commit completed work. + +## Core Surfaces + +- `state/map.yaml`: canonical mission, boundaries, live architecture, next action. +- `state/evidence.jsonl`: distilled belief-changing evidence only. +- `state/branches.json`: active and closed research or implementation branches. +- `notes/fresh-workspace-handoff.md`: compact re-entry packet. +- `notes/vibegeometry-implementation-plan.md`: near-term implementation sequence. +- `docs/architecture/`: detailed contracts and rationale. + +## Planned Artifact Pipeline + +```text +reference images + user instructions + -> scene brief + -> target visual trait extraction + -> geometry strategy + -> node graph manifest + -> Blender Python builder script + -> generated .blend scene + -> render output + -> render review and graph audit + -> accepted example or revised hypothesis +``` + +## Missing Or Incomplete Organs + +- Scene brief schema +- Node graph manifest schema +- Render review schema +- Blender Python builder helpers +- Geometry-node idiom library +- Headless Blender runner +- Reference-image storage and provenance rules +- Render comparison and review workflow +- Example scene fixtures + +## Current North Star + +Build the smallest reliable loop where an agent can describe a target, plan +procedural geometry, generate a legible Blender node graph through Python, +render it, and use review evidence to improve the next iteration. The loop +should reward coherent geometry flow, not just scripts that successfully make +Blender emit pixels. diff --git a/notes/vibegeometry-implementation-plan.md b/notes/vibegeometry-implementation-plan.md new file mode 100644 index 0000000..43c266c --- /dev/null +++ b/notes/vibegeometry-implementation-plan.md @@ -0,0 +1,67 @@ +# VibeGeometry Implementation Plan + +## Current Phase + +Build the first reliable scaffold for agent-authored Blender geometry-node +scenes. The immediate target is not a full procedural art factory. It is a +clean artifact loop with enough structure that agents can generate, inspect, +render, and revise graphs without losing the visual target. + +Planning belongs in this file and the handoff/map state surfaces. Architecture +docs should describe durable contracts, boundaries, and data shapes; they +should not carry live next-action clutter. + +## Near-Term Sequence + +1. Define the scene brief contract. + - Capture references, text instructions, target visual traits, constraints, + camera/composition requirements, acceptance criteria, and known unknowns. + - Preserve source paths or URLs for every reference image. + - Separate visual target description from implementation strategy. + +2. Define the node graph manifest contract. + - Describe node groups, sockets, exposed controls, modifiers, attribute + names, field flow, materials, and dependencies. + - Require enough naming and rationale for future agents to inspect the graph. + - Keep graph contracts Blender 5.0+ oriented. + +3. Build a minimal Python builder seam. + - Start with one tiny scene that proves graph construction, material setup, + camera, lighting, save, and render orchestration. + - Prefer small composable helpers for node group creation and socket linking. + - Avoid a giant builder abstraction until repeated graph idioms demand it. + +4. Build render review artifacts. + - Store render path, scene brief id, manifest id, script path, Blender + version, review status, visual misses, graph issues, and next hypothesis. + - Treat rendered output as the acceptance surface for visual claims. + +5. Add geometry-node idiom notes and examples. + - Document established patterns for distribution, instancing, curve/surface + generation, attributes, material variation, simulation zones, and exposed + parameters. + - Keep examples small enough to inspect. + - Promote only graph patterns that survive review. + +6. Generalize after the loop works. + - Add reference-image ingestion helpers. + - Add headless batch rendering. + - Add visual comparison or reviewer prompts. + - Add richer examples only after the contracts stop sliding. + +## Deferred + +- Large procedural asset library +- Automated visual scoring beyond basic review artifacts +- Fine-tuned graph-generation models +- Blender add-on packaging +- Complex simulation-node workflows before the basic graph loop is stable + +## Discipline + +- Work one bounded organ at a time. +- Explain target-to-graph data flow before broad refactors. +- Prefer explicit manifests and render evidence over implicit agent memory. +- If the diff grows while understanding shrinks, stop and simplify. +- Keep history in git; keep persisted state focused on what future sessions + need to believe. diff --git a/package.json b/package.json new file mode 100644 index 0000000..b532cab --- /dev/null +++ b/package.json @@ -0,0 +1,10 @@ +{ + "name": "vibegeometry", + "private": true, + "version": "0.0.0", + "description": "Persistent research repo for agent-authored Blender geometry-node scene generation.", + "scripts": { + "state:status": "\"C:\\Users\\Meta\\.cache\\codex-runtimes\\codex-primary-runtime\\dependencies\\python\\python.exe\" .\\tools\\vibegeometry_state.py status", + "state:prepare-compaction": "\"C:\\Users\\Meta\\.cache\\codex-runtimes\\codex-primary-runtime\\dependencies\\python\\python.exe\" .\\tools\\vibegeometry_prepare_compaction.py" + } +} diff --git a/prompts/README.md b/prompts/README.md new file mode 100644 index 0000000..1168ee2 --- /dev/null +++ b/prompts/README.md @@ -0,0 +1,14 @@ +# Prompts + +Reusable prompts for VibeGeometry agents live here. + +Expected prompt families: + +- scene brief extraction from reference images and instructions +- geometry strategy planning +- node graph manifest drafting +- Blender Python generation +- render review and graph audit + +Keep prompts tied to explicit artifacts. A prompt that only says "make it +better" is a small bureaucracy for producing fog. diff --git a/schemas/README.md b/schemas/README.md new file mode 100644 index 0000000..ef5f54f --- /dev/null +++ b/schemas/README.md @@ -0,0 +1,11 @@ +# Schemas + +Future JSON schemas live here. + +Planned contracts: + +- `scene-brief.schema.json` +- `node-graph-manifest.schema.json` +- `render-review.schema.json` + +Add schemas when the first concrete example needs validation. diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 0000000..73250e6 --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,7 @@ +# Scripts + +Utility scripts live here. + +Persistence machinery belongs in `tools/`. Blender runners, render helpers, +image prep utilities, and batch experiment scripts can live here once they +exist. diff --git a/state/branches.json b/state/branches.json new file mode 100644 index 0000000..0224b56 --- /dev/null +++ b/state/branches.json @@ -0,0 +1,3 @@ +{ + "branches": [] +} diff --git a/state/evidence.jsonl b/state/evidence.jsonl new file mode 100644 index 0000000..07e3cdf --- /dev/null +++ b/state/evidence.jsonl @@ -0,0 +1 @@ +{"ts":"2026-05-04T00:00:00+00:00","type":"scaffold","status":"accepted","note":"Initialized VibeGeometry with Epiphany-style persistence surfaces adapted to Blender 5.0+ geometry-node scene generation."} diff --git a/state/map.yaml b/state/map.yaml new file mode 100644 index 0000000..b1f9126 --- /dev/null +++ b/state/map.yaml @@ -0,0 +1,80 @@ +experiment: + name: VibeGeometry + status: incubation + phase: phase_0_scaffold + premise: > + Build a durable research and implementation home for agent-authored + Blender 5.0+ scenes that use Python to construct complex geometry-node + graphs for procedural geometry matching reference images and instructions. + +mission: + primary: > + Enable Codex and other agents to generate inspectable, reusable Blender + geometry-node graphs that produce specific visual results from reference + images, text instructions, and iterative render review. + near_term: > + Define the scene brief, node graph manifest, Python builder, render runner, + and review loop before building a large library of graph idioms. + +hard_boundaries: + - Blender 5.0+ is the target runtime unless a task explicitly chooses another version. + - Geometry nodes are the primary procedural modeling surface. + - Python scripts should construct and orchestrate node graphs, not hide the design in uninspectable imperative mesh code. + - Visual targets must preserve reference-image evidence and explicit instructions. + - A graph that runs is not accepted until rendered output is inspected against the target. + - Node groups must be named, parameterized, and legible enough for future agents to modify. + - Attribute creation, transformation, and consumption must be explicit. + - Prefer established Blender node idioms and current API guidance over bespoke graph inventions. + - Keep failed experiments only when they change future belief. + - Do not let proxy checks replace visual review. + +live_architecture: + pipeline: + - Ingest reference images and instructions into a scene brief. + - Extract target visual traits: silhouette, repetition, distribution, material response, surface breakup, camera, lighting, palette, and scale cues. + - Plan a geometry strategy in plain language before scripting. + - Emit a node graph manifest describing groups, sockets, attributes, fields, modifiers, materials, and exposed controls. + - Generate Blender Python that builds the scene and node graphs from the manifest. + - Run Blender headlessly or interactively to produce `.blend` files and render evidence. + - Review renders against the visual target and record accepted, rejected, or revised hypotheses. + persistence: + - `state/map.yaml` stores canonical mission, boundaries, architecture, artifacts, and next action. + - `state/scratch.md` stores one bounded active thought surface. + - `state/evidence.jsonl` stores distilled belief-changing records. + - `notes/fresh-workspace-handoff.md` stores compact re-entry instructions. + - `notes/vibegeometry-current-system-map.md` explains the live repo machinery. + - `notes/vibegeometry-implementation-plan.md` stores the near-term build sequence. + +active_subgoals: + - Keep the pipeline legible from visual target to graph strategy to Python builder to render review. + - Define artifact schemas before generating many example scenes. + - Build a minimal Blender runner only after the scene brief and graph manifest contracts are clear. + - Collect Blender 5.0+ geometry-node API and best-practice evidence before hardening graph builders. + - Preserve one-hypothesis iteration when visual results miss the target. + +current_artifacts: + canonical_docs: + - notes/architecture-rationale.md + - notes/fresh-workspace-handoff.md + - notes/vibegeometry-current-system-map.md + - notes/vibegeometry-implementation-plan.md + - docs/architecture/pipeline-overview.md + tools: + - tools/vibegeometry_state.py + - tools/vibegeometry_prepare_compaction.py + pending_contracts: + - schemas/scene-brief.schema.json + - schemas/node-graph-manifest.schema.json + - schemas/render-review.schema.json + +data_flow: + - Rehydrate from map, handoff, system map, implementation plan, and `npm run state:status`. + - Work one bounded pipeline organ. + - Verify the seam that matters. + - Add distilled evidence only when future belief changes. + - Commit completed work. + - If state grows faster than understanding, cut it back. + +current_status: + summary: VibeGeometry has an initial Epiphany-style persistence scaffold adapted for Blender geometry-node scene generation, with project-neutral state, handoff, architecture notes, and helper tools. + next_action: Draft the scene brief and node graph manifest schemas, then validate them against one tiny reference-driven example. diff --git a/state/scratch.md b/state/scratch.md new file mode 100644 index 0000000..3f04d56 --- /dev/null +++ b/state/scratch.md @@ -0,0 +1,11 @@ +# Scratch + +## Current Subgoal + +No active scratch subgoal. + +## Notes + +Use this file for temporary reasoning during one bounded task. Fold durable +lessons into `state/map.yaml`, `state/evidence.jsonl`, or the relevant note, +then clear this surface. diff --git a/tools/vibegeometry_prepare_compaction.py b/tools/vibegeometry_prepare_compaction.py new file mode 100644 index 0000000..2397e9c --- /dev/null +++ b/tools/vibegeometry_prepare_compaction.py @@ -0,0 +1,334 @@ +from __future__ import annotations + +import argparse +import json +import re +import subprocess +from dataclasses import dataclass +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[1] +STATE_DIR = ROOT / "state" +NOTES_DIR = ROOT / "notes" + +MAP_PATH = STATE_DIR / "map.yaml" +SCRATCH_PATH = STATE_DIR / "scratch.md" +BRANCHES_PATH = STATE_DIR / "branches.json" +EVIDENCE_PATH = STATE_DIR / "evidence.jsonl" +HANDOFF_PATH = NOTES_DIR / "fresh-workspace-handoff.md" +PLAN_PATH = NOTES_DIR / "vibegeometry-implementation-plan.md" +SYSTEM_MAP_PATH = NOTES_DIR / "vibegeometry-current-system-map.md" +AGENTS_PATH = ROOT / "AGENTS.md" + + +@dataclass +class Finding: + level: str + message: str + + +def read_text(path: Path) -> str: + return path.read_text(encoding="utf-8") + + +def run_git(args: list[str]) -> str: + completed = subprocess.run( + ["git", *args], + cwd=ROOT, + check=True, + capture_output=True, + text=True, + ) + return completed.stdout.strip() + + +def extract_map_field(name: str) -> str | None: + prefix = f" {name}:" + for line in read_text(MAP_PATH).splitlines(): + if line.startswith(prefix): + return line.split(":", 1)[1].strip() + return None + + +def extract_active_subgoals() -> list[str]: + lines = read_text(MAP_PATH).splitlines() + results: list[str] = [] + in_section = False + for line in lines: + if not in_section: + if line.strip() == "active_subgoals:": + in_section = True + continue + if line.startswith(" - "): + results.append(line[4:].strip()) + continue + if line and not line.startswith(" "): + break + return results + + +def current_scratch_subgoal() -> str | None: + lines = read_text(SCRATCH_PATH).splitlines() + for index, line in enumerate(lines): + if line.strip() != "## Current Subgoal": + continue + for candidate in lines[index + 1 :]: + stripped = candidate.strip() + if stripped: + return stripped + return None + + +def load_evidence() -> list[dict[str, Any]]: + records: list[dict[str, Any]] = [] + for line_number, line in enumerate(read_text(EVIDENCE_PATH).splitlines(), start=1): + if not line.strip(): + continue + try: + record = json.loads(line) + except json.JSONDecodeError as exc: + raise ValueError(f"evidence line {line_number} is invalid JSON: {exc}") from exc + if not isinstance(record, dict): + raise ValueError(f"evidence line {line_number} is not a JSON object") + records.append(record) + return records + + +def load_branches() -> dict[str, Any]: + data = json.loads(read_text(BRANCHES_PATH)) + if not isinstance(data, dict): + raise ValueError("branches.json must be a JSON object") + branches = data.get("branches") + if not isinstance(branches, list): + raise ValueError("branches.json must contain a branches list") + return data + + +def add_required_file_checks(findings: list[Finding]) -> None: + for path in [ + MAP_PATH, + SCRATCH_PATH, + BRANCHES_PATH, + EVIDENCE_PATH, + HANDOFF_PATH, + PLAN_PATH, + SYSTEM_MAP_PATH, + AGENTS_PATH, + ]: + if path.exists(): + findings.append(Finding("ok", f"found {path.relative_to(ROOT)}")) + else: + findings.append(Finding("error", f"missing {path.relative_to(ROOT)}")) + + +def add_content_checks(findings: list[Finding]) -> tuple[list[dict[str, Any]], dict[str, Any]]: + evidence: list[dict[str, Any]] = [] + branches: dict[str, Any] = {} + + summary = extract_map_field("summary") + next_action = extract_map_field("next_action") + if summary: + findings.append(Finding("ok", "state/map.yaml has current_status.summary")) + else: + findings.append(Finding("error", "state/map.yaml is missing current_status.summary")) + if next_action: + findings.append(Finding("ok", "state/map.yaml has current_status.next_action")) + else: + findings.append(Finding("error", "state/map.yaml is missing current_status.next_action")) + + subgoals = extract_active_subgoals() + if subgoals: + findings.append(Finding("ok", f"state/map.yaml has {len(subgoals)} active subgoal(s)")) + else: + findings.append(Finding("warn", "state/map.yaml has no active_subgoals entries")) + + scratch_subgoal = current_scratch_subgoal() + if scratch_subgoal == "No active scratch subgoal.": + findings.append(Finding("ok", "state/scratch.md has no stale active scratch subgoal")) + elif scratch_subgoal: + findings.append(Finding("warn", f"state/scratch.md has active scratch subgoal: {scratch_subgoal}")) + else: + findings.append(Finding("warn", "state/scratch.md has no Current Subgoal value")) + + try: + evidence = load_evidence() + findings.append(Finding("ok", f"state/evidence.jsonl parses ({len(evidence)} record(s))")) + except ValueError as exc: + findings.append(Finding("error", str(exc))) + + if EVIDENCE_PATH.exists() and EVIDENCE_PATH.stat().st_size > 25_000: + findings.append(Finding("warn", "state/evidence.jsonl is larger than 25 KB; consider distillation")) + + try: + branches = load_branches() + active = [ + branch + for branch in branches.get("branches", []) + if isinstance(branch, dict) and branch.get("status") == "active" + ] + findings.append(Finding("ok", f"state/branches.json parses ({len(active)} active branch(es))")) + except (json.JSONDecodeError, ValueError) as exc: + findings.append(Finding("error", f"branches.json parse failed: {exc}")) + + return evidence, branches + + +def add_handoff_checks(findings: list[Finding]) -> None: + text = read_text(HANDOFF_PATH) + stale_branch = re.search(r"Current branch before .*ahead \d+", text) + stale_head = re.search(r"Current HEAD before .*\b[0-9a-f]{7,40}\b", text) + if stale_branch or stale_head: + findings.append(Finding("error", "handoff embeds an exact branch or HEAD snapshot; use git commands instead")) + else: + findings.append(Finding("ok", "handoff avoids exact branch or HEAD snapshots")) + + required_phrases = [ + "Do not continue implementation automatically from a rehydrate-only request.", + "Do not trust this file for the exact live HEAD.", + "Immediate Re-entry Instruction", + ] + for phrase in required_phrases: + if phrase in text: + findings.append(Finding("ok", f"handoff contains: {phrase}")) + else: + findings.append(Finding("warn", f"handoff missing: {phrase}")) + + +def add_agents_checks(findings: list[Finding]) -> None: + text = read_text(AGENTS_PATH) + if "vibegeometry_prepare_compaction.py" in text: + findings.append(Finding("ok", "AGENTS.md tells agents to use the compaction helper")) + else: + findings.append(Finding("error", "AGENTS.md does not mention tools/vibegeometry_prepare_compaction.py")) + if "prepare for imminent compaction" in text.lower(): + findings.append(Finding("ok", "AGENTS.md names the imminent-compaction trigger")) + else: + findings.append(Finding("warn", "AGENTS.md does not name the imminent-compaction trigger phrase")) + + +def add_git_checks(findings: list[Finding]) -> tuple[str, str]: + status = run_git(["status", "--short", "--branch"]) + try: + log = run_git(["log", "--oneline", "-5"]) + except subprocess.CalledProcessError: + log = "(no commits yet)" + dirty_lines = [ + line + for line in status.splitlines()[1:] + if line.strip() + ] + if dirty_lines: + findings.append(Finding("warn", "git worktree has uncommitted changes; commit or explain before compaction")) + else: + findings.append(Finding("ok", "git worktree is clean")) + return status, log + + +def render_report(findings: list[Finding], status: str, log: str, evidence: list[dict[str, Any]]) -> str: + summary = extract_map_field("summary") or "(missing)" + next_action = extract_map_field("next_action") or "(missing)" + subgoals = extract_active_subgoals() + + counts = { + "ok": sum(1 for finding in findings if finding.level == "ok"), + "warn": sum(1 for finding in findings if finding.level == "warn"), + "error": sum(1 for finding in findings if finding.level == "error"), + } + + lines = [ + "VibeGeometry pre-compaction persistence check", + f"Workspace: {ROOT}", + f"Findings: {counts['ok']} ok, {counts['warn']} warn, {counts['error']} error", + "", + "Git status:", + status, + "", + "Recent commits:", + log, + "", + f"Summary: {summary}", + f"Next action: {next_action}", + ] + if subgoals: + lines.append("Active subgoals:") + lines.extend(f"- {item}" for item in subgoals) + if evidence: + latest = evidence[-1] + lines.extend( + [ + "", + "Latest distilled evidence:", + f"- {latest.get('ts', '(missing ts)')} {latest.get('type', '(missing type)')}/{latest.get('status', '(missing status)')}: {latest.get('note', '(missing note)')}", + ] + ) + + lines.append("") + lines.append("Findings:") + for finding in findings: + lines.append(f"[{finding.level.upper()}] {finding.message}") + + lines.extend( + [ + "", + "Pre-compaction checklist:", + "- Update state/map.yaml only if current understanding changed.", + "- Refresh notes/fresh-workspace-handoff.md if re-entry instructions changed.", + "- Add distilled evidence only for a belief-changing lesson, verification, rejected path, or scar.", + "- Keep exact branch or HEAD out of handoff prose; git commands own volatile truth.", + "- Commit completed persistence changes, or state why the worktree must stay dirty.", + "- Re-run this helper after edits before yielding to compaction.", + ] + ) + return "\n".join(lines) + + +def build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser( + description="Audit VibeGeometry persistent state before imminent compaction." + ) + parser.add_argument( + "--strict", + action="store_true", + help="Exit nonzero on warnings as well as errors.", + ) + return parser + + +def main() -> int: + args = build_parser().parse_args() + findings: list[Finding] = [] + evidence: list[dict[str, Any]] = [] + + add_required_file_checks(findings) + try: + evidence, _branches = add_content_checks(findings) + except OSError as exc: + findings.append(Finding("error", f"state content check failed: {exc}")) + try: + add_handoff_checks(findings) + except OSError as exc: + findings.append(Finding("error", f"handoff check failed: {exc}")) + try: + add_agents_checks(findings) + except OSError as exc: + findings.append(Finding("error", f"AGENTS check failed: {exc}")) + try: + status, log = add_git_checks(findings) + except (OSError, subprocess.CalledProcessError) as exc: + findings.append(Finding("error", f"git check failed: {exc}")) + status = "(git status unavailable)" + log = "(git log unavailable)" + + print(render_report(findings, status, log, evidence)) + + has_error = any(finding.level == "error" for finding in findings) + has_warning = any(finding.level == "warn" for finding in findings) + if has_error or (args.strict and has_warning): + return 1 + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/vibegeometry_state.py b/tools/vibegeometry_state.py new file mode 100644 index 0000000..6d05402 --- /dev/null +++ b/tools/vibegeometry_state.py @@ -0,0 +1,179 @@ +from __future__ import annotations + +import argparse +import json +from datetime import datetime, timezone +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[1] +STATE_DIR = ROOT / "state" +MAP_PATH = STATE_DIR / "map.yaml" +BRANCHES_PATH = STATE_DIR / "branches.json" +EVIDENCE_PATH = STATE_DIR / "evidence.jsonl" + + +def read_text(path: Path) -> str: + return path.read_text(encoding="utf-8") + + +def write_text(path: Path, content: str) -> None: + path.write_text(content, encoding="utf-8") + + +def load_branches() -> dict[str, Any]: + return json.loads(read_text(BRANCHES_PATH)) + + +def save_branches(data: dict[str, Any]) -> None: + write_text(BRANCHES_PATH, json.dumps(data, indent=2) + "\n") + + +def append_evidence(record: dict[str, Any]) -> None: + with EVIDENCE_PATH.open("a", encoding="utf-8") as handle: + handle.write(json.dumps(record, ensure_ascii=True) + "\n") + + +def utc_stamp() -> str: + return datetime.now(timezone.utc).replace(microsecond=0).isoformat() + + +def extract_map_field(name: str) -> str | None: + prefix = f" {name}:" + for line in read_text(MAP_PATH).splitlines(): + if line.startswith(prefix): + return line.split(":", 1)[1].strip() + return None + + +def extract_active_subgoals() -> list[str]: + lines = read_text(MAP_PATH).splitlines() + results: list[str] = [] + in_section = False + for line in lines: + if not in_section: + if line.strip() == "active_subgoals:": + in_section = True + continue + if line.startswith(" - "): + results.append(line[4:].strip()) + continue + if line and not line.startswith(" "): + break + return results + + +def cmd_status(_: argparse.Namespace) -> int: + branches = load_branches().get("branches", []) + active = [branch for branch in branches if branch.get("status") == "active"] + summary = extract_map_field("summary") or "(missing)" + next_action = extract_map_field("next_action") or "(missing)" + subgoals = extract_active_subgoals() + + print(f"Workspace: {ROOT}") + print(f"Summary: {summary}") + print(f"Next action: {next_action}") + print(f"Active branches: {len(active)} / {len(branches)}") + if subgoals: + print("Active subgoals:") + for item in subgoals: + print(f"- {item}") + return 0 + + +def cmd_add_evidence(args: argparse.Namespace) -> int: + record: dict[str, Any] = { + "ts": utc_stamp(), + "type": args.type, + "status": args.status, + "note": args.note, + } + if args.branch: + record["branch"] = args.branch + append_evidence(record) + print("Appended evidence record.") + return 0 + + +def cmd_add_branch(args: argparse.Namespace) -> int: + data = load_branches() + branches = data.setdefault("branches", []) + if any(branch.get("id") == args.id for branch in branches): + raise SystemExit(f"Branch '{args.id}' already exists.") + branches.append( + { + "id": args.id, + "hypothesis": args.hypothesis, + "status": "active", + "artifacts": args.artifact or [], + "notes": args.note or "", + } + ) + save_branches(data) + print(f"Added branch '{args.id}'.") + return 0 + + +def cmd_close_branch(args: argparse.Namespace) -> int: + data = load_branches() + for branch in data.get("branches", []): + if branch.get("id") != args.id: + continue + branch["status"] = args.status + if args.note: + branch["notes"] = args.note + save_branches(data) + print(f"Updated branch '{args.id}' to status '{args.status}'.") + return 0 + raise SystemExit(f"Branch '{args.id}' was not found.") + + +def build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser( + description="Inspect and update VibeGeometry state files." + ) + subparsers = parser.add_subparsers(dest="command", required=True) + + status_parser = subparsers.add_parser("status", help="Show a compact state summary.") + status_parser.set_defaults(func=cmd_status) + + evidence_parser = subparsers.add_parser( + "add-evidence", help="Append one distilled JSONL evidence record." + ) + evidence_parser.add_argument("--type", required=True) + evidence_parser.add_argument("--status", required=True) + evidence_parser.add_argument("--note", required=True) + evidence_parser.add_argument("--branch") + evidence_parser.set_defaults(func=cmd_add_evidence) + + add_branch_parser = subparsers.add_parser( + "add-branch", help="Create a new active branch entry." + ) + add_branch_parser.add_argument("--id", required=True) + add_branch_parser.add_argument("--hypothesis", required=True) + add_branch_parser.add_argument("--artifact", action="append") + add_branch_parser.add_argument("--note") + add_branch_parser.set_defaults(func=cmd_add_branch) + + close_branch_parser = subparsers.add_parser( + "close-branch", help="Close an existing branch." + ) + close_branch_parser.add_argument("--id", required=True) + close_branch_parser.add_argument( + "--status", required=True, choices=["accepted", "rejected", "archived"] + ) + close_branch_parser.add_argument("--note") + close_branch_parser.set_defaults(func=cmd_close_branch) + + return parser + + +def main() -> int: + parser = build_parser() + args = parser.parse_args() + return args.func(args) + + +if __name__ == "__main__": + raise SystemExit(main())