Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5186ab583b |
35
tests/docs/test_the_playground_genome.py
Normal file
35
tests/docs/test_the_playground_genome.py
Normal file
@@ -0,0 +1,35 @@
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def _content() -> str:
|
||||
return Path("the-playground-GENOME.md").read_text()
|
||||
|
||||
|
||||
def test_the_playground_genome_exists() -> None:
|
||||
assert Path("the-playground-GENOME.md").exists()
|
||||
|
||||
|
||||
def test_the_playground_genome_has_required_sections() -> None:
|
||||
content = _content()
|
||||
assert "# GENOME.md — the-playground" in content
|
||||
assert "## Project Overview" in content
|
||||
assert "## Architecture" in content
|
||||
assert "```mermaid" in content
|
||||
assert "## Entry Points" in content
|
||||
assert "## Data Flow" in content
|
||||
assert "## Key Abstractions" in content
|
||||
assert "## API Surface" in content
|
||||
assert "## Test Coverage Gaps" in content
|
||||
assert "## Security Considerations" in content
|
||||
assert "## Dependencies" in content
|
||||
assert "## Deployment" in content
|
||||
assert "## Technical Debt" in content
|
||||
|
||||
|
||||
def test_the_playground_genome_captures_repo_specific_findings() -> None:
|
||||
content = _content()
|
||||
assert "IndexedDB" in content
|
||||
assert "AudioContext" in content
|
||||
assert "smoke-test.html" in content
|
||||
assert "no tests ran" in content
|
||||
assert "innerHTML" in content
|
||||
539
the-playground-GENOME.md
Normal file
539
the-playground-GENOME.md
Normal file
@@ -0,0 +1,539 @@
|
||||
# GENOME.md — the-playground
|
||||
|
||||
Generated: 2026-04-15 00:19:15 EDT
|
||||
Repo: Timmy_Foundation/the-playground
|
||||
Issue: timmy-home #671
|
||||
|
||||
## Project Overview
|
||||
|
||||
The Sovereign Playground is a browser-only creative sandbox: a dark, local-first art toy with an entrance ritual, a canvas in the center, a sound panel on the left, a gallery on the right, and a footer action bar for save/download/clear/fullscreen.
|
||||
|
||||
The current codebase is much smaller than the README vision. The README describes a platform with Sound Studio, Visual Forge, Gallery, Games Floor, Video Forge, and a long roadmap of immersive experiences. The code on `main` today implements a solid prototype shell with:
|
||||
- a cinematic entrance screen
|
||||
- two actual canvas modes: `free-draw` and `ambient`
|
||||
- a basic Web Audio engine for notes/chords/scales
|
||||
- a basic Canvas 2D visual engine
|
||||
- an IndexedDB-backed gallery
|
||||
- a manual browser smoke harness
|
||||
|
||||
Quick measured facts from the fresh main clone I analyzed:
|
||||
- 10 JavaScript source files
|
||||
- 1 CSS design system file
|
||||
- 2 HTML entry pages (`index.html`, `smoke-test.html`)
|
||||
- 0 package manifests
|
||||
- 0 build steps
|
||||
- `python3 -m pytest -q` -> `no tests ran in 0.02s`
|
||||
- browser smoke harness shows 18 passing checks, but the summary is broken and still says `0 passed, 0 failed`
|
||||
|
||||
This repo is best understood as a browser-native prototype platform shell with one strong design language and three real cores:
|
||||
1. orchestration in `src/playground.js`
|
||||
2. browser engines (`PlaygroundAudio`, `PlaygroundVisual`, `PlaygroundGallery`)
|
||||
3. thin shared globals (`PlaygroundUtils`, `PlaygroundState`, `PlaygroundEvents`, `ModeManager`)
|
||||
|
||||
## Architecture
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
HTML[index.html] --> CSS[src/styles/design-system.css]
|
||||
HTML --> U[src/utils/utils.js]
|
||||
HTML --> S[src/utils/state.js]
|
||||
HTML --> E[src/utils/events.js]
|
||||
HTML --> A[src/engine/audio-engine.js]
|
||||
HTML --> V[src/engine/visual-engine.js]
|
||||
HTML --> G[src/gallery/gallery.js]
|
||||
HTML --> SP[src/panels/sound/sound-panel.js]
|
||||
HTML --> GP[src/panels/gallery/gallery-panel.js]
|
||||
HTML --> M[src/modes/mode-manager.js]
|
||||
HTML --> P[src/playground.js]
|
||||
|
||||
P --> A
|
||||
P --> V
|
||||
P --> G
|
||||
P --> SP
|
||||
P --> GP
|
||||
P --> M
|
||||
P --> S
|
||||
P --> E
|
||||
P --> U
|
||||
|
||||
User[User interactions] --> P
|
||||
P --> Canvas[Canvas 2D]
|
||||
P --> Audio[AudioContext]
|
||||
P --> DB[IndexedDB playground-gallery]
|
||||
DB --> GP
|
||||
SP --> A
|
||||
M --> Canvas
|
||||
Smoke[smoke-test.html] --> U
|
||||
Smoke --> S
|
||||
Smoke --> E
|
||||
Smoke --> A
|
||||
Smoke --> V
|
||||
Smoke --> G
|
||||
```
|
||||
|
||||
## Entry Points
|
||||
|
||||
### `index.html`
|
||||
The real application shell.
|
||||
- loads `src/styles/design-system.css`
|
||||
- renders the entrance curtain, header, panels, canvas, action bar, and toast container
|
||||
- loads 10 classic `<script>` files in a strict dependency order
|
||||
- has no framework, bundler, or module loader
|
||||
|
||||
Script order is the runtime contract:
|
||||
1. `src/utils/utils.js`
|
||||
2. `src/utils/state.js`
|
||||
3. `src/utils/events.js`
|
||||
4. `src/engine/audio-engine.js`
|
||||
5. `src/engine/visual-engine.js`
|
||||
6. `src/gallery/gallery.js`
|
||||
7. `src/panels/sound/sound-panel.js`
|
||||
8. `src/panels/gallery/gallery-panel.js`
|
||||
9. `src/modes/mode-manager.js`
|
||||
10. `src/playground.js`
|
||||
|
||||
Because everything is loaded as globals, this order matters. `src/playground.js` assumes the prior globals already exist.
|
||||
|
||||
### `src/playground.js`
|
||||
The orchestration nucleus.
|
||||
Responsibilities:
|
||||
- entrance particle animation
|
||||
- enter transition
|
||||
- engine construction and initialization
|
||||
- canvas sizing
|
||||
- gallery boot
|
||||
- sound panel boot
|
||||
- ambient particle loop
|
||||
- mode registration
|
||||
- save/download/clear/fullscreen button wiring
|
||||
- panel toggle wiring
|
||||
- keyboard shortcut wiring
|
||||
|
||||
If you want to know what the product actually does today, this is the file.
|
||||
|
||||
### `smoke-test.html`
|
||||
The only real automated harness shipped in the target repo.
|
||||
- dynamically loads a subset of source files
|
||||
- performs 18 browser assertions around utils/state/events/audio/visual/gallery
|
||||
- writes green/red lines into the DOM
|
||||
- currently has a broken summary counter
|
||||
|
||||
### Engine modules
|
||||
- `src/engine/audio-engine.js`
|
||||
- Web Audio wrapper for notes, chords, scales, note playback, and chord playback
|
||||
- `src/engine/visual-engine.js`
|
||||
- Canvas wrapper for resize, clear, line/circle drawing, seeded palette generation, and placeholder noise
|
||||
- `src/gallery/gallery.js`
|
||||
- IndexedDB persistence layer
|
||||
|
||||
### Panel / mode modules
|
||||
- `src/panels/sound/sound-panel.js`
|
||||
- renders sound controls and quick-play chord UI
|
||||
- `src/panels/gallery/gallery-panel.js`
|
||||
- renders gallery thumbnails and empty state
|
||||
- `src/modes/mode-manager.js`
|
||||
- registry/switcher for canvas modes
|
||||
|
||||
## Data Flow
|
||||
|
||||
### App boot flow
|
||||
1. Browser opens `index.html`.
|
||||
2. CSS design system establishes the entire visual identity.
|
||||
3. Utility/state/event globals load.
|
||||
4. Audio, visual, gallery, panel, and mode globals load.
|
||||
5. `src/playground.js` runs immediately in an IIFE.
|
||||
6. The entrance screen appears with animated gold particles.
|
||||
7. User clicks `Enter` or presses any key.
|
||||
8. `enterPlayground()`:
|
||||
- fades the entrance out
|
||||
- creates and initializes `PlaygroundAudio`
|
||||
- reveals the playground
|
||||
- calls `initPlayground()`
|
||||
- plays a welcome chord
|
||||
|
||||
### Main interaction flow
|
||||
1. `initPlayground()` creates `PlaygroundVisual(canvas)`.
|
||||
2. Canvas is resized to the container.
|
||||
3. `PlaygroundGallery` opens IndexedDB and initializes the gallery panel.
|
||||
4. `SoundPanel.init(audioEngine)` renders the left control surface.
|
||||
5. `ModeManager.register()` adds two modes:
|
||||
- `free-draw`
|
||||
- `ambient`
|
||||
6. `ModeManager.renderSelector()` creates mode buttons.
|
||||
7. `ModeManager.switch('ambient')` makes the experience feel alive on load.
|
||||
|
||||
### Draw mode flow
|
||||
1. User switches to `Draw`.
|
||||
2. `free-draw.init()` binds mouse and touch listeners.
|
||||
3. Pointer movement draws lines on the canvas via `visualEngine.drawLine()`.
|
||||
4. X-position is mapped to frequency with `PlaygroundUtils.map()`.
|
||||
5. `audioEngine.play()` emits short sine notes while drawing.
|
||||
6. The first interaction hides the “Click anywhere to begin” prompt.
|
||||
|
||||
### Save/export flow
|
||||
1. User clicks `Save`.
|
||||
2. Canvas is converted to PNG via `canvas.toBlob()`.
|
||||
3. `FileReader` converts the blob to a data URL.
|
||||
4. `galleryEngine.save()` writes an object into IndexedDB with:
|
||||
- `id`
|
||||
- `created`
|
||||
- `modified`
|
||||
- `type`
|
||||
- `name`
|
||||
- `data`
|
||||
- `mimeType`
|
||||
- `thumbnail`
|
||||
- `metadata.mode`
|
||||
5. `gallery:item-saved` fires on the event bus.
|
||||
6. `GalleryPanel` rerenders.
|
||||
|
||||
### Gallery render flow
|
||||
1. `GalleryPanel.render()` calls `gallery.getAll()`.
|
||||
2. Results are sorted newest-first by ISO timestamp.
|
||||
3. Gallery HTML is rebuilt via `innerHTML`.
|
||||
4. Clicking a thumb currently only shows a toast with the item id prefix.
|
||||
- there is no real open/view/edit flow yet
|
||||
|
||||
### Download flow
|
||||
1. User clicks `Download`.
|
||||
2. Canvas blob is created.
|
||||
3. `PlaygroundUtils.downloadBlob()` synthesizes an `<a download>` link.
|
||||
4. Browser downloads a PNG snapshot.
|
||||
|
||||
## Key Abstractions
|
||||
|
||||
### `PlaygroundUtils`
|
||||
A tiny global helpers object.
|
||||
Important methods:
|
||||
- `uuid()` -> `crypto.randomUUID()`
|
||||
- `clamp()`
|
||||
- `lerp()`
|
||||
- `map()`
|
||||
- `toast()`
|
||||
- `downloadBlob()`
|
||||
|
||||
It is intentionally small, but it is depended on by multiple subsystems.
|
||||
|
||||
### `PlaygroundState`
|
||||
A global mutable state container with sections for:
|
||||
- `canvas`
|
||||
- `audio`
|
||||
- `gallery`
|
||||
- `ui`
|
||||
- `recording`
|
||||
|
||||
It behaves more like a convenience registry than a true source-of-truth store. Real durable gallery data lives in IndexedDB, not here.
|
||||
|
||||
### `PlaygroundEvents`
|
||||
A minimal event bus:
|
||||
- `on(event, fn)`
|
||||
- `emit(event, data)`
|
||||
- `off(event, fn)`
|
||||
|
||||
This is the main loose-coupling seam across modules.
|
||||
|
||||
### `PlaygroundAudio`
|
||||
A lightweight music engine over `AudioContext`.
|
||||
Capabilities:
|
||||
- note-name to frequency conversion
|
||||
- chord construction
|
||||
- scale construction
|
||||
- one-shot oscillator playback
|
||||
- chord playback
|
||||
- analyser wiring for future visualization/reactivity
|
||||
|
||||
### `PlaygroundVisual`
|
||||
A minimal canvas wrapper.
|
||||
Capabilities:
|
||||
- resize canvas and bind context into `PlaygroundState`
|
||||
- clear canvas
|
||||
- draw lines and circles
|
||||
- deterministic palette generation from a seed
|
||||
- placeholder pseudo-noise function (`perlin2d`, not real Perlin)
|
||||
|
||||
### `PlaygroundGallery`
|
||||
A thin IndexedDB repository.
|
||||
Contract:
|
||||
- DB name: `playground-gallery`
|
||||
- store: `items`
|
||||
- indexes: `type`, `collection`, `created`
|
||||
- CRUD methods:
|
||||
- `init()`
|
||||
- `save(item)`
|
||||
- `getById(id)`
|
||||
- `getAll()`
|
||||
- `deleteItem(id)`
|
||||
|
||||
### `ModeManager`
|
||||
A registry + switcher for canvas modes.
|
||||
It holds:
|
||||
- `modes`
|
||||
- `current`
|
||||
- `register()`
|
||||
- `switch()`
|
||||
- `renderSelector()`
|
||||
|
||||
This is the intended extension point for future experiences.
|
||||
|
||||
### `SoundPanel` and `GalleryPanel`
|
||||
These are rendering adapters that convert state/engine methods into DOM UI.
|
||||
They keep the app readable by not putting every DOM template inside `src/playground.js`.
|
||||
|
||||
## API Surface
|
||||
|
||||
This repo has no network API. Its API surface is an in-browser global surface.
|
||||
|
||||
### Browser globals exposed by load order
|
||||
- `PlaygroundUtils`
|
||||
- `PlaygroundState`
|
||||
- `PlaygroundEvents`
|
||||
- `PlaygroundAudio`
|
||||
- `PlaygroundVisual`
|
||||
- `PlaygroundGallery`
|
||||
- `SoundPanel`
|
||||
- `GalleryPanel`
|
||||
- `ModeManager`
|
||||
|
||||
### Event bus contract
|
||||
Observed event names:
|
||||
- `audio:note-played`
|
||||
- `audio:chord-played`
|
||||
- `gallery:item-saved`
|
||||
- `gallery:item-deleted`
|
||||
- `canvas:mode-changed`
|
||||
- `playground:ready`
|
||||
|
||||
### IndexedDB object contract
|
||||
Saved gallery items can contain:
|
||||
- `id`
|
||||
- `created`
|
||||
- `modified`
|
||||
- `type`
|
||||
- `name`
|
||||
- `data`
|
||||
- `mimeType`
|
||||
- `thumbnail`
|
||||
- `metadata`
|
||||
|
||||
### UI control contract
|
||||
Important DOM ids and commands:
|
||||
- `btn-save`
|
||||
- `btn-download`
|
||||
- `btn-clear`
|
||||
- `btn-fullscreen`
|
||||
- `mode-selector`
|
||||
- `sound-content`
|
||||
- `gallery-content`
|
||||
- `playground-canvas`
|
||||
|
||||
Keyboard shortcuts implemented today:
|
||||
- `Ctrl+S` -> Save
|
||||
- `Ctrl+D` -> Download
|
||||
- `F11` -> Fullscreen
|
||||
- `Escape` -> exit fullscreen
|
||||
|
||||
## Test Coverage Gaps
|
||||
|
||||
### Current state
|
||||
What I verified on a fresh clone of `main`:
|
||||
- `find src -name '*.js' -print0 | xargs -0 -n1 node --check` -> passes
|
||||
- `python3 -m pytest -q` -> `no tests ran in 0.02s`
|
||||
- `smoke-test.html` runs 18 browser assertions successfully
|
||||
- but `smoke-test.html` reports `0 passed, 0 failed` in the summary even while showing 18 green checks
|
||||
|
||||
This means the repo has a manual browser smoke harness, but no real automated CI-grade test suite.
|
||||
|
||||
### What is covered by `smoke-test.html`
|
||||
- UUID/clamp/lerp helpers
|
||||
- default state and snapshot
|
||||
- event bus firing
|
||||
- AudioContext construction and music theory helpers
|
||||
- canvas visual primitives and deterministic palette generation
|
||||
- IndexedDB save/getAll/getById/delete flow
|
||||
|
||||
### What is not covered and should be
|
||||
1. `src/playground.js` orchestration
|
||||
- entrance flow
|
||||
- mode registration
|
||||
- action bar wiring
|
||||
- keyboard shortcuts
|
||||
- panel toggles
|
||||
|
||||
2. `ModeManager`
|
||||
- teardown/init switching order
|
||||
- active button state
|
||||
- event emission correctness
|
||||
|
||||
3. `SoundPanel`
|
||||
- BPM slider updates state
|
||||
- quality button activation
|
||||
- chord button actually invokes audio engine
|
||||
- volume slider is rendered but currently unwired
|
||||
|
||||
4. `GalleryPanel`
|
||||
- empty/non-empty rendering
|
||||
- item-count text updates
|
||||
- click behavior
|
||||
- escaping/sanitization of item fields before `innerHTML`
|
||||
|
||||
5. cross-module browser integration
|
||||
- draw mode pointer lifecycle
|
||||
- touch behavior
|
||||
- fullscreen and download wiring
|
||||
- prompt fade-out on first interaction
|
||||
|
||||
### Generated missing tests for critical paths
|
||||
|
||||
#### A. Mode switching contract test
|
||||
A Node+VM or browser test should verify teardown/init ordering and active button state.
|
||||
|
||||
```python
|
||||
# pseudo-test idea
|
||||
# load utils/state/events/mode-manager
|
||||
# register two fake modes with counters
|
||||
# switch twice
|
||||
# assert first teardown ran before second init
|
||||
# assert PlaygroundState.canvas.mode updated
|
||||
```
|
||||
|
||||
#### B. Smoke summary correctness test
|
||||
The current smoke harness is lying about pass/fail totals.
|
||||
|
||||
```python
|
||||
# browser-level assertion
|
||||
# after smoke-test.html finishes,
|
||||
# count the green result rows and compare them to the h2 summary
|
||||
```
|
||||
|
||||
#### C. GalleryPanel XSS regression test
|
||||
`GalleryPanel.render()` builds markup with `innerHTML` from gallery item data.
|
||||
That should be locked down with a test before the panel grows more capable.
|
||||
|
||||
```python
|
||||
# save item with name containing HTML-like content
|
||||
# render gallery
|
||||
# assert rendered text is escaped / inert
|
||||
# assert no unexpected nodes/scripts are created
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Strengths
|
||||
- zero network/API attack surface in the app itself
|
||||
- no dependency tree or third-party script loaders
|
||||
- local-first persistence using IndexedDB instead of remote storage
|
||||
- deterministic, transparent runtime based on classic script tags
|
||||
- reduced-motion CSS support already present
|
||||
|
||||
### Risks and caveats
|
||||
1. `innerHTML` is used in multiple modules.
|
||||
- `ModeManager.renderSelector()` builds buttons with `innerHTML`
|
||||
- `SoundPanel.render()` builds control markup with `innerHTML`
|
||||
- `GalleryPanel.render()` builds gallery thumbnails with `innerHTML`
|
||||
- The first two are fed by trusted in-repo data.
|
||||
- `GalleryPanel.render()` is the risky one because it interpolates gallery item data (`item.name`, `item.thumbnail`) coming back from IndexedDB.
|
||||
|
||||
2. Browser capability assumptions are strong.
|
||||
- `crypto.randomUUID()`
|
||||
- `AudioContext`
|
||||
- `indexedDB`
|
||||
- `canvas.toBlob()`
|
||||
- Fullscreen API
|
||||
- FileReader
|
||||
- all are required for the best path
|
||||
|
||||
3. No storage limits or cleanup policy.
|
||||
- IndexedDB can grow without quotas or cleanup UX inside the app
|
||||
- saved images are stored as data URLs, which can become heavy over time
|
||||
|
||||
4. No CSP/integrity story because the repo assumes direct static hosting or file-open execution.
|
||||
|
||||
## Dependencies
|
||||
|
||||
### Browser/runtime dependencies
|
||||
- Canvas 2D API
|
||||
- Web Audio API / `AudioContext`
|
||||
- IndexedDB
|
||||
- Fullscreen API
|
||||
- Blob / `toBlob`
|
||||
- FileReader
|
||||
- `crypto.randomUUID()`
|
||||
- standard DOM APIs
|
||||
|
||||
### Project/tooling dependencies
|
||||
- none declared
|
||||
- no `package.json`
|
||||
- no `requirements.txt`
|
||||
- no build tooling
|
||||
- no CI workflow files on `main`
|
||||
|
||||
### Verification tools used during analysis
|
||||
- `node --check` for JS syntax verification
|
||||
- browser execution of `smoke-test.html`
|
||||
- `pytest` baseline probe, which confirmed there is no Python test suite in this target repo
|
||||
|
||||
## Deployment
|
||||
|
||||
The deployment model is intentionally trivial.
|
||||
|
||||
How to run it today:
|
||||
- open `index.html` in a browser
|
||||
- or serve the repo as static files from any plain web server
|
||||
|
||||
There is no backend, no API contract, no environment variables, and no deployment automation in the target repo.
|
||||
|
||||
Practical verification flow:
|
||||
1. `find src -name '*.js' -print0 | xargs -0 -n1 node --check`
|
||||
2. open `smoke-test.html`
|
||||
3. open `index.html`
|
||||
4. click `Enter`
|
||||
5. verify:
|
||||
- entrance transition
|
||||
- ambient mode active by default
|
||||
- sound panel playable
|
||||
- save creates a gallery item in IndexedDB
|
||||
- download exports a PNG
|
||||
|
||||
## Technical Debt
|
||||
|
||||
### Highest-priority debt
|
||||
1. README vision vs code reality gap
|
||||
- the README describes a much larger platform than the current implementation
|
||||
- mainline code today is a polished shell plus two real modes
|
||||
|
||||
2. No real automated test suite
|
||||
- `python3 -m pytest -q` returns `no tests ran`
|
||||
- the only harness is `smoke-test.html`
|
||||
- the smoke harness summary is already broken
|
||||
|
||||
3. `GalleryPanel.render()` trusts item data too much
|
||||
- direct `innerHTML` interpolation of stored item fields is a future XSS footgun
|
||||
|
||||
4. Global load-order coupling
|
||||
- every major module assumes previous globals are already loaded
|
||||
- there is no module isolation or dependency enforcement beyond script order
|
||||
|
||||
5. Volume slider is fake right now
|
||||
- `vol-slider` exists in `SoundPanel.render()`
|
||||
- there is no listener wiring it to `audioEngine.masterGain`
|
||||
|
||||
### Meaningful product debt
|
||||
- gallery items do not really open; click only toasts an id prefix
|
||||
- no import/restore/export package flows
|
||||
- no video forge
|
||||
- no games floor
|
||||
- no persistence integration between `PlaygroundState.gallery` and IndexedDB
|
||||
- `mode-label` in the footer exists but is never updated
|
||||
- `canvas-overlay` exists but is unused
|
||||
- `perlin2d()` is explicitly a placeholder, not real Perlin noise
|
||||
- skip-link CSS exists, but no skip link appears in `index.html`
|
||||
|
||||
## Bottom Line
|
||||
|
||||
The Playground is a clean sovereign-web prototype: one HTML shell, one design system, a handful of browser engines, and a strong aesthetic identity. It already proves the interaction model.
|
||||
|
||||
What it does not yet have is the verification, hardening, and feature depth implied by its own vision. The core challenge now is not invention. It is contraction into truth:
|
||||
- make the shipped surface match the promise
|
||||
- turn `smoke-test.html` into real automated coverage
|
||||
- harden `innerHTML` paths
|
||||
- finish the panel/mode/gallery interactions that are still only half-born
|
||||
Reference in New Issue
Block a user