@pix3/cli 1.6.0 → 1.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -201
- package/README.md +788 -754
- package/dist/index.js +208 -203
- package/dist/runtime-types.json +1 -1
- package/dist/smoke/prebuilt/{chunk-J2SJVMDO.mjs → chunk-7MZGPAUK.mjs} +1 -1
- package/dist/smoke/prebuilt/chunk-7WOFGLW3.mjs +239 -0
- package/dist/smoke/prebuilt/runtime.mjs +1 -1
- package/dist/smoke/prebuilt/smoke-worker.mjs +1 -1
- package/dist/smoke/prebuilt/tree-defaults.mjs +1 -1
- package/dist/validate/prebuilt/bundle-main.js +13 -13
- package/dist/validate/prebuilt/chunk-HKG6YI6Y.js +239 -0
- package/dist/validate/prebuilt/runtime.js +1 -1
- package/kit/files/.claude/skills/pix3-nodes/SKILL.md +210 -210
- package/kit/files/.claude/skills/pix3-nodes/reference.md +32 -24
- package/kit/files/.claude/skills/pix3-scene-format/SKILL.md +238 -235
- package/kit/files/.claude/skills/pix3-scene-format/reference.md +28 -28
- package/kit/files/.claude/skills/pix3-scripts/SKILL.md +285 -281
- package/kit/files/.claude/skills/pix3-scripts/reference.md +69 -52
- package/kit/files/.claude/skills/pix3-verify/SKILL.md +298 -256
- package/kit/files/.claude/skills/pix3-verify/reference.md +21 -21
- package/kit/files/AGENTS.md +203 -203
- package/kit/files/CLAUDE.md +1 -1
- package/kit/kit.json +2 -2
- package/package.json +54 -54
- package/templates/empty-2d/files/README.md +20 -20
- package/templates/empty-2d/files/scenes/main.pix3scene +68 -68
- package/templates/empty-2d/template.yaml +7 -7
- package/templates/empty-3d/files/README.md +20 -20
- package/templates/empty-3d/files/scenes/main.pix3scene +108 -108
- package/templates/empty-3d/template.yaml +7 -7
- package/templates/idea-blank/files/README.md +22 -22
- package/templates/idea-blank/files/scenes/main.pix3scene +37 -37
- package/templates/idea-blank/template.yaml +10 -10
- package/templates/minigame-2d/files/README.md +44 -44
- package/templates/minigame-2d/files/design/tests/reachability.json +1 -1
- package/templates/minigame-2d/files/design/tests/routines/mute-music.json +44 -44
- package/templates/minigame-2d/files/scenes/main.pix3scene +84 -84
- package/templates/minigame-2d/files/scenes/menu.pix3scene +118 -118
- package/templates/minigame-2d/files/scenes/ui/settings-window.pix3scene +138 -138
- package/templates/minigame-2d/files/scripts/GameFlow.ts +133 -133
- package/templates/minigame-2d/files/scripts/MenuFlow.ts +217 -217
- package/templates/minigame-2d/files/scripts/SettingsWindow.ts +198 -198
- package/templates/minigame-2d/template.yaml +10 -10
- package/templates/playable-2d/files/README.md +30 -30
- package/templates/playable-2d/files/design/recipe.md +83 -83
- package/templates/playable-2d/files/design/tests/reachability.json +4 -4
- package/templates/playable-2d/files/design/tests/routines/intro-to-cta.json +32 -32
- package/templates/playable-2d/files/scenes/main.pix3scene +111 -111
- package/templates/playable-2d/files/scenes/ui/end-screen.pix3scene +91 -91
- package/templates/playable-2d/files/scenes/ui/intro.pix3scene +65 -65
- package/templates/playable-2d/files/scripts/GameFlow.ts +256 -256
- package/templates/playable-2d/template.yaml +9 -9
- package/templates/playable-3d/files/README.md +32 -32
- package/templates/playable-3d/files/design/recipe.md +127 -127
- package/templates/playable-3d/files/design/tests/reachability.json +4 -4
- package/templates/playable-3d/files/design/tests/routines/intro-to-cta.json +32 -32
- package/templates/playable-3d/files/scenes/main.pix3scene +230 -230
- package/templates/playable-3d/files/scenes/ui/end-screen.pix3scene +91 -91
- package/templates/playable-3d/files/scenes/ui/intro.pix3scene +65 -65
- package/templates/playable-3d/files/scripts/GameFlow.ts +309 -309
- package/templates/playable-3d/files/scripts/GameRules.ts +290 -290
- package/templates/playable-3d/files/scripts/ScoreHud.ts +113 -113
- package/templates/playable-3d/template.yaml +9 -9
- package/templates/recipe-arena-2d/files/README.md +48 -48
- package/templates/recipe-arena-2d/files/design/recipe.md +127 -127
- package/templates/recipe-arena-2d/files/scenes/main.pix3scene +292 -292
- package/templates/recipe-arena-2d/files/scenes/menu.pix3scene +100 -100
- package/templates/recipe-arena-2d/files/scenes/prefabs/hazard.pix3scene +42 -42
- package/templates/recipe-arena-2d/files/scenes/prefabs/pickup.pix3scene +45 -45
- package/templates/recipe-arena-2d/files/scenes/ui/result.pix3scene +85 -85
- package/templates/recipe-arena-2d/files/scripts/GameRules.ts +368 -368
- package/templates/recipe-arena-2d/files/scripts/MenuFlow.ts +106 -106
- package/templates/recipe-arena-2d/files/scripts/PlayerController.ts +151 -151
- package/templates/recipe-arena-2d/files/scripts/ScoreHud.ts +94 -94
- package/templates/recipe-arena-2d/files/scripts/Spawner.ts +214 -214
- package/templates/recipe-arena-2d/files/scripts/TouchRules.ts +174 -174
- package/templates/recipe-arena-2d/template.yaml +10 -10
- package/templates/recipe-blank-2d/files/README.md +61 -61
- package/templates/recipe-blank-2d/files/design/recipe.md +148 -148
- package/templates/recipe-blank-2d/files/design/tests/reachability.json +4 -4
- package/templates/recipe-blank-2d/files/design/tests/routines/restart.json +27 -27
- package/templates/recipe-blank-2d/files/scenes/main.pix3scene +224 -224
- package/templates/recipe-blank-2d/files/scenes/ui/result.pix3scene +85 -85
- package/templates/recipe-blank-2d/files/scripts/GameRules.ts +350 -350
- package/templates/recipe-blank-2d/files/scripts/ScoreHud.ts +110 -110
- package/templates/recipe-blank-2d/template.yaml +10 -10
- package/templates/recipe-bouncer-2d/files/README.md +81 -81
- package/templates/recipe-bouncer-2d/files/design/recipe.md +146 -146
- package/templates/recipe-bouncer-2d/files/scenes/main.pix3scene +615 -615
- package/templates/recipe-bouncer-2d/files/scenes/menu.pix3scene +191 -191
- package/templates/recipe-bouncer-2d/files/scenes/ui/result.pix3scene +87 -87
- package/templates/recipe-bouncer-2d/files/scripts/BallBody.ts +298 -298
- package/templates/recipe-bouncer-2d/files/scripts/GameRules.ts +377 -377
- package/templates/recipe-bouncer-2d/files/scripts/MenuFlow.ts +109 -109
- package/templates/recipe-bouncer-2d/files/scripts/PaddleController.ts +112 -112
- package/templates/recipe-bouncer-2d/files/scripts/ScoreHud.ts +94 -94
- package/templates/recipe-bouncer-2d/files/scripts/TouchRules.ts +263 -263
- package/templates/recipe-bouncer-2d/files/scripts/ball-collision.ts +307 -307
- package/templates/recipe-bouncer-2d/template.yaml +10 -10
- package/templates/recipe-grid-3d/files/README.md +22 -22
- package/templates/recipe-grid-3d/files/design/recipe.md +110 -110
- package/templates/recipe-grid-3d/files/scenes/main.pix3scene +169 -169
- package/templates/recipe-grid-3d/files/scenes/menu.pix3scene +100 -100
- package/templates/recipe-grid-3d/files/scenes/ui/result.pix3scene +105 -105
- package/templates/recipe-grid-3d/files/scripts/GridBoard.ts +319 -319
- package/templates/recipe-grid-3d/files/scripts/GridRules.ts +278 -278
- package/templates/recipe-grid-3d/files/scripts/MenuFlow.ts +106 -106
- package/templates/recipe-grid-3d/files/scripts/ScoreHud.ts +94 -94
- package/templates/recipe-grid-3d/template.yaml +10 -10
- package/templates/recipe-tapper-2d/files/README.md +39 -39
- package/templates/recipe-tapper-2d/files/design/recipe.md +132 -132
- package/templates/recipe-tapper-2d/files/design/tests/reachability.json +4 -4
- package/templates/recipe-tapper-2d/files/design/tests/routines/terminal-retry.json +23 -23
- package/templates/recipe-tapper-2d/files/scenes/main.pix3scene +269 -269
- package/templates/recipe-tapper-2d/files/scenes/menu.pix3scene +100 -100
- package/templates/recipe-tapper-2d/files/scenes/prefabs/hazard.pix3scene +42 -42
- package/templates/recipe-tapper-2d/files/scenes/prefabs/target.pix3scene +45 -45
- package/templates/recipe-tapper-2d/files/scenes/ui/result.pix3scene +85 -85
- package/templates/recipe-tapper-2d/files/scripts/GameRules.ts +368 -368
- package/templates/recipe-tapper-2d/files/scripts/MenuFlow.ts +106 -106
- package/templates/recipe-tapper-2d/files/scripts/ScoreHud.ts +94 -94
- package/templates/recipe-tapper-2d/files/scripts/Spawner.ts +214 -214
- package/templates/recipe-tapper-2d/files/scripts/TouchRules.ts +192 -192
- package/templates/recipe-tapper-2d/template.yaml +10 -10
- package/dist/smoke/prebuilt/chunk-OJXQXIIQ.mjs +0 -239
- package/dist/validate/prebuilt/chunk-BS2OZM7U.js +0 -239
|
@@ -1,130 +1,136 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: pix3-verify
|
|
3
|
-
description: How to check and run a Pix3 change — `pix3 check` / `pix3 validate` / `pix3 read`, the diagnostic codes, reading merge-log entries (the editor kept a human's value over yours), the live channel (`pix3 serve` + `pix3 mcp --workspace`, `game_run` with `expect` hashes, the barrier's answer fields and error codes), and how to report what was and was not verified. Use after every batch of edits to .pix3scene or scripts/*.ts, and before telling the user a change is done.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
<!-- Pix3 agent kit 1.6.
|
|
7
|
-
# Verify a change
|
|
8
|
-
|
|
9
|
-
Leave files that load and type-check, run the game when the live channel is there, and tell
|
|
10
|
-
the human exactly what to press and what they should see.
|
|
11
|
-
|
|
12
|
-
## 1. `pix3 check` after every batch
|
|
13
|
-
|
|
14
|
-
```bash
|
|
15
|
-
pix3 check # validate (both levels) + tsc over the scripts + merge-log + versions
|
|
16
|
-
pix3 check --json # the same, machine-readable
|
|
17
|
-
pix3 validate [paths] [--json] # scenes/prefabs only, no type-check
|
|
18
|
-
pix3 smoke scenes/main.pix3scene # then: run the game headless for 120 frames, report what threw
|
|
19
|
-
pix3 tree [scene] # outline of a scene (or of the project) instead of reading it
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
If `pix3` is not on your PATH, run the pinned one from `.mcp.json`: `npx -y @pix3/cli@1.6.
|
|
23
|
-
|
|
24
|
-
Exit code 1 = at least one **error**. Fix every error before reporting; warnings (off-screen
|
|
25
|
-
nodes, zero size, unused assets, out-of-range values) are for judgement.
|
|
26
|
-
|
|
27
|
-
`--json` answers `{ ok, errorCount, warningCount, files, diagnostics, notes, typecheck,
|
|
28
|
-
mergeLog, kit, … }`:
|
|
29
|
-
|
|
30
|
-
- `files` — every scene validated and every script type-checked, as
|
|
31
|
-
`[{ "file": "scenes/main.pix3scene", "sha256": "…" }]` (the key is `file`, not `path`) — the
|
|
32
|
-
**sha256 of its raw bytes**. These are the hashes `expect` and `pix3 ack --sha256` take.
|
|
33
|
-
- `diagnostics` — one list: `{ severity, code, file, line?, nodeId?, path?, message, fix? }`.
|
|
34
|
-
- `typecheck` — `{ ok, errors, tsconfig, mode, files, typescript, skipped }`; tsc errors are
|
|
35
|
-
`E_TYPE` diagnostics in the list above; `skipped` says why tsc did not run at all.
|
|
36
|
-
- `mergeLog` — the newest `.pix3/merge-log.jsonl` entries (section 2).
|
|
37
|
-
- `kit` — `{ version, cliVersion, upToDate }`: `pix3 kit --update` when it is stale.
|
|
38
|
-
|
|
39
|
-
The first `check` on a machine may install TypeScript once into `~/.pix3/typescript/` (it
|
|
40
|
-
prints the `npm install` it runs; `--offline` refuses and prints the command instead).
|
|
41
|
-
|
|
42
|
-
**A project with its own `package.json` needs `npm install` before `pix3 check` can
|
|
43
|
-
type-check.** Such a project has its own `tsconfig.json` and type-checks against its own
|
|
44
|
-
`node_modules`; without them `check` reports one `E_DEPENDENCIES_MISSING` (fix: `npm install`)
|
|
45
|
-
and skips tsc — the scenes are still validated. Run `npm install` once, then `check` again.
|
|
46
|
-
|
|
47
|
-
Codes and what they usually mean:
|
|
48
|
-
|
|
49
|
-
| Code | Usual cause / fix |
|
|
50
|
-
| --- | --- |
|
|
51
|
-
| `E_YAML` | Bad indentation, unquoted `#colour`, a `:` inside an unquoted string |
|
|
52
|
-
| `E_UNKNOWN_NODE_TYPE` | Typo in the node type — use the suggested name |
|
|
53
|
-
| `E_UNKNOWN_COMPONENT` | `user:X` with no exported `class X extends Script` that has `static getPropertySchema()` in `scripts/`; or a `core:` name that does not exist |
|
|
54
|
-
| `E_UNKNOWN_PROPERTY` | Key not read for that node / component — check `pix3-nodes`; a flat `horizontalAlign` belongs in `layout:` |
|
|
55
|
-
| `E_PROPERTY_TYPE` | `"10"` for a number, a colour without quotes, an enum value not in the list |
|
|
56
|
-
| `E_MISSING_RESOURCE` | `res://` path typo, or the asset was never written |
|
|
57
|
-
| `E_MISSING_PREFAB`, `E_PREFAB_*`, `E_DUPLICATE_ID` | Fix the `instance:` path; a prefab file needs exactly one root; ids are unique |
|
|
58
|
-
| `E_EMOJI_AS_ART` | A `label`/`text` that is only emoji — use a sprite or `ColorRect2D` |
|
|
59
|
-
| `E_SVG_INVALID`, `E_SVG_NO_SIZE`, `W_SVG_VIEWBOX_ONLY` | An `.svg` sprite without `xmlns` or without `width`/`height` in px — see the SVG template in `pix3-nodes` |
|
|
60
|
-
| `W_SVG_EXTERNAL_REF` | An `.svg` that links a file, URL or font — it will draw without it; inline it |
|
|
61
|
-
| `E_TYPE` | A TypeScript error in a script (`TS2339: Property … does not exist …`) |
|
|
62
|
-
| `E_TYPECHECK_UNAVAILABLE` | TypeScript could not be installed — run the printed command |
|
|
63
|
-
| `E_DEPENDENCIES_MISSING` | A project with its own `tsconfig.json` and no `node_modules` — `npm install`; tsc was skipped |
|
|
64
|
-
| `W_RUNTIME_VERSION_MISMATCH` / `W_RUNTIME_NOT_INSTALLED` | A project with its own `tsconfig.json` type-checks against its own `node_modules/@pix3/runtime` — `npm install` it at the CLI's version |
|
|
65
|
-
| `W_KIT_OUTDATED` | This kit is older than the CLI — `pix3 kit --update` |
|
|
66
|
-
|
|
67
|
-
`pix3 validate --help` and `pix3 check --help` list every code. Level 2 of validate compiles
|
|
68
|
-
and loads your scripts to check the properties of `user:` components; with `--no-hydrate` (or
|
|
69
|
-
when scripts cannot be loaded) those are **not** checked — say so if your change depends on them.
|
|
70
|
-
|
|
71
|
-
### `pix3 smoke` — when no editor is connected
|
|
72
|
-
|
|
73
|
-
`pix3 smoke <scene>` (after a green `check`) runs that scene in Node: your scripts compiled, the
|
|
74
|
-
scene loaded by the real loader, `--frames N` steps of 1/60 s (default 120). **Name the scene you
|
|
75
|
-
changed** — the game is `pix3 smoke scenes/main.pix3scene`. Input is empty, so a menu never
|
|
76
|
-
presses PLAY: smoking the menu (the build's entry scene) proves nothing about the game. With no
|
|
77
|
-
scene, smoke runs several, one line each and exit 1 if any fails: the top-level scenes your
|
|
78
|
-
uncommitted changes reach (git: the scene, a prefab or overlay it instances, a `user:` script it
|
|
79
|
-
attaches), else — no git, nothing changed, or a change it cannot trace — every top-level scene,
|
|
80
|
-
`scenes/main.pix3scene` first (`--changed` / `--all` force either; `--json` then answers
|
|
81
|
-
`{ ok, selection, reason, runs: [one report per scene] }`). Exit 1 lists every
|
|
82
|
-
`E_SMOKE_SCRIPT` (a hook threw: script, node, frame — 0 = `onAttach`/`onStart` — and a stack
|
|
83
|
-
pointing at your `.ts` line), `E_SMOKE_CONSOLE_ERROR`, `E_SMOKE_UNHANDLED` (a rejected promise
|
|
84
|
-
nobody awaited), `E_SMOKE_DOM` (a browser API the headless run lacks — `domAccess` names it; a
|
|
85
|
-
browser-only feature, not necessarily a bug: say so). `W_SMOKE_MISSING_RESOURCE` is a `res://`
|
|
86
|
-
typo. Exit 2 = it could not run (`E_SMOKE_NO_SCENE`: pass the scene; `E_SMOKE_TIMEOUT`: a loop
|
|
87
|
-
that never ends). Nothing is drawn, heard or tapped: a green smoke run proves the game starts and
|
|
88
|
-
runs without throwing, not that it plays — report it as exactly that, and tell the human what to
|
|
89
|
-
press and what they should see (section 4). With the live channel, `game_run` (section 3) is the
|
|
90
|
-
stronger check.
|
|
91
|
-
|
|
92
|
-
**No live channel: the fallback, in this order** — `pix3 check` (green), then
|
|
93
|
-
`pix3 smoke <the scene you changed>` (green), then ask the human to press Play in that scene,
|
|
94
|
-
naming what to press and what they should see.
|
|
95
|
-
|
|
96
|
-
## 2. Merge-log: the editor kept the human's value
|
|
97
|
-
|
|
98
|
-
The human edits the same files in the editor. When you write a value over one the human set
|
|
99
|
-
after your last read, the editor keeps the human's value and logs it to
|
|
100
|
-
`.pix3/merge-log.jsonl`; `pix3 check` prints the newest entries, e.g.
|
|
101
|
-
`scenes/main.pix3scene merged: the editor KEPT 1 human value(s) over yours`.
|
|
102
|
-
|
|
103
|
-
When you see one:
|
|
104
|
-
|
|
105
|
-
1. `pix3 read <file>` — prints the current file and records that you have read this version
|
|
106
|
-
(`.pix3/ack.json`).
|
|
107
|
-
2. Decide: is your value still intended given what the human did? If yes, write it again —
|
|
108
|
-
it now sticks. If unsure, ask the human (that is your one question this turn).
|
|
109
|
-
|
|
110
|
-
Without step 1 the editor keeps restoring the human's value on every write you make, even if
|
|
111
|
-
you write the same number the human chose.
|
|
112
|
-
|
|
113
|
-
`check` prints `note: … read confirmation for a version the editor has not recorded — ignored
|
|
114
|
-
as a note, not a merge-log line: your `pix3 read` named bytes the editor has no
|
|
115
|
-
having loaded or written (e.g. a version that was on disk only between two of its
|
|
116
|
-
Nothing was lost and nothing is protected by it; no action unless a KEPT line follows
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
1
|
+
---
|
|
2
|
+
name: pix3-verify
|
|
3
|
+
description: How to check and run a Pix3 change — `pix3 check` / `pix3 validate` / `pix3 read`, the diagnostic codes, reading merge-log entries (the editor kept a human's value over yours), the live channel (`pix3 serve` + `pix3 mcp --workspace`, `game_run` with `expect` hashes, the barrier's answer fields and error codes), and how to report what was and was not verified. Use after every batch of edits to .pix3scene or scripts/*.ts, and before telling the user a change is done.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!-- Pix3 agent kit 1.6.2 -->
|
|
7
|
+
# Verify a change
|
|
8
|
+
|
|
9
|
+
Leave files that load and type-check, run the game when the live channel is there, and tell
|
|
10
|
+
the human exactly what to press and what they should see.
|
|
11
|
+
|
|
12
|
+
## 1. `pix3 check` after every batch
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pix3 check # validate (both levels) + tsc over the scripts + merge-log + versions
|
|
16
|
+
pix3 check --json # the same, machine-readable
|
|
17
|
+
pix3 validate [paths] [--json] # scenes/prefabs only, no type-check
|
|
18
|
+
pix3 smoke scenes/main.pix3scene # then: run the game headless for 120 frames, report what threw
|
|
19
|
+
pix3 tree [scene] # outline of a scene (or of the project) instead of reading it
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
If `pix3` is not on your PATH, run the pinned one from `.mcp.json`: `npx -y @pix3/cli@1.6.2 check`.
|
|
23
|
+
|
|
24
|
+
Exit code 1 = at least one **error**. Fix every error before reporting; warnings (off-screen
|
|
25
|
+
nodes, zero size, unused assets, out-of-range values) are for judgement.
|
|
26
|
+
|
|
27
|
+
`--json` answers `{ ok, errorCount, warningCount, files, diagnostics, notes, typecheck,
|
|
28
|
+
mergeLog, kit, … }`:
|
|
29
|
+
|
|
30
|
+
- `files` — every scene validated and every script type-checked, as
|
|
31
|
+
`[{ "file": "scenes/main.pix3scene", "sha256": "…" }]` (the key is `file`, not `path`) — the
|
|
32
|
+
**sha256 of its raw bytes**. These are the hashes `expect` and `pix3 ack --sha256` take.
|
|
33
|
+
- `diagnostics` — one list: `{ severity, code, file, line?, nodeId?, path?, message, fix? }`.
|
|
34
|
+
- `typecheck` — `{ ok, errors, tsconfig, mode, files, typescript, skipped }`; tsc errors are
|
|
35
|
+
`E_TYPE` diagnostics in the list above; `skipped` says why tsc did not run at all.
|
|
36
|
+
- `mergeLog` — the newest `.pix3/merge-log.jsonl` entries (section 2).
|
|
37
|
+
- `kit` — `{ version, cliVersion, upToDate }`: `pix3 kit --update` when it is stale.
|
|
38
|
+
|
|
39
|
+
The first `check` on a machine may install TypeScript once into `~/.pix3/typescript/` (it
|
|
40
|
+
prints the `npm install` it runs; `--offline` refuses and prints the command instead).
|
|
41
|
+
|
|
42
|
+
**A project with its own `package.json` needs `npm install` before `pix3 check` can
|
|
43
|
+
type-check.** Such a project has its own `tsconfig.json` and type-checks against its own
|
|
44
|
+
`node_modules`; without them `check` reports one `E_DEPENDENCIES_MISSING` (fix: `npm install`)
|
|
45
|
+
and skips tsc — the scenes are still validated. Run `npm install` once, then `check` again.
|
|
46
|
+
|
|
47
|
+
Codes and what they usually mean:
|
|
48
|
+
|
|
49
|
+
| Code | Usual cause / fix |
|
|
50
|
+
| --- | --- |
|
|
51
|
+
| `E_YAML` | Bad indentation, unquoted `#colour`, a `:` inside an unquoted string |
|
|
52
|
+
| `E_UNKNOWN_NODE_TYPE` | Typo in the node type — use the suggested name |
|
|
53
|
+
| `E_UNKNOWN_COMPONENT` | `user:X` with no exported `class X extends Script` that has `static getPropertySchema()` in `scripts/`; or a `core:` name that does not exist |
|
|
54
|
+
| `E_UNKNOWN_PROPERTY` | Key not read for that node / component — check `pix3-nodes`; a flat `horizontalAlign` belongs in `layout:` |
|
|
55
|
+
| `E_PROPERTY_TYPE` | `"10"` for a number, a colour without quotes, an enum value not in the list |
|
|
56
|
+
| `E_MISSING_RESOURCE` | `res://` path typo, or the asset was never written |
|
|
57
|
+
| `E_MISSING_PREFAB`, `E_PREFAB_*`, `E_DUPLICATE_ID` | Fix the `instance:` path; a prefab file needs exactly one root; ids are unique |
|
|
58
|
+
| `E_EMOJI_AS_ART` | A `label`/`text` that is only emoji — use a sprite or `ColorRect2D` |
|
|
59
|
+
| `E_SVG_INVALID`, `E_SVG_NO_SIZE`, `W_SVG_VIEWBOX_ONLY` | An `.svg` sprite without `xmlns` or without `width`/`height` in px — see the SVG template in `pix3-nodes` |
|
|
60
|
+
| `W_SVG_EXTERNAL_REF` | An `.svg` that links a file, URL or font — it will draw without it; inline it |
|
|
61
|
+
| `E_TYPE` | A TypeScript error in a script (`TS2339: Property … does not exist …`) |
|
|
62
|
+
| `E_TYPECHECK_UNAVAILABLE` | TypeScript could not be installed — run the printed command |
|
|
63
|
+
| `E_DEPENDENCIES_MISSING` | A project with its own `tsconfig.json` and no `node_modules` — `npm install`; tsc was skipped |
|
|
64
|
+
| `W_RUNTIME_VERSION_MISMATCH` / `W_RUNTIME_NOT_INSTALLED` | A project with its own `tsconfig.json` type-checks against its own `node_modules/@pix3/runtime` — `npm install` it at the CLI's version |
|
|
65
|
+
| `W_KIT_OUTDATED` | This kit is older than the CLI — `pix3 kit --update` |
|
|
66
|
+
|
|
67
|
+
`pix3 validate --help` and `pix3 check --help` list every code. Level 2 of validate compiles
|
|
68
|
+
and loads your scripts to check the properties of `user:` components; with `--no-hydrate` (or
|
|
69
|
+
when scripts cannot be loaded) those are **not** checked — say so if your change depends on them.
|
|
70
|
+
|
|
71
|
+
### `pix3 smoke` — when no editor is connected
|
|
72
|
+
|
|
73
|
+
`pix3 smoke <scene>` (after a green `check`) runs that scene in Node: your scripts compiled, the
|
|
74
|
+
scene loaded by the real loader, `--frames N` steps of 1/60 s (default 120). **Name the scene you
|
|
75
|
+
changed** — the game is `pix3 smoke scenes/main.pix3scene`. Input is empty, so a menu never
|
|
76
|
+
presses PLAY: smoking the menu (the build's entry scene) proves nothing about the game. With no
|
|
77
|
+
scene, smoke runs several, one line each and exit 1 if any fails: the top-level scenes your
|
|
78
|
+
uncommitted changes reach (git: the scene, a prefab or overlay it instances, a `user:` script it
|
|
79
|
+
attaches), else — no git, nothing changed, or a change it cannot trace — every top-level scene,
|
|
80
|
+
`scenes/main.pix3scene` first (`--changed` / `--all` force either; `--json` then answers
|
|
81
|
+
`{ ok, selection, reason, runs: [one report per scene] }`). Exit 1 lists every
|
|
82
|
+
`E_SMOKE_SCRIPT` (a hook threw: script, node, frame — 0 = `onAttach`/`onStart` — and a stack
|
|
83
|
+
pointing at your `.ts` line), `E_SMOKE_CONSOLE_ERROR`, `E_SMOKE_UNHANDLED` (a rejected promise
|
|
84
|
+
nobody awaited), `E_SMOKE_DOM` (a browser API the headless run lacks — `domAccess` names it; a
|
|
85
|
+
browser-only feature, not necessarily a bug: say so). `W_SMOKE_MISSING_RESOURCE` is a `res://`
|
|
86
|
+
typo. Exit 2 = it could not run (`E_SMOKE_NO_SCENE`: pass the scene; `E_SMOKE_TIMEOUT`: a loop
|
|
87
|
+
that never ends). Nothing is drawn, heard or tapped: a green smoke run proves the game starts and
|
|
88
|
+
runs without throwing, not that it plays — report it as exactly that, and tell the human what to
|
|
89
|
+
press and what they should see (section 4). With the live channel, `game_run` (section 3) is the
|
|
90
|
+
stronger check.
|
|
91
|
+
|
|
92
|
+
**No live channel: the fallback, in this order** — `pix3 check` (green), then
|
|
93
|
+
`pix3 smoke <the scene you changed>` (green), then ask the human to press Play in that scene,
|
|
94
|
+
naming what to press and what they should see.
|
|
95
|
+
|
|
96
|
+
## 2. Merge-log: the editor kept the human's value
|
|
97
|
+
|
|
98
|
+
The human edits the same files in the editor. When you write a value over one the human set
|
|
99
|
+
after your last read, the editor keeps the human's value and logs it to
|
|
100
|
+
`.pix3/merge-log.jsonl`; `pix3 check` prints the newest entries, e.g.
|
|
101
|
+
`scenes/main.pix3scene merged: the editor KEPT 1 human value(s) over yours`.
|
|
102
|
+
|
|
103
|
+
When you see one:
|
|
104
|
+
|
|
105
|
+
1. `pix3 read <file>` — prints the current file and records that you have read this version
|
|
106
|
+
(`.pix3/ack.json`).
|
|
107
|
+
2. Decide: is your value still intended given what the human did? If yes, write it again —
|
|
108
|
+
it now sticks. If unsure, ask the human (that is your one question this turn).
|
|
109
|
+
|
|
110
|
+
Without step 1 the editor keeps restoring the human's value on every write you make, even if
|
|
111
|
+
you write the same number the human chose.
|
|
112
|
+
|
|
113
|
+
`check` prints `note: … read confirmation for a version the editor has not recorded — ignored
|
|
114
|
+
(harmless …)` as a note, not a merge-log line: your `pix3 read` named bytes the editor has no
|
|
115
|
+
record of having loaded or written (e.g. a version that was on disk only between two of its
|
|
116
|
+
polls). Nothing was lost and nothing is protected by it; no action unless a KEPT line follows,
|
|
117
|
+
and it stops printing once the file changes. (`pix3 ack <file> --sha256 <hash>` confirms a
|
|
118
|
+
version whose hash you took from `pix3 check --json` without printing it again.)
|
|
119
|
+
|
|
120
|
+
**A compile error hides level 2.** With `E_SCRIPT_COMPILE` in the list, every scene with `user:`
|
|
121
|
+
components is skipped at level 2 — `check` says `SKIPPED: N scene(s) …` and the summary line
|
|
122
|
+
reads `level 2 hydrated 0 file(s), N SKIPPED`. Their config/property errors are *missing*, not
|
|
123
|
+
passed: fix the compile error, run `check` again, and only then read the rest.
|
|
124
|
+
|
|
125
|
+
## 3. Live channel — run the game yourself
|
|
126
|
+
|
|
127
|
+
Available when the human runs `pix3 serve` in the project and connects the editor to it
|
|
128
|
+
(File → Connect to Workspace…); your MCP client starts `pix3 mcp --workspace` from `.mcp.json`
|
|
129
|
+
(`pix3 setup claude|codex` prints the registration for a client without it). Without a server
|
|
130
|
+
every tool answers `no_workspace_server`; without a connected window, `no_editor`.
|
|
131
|
+
|
|
132
|
+
The 14 tools:
|
|
133
|
+
|
|
128
134
|
- `project_status` — Whether a Pix3 editor window is connected to this project, what it has open, external versions it has not applied yet, unreadable files, merge conflicts and the latest merge-log entries (why a write of yours did not land as written).
|
|
129
135
|
- `play_start` — Enter play mode.
|
|
130
136
|
- `play_stop` — Exit play mode.
|
|
@@ -133,98 +139,120 @@ The 14 tools:
|
|
|
133
139
|
- `game_run` — Run the game frame by frame until a predicate holds (`until`, OR), fails (`fail`) or a budget runs out, or run a stored routine from design/tests/routines (`routine`, `args`).
|
|
134
140
|
- `game_input` — Send real input to the running game (steps: tap / key / keys / drag / hover / invoke / wait) and report the reaction; `observe` watches nodes over the window.
|
|
135
141
|
- `game_observe` — Read live node state of the running game (by name or id) without sending input.
|
|
136
|
-
- `read_errors` — Recent runtime errors captured in the editor (console.error, window errors, rejections).
|
|
142
|
+
- `read_errors` — Recent runtime errors captured in the editor (console.error, window errors, rejections), each with an epoch-ms `at`; `since` (epoch ms) returns only newer ones.
|
|
137
143
|
- `read_logs` — Recent editor log entries; `since` (epoch ms) returns only newer ones.
|
|
138
144
|
- `viewport_screenshot` — A screenshot as an image: the running game while playing, else the editor viewport.
|
|
139
145
|
- `generate_asset` — Generate an image with the editor's configured image provider (the user's keys — the editor asks the user to allow this for the session), post-process it and save it into the project.
|
|
140
146
|
- `generate_sfx` — Generate a prototype sound effect (WAV under res://sfx/) with the editor's LLM (asks the user to allow this for the session).
|
|
141
|
-
- `get_selection` — What the human has selected in the editor right now (node ids, primary, hovered).
|
|
142
|
-
|
|
143
|
-
The loop after a batch of edits:
|
|
144
|
-
|
|
145
|
-
1. `pix3 check --json` → take the `sha256` of every file you wrote from `files`
|
|
146
|
-
(`{ file, sha256 }` entries).
|
|
147
|
-
2. A barrier tool — `game_run`, `play_start` or `play_restart` — with
|
|
148
|
-
`expect: { "<path>": "<sha256>", … }`: the run starts only if the disk holds exactly those
|
|
149
|
-
versions and the editor loaded them.
|
|
150
|
-
3. Green = no error, `matchesAgent: true`, `matchesDisk: true`, empty `changedDuringRun`. Then
|
|
151
|
-
`read_errors`, `game_observe` / `viewport_screenshot` to see what happened.
|
|
152
|
-
4. `disk_differs_from_agent` with `mergeLog: true` → the editor merged your file with a human
|
|
153
|
-
edit: `pix3 read` it (section 2). With `recovery` → someone overwrote it; the named file under
|
|
154
|
-
`.pix3/recovery/` holds your version. Otherwise write the file again.
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
`
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
`
|
|
206
|
-
`
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
147
|
+
- `get_selection` — What the human has selected in the editor right now (node ids, primary, hovered).
|
|
148
|
+
|
|
149
|
+
The loop after a batch of edits:
|
|
150
|
+
|
|
151
|
+
1. `pix3 check --json` → take the `sha256` of every file you wrote from `files`
|
|
152
|
+
(`{ file, sha256 }` entries).
|
|
153
|
+
2. A barrier tool — `game_run`, `play_start` or `play_restart` — with
|
|
154
|
+
`expect: { "<path>": "<sha256>", … }`: the run starts only if the disk holds exactly those
|
|
155
|
+
versions and the editor loaded them.
|
|
156
|
+
3. Green = no error, `matchesAgent: true`, `matchesDisk: true`, empty `changedDuringRun`. Then
|
|
157
|
+
`read_errors`, `game_observe` / `viewport_screenshot` to see what happened.
|
|
158
|
+
4. `disk_differs_from_agent` with `mergeLog: true` → the editor merged your file with a human
|
|
159
|
+
edit: `pix3 read` it (section 2). With `recovery` → someone overwrote it; the named file under
|
|
160
|
+
`.pix3/recovery/` holds your version. Otherwise write the file again.
|
|
161
|
+
5. `expectation_stale` → the disk held your version when the call started, then moved on
|
|
162
|
+
(the editor merged your write, a human saved, another agent wrote) and the editor already
|
|
163
|
+
holds the new bytes. Not editor lag, and nothing started: `pix3 read` the file, re-run
|
|
164
|
+
`pix3 check --json`, pass the new hashes. `sync_timeout` is the other case — the editor is
|
|
165
|
+
still catching up with a write; call again when the writes are done.
|
|
166
|
+
|
|
167
|
+
### Two ways to prove behaviour
|
|
168
|
+
|
|
169
|
+
**Every barrier call stops the game and starts it again from the files on disk.** `game_run`
|
|
170
|
+
included: it is always a run from a fresh start, so it can never judge state that an earlier
|
|
171
|
+
`game_input` produced — that state is gone before its first frame. Pick the pattern by what
|
|
172
|
+
causes the behaviour:
|
|
173
|
+
|
|
174
|
+
- **(a) Autonomous behaviour** (spawns, timers, physics, a HUD that counts on its own):
|
|
175
|
+
one `game_run` with `expect`, predicates in `until` (any one ends the run as a pass) and
|
|
176
|
+
`fail` (any one ends it as a failure), e.g. `until:
|
|
177
|
+
[{ kind: "nodeProperty", name: "ScoreLabel", path: "text", op: "contains", value: "10" }]`,
|
|
178
|
+
`fail: [{ kind: "newErrors" }]` (a script threw). Read `verdict`, then the result's
|
|
179
|
+
`newErrors` count, then `read_errors` when it is not 0 (pass `since`, the epoch ms you noted
|
|
180
|
+
before the run, or old errors come back too).
|
|
181
|
+
**`game_run` outruns real time**: it steps hundreds of frames in a fraction of a second, so a
|
|
182
|
+
game whose `onStart` awaits something real (a physics WASM module, assets, a fetch) is still
|
|
183
|
+
initialising when the run ends — an empty screenshot or `ready: false` there is not a bug yet.
|
|
184
|
+
Give it real time with `settleMs` (e.g. `2000`) and assert readiness first
|
|
185
|
+
(`until: [{ kind: "gameState", path: "ready", op: "eq", value: true }]`); the report's
|
|
186
|
+
`notes`/`verdict` say `OUTRAN REAL TIME` when a run stepped far faster than real time and the
|
|
187
|
+
game's state never changed.
|
|
188
|
+
- **(b) Input-driven behaviour** (taps, keys, combos): `play_start` or `play_restart` with
|
|
189
|
+
`expect` (the barrier, once) → one or more `game_input` calls → `game_observe` / `read_logs`.
|
|
190
|
+
`game_input` and `game_observe` are observing tools: they neither stop nor resync, so the
|
|
191
|
+
state builds up across calls. Do **not** end with `game_run` to "check the result" — it
|
|
192
|
+
restarts the game.
|
|
193
|
+
|
|
194
|
+
### `game_input` — what the steps really do
|
|
195
|
+
|
|
196
|
+
- **Coordinates** (`x`, `y` on `tap` / `drag` / `hover`) are the 2D design space the scene
|
|
197
|
+
file uses: origin at the **centre**, **Y up**, the same numbers as a node's `position`. With
|
|
198
|
+
the 2D camera at rest, `{ x: 0, y: 0 }` is the centre of the viewport and the top edge of a
|
|
199
|
+
1080 x 1920 design is `y: 960` — not `(540, 960)` or `(960, 540)`. Coordinates follow the 2D
|
|
200
|
+
camera; a node under a `CanvasLayer2D` (a HUD) is pinned to the screen instead, so for HUD and
|
|
201
|
+
buttons prefer `target: "<node name or id>"`, which projects the node's live position the
|
|
202
|
+
right way for either.
|
|
203
|
+
- **`tap` holds the pointer down 80 ms by default** — a tap (a `Button2D` sees the press on one
|
|
204
|
+
tick and the release on a later one). A game that tells a tap from a **hold** (mine while the
|
|
205
|
+
finger is down, charge a shot) needs `holdMs` set to what it expects — the old 700 ms default
|
|
206
|
+
read as a hold and produced "no activity"; a NO ACTIVITY verdict now names the hold it used.
|
|
207
|
+
`frames` instead of `ms` / `holdMs` counts game ticks.
|
|
208
|
+
- **One call is capped at 15 s** of requested time (holds + drags + waits); longer answers
|
|
209
|
+
`Input script too long` — split it into several `game_input` calls.
|
|
210
|
+
- **Input goes through the engine's `InputService`**: `PointerEvent`s on the game canvas and
|
|
211
|
+
`KeyboardEvent`s on `window`. Scripts that read `this.input` see it. A game that listens for
|
|
212
|
+
DOM `mouse*` / `click` / `touch*` events, keys on `document`, or on a canvas of its own does
|
|
213
|
+
**not** — verify such input by hand.
|
|
214
|
+
|
|
215
|
+
### Reading the answers
|
|
216
|
+
|
|
217
|
+
- **Barrier answers** carry `revision`, `startupMs` (how long the game took to start),
|
|
218
|
+
`matchesAgent` / `agentExpectations`, `matchesDisk`, `changedDuringRun`,
|
|
219
|
+
`editorWroteDuringRun`, `editorChangedSinceAgentWrite` and `result` (the editor tool's own
|
|
220
|
+
answer). **Observing answers** are `{ revision, stale, result }`.
|
|
221
|
+
- `revision` describes the running game's verified files (every open scene, every script the
|
|
222
|
+
build read, `pix3project.yaml`) **compactly**: `{ files, digest, changed }` — the count, a
|
|
223
|
+
digest of the whole map, and only the entries that changed since the previous answer of your
|
|
224
|
+
`pix3 mcp` process (all of them on the first answer). `fullRevision: true` on any barrier or
|
|
225
|
+
observing call returns the whole `{ path: sha256 }` map; `project_status` →
|
|
226
|
+
`editor.playRevision` has it too while the game plays. Check `stale` and the match flags;
|
|
227
|
+
never paste `revision` into a report.
|
|
228
|
+
- `game_observe` nodes carry `size` (`{ width, height }`) and `bounds` (axis-aligned world
|
|
229
|
+
rectangle, rotation ignored) for 2D nodes that have a width/height — enough to tell "on
|
|
230
|
+
screen / overlapping" without a screenshot.
|
|
231
|
+
- **Labels and HUD text.** An observed node reports its rendered `text` only at the window's
|
|
232
|
+
**start and end** — `observed.<node>.before.text` / `.after.text` in `game_input`,
|
|
233
|
+
`nodes.<node>.text` (and `movement.<node>.before` / `.after` with `sampleMs` or `frames`) in
|
|
234
|
+
`game_observe`;
|
|
235
|
+
there is no per-frame text timeline. The one-line `verdict` does not count a text change:
|
|
236
|
+
it can say `NO ACTIVITY` while the label went from `x1` to `x3`. For HUD text, read the text
|
|
237
|
+
fields, never the verdict alone.
|
|
238
|
+
- **Timing proofs** ("the combo resets after 1 s", "x3 appears on the third hit"): add
|
|
239
|
+
temporary `console.info('[combo] x3')` markers in the script, note the current epoch ms
|
|
240
|
+
before the input, run it, then `read_logs` with `since` set to that value — every console line
|
|
241
|
+
the game prints arrives with its own epoch-ms `timestamp`, so the gaps between markers are the
|
|
242
|
+
timing. **Remove the markers afterwards** and run `pix3 check` again.
|
|
243
|
+
- **Movement below ~0.5 units reads as `moved: false`**, and a shake that returns to rest
|
|
244
|
+
inside the window has no endpoint delta at all — a small camera shake is not provable from
|
|
245
|
+
`moved`. Take a `viewport_screenshot` mid-shake or log the offset with a marker.
|
|
246
|
+
- **Sound**: synthetic input is not a user gesture, so the browser may keep Web Audio
|
|
247
|
+
suspended; nothing in the answers says whether a sound was heard. Report it as "code path
|
|
248
|
+
verified, audibility not".
|
|
249
|
+
- **Latency** (measured in trials): observing calls take about 1 s; a barrier call takes the
|
|
250
|
+
game's startup plus 1–2 s — a small 2D game a few seconds, a heavy 3D game 13–14 s
|
|
251
|
+
(`startupMs` says which). Batch several input steps into one `game_input` rather than many
|
|
252
|
+
calls.
|
|
253
|
+
|
|
254
|
+
The contract, as the CLI documents it:
|
|
255
|
+
|
|
228
256
|
A stdio MCP server the agent starts from its config (`.mcp.json`, `pix3 setup`). It has no port
|
|
229
257
|
of its own: it finds the running `pix3 serve` of the project (`--project`, else the nearest
|
|
230
258
|
ancestor of the cwd with `pix3project.yaml`) through `.pix3/workspace.json` and confirms it with
|
|
@@ -265,7 +293,12 @@ raw bytes of every file the agent wrote.
|
|
|
265
293
|
MCP process gives this call 100 s. These hashes (plus `expect`) are compared with `/ws/agent/hash` at that moment;
|
|
266
294
|
a mismatch retries the editor's sync for up to ~5 s. Then: loader/compiler errors →
|
|
267
295
|
`load_failed` (`{file, line, message, kind}`); a file that stays unreadable →
|
|
268
|
-
`pending_external`; hashes that never agree → `sync_timeout` with the differing paths
|
|
296
|
+
`pending_external`; hashes that never agree → `sync_timeout` with the differing paths — except
|
|
297
|
+
when every difference is an `expect` path whose disk version moved on *after* step 1 while the
|
|
298
|
+
editor already holds what the disk holds (`loadedHash === diskHash !== agentHash`): that is the
|
|
299
|
+
agent's expectation being stale (the editor merged the write with a human edit, a human saved,
|
|
300
|
+
another agent wrote), retrying cannot bring the old bytes back, and it answers
|
|
301
|
+
`expectation_stale` at once. The
|
|
269
302
|
window then starts the game (`game_run` starts play itself; `play_restart` of a stopped game is
|
|
270
303
|
a start) and answers only once the game is actually running: up to 30 s, as soon as it runs,
|
|
271
304
|
failing fast (`load_failed` with the play-mode error) when play mode stops instead; `startupMs`
|
|
@@ -281,7 +314,8 @@ The answer of a barrier tool:
|
|
|
281
314
|
|
|
282
315
|
```json
|
|
283
316
|
{
|
|
284
|
-
"revision": { "
|
|
317
|
+
"revision": { "files": 91, "digest": "<sha256 of the whole map>",
|
|
318
|
+
"changed": { "<path>": "<sha256 of the verified version>" }, "removed?": ["<path>"] },
|
|
285
319
|
"startupMs": 8123,
|
|
286
320
|
"matchesAgent": true,
|
|
287
321
|
"matchesDisk": true,
|
|
@@ -292,9 +326,15 @@ The answer of a barrier tool:
|
|
|
292
326
|
}
|
|
293
327
|
```
|
|
294
328
|
|
|
295
|
-
`
|
|
296
|
-
|
|
297
|
-
|
|
329
|
+
`revision` is **compact by default**: the verified map (every open scene, every source the build
|
|
330
|
+
read, the manifest — ~90 entries on a real game) used to open every answer in full. Now `files` is
|
|
331
|
+
its size, `digest` a sha256 over its sorted `path\0hash` lines (equal digests = equal maps), and
|
|
332
|
+
`changed` holds only the entries that are new or different since the **previous answer of this
|
|
333
|
+
`pix3 mcp` process** (the whole map on the first answer; `removed` names paths that dropped out).
|
|
334
|
+
Pass `fullRevision: true` to any barrier or observing tool for the plain `{ "<path>": "<sha256>" }`
|
|
335
|
+
map, or read `editor.playRevision` from `project_status` while the game plays. `startupMs` is
|
|
336
|
+
always present: the milliseconds from the start request until the game was running, or `null`
|
|
337
|
+
when no start happened (the game was already running, so there was nothing to measure). `matchesAgent` is `null` (with `agentExpectations: "none"`) without `expect`; `matchesDisk` is
|
|
298
338
|
whether every verified hash still matched the disk at the final check; `changedDuringRun` is what
|
|
299
339
|
the barrier did not verify: every file changed meanwhile by someone other than the editor, plus
|
|
300
340
|
every verified file whose hash moved between the two checks with no editor write on record
|
|
@@ -310,9 +350,10 @@ lazily.
|
|
|
310
350
|
|
|
311
351
|
**Observing tools** (`play_status`, `game_input`, `game_observe`, `viewport_screenshot`,
|
|
312
352
|
`read_errors`, `read_logs`) neither stop nor sync: `{ revision, stale, result }`, where `revision` is
|
|
313
|
-
what the running game was verified against (`null` when play was not started
|
|
314
|
-
and `stale: true` when the editor saw an external change during play or a
|
|
315
|
-
hash moved since.
|
|
353
|
+
what the running game was verified against (compact, as above; `null` when play was not started
|
|
354
|
+
through the barrier) and `stale: true` when the editor saw an external change during play or a
|
|
355
|
+
`revision` path's disk hash moved since. `read_errors` takes `since` (epoch ms) like `read_logs`;
|
|
356
|
+
every entry carries its `at`. Other tools (`project_status`, `play_stop`, `get_selection`, `generate_*`) answer
|
|
316
357
|
with the editor's result as is. `project_status` without a window answers
|
|
317
358
|
`{ connected: false, server, editor: null, message }` (not an error).
|
|
318
359
|
|
|
@@ -321,7 +362,8 @@ with the editor's result as is. `project_status` without a window answers
|
|
|
321
362
|
| Code | When |
|
|
322
363
|
| --- | --- |
|
|
323
364
|
| `disk_differs_from_agent` | Step 1: the disk does not hold the `expect` versions (`differing[]` with `recovery`, `mergeLog`, `hint`). |
|
|
324
|
-
| `
|
|
365
|
+
| `expectation_stale` | Step 2, at once: the disk moved past an `expect` version after step 1 and the editor already matches the disk (`differing[]` with `diskHash` / `agentHash` / `loadedHash`, plus `mergeLog` / `recovery` / `hint` as in step 1). Somebody wrote a newer version — re-read it, re-run `pix3 check --json`, pass the new hashes. Not editor lag. |
|
|
366
|
+
| `sync_timeout` | Step 2: the editor's loaded hashes did not match the disk within ~5 s (`differing[]` with `loadedHash` / `agentHash` / `diskHash`) — the editor is still catching up with a write. |
|
|
325
367
|
| `load_failed` | Step 2: the loader or the script compiler failed on the current files, or an open scene has no verified version (`errors[]`). At the start: the game was not running within 30 s, or play mode stopped (`result` carries it, with `startupMs`). |
|
|
326
368
|
| `pending_external` | Step 2: a file stays unreadable (partial / invalid write); the editor keeps its last good version. |
|
|
327
369
|
| `no_editor` | No window holds the lease, it did not answer (`reason: "no_reply"`), or it lost the lease mid-call (`reason: "lease_lost"`). |
|
|
@@ -335,10 +377,10 @@ channel off) is passed through as the editor wrote it.
|
|
|
335
377
|
session + lease + `pix3 mcp` process) opens a prompt in the editor naming the process and what it
|
|
336
378
|
calls itself (unverified); the call waits up to 60 s. Allowed → 20 generations, then it asks again.
|
|
337
379
|
In memory only: a reconnect, a new `pix3 serve` run, a lease takeover or a new `pix3 mcp` process
|
|
338
|
-
resets it; the status-bar Agent pill has a revoke button and switches the channel off.
|
|
339
|
-
|
|
340
|
-
### MCP configuration
|
|
341
|
-
|
|
380
|
+
resets it; the status-bar Agent pill has a revoke button and switches the channel off.
|
|
381
|
+
|
|
382
|
+
### MCP configuration
|
|
383
|
+
|
|
342
384
|
`pix3 new` and `pix3 kit` write the project's `.mcp.json` (Claude Code's project format), **pinned
|
|
343
385
|
to the CLI version that wrote it** — never a bare `@pix3/cli`:
|
|
344
386
|
|
|
@@ -356,31 +398,31 @@ best-effort, check `codex mcp --help` of your Codex version.
|
|
|
356
398
|
**Dev mode.** From a checkout of the pix3 repo (the CLI runs from `packages/pix3-cli/src/`), or
|
|
357
399
|
with `PIX3_CLI_DEV=1`, both write `node <repo>/packages/pix3-cli/src/index.ts mcp --workspace`
|
|
358
400
|
instead, so the channel can be tried before that version is on npm. `PIX3_CLI_DEV=0` forces the
|
|
359
|
-
pinned form.
|
|
360
|
-
|
|
361
|
-
## 4. Report honestly
|
|
362
|
-
|
|
363
|
-
A file that passes `check` loads and compiles. It is not a game that works. End the turn
|
|
364
|
-
with:
|
|
365
|
-
|
|
366
|
-
- what you changed (files), and whether `pix3 check` was green;
|
|
367
|
-
- whether you ran it (the barrier answer, and which pattern: `game_run` predicates or
|
|
368
|
-
`game_input` + observed text / log markers) — or **what the human should do to see it**:
|
|
369
|
-
"press Play in `scenes/main.pix3scene`, tap the gold stars — the combo label top-right should
|
|
370
|
-
count x2, x3";
|
|
371
|
-
- what you could not verify (feel, audibility, small shakes, anything the channel cannot see);
|
|
372
|
-
- placeholders you left — art (`ColorRect2D`, tinted PNGs, hand-written SVGs) and sound. A sound
|
|
373
|
-
you made with `pix3 sfx` is a real `.wav`, not a placeholder in the file sense, but say it is a
|
|
374
|
-
synthesised stand-in and that you could not listen to it.
|
|
375
|
-
|
|
376
|
-
If the human reports an error from the editor, ask for the exact text (the editor's console
|
|
377
|
-
or the load error shown on the scene), fix the first one, and check again.
|
|
378
|
-
|
|
379
|
-
## Known gaps
|
|
380
|
-
|
|
381
|
-
- `pix3 check` shows the newest merge-log entries, not only the ones since your last write —
|
|
382
|
-
compare their age with when you wrote.
|
|
383
|
-
- The barrier verifies the open scenes and the built scripts; prefabs and assets the game loads
|
|
384
|
-
lazily are only caught afterwards, in `changedDuringRun`.
|
|
385
|
-
- `.pix3/kit-manifest.json` (how `pix3 kit --update` knows which files are still the kit's) is
|
|
386
|
-
in the gitignored `.pix3/`: on a fresh clone every kit file counts as edited and is skipped.
|
|
401
|
+
pinned form.
|
|
402
|
+
|
|
403
|
+
## 4. Report honestly
|
|
404
|
+
|
|
405
|
+
A file that passes `check` loads and compiles. It is not a game that works. End the turn
|
|
406
|
+
with:
|
|
407
|
+
|
|
408
|
+
- what you changed (files), and whether `pix3 check` was green;
|
|
409
|
+
- whether you ran it (the barrier answer, and which pattern: `game_run` predicates or
|
|
410
|
+
`game_input` + observed text / log markers) — or **what the human should do to see it**:
|
|
411
|
+
"press Play in `scenes/main.pix3scene`, tap the gold stars — the combo label top-right should
|
|
412
|
+
count x2, x3";
|
|
413
|
+
- what you could not verify (feel, audibility, small shakes, anything the channel cannot see);
|
|
414
|
+
- placeholders you left — art (`ColorRect2D`, tinted PNGs, hand-written SVGs) and sound. A sound
|
|
415
|
+
you made with `pix3 sfx` is a real `.wav`, not a placeholder in the file sense, but say it is a
|
|
416
|
+
synthesised stand-in and that you could not listen to it.
|
|
417
|
+
|
|
418
|
+
If the human reports an error from the editor, ask for the exact text (the editor's console
|
|
419
|
+
or the load error shown on the scene), fix the first one, and check again.
|
|
420
|
+
|
|
421
|
+
## Known gaps
|
|
422
|
+
|
|
423
|
+
- `pix3 check` shows the newest merge-log entries, not only the ones since your last write —
|
|
424
|
+
compare their age with when you wrote.
|
|
425
|
+
- The barrier verifies the open scenes and the built scripts; prefabs and assets the game loads
|
|
426
|
+
lazily are only caught afterwards, in `changedDuringRun`.
|
|
427
|
+
- `.pix3/kit-manifest.json` (how `pix3 kit --update` knows which files are still the kit's) is
|
|
428
|
+
in the gitignored `.pix3/`: on a fresh clone every kit file counts as edited and is skipped.
|