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
3 changes: 2 additions & 1 deletion .agents/skills/uloop-pause-point/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ Choose the capture mode when enabling a pause point:
- `continuous` pauses Unity on every hit and remains armed.
- `trace` remains armed and records each hit without pausing Unity.
- In every mode, `CapturedVariables` holds the latest hit and `CapturedVariableHistory` holds only strictly older frames, so with a single hit the history is empty (for `single-shot` it always is). When the latest-hit frame is excluded, `CapturedVariableHistoryNote` explains that the latest hit's variables are in `CapturedVariables`.
- Prefer tracing a line that executes conditionally: a line that runs every frame fills the capped history within a fraction of a second and drops everything recorded before it.
- In trace mode, Status "Hit" does not mean Play Mode paused; the response carries a StatusNote saying the marker fired while the game kept running.
- An Expired response carries a RecommendedNextAction: re-enable the pause point with a longer --timeout-seconds (default 30) and trigger the code path again; clearing the expired marker first is not required.
- For an already-hit `continuous` or `trace` marker, `await-pause-point` waits for a **new** hit after the wait starts (`LastHitSequence` advancing). It does not return the stale hit that is already present. Read the current hit with `pause-point-status` instead. If await times out while waiting for that new hit, the error stays `PAUSE_POINT_WAIT_TIMEOUT` and `Details.Hint` tells you to pass `--resume-play` (or resume Play Mode) so another hit can occur. A freshly enabled marker (including `enable-pause-point --await`) has no prior hit, so the first hit satisfies the wait as before.
Expand Down Expand Up @@ -134,7 +135,7 @@ Every hit response embeds `CapturedVariables`: the method's in-scope locals, its
- Collection values (arrays, `List<T>`, dictionaries, plain objects) render as a JSON preview capped at 10 elements by default. When the elements you need sit past that cap (a 10x20 grid, a long list), re-enable with `--max-preview-elements <n>` (1–1000). The value set at enable time also caps the previews in every later `pause-point-status` response for that marker — status has no flag to change it.
- While Unity is still paused, `UloopPausePoint.TryGetCapturedValue("name")` (and `"this"`) returns live captured references for `execute-dynamic-code`; the return is a `(bool Found, object Value)` tuple, and the holder clears on resume. (file:line marker hits only — id-only markers store no capture) These are **live objects in their frame-completed state, not snapshots** — use them only to dig further into objects that are still alive, never to reconstruct what a value was at the paused line.

`CallerFrames`: up to two caller stack frames showing how execution reached the marker, nearest caller first — top-level for the latest hit in `pause-point-status` / `await-pause-point` responses, and on every `CapturedVariableHistory` frame in all hit-carrying responses (`enable-pause-point` / `clear-pause-point` payloads have no top-level capture, so their frames appear in the history only). Always present (empty array when no managed callers were captured — for example when the marker's method is called directly by the engine). Each frame has `Method`; `File` (project-relative, forward slashes) and `Line` are omitted when debug symbols are unavailable. A source path outside `Assets/`, `Packages/`, or `Library/PackageCache/` degrades to a method-only frame. Frame-selection rules: [references/captured-variables.md](references/captured-variables.md).
`CallerFrames`: up to two caller stack frames showing how execution reached the marker, nearest caller first — top-level for the latest hit in `pause-point-status` / `await-pause-point` responses, and on every `CapturedVariableHistory` frame in all hit-carrying responses (`enable-pause-point` / `clear-pause-point` payloads have no top-level capture, so their frames appear in the history only). Always present (empty array when no managed callers were captured — for example when the marker's method is called directly by the engine). Each frame has `Method`; `File` (project-relative, forward slashes) and `Line` are omitted when debug symbols are unavailable. A caller running as a hot-reload-patched body (a Harmony dynamic method) is reported as a method-only frame under its original `Type.Method` name; `File` and `Line` are omitted because a dynamic method carries no debug symbols. A source path outside `Assets/`, `Packages/`, or `Library/PackageCache/` degrades to a method-only frame. Frame-selection rules: [references/captured-variables.md](references/captured-variables.md).

For snapshot timing, preview/truncation caps, Unity-object `Value` semantics, capture-time vs live evidence, `Warning`/`MatchingLogs`, marker freshness, caller frames, and the raw capture API, read [references/captured-variables.md](references/captured-variables.md).

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,9 @@ Use `Generation`, `EnabledAtUtc`, and the hit sequence fields from the hit or st

Each hit records up to two managed caller frames (`CallerFrames`, nearest caller first). `pause-point-status` and `await-pause-point` responses carry them top-level for the latest hit and on every history frame; `enable-pause-point` / `clear-pause-point` responses carry them on history frames only, because those payloads have no top-level capture. The field is always present — an empty array when no managed callers were captured. Selection rules:

- Runtime machinery (`System.*`, `Microsoft.*`, `Mono.*`), patching infrastructure (`HarmonyLib.*`, `MonoMod.*`), and uloop's own frames are skipped. Unity engine and editor frames are kept because an entry point such as `UnityEditor.EditorApplication.update` is itself diagnostic.
- Runtime machinery (`System.*`, `Microsoft.*`, `Mono.*`), patching infrastructure (`HarmonyLib.*`, `MonoMod.*`), and uloop's own frames are skipped — except a Harmony patch body, which is a real application caller and is kept as described below. Unity engine and editor frames are kept because an entry point such as `UnityEditor.EditorApplication.update` is itself diagnostic.
- Async callers are reported by their logical method name (compiler state-machine frames are demangled to `Namespace.Type.Method`).
- Debug symbols (the Debug code-optimization prerequisite pause points already have) control only `File` and `Line`: a frame without symbols keeps its formatted `Method` and omits `File`/`Line`. A caller whose declaring type cannot be resolved — for example a hot-reload-patched body executing as a Harmony dynamic method — is reported by its raw method name instead. A source path outside `Assets/`, `Packages/`, or `Library/PackageCache/` degrades to a method-only frame so the payload never carries a machine path.
- Debug symbols (the Debug code-optimization prerequisite pause points already have) control only `File` and `Line`: a frame without symbols keeps its formatted `Method` and omits `File`/`Line`. A caller running as a hot-reload-patched body (a Harmony dynamic method) is reported as a method-only frame under its original `Type.Method` name; `File` and `Line` are omitted because a dynamic method carries no debug symbols. A source path outside `Assets/`, `Packages/`, or `Library/PackageCache/` degrades to a method-only frame so the payload never carries a machine path.
- The frames are the synchronous call chain at the moment the marker line ran. A marker that resumes after an `await` does not see its original awaiting caller — only dispatch machinery remains, so expect a method-only engine frame such as the Unity synchronization context (or an empty array); the awaiting method itself never appears. The engine-direct case (an `Update` marker) is a plain empty array.

Capturing the frames costs on the order of 0.1 ms per hit, which also bounds the extra trace-mode overhead per recorded hit.
3 changes: 2 additions & 1 deletion .claude/skills/uloop-pause-point/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ Choose the capture mode when enabling a pause point:
- `continuous` pauses Unity on every hit and remains armed.
- `trace` remains armed and records each hit without pausing Unity.
- In every mode, `CapturedVariables` holds the latest hit and `CapturedVariableHistory` holds only strictly older frames, so with a single hit the history is empty (for `single-shot` it always is). When the latest-hit frame is excluded, `CapturedVariableHistoryNote` explains that the latest hit's variables are in `CapturedVariables`.
- Prefer tracing a line that executes conditionally: a line that runs every frame fills the capped history within a fraction of a second and drops everything recorded before it.
- In trace mode, Status "Hit" does not mean Play Mode paused; the response carries a StatusNote saying the marker fired while the game kept running.
- An Expired response carries a RecommendedNextAction: re-enable the pause point with a longer --timeout-seconds (default 30) and trigger the code path again; clearing the expired marker first is not required.
- For an already-hit `continuous` or `trace` marker, `await-pause-point` waits for a **new** hit after the wait starts (`LastHitSequence` advancing). It does not return the stale hit that is already present. Read the current hit with `pause-point-status` instead. If await times out while waiting for that new hit, the error stays `PAUSE_POINT_WAIT_TIMEOUT` and `Details.Hint` tells you to pass `--resume-play` (or resume Play Mode) so another hit can occur. A freshly enabled marker (including `enable-pause-point --await`) has no prior hit, so the first hit satisfies the wait as before.
Expand Down Expand Up @@ -134,7 +135,7 @@ Every hit response embeds `CapturedVariables`: the method's in-scope locals, its
- Collection values (arrays, `List<T>`, dictionaries, plain objects) render as a JSON preview capped at 10 elements by default. When the elements you need sit past that cap (a 10x20 grid, a long list), re-enable with `--max-preview-elements <n>` (1–1000). The value set at enable time also caps the previews in every later `pause-point-status` response for that marker — status has no flag to change it.
- While Unity is still paused, `UloopPausePoint.TryGetCapturedValue("name")` (and `"this"`) returns live captured references for `execute-dynamic-code`; the return is a `(bool Found, object Value)` tuple, and the holder clears on resume. (file:line marker hits only — id-only markers store no capture) These are **live objects in their frame-completed state, not snapshots** — use them only to dig further into objects that are still alive, never to reconstruct what a value was at the paused line.

`CallerFrames`: up to two caller stack frames showing how execution reached the marker, nearest caller first — top-level for the latest hit in `pause-point-status` / `await-pause-point` responses, and on every `CapturedVariableHistory` frame in all hit-carrying responses (`enable-pause-point` / `clear-pause-point` payloads have no top-level capture, so their frames appear in the history only). Always present (empty array when no managed callers were captured — for example when the marker's method is called directly by the engine). Each frame has `Method`; `File` (project-relative, forward slashes) and `Line` are omitted when debug symbols are unavailable. A source path outside `Assets/`, `Packages/`, or `Library/PackageCache/` degrades to a method-only frame. Frame-selection rules: [references/captured-variables.md](references/captured-variables.md).
`CallerFrames`: up to two caller stack frames showing how execution reached the marker, nearest caller first — top-level for the latest hit in `pause-point-status` / `await-pause-point` responses, and on every `CapturedVariableHistory` frame in all hit-carrying responses (`enable-pause-point` / `clear-pause-point` payloads have no top-level capture, so their frames appear in the history only). Always present (empty array when no managed callers were captured — for example when the marker's method is called directly by the engine). Each frame has `Method`; `File` (project-relative, forward slashes) and `Line` are omitted when debug symbols are unavailable. A caller running as a hot-reload-patched body (a Harmony dynamic method) is reported as a method-only frame under its original `Type.Method` name; `File` and `Line` are omitted because a dynamic method carries no debug symbols. A source path outside `Assets/`, `Packages/`, or `Library/PackageCache/` degrades to a method-only frame. Frame-selection rules: [references/captured-variables.md](references/captured-variables.md).

For snapshot timing, preview/truncation caps, Unity-object `Value` semantics, capture-time vs live evidence, `Warning`/`MatchingLogs`, marker freshness, caller frames, and the raw capture API, read [references/captured-variables.md](references/captured-variables.md).

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,9 @@ Use `Generation`, `EnabledAtUtc`, and the hit sequence fields from the hit or st

Each hit records up to two managed caller frames (`CallerFrames`, nearest caller first). `pause-point-status` and `await-pause-point` responses carry them top-level for the latest hit and on every history frame; `enable-pause-point` / `clear-pause-point` responses carry them on history frames only, because those payloads have no top-level capture. The field is always present — an empty array when no managed callers were captured. Selection rules:

- Runtime machinery (`System.*`, `Microsoft.*`, `Mono.*`), patching infrastructure (`HarmonyLib.*`, `MonoMod.*`), and uloop's own frames are skipped. Unity engine and editor frames are kept because an entry point such as `UnityEditor.EditorApplication.update` is itself diagnostic.
- Runtime machinery (`System.*`, `Microsoft.*`, `Mono.*`), patching infrastructure (`HarmonyLib.*`, `MonoMod.*`), and uloop's own frames are skipped — except a Harmony patch body, which is a real application caller and is kept as described below. Unity engine and editor frames are kept because an entry point such as `UnityEditor.EditorApplication.update` is itself diagnostic.
- Async callers are reported by their logical method name (compiler state-machine frames are demangled to `Namespace.Type.Method`).
- Debug symbols (the Debug code-optimization prerequisite pause points already have) control only `File` and `Line`: a frame without symbols keeps its formatted `Method` and omits `File`/`Line`. A caller whose declaring type cannot be resolved — for example a hot-reload-patched body executing as a Harmony dynamic method — is reported by its raw method name instead. A source path outside `Assets/`, `Packages/`, or `Library/PackageCache/` degrades to a method-only frame so the payload never carries a machine path.
- Debug symbols (the Debug code-optimization prerequisite pause points already have) control only `File` and `Line`: a frame without symbols keeps its formatted `Method` and omits `File`/`Line`. A caller running as a hot-reload-patched body (a Harmony dynamic method) is reported as a method-only frame under its original `Type.Method` name; `File` and `Line` are omitted because a dynamic method carries no debug symbols. A source path outside `Assets/`, `Packages/`, or `Library/PackageCache/` degrades to a method-only frame so the payload never carries a machine path.
- The frames are the synchronous call chain at the moment the marker line ran. A marker that resumes after an `await` does not see its original awaiting caller — only dispatch machinery remains, so expect a method-only engine frame such as the Unity synchronization context (or an empty array); the awaiting method itself never appears. The engine-direct case (an `Update` marker) is a plain empty array.

Capturing the frames costs on the order of 0.1 ms per hit, which also bounds the extra trace-mode overhead per recorded hit.
Loading
Loading