Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 10 additions & 17 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -815,7 +815,7 @@ auto-generated per-PR notes; this file is the curated, human-readable history.
structure with its inputs.
- `dashboard/model/presentation-resolver.ts` — the ONE canonical presentation
resolver (base panel → selected/`defaultVariant` variant patch → tile
override → final validation), shared by the authoring session, saved-query
override → final validation), shared by Dashboard viewing, saved-query
mutation validation, and tests. Enforces the #280 rules: a missing selected
variant name fails (no silent fallback), neither a variant nor an override
may change `panel.cfg.type`, deleting a required field fails final
Expand All @@ -826,31 +826,24 @@ auto-generated per-PR notes; this file is the curated, human-readable history.
document normalization (orphan-placement pruning), size-hint-derived initial
placement, and `resolveActiveLayoutPlugin` (flow@1, or an unsupported
primary with a valid flow@1 fallback, else a load failure).
- `dashboard/application/dashboard-authoring-session.ts` +
`dashboard-commands.ts` + `dashboard-query-resolver.ts` — a
`DashboardAuthoringSession` owning one editable draft (a signal), its
in-memory `draftVersion`, dirty/selection state, and commit/export. Every
membership/placement/layout change is a fallible, atomic typed command
- `dashboard/application/dashboard-commands.ts` +
`dashboard-query-resolver.ts` — fallible, atomic typed commands for every
membership/placement/layout change
(`add-query`, `add-query-instance`, `remove-tile`, `move-tile`,
`update-tile`, `update-placement`, `change-layout`): clone the draft → apply
to an isolated candidate → normalize through the layout plugin → validate
the whole candidate workspace (structure/references/roles/limits, then
presentation resolution) → replace the draft only when valid; a failed
command leaves the previous draft byte-for-byte unchanged. `draftVersion`
guards stale async commands (`dashboard-command-stale`) and is separate from
the persisted `revision`, which increments once per successful repository
commit (never on a command, preview, or export). The **star rewires** to
`toggleMembership`, which maps to an `add-query`/`remove-tile` command and
dual-writes `spec.favorite` — no direct signal/document mutation of
membership anywhere.
to an isolated candidate → return diagnostics or the candidate without
mutating the input. The unused session wrapper was removed before gaining
a production caller; future non-DOM authoring must be built against the
shared `mutateWorkspace` pipeline.
- `dashboard/application/saved-query-mutation.ts` — `planSavedQueryMutation`
constructs and validates a complete candidate workspace for every
saved-query mutation (delete or replace), rejecting an invalidating one
unless the caller supplies an atomic repair (remove affected tiles/filters,
switch to another variant, or remap references), then commits mutation +
repair as one candidate.
A new `check:arch` rule keeps `dashboard/application` off the Dashboard UI.
Live Workbench wiring of the session/star UI lands with the phase-4 viewer.
Live Workbench wiring consumes these commands through the shared workspace
mutation path.

- **Dashboard read-only viewer runtime + normative `flow@1` layout** (#286,
phase 4 of #280). New pure/application modules, gated at the per-file
Expand Down
247 changes: 0 additions & 247 deletions src/dashboard/application/dashboard-authoring-session.ts

This file was deleted.

23 changes: 9 additions & 14 deletions src/dashboard/application/dashboard-commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@
// it clones the current draft, applies one command to that isolated candidate,
// and returns either the candidate dashboard (plus an optional command value)
// or the command-level diagnostics that make it impossible — WITHOUT mutating
// the input draft. The session (dashboard-authoring-session.ts) then normalizes
// through the layout plugin, runs whole-workspace + presentation validation,
// and only replaces its draft when the candidate is fully valid.
// the input draft. Callers remain responsible for normalizing through the
// layout plugin, running whole-workspace + presentation validation, and only
// adopting a candidate when it is fully valid.
//
// Command-level failures caught here are the ones no downstream validation
// could see because they concern the command itself, not the resulting shape:
// a missing query, a duplicate default instance, a missing tile, an
// out-of-range move index (never silently clamped, per #280), an unsafe tile
// patch, or an invalid placement. Role/limit/reference/presentation failures
// are left to the session's validation stage.
// are left to the caller's validation stage.

import { cloneJson } from '../../core/saved-query.js';
import { diagnostic } from '../model/workspace-diagnostics.js';
Expand Down Expand Up @@ -42,11 +42,6 @@ export type DashboardCommand =
| { type: 'update-placement'; tileId: string; placement: Record<string, unknown> }
| { type: 'change-layout'; layout: DashboardLayoutDocumentV1 };

/** The #280 command-result union, `draftVersion` included on both arms. */
export type DashboardCommandResult<T = void> =
| { ok: true; value: T; document: DashboardDocumentV1; draftVersion: number }
| { ok: false; diagnostics: WorkspaceDiagnostic[]; draftVersion: number };

export interface ApplyCommandContext {
resolver: QueryResolver;
/** Mint a fresh tile instance ID. */
Expand Down Expand Up @@ -169,8 +164,8 @@ function applyCommandToClone(
const index = tileIndex(tiles, command.tileId);
if (index < 0) return failWith(missingTile(command.tileId));
tiles.splice(index, 1);
// The removed tile's placement becomes an orphan; layout normalization
// (the session's next step) prunes it.
// The removed tile's placement becomes an orphan; the caller's layout
// normalization step prunes it.
return { ok: true, dashboard, value: undefined };
}

Expand Down Expand Up @@ -222,7 +217,7 @@ function applyCommandToClone(
const index = tileIndex(tiles, command.tileId);
if (index < 0) return failWith(missingTile(command.tileId));
// Validated through the ACTIVE engine's own plugin (`ctx.plugin`,
// resolved by the session from the current document — grid: span
// resolved by the caller from the current document — grid: span
// 1..12; flow: span 1..3) rather than a hardcoded flow plugin (#291).
const placementDiags = ctx.plugin.validatePlacement(command.placement,
['layout', 'items', command.tileId]);
Expand All @@ -231,9 +226,9 @@ function applyCommandToClone(
return { ok: true, dashboard, value: undefined };
}

// change-layout: the load/version/fallback failures are the session's
// change-layout: load/version/fallback failures belong to the caller's
// registry-resolution step; a normalization that produces an invalid
// document is the session's validation step. Here we install the new
// document belongs to the caller's validation step. Here we install the new
// layout AND, when it is an engine switch (#291 owner decision 3),
// convert placements between engines:
// - flow -> grafana-grid: seed every tile's grid placement from its
Expand Down
2 changes: 1 addition & 1 deletion src/dashboard/layouts/flow-layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ type Path = (string | number)[];
const isObject = (value: unknown): value is Record<string, unknown> =>
!!value && typeof value === 'object' && !Array.isArray(value);

/** The narrow layout-plugin contract the authoring session/commands use. */
/** The narrow layout-plugin contract the authoring commands and callers use. */
export interface DashboardLayoutPlugin {
readonly type: string;
readonly version: number;
Expand Down
Loading