mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2026-04-01 21:17:24 +02:00
feat: Implement Story 1.6 - Move Shared Defs Resources to the Dedicated Host
- Added documentation for Story 1.6 outlining the requirements and acceptance criteria for migrating shared defs-backed resources to a dedicated host. - Updated sprint status to reflect the readiness of Story 1.6 for development. - Created epics document detailing the breakdown of the Fantasy-Map-Generator project, including functional and non-functional requirements. - Added implementation readiness report assessing the current state of documentation and readiness for the project. - Introduced end-to-end tests for scene bootstrap to ensure runtime hosts are created and reused correctly.
This commit is contained in:
parent
df18f69516
commit
125403b82f
10 changed files with 1522 additions and 0 deletions
|
|
@ -0,0 +1,99 @@
|
|||
# Story 1.1: Bootstrap Scene Container and Defs Host
|
||||
|
||||
Status: ready-for-dev
|
||||
|
||||
<!-- Note: Validation is optional. Run validate-create-story for quality check before dev-story. -->
|
||||
|
||||
## Story
|
||||
|
||||
As a map maintainer,
|
||||
I want runtime initialization to create a dedicated scene container and defs host,
|
||||
so that split layer surfaces can share resources without depending on one map SVG root.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
1. Given the current runtime starts from one canonical map host, when the layered runtime bootstrap runs, then it creates or reuses a dedicated scene container for layer surfaces and a dedicated defs host outside individual layer surfaces, and the visible map loads without user-facing regressions at startup.
|
||||
2. Given existing code expects the map to initialize once, when the new bootstrap path is enabled, then the scene container and defs host are available through stable runtime references, and initialization does not require changing user-facing layer controls.
|
||||
|
||||
## Tasks / Subtasks
|
||||
|
||||
- [ ] Audit the current bootstrap ownership before changing runtime structure.
|
||||
- [ ] Confirm which DOM nodes already exist in the HTML shell and which are still assumed to be canonical at runtime.
|
||||
- [ ] Inventory the startup and load paths that currently rebind `svg`, `defs`, and `viewbox` globals.
|
||||
- [ ] Create or reuse the layered runtime hosts.
|
||||
- [ ] Establish one scene container under the map host for layer surfaces.
|
||||
- [ ] Establish one dedicated runtime defs host outside individual layer surfaces.
|
||||
- [ ] Preserve the existing WebGL canvas mount so current relief rendering does not regress during the bootstrap change.
|
||||
- [ ] Publish stable references for the new hosts.
|
||||
- [ ] Expose bootstrap references through one runtime owner instead of scattering raw DOM lookups.
|
||||
- [ ] Keep legacy globals working during the transition without making them the new source of truth.
|
||||
- [ ] Update startup and saved-map reload to reuse the new hosts instead of assuming the initial DOM is always present.
|
||||
- [ ] Keep existing layer controls and initial draw order unchanged.
|
||||
- [ ] Ensure load-from-file can rebuild host references after replacing the map SVG.
|
||||
- [ ] Perform manual smoke verification.
|
||||
- [ ] Fresh page load renders normally.
|
||||
- [ ] Loading an existing map still rebinds all required runtime references.
|
||||
- [ ] Relief canvas and fixed UI elements remain correctly mounted.
|
||||
|
||||
## Dev Notes
|
||||
|
||||
### Context Summary
|
||||
|
||||
- The HTML shell already contains a positioned `#map-container`, the main `#map` SVG, and a sibling `#webgl-canvas`. Do not duplicate those elements if they can be reused as the scene bootstrap root.
|
||||
- The current runtime still treats the map SVG as canonical: `public/main.js` binds `svg = d3.select("#map")`, `defs = svg.select("#deftemp")`, and `viewbox = svg.select("#viewbox")` during startup.
|
||||
- The saved-map load path tears out and reinserts the SVG, then rebinds all global layer selections in `public/modules/io/load.js`. Any new scene bootstrap must survive that path.
|
||||
- There is already a second hidden SVG asset host at `#defElements` in the HTML shell. Treat that as a static asset library, not automatically as the new runtime defs host.
|
||||
|
||||
### Technical Requirements
|
||||
|
||||
- Reuse existing DOM nodes when practical. The acceptance criteria require stable runtime references, not a full DOM redesign in this story.
|
||||
- Keep the runtime compatible with the current global-variable model. New TypeScript code must use ambient globals directly, not `window` or `globalThis` wrappers.
|
||||
- New TypeScript modules must follow the project Global Module Pattern: declare global, implement a class, then assign `window.ModuleName = new ModuleClass()`.
|
||||
- Avoid grouped SVG buckets or speculative abstractions here. The goal is bootstrap ownership only.
|
||||
|
||||
### Architecture Compliance
|
||||
|
||||
- This story is the Phase 1 foundation from the layered-map architecture: create one scene container, one defs host, and stable references before any layer split begins.
|
||||
- `svg` and `viewbox` remain compatibility-era globals after this story. They must stop being the architectural source of truth, but they are not removed yet.
|
||||
- The runtime defs host must live outside individual layer surfaces so later split SVG shells can reference shared resources by stable ID.
|
||||
|
||||
### Project Structure Notes
|
||||
|
||||
- Expected touch points:
|
||||
- `src/index.html`
|
||||
- `public/main.js`
|
||||
- `public/modules/io/load.js`
|
||||
- `src/types/global.ts`
|
||||
- Expected new module if needed:
|
||||
- `src/modules/scene.ts` or a similarly narrow runtime bootstrap owner
|
||||
- Do not add a separate helper file unless multiple call sites genuinely need it.
|
||||
- Keep new foundation code in `src/`; do not add new runtime architecture code to `public/modules/`.
|
||||
|
||||
### Testing Notes
|
||||
|
||||
- The architecture document explicitly defers formal test work for this tranche.
|
||||
- Do manual verification for fresh load, saved-map load, and relief visibility.
|
||||
- If a pure helper is extracted while implementing bootstrap ownership, keep it testable, but do not expand scope into a new test suite in this story.
|
||||
|
||||
### References
|
||||
|
||||
- [Source: _bmad-output/planning-artifacts/epics.md, Epic 1, Story 1.1]
|
||||
- [Source: _bmad-output/planning-artifacts/architecture-layered-map-dom-split.md, Core Decisions, Runtime Structure, Migration Plan]
|
||||
- [Source: docs/architecture-globals.md, main.js globals and module loading order]
|
||||
- [Source: src/index.html, `#map-container`, `#map`, `#webgl-canvas`, `#defElements`]
|
||||
- [Source: public/main.js, bootstrap globals and initial layer append order]
|
||||
- [Source: public/modules/io/load.js, saved-map SVG replacement and global rebinding]
|
||||
|
||||
## Dev Agent Record
|
||||
|
||||
### Agent Model Used
|
||||
|
||||
TBD
|
||||
|
||||
### Debug Log References
|
||||
|
||||
### Completion Notes List
|
||||
|
||||
- Story context prepared on 2026-03-13.
|
||||
|
||||
### File List
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
# Story 1.2: Add Scene Module for Shared Camera State
|
||||
|
||||
Status: ready-for-dev
|
||||
|
||||
<!-- Note: Validation is optional. Run validate-create-story for quality check before dev-story. -->
|
||||
|
||||
## Story
|
||||
|
||||
As a map maintainer,
|
||||
I want a Scene module to own shared camera and viewport state,
|
||||
so that all layer surfaces consume one authoritative transform contract.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
1. Given shared globals such as `scale`, `viewX`, `viewY`, `graphWidth`, and `graphHeight` already exist, when the Scene module is introduced, then it exposes a single runtime contract for camera and viewport state, and existing global values remain authoritative and usable during migration.
|
||||
2. Given multiple surfaces will render the same map, when camera state changes, then the Scene module provides one consistent state source for all registered surfaces, and no feature code needs to read transforms from DOM structure directly.
|
||||
|
||||
## Tasks / Subtasks
|
||||
|
||||
- [ ] Create a narrow Scene runtime module.
|
||||
- [ ] Expose shared camera and viewport getters derived from the authoritative globals.
|
||||
- [ ] Expose scene-host references established in Story 1.1 through the same contract.
|
||||
- [ ] Centralize transform ownership.
|
||||
- [ ] Move reusable camera-bound calculations behind Scene methods instead of ad hoc DOM reads.
|
||||
- [ ] Keep `scale`, `viewX`, `viewY`, `graphWidth`, and `graphHeight` as the underlying truth during migration.
|
||||
- [ ] Update runtime consumers that already operate across surfaces.
|
||||
- [ ] Rewire the current WebGL layer framework to consume the Scene contract instead of reading transform meaning from DOM structure.
|
||||
- [ ] Ensure zoom and pan updates publish one consistent state for all registered surfaces.
|
||||
- [ ] Keep compatibility intact.
|
||||
- [ ] Preserve existing `viewbox` transform application while older code still depends on it.
|
||||
- [ ] Do not break callers that still read the existing globals directly.
|
||||
- [ ] Perform manual smoke verification.
|
||||
- [ ] Zoom and pan keep SVG and WebGL content aligned.
|
||||
- [ ] Startup and resize continue to use the correct viewport bounds.
|
||||
|
||||
## Dev Notes
|
||||
|
||||
### Context Summary
|
||||
|
||||
- `public/main.js` owns the current zoom flow: `onZoom` updates `scale`, `viewX`, and `viewY`, then `handleZoom` applies `viewbox.attr("transform", ...)` and triggers `WebGLLayer.rerender()`.
|
||||
- `src/modules/webgl-layer.ts` already has `syncTransform()` logic that computes camera bounds from those globals. That logic is the best initial candidate to move behind a shared Scene contract instead of duplicating it elsewhere.
|
||||
- The architecture requires Scene to own shared transform meaning while keeping the global values authoritative. This story should centralize interpretation, not rename the underlying globals.
|
||||
|
||||
### Technical Requirements
|
||||
|
||||
- Implement the Scene module in `src/` as a TypeScript global module and register it through `src/modules/index.ts`.
|
||||
- Use bare ambient globals declared in `src/types/global.ts`. Do not introduce `window.scale` or `globalThis.viewX` usage.
|
||||
- Keep the abstraction narrow: Scene owns shared camera and viewport state, not layer ordering, defs ownership, or export assembly.
|
||||
- Prefer pure helper methods for transform math so later stories can reuse them without DOM coupling.
|
||||
|
||||
### Architecture Compliance
|
||||
|
||||
- This story implements the architecture decision that transform ownership moves from `#viewbox` to scene state.
|
||||
- `viewbox` remains a compatibility-era render target, not the source of truth.
|
||||
- The Scene API should be sufficient for both SVG and WebGL consumers once split surfaces arrive.
|
||||
|
||||
### Previous Story Intelligence
|
||||
|
||||
- Story 1.1 should leave the runtime with stable scene-host and defs-host references. Reuse those references here instead of letting Scene rediscover DOM nodes on every call.
|
||||
- Do not fold bootstrap ownership and camera ownership into one large module. Story 1.1 is about host setup; this story is about transform semantics.
|
||||
|
||||
### Project Structure Notes
|
||||
|
||||
- Expected touch points:
|
||||
- `src/modules/scene.ts`
|
||||
- `src/modules/index.ts`
|
||||
- `src/modules/webgl-layer.ts`
|
||||
- `public/main.js`
|
||||
- `src/types/global.ts`
|
||||
- Keep any math helpers inside the Scene module unless there are multiple concrete callers that justify extraction.
|
||||
- Keep the scene contract in `src/`; legacy `public/main.js` should consume it, not own a second parallel source of truth.
|
||||
|
||||
### Testing Notes
|
||||
|
||||
- Formal automated test work is out of scope for this tranche.
|
||||
- Keep transform calculation logic pure enough for later coverage.
|
||||
- Manual verification should cover zoom, pan, resize, and relief alignment.
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Build on the runtime references introduced in Story 1.1.
|
||||
|
||||
### References
|
||||
|
||||
- [Source: _bmad-output/planning-artifacts/epics.md, Epic 1, Story 1.2]
|
||||
- [Source: _bmad-output/planning-artifacts/architecture-layered-map-dom-split.md, Decision 3, Runtime Structure, Migration Plan]
|
||||
- [Source: docs/architecture-globals.md, `scale`, `viewX`, `viewY`, `graphWidth`, `graphHeight` globals]
|
||||
- [Source: public/main.js, zoom pipeline and `handleZoom`]
|
||||
- [Source: src/modules/webgl-layer.ts, `syncTransform()` and current camera ownership]
|
||||
|
||||
## Dev Agent Record
|
||||
|
||||
### Agent Model Used
|
||||
|
||||
TBD
|
||||
|
||||
### Debug Log References
|
||||
|
||||
### Completion Notes List
|
||||
|
||||
- Story context prepared on 2026-03-13.
|
||||
|
||||
### File List
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
# Story 1.3: Add Layers Registry as the Ordering Source of Truth
|
||||
|
||||
Status: ready-for-dev
|
||||
|
||||
<!-- Note: Validation is optional. Run validate-create-story for quality check before dev-story. -->
|
||||
|
||||
## Story
|
||||
|
||||
As a map maintainer,
|
||||
I want a centralized Layers registry,
|
||||
so that visibility, order, and surface ownership are managed consistently instead of inferred from DOM position.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
1. Given logical map layers currently rely on DOM placement and ad hoc lookups, when a layer is registered, then the registry stores its `id`, `kind`, `order`, `visible` state, and surface handle, and callers can query layer state without inferring it from DOM order.
|
||||
2. Given runtime layer order changes, when reorder operations are applied through the registry, then all registered layer surfaces receive the updated ordering atomically, and the registry remains the single source of truth for visibility and order.
|
||||
|
||||
## Tasks / Subtasks
|
||||
|
||||
- [ ] Create the Layers registry module.
|
||||
- [ ] Define the minimum record shape: `id`, `kind`, `order`, `visible`, `surface`.
|
||||
- [ ] Expose lookup and mutation APIs that are narrow enough to become the single ordering contract.
|
||||
- [ ] Bootstrap the current layer stack into the registry.
|
||||
- [ ] Register the existing logical SVG layers in their current order.
|
||||
- [ ] Register the current WebGL surface path so mixed rendering already has a place in the model.
|
||||
- [ ] Move order and visibility mutations behind the registry.
|
||||
- [ ] Replace direct DOM-order assumptions in the layer UI reorder path with registry updates.
|
||||
- [ ] Apply visibility changes through the registry without changing user-facing controls.
|
||||
- [ ] Ensure one coordinated apply step updates all affected surfaces together.
|
||||
- [ ] Preserve compatibility for migration-era callers.
|
||||
- [ ] Keep existing layer IDs and toggle IDs stable.
|
||||
- [ ] Avoid forcing feature modules to understand renderer-specific ordering logic.
|
||||
- [ ] Perform manual smoke verification.
|
||||
- [ ] Reordering through the existing Layers UI still changes the visible stack correctly.
|
||||
- [ ] Visibility toggles still map to the correct runtime surface.
|
||||
|
||||
## Dev Notes
|
||||
|
||||
### Context Summary
|
||||
|
||||
- The current reorder path is still DOM-driven. `public/modules/ui/layers.js` maps toggle IDs to concrete DOM groups in `getLayer(id)` and then calls `insertAfter` or `insertBefore` directly in `moveLayer`.
|
||||
- The initial stack order is created in `public/main.js` by appending groups to `viewbox` in sequence. Older-map migration code in `public/modules/dynamic/auto-update.js` also inserts groups relative to concrete siblings such as `#terrain` and `#borders`.
|
||||
- The registry has to become the source of truth without breaking those legacy IDs immediately. This story is about authoritative state ownership, not yet about fully removing all DOM-based insertion helpers.
|
||||
|
||||
### Technical Requirements
|
||||
|
||||
- Implement the registry in `src/` as a global module, likely `src/modules/layers.ts`.
|
||||
- Keep the public contract minimal. Do not add export metadata yet; that belongs to Epic 4.
|
||||
- Store actual surface handles, not just selectors, so later stories can mount independent SVG shells and WebGL surfaces under one contract.
|
||||
- Ensure reorder application is atomic from the user perspective. Avoid partial states where one surface has moved and another has not.
|
||||
|
||||
### Architecture Compliance
|
||||
|
||||
- This story implements Decision 1 from the architecture: the Layers registry is the single source of truth for ordering and visibility.
|
||||
- The runtime must support arbitrary future ordering of SVG and WebGL layers. Do not hard-code separate ordering buckets.
|
||||
- The registry should own state; individual layer modules should not infer state from the DOM.
|
||||
|
||||
### Previous Story Intelligence
|
||||
|
||||
- Story 1.1 introduces stable runtime hosts; the registry should refer to those hosts rather than embedding DOM queries in each layer record.
|
||||
- Story 1.2 centralizes shared transform state; do not mix camera ownership into the registry API.
|
||||
- The current sortable UI already maps toggle IDs to concrete layer IDs in `getLayer(id)`. Preserve those IDs so Story 1.5 can bridge legacy callers cleanly.
|
||||
|
||||
### Project Structure Notes
|
||||
|
||||
- Expected touch points:
|
||||
- `src/modules/layers.ts`
|
||||
- `src/modules/index.ts`
|
||||
- `src/types/global.ts`
|
||||
- `public/modules/ui/layers.js`
|
||||
- `public/main.js`
|
||||
- `public/modules/dynamic/auto-update.js`
|
||||
- Keep migration focused. Do not rewrite every legacy caller in this story.
|
||||
- Keep the registry implementation in `src/` and expose only the smallest legacy-facing hooks needed in `public/modules/ui/layers.js`.
|
||||
|
||||
### Testing Notes
|
||||
|
||||
- Manual verification is sufficient for this tranche.
|
||||
- Verify the existing sortable Layers UI still works and that no layer disappears from the visible stack after a reorder.
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Story 1.1 provides runtime host references.
|
||||
- Story 1.2 provides the shared Scene contract that registry-managed surfaces will consume.
|
||||
|
||||
### References
|
||||
|
||||
- [Source: _bmad-output/planning-artifacts/epics.md, Epic 1, Story 1.3]
|
||||
- [Source: _bmad-output/planning-artifacts/architecture-layered-map-dom-split.md, Decision 1, Decision 2, Migration Plan]
|
||||
- [Source: public/modules/ui/layers.js, `moveLayer` and `getLayer`]
|
||||
- [Source: public/main.js, layer append order]
|
||||
- [Source: public/modules/dynamic/auto-update.js, compatibility inserts relative to existing siblings]
|
||||
|
||||
## Dev Agent Record
|
||||
|
||||
### Agent Model Used
|
||||
|
||||
TBD
|
||||
|
||||
### Debug Log References
|
||||
|
||||
### Completion Notes List
|
||||
|
||||
- Story context prepared on 2026-03-13.
|
||||
|
||||
### File List
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
# Story 1.4: Add Layer Surface Lifecycle Ownership
|
||||
|
||||
Status: ready-for-dev
|
||||
|
||||
<!-- Note: Validation is optional. Run validate-create-story for quality check before dev-story. -->
|
||||
|
||||
## Story
|
||||
|
||||
As a map maintainer,
|
||||
I want a Layer abstraction to own each surface lifecycle,
|
||||
so that individual layers can be created, mounted, updated, and disposed without leaking renderer-specific details.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
1. Given the runtime needs one surface per logical layer, when a layer is instantiated, then the Layer abstraction owns creation, mount, update, and teardown for that surface, and caller code interacts with the layer through a stable contract rather than direct DOM assumptions.
|
||||
2. Given both SVG and WebGL layers will exist, when a surface is registered with the Layer abstraction, then surface lifecycle handling works without exposing renderer-specific branching to feature modules, and layer modules remain responsible only for drawing their own surface.
|
||||
|
||||
## Tasks / Subtasks
|
||||
|
||||
- [ ] Introduce the Layer lifecycle contract.
|
||||
- [ ] Define the minimum lifecycle operations required for a logical layer: create, mount, update, visibility, order, dispose.
|
||||
- [ ] Ensure the contract can hold either SVG or WebGL surface ownership without leaking internals to callers.
|
||||
- [ ] Integrate the lifecycle contract with the Layers registry.
|
||||
- [ ] Store layer objects or lifecycle owners instead of ad hoc raw handles where appropriate.
|
||||
- [ ] Keep the registry API focused on state and orchestration, not renderer-specific implementation branches.
|
||||
- [ ] Adapt current surfaces to the new ownership model.
|
||||
- [ ] Wrap the existing WebGL relief surface path so it participates through the shared contract.
|
||||
- [ ] Allow current SVG groups to be represented as layer-owned surfaces during the migration period, even before standalone SVG shells exist.
|
||||
- [ ] Preserve module boundaries.
|
||||
- [ ] Keep feature renderers responsible for drawing content only.
|
||||
- [ ] Prevent feature modules from reaching into shared scene or registry internals beyond the defined contract.
|
||||
- [ ] Perform manual smoke verification.
|
||||
- [ ] Relief rendering still mounts and clears correctly.
|
||||
- [ ] Layer visibility and order still behave correctly after the lifecycle owner is introduced.
|
||||
|
||||
## Dev Notes
|
||||
|
||||
### Context Summary
|
||||
|
||||
- `src/modules/webgl-layer.ts` already owns one shared Three.js scene and keeps a `Map<string, RegisteredLayer>` of WebGL groups. Reuse that work. Do not create a second parallel WebGL ownership model.
|
||||
- `src/renderers/draw-relief-icons.ts` currently delegates to `TextureAtlasLayer`, which triggers `WebGLLayer.rerender()` directly. That is a concrete example of a feature module that should eventually talk only to its own surface owner.
|
||||
- Current SVG layers are still raw D3 groups under `viewbox`. During Epic 1 the lifecycle abstraction can wrap those existing groups first; standalone SVG shells belong to Epic 2.
|
||||
|
||||
### Technical Requirements
|
||||
|
||||
- Keep the abstraction narrow and named after the architecture: `Layer` or similarly direct terminology.
|
||||
- Avoid generic factories or strategy trees. One lifecycle owner with SVG and WebGL-capable implementations is enough for this phase.
|
||||
- Do not force feature modules to pass renderer flags around. If two surface kinds need separate logic, isolate that inside the lifecycle owner.
|
||||
- Preserve the current `WebGLLayer` canvas and shared context budget.
|
||||
|
||||
### Architecture Compliance
|
||||
|
||||
- This story implements the architecture requirement that each logical layer owns one surface lifecycle and that layer modules own only their own drawing surface.
|
||||
- The abstraction must support incremental migration. Existing SVG groups can be wrapped now; split SVG shells come later.
|
||||
|
||||
### Previous Story Intelligence
|
||||
|
||||
- Story 1.3 should already establish the registry as the single ordering authority. Plug lifecycle ownership into that registry instead of storing a second map of surfaces.
|
||||
- The existing `WebGLLayer` module already manages per-layer groups and a shared renderer. Extend that ownership model rather than replacing it.
|
||||
- Story 1.2 should already own shared camera meaning. The lifecycle contract should consume Scene state, not recreate transform helpers.
|
||||
|
||||
### Project Structure Notes
|
||||
|
||||
- Expected touch points:
|
||||
- `src/modules/layer.ts`
|
||||
- `src/modules/layers.ts`
|
||||
- `src/modules/webgl-layer.ts`
|
||||
- `src/modules/texture-atlas-layer.ts`
|
||||
- `src/renderers/draw-relief-icons.ts`
|
||||
- `src/types/global.ts`
|
||||
- Keep renderer-specific logic close to the existing modules rather than introducing extra indirection files.
|
||||
- Avoid adding separate generic adapter directories or pattern-heavy wrappers for only one WebGL and one SVG path.
|
||||
|
||||
### Testing Notes
|
||||
|
||||
- Manual validation is sufficient.
|
||||
- Focus on mount, redraw, hide/show, and cleanup behavior for relief because that is the current live mixed-render surface.
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Story 1.3 should land first so the lifecycle owner plugs into the registry instead of inventing a second source of truth.
|
||||
|
||||
### References
|
||||
|
||||
- [Source: _bmad-output/planning-artifacts/epics.md, Epic 1, Story 1.4]
|
||||
- [Source: _bmad-output/planning-artifacts/architecture-layered-map-dom-split.md, Decision 2, Decision 4, Clean Code Rules, Migration Plan]
|
||||
- [Source: src/modules/webgl-layer.ts, shared renderer and per-layer group ownership]
|
||||
- [Source: src/renderers/draw-relief-icons.ts, current feature-to-WebGL integration]
|
||||
- [Source: src/modules/texture-atlas-layer.ts, current WebGL rerender trigger]
|
||||
|
||||
## Dev Agent Record
|
||||
|
||||
### Agent Model Used
|
||||
|
||||
TBD
|
||||
|
||||
### Debug Log References
|
||||
|
||||
### Completion Notes List
|
||||
|
||||
- Story context prepared on 2026-03-13.
|
||||
|
||||
### File List
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
# Story 1.5: Add Compatibility Lookups for Legacy Single-SVG Callers
|
||||
|
||||
Status: ready-for-dev
|
||||
|
||||
<!-- Note: Validation is optional. Run validate-create-story for quality check before dev-story. -->
|
||||
|
||||
## Story
|
||||
|
||||
As a map maintainer,
|
||||
I want compatibility helpers for legacy single-SVG access patterns,
|
||||
so that existing workflows keep working while code migrates to the new scene and layer contracts.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
1. Given legacy code still expects `svg`, `viewbox`, or one shared selector scope, when compatibility helpers are introduced, then the runtime provides `getLayerSvg(id)`, `getLayerSurface(id)`, and `queryMap(selector)`, and callers can continue to function during migration without depending on one canonical map SVG.
|
||||
2. Given new code is added after the compatibility layer exists, when it needs layer or scene access, then it can use the new layer and scene contracts directly, and the compatibility layer remains a migration bridge rather than the new source of truth.
|
||||
|
||||
## Tasks / Subtasks
|
||||
|
||||
- [ ] Add the compatibility bridge API.
|
||||
- [ ] Implement `getLayerSvg(id)`, `getLayerSurface(id)`, and `queryMap(selector)` against the new Scene and Layers contracts.
|
||||
- [ ] Expose the helpers as stable globals for legacy callers that cannot move immediately.
|
||||
- [ ] Type and document the bridge.
|
||||
- [ ] Add ambient global declarations for the new helpers.
|
||||
- [ ] Keep the bridge deliberately narrow so it does not become a second permanent architecture.
|
||||
- [ ] Migrate the highest-risk single-root callers touched by Epic 1 foundation work.
|
||||
- [ ] Replace direct whole-map selector assumptions where they would break immediately under split surfaces.
|
||||
- [ ] Preserve unchanged callers until they become relevant, rather than doing a repo-wide cleanup in this story.
|
||||
- [ ] Verify legacy workflows still function.
|
||||
- [ ] Saved-map load and export paths still find required map elements.
|
||||
- [ ] Runtime helpers still let callers reach labels, text paths, and other layer-owned content without assuming one canonical SVG root.
|
||||
|
||||
## Dev Notes
|
||||
|
||||
### Context Summary
|
||||
|
||||
- The codebase has many single-SVG assumptions. Current hotspots include:
|
||||
- `public/main.js` and `public/modules/io/load.js` rebinding `svg`, `defs`, and `viewbox`
|
||||
- `public/modules/io/save.js` and `public/modules/io/export.js` operating on `svg.node()` and `#viewbox`
|
||||
- `src/modules/fonts.ts`, where `getUsedFonts(svg)` queries `#labels` and `#legend` inside a single SVG root
|
||||
- `src/renderers/draw-state-labels.ts`, which appends text paths to `defs > g#deftemp > g#textPaths`
|
||||
- `public/modules/dynamic/auto-update.js`, which uses selectors such as `#viewbox > #routes > g`
|
||||
- This story is a bridge. New code written for the layered runtime should use Scene and Layers directly.
|
||||
|
||||
### Technical Requirements
|
||||
|
||||
- Implement the compatibility API in `src/` so it is typed and globally available to both TypeScript and legacy JS.
|
||||
- Return real layer surfaces or layer-local SVG roots where available. Do not fake a new canonical SVG document.
|
||||
- `queryMap(selector)` should be controlled and predictable. It must not silently reintroduce global DOM coupling as a hidden permanent pattern.
|
||||
- Preserve current IDs and selectors where possible so callers can migrate incrementally.
|
||||
|
||||
### Architecture Compliance
|
||||
|
||||
- This story implements Decision 4 from the architecture: a thin compatibility layer for migration-era callers.
|
||||
- The compatibility API must not become the new source of truth. Scene and Layers remain authoritative.
|
||||
- The goal is operational continuity for legacy workflows while the map DOM is being split.
|
||||
|
||||
### Previous Story Intelligence
|
||||
|
||||
- Story 1.3 should already provide registry-backed layer lookup. Build `getLayerSurface(id)` on that instead of re-querying the DOM.
|
||||
- Story 1.4 should already wrap surfaces in lifecycle ownership. Compatibility helpers should return current owned surfaces, not bypass the lifecycle layer.
|
||||
- Story 1.1 should already provide stable host references after load and reload. Reuse those references for `queryMap(selector)` resolution order.
|
||||
|
||||
### Project Structure Notes
|
||||
|
||||
- Expected touch points:
|
||||
- `src/modules` or `src/utils` compatibility bridge module
|
||||
- `src/types/global.ts`
|
||||
- `src/modules/fonts.ts`
|
||||
- `src/renderers/draw-state-labels.ts`
|
||||
- `public/modules/io/save.js`
|
||||
- `public/modules/io/export.js`
|
||||
- `public/modules/io/load.js`
|
||||
- Only migrate the callers that the new foundation stories would otherwise break.
|
||||
- Keep the bridge in one place in `src/`; do not sprinkle ad hoc fallback selector logic across legacy files.
|
||||
|
||||
### Testing Notes
|
||||
|
||||
- Manual verification is sufficient.
|
||||
- Validate save/export, labels/text paths, and at least one legacy selector-heavy workflow after the helpers are introduced.
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Requires Scene and Layers to exist first so the compatibility helpers can delegate to the real runtime contracts.
|
||||
|
||||
### References
|
||||
|
||||
- [Source: _bmad-output/planning-artifacts/epics.md, Epic 1, Story 1.5]
|
||||
- [Source: _bmad-output/planning-artifacts/architecture-layered-map-dom-split.md, Decision 3, Decision 4]
|
||||
- [Source: public/modules/io/load.js, global rebinding after map replacement]
|
||||
- [Source: public/modules/io/export.js, single-SVG export assumptions]
|
||||
- [Source: public/modules/io/save.js, `getUsedFonts(svg.node())` and `#viewbox` assumptions]
|
||||
- [Source: src/modules/fonts.ts, single-SVG font collection]
|
||||
- [Source: src/renderers/draw-state-labels.ts, defs and text path assumptions]
|
||||
|
||||
## Dev Agent Record
|
||||
|
||||
### Agent Model Used
|
||||
|
||||
TBD
|
||||
|
||||
### Debug Log References
|
||||
|
||||
### Completion Notes List
|
||||
|
||||
- Story context prepared on 2026-03-13.
|
||||
|
||||
### File List
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
# Story 1.6: Move Shared Defs Resources to the Dedicated Host
|
||||
|
||||
Status: ready-for-dev
|
||||
|
||||
<!-- Note: Validation is optional. Run validate-create-story for quality check before dev-story. -->
|
||||
|
||||
## Story
|
||||
|
||||
As a map maintainer,
|
||||
I want shared defs-backed resources to be registered in one dedicated host,
|
||||
so that split surfaces can keep using stable IDs for filters, masks, symbols, markers, patterns, and text paths.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
1. Given runtime resources currently live under one shared SVG structure, when defs registration is migrated, then shared resources are created under the dedicated defs host with stable identifiers, and layer surfaces can reference those resources without duplicating them per layer.
|
||||
2. Given a split surface uses a filter, mask, symbol, marker, pattern, or text path, when the layer renders, then the resource reference resolves from the dedicated defs host, and existing visual behavior remains unchanged for supported runtime features.
|
||||
|
||||
## Tasks / Subtasks
|
||||
|
||||
- [ ] Establish one runtime defs owner.
|
||||
- [ ] Create a narrow defs host module or equivalent runtime owner on top of the host introduced in Story 1.1.
|
||||
- [ ] Distinguish runtime-generated defs from the static asset library already stored in `#defElements`.
|
||||
- [ ] Migrate the current runtime writers for shared defs-backed resources.
|
||||
- [ ] Move feature paths and masks now written through `defs.select(...)` to the dedicated host.
|
||||
- [ ] Move text path registration used by state labels to the dedicated host.
|
||||
- [ ] Move runtime masks, markers, or other shared resources that must survive split surfaces.
|
||||
- [ ] Preserve stable IDs and references.
|
||||
- [ ] Keep existing IDs intact wherever possible so current `url(#id)` and `href="#id"` references continue to resolve.
|
||||
- [ ] Avoid duplicating identical resources into per-layer surfaces.
|
||||
- [ ] Keep export work out of scope for this story.
|
||||
- [ ] Do not redesign the export assembler here.
|
||||
- [ ] Only make the runtime defs placement compatible with later export assembly.
|
||||
- [ ] Perform manual smoke verification.
|
||||
- [ ] Filters, masks, symbols, markers, patterns, and text-path-backed labels still render.
|
||||
- [ ] Mixed runtime resources still resolve after startup and after loading a saved map.
|
||||
|
||||
## Dev Notes
|
||||
|
||||
### Context Summary
|
||||
|
||||
- The current runtime writes shared resources into the main map defs tree. `public/main.js` binds `defs = svg.select("#deftemp")`.
|
||||
- Current high-risk shared defs writers include:
|
||||
- `src/renderers/draw-features.ts` writing `#featurePaths`, `#land`, and `#water`
|
||||
- `src/renderers/draw-state-labels.ts` writing text paths under `defs > g#deftemp > g#textPaths`
|
||||
- `public/modules/dynamic/auto-update.js` appending masks such as `#fog`
|
||||
- `src/modules/emblem/renderer.ts` and export code that rely on stable symbol/pattern IDs
|
||||
- The HTML shell also contains `#defElements`, a hidden SVG used as a static resource library for markers, relief symbols, and similar assets. Do not collapse static asset storage and runtime-generated shared defs into one accidental abstraction.
|
||||
|
||||
### Technical Requirements
|
||||
|
||||
- Keep resource IDs stable. The migration should change ownership, not the externally referenced names.
|
||||
- The dedicated defs host must be reachable by all layer surfaces after the map DOM is split.
|
||||
- Prefer one focused defs owner module over scattered DOM writes.
|
||||
- Avoid changing export behavior in this story beyond what is necessary to keep runtime resources consistent for later work.
|
||||
|
||||
### Architecture Compliance
|
||||
|
||||
- This story implements the architecture decision that runtime shared defs live in a dedicated host separate from layer surfaces.
|
||||
- Shared defs are a first-class requirement for later split layers and export assembly.
|
||||
- Runtime and export are separate pipelines; this story only handles runtime ownership.
|
||||
|
||||
### Previous Story Intelligence
|
||||
|
||||
- Story 1.1 should already establish the dedicated runtime defs host. Reuse that host instead of creating another hidden SVG container.
|
||||
- Story 1.5 should already provide compatibility lookups for legacy defs consumers. Use that bridge for remaining migration-era callers rather than leaving direct `svg.select("#deftemp")` assumptions behind.
|
||||
- Story 1.3 and Story 1.4 should already make surface ownership explicit. Keep defs ownership separate from layer lifecycle ownership.
|
||||
|
||||
### Project Structure Notes
|
||||
|
||||
- Expected touch points:
|
||||
- `src/modules/defs.ts` or equivalent narrow owner
|
||||
- `src/modules/index.ts`
|
||||
- `src/types/global.ts`
|
||||
- `public/main.js`
|
||||
- `src/renderers/draw-features.ts`
|
||||
- `src/renderers/draw-state-labels.ts`
|
||||
- `public/modules/dynamic/auto-update.js`
|
||||
- `public/modules/io/export.js`
|
||||
- Keep changes focused on shared defs ownership. Do not start split-layer rendering in this story.
|
||||
- Keep static resource assets in the existing HTML asset library and move only runtime-generated shared defs into the new owner.
|
||||
|
||||
### Testing Notes
|
||||
|
||||
- Manual validation is sufficient.
|
||||
- Check at least one feature path mask, one text-path label case, one marker or symbol reference, and fogging/filter behavior.
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Story 1.1 must establish the dedicated runtime defs host first.
|
||||
- Story 1.5 should land before or alongside this story if compatibility callers still search the old defs tree.
|
||||
|
||||
### References
|
||||
|
||||
- [Source: _bmad-output/planning-artifacts/epics.md, Epic 1, Story 1.6]
|
||||
- [Source: _bmad-output/planning-artifacts/architecture-layered-map-dom-split.md, Decision 2, Decision 4, Shared Resource Strategy, Migration Plan]
|
||||
- [Source: public/main.js, current `defs = svg.select("#deftemp")` binding]
|
||||
- [Source: src/renderers/draw-features.ts, feature paths and masks in defs]
|
||||
- [Source: src/renderers/draw-state-labels.ts, text paths in defs]
|
||||
- [Source: public/modules/dynamic/auto-update.js, runtime mask creation]
|
||||
- [Source: src/index.html, `#deftemp` and `#defElements`]
|
||||
- [Source: public/modules/io/export.js, current defs cloning and cleanup]
|
||||
|
||||
## Dev Agent Record
|
||||
|
||||
### Agent Model Used
|
||||
|
||||
TBD
|
||||
|
||||
### Debug Log References
|
||||
|
||||
### Completion Notes List
|
||||
|
||||
- Story context prepared on 2026-03-13.
|
||||
|
||||
### File List
|
||||
72
_bmad-output/implementation-artifacts/sprint-status.yaml
Normal file
72
_bmad-output/implementation-artifacts/sprint-status.yaml
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
# generated: 2026-03-13
|
||||
# project: Fantasy-Map-Generator
|
||||
# project_key: NOKEY
|
||||
# tracking_system: file-system
|
||||
# story_location: /Users/azgaar/Fantasy-Map-Generator/_bmad-output/implementation-artifacts
|
||||
|
||||
# STATUS DEFINITIONS:
|
||||
# ==================
|
||||
# Epic Status:
|
||||
# - backlog: Epic not yet started
|
||||
# - in-progress: Epic actively being worked on
|
||||
# - done: All stories in epic completed
|
||||
#
|
||||
# Epic Status Transitions:
|
||||
# - backlog -> in-progress: Automatically when first story is created (via create-story)
|
||||
# - in-progress -> done: Manually when all stories reach 'done' status
|
||||
#
|
||||
# Story Status:
|
||||
# - backlog: Story only exists in epic file
|
||||
# - ready-for-dev: Story file created in stories folder
|
||||
# - in-progress: Developer actively working on implementation
|
||||
# - review: Ready for code review (via Dev's code-review workflow)
|
||||
# - done: Story completed
|
||||
#
|
||||
# Retrospective Status:
|
||||
# - optional: Can be completed but not required
|
||||
# - done: Retrospective has been completed
|
||||
#
|
||||
# WORKFLOW NOTES:
|
||||
# ===============
|
||||
# - Epic transitions to 'in-progress' automatically when first story is created
|
||||
# - Stories can be worked in parallel if team capacity allows
|
||||
# - SM typically creates next story after previous one is 'done' to incorporate learnings
|
||||
# - Dev moves story to 'review', then runs code-review (fresh context, different LLM recommended)
|
||||
|
||||
generated: 2026-03-13
|
||||
project: Fantasy-Map-Generator
|
||||
project_key: NOKEY
|
||||
tracking_system: file-system
|
||||
story_location: /Users/azgaar/Fantasy-Map-Generator/_bmad-output/implementation-artifacts
|
||||
|
||||
development_status:
|
||||
epic-1: in-progress
|
||||
1-1-bootstrap-scene-container-and-defs-host: in-progress
|
||||
1-2-add-scene-module-for-shared-camera-state: ready-for-dev
|
||||
1-3-add-layers-registry-as-the-ordering-source-of-truth: ready-for-dev
|
||||
1-4-add-layer-surface-lifecycle-ownership: ready-for-dev
|
||||
1-5-add-compatibility-lookups-for-legacy-single-svg-callers: ready-for-dev
|
||||
1-6-move-shared-defs-resources-to-the-dedicated-host: ready-for-dev
|
||||
epic-1-retrospective: optional
|
||||
|
||||
epic-2: backlog
|
||||
2-1-create-reusable-standalone-svg-shell-mounting: backlog
|
||||
2-2-migrate-a-representative-low-risk-svg-layer-to-a-dedicated-shell: backlog
|
||||
2-3-migrate-remaining-approved-low-risk-svg-layers: backlog
|
||||
2-4-drive-reorder-and-visibility-for-split-svg-layers-from-the-registry: backlog
|
||||
2-5-preserve-pointer-and-editing-workflows-for-split-svg-layers: backlog
|
||||
epic-2-retrospective: optional
|
||||
|
||||
epic-3: backlog
|
||||
3-1-register-webgl-surfaces-in-the-shared-layer-model: backlog
|
||||
3-2-unify-mixed-layer-ordering-and-visibility-controls: backlog
|
||||
3-3-keep-scene-transforms-and-interaction-correct-in-mixed-rendering: backlog
|
||||
3-4-preserve-atomic-updates-and-no-regressions-guardrails-in-mixed-mode: backlog
|
||||
epic-3-retrospective: optional
|
||||
|
||||
epic-4: backlog
|
||||
4-1-add-export-participation-metadata-to-the-layer-registry: backlog
|
||||
4-2-assemble-unified-svg-export-from-registry-order: backlog
|
||||
4-3-clone-only-required-defs-resources-into-export: backlog
|
||||
4-4-preserve-text-paths-masks-and-filtered-layers-in-export: backlog
|
||||
epic-4-retrospective: optional
|
||||
480
_bmad-output/planning-artifacts/epics.md
Normal file
480
_bmad-output/planning-artifacts/epics.md
Normal file
|
|
@ -0,0 +1,480 @@
|
|||
---
|
||||
stepsCompleted:
|
||||
- step-01-validate-prerequisites
|
||||
- step-02-design-epics
|
||||
- step-03-create-stories
|
||||
- step-04-final-validation
|
||||
inputDocuments:
|
||||
- _bmad-output/planning-artifacts/prd-layered-map-dom-split.md
|
||||
- _bmad-output/planning-artifacts/architecture-layered-map-dom-split.md
|
||||
---
|
||||
|
||||
# Fantasy-Map-Generator - Epic Breakdown
|
||||
|
||||
## Overview
|
||||
|
||||
This document provides the complete epic and story breakdown for Fantasy-Map-Generator, decomposing the requirements from the PRD, UX Design if it exists, and Architecture requirements into implementable stories.
|
||||
|
||||
## Requirements Inventory
|
||||
|
||||
### Functional Requirements
|
||||
|
||||
FR1: Users can reorder layers in any sequence supported today, regardless of whether the layer is implemented in SVG or WebGL.
|
||||
FR2: Visibility toggles behave exactly as they do now.
|
||||
FR3: Editing and pointer interaction remain correct after layer splitting.
|
||||
FR4: Shared styles, filters, masks, and text path resources remain available where needed.
|
||||
FR5: Existing layer-based workflows continue to function without exposing rendering implementation details.
|
||||
FR6: Export produces equivalent output to the current behavior for supported layers.
|
||||
FR7: Legacy code paths that depend on svg, viewbox, or one selector scope have a compatibility path during migration.
|
||||
|
||||
### NonFunctional Requirements
|
||||
|
||||
NFR1: The architecture must support incremental migration rather than a flag-day rewrite.
|
||||
NFR2: Test design and execution are out of scope for this implementation tranche and will be handled in a separate follow-up activity.
|
||||
NFR3: Layer order changes must update all runtime surfaces atomically.
|
||||
NFR4: Performance must not regress for existing SVG-only layers.
|
||||
NFR5: The architecture must remain compatible with the existing global-variable runtime model.
|
||||
|
||||
### Additional Requirements
|
||||
|
||||
- The layer registry is the single source of truth and each layer must register id, kind, order, visible state, and surface.
|
||||
- The runtime scene must use one independent surface per logical layer, including dedicated SVG shells for SVG layers and registered canvas surfaces for WebGL layers.
|
||||
- Shared transform ownership moves from the old viewbox DOM element to scene state, while scale, viewX, viewY, graphWidth, and graphHeight remain authoritative globals.
|
||||
- svg and viewbox must stop being architectural source-of-truth globals and instead be supported only through a temporary compatibility layer during migration.
|
||||
- The compatibility layer must provide getLayerSvg(id), getLayerSurface(id), and queryMap(selector).
|
||||
- Runtime shared defs resources must live in a dedicated defs host separate from layer surfaces.
|
||||
- Filters, patterns, masks, symbols, markers, and text paths must use stable IDs and remain available across split surfaces.
|
||||
- Export must use a separate assembly pipeline that rebuilds a unified SVG from registry order, layer outputs, and only the required defs resources.
|
||||
- Runtime structure must allow arbitrary ordering of SVG and WebGL layers without grouped SVG buckets.
|
||||
- Layer modules should own only their own drawing surface; scene owns shared runtime state; defs owns shared SVG resources.
|
||||
- The migration should proceed in phases: foundation, initial split layers, mixed rendering, then export hardening.
|
||||
- No starter template requirement was identified in the architecture document.
|
||||
- No UX document was provided, so no UX-specific requirements were added in this step.
|
||||
|
||||
### FR Coverage Map
|
||||
|
||||
FR1: Epic 2 and Epic 3 - Reordering remains correct first for split SVG layers and then for mixed SVG and WebGL layers.
|
||||
FR2: Epic 2 and Epic 3 - Visibility behavior remains unchanged for split layers and mixed rendering.
|
||||
FR3: Epic 2 and Epic 3 - Pointer and editing workflows remain correct through the split-layer and mixed-render transitions.
|
||||
FR4: Epic 1 and Epic 4 - Shared defs-backed resources are preserved in runtime and in unified export output.
|
||||
FR5: Epic 1, Epic 2, and Epic 3 - Existing workflows continue to work while the runtime architecture changes underneath them.
|
||||
FR6: Epic 4 - Export output remains equivalent for supported layers after the runtime moves to split surfaces.
|
||||
FR7: Epic 1 - Legacy single-SVG callers keep a compatibility path during migration.
|
||||
|
||||
## Epic List
|
||||
|
||||
### Epic 1: Foundation for Layered Runtime
|
||||
|
||||
Establish the shared scene, defs, layer registry, layer lifecycle, and compatibility contracts so the runtime can migrate away from one canonical SVG root without breaking existing map behavior.
|
||||
**FRs covered:** FR4, FR5, FR7
|
||||
|
||||
### Epic 2: First Split SVG Layers
|
||||
|
||||
Move approved low-risk SVG layers into standalone SVG shells while preserving visual behavior, layer controls, and editing interactions.
|
||||
**FRs covered:** FR1, FR2, FR3, FR5
|
||||
|
||||
### Epic 3: Mixed SVG and WebGL Ordering
|
||||
|
||||
Register WebGL surfaces under the same ordering and visibility model so users can interleave SVG and WebGL layers through one control path.
|
||||
**FRs covered:** FR1, FR2, FR3, FR5
|
||||
|
||||
### Epic 4: Unified Export Assembly
|
||||
|
||||
Assemble a unified SVG export from registry state and shared defs resources so supported layered maps export with equivalent output quality.
|
||||
**FRs covered:** FR4, FR6
|
||||
|
||||
## Epic 1: Foundation for Layered Runtime
|
||||
|
||||
Establish the shared scene, defs, layer registry, layer lifecycle, and compatibility contracts so the runtime can migrate away from one canonical SVG root without breaking existing map behavior.
|
||||
|
||||
### Story 1.1: Bootstrap Scene Container and Defs Host
|
||||
|
||||
As a map maintainer,
|
||||
I want runtime initialization to create a dedicated scene container and defs host,
|
||||
So that split layer surfaces can share resources without depending on one map SVG root.
|
||||
|
||||
**Implements:** FR4, FR5; NFR1, NFR5.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
|
||||
**Given** the current runtime starts from one canonical map host
|
||||
**When** the layered runtime bootstrap runs
|
||||
**Then** it creates or reuses a dedicated scene container for layer surfaces and a dedicated defs host outside individual layer surfaces
|
||||
**And** the visible map loads without user-facing regressions at startup.
|
||||
|
||||
**Given** existing code expects the map to initialize once
|
||||
**When** the new bootstrap path is enabled
|
||||
**Then** the scene container and defs host are available through stable runtime references
|
||||
**And** initialization does not require changing user-facing layer controls.
|
||||
|
||||
### Story 1.2: Add Scene Module for Shared Camera State
|
||||
|
||||
As a map maintainer,
|
||||
I want a Scene module to own shared camera and viewport state,
|
||||
So that all layer surfaces consume one authoritative transform contract.
|
||||
|
||||
**Implements:** FR5; NFR1, NFR5.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
|
||||
**Given** shared globals such as scale, viewX, viewY, graphWidth, and graphHeight already exist
|
||||
**When** the Scene module is introduced
|
||||
**Then** it exposes a single runtime contract for camera and viewport state
|
||||
**And** existing global values remain authoritative and usable during migration.
|
||||
|
||||
**Given** multiple surfaces will render the same map
|
||||
**When** camera state changes
|
||||
**Then** the Scene module provides one consistent state source for all registered surfaces
|
||||
**And** no feature code needs to read transforms from DOM structure directly.
|
||||
|
||||
### Story 1.3: Add Layers Registry as the Ordering Source of Truth
|
||||
|
||||
As a map maintainer,
|
||||
I want a centralized Layers registry,
|
||||
So that visibility, order, and surface ownership are managed consistently instead of inferred from DOM position.
|
||||
|
||||
**Implements:** FR5; NFR1, NFR3, NFR5.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
|
||||
**Given** logical map layers currently rely on DOM placement and ad hoc lookups
|
||||
**When** a layer is registered
|
||||
**Then** the registry stores its id, kind, order, visible state, and surface handle
|
||||
**And** callers can query layer state without inferring it from DOM order.
|
||||
|
||||
**Given** runtime layer order changes
|
||||
**When** reorder operations are applied through the registry
|
||||
**Then** all registered layer surfaces receive the updated ordering atomically
|
||||
**And** the registry remains the single source of truth for visibility and order.
|
||||
|
||||
### Story 1.4: Add Layer Surface Lifecycle Ownership
|
||||
|
||||
As a map maintainer,
|
||||
I want a Layer abstraction to own each surface lifecycle,
|
||||
So that individual layers can be created, mounted, updated, and disposed without leaking renderer-specific details.
|
||||
|
||||
**Implements:** FR5; NFR1, NFR5.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
|
||||
**Given** the runtime needs one surface per logical layer
|
||||
**When** a layer is instantiated
|
||||
**Then** the Layer abstraction owns creation, mount, update, and teardown for that surface
|
||||
**And** caller code interacts with the layer through a stable contract rather than direct DOM assumptions.
|
||||
|
||||
**Given** both SVG and WebGL layers will exist
|
||||
**When** a surface is registered with the Layer abstraction
|
||||
**Then** surface lifecycle handling works without exposing renderer-specific branching to feature modules
|
||||
**And** layer modules remain responsible only for drawing their own surface.
|
||||
|
||||
### Story 1.5: Add Compatibility Lookups for Legacy Single-SVG Callers
|
||||
|
||||
As a map maintainer,
|
||||
I want compatibility helpers for legacy single-SVG access patterns,
|
||||
So that existing workflows keep working while code migrates to the new scene and layer contracts.
|
||||
|
||||
**Implements:** FR5, FR7; NFR1, NFR5.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
|
||||
**Given** legacy code still expects svg, viewbox, or one shared selector scope
|
||||
**When** compatibility helpers are introduced
|
||||
**Then** the runtime provides getLayerSvg(id), getLayerSurface(id), and queryMap(selector)
|
||||
**And** callers can continue to function during migration without depending on one canonical map SVG.
|
||||
|
||||
**Given** new code is added after the compatibility layer exists
|
||||
**When** it needs layer or scene access
|
||||
**Then** it can use the new layer and scene contracts directly
|
||||
**And** the compatibility layer remains a migration bridge rather than the new source of truth.
|
||||
|
||||
### Story 1.6: Move Shared Defs Resources to the Dedicated Host
|
||||
|
||||
As a map maintainer,
|
||||
I want shared defs-backed resources to be registered in one dedicated host,
|
||||
So that split surfaces can keep using stable IDs for filters, masks, symbols, markers, patterns, and text paths.
|
||||
|
||||
**Implements:** FR4, FR5; NFR1, NFR5.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
|
||||
**Given** runtime resources currently live under one shared SVG structure
|
||||
**When** defs registration is migrated
|
||||
**Then** shared resources are created under the dedicated defs host with stable identifiers
|
||||
**And** layer surfaces can reference those resources without duplicating them per layer.
|
||||
|
||||
**Given** a split surface uses a filter, mask, symbol, marker, pattern, or text path
|
||||
**When** the layer renders
|
||||
**Then** the resource reference resolves from the dedicated defs host
|
||||
**And** existing visual behavior remains unchanged for supported runtime features.
|
||||
|
||||
## Epic 2: First Split SVG Layers
|
||||
|
||||
Move approved low-risk SVG layers into standalone SVG shells while preserving visual behavior, layer controls, and editing interactions.
|
||||
|
||||
### Story 2.1: Create Reusable Standalone SVG Shell Mounting
|
||||
|
||||
As a map maintainer,
|
||||
I want low-risk SVG layers to mount into dedicated SVG shells,
|
||||
So that selected layers can leave the single-root SVG without changing their visible behavior.
|
||||
|
||||
**Implements:** FR5; NFR1, NFR5.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
|
||||
**Given** a low-risk SVG layer is selected for migration
|
||||
**When** its surface is created through the layered runtime
|
||||
**Then** the layer mounts into its own dedicated SVG shell inside the scene container
|
||||
**And** the shell can participate in registry-controlled ordering and visibility.
|
||||
|
||||
**Given** the shell mounting path is in place
|
||||
**When** another approved low-risk SVG layer is migrated
|
||||
**Then** the same mounting mechanism can be reused without introducing layer-specific bootstrap hacks
|
||||
**And** the migration remains incremental.
|
||||
|
||||
### Story 2.2: Migrate a Representative Low-Risk SVG Layer to a Dedicated Shell
|
||||
|
||||
As a map maintainer,
|
||||
I want one representative low-risk SVG layer migrated first,
|
||||
So that the split-surface path is proven on a contained layer before broader rollout.
|
||||
|
||||
**Implements:** FR3, FR5; NFR1, NFR4.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
|
||||
**Given** the standalone shell path exists
|
||||
**When** the first approved low-risk SVG layer is migrated
|
||||
**Then** it renders from its dedicated shell with unchanged visual output for supported behavior
|
||||
**And** its feature workflow continues to operate through the new scene and layer contracts.
|
||||
|
||||
**Given** the representative layer is split out
|
||||
**When** users interact with that layer through existing tools
|
||||
**Then** pointer targeting and editing behavior remain correct
|
||||
**And** the migration does not require unrelated layers to move at the same time.
|
||||
|
||||
### Story 2.3: Migrate Remaining Approved Low-Risk SVG Layers
|
||||
|
||||
As a map maintainer,
|
||||
I want the remaining approved low-risk SVG layers moved through the same split-layer path,
|
||||
So that the runtime can prove multi-layer SVG splitting before mixed rendering begins.
|
||||
|
||||
**Implements:** FR3, FR5; NFR1, NFR4.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
|
||||
**Given** the representative migration pattern has been established
|
||||
**When** additional approved low-risk SVG layers are migrated
|
||||
**Then** each layer renders from its own SVG shell using the shared scene, layer, and defs contracts
|
||||
**And** each migrated layer preserves its existing visible behavior for supported workflows.
|
||||
|
||||
**Given** more than one low-risk SVG layer has been split
|
||||
**When** they coexist in the runtime
|
||||
**Then** they can render independently without grouped SVG buckets
|
||||
**And** layer modules remain responsible only for their own surfaces.
|
||||
|
||||
### Story 2.4: Drive Reorder and Visibility for Split SVG Layers from the Registry
|
||||
|
||||
As a map user,
|
||||
I want reordered and toggled split SVG layers to behave exactly as before,
|
||||
So that layer management remains familiar while the runtime architecture changes.
|
||||
|
||||
**Implements:** FR1, FR2, FR5; NFR3, NFR5.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
|
||||
**Given** one or more low-risk SVG layers have been split into standalone shells
|
||||
**When** a user reorders those layers through existing controls
|
||||
**Then** the registry updates the shell ordering atomically
|
||||
**And** the visible stacking order matches the updated control order.
|
||||
|
||||
**Given** a user toggles visibility for a split SVG layer
|
||||
**When** the layer state changes
|
||||
**Then** the registry applies the visible state to the correct shell
|
||||
**And** toggle behavior matches existing user expectations.
|
||||
|
||||
### Story 2.5: Preserve Pointer and Editing Workflows for Split SVG Layers
|
||||
|
||||
As a map user,
|
||||
I want editing and pointer interactions to remain correct on split SVG layers,
|
||||
So that the architectural migration does not break existing map workflows.
|
||||
|
||||
**Implements:** FR3, FR5; NFR4, NFR5.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
|
||||
**Given** a split SVG layer supports pointer or editing workflows today
|
||||
**When** a user clicks, hovers, drags, or edits through the existing UI
|
||||
**Then** events reach the correct standalone layer surface
|
||||
**And** the workflow behaves the same way it did before the split.
|
||||
|
||||
**Given** multiple split SVG layers can overlap in the scene
|
||||
**When** the active tool resolves pointer targets
|
||||
**Then** targeting respects the registry-driven order and layer visibility state
|
||||
**And** no workflow depends on a future mixed-render story to function.
|
||||
|
||||
## Epic 3: Mixed SVG and WebGL Ordering
|
||||
|
||||
Register WebGL surfaces under the same ordering and visibility model so users can interleave SVG and WebGL layers through one control path.
|
||||
|
||||
### Story 3.1: Register WebGL Surfaces in the Shared Layer Model
|
||||
|
||||
As a map maintainer,
|
||||
I want WebGL surfaces registered in the same Layers model as SVG layers,
|
||||
So that render technology no longer determines whether a layer can participate in the ordered scene.
|
||||
|
||||
**Implements:** FR1, FR5; NFR1, NFR5.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
|
||||
**Given** the Layers registry already manages SVG layer surfaces
|
||||
**When** a WebGL layer is registered
|
||||
**Then** it receives the same core metadata contract of id, kind, order, visible state, and surface handle
|
||||
**And** the registry can treat SVG and WebGL layers as peers in the same scene.
|
||||
|
||||
**Given** existing WebGL behavior already depends on shared scene context
|
||||
**When** registration is moved into the shared layer model
|
||||
**Then** WebGL layers keep access to the required scene and camera state
|
||||
**And** they no longer rely on special ordering paths outside the registry.
|
||||
|
||||
### Story 3.2: Unify Mixed-Layer Ordering and Visibility Controls
|
||||
|
||||
As a map user,
|
||||
I want one layer control path to reorder and toggle both SVG and WebGL layers,
|
||||
So that I can manage the full layer stack without caring how any layer is rendered.
|
||||
|
||||
**Implements:** FR1, FR2, FR5; NFR3, NFR5.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
|
||||
**Given** both SVG and WebGL layers are registered in the scene
|
||||
**When** a user changes layer order through the existing controls
|
||||
**Then** the registry applies the new order across both render technologies atomically
|
||||
**And** the visible stack matches the requested mixed order.
|
||||
|
||||
**Given** a user toggles visibility for an SVG or WebGL layer
|
||||
**When** the control state changes
|
||||
**Then** the runtime updates the correct surface regardless of renderer type
|
||||
**And** visibility behavior remains consistent across the mixed stack.
|
||||
|
||||
### Story 3.3: Keep Scene Transforms and Interaction Correct in Mixed Rendering
|
||||
|
||||
As a map user,
|
||||
I want zoom, pan, and interactions to stay aligned across SVG and WebGL layers,
|
||||
So that the map behaves as one coherent scene after mixed rendering is enabled.
|
||||
|
||||
**Implements:** FR3, FR5; NFR4, NFR5.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
|
||||
**Given** the scene contains both SVG and WebGL surfaces
|
||||
**When** camera state changes through zoom or pan
|
||||
**Then** all registered surfaces consume the same scene transform state
|
||||
**And** the map remains visually aligned across renderer boundaries.
|
||||
|
||||
**Given** a user interacts with mixed-render content through existing tools
|
||||
**When** the runtime resolves those interactions
|
||||
**Then** the correct layer surface handles the interaction in scene order
|
||||
**And** mixed rendering does not degrade the expected behavior of existing workflows.
|
||||
|
||||
### Story 3.4: Preserve Atomic Updates and No-Regressions Guardrails in Mixed Mode
|
||||
|
||||
As a map maintainer,
|
||||
I want mixed-layer updates applied through one controlled pipeline,
|
||||
So that the runtime preserves atomic layer changes and avoids regressions for existing SVG-heavy maps.
|
||||
|
||||
**Implements:** FR1, FR2, FR5; NFR3, NFR4.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
|
||||
**Given** layer order or visibility changes affect both SVG and WebGL surfaces
|
||||
**When** the runtime applies an update
|
||||
**Then** all affected surfaces transition through one coordinated update path
|
||||
**And** partial mixed-stack states are not exposed to the user.
|
||||
|
||||
**Given** an existing SVG-heavy map uses no new mixed-render features
|
||||
**When** it runs on the layered runtime
|
||||
**Then** the runtime preserves current behavior for supported workflows
|
||||
**And** the migration does not introduce avoidable performance regressions for SVG-only scenarios.
|
||||
|
||||
## Epic 4: Unified Export Assembly
|
||||
|
||||
Assemble a unified SVG export from registry state and shared defs resources so supported layered maps export with equivalent output quality.
|
||||
|
||||
### Story 4.1: Add Export Participation Metadata to the Layer Registry
|
||||
|
||||
As a map maintainer,
|
||||
I want each registered layer to declare how it participates in export,
|
||||
So that unified SVG assembly can rebuild export output from registry state instead of the live runtime DOM.
|
||||
|
||||
**Implements:** FR6; NFR1, NFR5.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
|
||||
**Given** the export pipeline can no longer assume the runtime DOM is the final SVG document
|
||||
**When** a layer is registered for export
|
||||
**Then** the registry stores the metadata needed to include, order, or skip that layer during export assembly
|
||||
**And** export logic reads registry state rather than scraping one runtime SVG root.
|
||||
|
||||
**Given** supported layers have different render technologies
|
||||
**When** export participation metadata is evaluated
|
||||
**Then** the assembler can determine which layers contribute to the final SVG output
|
||||
**And** unsupported layers can be excluded intentionally rather than failing implicitly.
|
||||
|
||||
### Story 4.2: Assemble Unified SVG Export from Registry Order
|
||||
|
||||
As a map user,
|
||||
I want export output assembled from the layered runtime state,
|
||||
So that my exported map preserves supported layer order after the DOM is split into separate surfaces.
|
||||
|
||||
**Implements:** FR6; NFR1, NFR5.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
|
||||
**Given** the runtime scene is composed of multiple registered surfaces
|
||||
**When** a user exports the map
|
||||
**Then** the export pipeline assembles one unified SVG document using registry order
|
||||
**And** the supported exported layer stack matches the user-visible order.
|
||||
|
||||
**Given** the runtime no longer uses one canonical map SVG as its model
|
||||
**When** export runs
|
||||
**Then** the export pipeline treats the live DOM as an implementation detail rather than the export source of truth
|
||||
**And** supported layers still produce equivalent SVG output.
|
||||
|
||||
### Story 4.3: Clone Only Required Defs Resources into Export
|
||||
|
||||
As a map user,
|
||||
I want exported SVG files to include only the defs resources they actually use,
|
||||
So that exported maps remain correct without dragging along unrelated runtime resources.
|
||||
|
||||
**Implements:** FR4, FR6; NFR1, NFR5.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
|
||||
**Given** the runtime defs host may contain resources for many layers
|
||||
**When** export assembly runs
|
||||
**Then** the pipeline includes only defs resources referenced by the exported layers
|
||||
**And** exported resources preserve the stable IDs required by those layers.
|
||||
|
||||
**Given** an exported layer references shared defs-backed assets
|
||||
**When** the export document is opened independently of the runtime
|
||||
**Then** those references resolve correctly within the assembled SVG
|
||||
**And** unrelated defs content is not copied by default.
|
||||
|
||||
### Story 4.4: Preserve Text Paths, Masks, and Filtered Layers in Export
|
||||
|
||||
As a map user,
|
||||
I want exported maps to preserve supported advanced SVG features,
|
||||
So that split-surface runtime changes do not degrade final output quality.
|
||||
|
||||
**Implements:** FR4, FR6; NFR1, NFR5.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
|
||||
**Given** supported exported layers use text paths, masks, filters, symbols, markers, or patterns
|
||||
**When** export assembly completes
|
||||
**Then** the unified SVG preserves those features with correct references and ordering
|
||||
**And** output remains equivalent for supported layers.
|
||||
|
||||
**Given** advanced defs-backed features participate in export
|
||||
**When** the assembler processes them
|
||||
**Then** it clones the required resources and wiring needed for those features to render correctly outside the runtime
|
||||
**And** export hardening remains isolated from runtime ordering logic.
|
||||
|
|
@ -0,0 +1,236 @@
|
|||
---
|
||||
stepsCompleted:
|
||||
- step-01-document-discovery
|
||||
inputDocuments:
|
||||
- _bmad-output/planning-artifacts/prd-layered-map-dom-split.md
|
||||
- _bmad-output/planning-artifacts/architecture-layered-map-dom-split.md
|
||||
- _bmad-output/planning-artifacts/epics.md
|
||||
excludedDocuments:
|
||||
- _bmad-output/planning-artifacts/prd.md
|
||||
- _bmad-output/planning-artifacts/architecture.md
|
||||
missingDocuments:
|
||||
- UX design document
|
||||
---
|
||||
|
||||
# Implementation Readiness Assessment Report
|
||||
|
||||
**Date:** 2026-03-12
|
||||
**Project:** Fantasy-Map-Generator
|
||||
|
||||
## Document Discovery
|
||||
|
||||
### PRD Files Found
|
||||
|
||||
**Whole Documents:**
|
||||
|
||||
- \_bmad-output/planning-artifacts/prd-layered-map-dom-split.md (8695 bytes, modified Mar 12 23:06:55 2026) [selected]
|
||||
- \_bmad-output/planning-artifacts/prd.md (31818 bytes, modified Mar 12 03:42:10 2026) [excluded]
|
||||
|
||||
### Architecture Files Found
|
||||
|
||||
**Whole Documents:**
|
||||
|
||||
- \_bmad-output/planning-artifacts/architecture-layered-map-dom-split.md (7274 bytes, modified Mar 12 23:22:49 2026) [selected]
|
||||
- \_bmad-output/planning-artifacts/architecture.md (50648 bytes, modified Mar 12 19:00:05 2026) [excluded]
|
||||
|
||||
### Epics and Stories Files Found
|
||||
|
||||
**Whole Documents:**
|
||||
|
||||
- \_bmad-output/planning-artifacts/epics.md (22401 bytes, modified Mar 12 23:55:41 2026) [selected]
|
||||
|
||||
### UX Files Found
|
||||
|
||||
- None found
|
||||
|
||||
## Discovery Issues
|
||||
|
||||
- Duplicate document families exist for PRD and Architecture. This assessment uses the layered-map documents explicitly requested by the user and excludes the baseline documents from scope.
|
||||
- No UX design artifact was found. Readiness assessment will proceed without UX-specific validation.
|
||||
|
||||
## Assessment Scope
|
||||
|
||||
This readiness assessment is scoped to:
|
||||
|
||||
- \_bmad-output/planning-artifacts/prd-layered-map-dom-split.md
|
||||
- \_bmad-output/planning-artifacts/architecture-layered-map-dom-split.md
|
||||
- \_bmad-output/planning-artifacts/epics.md
|
||||
|
||||
## PRD Analysis
|
||||
|
||||
### Functional Requirements
|
||||
|
||||
FR1: Users can reorder layers in any sequence supported today, regardless of whether the layer is implemented in SVG or WebGL.
|
||||
FR2: Visibility toggles behave exactly as they do now.
|
||||
FR3: Editing and pointer interaction remain correct after layer splitting.
|
||||
FR4: Shared styles, filters, masks, and text path resources remain available where needed.
|
||||
FR5: Existing layer-based workflows continue to function without exposing rendering implementation details.
|
||||
FR6: Export produces equivalent output to the current behavior for supported layers.
|
||||
FR7: Legacy code paths that depend on svg, viewbox, or one selector scope have a compatibility path during migration.
|
||||
|
||||
Total FRs: 7
|
||||
|
||||
### Non-Functional Requirements
|
||||
|
||||
NFR1: The architecture must support incremental migration rather than a flag-day rewrite.
|
||||
NFR2: Test design and execution are out of scope for this implementation tranche and will be handled in a separate follow-up activity.
|
||||
NFR3: Layer order changes must update all runtime surfaces atomically.
|
||||
NFR4: Performance must not regress for existing SVG-only layers.
|
||||
NFR5: The architecture must remain compatible with the existing global-variable runtime model.
|
||||
|
||||
Total NFRs: 5
|
||||
|
||||
### Additional Requirements
|
||||
|
||||
- The initiative is explicitly constrained to preserve current user-visible layer controls, visibility behavior, edit workflows, and output quality while the runtime architecture changes underneath.
|
||||
- Shared map state remains globally accessible, but globals tied to one concrete SVG root may be narrowed, redefined, or placed behind compatibility adapters.
|
||||
- Shared defs, masks, filters, symbols, and text paths are first-class requirements rather than deferred cleanup.
|
||||
- The codebase needs an inventory and migration plan for hotspots that assume one map SVG or one shared selector scope.
|
||||
- Automated and manual testing are out of scope for this implementation tranche and deferred until the new layer model stabilizes.
|
||||
- The current PRD leaves several architecture-shaping questions open, including which layers are low-risk for splitting first and how export assembly should be sourced.
|
||||
|
||||
### PRD Completeness Assessment
|
||||
|
||||
The PRD is sufficient to define the product goal, scope boundaries, functional requirements, non-functional requirements, and acceptance themes for the layered-runtime initiative. It is intentionally incomplete on implementation specifics and leaves several open questions for architecture and execution planning, which is acceptable because those details are partially answered by the selected architecture document. The main residual gap at the PRD layer is that it does not name the initial low-risk split candidates or define a UX artifact, so implementation readiness depends on the architecture and epics compensating for that missing specificity.
|
||||
|
||||
## Epic Coverage Validation
|
||||
|
||||
### Epic FR Coverage Extracted
|
||||
|
||||
FR1: Covered in Epic 2 and Epic 3
|
||||
FR2: Covered in Epic 2 and Epic 3
|
||||
FR3: Covered in Epic 2 and Epic 3
|
||||
FR4: Covered in Epic 1 and Epic 4
|
||||
FR5: Covered in Epic 1, Epic 2, and Epic 3
|
||||
FR6: Covered in Epic 4
|
||||
FR7: Covered in Epic 1
|
||||
|
||||
Total FRs in epics: 7
|
||||
|
||||
### Coverage Matrix
|
||||
|
||||
| FR Number | PRD Requirement | Epic Coverage | Status |
|
||||
| --------- | ------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | ------- |
|
||||
| FR1 | Users can reorder layers in any sequence supported today, regardless of whether the layer is implemented in SVG or WebGL. | Epic 2 Story 2.4; Epic 3 Stories 3.1, 3.2, 3.4 | Covered |
|
||||
| FR2 | Visibility toggles behave exactly as they do now. | Epic 2 Story 2.4; Epic 3 Stories 3.2, 3.4 | Covered |
|
||||
| FR3 | Editing and pointer interaction remain correct after layer splitting. | Epic 2 Stories 2.2, 2.3, 2.5; Epic 3 Story 3.3 | Covered |
|
||||
| FR4 | Shared styles, filters, masks, and text path resources remain available where needed. | Epic 1 Stories 1.1, 1.6; Epic 4 Stories 4.3, 4.4 | Covered |
|
||||
| FR5 | Existing layer-based workflows continue to function without exposing rendering implementation details. | Epic 1 Stories 1.1 to 1.6; Epic 2 Stories 2.1 to 2.5; Epic 3 Stories 3.1 to 3.4 | Covered |
|
||||
| FR6 | Export produces equivalent output to the current behavior for supported layers. | Epic 4 Stories 4.1 to 4.4 | Covered |
|
||||
| FR7 | Legacy code paths that depend on svg, viewbox, or one selector scope have a compatibility path during migration. | Epic 1 Story 1.5 | Covered |
|
||||
|
||||
### Missing Requirements
|
||||
|
||||
No PRD functional requirements are missing from the current epics and stories document.
|
||||
|
||||
Residual traceability note:
|
||||
|
||||
- FR3 and FR5 are covered broadly, but validation depends on maintaining story acceptance criteria discipline during implementation because these requirements span many modules and workflows.
|
||||
- FR4 and FR6 coverage is present, but export fidelity remains low priority and therefore carries sequencing risk rather than outright coverage risk.
|
||||
|
||||
### Coverage Statistics
|
||||
|
||||
- Total PRD FRs: 7
|
||||
- FRs covered in epics: 7
|
||||
- Coverage percentage: 100%
|
||||
|
||||
## UX Alignment Assessment
|
||||
|
||||
### UX Document Status
|
||||
|
||||
Not Found.
|
||||
|
||||
### Alignment Issues
|
||||
|
||||
- No dedicated UX planning artifact exists for this initiative, so there is no separately reviewed source for interaction flows, affordances, edge-case behavior, or acceptance-level user experience constraints.
|
||||
- The PRD clearly implies a user-facing application because it requires stable layer controls, visibility behavior, edit workflows, and output quality, but those expectations are not decomposed into a dedicated UX document.
|
||||
- The architecture preserves current user-visible behavior and interaction correctness, which partially compensates for missing UX documentation, but it does not replace explicit UX decisions for complex editing workflows.
|
||||
|
||||
### Warnings
|
||||
|
||||
- UX is implied by the problem and requirements, but no UX artifact was provided for readiness validation.
|
||||
- Implementation can proceed for this architecture tranche because the stated goal is behavioral parity rather than new interaction design, but QA and acceptance review later will carry extra ambiguity around interaction regressions.
|
||||
- If future stories introduce changed controls, changed affordances, or non-parity interaction behavior, a UX artifact should be created before implementation continues.
|
||||
|
||||
## Epic Quality Review
|
||||
|
||||
### Best Practices Compliance Summary
|
||||
|
||||
- Epic independence is mostly preserved in sequence: Epic 2 can build on Epic 1, Epic 3 can build on Epics 1 and 2, and Epic 4 can remain low-priority and isolated to export.
|
||||
- Within-epic forward dependencies are mostly controlled. The story order generally builds from infrastructure to usage without explicit dependence on future stories.
|
||||
- Traceability is present because every story includes an Implements line and the document includes a coverage map.
|
||||
|
||||
### 🔴 Critical Violations
|
||||
|
||||
1. The epic structure is phase-driven technical delivery, not user-value-driven delivery.
|
||||
|
||||
- Epic 1 is explicitly foundation work: scene, defs, registry, layer lifecycle, compatibility contracts.
|
||||
- Epic 2 and Epic 3 are migration phases keyed to renderer architecture.
|
||||
- Epic 4 is an export pipeline phase.
|
||||
- This directly conflicts with the workflow standard that epics should organize around user value rather than technical layers or infrastructure milestones.
|
||||
- Recommendation: accept these as implementation phases if that is the deliberate exception, but mark the document as not fully compliant with BMAD epic-quality rules.
|
||||
|
||||
2. Several stories are technical maintenance stories rather than meaningful user capabilities.
|
||||
|
||||
- Examples: Story 1.2 Add Scene Module for Shared Camera State, Story 1.3 Add Layers Registry as the Ordering Source of Truth, Story 1.4 Add Layer Surface Lifecycle Ownership, Story 3.1 Register WebGL Surfaces in the Shared Layer Model, Story 4.1 Add Export Participation Metadata to the Layer Registry.
|
||||
- These may be valid implementation work items, but they are not strong user stories under the workflow’s own standard.
|
||||
- Recommendation: either convert them into engineering tasks under broader user-facing stories, or explicitly classify this artifact as an architecture-delivery plan rather than pure user-story decomposition.
|
||||
|
||||
### 🟠 Major Issues
|
||||
|
||||
1. Acceptance criteria are testable but often too abstract for direct implementation handoff.
|
||||
|
||||
- Many ACs assert outcomes like "existing visual behavior remains unchanged" or "workflow behaves the same way it did before" without naming concrete layers, tools, or observables.
|
||||
- This makes implementation review and later QA interpretation more subjective than it should be.
|
||||
- Recommendation: add concrete examples or named validation targets per story, especially for split-layer migrations and export fidelity.
|
||||
|
||||
2. Low-risk SVG migration scope is still undefined at story level.
|
||||
|
||||
- Epic 2 depends on "approved low-risk SVG layers," but the selected documents never identify which layers those are.
|
||||
- Story 2.2 and Story 2.3 are therefore not fully actionable without another planning decision.
|
||||
- Recommendation: add a shortlist of candidate layers or a prerequisite decision artifact before implementation begins.
|
||||
|
||||
3. Story sizing is uneven in a few places.
|
||||
|
||||
- Story 2.3, Story 3.2, Story 3.3, and Story 4.2 through 4.4 may each span multiple modules, cross-layer behaviors, and regression-sensitive flows.
|
||||
- They may be larger than a single clean dev-agent session depending on the current codebase reality.
|
||||
- Recommendation: consider splitting the highest-risk stories by concrete layer set, runtime behavior, or export feature class.
|
||||
|
||||
### 🟡 Minor Concerns
|
||||
|
||||
1. Missing UX artifact increases ambiguity around interaction-parity acceptance.
|
||||
2. The document is consistent, but several stories are written from the perspective of a "map maintainer" rather than a direct end user, which weakens the user-story framing.
|
||||
3. Export remains intentionally low priority, which is fine strategically, but it means FR6 readiness is later than the rest of the plan and should be tracked as a sequencing risk.
|
||||
|
||||
### Dependency Review
|
||||
|
||||
- No explicit within-epic forward dependency violations were found.
|
||||
- No starter-template requirement exists in the architecture, so there is no missing greenfield bootstrap story.
|
||||
- The brownfield migration shape is reflected appropriately through compatibility and incremental split stories.
|
||||
- Database/entity timing rules are not relevant to this initiative and no analogous upfront persistence anti-pattern was found.
|
||||
|
||||
### Quality Review Verdict
|
||||
|
||||
The current epics and stories are workable as an implementation roadmap for an architectural migration, but they do not fully satisfy the workflow's own best-practice standard for user-value-first epics and user-facing stories. This is the main readiness defect, and it should be treated as a conscious tradeoff rather than ignored.
|
||||
|
||||
## Summary and Recommendations
|
||||
|
||||
### Overall Readiness Status
|
||||
|
||||
READY
|
||||
|
||||
### Critical Issues Requiring Immediate Action
|
||||
|
||||
1. No blocking issues remain because the user explicitly accepted the phase-based migration roadmap as a deliberate exception to the workflow's default epic-design standard.
|
||||
2. The previously identified epic/story framing issues remain documented as accepted planning debt and should be monitored during implementation.
|
||||
|
||||
### Recommended Next Steps
|
||||
|
||||
1. Proceed into sprint planning and story execution using the current phase-based roadmap.
|
||||
2. Tighten acceptance criteria opportunistically during story preparation, especially where parity or export behavior is still abstract.
|
||||
3. Identify the first low-risk SVG layer candidates before Epic 2 implementation work starts.
|
||||
4. Add a lightweight UX parity note if future implementation changes interaction behavior instead of preserving parity.
|
||||
|
||||
### Final Note
|
||||
|
||||
This assessment identified 8 issues across 3 severity categories, plus 1 scope warning about missing UX documentation. None of the PRD functional requirements are missing from the plan, and the architecture-to-epics traceability is coherent. On 2026-03-13, the user explicitly accepted the phase-driven engineering roadmap as a conscious exception to the workflow's default user-value-first epic standard. With that exception accepted, the planning set is cleared for implementation and the readiness status is marked READY.
|
||||
100
tests/e2e/scene-bootstrap.spec.ts
Normal file
100
tests/e2e/scene-bootstrap.spec.ts
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
import path from "path";
|
||||
import { expect, test } from "@playwright/test";
|
||||
|
||||
async function collectCriticalErrors(page: import("@playwright/test").Page, action: () => Promise<void>) {
|
||||
const errors: string[] = [];
|
||||
|
||||
page.on("pageerror", error => errors.push(`pageerror: ${error.message}`));
|
||||
page.on("console", message => {
|
||||
if (message.type() === "error") {
|
||||
errors.push(`console.error: ${message.text()}`);
|
||||
}
|
||||
});
|
||||
|
||||
await action();
|
||||
|
||||
return errors.filter(
|
||||
error =>
|
||||
!error.includes("fonts.googleapis.com") &&
|
||||
!error.includes("google-analytics") &&
|
||||
!error.includes("googletagmanager") &&
|
||||
!error.includes("Failed to load resource"),
|
||||
);
|
||||
}
|
||||
|
||||
test.describe("scene bootstrap", () => {
|
||||
test.beforeEach(async ({ context, page }) => {
|
||||
await context.clearCookies();
|
||||
await page.goto("/");
|
||||
await page.evaluate(() => {
|
||||
localStorage.clear();
|
||||
sessionStorage.clear();
|
||||
});
|
||||
});
|
||||
|
||||
test("creates runtime hosts on fresh load", async ({ page }) => {
|
||||
const criticalErrors = await collectCriticalErrors(page, async () => {
|
||||
await page.goto("/?seed=test-scene-bootstrap&width=1280&height=720");
|
||||
await page.waitForFunction(() => (window as any).mapId !== undefined, {timeout: 60000});
|
||||
});
|
||||
|
||||
const runtimeHosts = await page.evaluate(() => {
|
||||
const mapContainer = document.getElementById("map-container");
|
||||
const sceneContainer = document.getElementById("map-scene");
|
||||
const defsHost = document.getElementById("runtime-defs-host");
|
||||
const map = document.getElementById("map");
|
||||
const canvas = document.getElementById("webgl-canvas");
|
||||
|
||||
return {
|
||||
hasMapContainer: !!mapContainer,
|
||||
hasSceneContainer: !!sceneContainer,
|
||||
hasDefsHost: !!defsHost,
|
||||
mapInsideScene: !!sceneContainer?.contains(map),
|
||||
canvasInsideScene: !!sceneContainer?.contains(canvas),
|
||||
defsOutsideScene: !!defsHost && !!sceneContainer && !sceneContainer.contains(defsHost),
|
||||
};
|
||||
});
|
||||
|
||||
expect(runtimeHosts.hasMapContainer).toBe(true);
|
||||
expect(runtimeHosts.hasSceneContainer).toBe(true);
|
||||
expect(runtimeHosts.hasDefsHost).toBe(true);
|
||||
expect(runtimeHosts.mapInsideScene).toBe(true);
|
||||
expect(runtimeHosts.canvasInsideScene).toBe(true);
|
||||
expect(runtimeHosts.defsOutsideScene).toBe(true);
|
||||
expect(criticalErrors).toEqual([]);
|
||||
});
|
||||
|
||||
test("reuses runtime hosts after loading a saved map", async ({ page }) => {
|
||||
const criticalErrors = await collectCriticalErrors(page, async () => {
|
||||
await page.goto("/");
|
||||
await page.waitForSelector("#mapToLoad", {state: "attached"});
|
||||
const mapFilePath = path.join(__dirname, "../fixtures/demo.map");
|
||||
await page.locator("#mapToLoad").setInputFiles(mapFilePath);
|
||||
await page.waitForFunction(() => (window as any).mapId !== undefined, {timeout: 120000});
|
||||
});
|
||||
|
||||
const runtimeHosts = await page.evaluate(() => {
|
||||
const sceneContainer = document.getElementById("map-scene");
|
||||
const defsHost = document.getElementById("runtime-defs-host");
|
||||
const map = document.getElementById("map");
|
||||
const viewbox = document.getElementById("viewbox");
|
||||
const deftemp = document.getElementById("deftemp");
|
||||
const canvas = document.getElementById("webgl-canvas");
|
||||
|
||||
return {
|
||||
hasSceneContainer: !!sceneContainer,
|
||||
hasDefsHost: !!defsHost,
|
||||
mapInsideScene: !!sceneContainer?.contains(map),
|
||||
canvasInsideScene: !!sceneContainer?.contains(canvas),
|
||||
rebuiltMapSelections: !!viewbox && !!deftemp,
|
||||
};
|
||||
});
|
||||
|
||||
expect(runtimeHosts.hasSceneContainer).toBe(true);
|
||||
expect(runtimeHosts.hasDefsHost).toBe(true);
|
||||
expect(runtimeHosts.mapInsideScene).toBe(true);
|
||||
expect(runtimeHosts.canvasInsideScene).toBe(true);
|
||||
expect(runtimeHosts.rebuiltMapSelections).toBe(true);
|
||||
expect(criticalErrors).toEqual([]);
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue