13 KiB
#region ScenarioRegistry.Spec [C:3] [TYPE ADR] [SEMANTICS spec,requirements,scenario,registry,lifecycle,management] @BRIEF First-class persistence, catalog, and lifecycle management for user-created dashboard test scenarios — the missing "find / open / manage" layer after generation. @RELATION DEPENDS_ON -> [Doc.Adr.ADR0001] @RELATION DEPENDS_ON -> [Doc.Adr.ADR0002] @RELATION DEPENDS_ON -> [Doc.Adr.ADR0005] @RELATION DEPENDS_ON -> [DashboardScenarioModel.Spec] @RELATION DEPENDS_ON -> [DashboardScenarioUi.Spec] @RELATION DEPENDS_ON -> [DatasetLineageBlastRadius.Spec] @RATIONALE 038/039 cover scenario generation but not post-creation management; a scenario is currently an ephemeral agent-session artifact materialized to git, with no registry, list, detail, versions, or lifecycle status. Without a persisted source of truth, the editor (043) and runner (044) have nothing stable to read from. @REJECTED Treating a scenario as a git-file-only artifact without a registry — rejected because search/filter/ownership/status/stale-detection/versions need a queryable projection, and a UI without a registry would be a facade over nonexistent runtime (the exact anti-pattern 042 exists to prevent). @REJECTED Hard-deleting a scenario that has run history — rejected because audit trail and reproducibility must survive; the canonical lifecycle terminal is archive.
Navigation (DSA Indexer keywords)
@SEMANTICS: spec, requirements, feature, scenario, registry, lifecycle, catalog, revision, stale
Feature Branch: 042-dashboard-scenario-registry
Created: 2026-08-07 | Status: Draft
Input: "Provide a first-class Scenario Registry: persistent storage, list/search/filter, scenario detail, ownership, lifecycle statuses, immutable revisions, clone/archive, and stale detection driven by dashboard/lineage change. This is the source of truth consumed by the Scenario Editor (043) and Scenario Execution Engine (044)."
User Scenarios
Story 1 — Find a Saved Scenario (P1)
Why P1: After "Save" the user falls into a void; finding scenarios across dashboards is the entry point for edit and run.
Independent Test: Create a persisted scenario fixture and verify the registry list returns it with dashboard, status, last run, and health, filterable by search/dashboard/status.
Acceptance:
- Given scenarios are persisted When the registry opens Then each row shows name, dashboard, lifecycle status, validation status, last run, last successful run, health, and last modified.
- Given the user searches or filters When the query changes Then results filter by name/tags, dashboard, status, and owner without a full reload.
- Given no scenarios exist When the registry renders Then an empty state with a "Create scenario" CTA is shown.
Story 2 — Open a Scenario Detail (P1)
Why P1: Viewing one scenario as a first-class page (not a chat session) is required for edit, run, and versioning.
Independent Test: Open a scenario by id and verify the detail shows overview metadata, steps, parameters, baselines, revisions, runs, and artifacts tabs from the registry, not from agent events.
Acceptance:
- Given a scenario id When the detail page opens Then it renders Overview (status, revision, dashboard, coverage, automation split, last run) plus Steps / Parameters / Baselines / Runs / Revisions / Artifacts tabs.
- Given the frontend requests a scenario by id When it loads Then
GET /dashboard-testing/scenarios/{id}returns the persisted graph (this route currently missing — 039 calls it but backend 404s). - Given a scenario detail is open When data is stale Then a refresh banner with the newer version is shown, never a silent overwrite.
Story 3 — Version a Scenario With Immutable Revisions (P1)
Why P1: Reproducibility requires runs pinned to an immutable revision snapshot, not to a moving "current" state.
Independent Test: Resolve a persisted scenario and verify a new revision is created with parent link and that a run references the exact revision hash.
Acceptance:
- Given a scenario is edited When a new revision is saved Then
scenario_id(UUID),revision_id(UUID), andparent_revision_idform a linked chain, and acandidaterevision is created without advancingcurrent_revision. - Given a run starts When it references a scenario Then it pins
scenario_id+revision_id+content_hashso later edits never change what was executed. - Given revisions exist When the user requests a diff Then the change set between two revisions is returned (added/changed/removed).
Story 4 — Detect Scenario Staleness (P2)
Why P2: A scenario pins chart/filter/metric references; when the dashboard or lineage changes the scenario becomes stale and must be revalidated.
Independent Test: Feed a dashboard release diff / lineage change and verify affected scenarios transition to NEEDS_REVALIDATION with a listed reason.
Acceptance:
- Given a dashboard release removes a referenced chart When the registry recomputes staleness Then scenarios referencing it become
NEEDS_REVALIDATIONorBLOCKEDwith a reason. - Given a lineage blast-radius change touches a scenario's dataset When evaluated Then the affected scenarios are flagged via 041 blast radius, not by blind rescan.
- Given a scenario is stale When the user runs it Then run is warning-gated (or blocked per policy), never silently executed against changed truth.
Story 5 — Manage Scenario Lifecycle (P2)
Why P2: Scenarios need explicit lifecycle states and operations (archive/restore/clone) with RBAC, and never destructive deletion of run history.
Independent Test: Perform clone, archive, and restore on a scenario and verify state transitions and RBAC enforcement.
Acceptance:
- Given the lifecycle states DRAFT/READY/STALE/DISABLED/DEPRECATED/ARCHIVED When transitions are requested Then only valid transitions apply and each is audit-logged.
- Given a scenario has run history When a user requests deletion Then the operation is archive (audit trail preserved), not hard delete; hard delete requires explicit admin scope and an empty-run guard.
- Given clone is requested When executed Then a new scenario with its own id, derived revisions, and cleared owner lineage is created from a snapshot.
Edge & Failure Cases
| # | Scenario | Category | Expected Behavior | Recovery |
|---|---|---|---|---|
| E1 | Scenario not found | data | 404 NOT_FOUND | Navigate to list |
| E2 | Concurrent edit on same revision | concurrency | 409 with current revision hash | Reload / compare / discard |
| E3 | Revision not found in chain | data | 404 for the revision | Show revision history |
| E4 | Stale scenario run attempted | data-quality | Warning-gate or block | Revalidate / mark pending |
| E5 | Blast-radius engine unavailable | integration | Staleness skipped with warning, never false-stale | Retry / manual revalidate |
| E6 | RBAC denied on archive/clone | auth | 403 permission_denied, no confirm control | Contact admin |
| E7 | Dashboard deleted while scenario references it | data-integrity | Scenario flagged orphan/blocked with reason | Re-target / deprecate |
| E8 | 429 rate limit on list/query | throttling | Retry-After honored | Wait and retry |
Requirements
Functional
- SCREG-FR-001: The system MUST persist scenarios as a first-class entity in a
scenario_registryprojection (scenario_id, revision, dashboard, environment compatibility, owner, tags, lifecycle status, validation status, last run, health). - SCREG-FR-002: The system MUST expose
GET /dashboard-testing/scenarios(list/search/filter by name, tag, dashboard, status, owner) andGET /dashboard-testing/scenarios/{id}(detail). - SCREG-FR-003: Every executable-graph edit MUST create a new immutable
candidaterevision (revision_idUUID +content_hash+parent_revision_id); the content hash includes the 038 Verification Program (SQL/DSL/assertion/AgentEvaluationSpec) but excludes ParameterBindings and other runtime state. Runs pin an explicit revision snapshot. Entity-metadata edits (name/description/tags) MUST NOT create an executable revision (content_hash unchanged). - SCREG-FR-003a:
ActivateCurrentRevisionMUST be a separate atomic operation. It may advancecurrent_revisiononly after deterministic eligibility checks and the recorded 036 delegated-authority decision or ActionApprovalGate; saving a revision never silently changes an automation target. - SCREG-FR-004: Scenario detail MUST be loadable by id independent of any agent session (not event-driven).
- SCREG-FR-005: Lifecycle states MUST include DRAFT, READY, STALE, NEEDS_REVALIDATION, BLOCKED, DISABLED, DEPRECATED, ARCHIVED; only valid transitions apply and are audit-logged.
- SCREG-FR-006: The system MUST support lifecycle operations clone, rename, archive, restore, and RBAC-scoped delete (archive-only when run history exists).
- SCREG-FR-007: Staleness MUST be computed from dashboard release diffs and 041 lineage blast radius; affected scenarios transition to NEEDS_REVALIDATION/BLOCKED with a reason.
- SCREG-FR-008: RBAC MUST distinguish scenario:view, scenario:create, scenario:edit, scenario:archive; editing a scenario does NOT grant running it.
- SCREG-FR-009: Hard delete MUST be forbidden for scenarios with run history; terminal lifecycle is archive to preserve audit reproducibility.
- SCREG-FR-010: Registry health and staleness findings MUST emit an idempotent 036 InvestigationSignal, from which 047 may link an Investigation Queue item. An analyst opens a persistent agent case explicitly; signal production MUST NOT auto-start an agent conversation or tool action.
- SCREG-FR-011: A delegated agent MAY create/save validated immutable revisions and portfolio operations through the same server-owned handle, ACL, lifecycle and outbox contracts as an analyst. Risky lifecycle/publish actions remain subject to their ActionApprovalGate policy.
- SCREG-FR-012: Registry workflow, stale recovery and lifecycle actions MUST use persistent pages, inline panels or agent cases; modal/dialog interaction MUST NOT be required.
Key Entities
- ScenarioRegistryEntry: Queryable projection of a scenario: name, description, dashboard, environment compatibility, owner, tags, current revision, lifecycle status, validation status, last run, last successful run, health, last modified, baseline compatibility.
- ScenarioRevision: Immutable snapshot of a
DashboardTestScenariograph (revision_idUUID +content_hash+parent_revision_id); later saves arecandidate, and only explicit activation makes one revisioncurrent.scenario_idis a UUID assigned at Save;scenario_keyis the semantic slug. - ScenarioLifecycleState: DRAFT / READY / STALE / NEEDS_REVALIDATION / BLOCKED / DISABLED / DEPRECATED / ARCHIVED with a valid-transition map.
- ScenarioStalenessSignal: Cause of staleness (dashboard release diff, lineage blast-radius change, removed reference) with severity and affected scenario ids.
Success Criteria
- SC-001: A persisted scenario is listable, searchable, and filterable and renders a full detail page from the registry within 200ms of data load.
- SC-002:
GET /scenarios/{id}returns the persisted graph for 100% of fixture scenarios (fixes the current missing-route 404). - SC-003: Every run references an immutable revision snapshot; editing a scenario after a run never changes what was executed.
- SC-004: 100% of fixture lineage/dashboard changes that invalidate a referenced chart/filter/dataset mark the scenario NEEDS_REVALIDATION/BLOCKED with a reason.
- SC-005: No scenario with run history can be hard-deleted; archive is the terminal state preserving the audit trail.
Clarifications
Session 2026-08-07
- Q: Is 042 a UI feature or backend? → A: Backend-first (persistence, CRUD, versions, staleness) + a thin registry/detail list surface consumed by 043/045; heavy view/edit UI is 043, run monitor is 045.
- Q: Where does the scenario graph live? → A: In a
scenario_registryprojection (canonical) plus the materializedscenario.yaml/runner.plan.jsonin the dashboard git repo (artifacts). The registry is the queryable source of truth. - Q: How does staleness work? → A: From dashboard release
StructureDiff(037) and 041 lineage blast radius; never a blind rescan. - Q: Is delete ever allowed? → A: Only archive for scenarios with runs; hard delete requires admin scope and empty run history.
Implementation Status & MVP Debt (audit 2026-08-07)
Facts (code check):
- 🔴 No
Scenario/scenario_registryDB table exists; scenario lives as in-memory Pydantic + materialized git artifact. - 🔴
GET /dashboard-testing/scenarios/{id}is called by frontendgetScenarioDraftbut does not exist on the backend (only POST routes) → 404. - 🟡
scenario_key+content_hashare emitted by 038;revision_id/content_hashare the registry revision base — the technical basis for revisions is present, but there is no revision storage/UX.
#endregion ScenarioRegistry.Spec