What is actually wrong with using PressRaven to build games — traced to specific lines, with a tiered fix plan.
Tiers 0–3 are implemented. App builds clean, RavenKit's tests pass, export-machine.py stellar --verify still exits 0, and nothing is committed in any of the three repos. Jump to what was built.
Three things below turned out to be wrong, and the third matters more than anything in the original diagnosis:
bank_target: 0, which says no corpus bank is expected — they're driven app-side. Nothing to fix.displayed_positions legitimately has one declaration per mini game; de-duping by key would drop a whole game's fields. It has to happen after applies_when filtering, per entry. Built that way.the-rally had no schema block whatsoever.Almost nothing you asked for is missing. The round trip into the game app exists and works. The Claude-Code session launcher exists, copies to your clipboard, and opens Terminal. The mini games carry a rich authored layer — mechanic, build status, bank target, divergence notes.
What's wrong is that these are reachable only by paths you'd have to already know about, and two of them are actively mislabelled. The friction is real, but it is surface friction, not absence.
File → Export to App…, backed by tools/export-machine.py, with a Write / Dry Run / Cancel confirm that lists every file it will overwrite. It was greyed out because you were on Test Game, which declares no machine target. Stellar declares eleven outputs into ~/Developer/Stellar/Data/.Ways In → Start a Session… writes a session log, copies the packet to your clipboard, and launches Terminal running claude in the game repo.mini_games[] holds nine games with mechanic, build status, bank target and a long divergence essay each. Nothing renders it.Two different buttons are both called "Start a Session." The prominent one in the brief exports a markdown file. The one buried in the Ways In menu opens the terminal. You clicked the prominent one and got a file, exactly as designed. That alone explains annotation #5.
I also verified the corpus and the shipping game are currently in sync — export-machine.py stellar --verify exits 0.
Tags: exists already built, just hidden · bug code defect · gap genuinely absent · data corpus problem
| # | Your note | What's actually happening |
|---|---|---|
| 1 | Why would I choose New Seed? | gap New Seed captures one entry idea into an existing guide — it's the ⌃⌥Space global-capture action, topmost because it's highest-frequency. It's useless as a first action, and the File menu omits the explanatory subtitles the sidebar + menu has. |
| 2 | Guide appears in sidebar, but we stay on Dashboard | bug NewGuideSheet.swift:47 discards createGuide's return value and never sets selection. The navigation helper already exists at AppMainView.swift:214. One-line fix. |
| 3–5 | What am I supposed to do? Which button? | bug The prominent Start a Session posts .pressRavenExportPacket → save panel → .md file. Ways In → Start a Session… posts .pressRavenStartSession → clipboard + Terminal. Same name, different actions. |
| 6 | Scope is empty — what now? | exists The (0) is the entry count. An empty guide has nothing to scope, and the group rows below it are the guide's mini games — Test Game declares none. Your instinct was right. |
| 7 | Draft Here brings up… this | gap Draft Here lays out one section per mini game. Test Game has no mini_games, so it lays out zero sections and collapses the brief. Root cause: categories/game/category.json declares nothing about mini games, so every new Game guide is born an empty husk. |
| 8–10 | Games secondary, titles not clickable, no game view | gap Correct on all three. Category headers are inert Section strings; SidebarItem has three cases and a category isn't even representable as a selection; the detail pane has three arms. No per-game view exists anywhere. Also: no recency state at all — window restoration is deliberately off, so selection resets to Dashboard every launch. |
| 11 | No description of the game itself | gap The description is authored and unrendered. mini_games[] carries label, mechanic, build status, bank target, priorities, reskin and a divergence essay per game. Its only consumer is the session-launcher's picker. |
| 12 | Descriptive text looks like notes + tags | bug Neither. It's every scalar schema field's value with the field name stripped, joined by " · ". Your example is ten fields: family, class, concept, difficulty, time_tier, rotation (not number), phase, rule_tier, verdict, moment. |
| 13 | No example of how an item is constructed | gap Correct. The only example generator writes into the exported packet, never the UI — and it ignores applies_when, so it mixes fields from all nine mini games into one incoherent example. That's what Claude currently sees. |
| 14 | Fields appearing twice | bug Two independent defects. (a) FieldDef.id is its key, and the resolver appends extension blocks with no de-duplication — Stellar resolves to 75 fields with 14 colliding keys. ForEach builds identity before applies_when is checked. (b) a boxed string_map prints its label twice — the same bug already fixed for string_list. |
| 15 | Fields empty, but the game WORKS | bug Three compounding causes, none of them a corpus fault — see below. |
| 16 | Is commit unsafe? Live render? Integrate back? | exists Not unsafe — see below. |
applies_when mostly gates on family alone.the-thread: 18 item, 87 rule, 48 flavor. Their populated fields barely overlap, so each renders the others' fields as empty.verdict: clean, faults is [] and explanation is "" by contract — a clean formation has no faults. The entry you screenshotted says so in its own confidence note.Compounding it: empty string_map and object_list fields draw nothing at all inside their labelled box, because only prose and string_list have a ghost state. So "not applicable" and "not yet written" look identical — and both look like breakage.
The game works because the exporter projects each class through its own shape, using only that class's fields. The app has no class awareness when rendering.
In the "could it break the game" sense — no. It runs git add -A in the corpus repo only. It never touches the app repo or the game repo, and nothing is ever written back into the corpus by the exporter.
Real caveats, in order:
test-game guide you made during this review."Commit Edit" ⌘S is not git. It flushes the in-progress text edit to JSON. Two unrelated things share the word "commit" — worth renaming on its own.
No. The game reads ~/Developer/Stellar/Data/*.json, refreshed only by the export. Editing in PressRaven changes the corpus; the game sees nothing until you push. There's no risk of a commit overwriting unrendered work — the flow is strictly one-directional and the repos are separate.
It already exists. Why it feels absent:
--verify exists precisely for this and the app never calls it.README.md, docs/, or DIVERGENCES.md.pre-commit hook running --verify. Stellar does not.Independent of any UI work — the cheapest wins available.
blow-the-whistle is not declared in mini_games[], yet holds 225 of Stellar's 430 entries (52%). Undeclared families sort last and render with their raw slug as the heading. Meanwhile machine_target.outputs does reference it — so the exporter knows about a mini game the document doesn't. One array entry fixes it.family: "stories" — blocks 6 and 11 declare the same four fields with the same applies_when. Unlike the other duplicates, applies_when can't disambiguate these; they'll double regardless of the view fix.order: 8 on both show-up-or-lose-it and the-rally.built with zero entries — the-streak, tag-the-photo, called-shot, show-up-or-lose-it. Either the status is wrong or that content lives somewhere the corpus doesn't know about.One more, found while verifying: Stellar's section strip currently reads BRIEF · THE · THE · STORIES · THE · THE · BLOW-THE-WHISTLE, because the tab label takes only the first word of the mini-game name. Four identical tabs.
Ordered by value per unit effort. Each tier is independently shippable.
blow-the-whistle in mini_games[] — un-orphans 52% of the guidestories extension blocksorder: 8test-game guideapplies_when before the ForEachstring_map honour beat-label placementapplies_when on class, and give string_map/object_list a ghost state--verify on a timer and badge it: "Stellar — 3 changes not yet in the app"README.md — it's described nowhere outside the script's docstringAll four tiers, plus one thing you raised while I was working. Nothing committed anywhere.
blow-the-whistle declared — its 225 entries have a home. Mechanic deliberately left unset, with a note saying why: the three-way call resembles checkpoint-authority but was never confirmed, and guessing would make the gap report show a resolved question that isn't. Your call.stories block merged into the current one, keeping the correct string_list shape for frames.order: 8 resolved.test-game moved to .backups/ — out of the working tree, still recoverable.applies_when then de-dupes, last wins, warning in debug. Applied at every consumer.newEntryExample takes a representative entry — it was emitting an object mixing all nine mini games, which is the shape sessions were being told to imitate.string_map header fixed; section strip shows real mini-game names.class. Undeclared families are named with their counts. Clicking a mini game isolates it.label value pairs, labels set back from values.string_map and object_list gained ghost states — empty now reads as its prompt, not a blank box.bool field type through the whole interpreter. Required: the rules bank stores legal as a real boolean and there was no type to declare it.the-thread's item fields gated on class; new blocks declaring the rules bank, the-rally, and the whiteboard's missing fields. Every populated field in Stellar is now declared — a rule entry offers 26 fields instead of the whole union.--verify in the background and shows an amber arrow when the corpus is ahead of the game. Click writes.README.md documents the machine target — it appeared nowhere before.pre-commit hook verifying Data/. Installed in .git/hooks, not via core.hooksPath, because Stellar uses your global hook dispatcher and a repo-level core.hooksPath would shadow every global hook. Tested both ways.mini_games[] was hand-edit-only. There's now a New Mini Game sheet with a mechanic picker sourced from the mechanics catalog, reachable from the roster and from Ways In. Same root cause as the empty Test Game: the game category had no authoring surface for its own structure.mechanic for blow-the-whistle.string_list fix already in the codebase is the same fix as the string_map one.class gating touches 13 extension blocks — mechanical but broad, and easy to get subtly wrong.--verify passing is less reassuring than it looks.stories blocks demonstrate.82 / 100
High confidence in the diagnosis — every claim is traced to a specific line or verified against corpus data, and both rendering bugs are code-certain. Held below 90 by three things: I have not run the app to confirm the fixes behave as predicted; the schema de-duplication touches shared machinery whose full blast radius I have not measured; and Tier 2 is genuinely a design problem where my proposal is a starting point, not an optimal answer.