Scaffold VibeGeometry
This commit is contained in:
commit
f8b4e5e09e
21 changed files with 1285 additions and 0 deletions
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
__pycache__/
|
||||
*.pyc
|
||||
.venv/
|
||||
node_modules/
|
||||
state/scratch-compaction-*.md
|
||||
experiments/renders/
|
||||
experiments/blender-cache/
|
||||
*.blend1
|
||||
*.blend2
|
||||
144
AGENTS.md
Normal file
144
AGENTS.md
Normal file
|
|
@ -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-<guid>.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-<guid>.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.
|
||||
109
README.md
Normal file
109
README.md
Normal file
|
|
@ -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.
|
||||
64
docs/architecture/pipeline-overview.md
Normal file
64
docs/architecture/pipeline-overview.md
Normal file
|
|
@ -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.
|
||||
15
docs/research/blender-5-geometry-nodes.md
Normal file
15
docs/research/blender-5-geometry-nodes.md
Normal file
|
|
@ -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.
|
||||
11
examples/README.md
Normal file
11
examples/README.md
Normal file
|
|
@ -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.
|
||||
7
experiments/README.md
Normal file
7
experiments/README.md
Normal file
|
|
@ -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.
|
||||
86
notes/architecture-rationale.md
Normal file
86
notes/architecture-rationale.md
Normal file
|
|
@ -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.
|
||||
64
notes/fresh-workspace-handoff.md
Normal file
64
notes/fresh-workspace-handoff.md
Normal file
|
|
@ -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-<guid>.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.
|
||||
59
notes/vibegeometry-current-system-map.md
Normal file
59
notes/vibegeometry-current-system-map.md
Normal file
|
|
@ -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.
|
||||
67
notes/vibegeometry-implementation-plan.md
Normal file
67
notes/vibegeometry-implementation-plan.md
Normal file
|
|
@ -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.
|
||||
10
package.json
Normal file
10
package.json
Normal file
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
14
prompts/README.md
Normal file
14
prompts/README.md
Normal file
|
|
@ -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.
|
||||
11
schemas/README.md
Normal file
11
schemas/README.md
Normal file
|
|
@ -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.
|
||||
7
scripts/README.md
Normal file
7
scripts/README.md
Normal file
|
|
@ -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.
|
||||
3
state/branches.json
Normal file
3
state/branches.json
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"branches": []
|
||||
}
|
||||
1
state/evidence.jsonl
Normal file
1
state/evidence.jsonl
Normal file
|
|
@ -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."}
|
||||
80
state/map.yaml
Normal file
80
state/map.yaml
Normal file
|
|
@ -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.
|
||||
11
state/scratch.md
Normal file
11
state/scratch.md
Normal file
|
|
@ -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.
|
||||
334
tools/vibegeometry_prepare_compaction.py
Normal file
334
tools/vibegeometry_prepare_compaction.py
Normal file
|
|
@ -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())
|
||||
179
tools/vibegeometry_state.py
Normal file
179
tools/vibegeometry_state.py
Normal file
|
|
@ -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())
|
||||
Loading…
Reference in a new issue