Skip to content

Commit a3b4d1d

Browse files
author
DavidQ
committed
Complete Asset Browser V2 and harden V2 validation with targeted evidence - PR 11.197B.
1 parent d1a658b commit a3b4d1d

6 files changed

Lines changed: 279 additions & 48 deletions

File tree

docs/dev/codex_commands.md

Lines changed: 80 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,84 @@
1-
# Codex Commands — PR_11_196
1+
# Codex Commands — PR_11_197B
22

33
Model: GPT-5.4-codex
44
Reasoning: high
55

6-
## Command
7-
8-
```text
9-
You are implementing PR_11_196 — V2 Runtime Validation + Cleanup Pass.
10-
11-
Follow docs/pr/PR_11_196_V2_RUNTIME_VALIDATION_CLEANUP.md exactly.
12-
13-
Important:
14-
- ChatGPT did not write implementation code in this bundle.
15-
- You write the implementation fixes.
16-
- Keep the PR testable.
17-
- Do not modify schemas, samples, games, Workspace Manager v1, platformShell, or tools/shared/*.
18-
- Do not copy/paste legacy tool code.
19-
- Re-engineer only the V2 runtime structure.
20-
21-
Target tools:
22-
- Palette Manager V2
23-
- SVG Asset Studio V2
24-
- Vector Map Editor V2
25-
- Tilemap Studio V2
26-
- Asset Browser V2
27-
28-
Required implementation behavior:
29-
- HTML owns static layout, CSS links, shared header mount, and module script tags.
30-
- JS owns session read, validation, DOM population, rendering, and empty/error states only.
31-
- No fallback data.
32-
- No default sample data.
33-
- No v1 coupling.
34-
35-
After implementation:
36-
1. Run targeted syntax checks for changed JS files.
37-
2. Run text checks that verify target HTML and JS compliance.
38-
3. Write report to docs/dev/reports/pr_11_196_v2_runtime_validation_cleanup_report.md.
39-
4. Create final ZIP at tmp/PR_11_196.zip with repo-relative structure.
40-
```
6+
## Execute
7+
8+
Use this PR as a Codex implementation task. Do not ask questions. Make the smallest scoped valid change.
9+
10+
### Required Task
11+
Complete Asset Browser V2 as the testable implementation target and bundle it with V2 validation hardening.
12+
13+
### Scope Lock
14+
Only edit files required for:
15+
- `tools/asset-browser-v2/index.html`
16+
- `tools/asset-browser-v2/index.js`
17+
- narrowly scoped V2 validation/test files if needed
18+
- docs/dev/reports evidence
19+
- docs/dev/roadmaps/MASTER_ROADMAP_ENGINE.md status-only marker change if execution-backed
20+
21+
Do not edit schemas, samples, games, Workspace Manager v1, platformShell, or `tools/shared/*`.
22+
23+
### Implementation Rules
24+
- Re-engineer the tool. Do not copy/paste legacy Asset Browser code.
25+
- `index.html` owns static shell, CSS links, header mount, layout, and DOM nodes.
26+
- `index.js` owns behavior only.
27+
- Use `data-tool-id="asset-browser-v2"`.
28+
- Header mount must be `<div id="shared-theme-header"></div>`.
29+
- Use existing `src/engine/theme` and existing accordion styling where applicable.
30+
- Use two menu areas only if needed: `menuTool` and `menuWorkspace`.
31+
- Read session data only, using the established V2 hostContextId/session pattern.
32+
- Do not fetch, guess, synthesize, default, or fallback data.
33+
- Empty state and invalid state must be explicit and actionable.
34+
- No helper classes. Single class only.
35+
- No alias/pass-through variables.
36+
- No abstraction layers.
37+
38+
### Banned Patterns
39+
Reject your own diff if it includes:
40+
- `document.body.innerHTML` for page construction
41+
- `document.head.insertAdjacentHTML` for CSS/style injection
42+
- dynamic header script injection from JS
43+
- `platformShell`
44+
- `assetUsageIntegration`
45+
- `tools/shared/`
46+
- Workspace Manager v1 wiring
47+
- fallback/default/sample data
48+
- copied legacy code blocks
49+
50+
### Validation Commands
51+
Run targeted validation only:
52+
53+
1. Syntax checks for changed JS files.
54+
2. Static checks for V2 shell compliance:
55+
- Asset Browser V2 `index.html` includes `shared-theme-header`.
56+
- Asset Browser V2 `index.js` does not include `document.body.innerHTML`.
57+
- Asset Browser V2 `index.js` does not include `document.head.insertAdjacentHTML`.
58+
- Asset Browser V2 references `asset-browser-v2`.
59+
3. Manual/UAT or lightweight browser validation evidence for:
60+
- direct open empty state
61+
- invalid session state
62+
- valid session state render
63+
- shared header visible
64+
65+
Do not run full samples smoke unless a broad shared sample loader/framework file is changed. If skipped, document why.
66+
67+
### Evidence
68+
Write results to:
69+
70+
`docs/dev/reports/PR_11_197B_v2_asset_browser_validation.md`
71+
72+
Include:
73+
- files changed
74+
- validation commands run
75+
- pass/fail results
76+
- full samples smoke skipped/running decision and reason
77+
- screenshots/log snippets if available
78+
79+
### Final Artifact
80+
Create final ZIP at:
81+
82+
`tmp/PR_11_197B.zip`
83+
84+
The ZIP must preserve repo-relative structure and include implementation changes and evidence.

docs/dev/commit_comment.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Validate and clean up V2 runtime shell separation after HTML-first re-engineer batch - PR 11.196
1+
Complete Asset Browser V2 and harden V2 validation with targeted evidence - PR 11.197B
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# PR_11_197B Expected Evidence - Fulfilled
2+
3+
Execution evidence was written to:
4+
5+
```text
6+
docs/dev/reports/PR_11_197B_v2_asset_browser_validation.md
7+
```
8+
9+
Final ZIP artifact:
10+
11+
```text
12+
tmp/PR_11_197B.zip
13+
```
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# PR_11_197B Asset Browser V2 Validation
2+
3+
## Purpose
4+
Complete Asset Browser V2 as the focused, testable V2 implementation target while preserving the HTML-first/static-shell and session-only runtime architecture.
5+
6+
## Files Changed
7+
- `tools/asset-browser-v2/index.js`
8+
- `docs/dev/reports/PR_11_197B_v2_asset_browser_validation.md`
9+
- `docs/dev/reports/PR_11_197B_expected_evidence.md`
10+
11+
Unchanged but packaged for review context:
12+
- `tools/asset-browser-v2/index.html`
13+
14+
## Implementation Summary
15+
- Kept `tools/asset-browser-v2/index.html` as the static shell owner: CSS links, shared header mount, page layout, menu regions, state containers, and module script tags remain in HTML.
16+
- Kept `tools/asset-browser-v2/index.js` behavior-only: title/tool id setup, session read, validation, DOM population, event binding, dynamic render, empty state, and invalid state.
17+
- Tightened valid session contract to require explicit `assetCatalog.entries[]` fields: `id`, `label`, `kind`, and `path`.
18+
- Removed auto-previewing the first asset as an implicit selection. Valid sessions now render list/count and instruct the user to select an entry to inspect session-backed metadata.
19+
- No legacy Asset Browser implementation code was copied.
20+
- No fallback/default/sample data was introduced.
21+
22+
## Validation Commands Run
23+
24+
```powershell
25+
node --check tools/asset-browser-v2/index.js
26+
```
27+
28+
Result: passed.
29+
30+
Static banned-pattern check:
31+
32+
```powershell
33+
rg -n "document\.body\.innerHTML|document\.head\.insertAdjacentHTML|createElement\(|appendChild\(|platformShell|assetUsageIntegration|tools/shared/|\.\.\/shared|Workspace Manager|fallback|default data|sample data|demo data" -- tools/asset-browser-v2/index.js
34+
```
35+
36+
Result: passed. No matches.
37+
38+
HTML shell marker check verified:
39+
- `id="shared-theme-header"`
40+
- `src="../../src/engine/theme/mount-shared-header.js"`
41+
- `src="./index.js"`
42+
- `data-tool-id="asset-browser-v2"`
43+
- `<title>Asset Browser V2</title>`
44+
45+
Result: passed.
46+
47+
## Lightweight Browser Validation Evidence
48+
A Node VM harness executed `tools/asset-browser-v2/index.js` with minimal browser/session mocks and verified direct empty, invalid session, and valid session states.
49+
50+
Log snippets:
51+
52+
```text
53+
[ASSET_BROWSER_V2_UAT_EMPTY] No hostContextId was provided. Open Asset Browser V2 with a valid Tool V2 session URL.
54+
[ASSET_BROWSER_V2_UAT_INVALID] Asset catalog session data is invalid. Every entry requires id, label, kind, and path.
55+
[ASSET_BROWSER_V2_UAT_VALID] Session Asset Catalog 1 asset
56+
[ASSET_BROWSER_V2_LOGS] [ASSET_BROWSER_V2_ENTRY] | [SESSION_CONTEXT_READ] | [ASSET_BROWSER_V2_CONTRACT_LOADED]
57+
```
58+
59+
Manual/UAT coverage from the harness:
60+
- Direct open empty state: passed.
61+
- Invalid session state: passed.
62+
- Valid session render: passed.
63+
- Shared header visible: passed by static HTML marker check for `#shared-theme-header`.
64+
65+
## Banned Path Check Result
66+
Scoped status check found no changes under:
67+
- schemas
68+
- samples
69+
- games
70+
- `start_of_day/**`
71+
- Workspace Manager v1
72+
- `tools/shared/**`
73+
- `platformShell`
74+
- `assetUsageIntegration`
75+
76+
No schema, sample, game, Workspace Manager v1, platformShell, or tools/shared files were changed.
77+
78+
## Full Samples Smoke Decision
79+
Full samples smoke test skipped.
80+
81+
Reason: PR_11_197B changes only isolated Asset Browser V2 behavior and report evidence. It does not modify shared sample loader/framework code.
82+
83+
## Final ZIP
84+
85+
```text
86+
tmp/PR_11_197B.zip
87+
```
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# PR_11_197B — Asset Browser V2 Completion + V2 Validation Hardening
2+
3+
## Purpose
4+
Bundle the rejected validation-only PR with a concrete testable V2 implementation target.
5+
6+
Codex must complete Asset Browser V2 using the active V2 architecture and add/execute targeted validation evidence for the V2 lane.
7+
8+
## Scope
9+
- Complete/repair `tools/asset-browser-v2/` only as the testable implementation target.
10+
- Add or update targeted validation that verifies V2 tool shell boundaries.
11+
- Keep the validation focused on V2 tools touched by PR_11_188 through PR_11_196.
12+
13+
## Mandatory Architecture
14+
- Tool directory name and display name must end with `V2`.
15+
- `tools/asset-browser-v2/index.html` owns static shell, CSS links, header mount, page layout, and root DOM nodes.
16+
- `tools/asset-browser-v2/index.js` owns behavior only.
17+
- Header mount must be exactly: `<div id="shared-theme-header"></div>`.
18+
- Tool must read session-backed data only.
19+
- Tool must not fetch, guess, synthesize, default, or fallback data.
20+
- Tool must show explicit empty/error states when session data is missing or invalid.
21+
22+
## Banned
23+
- No schema changes.
24+
- No sample changes.
25+
- No game changes.
26+
- No Workspace Manager v1 work.
27+
- No platformShell.
28+
- No `tools/shared/*` imports.
29+
- No assetUsageIntegration.
30+
- No legacy code copy/paste.
31+
- No helper classes.
32+
- No alias/pass-through variables.
33+
- No `document.body.innerHTML` page construction.
34+
- No `document.head.insertAdjacentHTML` CSS injection.
35+
- No dynamic header script injection from JS.
36+
37+
## Testable Change Required
38+
Codex must produce real implementation edits for Asset Browser V2 and run targeted validation.
39+
40+
Minimum acceptance:
41+
- Asset Browser V2 opens directly.
42+
- Direct open with no session shows actionable empty state.
43+
- Session-backed open renders valid asset list from session data.
44+
- Invalid session data shows actionable error state.
45+
- Header renders through shared theme header mount.
46+
- `index.js` does not build the page shell.
47+
48+
## Validation Required
49+
Codex must run targeted validation only, not full sample smoke.
50+
51+
Required checks:
52+
- Syntax check changed JS files.
53+
- Static grep/check that V2 `index.html` contains `shared-theme-header`.
54+
- Static grep/check that V2 `index.js` does not contain `document.body.innerHTML` or `document.head.insertAdjacentHTML`.
55+
- Tool-specific manual/UAT evidence for Asset Browser V2 empty, invalid, and valid session states.
56+
57+
## Full Samples Smoke
58+
Do not run full samples smoke for this PR.
59+
Reason: change is scoped to one V2 tool plus targeted V2 validation. Full samples smoke is too broad and takes about 20 minutes.
60+
61+
## Roadmap
62+
If roadmap status is updated, only change status markers in `docs/dev/roadmaps/MASTER_ROADMAP_ENGINE.md` using `[ ] -> [.]` or `[.] -> [x]`.
63+
Do not rewrite, reflow, delete, or paraphrase roadmap text.
64+
65+
## Deliverables
66+
Codex must create the final repo ZIP at:
67+
68+
`<project folder>/tmp/PR_11_197B.zip`
69+
70+
The final ZIP must include implementation changes, validation evidence, and any report files generated by Codex.

tools/asset-browser-v2/index.js

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
class AssetBrowserV2 {
22
constructor() {
33
console.log("[ASSET_BROWSER_V2_ENTRY]");
4+
this.assets = [];
45
this.start();
56
}
67

78
start() {
89
document.title = "Asset Browser V2";
910
document.body.dataset.toolId = "asset-browser-v2";
11+
this.bindAssetSelection();
1012
this.readSession();
1113
}
1214

@@ -54,37 +56,51 @@ class AssetBrowserV2 {
5456
return;
5557
}
5658
if (assetCatalog.entries.length === 0) {
57-
this.renderEmpty("Asset Browser V2 loaded a valid asset catalog with zero entries.");
59+
this.assets = [];
60+
document.getElementById("assetBrowserV2SessionReadout").textContent = `Session: loaded\nContext: ${new URL(window.location.href).searchParams.get("hostContextId")}\nTool: ${typeof sessionContext.toolId === "string" && sessionContext.toolId.trim() ? sessionContext.toolId.trim() : "not provided"}`;
61+
document.getElementById("assetBrowserV2ContractReadout").textContent = "payloadJson loaded\npayloadJson.assetCatalog valid\nentries[] valid but empty";
62+
document.getElementById("assetBrowserV2WorkspaceReadout").textContent = "Workspace session context was read. Workspace writes are deferred for this isolated V2 entry.";
63+
document.getElementById("assetBrowserV2Title").textContent = assetCatalog.name.trim();
64+
document.getElementById("assetBrowserV2Count").textContent = "0 assets";
65+
document.getElementById("assetBrowserV2State").textContent = "Asset Browser V2 loaded a valid session asset catalog with zero entries. Add explicit entries to assetCatalog.entries[].";
66+
document.getElementById("assetBrowserV2List").innerHTML = "";
67+
document.getElementById("assetBrowserV2Preview").textContent = "";
5868
return;
5969
}
60-
if (assetCatalog.entries.some((entry) => !entry || typeof entry !== "object" || Array.isArray(entry) || typeof entry.id !== "string" || !entry.id.trim() || typeof entry.path !== "string" || !entry.path.trim())) {
61-
this.renderError("Asset catalog session data is invalid. Every entry requires id and path.");
70+
if (assetCatalog.entries.some((entry) => !entry || typeof entry !== "object" || Array.isArray(entry) || typeof entry.id !== "string" || !entry.id.trim() || typeof entry.label !== "string" || !entry.label.trim() || typeof entry.kind !== "string" || !entry.kind.trim() || typeof entry.path !== "string" || !entry.path.trim())) {
71+
this.renderError("Asset catalog session data is invalid. Every entry requires id, label, kind, and path.");
6272
return;
6373
}
74+
this.assets = assetCatalog.entries.map((entry) => ({
75+
id: entry.id.trim(),
76+
label: entry.label.trim(),
77+
kind: entry.kind.trim(),
78+
path: entry.path.trim()
79+
}));
6480
document.getElementById("assetBrowserV2SessionReadout").textContent = `Session: loaded\nContext: ${new URL(window.location.href).searchParams.get("hostContextId")}\nTool: ${typeof sessionContext.toolId === "string" && sessionContext.toolId.trim() ? sessionContext.toolId.trim() : "not provided"}`;
6581
document.getElementById("assetBrowserV2ContractReadout").textContent = "payloadJson loaded\npayloadJson.assetCatalog valid\nentries[] valid";
6682
document.getElementById("assetBrowserV2WorkspaceReadout").textContent = "Workspace session context was read. Workspace writes are deferred for this isolated V2 entry.";
6783
document.getElementById("assetBrowserV2Title").textContent = assetCatalog.name.trim();
68-
document.getElementById("assetBrowserV2Count").textContent = `${assetCatalog.entries.length} asset${assetCatalog.entries.length === 1 ? "" : "s"}`;
84+
document.getElementById("assetBrowserV2Count").textContent = `${this.assets.length} asset${this.assets.length === 1 ? "" : "s"}`;
6985
document.getElementById("assetBrowserV2State").textContent = "Asset Browser V2 loaded the session asset catalog.";
70-
document.getElementById("assetBrowserV2List").innerHTML = assetCatalog.entries.map((entry) => `<button type="button" data-asset-id="${this.escapeHtml(entry.id.trim())}">${this.escapeHtml(entry.id.trim())}<br><span>${this.escapeHtml(entry.path.trim())}</span></button>`).join("");
71-
document.getElementById("assetBrowserV2Preview").textContent = JSON.stringify(assetCatalog.entries[0], null, 2);
72-
this.bindAssetSelection(assetCatalog.entries);
86+
document.getElementById("assetBrowserV2List").innerHTML = this.assets.map((entry) => `<button type="button" data-asset-id="${this.escapeHtml(entry.id)}"><strong>${this.escapeHtml(entry.label)}</strong><br><span>${this.escapeHtml(entry.kind)} | ${this.escapeHtml(entry.path)}</span></button>`).join("");
87+
document.getElementById("assetBrowserV2Preview").textContent = "Select an asset entry to inspect its session-backed metadata.";
7388
}
7489

75-
bindAssetSelection(entries) {
90+
bindAssetSelection() {
7691
document.getElementById("assetBrowserV2List").addEventListener("click", (event) => {
7792
if (!(event.target instanceof Element) || !event.target.closest("[data-asset-id]")) {
7893
return;
7994
}
80-
if (!entries.some((entry) => entry.id === event.target.closest("[data-asset-id]").dataset.assetId)) {
95+
if (!this.assets.some((entry) => entry.id === event.target.closest("[data-asset-id]").dataset.assetId)) {
8196
return;
8297
}
83-
document.getElementById("assetBrowserV2Preview").textContent = JSON.stringify(entries.find((entry) => entry.id === event.target.closest("[data-asset-id]").dataset.assetId), null, 2);
84-
}, { once: false });
98+
document.getElementById("assetBrowserV2Preview").textContent = JSON.stringify(this.assets.find((entry) => entry.id === event.target.closest("[data-asset-id]").dataset.assetId), null, 2);
99+
});
85100
}
86101

87102
renderEmpty(message) {
103+
this.assets = [];
88104
document.getElementById("assetBrowserV2SessionReadout").textContent = "Session: missing";
89105
document.getElementById("assetBrowserV2ContractReadout").textContent = "payloadJson.assetCatalog not loaded";
90106
document.getElementById("assetBrowserV2WorkspaceReadout").textContent = "Workspace session context is not available.";
@@ -96,6 +112,7 @@ class AssetBrowserV2 {
96112
}
97113

98114
renderError(message) {
115+
this.assets = [];
99116
document.getElementById("assetBrowserV2SessionReadout").textContent = "Session: read attempted";
100117
document.getElementById("assetBrowserV2ContractReadout").textContent = "payloadJson.assetCatalog invalid";
101118
document.getElementById("assetBrowserV2WorkspaceReadout").textContent = "Workspace writes are disabled for invalid Asset Browser V2 session data.";

0 commit comments

Comments
 (0)