Release 1.1.0 — one config file, and a process weight you choose

`project.yaml` at the repo root is now the single source of truth for a
project's configuration, replacing `production/stage.txt`,
`production/review-mode.txt` and `.claude/docs/technical-preferences.md` (all
three still read as a fallback; nothing is force-deleted).

**`modes.rigor` — one question instead of six.** `minimal`, `standard` or
`full` fronts six knobs: `modes.workflow`, `docs.density`, `qa.level`,
`modes.story_granularity`, `modes.review_mode` and `team.size`. It defaults to
`minimal`, which is a change from 1.0's behaviour: the heavier tier was
measured costing several times more to reach working code without producing a
better result, and returning nothing when a fresh developer inherited the
project. Pin `modes.rigor: standard` to keep 1.0's process level.

Also in this release:

- `/settings` to view or change any setting, and `project.local.yaml` for
  per-developer preferences that should not be committed.
- `system_overrides` to hold one system to a higher standard than the rest of
  the project, and `testing.strict` to set per test type whether missing
  evidence blocks a story or warns.
- A story that changes anything the player sees is not closed until the game
  has been launched, observed, and a screenshot retained in
  `production/qa/evidence/`. A parse check is not a run. This holds at every
  rigor level, including `minimal`, where automated tests are waived but the
  look is not.
- The code root is engine-specific and enforced as such: Godot `src/`, Unity
  `Assets/`, Unreal `Source/<Module>/`. Two of the three will not build a
  project laid out any other way.
- Migration tooling for existing projects: `.claude/scripts/migrate-v1-config.sh`
  converts a 1.0 layout, non-destructively by default, with a report to review
  before `--finalize` deletes anything.
- Ask-before-write holds regardless of personal automation settings.

Upgrading from 1.0 or earlier: see UPGRADING.md. Full list of changes:
CHANGELOG.md.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016xvzG3i7pWQg4LjyDwWMXd
This commit is contained in:
Donchitos
2026-09-23 12:25:11 +10:00
co-authored by Claude Opus 5
parent 984023ddac
commit d05699707f
291 changed files with 24374 additions and 7740 deletions
+178
View File
@@ -0,0 +1,178 @@
# Run and Observe
Shared procedure for any skill that closes a story which changes something a
player can see. Referenced from the point of use in `/dev-story` (Phase 6) and
`/story-done` (Phase 3). Those skills keep the one load-bearing imperative
inline — **a parse check is not a run, and a story nobody looked at does not
close** — and cite this file for how to look.
`.claude/docs/coding-standards.md` is the authority for *what* evidence a story
needs. This file is the procedure for *producing* the visual half of it.
## The rule
Every story that changes something player-observable is **launched and observed
before it closes**, and the observation is a **retained screenshot** in
`production/qa/evidence/`. Compiling proves the code loads. Tests prove the
logic. Neither proves the text fits in the box.
The agent can see images: Claude Code's `Read` tool renders a PNG. The whole
problem is getting a picture out of the engine unattended, and every supported
engine has a native way.
## The procedure
1. **Launch windowed** at a fixed resolution — `1280x720` unless the project
says otherwise — using `commands.run` from `project.yaml`. Never the
headless / batch / null-RHI flag: those exist to *skip* rendering.
2. **Launch straight into the scene or map the story touched.** A feature that
is only reachable through the main menu and three clicks is a feature the
run will not reach. Stories should be written so their surface is reachable
from a launch argument (a scene path, a map URL, a `--scene` flag).
3. **Wait, capture, quit.** Let the engine render real frames — 60 is a safe
floor — capture one, and exit. Each engine's mechanism is below.
4. **Look.** `Read` the PNG. Compare what is on screen to the acceptance
criteria. Clipped text, an overflowing panel, a missing element, the wrong
colour — these are defects, and this is the only step that finds them.
5. **Retain.** Copy the image to
`production/qa/evidence/[story-slug]/[NN]-[what-it-shows].png` and reference
it from the evidence doc. A screenshot that was taken and discarded is an
assertion, not evidence.
6. **Report** one `Run result:` line in the implementation summary — vocabulary
below.
## Per engine
| `engine.name` | Launch | Capture | Output lands in | Never use | Scaffold needed |
|---|---|---|---|---|---|
| Godot 4 | `godot --path . --windowed --resolution 1280x720 <scene>.tscn` | `--write-movie <path>.png --quit-after <N>` | beside the given path, `<name>00000000.png` … | `--headless` | **none** |
| Unity 6 | built player `.exe -screen-width 1280 -screen-height 720 -screen-fullscreen 0 --scene <Name>` | `ScreenCapture.CaptureScreenshot(<abs path>)` from a bootstrap script | the absolute path you pass | `-batchmode`, `-nographics` | one MonoBehaviour |
| Unreal 5 | `MSYS_NO_PATHCONV=1 UnrealEditor.exe <Game>.uproject <MapURL> -game -windowed -ResX=1280 -ResY=720` | `HighResShot filename=<path>` console command | `Saved/Screenshots/Windows/` | `-nullrhi`, `-unattended` alone | one actor or `-ExecCmds` |
### Godot 4 — zero scaffold
```
godot --path . --windowed --resolution 1280x720 --write-movie shots/shop.png --quit-after 60 res://scenes/shop.tscn
```
`--write-movie` is Movie Maker mode: with a `.png` target it writes a frame
sequence and `--quit-after` sets the frame count. Filenames carry **8**
zero-padded digits — `shop00000000.png` through `shop00000059.png` — so take the
last. `--fixed-fps` is applied automatically. The scene path is positional; any
`.tscn` can be launched directly, which is how step 2 is satisfied.
Capture-on-trigger alternative, for a state a scene launch cannot reach: an
autoload that on the wanted condition does
`await RenderingServer.frame_post_draw`, then
`get_viewport().get_texture().get_image().save_png(path)`, then
`get_tree().quit()`.
### Unity 6 — one MonoBehaviour
`-batchmode` displays nothing, so the run is either the **built player** or the
editor in play mode. The player is the robust path:
```
Builds/Win64/Game.exe -screen-width 1280 -screen-height 720 -screen-fullscreen 0 --scene Shop --screenshot C:/abs/path/shop.png --after 60
```
`-screen-*` are Unity's own player arguments. `--scene`, `--screenshot` and
`--after` are **yours**: a `ScreenshotOnArg` MonoBehaviour in the bootstrap
scene parses `System.Environment.GetCommandLineArgs()`, loads `--scene`, waits
`--after` frames, calls `ScreenCapture.CaptureScreenshot(path)`, waits one more
frame, then `Application.Quit()`. Pass an **absolute** path — the relative base
differs between editor and player. `CaptureScreenshot` captures the last frame
*presented*, so it must run after at least one rendered frame.
`/test-setup` scaffolds this script under the code root. Building the player
first costs 1–3 minutes on a small project; that is the price of looking at
the thing you ship rather than the editor.
### Unreal 5 — one actor, or `-ExecCmds`
```
MSYS_NO_PATHCONV=1 UnrealEditor.exe Game.uproject /Game/Maps/Shop -game -windowed -ResX=1280 -ResY=720
```
`-game` runs standalone without the editor UI. The map is a **positional URL
immediately after the `.uproject`** — not a console command. **The
`MSYS_NO_PATHCONV=1` prefix is required on Windows:** Claude Code runs commands
in Git Bash, which rewrites any argument beginning with `/` as a Windows path, so
`/Game/Maps/Shop` reaches the engine as `C:/Program Files/Git/Game/Maps/Shop`
and the game stops on a "map not found" dialog nobody can see. Alternatively
pass the map through `-ExecCmds="open /Game/Maps/Shop"`, which is not converted. Capture is the
`HighResShot` console command, fired either from a dev actor (`Execute Console
Command` on BeginPlay after a short delay, then `quit`) or from the launch line:
```
-ExecCmds="HighResShot filename=C:/abs/path/shop.png,Quit"
```
`-ExecCmds` is comma-separated. Output defaults to `Saved/Screenshots/Windows/`.
**Do not `Quit` on the same frame as the request** — the file flushes late and
an immediate quit can leave nothing on disk; a one-second delay before `quit`
is enough. The Blueprint node `Take High Res Screenshot` is editor-only and
does not work in a `-game` launch.
### Any engine — OS capture fallback
On Windows, PowerShell with `System.Drawing` can `CopyFromScreen` into a bitmap
and save it. It needs a visible desktop and the game window on top, and it
captures whatever is there — it cannot be told "the shop panel". Use it when the
engine-native path is unavailable, and say so in the run result. Linux CI with
no display needs `xvfb-run` in front of the launch; `--headless` is not a
substitute.
## The `Run result:` line
Exactly one of three shapes, in the implementation summary:
- **`Run result: OBSERVED — <what was on screen, one line>`** with the retained
path. *"Shop panel renders, all 6 slots inside the frame, prices legible at
720p — `production/qa/evidence/shop-panel/01-shop-open.png`."*
- **`Run result: NOT VERIFIED — <reason>`**. Engine binary not found,
`commands.run` unset, launch crashed, screenshot empty. This is a **blocker**
at the default gate level for Visual/Feel and UI stories, not a note — a
story nobody looked at does not close. Under an explicit
`testing.strict.visual`/`.ui: false` it is recorded and the story may close
with the gap in `## Completion Notes`.
- **`Run result: N/A — <reason>`**, only for a story with genuinely nothing
observable — a pure data migration, a save-format change with no surface.
Say why. "It's a Logic story" is not a reason: a damage formula has a number
on screen somewhere.
The run is **not waived at `qa.level: minimal`**. Tests are waived there; the
look is not. It is the cheapest verification in the pipeline and the one
whose absence produces a well-tested game that looks wrong.
## What a still can and cannot show
**Can:** layout, clipping, overflow, presence, alignment, colour, legibility,
that the right screen appeared at all. This is where shipped visual defects
actually live.
**Cannot:** timing, responsiveness, weight, animation curves, audio sync — the
"feel" half of Visual/Feel. Godot's `--write-movie` gives a frame sequence; the
other engines need OS video capture. Feel stays a human check in `/team-qa`.
Say which half the screenshot covered.
## Reaching the state
A screenshot of the title screen verifies nothing about depth 2. Either launch
the scene/map directly (Godot scene arg, Unreal map URL, Unity `--scene`) or
give the bootstrap a debug argument that sets state (`--gold=500 --depth=2`).
Write stories so their surface is reachable one of those ways; a story that is
not is a story that will be marked `NOT VERIFIED` every time.
## Finding the engine
None of the three engines installs onto `PATH` on Windows. `engine.path` in
`project.yaml` is where the editor lives; resolve it before concluding "no
engine on this machine". Absence of a path is not absence of an engine.
---
*Commands and APIs above verified against the official Godot 4.7, Unity 6.3 LTS
and Unreal 5.8 references on 2026-09-14. `docs/engine-reference/<engine>/` is
the project's pinned authority; check it before trusting a version-qualified
claim here.*