docs(simulator): design spec + BDDs for curator's X-ray experience simulator
Discovery session 0003. Web-based, Docker-on-localhost simulator to feel the
selection model: real hef.selection wired to a synthetic fixture catalog, with
a full-transparency X-ray (ranked pool + distances + brain/mood grid maps) and
live model knobs (weights, pool size, approved-only).
- docs/superpowers/specs/2026-06-04-experience-simulator-design.md
- features/{selection_xray,model_knobs,fixture_catalog,simulator_service}.feature
- one additive hef.selection.ranked_candidates() change planned (single source of truth)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
Feature: Synthetic fixture catalog
|
||||
As a curator with an empty real catalog
|
||||
I want a dense, representative stand-in catalog generated on demand
|
||||
So that I can feel nearest-match behavior everywhere before any media is ingested
|
||||
|
||||
Scenario: The fixture catalog spans the coordinate space and all content modes
|
||||
When the fixture catalog is generated
|
||||
Then it contains records covering the brain plane and the mood plane
|
||||
And it contains records in each of the "audio", "video", and "av" modes
|
||||
And it contains a mix of "proposed" and "approved" records
|
||||
|
||||
Scenario: The fixture catalog is valid
|
||||
When the fixture catalog is generated
|
||||
Then every record passes hef.catalog validation
|
||||
And no record references real media
|
||||
|
||||
Scenario: Generation is deterministic
|
||||
Given a fixed seed
|
||||
When the fixture catalog is generated twice
|
||||
Then both runs produce identical records
|
||||
|
||||
Scenario: The real catalog is used when it is populated
|
||||
Given catalog/library.jsonl is non-empty
|
||||
When the simulator starts pointed at the real catalog
|
||||
Then it loads the real records through the same loader instead of the fixtures
|
||||
@@ -0,0 +1,32 @@
|
||||
Feature: Feeling the model's own parameters
|
||||
As a curator tuning the algorithm, not just the curation
|
||||
I want to adjust the selection model's parameters live
|
||||
So that I can feel how weighting, pool size, and the approved gate reshape picks
|
||||
|
||||
Background:
|
||||
Given the simulator is loaded with the synthetic fixture catalog
|
||||
And the dials are set to a fixed knob position with mode "av"
|
||||
|
||||
Scenario: Reweighting the brain plane can change the winner
|
||||
Given the brain and mood weights are equal
|
||||
And the current pick is recorded
|
||||
When I increase the brain weight relative to the mood weight
|
||||
Then the ranked pool may reorder so that brain-plane distance dominates
|
||||
And the picked piece can differ from the recorded pick
|
||||
|
||||
Scenario: Pool size controls how many candidates are shown and shuffled
|
||||
When I set the pool size to 1
|
||||
Then the pool shows exactly the single nearest candidate
|
||||
When I set the pool size to 5
|
||||
Then the pool shows up to the five nearest candidates
|
||||
|
||||
Scenario: Approved-only narrows the pool to human-blessed records
|
||||
Given the fixture catalog contains both "proposed" and "approved" records
|
||||
When I turn the approved-only toggle on
|
||||
Then every candidate in the pool has review_status "approved"
|
||||
When I turn the approved-only toggle off
|
||||
Then "proposed" records may appear in the pool again
|
||||
|
||||
Scenario: Default model parameters reproduce the shipped selection behavior
|
||||
Given the weights are equal, the pool size is the default, and approved-only is off
|
||||
Then the picked piece matches what hef.selection.select would return for the same dials
|
||||
@@ -0,0 +1,48 @@
|
||||
Feature: Curator's X-ray of the selection model
|
||||
As a curator
|
||||
I want to turn the installation's dials against a representative catalog and see
|
||||
the full ranked pool the nearest-match algorithm picks from
|
||||
So that I can judge whether the tagging rubric and selection surface fitting pieces
|
||||
|
||||
Background:
|
||||
Given the simulator is loaded with the synthetic fixture catalog
|
||||
And the fixture catalog spans the brain plane and the mood plane across all content modes
|
||||
|
||||
Scenario: A pick is always accompanied by its candidate pool
|
||||
When I set the mode to "av" and the dials to left=1 right=3 dark=4 light=1
|
||||
Then a piece is picked
|
||||
And the ranked pool is shown nearest-first with each candidate's distance
|
||||
And the picked piece is the rank-1 candidate in the pool
|
||||
|
||||
Scenario: Turning Dark up while Light stays low pulls the pick toward somber pieces
|
||||
Given the mode is "av" and the dials are left=2 right=2 dark=0 light=0
|
||||
When I raise the Dark dial from 0 to 4
|
||||
Then the picked piece's dark coordinate is higher than before
|
||||
And the pool's nearest candidates cluster toward the high-dark / low-light region
|
||||
|
||||
Scenario: Turning a dial moves the knob point on the coordinate map
|
||||
Given the mode is "av"
|
||||
When I change any of the Left, Right, Dark, or Light dials
|
||||
Then the knob point on the brain or mood grid map moves to the new coordinate
|
||||
And the candidate dots reflect the new neighborhood
|
||||
|
||||
Scenario: None mode is the void / rest state
|
||||
When I set the mode to "none"
|
||||
Then no piece is picked
|
||||
And the pool is empty
|
||||
And the screen shows the void / rest state
|
||||
|
||||
Scenario: A+V mode falls back to audio and video when av records are thin
|
||||
Given the fixture catalog has fewer than the pool size of native "av" records near the knob point
|
||||
When I set the mode to "av"
|
||||
Then the pool includes "audio" and "video" records so it is not starved
|
||||
|
||||
Scenario Outline: Each content mode restricts the pool to eligible records
|
||||
When I set the mode to "<mode>"
|
||||
Then every candidate in the pool is eligible for "<mode>"
|
||||
|
||||
Examples:
|
||||
| mode |
|
||||
| audio |
|
||||
| video |
|
||||
| av |
|
||||
@@ -0,0 +1,27 @@
|
||||
Feature: Simulator service and API
|
||||
As an operator
|
||||
I want to run the simulator locally in one container and drive it over a small API
|
||||
So that the browser X-ray and its behavior are testable without the physical stack
|
||||
|
||||
Scenario: The service runs locally in a container
|
||||
When I start the simulator container
|
||||
Then the X-ray is reachable in a browser on localhost
|
||||
And no external network access is required
|
||||
|
||||
Scenario: Selecting returns the pick and the ranked pool
|
||||
When I POST a valid selection request with dials and mode "av"
|
||||
Then the response contains a picked record
|
||||
And the response contains the ranked pool with a distance and rank per candidate
|
||||
|
||||
Scenario: None mode returns the void over the API
|
||||
When I POST a selection request with mode "none"
|
||||
Then the response pick is null
|
||||
And the response pool is empty
|
||||
|
||||
Scenario: Invalid dial values are rejected
|
||||
When I POST a selection request with a dial value outside 0 to 4
|
||||
Then the API responds with a validation error
|
||||
|
||||
Scenario: Catalog metadata is available for the header
|
||||
When I GET the catalog metadata
|
||||
Then the response reports record counts, a per-mode breakdown, and the proposed/approved split
|
||||
Reference in New Issue
Block a user