Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Digit keys are `Digit0`-`Digit9` or `Numpad0`-`Numpad9` — bare `0`-`9` is reje

Before writing a `--trigger` command that differs from the example, load the skill of the tool you are about to trigger. `--trigger` runs a single uloop subcommand in-process only after the marker's arming is confirmed, so the input cannot land before arming and nothing needs to run in the background. `execute-dynamic-code` is one such command: `--trigger "execute-dynamic-code --code-file <path>"`. A short snippet can go inline because quotes keep whitespace as one argument (`--trigger "execute-dynamic-code --code 'return 1;'"`); the tokenizer does not handle escaped or nested quotes, so snippets that contain quotes or are otherwise complex should use `--code-file` — inline `--code` still works for simple snippets. One race does remain: the marker itself can hit before the trigger executes (for example on a line that runs every frame). Commands that cannot run while PlayMode is paused are then rejected — a hit response carries `TriggerFailed: true` at the top level and a `Warning` explaining that no input reached the game (the triggered command's own response stays in `TriggerResult`), so do not treat such a hit as input-driven; a timeout or expired wait that observed the same rejection also carries `Error.Details.TriggerFailed: true` and a Hint pointing at `Details.TriggerResult`. That safety valve does not apply to `execute-dynamic-code`, which still runs while paused: a pre-hit trigger of it will execute. If the trigger command itself is rejected before it runs — its argument parsing fails (`INVALID_ARGUMENT`) or the command name is unknown (`UNKNOWN_COMMAND`) — the wait is abandoned immediately with a `PAUSE_POINT_TRIGGER_FAILED` error instead of waiting out `--timeout-seconds`: the marker stays armed, a PlayMode resumed by `--resume-play` is paused again, and `Error.NextActions` carries the recovery commands — fix the trigger value and re-run the same command. The hit response additionally carries `TriggerResult` with the triggered command's own response (or `Completed: false` — with the reason in `Error` when the trigger was skipped, or with an `Explanation` when the wait settled before the trigger reported its result). The trigger string cannot name another pause-point wait (`await-pause-point`/`enable-pause-point`) and cannot pass `--project-path` — the enclosing command's project is used. `await-pause-point --id <id> --trigger ...` accepts the same flag for a marker enabled earlier. Both commands also accept `--resume-play` — see [fast-progressing-games.md](fast-progressing-games.md).

When the game reaches the line on its own, omit `--trigger`. Fall back to split steps only when the triggering action is not a single uloop command (several inputs in sequence, an external event). `execute-dynamic-code` — whether `--code-file` or inline `--code` — is one command; do not split the wait just to run it. When you do need split steps: run `enable-pause-point` without `--await` in the foreground (its response returning is the arm confirmation), then start `uloop await-pause-point --id <id>` in the background, then send the inputs. Do not approximate arm-waiting with a fixed sleep after a backgrounded enable.
When the game reaches the line on its own, omit `--trigger`. To block until it hits, wait on the marker with no trigger command at all: `uloop await-pause-point --id <id> --timeout-seconds <n>` (also offered in the enable response's `RecommendedNextAction`) — `--trigger` is optional there. Fall back to split steps only when the triggering action is not a single uloop command (several inputs in sequence, an external event). `execute-dynamic-code` — whether `--code-file` or inline `--code` — is one command; do not split the wait just to run it. When you do need split steps: run `enable-pause-point` without `--await` in the foreground (its response returning is the arm confirmation), then start `uloop await-pause-point --id <id>` in the background, then send the inputs. Do not approximate arm-waiting with a fixed sleep after a backgrounded enable.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

`--timeout-seconds` on enable starts the marker lifetime clock at enable time and is also the deadline `--await` waits against, so size it to cover both the trigger and the wait. Give any agent-shell timeout or yield window more room than `--timeout-seconds`, never the same value: the response prints only when the wait ends, so a wrapper that cuts off at the same boundary reports empty output even though the command completed and printed just past the cutoff. When that happens, do not re-run the enable blindly — read the outcome with `uloop pause-point-status --id <id>`; an expired marker's record stays readable there. Some agent shells cap a foreground call's output window (often around 30 seconds) regardless of any timeout you configure and report the call as completed with empty output; when the wait must exceed that cap, use the split steps above (enable without --await, then await in the background) instead of one long foreground wait.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Expired responses include `MethodEntryCount`: `0` means the armed method was nev

A pause point hits only when control flow reaches the patched line (or the `Pause(id)` call). `simulate-keyboard` returning `PressEdgeObserved=true` means the input edge was observed, not that your target game logic has reached the pause line yet.

If a `simulate-*` command instead returns a failure whose message says PlayMode is paused, suspect a pause point hit rather than an unrelated failure: an active pause point can make PlayMode paused mid-simulation, and the `simulate-*` call surfaces that as a preflight failure. The failure response names the responsible marker in `RejectedByActivePausePointId`. Check `uloop pause-point-status --id <id>` first to confirm the hit before treating it as a bug in the simulated action itself.
If a `simulate-*` command instead returns a failure whose message says PlayMode is paused, suspect a pause point hit rather than an unrelated failure: an active pause point can make PlayMode paused mid-simulation, and the `simulate-*` call surfaces that as a preflight failure. The failure response names the responsible marker in `RejectedByActivePausePointId`, and sets `RejectedBeforeExecution: true` for any pre-execution refusal. A `--trigger` refused that way aborts the wait right away with `PAUSE_POINT_TRIGGER_FAILED` quoting the refusal, instead of waiting the marker out — unless `RejectedByActivePausePointId` names the very marker being awaited, which means that marker was hit before the trigger ran; that wait keeps running, because the hit is its success. Check `uloop pause-point-status --id <id>` first to confirm the hit before treating it as a bug in the simulated action itself.

## Locating Where Control Flow Stops

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Returns JSON with:
- `DeferredLatchSyncScheduled` (boolean): Set `true` on successful `ReleaseAll` / `KeyUp` when a one-shot player-update latch sync was scheduled. Omitted when false. The sync runs on the next Dynamic/Fixed/Manual Input System update (not Editor); while PlayMode is paused it therefore waits until resume. Gameplay polling during that same input update may still see a stale press; polling from `Update` after that input update should see the key up
- `InterruptedByPausePoint` / `PausePointId` / `PausePointHitCount` / `PausePointHits`: Pause-point interruption info (all nullable except the boolean). `PausePointHits` lists every marker hit during this input in hit order; `PausePointId` only names the latest one. See the Pause Point Inspection section in SKILL.md
- `RejectedByActivePausePointId` (string, nullable): Set when an active pause point rejected this call before any input was injected — distinct from `PausePointId`, which reports a marker hit during the call. When set, the action never happened, so do not read `Success` alone
- `RejectedBeforeExecution` (boolean): `true` when the PlayMode preflight refused this call before any input was injected, whatever the reason (PlayMode not running, PlayMode paused, an active pause point). A mid-flight failure leaves it `false`. `await-pause-point --trigger` reads this to abort the wait immediately instead of waiting the marker out
Comment thread
coderabbitai[bot] marked this conversation as resolved.
- `PressDeliveredToGame` (boolean, nullable): Set only when a `Press` / `KeyDown` was interrupted by a pause point. `true`: the Input System applied the press in a gameplay update before the pause, so the game may already have consumed it. Do not retry; re-check the affected state and `pause-point-status`. `false`: the queued edge was discarded before any gameplay update, the game never observed a press, and a retry after resume is safe. `null` for other actions and uninterrupted responses. Distinct from `PressEdgeObserved`, which says whether a gameplay update saw the press edge (`wasPressedThisFrame`) and can still be `false` after apply
- `PressEdgeObserved` (boolean, nullable): For `Press` and `KeyDown`, whether the press edge (`wasPressedThisFrame`) was visible inside a gameplay input update. `false` means the CLI succeeded but gameplay polling most likely missed the edge — verify with a focused log instead of trusting `Success` alone. `null` for every other action (`KeyUp`, `ReleaseAll`) and for timed-out responses; pause-point interruptions still report the observed value. When a single-shot pause point is armed, do not blindly retry on `false`: the input may still have registered late, so check `pause-point-status` for a hit first — a blind retry can consume a re-enabled marker or double-fire the scenario
- `PressHoldExtendedFrames` (integer, nullable): Extra observation frames the key stayed held beyond the normal duration window while waiting for `wasPressedThisFrame`; `null` when the release was not delayed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Digit keys are `Digit0`-`Digit9` or `Numpad0`-`Numpad9` — bare `0`-`9` is reje

Before writing a `--trigger` command that differs from the example, load the skill of the tool you are about to trigger. `--trigger` runs a single uloop subcommand in-process only after the marker's arming is confirmed, so the input cannot land before arming and nothing needs to run in the background. `execute-dynamic-code` is one such command: `--trigger "execute-dynamic-code --code-file <path>"`. A short snippet can go inline because quotes keep whitespace as one argument (`--trigger "execute-dynamic-code --code 'return 1;'"`); the tokenizer does not handle escaped or nested quotes, so snippets that contain quotes or are otherwise complex should use `--code-file` — inline `--code` still works for simple snippets. One race does remain: the marker itself can hit before the trigger executes (for example on a line that runs every frame). Commands that cannot run while PlayMode is paused are then rejected — a hit response carries `TriggerFailed: true` at the top level and a `Warning` explaining that no input reached the game (the triggered command's own response stays in `TriggerResult`), so do not treat such a hit as input-driven; a timeout or expired wait that observed the same rejection also carries `Error.Details.TriggerFailed: true` and a Hint pointing at `Details.TriggerResult`. That safety valve does not apply to `execute-dynamic-code`, which still runs while paused: a pre-hit trigger of it will execute. If the trigger command itself is rejected before it runs — its argument parsing fails (`INVALID_ARGUMENT`) or the command name is unknown (`UNKNOWN_COMMAND`) — the wait is abandoned immediately with a `PAUSE_POINT_TRIGGER_FAILED` error instead of waiting out `--timeout-seconds`: the marker stays armed, a PlayMode resumed by `--resume-play` is paused again, and `Error.NextActions` carries the recovery commands — fix the trigger value and re-run the same command. The hit response additionally carries `TriggerResult` with the triggered command's own response (or `Completed: false` — with the reason in `Error` when the trigger was skipped, or with an `Explanation` when the wait settled before the trigger reported its result). The trigger string cannot name another pause-point wait (`await-pause-point`/`enable-pause-point`) and cannot pass `--project-path` — the enclosing command's project is used. `await-pause-point --id <id> --trigger ...` accepts the same flag for a marker enabled earlier. Both commands also accept `--resume-play` — see [fast-progressing-games.md](fast-progressing-games.md).

When the game reaches the line on its own, omit `--trigger`. Fall back to split steps only when the triggering action is not a single uloop command (several inputs in sequence, an external event). `execute-dynamic-code` — whether `--code-file` or inline `--code` — is one command; do not split the wait just to run it. When you do need split steps: run `enable-pause-point` without `--await` in the foreground (its response returning is the arm confirmation), then start `uloop await-pause-point --id <id>` in the background, then send the inputs. Do not approximate arm-waiting with a fixed sleep after a backgrounded enable.
When the game reaches the line on its own, omit `--trigger`. To block until it hits, wait on the marker with no trigger command at all: `uloop await-pause-point --id <id> --timeout-seconds <n>` (also offered in the enable response's `RecommendedNextAction`) — `--trigger` is optional there. Fall back to split steps only when the triggering action is not a single uloop command (several inputs in sequence, an external event). `execute-dynamic-code` — whether `--code-file` or inline `--code` — is one command; do not split the wait just to run it. When you do need split steps: run `enable-pause-point` without `--await` in the foreground (its response returning is the arm confirmation), then start `uloop await-pause-point --id <id>` in the background, then send the inputs. Do not approximate arm-waiting with a fixed sleep after a backgrounded enable.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

`--timeout-seconds` on enable starts the marker lifetime clock at enable time and is also the deadline `--await` waits against, so size it to cover both the trigger and the wait. Give any agent-shell timeout or yield window more room than `--timeout-seconds`, never the same value: the response prints only when the wait ends, so a wrapper that cuts off at the same boundary reports empty output even though the command completed and printed just past the cutoff. When that happens, do not re-run the enable blindly — read the outcome with `uloop pause-point-status --id <id>`; an expired marker's record stays readable there. Some agent shells cap a foreground call's output window (often around 30 seconds) regardless of any timeout you configure and report the call as completed with empty output; when the wait must exceed that cap, use the split steps above (enable without --await, then await in the background) instead of one long foreground wait.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Expired responses include `MethodEntryCount`: `0` means the armed method was nev

A pause point hits only when control flow reaches the patched line (or the `Pause(id)` call). `simulate-keyboard` returning `PressEdgeObserved=true` means the input edge was observed, not that your target game logic has reached the pause line yet.

If a `simulate-*` command instead returns a failure whose message says PlayMode is paused, suspect a pause point hit rather than an unrelated failure: an active pause point can make PlayMode paused mid-simulation, and the `simulate-*` call surfaces that as a preflight failure. The failure response names the responsible marker in `RejectedByActivePausePointId`. Check `uloop pause-point-status --id <id>` first to confirm the hit before treating it as a bug in the simulated action itself.
If a `simulate-*` command instead returns a failure whose message says PlayMode is paused, suspect a pause point hit rather than an unrelated failure: an active pause point can make PlayMode paused mid-simulation, and the `simulate-*` call surfaces that as a preflight failure. The failure response names the responsible marker in `RejectedByActivePausePointId`, and sets `RejectedBeforeExecution: true` for any pre-execution refusal. A `--trigger` refused that way aborts the wait right away with `PAUSE_POINT_TRIGGER_FAILED` quoting the refusal, instead of waiting the marker out — unless `RejectedByActivePausePointId` names the very marker being awaited, which means that marker was hit before the trigger ran; that wait keeps running, because the hit is its success. Check `uloop pause-point-status --id <id>` first to confirm the hit before treating it as a bug in the simulated action itself.

## Locating Where Control Flow Stops

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Returns JSON with:
- `DeferredLatchSyncScheduled` (boolean): Set `true` on successful `ReleaseAll` / `KeyUp` when a one-shot player-update latch sync was scheduled. Omitted when false. The sync runs on the next Dynamic/Fixed/Manual Input System update (not Editor); while PlayMode is paused it therefore waits until resume. Gameplay polling during that same input update may still see a stale press; polling from `Update` after that input update should see the key up
- `InterruptedByPausePoint` / `PausePointId` / `PausePointHitCount` / `PausePointHits`: Pause-point interruption info (all nullable except the boolean). `PausePointHits` lists every marker hit during this input in hit order; `PausePointId` only names the latest one. See the Pause Point Inspection section in SKILL.md
- `RejectedByActivePausePointId` (string, nullable): Set when an active pause point rejected this call before any input was injected — distinct from `PausePointId`, which reports a marker hit during the call. When set, the action never happened, so do not read `Success` alone
- `RejectedBeforeExecution` (boolean): `true` when the PlayMode preflight refused this call before any input was injected, whatever the reason (PlayMode not running, PlayMode paused, an active pause point). A mid-flight failure leaves it `false`. `await-pause-point --trigger` reads this to abort the wait immediately instead of waiting the marker out
- `PressDeliveredToGame` (boolean, nullable): Set only when a `Press` / `KeyDown` was interrupted by a pause point. `true`: the Input System applied the press in a gameplay update before the pause, so the game may already have consumed it. Do not retry; re-check the affected state and `pause-point-status`. `false`: the queued edge was discarded before any gameplay update, the game never observed a press, and a retry after resume is safe. `null` for other actions and uninterrupted responses. Distinct from `PressEdgeObserved`, which says whether a gameplay update saw the press edge (`wasPressedThisFrame`) and can still be `false` after apply
- `PressEdgeObserved` (boolean, nullable): For `Press` and `KeyDown`, whether the press edge (`wasPressedThisFrame`) was visible inside a gameplay input update. `false` means the CLI succeeded but gameplay polling most likely missed the edge — verify with a focused log instead of trusting `Success` alone. `null` for every other action (`KeyUp`, `ReleaseAll`) and for timed-out responses; pause-point interruptions still report the observed value. When a single-shot pause point is armed, do not blindly retry on `false`: the input may still have registered late, so check `pause-point-status` for a hit first — a blind retry can consume a re-enabled marker or double-fire the scenario
- `PressHoldExtendedFrames` (integer, nullable): Extra observation frames the key stayed held beyond the normal duration window while waiting for `wasPressedThisFrame`; `null` when the release was not delayed
Expand Down
Loading
Loading