From 8e7f8c5f3b27092784582393691e204f0b954a5e Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Sun, 1 Nov 2026 18:24:35 +0100 Subject: [PATCH 1/2] fix(tooling): build first, then start the app, then list it The notes had agents start `crafter-build -r` in the background and run `crafter-debug ls --wait 60s` straight after. That raced the build: the first agent to follow them ran the plain build's stub, which said "no debug build yet" and exited, since the client `ls --wait` needs is one of the things the debug build compiles, and it polled by hand until the build was done. The race came from the order, so the order changes: build with the flag in the foreground, which ends once the app and the client are built, then start the app with -r in the background, then `ls --wait`, which now only waits for what it is for, the app registering. A launcher that polls for the build instead was tried and dropped: a shell loop that reruns itself each second, and waits out its whole --wait when no build is coming at all. The consumer notes, the client's guide and the README say so, and the stub and the not-yet-built launcher name the step that was skipped. Co-Authored-By: Claude Opus 5.5 --- README.md | 3 ++- implementations/Crafter.Debug.Cli-Client.cpp | 14 ++++++++------ tooling/AGENTS.consumer.md | 3 ++- tooling/interfaces/Crafter.Debug.Build.cppm | 4 ++-- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 5481d6b..247d77d 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,8 @@ Runtime debugging for AI agents, and for people. An app built with Crafter.Debug Breakpoints and memory stay with a debugger; `crafter-debug debugger` prints how to attach one. ```console -$ crafter-build --debug-api -r & # the app's debug build, in the background +$ crafter-build --debug-api # the app's debug build, and the client with it +$ crafter-build --debug-api -r & # the app, in the background $ ./build/crafter-debug ls --wait 60s game id 4f1c09a2b7e3d851 pid 3 (sandbox 48211) up 4s port 41207 $ ./build/crafter-debug call Lobby_Start && ./build/crafter-debug wait --frames 30 diff --git a/implementations/Crafter.Debug.Cli-Client.cpp b/implementations/Crafter.Debug.Cli-Client.cpp index 3cbb467..bc4d041 100644 --- a/implementations/Crafter.Debug.Cli-Client.cpp +++ b/implementations/Crafter.Debug.Cli-Client.cpp @@ -974,13 +974,15 @@ memory are a debugger's job; `crafter-debug debugger` prints how to attach one. ## The loop -1. Build and start the app from its debug build, in the background (it does - not exit): `crafter-build --debug-api -r` (use the project's own flag). -2. `./build/crafter-debug ls --wait 60s` until it is listed. -3. `cmds` and `help ` to see what it offers, `get '*'` for its state. -4. Act and observe: `call`, `set`, `pause --when`, `wait`, `step`, `trace`, +1. Build with the debug flag, in the foreground: `crafter-build --debug-api` + (use the project's own flag). That build compiles this client too. +2. Start the app in the background, since it does not exit: + `crafter-build --debug-api -r`. +3. `./build/crafter-debug ls --wait 60s` until it is listed. +4. `cmds` and `help ` to see what it offers, `get '*'` for its state. +5. Act and observe: `call`, `set`, `pause --when`, `wait`, `step`, `trace`, `log --level warn`, `screenshot` (open the PNG it prints with Read). -5. `quit` when done. Killing the background task may leave the app running +6. `quit` when done. Killing the background task may leave the app running if crafter-build's alias lacks --die-with-parent; `ls` shows leftovers. ## Commands diff --git a/tooling/AGENTS.consumer.md b/tooling/AGENTS.consumer.md index 3d4e544..f44536b 100644 --- a/tooling/AGENTS.consumer.md +++ b/tooling/AGENTS.consumer.md @@ -4,7 +4,8 @@ This app can be driven while it runs: call its commands, read and set its state, ### Driving it -- Start the debug build in the background, since it does not exit: `crafter-build -r`. +- Build with the debug flag first, in the foreground: `crafter-build `. That build also compiles the client `./build/crafter-debug` runs. The first one takes a few minutes. +- Then start the app in the background, since it does not exit: `crafter-build -r`. - From the project directory, use `./build/crafter-debug`: - `ls --wait 60s` until it is listed; `status`, `cmds`, `help `, `get '*'` - `call key=value ...`, `set ` diff --git a/tooling/interfaces/Crafter.Debug.Build.cppm b/tooling/interfaces/Crafter.Debug.Build.cppm index 0c2aee6..00b8b43 100644 --- a/tooling/interfaces/Crafter.Debug.Build.cppm +++ b/tooling/interfaces/Crafter.Debug.Build.cppm @@ -87,7 +87,7 @@ namespace Crafter::Debug { "# project with {0}: runs the crafter-debug client that build compiled.\n" "client={1}\n" "if [ ! -x \"$client\" ]; then\n" - " echo \"crafter-debug: the client is not built yet; build with: crafter-build {0}\" >&2\n" + " echo \"crafter-debug: the client is not built yet; let crafter-build {0} finish first, which builds it with the app\" >&2\n" " exit 2\n" "fi\n", flag, ShellQuote(client.string())); @@ -113,7 +113,7 @@ namespace Crafter::Debug { return std::format( "#!/bin/sh\n" "# Written by Crafter.Debug (Debug::Use): this project has no debug build yet.\n" - "echo \"crafter-debug: no debug build yet; build with: crafter-build {}\" >&2\n" + "echo \"crafter-debug: no debug build yet; build with: crafter-build {0}, then start the app in the background with: crafter-build {0} -r\" >&2\n" "exit 2\n", flag); } From 02f4564442869ecc2e34b8eb5cbb1d114890b4ef Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Sun, 1 Nov 2026 18:24:35 +0100 Subject: [PATCH 2/2] fix(examples): restart ball ids when the scene is cleared World_Reset kept counting, so after one reset the ball dropped from 60 m was ball 10, not the ball 5 the plain build's output and the log from before the reset name. The agent in the first check noticed and worked around it. Clear now restarts ids at 1, so Reset brings back balls 1 to 5. Co-Authored-By: Claude Opus 5.5 --- examples/Basic/implementations/Basic.World.cpp | 1 + examples/Basic/interfaces/Basic.World.cppm | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/Basic/implementations/Basic.World.cpp b/examples/Basic/implementations/Basic.World.cpp index 14459f5..eb27403 100644 --- a/examples/Basic/implementations/Basic.World.cpp +++ b/examples/Basic/implementations/Basic.World.cpp @@ -16,6 +16,7 @@ void Basic::World::Reset() { void Basic::World::Clear() { balls.clear(); + nextId = 1; time = 0; fastest = 0; } diff --git a/examples/Basic/interfaces/Basic.World.cppm b/examples/Basic/interfaces/Basic.World.cppm index 3ec2b06..892f5c7 100644 --- a/examples/Basic/interfaces/Basic.World.cppm +++ b/examples/Basic/interfaces/Basic.World.cppm @@ -43,8 +43,9 @@ export namespace Basic { Physics physics; Floor floor; - // Removes every ball and drops the scene's five. + // Removes every ball and drops the scene's five, as balls 1 to 5. void Reset(); + // Removes every ball; ids start again from 1. void Clear(); // Returns the new ball's id. std::int32_t Spawn(float x, float y, float vy, float radius);