Skip to content

Commit 71c6f48

Browse files
author
DavidQ
committed
docs: add BUILD_PR debug / inspector tools bundle
1 parent cc90a7e commit 71c6f48

16 files changed

+1173
-8
lines changed

docs/dev/CODEX_COMMANDS.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,17 @@ MODEL: GPT-5.4
22
REASONING: high
33

44
COMMAND:
5-
Create BUILD_PR_ASSET_PIPELINE_VALIDATION_OUTPUT
5+
Create BUILD_PR_DEBUG_INSPECTOR_TOOLS
66

77
Scope:
8-
- add validation stage
9-
- add export/output stage
10-
- integrate with pipeline
8+
- add state inspector tool
9+
- add replay visualizer tool
10+
- add basic performance profiler
11+
- integrate with tool host
1112

1213
Validation:
1314
- npm run test:launch-smoke -- --tools
14-
- verify assets validate and export
15+
- verify tools launch and inspect data
1516

1617
Output:
17-
<project>/tmp/BUILD_PR_ASSET_PIPELINE_VALIDATION_OUTPUT_delta.zip
18+
<project>/tmp/BUILD_PR_DEBUG_INSPECTOR_TOOLS_delta.zip

docs/dev/COMMIT_COMMENT.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
docs: add BUILD_PR asset pipeline validation & output bundle
1+
docs: add BUILD_PR debug / inspector tools bundle
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# BUILD_PR_DEBUG_INSPECTOR_TOOLS Report
2+
3+
## Scope Outcome
4+
- Added three new debug tools:
5+
- State Inspector
6+
- Replay Visualizer
7+
- Performance Profiler (basic)
8+
- Integrated tools with Tool Host by registering them as active/visible entries in the shared tool registry.
9+
- Kept implementations lightweight and read-only focused for inspection/visualization flows.
10+
11+
## Tool Additions
12+
- **State Inspector**
13+
- Captures runtime snapshot with host context, project manifest storage, toolbox storage keys, and registered boot contracts.
14+
- Supports pasted JSON inspection for arbitrary state payloads.
15+
- **Replay Visualizer**
16+
- Loads replay JSON payloads, normalizes event timelines, supports scrub/play/pause/reset, and renders current frame details.
17+
- Pulls replay state from host context when available.
18+
- **Performance Profiler**
19+
- Runs deterministic workload timing profile (min/max/avg/p95).
20+
- Runs frame cadence sample and reports estimated FPS.
21+
22+
## Host Integration
23+
- Updated `tools/toolRegistry.js` with active tool entries:
24+
- `state-inspector`
25+
- `replay-visualizer`
26+
- `performance-profiler`
27+
- Existing Tool Host manifest/runtime already consume active visible registry entries, so no Tool Host runtime refactor was required.
28+
29+
## Validation
30+
- Required smoke command:
31+
- `npm run test:launch-smoke -- --tools`
32+
- Result: PASS (`12/12` tools)
33+
- Evidence: `docs/dev/reports/launch_smoke_report.md`
34+
- Debug tool inspection validation:
35+
- Verified new tool folders and index entrypoints exist.
36+
- Verified Tool Host manifest includes all three new tool IDs.
37+
- Verified state snapshot/replay timeline/performance summary helper behaviors.
38+
- Result: PASS
39+
- Evidence: `docs/dev/reports/BUILD_PR_DEBUG_INSPECTOR_TOOLS_validation.txt`
40+
41+
## Exact Files Changed
42+
- `docs/pr/BUILD_PR_DEBUG_INSPECTOR_TOOLS.md`
43+
- `docs/dev/reports/debug_inspector_tools_targets.txt`
44+
- `tools/toolRegistry.js`
45+
- `tools/shared/debugInspectorData.js`
46+
- `tools/shared/debugInspectorTools.css`
47+
- `tools/State Inspector/index.html`
48+
- `tools/State Inspector/main.js`
49+
- `tools/Replay Visualizer/index.html`
50+
- `tools/Replay Visualizer/main.js`
51+
- `tools/Performance Profiler/index.html`
52+
- `tools/Performance Profiler/main.js`
53+
- `docs/dev/reports/launch_smoke_report.md`
54+
- `docs/dev/reports/BUILD_PR_DEBUG_INSPECTOR_TOOLS_validation.txt`
55+
- `docs/dev/reports/BUILD_PR_DEBUG_INSPECTOR_TOOLS_report.md`
482 Bytes
Binary file not shown.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
DEBUG / INSPECTOR TOOL TARGETS
2+
3+
- State Inspector
4+
- Replay Visualizer
5+
- Performance Profiler (basic)
6+
7+
Keep:
8+
- minimal UI
9+
- read-only interactions initially

docs/dev/reports/launch_smoke_report.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Launch Smoke Report
22

3-
Generated: 2026-04-12T00:38:22.248Z
3+
Generated: 2026-04-12T00:46:57.178Z
44

55
Filters: games=false, samples=false, tools=true, sampleRange=all
66

@@ -9,8 +9,11 @@ Filters: games=false, samples=false, tools=true, sampleRange=all
99
| PASS | tool | Asset Browser | tools\Asset Browser\index.html | | npm install --prefix ./tmp ws → npm run test:launch-smoke |
1010
| PASS | tool | Palette Browser | tools\Palette Browser\index.html | | npm install --prefix ./tmp ws → npm run test:launch-smoke |
1111
| PASS | tool | Parallax Scene Studio | tools\Parallax Scene Studio\index.html | | npm install --prefix ./tmp ws → npm run test:launch-smoke |
12+
| PASS | tool | Performance Profiler | tools\Performance Profiler\index.html | | npm install --prefix ./tmp ws → npm run test:launch-smoke |
13+
| PASS | tool | Replay Visualizer | tools\Replay Visualizer\index.html | | npm install --prefix ./tmp ws → npm run test:launch-smoke |
1214
| PASS | tool | Sprite Editor | tools\Sprite Editor\index.html | | npm install --prefix ./tmp ws → npm run test:launch-smoke |
1315
| PASS | tool | SpriteEditor_old_keep | tools\SpriteEditor_old_keep\index.html | | npm install --prefix ./tmp ws → npm run test:launch-smoke |
16+
| PASS | tool | State Inspector | tools\State Inspector\index.html | | npm install --prefix ./tmp ws → npm run test:launch-smoke |
1417
| PASS | tool | Tilemap Studio | tools\Tilemap Studio\index.html | | npm install --prefix ./tmp ws → npm run test:launch-smoke |
1518
| PASS | tool | Tool Host | tools\Tool Host\index.html | | npm install --prefix ./tmp ws → npm run test:launch-smoke |
1619
| PASS | tool | Vector Asset Studio | tools\Vector Asset Studio\index.html | | npm install --prefix ./tmp ws → npm run test:launch-smoke |
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# BUILD_PR_DEBUG_INSPECTOR_TOOLS
2+
3+
## Purpose
4+
Introduce initial debug and inspector tools to improve visibility into runtime state, asset flow, and performance.
5+
6+
## Goals
7+
- enable state inspection
8+
- visualize runtime events/replay
9+
- provide basic performance metrics
10+
11+
## Scope
12+
- State Inspector Tool
13+
- Replay Visualizer Tool
14+
- Performance Profiler Tool (baseline)
15+
16+
## Out of Scope
17+
- deep profiling integrations
18+
- full timeline debugging systems
19+
- UI-heavy redesigns
20+
21+
## Strategy
22+
- integrate with existing tool host
23+
- leverage shared project/state systems
24+
- keep tools lightweight and opt-in
25+
26+
## Validation
27+
- npm run test:launch-smoke -- --tools
28+
- tools launch without errors
29+
- basic inspection works
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>Performance Profiler</title>
7+
<link rel="stylesheet" href="../../src/engine/ui/hubCommon.css" />
8+
<link rel="stylesheet" href="../shared/platformShell.css" />
9+
<link rel="stylesheet" href="../shared/debugInspectorTools.css" />
10+
</head>
11+
<body class="tools-platform-tool-page" data-tool-id="performance-profiler" data-tools-platform-page="tool">
12+
<div data-tools-platform-header></div>
13+
<div class="debug-tool-shell app-shell">
14+
<section class="panel debug-tool-panel">
15+
<h2>Performance Profiler</h2>
16+
<p>Run baseline deterministic workload and frame cadence sampling to inspect timing health.</p>
17+
<div class="debug-tool-actions">
18+
<button type="button" id="runWorkloadButton">Run Workload Profile</button>
19+
<button type="button" id="runFrameSampleButton">Run Frame Sample</button>
20+
<button type="button" id="stopProfilerButton">Stop</button>
21+
</div>
22+
<div id="profilerStatusText" class="debug-tool-meta">Profiler ready.</div>
23+
</section>
24+
25+
<section class="debug-tool-grid">
26+
<section class="panel debug-tool-panel">
27+
<h3>Profile Settings</h3>
28+
<label>
29+
Workload iterations
30+
<input id="workloadIterationsInput" type="number" min="10" max="5000" step="10" value="300" />
31+
</label>
32+
<label>
33+
Work size per iteration
34+
<input id="workSizeInput" type="number" min="100" max="100000" step="100" value="3000" />
35+
</label>
36+
<label>
37+
Frame samples
38+
<input id="frameSamplesInput" type="number" min="10" max="600" step="10" value="120" />
39+
</label>
40+
</section>
41+
42+
<section class="panel debug-tool-panel">
43+
<h3>Profile Output</h3>
44+
<pre id="profileOutput" class="debug-tool-readout">Run a workload or frame sample to inspect performance metrics.</pre>
45+
</section>
46+
</section>
47+
</div>
48+
<div data-tools-platform-status></div>
49+
<script type="module" src="../shared/platformShell.js"></script>
50+
<script type="module" src="./main.js"></script>
51+
</body>
52+
</html>

tools/Performance Profiler/main.js

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
import {
2+
runDeterministicWorkloadIteration,
3+
summarizeDurationSamples,
4+
toPrettyJson
5+
} from "../shared/debugInspectorData.js";
6+
import { readToolHostSharedContextFromLocation } from "../shared/toolHostSharedContext.js";
7+
import { registerToolBootContract } from "../shared/toolBootContract.js";
8+
9+
const refs = {
10+
runWorkloadButton: document.getElementById("runWorkloadButton"),
11+
runFrameSampleButton: document.getElementById("runFrameSampleButton"),
12+
stopButton: document.getElementById("stopProfilerButton"),
13+
statusText: document.getElementById("profilerStatusText"),
14+
workloadIterationsInput: document.getElementById("workloadIterationsInput"),
15+
workSizeInput: document.getElementById("workSizeInput"),
16+
frameSamplesInput: document.getElementById("frameSamplesInput"),
17+
output: document.getElementById("profileOutput")
18+
};
19+
20+
const state = {
21+
frameHandle: 0,
22+
frameSampling: false
23+
};
24+
25+
function setStatus(message) {
26+
if (refs.statusText instanceof HTMLElement) {
27+
refs.statusText.textContent = message;
28+
}
29+
}
30+
31+
function readPositiveInt(input, fallback, min, max) {
32+
if (!(input instanceof HTMLInputElement)) {
33+
return fallback;
34+
}
35+
const numeric = Math.trunc(Number(input.value));
36+
if (!Number.isFinite(numeric)) {
37+
return fallback;
38+
}
39+
return Math.max(min, Math.min(max, numeric));
40+
}
41+
42+
function writeOutput(payload) {
43+
if (refs.output instanceof HTMLElement) {
44+
refs.output.textContent = toPrettyJson(payload);
45+
}
46+
}
47+
48+
function runWorkloadProfile() {
49+
const iterations = readPositiveInt(refs.workloadIterationsInput, 300, 10, 5000);
50+
const workSize = readPositiveInt(refs.workSizeInput, 3000, 100, 100000);
51+
const durations = [];
52+
let checksum = 0;
53+
54+
for (let index = 0; index < iterations; index += 1) {
55+
const started = performance.now();
56+
checksum += runDeterministicWorkloadIteration(workSize);
57+
durations.push(performance.now() - started);
58+
}
59+
60+
const summary = summarizeDurationSamples(durations);
61+
writeOutput({
62+
schema: "tools.performance-profiler.workload-profile/1",
63+
iterations,
64+
workSize,
65+
checksum,
66+
summary
67+
});
68+
setStatus(`Workload profile complete. iterations=${iterations}, avg=${summary.avgMs.toFixed(3)}ms, p95=${summary.p95Ms.toFixed(3)}ms.`);
69+
}
70+
71+
function stopFrameSampling() {
72+
if (state.frameHandle) {
73+
cancelAnimationFrame(state.frameHandle);
74+
state.frameHandle = 0;
75+
}
76+
state.frameSampling = false;
77+
}
78+
79+
function runFrameSample() {
80+
stopFrameSampling();
81+
const frameSamples = readPositiveInt(refs.frameSamplesInput, 120, 10, 600);
82+
const frameDurations = [];
83+
let lastTimestamp = 0;
84+
let sampleCount = 0;
85+
state.frameSampling = true;
86+
87+
function tick(timestamp) {
88+
if (!state.frameSampling) {
89+
return;
90+
}
91+
92+
if (lastTimestamp > 0) {
93+
frameDurations.push(Math.max(0, timestamp - lastTimestamp));
94+
sampleCount += 1;
95+
}
96+
lastTimestamp = timestamp;
97+
98+
if (sampleCount >= frameSamples) {
99+
stopFrameSampling();
100+
const summary = summarizeDurationSamples(frameDurations);
101+
const avgFps = summary.avgMs > 0 ? 1000 / summary.avgMs : 0;
102+
writeOutput({
103+
schema: "tools.performance-profiler.frame-sample/1",
104+
frameSamples,
105+
summary,
106+
estimatedFps: avgFps
107+
});
108+
setStatus(`Frame sample complete. samples=${frameSamples}, avg=${summary.avgMs.toFixed(3)}ms, estFps=${avgFps.toFixed(2)}.`);
109+
return;
110+
}
111+
112+
state.frameHandle = requestAnimationFrame(tick);
113+
}
114+
115+
state.frameHandle = requestAnimationFrame(tick);
116+
setStatus(`Frame sampling started (${frameSamples} samples).`);
117+
}
118+
119+
function bindEvents() {
120+
if (refs.runWorkloadButton instanceof HTMLButtonElement) {
121+
refs.runWorkloadButton.addEventListener("click", runWorkloadProfile);
122+
}
123+
if (refs.runFrameSampleButton instanceof HTMLButtonElement) {
124+
refs.runFrameSampleButton.addEventListener("click", runFrameSample);
125+
}
126+
if (refs.stopButton instanceof HTMLButtonElement) {
127+
refs.stopButton.addEventListener("click", () => {
128+
stopFrameSampling();
129+
setStatus("Profiler stopped.");
130+
});
131+
}
132+
}
133+
134+
let initialized = false;
135+
136+
const performanceProfilerApi = {
137+
captureProjectState() {
138+
return {
139+
workloadIterations: readPositiveInt(refs.workloadIterationsInput, 300, 10, 5000),
140+
workSize: readPositiveInt(refs.workSizeInput, 3000, 100, 100000),
141+
frameSamples: readPositiveInt(refs.frameSamplesInput, 120, 10, 600)
142+
};
143+
},
144+
applyProjectState(snapshot) {
145+
if (refs.workloadIterationsInput instanceof HTMLInputElement && Number.isFinite(snapshot?.workloadIterations)) {
146+
refs.workloadIterationsInput.value = String(snapshot.workloadIterations);
147+
}
148+
if (refs.workSizeInput instanceof HTMLInputElement && Number.isFinite(snapshot?.workSize)) {
149+
refs.workSizeInput.value = String(snapshot.workSize);
150+
}
151+
if (refs.frameSamplesInput instanceof HTMLInputElement && Number.isFinite(snapshot?.frameSamples)) {
152+
refs.frameSamplesInput.value = String(snapshot.frameSamples);
153+
}
154+
return true;
155+
}
156+
};
157+
158+
function bootPerformanceProfiler() {
159+
if (!initialized) {
160+
bindEvents();
161+
const hostContext = readToolHostSharedContextFromLocation(window.location);
162+
if (hostContext?.state && typeof hostContext.state === "object") {
163+
performanceProfilerApi.applyProjectState(hostContext.state);
164+
setStatus("Profiler initialized from host context state.");
165+
}
166+
initialized = true;
167+
}
168+
window.performanceProfilerApp = performanceProfilerApi;
169+
return performanceProfilerApi;
170+
}
171+
172+
registerToolBootContract("performance-profiler", {
173+
init: bootPerformanceProfiler,
174+
destroy() {
175+
stopFrameSampling();
176+
return true;
177+
},
178+
getApi() {
179+
return window.performanceProfilerApp || null;
180+
}
181+
});
182+
183+
bootPerformanceProfiler();

0 commit comments

Comments
 (0)