diff --git a/docs/en/demo-cookbook.md b/docs/en/demo-cookbook.md index b5dd770f..f2fde707 100644 --- a/docs/en/demo-cookbook.md +++ b/docs/en/demo-cookbook.md @@ -36,6 +36,7 @@ Use it when you want to move from "I can see the Demo" to "I know which code and | `builtin-hosted-controls-route` / Built-in hosted Controls/Panel route | Authoring | `src/AsterGraph.Avalonia/Hosting/AsterGraphHostBuilder.cs` (`UseDefaultWorkbench`, `BuildAvaloniaView`); `src/AsterGraph.Avalonia/Hosting/AsterGraphWorkbenchOptions.cs` (`AsterGraphWorkbenchPanelState`); `src/AsterGraph.Editor/Runtime/GraphEditorCommandDescriptorSnapshot.cs` (`RecoveryHint`) | `src/AsterGraph.Demo/DemoGraphFactory.cs` (`hosted-controls-panel`); `tests/AsterGraph.Editor.Tests/GraphEditorViewTests.cs` (`AuthoringToolsChrome_ProjectsStockSelectionLayoutActions`); `tests/AsterGraph.Demo.Tests/DemoCookbookNavigationTests.cs` (`PART_CookbookWorkspaceNavigationPanel`) | [Demo Cookbook](./demo-cookbook.md); catalog path: `docs/en/demo-cookbook.md`; evidence: `HOSTED_CONTROLS_PANEL_COMPOSITION_OK` | `HOSTED_CONTROLS_PANEL_COMPOSITION_OK`, `DESIGNER_WORKBENCH_AUTHORING_OK` | | `interaction-selection-marquee-route` / Interaction selection marquee route | Authoring | `src/AsterGraph.Editor/Runtime/IGraphEditorQueries.cs` (`GetSelectionRectangleSnapshot`); `src/AsterGraph.Avalonia/Controls/Internal/Overlay/NodeCanvasOverlayCoordinator.cs` (`UpdateMarqueeSelection`); `src/AsterGraph.Editor/Runtime/IGraphEditorCommands.cs` (`SelectAll`) | `src/AsterGraph.Demo/DemoGraphFactory.cs` (`selection-marquee-workbench`); `tests/AsterGraph.Editor.Tests/GraphEditorSelectionTransformContractsTests.cs` (`Queries_GetSelectionRectangleSnapshot_ReturnsNodesAndConnectionsInRectangle`); `tests/AsterGraph.Editor.Tests/NodeCanvasOverlayCoordinatorTests.cs` (`UpdateMarqueeSelection_WithFinalizeTrue_UsesBackendSelectionRectangleQuery`) | [Demo Cookbook](./demo-cookbook.md); catalog path: `docs/en/demo-cookbook.md`; evidence: `INTERACTION_SELECTION_MARQUEE_FIXTURE_OK` | `INTERACTION_SELECTION_MARQUEE_FIXTURE_OK`, `SELECTION_RECTANGLE_QUERY_OK`, `SELECTION_RECTANGLE_MARQUEE_OK` | | `interaction-lasso-screenshot-proof-route` / Interaction lasso screenshot proof route | Authoring | `src/AsterGraph.Avalonia/Controls/NodeCanvasSelectionMode.cs` (`Lasso`); `src/AsterGraph.Avalonia/Controls/NodeCanvas.axaml.cs` (`UpdateLassoFeedback`, `ClearLassoFeedback`) | `src/AsterGraph.Demo/DemoGraphFactory.cs` (`selection-marquee-workbench`); `tests/AsterGraph.Editor.Tests/NodeCanvasStandaloneTests.cs` (`LassoSelectionMode_RendersTransientFeedbackPathOnlyDuringDrag`); `tests/AsterGraph.Demo.Tests/CookbookShellVisualGateStates.json` (`shell-cookbook-lasso-screenshot-proof`) | [Demo Cookbook](./demo-cookbook.md); catalog path: `docs/en/demo-cookbook.md`; evidence: `LASSO_SCREENSHOT_PROOF_BOUNDARY_OK` | `LASSO_SCREENSHOT_PROOF_BOUNDARY_OK`, `NodeCanvasSelectionMode.Lasso`, `LassoSelectionMode_RendersTransientFeedbackPathOnlyDuringDrag` | +| `whiteboard-primitive-screenshot-proof-route` / Whiteboard primitive screenshot proof route | Authoring | `src/AsterGraph.Core/Models/GraphWhiteboardPrimitive.cs` (`GraphWhiteboardPrimitive`); `src/AsterGraph.Core/Models/GraphWhiteboardPrimitiveRendererAdapter.cs` (`GraphWhiteboardPrimitiveRendererAdapter`); `src/AsterGraph.Core/Models/GraphWhiteboardPrimitivePersistenceDecision.cs` (`GraphWhiteboardPrimitivePersistenceDecision`) | `src/AsterGraph.Demo/DemoGraphFactory.cs` (`selection-marquee-workbench`); `tests/AsterGraph.Demo.Tests/CookbookScreenshotGateRoutes.json` (`cookbook-whiteboard-primitive-screenshot-proof`); `tests/AsterGraph.Demo.Tests/CookbookShellVisualGateStates.json` (`shell-cookbook-whiteboard-primitive-screenshot-proof`) | [Demo Cookbook](./demo-cookbook.md); catalog path: `docs/en/demo-cookbook.md`; evidence: `WHITEBOARD_PRIMITIVE_SCREENSHOT_GATE_OK` | `WHITEBOARD_PRIMITIVE_SCREENSHOT_GATE_OK`, `GraphWhiteboardPrimitivePersistenceDecision`, `cookbook-whiteboard-primitive-screenshot-proof` | | `interaction-keyboard-navigation-route` / Interaction keyboard navigation route | Authoring | `src/AsterGraph.Avalonia/Controls/NodeCanvas.axaml.cs` (`HandleCanvasKeyDown`, `TryHandleCanvasArrowKey`); `src/AsterGraph.Avalonia/Controls/Internal/Automation/NodeCanvasAutomationPeer.cs` (`NodeCanvasAutomationPeer`); `src/AsterGraph.Editor/Runtime/Internal/GraphEditorCommandDescriptorCatalog.cs` (`viewport.zoom-in`) | `src/AsterGraph.Demo/DemoGraphFactory.cs` (`keyboard-navigation-lab`); `tests/AsterGraph.Editor.Tests/NodeCanvasStandaloneTests.cs` (`ArrowKey_Nudge_MovesSelectedNodesWhenNodesAreSelected`, `ArrowKey_Navigate_SelectsNearestNodeWhenNoSelection`); `tests/AsterGraph.Editor.Tests/GraphEditorViewTests.cs` (`DefaultChromeMode_ExposesCanvasAndNodeAutomationPeers`) | [Demo Cookbook](./demo-cookbook.md); catalog path: `docs/en/demo-cookbook.md`; evidence: `INTERACTION_KEYBOARD_NAVIGATION_FIXTURE_OK` | `INTERACTION_KEYBOARD_NAVIGATION_FIXTURE_OK`, `CANVAS_KEYBOARD_NAVIGATION_OK`, `ARROW_KEY_NEAREST_NODE_OK` | | `interaction-host-event-inspector-route` / Interaction host event inspector route | ReviewHelp | `src/AsterGraph.Editor/Runtime/IGraphEditorEvents.cs` (`IGraphEditorEvents`); `src/AsterGraph.Editor/Runtime/Mutation/GraphEditorSessionMutation.cs` (`BeginMutation`, `FlushPendingEvents`) | `src/AsterGraph.Demo/DemoGraphFactory.cs` (`host-event-inspector`); `tests/AsterGraph.Editor.Tests/GraphEditorSessionTests.cs` (`SessionEvents_SubscribeAndUnsubscribe_DoNotLeakMemory`, `SessionEvents_SelectionChanges_AreThrottledToBoundedCadence`) | [Demo Cookbook](./demo-cookbook.md); catalog path: `docs/en/demo-cookbook.md`; evidence: `INTERACTION_HOST_EVENT_INSPECTOR_FIXTURE_OK` | `INTERACTION_HOST_EVENT_INSPECTOR_FIXTURE_OK`, `HOST_EVENT_SUBSCRIPTION_OK`, `EVENT_BATCHING_CADENCE_OK` | | `lifecycle-workspace-save-restore-route` / Lifecycle workspace save/restore route | DiagnosticsSupport | `src/AsterGraph.Editor/Runtime/IGraphEditorCommands.cs` (`SaveWorkspace`, `LoadWorkspace`); `src/AsterGraph.Editor/Runtime/Internal/GraphEditorCommandDescriptorCatalog.cs` (`workspace.save`) | `src/AsterGraph.Demo/DemoGraphFactory.cs` (`workspace-save-restore`); `tests/AsterGraph.Editor.Tests/GraphEditorDiagnosticsInspectionTests.cs` (`workspace.save.succeeded`); `tests/AsterGraph.Editor.Tests/GraphEditorServiceSeamsTests.cs` (`workspace.load.succeeded`) | [Demo Cookbook](./demo-cookbook.md); catalog path: `docs/en/demo-cookbook.md`; evidence: `LIFECYCLE_WORKSPACE_SAVE_RESTORE_FIXTURE_OK` | `LIFECYCLE_WORKSPACE_SAVE_RESTORE_FIXTURE_OK`, `WORKSPACE_SAVE_LOAD_DIAGNOSTICS_OK` | @@ -54,7 +55,7 @@ Use it when you want to move from "I can see the Demo" to "I know which code and `DEMO_COOKBOOK_ROUTE_COVERAGE_OK` proves the cookbook keeps route posture explicit: - `Supported SDK route`: `starter-host-route`, `interaction-selection-marquee-route`, `interaction-lasso-screenshot-proof-route`, `interaction-keyboard-navigation-route`, `lifecycle-clipboard-fragment-route`, and authoring contract routes. -- `Proof/demo route`: `builtin-minimap-workbench-route`, `builtin-background-grid-route`, `interaction-host-event-inspector-route`, `lifecycle-workspace-save-restore-route`, `lifecycle-validation-helper-route`, `groups-subgraphs-route`, `plugin-trust-route`, `diagnostics-support-route`, and `review-help-route`. +- `Proof/demo route`: `builtin-minimap-workbench-route`, `builtin-background-grid-route`, `whiteboard-primitive-screenshot-proof-route`, `interaction-host-event-inspector-route`, `lifecycle-workspace-save-restore-route`, `lifecycle-validation-helper-route`, `groups-subgraphs-route`, `plugin-trust-route`, `diagnostics-support-route`, and `review-help-route`. - `Hosted UI route`: copy `AsterGraphEditorFactory.Create(...)` + `AsterGraphAvaloniaViewFactory.Create(...)` or thin `AsterGraphHostBuilder` composition. - `Runtime-only route`: use `AsterGraphEditorFactory.CreateSession(...)` + `IGraphEditorSession` when the host owns its UI. - `Plugin route`: use `DiscoverPluginCandidates(...)` / `StagePluginPackage(...)` plus host-owned `PluginTrustPolicy`; it remains trusted in-process, not sandboxed. @@ -77,6 +78,7 @@ Category-derived route posture comes from `DemoCookbookRecipeCategory` and `Reci - `builtin-node-resizer-route` / Built-in NodeResizer route / Authoring: `src/AsterGraph.Avalonia/Controls/NodeResizer.cs` (`NodeResizer`); `src/AsterGraph.Editor/Runtime/IGraphEditorCommands.cs` (`TrySetNodeSize`); `src/AsterGraph.Avalonia/Hosting/AsterGraphBuiltInComponentCatalog.cs` (`builtin-node-resizer-route`); `src/AsterGraph.Demo/DemoGraphFactory.cs` (`validation-prevent-cycle`); `tests/AsterGraph.Editor.Tests/AsterGraphBuiltInNodeResizerTests.cs` (`NodeResizer_RendersStableHandlesAndCommitsResizeThroughSessionCommand`); `docs/en/demo-cookbook.md` (`BUILTIN_NODE_RESIZER_OK`). Built-in NodeResizer route: compose `NodeResizer` with an `IGraphEditorSession` and node id, then inspect TrySetNodeSize evidence. Supported seams live in `AsterGraph.Avalonia` NodeResizer and `AsterGraph.Editor` node surface size commands. Demo cookbook provides a screenshot fixture and proof anchors only; Demo does not add an alternate resize engine. NodeResizer coverage is limited to the public NodeResizer control, TrySetNodeSize, and screenshot fixture; it does not add an alternate resize engine, layout runtime, or workflow automation. - `interaction-selection-marquee-route`: Interaction selection marquee route: launch `selection-marquee-workbench` and inspect GetSelectionRectangleSnapshot plus marquee-selection proof evidence. Supported seams live in `AsterGraph.Editor` selection query/command contracts and `AsterGraph.Avalonia` overlay coordinator controls. Demo cookbook provides a rectangular fixture and screenshot-gate coverage only; Demo does not add another selection model or hit-test path. - `interaction-lasso-screenshot-proof-route`: Interaction lasso screenshot proof route: launch `selection-marquee-workbench`, set `NodeCanvasSelectionMode.Lasso`, and capture the active transient lasso overlay. Supported seams live in `AsterGraph.Avalonia` NodeCanvas lasso feedback and the existing `AsterGraph.Editor` selection contracts. Demo cookbook provides a screenshot proof fixture and manifest state only; Demo does not add toolbar UX, eraser behavior, drawing primitives, persistence, or a renderer rewrite. +- `whiteboard-primitive-screenshot-proof-route`: Whiteboard primitive screenshot proof route: launch `selection-marquee-workbench` and capture `cookbook-whiteboard-primitive-screenshot-proof` plus `shell-cookbook-whiteboard-primitive-screenshot-proof` metadata. Supported seams remain internal `AsterGraph.Core` whiteboard primitive model, renderer adapter, and persistence decision contracts plus existing Demo screenshot gate manifests. Demo cookbook provides proof-route plumbing only; Demo does not add a public drawing API, pointer coordinator, toolbar, eraser, persisted whiteboard primitive state, GraphDocument schema change, renderer rewrite, UI redesign, or full whiteboard parity. - `interaction-keyboard-navigation-route`: Interaction keyboard navigation route: launch `keyboard-navigation-lab` and inspect NodeCanvas arrow-key, viewport shortcut, and automation peer evidence. Supported seams live in `AsterGraph.Avalonia` canvas controls and `AsterGraph.Editor` command descriptor/shortcut contracts. Demo cookbook provides a spatial fixture and screenshot-gate coverage only; Demo does not add a separate input model or accessibility framework. - `interaction-host-event-inspector-route`: Interaction host event inspector route: launch `host-event-inspector` and inspect IGraphEditorEvents plus mutation batching evidence. Supported seams live in `AsterGraph.Editor` session event contracts and mutation batching internals. Demo cookbook provides a host-observable fixture and screenshot-gate coverage only; Demo does not add telemetry, remote sync, or a new event broker. - `lifecycle-workspace-save-restore-route`: Lifecycle workspace save/restore route: launch `workspace-save-restore` and inspect SaveWorkspace, LoadWorkspace, and workspace diagnostics evidence. Supported seams live in `AsterGraph.Editor` workspace command contracts, command descriptors, and diagnostics surfaces. Demo cookbook provides a save/restore fixture and screenshot-gate coverage only; Demo does not add a persistence engine or storage policy. @@ -102,6 +104,7 @@ Category-derived route posture comes from `DemoCookbookRecipeCategory` and `Reci - `builtin-node-resizer-route`: NodeResizer coverage is limited to the public NodeResizer control, TrySetNodeSize, and screenshot fixture; it does not add an alternate resize engine, layout runtime, or workflow automation. - `interaction-selection-marquee-route`: Selection marquee fixture coverage is limited to existing selection query, command, and overlay contracts; it does not add a spatial index, alternate selection model, or custom hit-test runtime. - `interaction-lasso-screenshot-proof-route`: Lasso screenshot proof coverage is limited to existing lasso selection activation, transient overlay feedback, Cookbook scene capture, and shell visual metadata; the explicit exclusions remain no toolbar UX, no eraser behavior, no drawing primitives, no persistence, no strict pixel baselines, no retained API removal, and no full whiteboard parity. +- `whiteboard-primitive-screenshot-proof-route`: Whiteboard primitive screenshot proof coverage is limited to route metadata, shell state, non-overlap visual proof, and existing internal whiteboard decisions; it does not add public drawing APIs, pointer coordinators, toolbar activation, eraser behavior, persisted whiteboard primitive state, GraphDocument schema changes, strict pixel baselines, retained API removal, or full whiteboard parity. - `interaction-keyboard-navigation-route`: Keyboard navigation fixture coverage is limited to existing Avalonia canvas controls, automation peers, and command shortcut contracts; it does not add a custom input framework or full accessibility provider suite. - `interaction-host-event-inspector-route`: Host event fixture coverage is bounded to existing session event contracts and mutation batching; it does not add telemetry, remote sync, time-based throttling, or a separate event broker. - `lifecycle-workspace-save-restore-route`: Workspace lifecycle fixture coverage is limited to existing save/load commands, command descriptors, and diagnostics; it does not add a persistence engine, cloud sync, storage policy, or migration runtime. @@ -143,6 +146,7 @@ Route boundary strings for the v0.79 catalog: - `GraphOperations`: built-in screenshot fixtures use `minimap-workbench`, `background-grid-density`, `hosted-controls-panel`, `keyboard-navigation-lab`, `host-event-inspector`, `selection-marquee-workbench`, `clipboard-fragment-roundtrip`, and `validation-prevent-cycle` so Cookbook routes capture distinct graph documents instead of reusing the same scene. - `GraphOperations`: interaction screenshot fixtures use `selection-marquee-workbench`, `keyboard-navigation-lab`, and `host-event-inspector` so selection, keyboard, and event routes capture distinct graph documents. - `GraphOperations`: Phase 536 / GitHub #195 / `avalonia-node-map-uvd` adds `cookbook-interaction-lasso-screenshot-proof` on `selection-marquee-workbench` as a bounded lasso screenshot proof route, not a new graph fixture family. +- `GraphOperations`: Phase 550 / GitHub #223 / `avalonia-node-map-wjr` adds `cookbook-whiteboard-primitive-screenshot-proof` on `selection-marquee-workbench` as a bounded whiteboard primitive visual proof route, not a new drawing fixture family. - `GraphOperations`: lifecycle screenshot fixtures use `workspace-save-restore`, `clipboard-fragment-roundtrip`, and `validation-prevent-cycle` so save/load, clipboard, and validation helper routes capture distinct graph documents. - `GraphOperations`: `LAYOUT_PROVIDER_EVIDENCE_EXPANSION` keeps layout evidence synchronous and host-owned through `UseLayoutProvider(...)` / `IGraphLayoutProvider.CreateLayoutPlan(GraphLayoutRequest)`. `GraphLayoutRequest`, `GraphLayoutPlan`, `PreviewLayoutPlan`, `TryApplyLayoutPlan`, `TryApplyLayoutRequest`, `TrySnapSelectedNodesToGrid`, and `TrySnapAllNodesToGrid` stay on `IGraphEditorSession` commands and are verified through `GraphEditorLayoutProviderSeamTests`, `LAYOUT_PROVIDER_SEAM_OK`, `LAYOUT_PREVIEW_APPLY_CANCEL_OK`, and `LAYOUT_UNDO_TRANSACTION_OK`. This proof does not make layout execution asynchronous or cancellable. - `NodeMetadata`: metadata and trust evidence stay anchored to recipe code or proof markers. @@ -152,6 +156,7 @@ Route boundary strings for the v0.79 catalog: - `SupportEvidence`: each route keeps support claims tied to local proof or docs evidence. - `SupportEvidence`: built-in proof markers `BUILTIN_MINIMAP_WORKBENCH_OK`, `GRID_BACKGROUND_DENSITY_OK`, `HOSTED_CONTROLS_PANEL_COMPOSITION_OK`, `BUILTIN_STANDALONE_CONTROLS_OK`, `BUILTIN_STANDALONE_PANEL_OK`, `BUILTIN_NODE_TOOLBAR_OK`, `BUILTIN_EDGE_TOOLBAR_OK`, and `BUILTIN_NODE_RESIZER_OK` bind the batch to existing workbench and standalone surfaces. - `SupportEvidence`: interaction proof markers `INTERACTION_SELECTION_MARQUEE_FIXTURE_OK`, `LASSO_SCREENSHOT_PROOF_BOUNDARY_OK`, `INTERACTION_KEYBOARD_NAVIGATION_FIXTURE_OK`, and `INTERACTION_HOST_EVENT_INSPECTOR_FIXTURE_OK` bind the batch to existing interaction and event surfaces. +- `SupportEvidence`: whiteboard proof marker `WHITEBOARD_PRIMITIVE_SCREENSHOT_GATE_OK` binds the Phase 550 route to `GraphWhiteboardPrimitivePersistenceDecision`, `cookbook-whiteboard-primitive-screenshot-proof`, and `shell-cookbook-whiteboard-primitive-screenshot-proof`. - `SupportEvidence`: lifecycle proof markers `LIFECYCLE_WORKSPACE_SAVE_RESTORE_FIXTURE_OK`, `LIFECYCLE_CLIPBOARD_FRAGMENT_FIXTURE_OK`, `LIFECYCLE_VALIDATION_HELPER_FIXTURE_OK`, `WORKSPACE_SAVE_LOAD_DIAGNOSTICS_OK`, `CLIPBOARD_FRAGMENT_ROUNDTRIP_OK`, and `VALIDATION_HELPER_ROUTE_OK` bind the batch to existing lifecycle surfaces. - `HostCodeExample`: host-copy examples remain anchored to starter or consumer sample code. @@ -169,6 +174,7 @@ Route boundary strings for the v0.79 catalog: - `Selection`: marquee selection supports union and toggle modes through modifier keys (`UpdateMarqueeSelection`); arrow-key nudge moves selected nodes with shift for larger steps (`ArrowKey_Nudge_MovesSelectedNodesWhenNodesAreSelected`). - `Selection`: the runnable interaction fixtures keep rectangle selection (`selection-marquee-workbench`), arrow-key movement (`ArrowKey_Nudge_MovesSelectedNodesWhenNodesAreSelected`), and event cadence (`SessionEvents_SelectionChanges_AreThrottledToBoundedCadence`) source-backed. - `Selection`: Phase 536 keeps lasso screenshot proof source-backed through `NodeCanvasSelectionMode.Lasso`, `LassoSelectionMode_RendersTransientFeedbackPathOnlyDuringDrag`, `shell-cookbook-lasso-screenshot-proof`, and `full-window-shell-lasso-state`; it adds no toolbar UX, no eraser, no drawing primitives, no persistence, and no full whiteboard parity. +- `Selection`: Phase 550 keeps whiteboard primitive screenshot proof source-backed through `selection-marquee-workbench`, `WHITEBOARD_PRIMITIVE_SCREENSHOT_GATE_OK`, and `GraphWhiteboardPrimitivePersistenceDecision`; it adds no public drawing API, no pointer coordinator, no toolbar, no eraser, no persisted whiteboard primitive state, and no GraphDocument schema change. - `Connection`: the interaction fixtures keep selected-route and mutation effects inspectable through `INTERACTION_SELECTION_MARQUEE_FIXTURE_OK` and `IGraphEditorEvents`. - `Connection`: lifecycle fixtures keep fragment connection remapping and invalid route repair inspectable through `SessionCommands_RoundTripFragmentWorkspaceAndTemplateLibrary` and `validation.connection.route.reset`. - `Inspection`: hosted entry points, minimap cadence, trust decisions, support logs, and proof panels remain inspectable (`BuildAvaloniaApp`, `ToMiniMapBudgetMarker`, `PluginTrustDecision`, `RuntimeLogs`, `DemoHostMenuGroups.Proof`). @@ -177,6 +183,7 @@ Route boundary strings for the v0.79 catalog: - `Inspection`: automation peers expose node titles and canvas group structure (`DefaultChromeMode_ExposesCanvasAndNodeAutomationPeers`). - `Inspection`: interaction fixtures expose `GetSelectionRectangleSnapshot`, `keyboard-navigation-lab`, `host-event-inspector`, and `DefaultChromeMode_ExposesCanvasAndNodeAutomationPeers` as screenshot-backed cues. - `Inspection`: lasso proof artifacts expose `PART_NodeCanvas`, `shell-cookbook-lasso-screenshot-proof`, and `full-window-shell-lasso-state` as screenshot-backed cues for the active transient lasso path. +- `Inspection`: whiteboard primitive proof artifacts expose `cookbook-whiteboard-primitive-screenshot-proof`, `shell-cookbook-whiteboard-primitive-screenshot-proof`, `PART_NodeCanvas`, and `PART_CookbookWorkspaceRecipeContentPanel` as screenshot-backed route metadata and shell state cues for non-overlap review. - `Inspection`: lifecycle fixtures expose `workspace.save`, `workspace.save.succeeded`, `workspace.load.succeeded`, `astergraph.clipboard/v1`, `TryFocusValidationIssue`, and `validation-prevent-cycle` as screenshot-backed cues. - `ValidationRuntimeFeedback`: extension, runtime, review, and projection feedback stay local and source-backed (`PluginTrust`, `RuntimeDiagnosticEntry`, `ValidationFeedback`, `MINIMAP_LIGHTWEIGHT_PROJECTION_OK`). - `ValidationRuntimeFeedback`: built-in routes keep feedback source-backed through `ToMiniMapBudgetMarker`, `AuthoringToolsChrome_ProjectsStockSelectionLayoutActions`, `RecoveryHint`, `CreateCommandActions`, `CreateNodeActions`, `CreateConnectionActions`, and `TrySetNodeSize`. @@ -204,13 +211,13 @@ dotnet test tests/AsterGraph.Demo.Tests/AsterGraph.Demo.Tests.csproj --configura `DemoCookbookScreenshotGateTests` reads `tests/AsterGraph.Demo.Tests/CookbookScreenshotGateRoutes.json`, captures each route through the canonical scene PNG exporter, and writes PNG plus metadata under `artifacts/test-results/cookbook-screenshot-gate`. This scene gate proves graph document, viewport, node/edge rendering, and route metadata only; it does not capture the Demo window chrome, host drawer, left Cookbook navigation, overlays, or shell panels. Its metadata records `PngSha256` plus a Phase 512 `DriftMeasurement` object with `Policy=record-only`, `PngHashPurpose=drift-evidence`, `StrictPixelBaselineEnforced=false`, `HostRuntimeDescription`, `OsDescription`, and `ProcessArchitecture` so generated artifacts can be compared across hosts without enforcing strict pixel hash pass/fail. Add future Cookbook routes by appending manifest rows; do not edit the test internals for routine route coverage. -The same test class also includes a manifest-driven full-window shell visual gate. `tests/AsterGraph.Demo.Tests/CookbookShellVisualGateStates.json` currently captures `shell-cookbook-default-open`, `shell-cookbook-default-open-zh-cn`, `shell-cookbook-default-closed`, `shell-cookbook-default-closed-zh-cn`, `shell-runtime-diagnostics-open`, `shell-runtime-diagnostics-open-zh-cn`, `shell-runtime-diagnostics-closed`, the bounded Phase 508 flyout state `shell-cookbook-default-view-menu-flyout`, the bounded Phase 509 tooltip popup state `shell-cookbook-default-host-command-tooltip-popup`, the bounded Phase 510 context-menu state `shell-cookbook-default-canvas-context-menu`, and the bounded Phase 536 lasso state `shell-cookbook-lasso-screenshot-proof`. Each shell state declares its own language/theme metadata, so the gate now covers the English Cookbook drawer, the Chinese Cookbook drawer, the English and Chinese Cookbook closed-drawer shells, the English and Chinese runtime diagnostics drawers, the selected English runtime diagnostics closed-shell state, one English View menu flyout, one English disabled host-command tooltip popup, one English canvas context menu, and one English active lasso overlay capture. It opens the Demo in Avalonia headless Skia rendering, selects each shell state, captures the full window, and writes PNG plus metadata under `artifacts/test-results/cookbook-shell-visual-gate`. The metadata records `CaptureScope=full-window-shell-state` for base shell states, `CaptureScope=full-window-shell-flyout-state` for the flyout row, `CaptureScope=full-window-shell-popup-state` for the tooltip popup row, `CaptureScope=full-window-shell-context-menu-state` for the context-menu row, or `CaptureScope=full-window-shell-lasso-state` for the lasso row, requested and actual pixel dimensions, selected route and recipe, selected language/theme, covered shell parts, optional flyout menu part, covered flyout headers, optional popup target part, covered popup text, optional context-menu target part, covered context-menu headers, optional lasso target part, `NodeCanvasSelectionMode.Lasso`, `LassoSelectionMode_RendersTransientFeedbackPathOnlyDuringDrag`, non-transparent pixel count, distinct color count, `PngSha256`, and the same record-only `DriftMeasurement` host metadata used by the scene gate. +The same test class also includes a manifest-driven full-window shell visual gate. `tests/AsterGraph.Demo.Tests/CookbookShellVisualGateStates.json` currently captures `shell-cookbook-default-open`, `shell-cookbook-default-open-zh-cn`, `shell-cookbook-default-closed`, `shell-cookbook-default-closed-zh-cn`, `shell-runtime-diagnostics-open`, `shell-runtime-diagnostics-open-zh-cn`, `shell-runtime-diagnostics-closed`, the bounded Phase 508 flyout state `shell-cookbook-default-view-menu-flyout`, the bounded Phase 509 tooltip popup state `shell-cookbook-default-host-command-tooltip-popup`, the bounded Phase 510 context-menu state `shell-cookbook-default-canvas-context-menu`, the bounded Phase 536 lasso state `shell-cookbook-lasso-screenshot-proof`, and the bounded Phase 550 whiteboard primitive proof state `shell-cookbook-whiteboard-primitive-screenshot-proof`. Each shell state declares its own language/theme metadata, so the gate now covers the English Cookbook drawer, the Chinese Cookbook drawer, the English and Chinese Cookbook closed-drawer shells, the English and Chinese runtime diagnostics drawers, the selected English runtime diagnostics closed-shell state, one English View menu flyout, one English disabled host-command tooltip popup, one English canvas context menu, one English active lasso overlay capture, and one English whiteboard primitive proof route capture. It opens the Demo in Avalonia headless Skia rendering, selects each shell state, captures the full window, and writes PNG plus metadata under `artifacts/test-results/cookbook-shell-visual-gate`. The metadata records `CaptureScope=full-window-shell-state` for base and Phase 550 whiteboard primitive proof states, `CaptureScope=full-window-shell-flyout-state` for the flyout row, `CaptureScope=full-window-shell-popup-state` for the tooltip popup row, `CaptureScope=full-window-shell-context-menu-state` for the context-menu row, or `CaptureScope=full-window-shell-lasso-state` for the lasso row, requested and actual pixel dimensions, selected route and recipe, selected language/theme, covered shell parts, optional flyout menu part, covered flyout headers, optional popup target part, covered popup text, optional context-menu target part, covered context-menu headers, optional lasso target part, `NodeCanvasSelectionMode.Lasso`, `LassoSelectionMode_RendersTransientFeedbackPathOnlyDuringDrag`, non-transparent pixel count, distinct color count, `PngSha256`, and the same record-only `DriftMeasurement` host metadata used by the scene gate. The `shell-cookbook-default-open` and `shell-cookbook-default-open-zh-cn` states cover `PART_HostMenu`, `PART_HostShellSplitView`, `PART_CookbookWorkspaceNavigationPanel`, `PART_MainGraphEditorHost`, and `PART_CookbookWorkspaceRecipeContentPanel`. The `shell-cookbook-default-view-menu-flyout` state additionally opens `PART_ViewMenu`, writes `shell-cookbook-default-view-menu-flyout.png`, and requires the flyout headers `Show header`, `Show library`, `Show inspector`, `Show status bar`, `Show mini map`, and `Open view controls`. The `shell-cookbook-default-host-command-tooltip-popup` state opens the disabled undo command tooltip on `PART_HostCommand_history.undo`, writes `shell-cookbook-default-host-command-tooltip-popup.png`, records `full-window-shell-popup-state`, and requires the popup text `Nothing to undo yet.`. The `shell-cookbook-default-canvas-context-menu` state opens the default canvas context menu on `PART_NodeCanvas`, writes `shell-cookbook-default-canvas-context-menu.png`, records `full-window-shell-context-menu-state`, and requires the context-menu headers `Add Node`, `Fit View`, and `Reset View`. The `shell-cookbook-default-closed`, `shell-cookbook-default-closed-zh-cn`, and `shell-runtime-diagnostics-closed` states keep the required parts to closed-shell chrome only: `PART_HostMenu`, `PART_HostShellSplitView`, and `PART_MainGraphEditorHost`. The `shell-runtime-diagnostics-open` and `shell-runtime-diagnostics-open-zh-cn` states cover the runtime drawer with `PART_RuntimeSummarySection`, `PART_RuntimeInspectionSection`, `PART_RuntimeDiagnosticsSection`, and `PART_MainGraphEditorHost`. Phase 511 / GitHub #142 / `avalonia-node-map-9rq` adds only the two bounded `zh-CN` + `canonical-dark` shell-state rows: `shell-cookbook-default-closed-zh-cn` and `shell-runtime-diagnostics-open-zh-cn`. All states assert the expected drawer-open or drawer-closed state, minimum rendered size, nonblank pixels, distinct colors, output path, metadata source, declared language/theme, and required named shell parts. -The first gate route is `starter-host-route` with the `ai-pipeline` scenario, `1480x900` viewport metadata, English UI text, and the `canonical-dark` theme. The built-in batch adds `builtin-minimap-workbench-route` / `minimap-workbench`, `builtin-background-grid-route` / `background-grid-density`, `builtin-hosted-controls-route` / `hosted-controls-panel`, `builtin-standalone-controls-route` / `keyboard-navigation-lab`, `builtin-standalone-panel-route` / `host-event-inspector`, `builtin-node-toolbar-route` / `selection-marquee-workbench`, `builtin-edge-toolbar-route` / `clipboard-fragment-roundtrip`, and `builtin-node-resizer-route` / `validation-prevent-cycle`. The interaction batch adds `interaction-selection-marquee-route` / `selection-marquee-workbench`, `interaction-lasso-screenshot-proof-route` / `selection-marquee-workbench`, `interaction-keyboard-navigation-route` / `keyboard-navigation-lab`, and `interaction-host-event-inspector-route` / `host-event-inspector`. The lifecycle batch adds `lifecycle-workspace-save-restore-route` / `workspace-save-restore`, `lifecycle-clipboard-fragment-route` / `clipboard-fragment-roundtrip`, and `lifecycle-validation-helper-route` / `validation-prevent-cycle`; each manifest row records `expectedDocumentTitle`, `minimumNodeCount`, `minimumConnectionCount`, and `requiredNodeIds` so the gate proves distinct scene fixtures. Phase 536 / GitHub #195 / `avalonia-node-map-uvd` adds only the lasso screenshot proof rows `cookbook-interaction-lasso-screenshot-proof` and `shell-cookbook-lasso-screenshot-proof`; no strict pixel baseline, toolbar UX, eraser, drawing primitives, persistence, renderer rewrite, retained API removal, or full whiteboard parity is claimed. Attach the generated before/after PNG and `metadata.json` when a PR changes visuals. +The first gate route is `starter-host-route` with the `ai-pipeline` scenario, `1480x900` viewport metadata, English UI text, and the `canonical-dark` theme. The built-in batch adds `builtin-minimap-workbench-route` / `minimap-workbench`, `builtin-background-grid-route` / `background-grid-density`, `builtin-hosted-controls-route` / `hosted-controls-panel`, `builtin-standalone-controls-route` / `keyboard-navigation-lab`, `builtin-standalone-panel-route` / `host-event-inspector`, `builtin-node-toolbar-route` / `selection-marquee-workbench`, `builtin-edge-toolbar-route` / `clipboard-fragment-roundtrip`, and `builtin-node-resizer-route` / `validation-prevent-cycle`. The interaction batch adds `interaction-selection-marquee-route` / `selection-marquee-workbench`, `interaction-lasso-screenshot-proof-route` / `selection-marquee-workbench`, `whiteboard-primitive-screenshot-proof-route` / `selection-marquee-workbench`, `interaction-keyboard-navigation-route` / `keyboard-navigation-lab`, and `interaction-host-event-inspector-route` / `host-event-inspector`. The lifecycle batch adds `lifecycle-workspace-save-restore-route` / `workspace-save-restore`, `lifecycle-clipboard-fragment-route` / `clipboard-fragment-roundtrip`, and `lifecycle-validation-helper-route` / `validation-prevent-cycle`; each manifest row records `expectedDocumentTitle`, `minimumNodeCount`, `minimumConnectionCount`, and `requiredNodeIds` so the gate proves distinct scene fixtures. Phase 536 / GitHub #195 / `avalonia-node-map-uvd` adds only the lasso screenshot proof rows `cookbook-interaction-lasso-screenshot-proof` and `shell-cookbook-lasso-screenshot-proof`; no strict pixel baseline, toolbar UX, eraser, drawing primitives, persistence, renderer rewrite, retained API removal, or full whiteboard parity is claimed. Phase 550 / GitHub #223 / `avalonia-node-map-wjr` adds only the whiteboard primitive proof rows `cookbook-whiteboard-primitive-screenshot-proof` and `shell-cookbook-whiteboard-primitive-screenshot-proof`; it records route metadata, shell state, and non-overlap visual proof with `WHITEBOARD_PRIMITIVE_SCREENSHOT_GATE_OK`, but still claims no public drawing API, no pointer coordinator, no toolbar, no eraser, no persisted whiteboard primitive state, no GraphDocument schema change, no renderer rewrite, no UI redesign, and no full whiteboard parity. Attach the generated before/after PNG and `metadata.json` when a PR changes visuals. -CI posture: both the scene PNG gate and the full-window shell visual gate are wired into `AsterGraph.Demo.Tests`, so the normal net9 validation lane runs them. The scene gate checks deterministic artifact generation, PNG validity, route metadata, and minimum image invariants. The shell gate additionally fails when a full-window artifact is missing, blank, undersized, has the wrong drawer state, misses the expected language/theme metadata, no longer covers the expected shell parts, when the Phase 508 flyout row cannot open `PART_ViewMenu` and record its required headers, when the Phase 509 popup row cannot open `PART_HostCommand_history.undo` and record `Nothing to undo yet.`, when the Phase 510 context-menu row cannot open `PART_NodeCanvas` and record `Add Node`, `Fit View`, and `Reset View`, when the Phase 511 rows cannot record `zh-CN` + `canonical-dark` metadata for the closed Cookbook shell and runtime diagnostics drawer, or when the Phase 536 lasso row cannot open `PART_NodeCanvas`, set `NodeCanvasSelectionMode.Lasso`, record `full-window-shell-lasso-state`, and keep the `LassoSelectionMode_RendersTransientFeedbackPathOnlyDuringDrag` transient path visible for capture. Phase 512 / GitHub #143 / `avalonia-node-map-1j4` keeps both gates as record-only pixel-baseline drift measurement: generated metadata records `PngSha256`, `DriftMeasurement.Policy=record-only`, `PngHashPurpose=drift-evidence`, `StrictPixelBaselineEnforced=false`, `HostRuntimeDescription`, `OsDescription`, and `ProcessArchitecture`, with no strict pixel baseline enforcement. Broad context-menu coverage beyond the listed canvas row, broad popup coverage beyond the listed tooltip popup, broad language/theme variants beyond the listed shell states, visual redesign, runtime behavior changes, public API changes, retained API removal, and strict pixel baselines remain outside this gate; Phase 508 / GitHub #139 / `avalonia-node-map-2nu` only adds one bounded View menu flyout manifest state, Phase 509 / GitHub #140 / `avalonia-node-map-0ff` only adds one bounded host-command tooltip popup manifest state, Phase 510 / GitHub #141 / `avalonia-node-map-8lu` only adds one bounded canvas context-menu manifest state, Phase 511 / GitHub #142 / `avalonia-node-map-9rq` only adds two bounded `zh-CN` + `canonical-dark` shell-state rows, and Phase 536 / GitHub #195 / `avalonia-node-map-uvd` only adds one bounded lasso screenshot state. +CI posture: both the scene PNG gate and the full-window shell visual gate are wired into `AsterGraph.Demo.Tests`, so the normal net9 validation lane runs them. The scene gate checks deterministic artifact generation, PNG validity, route metadata, and minimum image invariants. The shell gate additionally fails when a full-window artifact is missing, blank, undersized, has the wrong drawer state, misses the expected language/theme metadata, no longer covers the expected shell parts, when the Phase 508 flyout row cannot open `PART_ViewMenu` and record its required headers, when the Phase 509 popup row cannot open `PART_HostCommand_history.undo` and record `Nothing to undo yet.`, when the Phase 510 context-menu row cannot open `PART_NodeCanvas` and record `Add Node`, `Fit View`, and `Reset View`, when the Phase 511 rows cannot record `zh-CN` + `canonical-dark` metadata for the closed Cookbook shell and runtime diagnostics drawer, when the Phase 536 lasso row cannot open `PART_NodeCanvas`, set `NodeCanvasSelectionMode.Lasso`, record `full-window-shell-lasso-state`, and keep the `LassoSelectionMode_RendersTransientFeedbackPathOnlyDuringDrag` transient path visible for capture, or when the Phase 550 whiteboard primitive row cannot record `cookbook-whiteboard-primitive-screenshot-proof`, `shell-cookbook-whiteboard-primitive-screenshot-proof`, `PART_NodeCanvas`, and `PART_CookbookWorkspaceRecipeContentPanel` as proof-only route metadata and shell state. Phase 512 / GitHub #143 / `avalonia-node-map-1j4` keeps both gates as record-only pixel-baseline drift measurement: generated metadata records `PngSha256`, `DriftMeasurement.Policy=record-only`, `PngHashPurpose=drift-evidence`, `StrictPixelBaselineEnforced=false`, `HostRuntimeDescription`, `OsDescription`, and `ProcessArchitecture`, with no strict pixel baseline enforcement. Broad context-menu coverage beyond the listed canvas row, broad popup coverage beyond the listed tooltip popup, broad language/theme variants beyond the listed shell states, visual redesign, runtime behavior changes, public API changes, retained API removal, and strict pixel baselines remain outside this gate; Phase 508 / GitHub #139 / `avalonia-node-map-2nu` only adds one bounded View menu flyout manifest state, Phase 509 / GitHub #140 / `avalonia-node-map-0ff` only adds one bounded host-command tooltip popup manifest state, Phase 510 / GitHub #141 / `avalonia-node-map-8lu` only adds one bounded canvas context-menu manifest state, Phase 511 / GitHub #142 / `avalonia-node-map-9rq` only adds two bounded `zh-CN` + `canonical-dark` shell-state rows, Phase 536 / GitHub #195 / `avalonia-node-map-uvd` only adds one bounded lasso screenshot state, and Phase 550 / GitHub #223 / `avalonia-node-map-wjr` only adds one bounded whiteboard primitive proof route/state pair. Phase 515 / GitHub #151 / `avalonia-node-map-t44` decides the strict pixel-baseline policy from that Phase 512 evidence: strict single-hash enforcement remains deferred. The reviewed evidence is two successful Phase 512 release-validation artifact sets, each with 25 metadata.json files under `artifacts/test-results`, where every generated scene and shell artifact recorded `PngSha256`, `DriftMeasurement`, `Policy=record-only`, `PngHashPurpose=drift-evidence`, `StrictPixelBaselineEnforced=false`, `HostRuntimeDescription`, `OsDescription`, and `ProcessArchitecture`. One same-path full-window shell popup artifact, `shell-cookbook-default-host-command-tooltip-popup`, produced different `PngSha256` values across those two successful Windows release-validation runs while the metadata stayed record-only. That is enough evidence to keep the current invariants, but not enough to make a brittle single-hash pass/fail gate. The trigger for a future pass/fail policy is repeatable no-drift evidence for the same route/state set across supported CI hosts, or an explicitly host-keyed or tolerant comparator with reviewed thresholds. diff --git a/docs/en/phase-0-reactflow-parity-audit.md b/docs/en/phase-0-reactflow-parity-audit.md index 61615c62..aa594f8a 100644 --- a/docs/en/phase-0-reactflow-parity-audit.md +++ b/docs/en/phase-0-reactflow-parity-audit.md @@ -242,6 +242,10 @@ Phase 548 is GitHub #219 / `avalonia-node-map-10p`, the first whiteboard primiti Phase 549 is GitHub #221 / `avalonia-node-map-3l6`, the whiteboard primitive persistence decision implementation gate. This internal Core policy slice records `WHITEBOARD_PRIMITIVE_PERSISTENCE_DECISION_GATE`: `GraphWhiteboardPrimitivePersistenceDecision` chooses a separate annotation surface and `ExcludedFromCurrentGraphDocumentSchema` for current whiteboard primitives, while keeping `GraphDocumentCompatibility`, `GraphDocumentSerializer`, and `CurrentSchemaVersion` graph-document scoped. The contract lists migration policy, compatibility tests, workspace persistence boundary tests, clipboard fragment boundary tests, and screenshot artifact boundary tests as required before any future GraphDocument schema change. Phase 549 is stacked after PR #220 and must not merge before Phase 548. It authorizes no GraphDocument schema change, no schema version bump, no workspace persistence behavior change, no public drawing API, no Avalonia pointer coordinator edits, no toolbar/tool activation, no eraser behavior, no screenshot manifest expansion, no Cookbook visual proof implementation, no UI redesign, no retained API removal, and no full React Flow whiteboard parity. +## Phase 550 Update + +Phase 550 is GitHub #223 / `avalonia-node-map-wjr`, the whiteboard primitive Cookbook screenshot implementation gate. This proof-only Demo/Cookbook slice records `WHITEBOARD_PRIMITIVE_COOKBOOK_SCREENSHOT_GATE`: `cookbook-whiteboard-primitive-screenshot-proof` and `shell-cookbook-whiteboard-primitive-screenshot-proof` reuse `selection-marquee-workbench` to provide whiteboard primitive visual proof with route metadata, shell state, and non-overlap evidence after the internal model, renderer adapter, and persistence decision gates. Phase 550 is stacked after PR #222 and must not merge before Phase 549. It authorizes no public drawing API, no pointer coordinator, no toolbar, no eraser, no persisted whiteboard primitive state, no GraphDocument schema change, no renderer rewrite, no UI redesign, no retained API removal, and no full React Flow whiteboard parity. + ## Phase 489 Update Phase 489 closed GitHub #101 / `avalonia-node-map-6sc` through PR #102 as a renderer virtualization design spike on branch `perf/renderer-virtualization-spike`. This slice was docs/tests only: it defined the proof contract required before any future ItemsRepeater/Skia-style renderer virtualization, background graph index, or graph-size claim expansion. It made no public API change and no runtime change. The current evidence remains viewport-budgeted scene projection/rendering, not a true renderer virtualization contract; `xlarge` stays telemetry-only. @@ -573,6 +577,8 @@ Phase 548 records the whiteboard primitive renderer adapter skeleton through Git Phase 549 records the whiteboard primitive persistence decision implementation gate through GitHub #221 / `avalonia-node-map-3l6`. It adds only the internal `GraphWhiteboardPrimitivePersistenceDecision` policy contract: current whiteboard primitives use a separate annotation surface, remain `ExcludedFromCurrentGraphDocumentSchema`, and do not change `GraphDocumentCompatibility`, `GraphDocumentSerializer`, `CurrentSchemaVersion`, workspace persistence, clipboard fragments, screenshot artifacts, or public drawing APIs. +Phase 550 records the whiteboard primitive Cookbook screenshot implementation gate through GitHub #223 / `avalonia-node-map-wjr`. It adds only the proof route/state pair `cookbook-whiteboard-primitive-screenshot-proof` and `shell-cookbook-whiteboard-primitive-screenshot-proof`, plus route metadata, shell state, and non-overlap visual proof for existing internal whiteboard primitive decisions; public drawing APIs, pointer coordinators, toolbar work, eraser behavior, persisted whiteboard primitive state, GraphDocument schema changes, renderer rewrites, UI redesign, and full React Flow whiteboard parity remain out of scope. + | GitHub | Bead | Title | Priority | Likely write set | Parallelism | | --- | --- | --- | --- | --- | --- | | #193 | `avalonia-node-map-8l6` | Phase 535: refresh post-lasso visual feedback parity queue | P2 | parity roadmap docs and focused docs tests | Current docs/test queue refresh. Blocks the next implementation wave because it replaces the stale Phase 534 current row with tracker-backed follow-ups. | @@ -590,7 +596,7 @@ Phase 549 records the whiteboard primitive persistence decision implementation g | #217 | `avalonia-node-map-rs0` | Phase 547: whiteboard primitive model skeleton | P2 | Core/Editor primitive model tests, internal model types, and renderer-neutral contract docs | Stacked after PR #216; do not merge before Phase 546. This slice adds the internal model skeleton only and must not touch renderer adapters, persistence/schema, pointer coordinators, screenshot manifests, toolbar UX, or eraser behavior. | | #219 | `avalonia-node-map-10p` | Phase 548: whiteboard primitive renderer adapter skeleton | P2 | renderer adapter tests, scene snapshot projection proof, and topmost primitive hit-testing evidence | Stacked after PR #218; do not merge before Phase 547. Must keep persistence/schema, Cookbook screenshot rows, pointer coordinators, and toolbar activation out of scope. | | #221 | `avalonia-node-map-3l6` | Phase 549: whiteboard primitive persistence decision implementation gate | P3 | persistence decision tests, schema/surface choice, migration criteria, and compatibility coverage | Stacked after PR #220; do not merge before Phase 548. Chooses separate annotation surface and keeps GraphDocument schema/version/workspace persistence unchanged before any saved whiteboard primitive claim. | -| TBD | TBD | Phase 550: whiteboard primitive Cookbook screenshot implementation gate | P3 | Cookbook screenshot implementation gate docs/tests, route metadata, shell state, and non-overlap visual proof | Candidate after model, renderer, and persistence decisions. Must add visual proof before any whiteboard visual parity claim. | +| #223 | `avalonia-node-map-wjr` | Phase 550: whiteboard primitive Cookbook screenshot implementation gate | P3 | Cookbook screenshot implementation gate docs/tests, route metadata, shell state, and non-overlap visual proof | Stacked after PR #222; do not merge before Phase 549. Adds the whiteboard primitive visual proof route/state pair only, with no public drawing API, pointer coordinator, toolbar, eraser, persisted whiteboard primitive state, GraphDocument schema change, renderer rewrite, UI redesign, or full whiteboard parity claim. | ## Recommended Parallel Worktree Plan @@ -706,4 +712,6 @@ Current coverage includes scene-level route captures plus ten manifest-driven fu - Phase 546 is GitHub #215 / `avalonia-node-map-0l9`; it records the post-Phase-545 whiteboard implementation queue refresh and `WHITEBOARD_POST_GATE_QUEUE_REFRESH` without runtime behavior changes, public API changes, production model/schema changes, renderer-layer changes, screenshot manifest expansion, pointer coordinator edits, UI redesign, drawing tool implementation, eraser behavior, toolbar work, retained API removal, or full React Flow whiteboard parity. - Phase 547 is GitHub #217 / `avalonia-node-map-rs0`; it records `WHITEBOARD_PRIMITIVE_MODEL_SKELETON` with internal Core model types and focused Core/Editor contract tests only. Renderer adapters, persistence/schema, pointer coordinators, toolbar/tool activation, eraser behavior, screenshot manifests, Cookbook visual proof, public API claim expansion, retained API removal, and full React Flow whiteboard parity remain out of scope. - Phase 548 is GitHub #219 / `avalonia-node-map-10p`; it records `WHITEBOARD_PRIMITIVE_RENDERER_ADAPTER_SKELETON` with internal Core renderer-neutral projection and hit-testing tests only. GraphDocument schema changes, public drawing API, Avalonia renderer rewrites, NodeCanvasConnectionSceneRenderer edits, pointer coordinators, toolbar/tool activation, eraser behavior, persistence/schema work, screenshot manifests, Cookbook visual proof, retained API removal, and full React Flow whiteboard parity remain out of scope. -- Product code remains out of scope for Phase 478, Phase 484, Phase 490, Phase 491, Phase 492, Phase 493, Phase 494, Phase 495, Phase 497, Phase 498, Phase 499, Phase 500, Phase 501, Phase 502, Phase 503, Phase 504, Phase 505, Phase 506, Phase 507, Phase 508, Phase 509, Phase 510, Phase 511, Phase 512, Phase 513, Phase 520, Phase 521, Phase 522, Phase 523, Phase 524, Phase 525, Phase 526, Phase 527, Phase 528, Phase 529, Phase 535, Phase 539, Phase 540, Phase 541, and Phase 546 unless a focused test proves a specific missing contract. +- Phase 549 is GitHub #221 / `avalonia-node-map-3l6`; it records `WHITEBOARD_PRIMITIVE_PERSISTENCE_DECISION_GATE` with the internal `GraphWhiteboardPrimitivePersistenceDecision` policy contract only. GraphDocument schema changes, schema version bumps, workspace persistence behavior changes, public drawing APIs, pointer coordinators, toolbar/tool activation, eraser behavior, screenshot manifest expansion, UI redesign, retained API removal, and full React Flow whiteboard parity remain out of scope. +- Phase 550 is GitHub #223 / `avalonia-node-map-wjr`; it records `WHITEBOARD_PRIMITIVE_COOKBOOK_SCREENSHOT_GATE` with `cookbook-whiteboard-primitive-screenshot-proof`, `shell-cookbook-whiteboard-primitive-screenshot-proof`, route metadata, shell state, non-overlap visual proof, and `WHITEBOARD_PRIMITIVE_SCREENSHOT_GATE_OK` only. Public drawing APIs, pointer coordinators, toolbar work, eraser behavior, persisted whiteboard primitive state, GraphDocument schema changes, renderer rewrites, UI redesign, retained API removal, and full React Flow whiteboard parity remain out of scope. +- Product code remains out of scope for Phase 478, Phase 484, Phase 490, Phase 491, Phase 492, Phase 493, Phase 494, Phase 495, Phase 497, Phase 498, Phase 499, Phase 500, Phase 501, Phase 502, Phase 503, Phase 504, Phase 505, Phase 506, Phase 507, Phase 508, Phase 509, Phase 510, Phase 511, Phase 512, Phase 513, Phase 520, Phase 521, Phase 522, Phase 523, Phase 524, Phase 525, Phase 526, Phase 527, Phase 528, Phase 529, Phase 535, Phase 539, Phase 540, Phase 541, Phase 546, and Phase 550 unless a focused test proves a specific missing contract. diff --git a/docs/zh-CN/demo-cookbook.md b/docs/zh-CN/demo-cookbook.md index 63c65414..c4102091 100644 --- a/docs/zh-CN/demo-cookbook.md +++ b/docs/zh-CN/demo-cookbook.md @@ -36,6 +36,7 @@ Demo cookbook 是面向 AsterGraph 评估者的“代码 + 演示”索引。它 | `builtin-hosted-controls-route` / Built-in hosted Controls/Panel route | Authoring | `src/AsterGraph.Avalonia/Hosting/AsterGraphHostBuilder.cs` (`UseDefaultWorkbench`、`BuildAvaloniaView`);`src/AsterGraph.Avalonia/Hosting/AsterGraphWorkbenchOptions.cs` (`AsterGraphWorkbenchPanelState`);`src/AsterGraph.Editor/Runtime/GraphEditorCommandDescriptorSnapshot.cs` (`RecoveryHint`) | `src/AsterGraph.Demo/DemoGraphFactory.cs` (`hosted-controls-panel`);`tests/AsterGraph.Editor.Tests/GraphEditorViewTests.cs` (`AuthoringToolsChrome_ProjectsStockSelectionLayoutActions`);`tests/AsterGraph.Demo.Tests/DemoCookbookNavigationTests.cs` (`PART_CookbookWorkspaceNavigationPanel`) | [Demo Cookbook](./demo-cookbook.md);catalog path: `docs/en/demo-cookbook.md`;evidence: `HOSTED_CONTROLS_PANEL_COMPOSITION_OK` | `HOSTED_CONTROLS_PANEL_COMPOSITION_OK`、`DESIGNER_WORKBENCH_AUTHORING_OK` | | `interaction-selection-marquee-route` / Interaction selection marquee route | Authoring | `src/AsterGraph.Editor/Runtime/IGraphEditorQueries.cs` (`GetSelectionRectangleSnapshot`);`src/AsterGraph.Avalonia/Controls/Internal/Overlay/NodeCanvasOverlayCoordinator.cs` (`UpdateMarqueeSelection`);`src/AsterGraph.Editor/Runtime/IGraphEditorCommands.cs` (`SelectAll`) | `src/AsterGraph.Demo/DemoGraphFactory.cs` (`selection-marquee-workbench`);`tests/AsterGraph.Editor.Tests/GraphEditorSelectionTransformContractsTests.cs` (`Queries_GetSelectionRectangleSnapshot_ReturnsNodesAndConnectionsInRectangle`);`tests/AsterGraph.Editor.Tests/NodeCanvasOverlayCoordinatorTests.cs` (`UpdateMarqueeSelection_WithFinalizeTrue_UsesBackendSelectionRectangleQuery`) | [Demo Cookbook](./demo-cookbook.md);catalog path: `docs/en/demo-cookbook.md`;evidence: `INTERACTION_SELECTION_MARQUEE_FIXTURE_OK` | `INTERACTION_SELECTION_MARQUEE_FIXTURE_OK`、`SELECTION_RECTANGLE_QUERY_OK`、`SELECTION_RECTANGLE_MARQUEE_OK` | | `interaction-lasso-screenshot-proof-route` / Interaction lasso screenshot proof route | Authoring | `src/AsterGraph.Avalonia/Controls/NodeCanvasSelectionMode.cs` (`Lasso`);`src/AsterGraph.Avalonia/Controls/NodeCanvas.axaml.cs` (`UpdateLassoFeedback`、`ClearLassoFeedback`) | `src/AsterGraph.Demo/DemoGraphFactory.cs` (`selection-marquee-workbench`);`tests/AsterGraph.Editor.Tests/NodeCanvasStandaloneTests.cs` (`LassoSelectionMode_RendersTransientFeedbackPathOnlyDuringDrag`);`tests/AsterGraph.Demo.Tests/CookbookShellVisualGateStates.json` (`shell-cookbook-lasso-screenshot-proof`) | [Demo Cookbook](./demo-cookbook.md);catalog path: `docs/en/demo-cookbook.md`;evidence: `LASSO_SCREENSHOT_PROOF_BOUNDARY_OK` | `LASSO_SCREENSHOT_PROOF_BOUNDARY_OK`、`NodeCanvasSelectionMode.Lasso`、`LassoSelectionMode_RendersTransientFeedbackPathOnlyDuringDrag` | +| `whiteboard-primitive-screenshot-proof-route` / Whiteboard primitive screenshot proof route | Authoring | `src/AsterGraph.Core/Models/GraphWhiteboardPrimitive.cs` (`GraphWhiteboardPrimitive`);`src/AsterGraph.Core/Models/GraphWhiteboardPrimitiveRendererAdapter.cs` (`GraphWhiteboardPrimitiveRendererAdapter`);`src/AsterGraph.Core/Models/GraphWhiteboardPrimitivePersistenceDecision.cs` (`GraphWhiteboardPrimitivePersistenceDecision`) | `src/AsterGraph.Demo/DemoGraphFactory.cs` (`selection-marquee-workbench`);`tests/AsterGraph.Demo.Tests/CookbookScreenshotGateRoutes.json` (`cookbook-whiteboard-primitive-screenshot-proof`);`tests/AsterGraph.Demo.Tests/CookbookShellVisualGateStates.json` (`shell-cookbook-whiteboard-primitive-screenshot-proof`) | [Demo Cookbook](./demo-cookbook.md);catalog path: `docs/en/demo-cookbook.md`;evidence: `WHITEBOARD_PRIMITIVE_SCREENSHOT_GATE_OK` | `WHITEBOARD_PRIMITIVE_SCREENSHOT_GATE_OK`、`GraphWhiteboardPrimitivePersistenceDecision`、`cookbook-whiteboard-primitive-screenshot-proof` | | `interaction-keyboard-navigation-route` / Interaction keyboard navigation route | Authoring | `src/AsterGraph.Avalonia/Controls/NodeCanvas.axaml.cs` (`HandleCanvasKeyDown`、`TryHandleCanvasArrowKey`);`src/AsterGraph.Avalonia/Controls/Internal/Automation/NodeCanvasAutomationPeer.cs` (`NodeCanvasAutomationPeer`);`src/AsterGraph.Editor/Runtime/Internal/GraphEditorCommandDescriptorCatalog.cs` (`viewport.zoom-in`) | `src/AsterGraph.Demo/DemoGraphFactory.cs` (`keyboard-navigation-lab`);`tests/AsterGraph.Editor.Tests/NodeCanvasStandaloneTests.cs` (`ArrowKey_Nudge_MovesSelectedNodesWhenNodesAreSelected`、`ArrowKey_Navigate_SelectsNearestNodeWhenNoSelection`);`tests/AsterGraph.Editor.Tests/GraphEditorViewTests.cs` (`DefaultChromeMode_ExposesCanvasAndNodeAutomationPeers`) | [Demo Cookbook](./demo-cookbook.md);catalog path: `docs/en/demo-cookbook.md`;evidence: `INTERACTION_KEYBOARD_NAVIGATION_FIXTURE_OK` | `INTERACTION_KEYBOARD_NAVIGATION_FIXTURE_OK`、`CANVAS_KEYBOARD_NAVIGATION_OK`、`ARROW_KEY_NEAREST_NODE_OK` | | `interaction-host-event-inspector-route` / Interaction host event inspector route | ReviewHelp | `src/AsterGraph.Editor/Runtime/IGraphEditorEvents.cs` (`IGraphEditorEvents`);`src/AsterGraph.Editor/Runtime/Mutation/GraphEditorSessionMutation.cs` (`BeginMutation`、`FlushPendingEvents`) | `src/AsterGraph.Demo/DemoGraphFactory.cs` (`host-event-inspector`);`tests/AsterGraph.Editor.Tests/GraphEditorSessionTests.cs` (`SessionEvents_SubscribeAndUnsubscribe_DoNotLeakMemory`、`SessionEvents_SelectionChanges_AreThrottledToBoundedCadence`) | [Demo Cookbook](./demo-cookbook.md);catalog path: `docs/en/demo-cookbook.md`;evidence: `INTERACTION_HOST_EVENT_INSPECTOR_FIXTURE_OK` | `INTERACTION_HOST_EVENT_INSPECTOR_FIXTURE_OK`、`HOST_EVENT_SUBSCRIPTION_OK`、`EVENT_BATCHING_CADENCE_OK` | | `lifecycle-workspace-save-restore-route` / Lifecycle workspace save/restore route | DiagnosticsSupport | `src/AsterGraph.Editor/Runtime/IGraphEditorCommands.cs` (`SaveWorkspace`、`LoadWorkspace`);`src/AsterGraph.Editor/Runtime/Internal/GraphEditorCommandDescriptorCatalog.cs` (`workspace.save`) | `src/AsterGraph.Demo/DemoGraphFactory.cs` (`workspace-save-restore`);`tests/AsterGraph.Editor.Tests/GraphEditorDiagnosticsInspectionTests.cs` (`workspace.save.succeeded`);`tests/AsterGraph.Editor.Tests/GraphEditorServiceSeamsTests.cs` (`workspace.load.succeeded`) | [Demo Cookbook](./demo-cookbook.md);catalog path: `docs/en/demo-cookbook.md`;evidence: `LIFECYCLE_WORKSPACE_SAVE_RESTORE_FIXTURE_OK` | `LIFECYCLE_WORKSPACE_SAVE_RESTORE_FIXTURE_OK`、`WORKSPACE_SAVE_LOAD_DIAGNOSTICS_OK` | @@ -54,7 +55,7 @@ Demo cookbook 是面向 AsterGraph 评估者的“代码 + 演示”索引。它 `DEMO_COOKBOOK_ROUTE_COVERAGE_OK` 证明 cookbook 明确区分路线姿态: - `Supported SDK route`: `starter-host-route`、`interaction-selection-marquee-route`、`interaction-lasso-screenshot-proof-route`、`interaction-keyboard-navigation-route`、`lifecycle-clipboard-fragment-route` 和 authoring contract routes。 -- `Proof/demo route`: `builtin-minimap-workbench-route`、`builtin-background-grid-route`、`interaction-host-event-inspector-route`、`lifecycle-workspace-save-restore-route`、`lifecycle-validation-helper-route`、`groups-subgraphs-route`、`plugin-trust-route`、`diagnostics-support-route` 和 `review-help-route`。 +- `Proof/demo route`: `builtin-minimap-workbench-route`、`builtin-background-grid-route`、`whiteboard-primitive-screenshot-proof-route`、`interaction-host-event-inspector-route`、`lifecycle-workspace-save-restore-route`、`lifecycle-validation-helper-route`、`groups-subgraphs-route`、`plugin-trust-route`、`diagnostics-support-route` 和 `review-help-route`。 - `Hosted UI route`: 复制 `AsterGraphEditorFactory.Create(...)` + `AsterGraphAvaloniaViewFactory.Create(...)`,或薄封装 `AsterGraphHostBuilder` 组合。 - `Runtime-only route`: 宿主持有自己的 UI 时使用 `AsterGraphEditorFactory.CreateSession(...)` + `IGraphEditorSession`。 - `Plugin route`: 使用 `DiscoverPluginCandidates(...)` / `StagePluginPackage(...)` 加宿主自有 `PluginTrustPolicy`;它仍是 trusted in-process,不是 sandbox。 @@ -77,6 +78,7 @@ Category-derived route posture 来自 `DemoCookbookRecipeCategory` 和 `Recipe.I - `builtin-node-resizer-route` / Built-in NodeResizer route / Authoring: `src/AsterGraph.Avalonia/Controls/NodeResizer.cs` (`NodeResizer`);`src/AsterGraph.Editor/Runtime/IGraphEditorCommands.cs` (`TrySetNodeSize`);`src/AsterGraph.Avalonia/Hosting/AsterGraphBuiltInComponentCatalog.cs` (`builtin-node-resizer-route`);`src/AsterGraph.Demo/DemoGraphFactory.cs` (`validation-prevent-cycle`);`tests/AsterGraph.Editor.Tests/AsterGraphBuiltInNodeResizerTests.cs` (`NodeResizer_RendersStableHandlesAndCommitsResizeThroughSessionCommand`);`docs/en/demo-cookbook.md` (`BUILTIN_NODE_RESIZER_OK`)。Built-in NodeResizer route: compose `NodeResizer` with an `IGraphEditorSession` and node id, then inspect TrySetNodeSize evidence. Supported seams live in `AsterGraph.Avalonia` NodeResizer and `AsterGraph.Editor` node surface size commands. Demo cookbook provides a screenshot fixture and proof anchors only; Demo does not add an alternate resize engine. NodeResizer coverage is limited to the public NodeResizer control, TrySetNodeSize, and screenshot fixture; it does not add an alternate resize engine, layout runtime, or workflow automation. - `interaction-selection-marquee-route`: Interaction selection marquee route: launch `selection-marquee-workbench` and inspect GetSelectionRectangleSnapshot plus marquee-selection proof evidence. Supported seams live in `AsterGraph.Editor` selection query/command contracts and `AsterGraph.Avalonia` overlay coordinator controls. Demo cookbook provides a rectangular fixture and screenshot-gate coverage only; Demo does not add another selection model or hit-test path. - `interaction-lasso-screenshot-proof-route`: Interaction lasso screenshot proof route: launch `selection-marquee-workbench`, set `NodeCanvasSelectionMode.Lasso`, and capture the active transient lasso overlay. Supported seams live in `AsterGraph.Avalonia` NodeCanvas lasso feedback and the existing `AsterGraph.Editor` selection contracts. Demo cookbook provides a screenshot proof fixture and manifest state only; Demo does not add toolbar UX, eraser behavior, drawing primitives, persistence, or a renderer rewrite. +- `whiteboard-primitive-screenshot-proof-route`: Whiteboard primitive screenshot proof route: launch `selection-marquee-workbench` and capture `cookbook-whiteboard-primitive-screenshot-proof` plus `shell-cookbook-whiteboard-primitive-screenshot-proof` metadata. Supported seams remain internal `AsterGraph.Core` whiteboard primitive model, renderer adapter, and persistence decision contracts plus existing Demo screenshot gate manifests. Demo cookbook provides proof-route plumbing only; Demo does not add a public drawing API, pointer coordinator, toolbar, eraser, persisted whiteboard primitive state, GraphDocument schema change, renderer rewrite, UI redesign, or full whiteboard parity. - `interaction-keyboard-navigation-route`: Interaction keyboard navigation route: launch `keyboard-navigation-lab` and inspect NodeCanvas arrow-key, viewport shortcut, and automation peer evidence. Supported seams live in `AsterGraph.Avalonia` canvas controls and `AsterGraph.Editor` command descriptor/shortcut contracts. Demo cookbook provides a spatial fixture and screenshot-gate coverage only; Demo does not add a separate input model or accessibility framework. - `interaction-host-event-inspector-route`: Interaction host event inspector route: launch `host-event-inspector` and inspect IGraphEditorEvents plus mutation batching evidence. Supported seams live in `AsterGraph.Editor` session event contracts and mutation batching internals. Demo cookbook provides a host-observable fixture and screenshot-gate coverage only; Demo does not add telemetry, remote sync, or a new event broker. - `lifecycle-workspace-save-restore-route`: Lifecycle workspace save/restore route: launch `workspace-save-restore` and inspect SaveWorkspace, LoadWorkspace, and workspace diagnostics evidence. Supported seams live in `AsterGraph.Editor` workspace command contracts, command descriptors, and diagnostics surfaces. Demo cookbook provides a save/restore fixture and screenshot-gate coverage only; Demo does not add a persistence engine or storage policy. @@ -102,6 +104,7 @@ Category-derived route posture 来自 `DemoCookbookRecipeCategory` 和 `Recipe.I - `builtin-node-resizer-route`: NodeResizer coverage is limited to the public NodeResizer control, TrySetNodeSize, and screenshot fixture; it does not add an alternate resize engine, layout runtime, or workflow automation. - `interaction-selection-marquee-route`: Selection marquee fixture coverage is limited to existing selection query, command, and overlay contracts; it does not add a spatial index, alternate selection model, or custom hit-test runtime. - `interaction-lasso-screenshot-proof-route`: Lasso screenshot proof coverage is limited to existing lasso selection activation, transient overlay feedback, Cookbook scene capture, and shell visual metadata; the explicit exclusions remain no toolbar UX, no eraser behavior, no drawing primitives, no persistence, no strict pixel baselines, no retained API removal, and no full whiteboard parity. +- `whiteboard-primitive-screenshot-proof-route`: Whiteboard primitive screenshot proof coverage is limited to route metadata, shell state, non-overlap visual proof, and existing internal whiteboard decisions; it does not add public drawing APIs, pointer coordinators, toolbar activation, eraser behavior, persisted whiteboard primitive state, GraphDocument schema changes, strict pixel baselines, retained API removal, or full whiteboard parity. - `interaction-keyboard-navigation-route`: Keyboard navigation fixture coverage is limited to existing Avalonia canvas controls, automation peers, and command shortcut contracts; it does not add a custom input framework or full accessibility provider suite. - `interaction-host-event-inspector-route`: Host event fixture coverage is bounded to existing session event contracts and mutation batching; it does not add telemetry, remote sync, time-based throttling, or a separate event broker. - `lifecycle-workspace-save-restore-route`: Workspace lifecycle fixture coverage is limited to existing save/load commands, command descriptors, and diagnostics; it does not add a persistence engine, cloud sync, storage policy, or migration runtime. @@ -143,6 +146,7 @@ v0.79 catalog 的 route boundary 字符串: - `GraphOperations`: built-in screenshot fixtures 使用 `minimap-workbench`、`background-grid-density`、`hosted-controls-panel`、`keyboard-navigation-lab`、`host-event-inspector`、`selection-marquee-workbench`、`clipboard-fragment-roundtrip` 和 `validation-prevent-cycle`,确保 Cookbook 路线捕获的是不同 graph document,而不是复用同一个场景。 - `GraphOperations`: interaction screenshot fixtures 使用 `selection-marquee-workbench`、`keyboard-navigation-lab` 和 `host-event-inspector`,确保 selection、keyboard 和 event 路线捕获不同 graph document。 - `GraphOperations`: Phase 536 / GitHub #195 / `avalonia-node-map-uvd` 在 `selection-marquee-workbench` 上增加 `cookbook-interaction-lasso-screenshot-proof`,作为有界 lasso screenshot proof route,而不是新的 graph fixture family。 +- `GraphOperations`: Phase 550 / GitHub #223 / `avalonia-node-map-wjr` 在 `selection-marquee-workbench` 上增加 `cookbook-whiteboard-primitive-screenshot-proof`,作为有界 whiteboard primitive visual proof route,而不是新的 drawing fixture family。 - `GraphOperations`: lifecycle screenshot fixtures 使用 `workspace-save-restore`、`clipboard-fragment-roundtrip` 和 `validation-prevent-cycle`,确保 save/load、clipboard 和 validation helper 路线捕获不同 graph document。 - `GraphOperations`: `LAYOUT_PROVIDER_EVIDENCE_EXPANSION` 表示 layout evidence 同步且由宿主拥有,入口是 `UseLayoutProvider(...)` / `IGraphLayoutProvider.CreateLayoutPlan(GraphLayoutRequest)`。`GraphLayoutRequest`、`GraphLayoutPlan`、`PreviewLayoutPlan`、`TryApplyLayoutPlan`、`TryApplyLayoutRequest`、`TrySnapSelectedNodesToGrid` 和 `TrySnapAllNodesToGrid` 继续走 `IGraphEditorSession` commands,并由 `GraphEditorLayoutProviderSeamTests`、`LAYOUT_PROVIDER_SEAM_OK`、`LAYOUT_PREVIEW_APPLY_CANCEL_OK` 和 `LAYOUT_UNDO_TRANSACTION_OK` 验证。这个 proof 不表示 layout execution 已变成异步或可取消。 - `NodeMetadata`: metadata 与 trust evidence 只锚定到 recipe 代码或 proof marker。 @@ -152,6 +156,7 @@ v0.79 catalog 的 route boundary 字符串: - `SupportEvidence`: 每条路线的支持声明都绑定到本地 proof 或 docs evidence。 - `SupportEvidence`: built-in proof marker `BUILTIN_MINIMAP_WORKBENCH_OK`、`GRID_BACKGROUND_DENSITY_OK`、`HOSTED_CONTROLS_PANEL_COMPOSITION_OK`、`BUILTIN_STANDALONE_CONTROLS_OK`、`BUILTIN_STANDALONE_PANEL_OK`、`BUILTIN_NODE_TOOLBAR_OK`、`BUILTIN_EDGE_TOOLBAR_OK` 和 `BUILTIN_NODE_RESIZER_OK` 把这批 recipe 绑定到现有 workbench 与 standalone surfaces。 - `SupportEvidence`: interaction proof marker `INTERACTION_SELECTION_MARQUEE_FIXTURE_OK`、`LASSO_SCREENSHOT_PROOF_BOUNDARY_OK`、`INTERACTION_KEYBOARD_NAVIGATION_FIXTURE_OK` 和 `INTERACTION_HOST_EVENT_INSPECTOR_FIXTURE_OK` 把这批 recipe 绑定到现有 interaction 和 event surfaces。 +- `SupportEvidence`: whiteboard proof marker `WHITEBOARD_PRIMITIVE_SCREENSHOT_GATE_OK` 把 Phase 550 route 绑定到 `GraphWhiteboardPrimitivePersistenceDecision`、`cookbook-whiteboard-primitive-screenshot-proof` 和 `shell-cookbook-whiteboard-primitive-screenshot-proof`。 - `SupportEvidence`: lifecycle proof marker `LIFECYCLE_WORKSPACE_SAVE_RESTORE_FIXTURE_OK`、`LIFECYCLE_CLIPBOARD_FRAGMENT_FIXTURE_OK`、`LIFECYCLE_VALIDATION_HELPER_FIXTURE_OK`、`WORKSPACE_SAVE_LOAD_DIAGNOSTICS_OK`、`CLIPBOARD_FRAGMENT_ROUNDTRIP_OK` 和 `VALIDATION_HELPER_ROUTE_OK` 把这批 recipe 绑定到现有 lifecycle surfaces。 - `HostCodeExample`: 可复制的宿主示例仍锚定到 starter 或 consumer sample 代码。 @@ -169,6 +174,7 @@ v0.79 catalog 的 route boundary 字符串: - `Selection`: marquee selection 通过 modifier key 支持 union 和 toggle 模式(`UpdateMarqueeSelection`);arrow-key nudge 在按住 shift 时移动更大步长(`ArrowKey_Nudge_MovesSelectedNodesWhenNodesAreSelected`)。 - `Selection`: 可运行 interaction fixture 让 rectangle selection(`selection-marquee-workbench`)、arrow-key movement(`ArrowKey_Nudge_MovesSelectedNodesWhenNodesAreSelected`)和 event cadence(`SessionEvents_SelectionChanges_AreThrottledToBoundedCadence`)保持 source-backed。 - `Selection`: Phase 536 通过 `NodeCanvasSelectionMode.Lasso`、`LassoSelectionMode_RendersTransientFeedbackPathOnlyDuringDrag`、`shell-cookbook-lasso-screenshot-proof` 和 `full-window-shell-lasso-state` 让 lasso screenshot proof 保持 source-backed;边界明确排除 no toolbar UX、no eraser、no drawing primitives、no persistence 和 no full whiteboard parity。 +- `Selection`: Phase 550 通过 `selection-marquee-workbench`、`WHITEBOARD_PRIMITIVE_SCREENSHOT_GATE_OK` 和 `GraphWhiteboardPrimitivePersistenceDecision` 让 whiteboard primitive screenshot proof 保持 source-backed;它不新增 public drawing API、pointer coordinator、toolbar、eraser、persisted whiteboard primitive state 或 GraphDocument schema change。 - `Connection`: interaction fixture 通过 `INTERACTION_SELECTION_MARQUEE_FIXTURE_OK` 和 `IGraphEditorEvents` 保持 selected-route 与 mutation effects 可检查。 - `Connection`: lifecycle fixture 通过 `SessionCommands_RoundTripFragmentWorkspaceAndTemplateLibrary` 与 `validation.connection.route.reset` 保持 fragment connection remapping 和 invalid route repair 可检查。 - `Inspection`: hosted entry points、minimap cadence、trust decisions、support logs 和 proof panels 都保持可检查(`BuildAvaloniaApp`、`ToMiniMapBudgetMarker`、`PluginTrustDecision`、`RuntimeLogs`、`DemoHostMenuGroups.Proof`)。 @@ -177,6 +183,7 @@ v0.79 catalog 的 route boundary 字符串: - `Inspection`: automation peers 向 accessibility tools 暴露节点标题和画布组结构(`DefaultChromeMode_ExposesCanvasAndNodeAutomationPeers`)。 - `Inspection`: interaction fixture 把 `GetSelectionRectangleSnapshot`、`keyboard-navigation-lab`、`host-event-inspector` 和 `DefaultChromeMode_ExposesCanvasAndNodeAutomationPeers` 作为 screenshot-backed cues。 - `Inspection`: lasso proof artifacts 把 `PART_NodeCanvas`、`shell-cookbook-lasso-screenshot-proof` 和 `full-window-shell-lasso-state` 作为 active transient lasso path 的 screenshot-backed cues。 +- `Inspection`: whiteboard primitive proof artifacts 把 `cookbook-whiteboard-primitive-screenshot-proof`、`shell-cookbook-whiteboard-primitive-screenshot-proof`、`PART_NodeCanvas` 和 `PART_CookbookWorkspaceRecipeContentPanel` 作为 non-overlap review 的 screenshot-backed route metadata 和 shell state cues。 - `Inspection`: lifecycle fixture 把 `workspace.save`、`workspace.save.succeeded`、`workspace.load.succeeded`、`astergraph.clipboard/v1`、`TryFocusValidationIssue` 和 `validation-prevent-cycle` 作为 screenshot-backed cues。 - `ValidationRuntimeFeedback`: extension、runtime、review 和 projection feedback 都保持本地、source-backed(`PluginTrust`、`RuntimeDiagnosticEntry`、`ValidationFeedback`、`MINIMAP_LIGHTWEIGHT_PROJECTION_OK`)。 - `ValidationRuntimeFeedback`: built-in route 继续通过 `ToMiniMapBudgetMarker`、`AuthoringToolsChrome_ProjectsStockSelectionLayoutActions`、`RecoveryHint`、`CreateCommandActions`、`CreateNodeActions`、`CreateConnectionActions` 和 `TrySetNodeSize` 保持 source-backed。 @@ -204,13 +211,13 @@ dotnet test tests/AsterGraph.Demo.Tests/AsterGraph.Demo.Tests.csproj --configura `DemoCookbookScreenshotGateTests` 会读取 `tests/AsterGraph.Demo.Tests/CookbookScreenshotGateRoutes.json`,通过 canonical scene PNG exporter 捕获每条路线,并把 PNG 与 metadata 写到 `artifacts/test-results/cookbook-screenshot-gate`。这个 scene gate 只证明 graph document、viewport、node/edge rendering 和 route metadata;它不捕获 Demo window chrome、host drawer、左侧 Cookbook navigation、overlays 或 shell panels。它的 metadata 会记录 `PngSha256`,并新增 Phase 512 `DriftMeasurement` object:`Policy=record-only`、`PngHashPurpose=drift-evidence`、`StrictPixelBaselineEnforced=false`、`HostRuntimeDescription`、`OsDescription` 和 `ProcessArchitecture`,用于跨 host 比较 generated artifacts,但不引入 strict pixel hash pass/fail。后续要增加 Cookbook 路线时,只追加 manifest 行;常规路线覆盖不要修改 test internals。 -同一个 test class 还包含 manifest-driven full-window shell visual gate。`tests/AsterGraph.Demo.Tests/CookbookShellVisualGateStates.json` 当前捕获 `shell-cookbook-default-open`、`shell-cookbook-default-open-zh-cn`、`shell-cookbook-default-closed`、`shell-cookbook-default-closed-zh-cn`、`shell-runtime-diagnostics-open`、`shell-runtime-diagnostics-open-zh-cn`、`shell-runtime-diagnostics-closed`、有界的 Phase 508 flyout state:`shell-cookbook-default-view-menu-flyout`、有界的 Phase 509 tooltip popup state:`shell-cookbook-default-host-command-tooltip-popup`、有界的 Phase 510 context-menu state:`shell-cookbook-default-canvas-context-menu`,以及有界的 Phase 536 lasso state:`shell-cookbook-lasso-screenshot-proof`。每个 shell state 都声明自己的 language/theme metadata,所以 gate 现在覆盖 English Cookbook drawer、Chinese Cookbook drawer、English 和 Chinese Cookbook closed-drawer shell、English 和 Chinese runtime diagnostics drawer、selected English runtime diagnostics closed-shell state、一条 English View menu flyout、一条 English disabled host-command tooltip popup、一条 English canvas context menu,以及一条 English active lasso overlay capture。它会用 Avalonia headless Skia rendering 打开 Demo、切到每个 shell state、捕获 full-window PNG,并把 PNG 与 metadata 写到 `artifacts/test-results/cookbook-shell-visual-gate`。metadata 会为基础 shell states 记录 `CaptureScope=full-window-shell-state`,为 flyout row 记录 `CaptureScope=full-window-shell-flyout-state`,为 tooltip popup row 记录 `CaptureScope=full-window-shell-popup-state`,为 context-menu row 记录 `CaptureScope=full-window-shell-context-menu-state`,为 lasso row 记录 `CaptureScope=full-window-shell-lasso-state`,并记录 requested/actual pixel dimensions、选中的 route 和 recipe、选中的 language/theme、覆盖到的 shell parts、可选 flyout menu part、covered flyout headers、可选 popup target part、covered popup text、可选 context-menu target part、covered context-menu headers、可选 lasso target part、`NodeCanvasSelectionMode.Lasso`、`LassoSelectionMode_RendersTransientFeedbackPathOnlyDuringDrag`、non-transparent pixel count、distinct color count、`PngSha256`,以及和 scene gate 相同的 record-only `DriftMeasurement` host metadata。 +同一个 test class 还包含 manifest-driven full-window shell visual gate。`tests/AsterGraph.Demo.Tests/CookbookShellVisualGateStates.json` 当前捕获 `shell-cookbook-default-open`、`shell-cookbook-default-open-zh-cn`、`shell-cookbook-default-closed`、`shell-cookbook-default-closed-zh-cn`、`shell-runtime-diagnostics-open`、`shell-runtime-diagnostics-open-zh-cn`、`shell-runtime-diagnostics-closed`、有界的 Phase 508 flyout state:`shell-cookbook-default-view-menu-flyout`、有界的 Phase 509 tooltip popup state:`shell-cookbook-default-host-command-tooltip-popup`、有界的 Phase 510 context-menu state:`shell-cookbook-default-canvas-context-menu`、有界的 Phase 536 lasso state:`shell-cookbook-lasso-screenshot-proof`,以及有界的 Phase 550 whiteboard primitive proof state:`shell-cookbook-whiteboard-primitive-screenshot-proof`。每个 shell state 都声明自己的 language/theme metadata,所以 gate 现在覆盖 English Cookbook drawer、Chinese Cookbook drawer、English 和 Chinese Cookbook closed-drawer shell、English 和 Chinese runtime diagnostics drawer、selected English runtime diagnostics closed-shell state、一条 English View menu flyout、一条 English disabled host-command tooltip popup、一条 English canvas context menu、一条 English active lasso overlay capture,以及一条 English whiteboard primitive proof route capture。它会用 Avalonia headless Skia rendering 打开 Demo、切到每个 shell state、捕获 full-window PNG,并把 PNG 与 metadata 写到 `artifacts/test-results/cookbook-shell-visual-gate`。metadata 会为基础 shell states 和 Phase 550 whiteboard primitive proof state 记录 `CaptureScope=full-window-shell-state`,为 flyout row 记录 `CaptureScope=full-window-shell-flyout-state`,为 tooltip popup row 记录 `CaptureScope=full-window-shell-popup-state`,为 context-menu row 记录 `CaptureScope=full-window-shell-context-menu-state`,为 lasso row 记录 `CaptureScope=full-window-shell-lasso-state`,并记录 requested/actual pixel dimensions、选中的 route 和 recipe、选中的 language/theme、覆盖到的 shell parts、可选 flyout menu part、covered flyout headers、可选 popup target part、covered popup text、可选 context-menu target part、covered context-menu headers、可选 lasso target part、`NodeCanvasSelectionMode.Lasso`、`LassoSelectionMode_RendersTransientFeedbackPathOnlyDuringDrag`、non-transparent pixel count、distinct color count、`PngSha256`,以及和 scene gate 相同的 record-only `DriftMeasurement` host metadata。 `shell-cookbook-default-open` 和 `shell-cookbook-default-open-zh-cn` 覆盖 `PART_HostMenu`、`PART_HostShellSplitView`、`PART_CookbookWorkspaceNavigationPanel`、`PART_MainGraphEditorHost` 和 `PART_CookbookWorkspaceRecipeContentPanel`。`shell-cookbook-default-view-menu-flyout` 还会打开 `PART_ViewMenu`,写出 `shell-cookbook-default-view-menu-flyout.png`,并要求 flyout headers:`Show header`、`Show library`、`Show inspector`、`Show status bar`、`Show mini map` 和 `Open view controls`。`shell-cookbook-default-host-command-tooltip-popup` 会打开 `PART_HostCommand_history.undo` 上的 disabled undo command tooltip,写出 `shell-cookbook-default-host-command-tooltip-popup.png`,记录 `full-window-shell-popup-state`,并要求 popup text:`Nothing to undo yet.`。`shell-cookbook-default-canvas-context-menu` 会打开 `PART_NodeCanvas` 上的 default canvas context menu,写出 `shell-cookbook-default-canvas-context-menu.png`,记录 `full-window-shell-context-menu-state`,并要求 context-menu headers:`Add Node`、`Fit View` 和 `Reset View`。`shell-cookbook-default-closed`、`shell-cookbook-default-closed-zh-cn` 和 `shell-runtime-diagnostics-closed` 只把 required parts 限定为 closed-shell chrome:`PART_HostMenu`、`PART_HostShellSplitView` 和 `PART_MainGraphEditorHost`。`shell-runtime-diagnostics-open` 和 `shell-runtime-diagnostics-open-zh-cn` 覆盖 runtime drawer 中的 `PART_RuntimeSummarySection`、`PART_RuntimeInspectionSection`、`PART_RuntimeDiagnosticsSection` 和 `PART_MainGraphEditorHost`。Phase 511 / GitHub #142 / `avalonia-node-map-9rq` 只新增两条有界 `zh-CN` + `canonical-dark` shell-state rows:`shell-cookbook-default-closed-zh-cn` 和 `shell-runtime-diagnostics-open-zh-cn`。所有 state 都会验证预期 drawer open/closed 状态、最小渲染尺寸、nonblank pixels、distinct colors、output path、metadata source、声明的 language/theme 和 required named shell parts。 -第一条 gate 路线是 `starter-host-route` + `ai-pipeline` scenario,记录 `1480x900` viewport metadata、English UI text 和 `canonical-dark` theme。Built-in batch 还加入 `builtin-minimap-workbench-route` / `minimap-workbench`、`builtin-background-grid-route` / `background-grid-density`、`builtin-hosted-controls-route` / `hosted-controls-panel`、`builtin-standalone-controls-route` / `keyboard-navigation-lab`、`builtin-standalone-panel-route` / `host-event-inspector`、`builtin-node-toolbar-route` / `selection-marquee-workbench`、`builtin-edge-toolbar-route` / `clipboard-fragment-roundtrip` 和 `builtin-node-resizer-route` / `validation-prevent-cycle`。Interaction batch 加入 `interaction-selection-marquee-route` / `selection-marquee-workbench`、`interaction-lasso-screenshot-proof-route` / `selection-marquee-workbench`、`interaction-keyboard-navigation-route` / `keyboard-navigation-lab` 和 `interaction-host-event-inspector-route` / `host-event-inspector`。Lifecycle batch 加入 `lifecycle-workspace-save-restore-route` / `workspace-save-restore`、`lifecycle-clipboard-fragment-route` / `clipboard-fragment-roundtrip` 和 `lifecycle-validation-helper-route` / `validation-prevent-cycle`;每个 manifest row 都记录 `expectedDocumentTitle`、`minimumNodeCount`、`minimumConnectionCount` 和 `requiredNodeIds`,确保 gate 证明这些是不同的 scene fixture。Phase 536 / GitHub #195 / `avalonia-node-map-uvd` 只新增 lasso screenshot proof rows:`cookbook-interaction-lasso-screenshot-proof` 和 `shell-cookbook-lasso-screenshot-proof`;边界明确排除 no strict pixel baseline、no toolbar UX、no eraser、no drawing primitives、no persistence、no renderer rewrite、no retained API removal 和 no full whiteboard parity。视觉 PR 需要附上生成的 before/after PNG 和 `metadata.json`。 +第一条 gate 路线是 `starter-host-route` + `ai-pipeline` scenario,记录 `1480x900` viewport metadata、English UI text 和 `canonical-dark` theme。Built-in batch 还加入 `builtin-minimap-workbench-route` / `minimap-workbench`、`builtin-background-grid-route` / `background-grid-density`、`builtin-hosted-controls-route` / `hosted-controls-panel`、`builtin-standalone-controls-route` / `keyboard-navigation-lab`、`builtin-standalone-panel-route` / `host-event-inspector`、`builtin-node-toolbar-route` / `selection-marquee-workbench`、`builtin-edge-toolbar-route` / `clipboard-fragment-roundtrip` 和 `builtin-node-resizer-route` / `validation-prevent-cycle`。Interaction batch 加入 `interaction-selection-marquee-route` / `selection-marquee-workbench`、`interaction-lasso-screenshot-proof-route` / `selection-marquee-workbench`、`whiteboard-primitive-screenshot-proof-route` / `selection-marquee-workbench`、`interaction-keyboard-navigation-route` / `keyboard-navigation-lab` 和 `interaction-host-event-inspector-route` / `host-event-inspector`。Lifecycle batch 加入 `lifecycle-workspace-save-restore-route` / `workspace-save-restore`、`lifecycle-clipboard-fragment-route` / `clipboard-fragment-roundtrip` 和 `lifecycle-validation-helper-route` / `validation-prevent-cycle`;每个 manifest row 都记录 `expectedDocumentTitle`、`minimumNodeCount`、`minimumConnectionCount` 和 `requiredNodeIds`,确保 gate 证明这些是不同的 scene fixture。Phase 536 / GitHub #195 / `avalonia-node-map-uvd` 只新增 lasso screenshot proof rows:`cookbook-interaction-lasso-screenshot-proof` 和 `shell-cookbook-lasso-screenshot-proof`;边界明确排除 no strict pixel baseline、no toolbar UX、no eraser、no drawing primitives、no persistence、no renderer rewrite、no retained API removal 和 no full whiteboard parity。Phase 550 / GitHub #223 / `avalonia-node-map-wjr` 只新增 whiteboard primitive proof rows:`cookbook-whiteboard-primitive-screenshot-proof` 和 `shell-cookbook-whiteboard-primitive-screenshot-proof`;它通过 `WHITEBOARD_PRIMITIVE_SCREENSHOT_GATE_OK` 记录 route metadata、shell state 和 non-overlap visual proof,但仍然不声明 public drawing API、pointer coordinator、toolbar、eraser、persisted whiteboard primitive state、GraphDocument schema change、renderer rewrite、UI redesign 或 full whiteboard parity。视觉 PR 需要附上生成的 before/after PNG 和 `metadata.json`。 -CI 姿态:scene PNG gate 和 full-window shell visual gate 都已经接入 `AsterGraph.Demo.Tests`,所以正常 net9 validation lane 会执行它们。scene gate 验证确定性 artifact 生成、PNG 有效性、route metadata 和最小图片不变量;shell gate 还会在 full-window artifact 缺失、空白、尺寸过小、drawer state 错误、缺少预期 language/theme metadata、未覆盖预期 shell parts、Phase 508 flyout row 无法打开 `PART_ViewMenu` 并记录 required headers、Phase 509 popup row 无法打开 `PART_HostCommand_history.undo` 并记录 `Nothing to undo yet.`、Phase 510 context-menu row 无法打开 `PART_NodeCanvas` 并记录 `Add Node`、`Fit View` 和 `Reset View`、Phase 511 rows 无法为 closed Cookbook shell 和 runtime diagnostics drawer 记录 `zh-CN` + `canonical-dark` metadata,或 Phase 536 lasso row 无法打开 `PART_NodeCanvas`、设置 `NodeCanvasSelectionMode.Lasso`、记录 `full-window-shell-lasso-state`,并在 capture 时保持 `LassoSelectionMode_RendersTransientFeedbackPathOnlyDuringDrag` transient path 可见时失败。Phase 512 / GitHub #143 / `avalonia-node-map-1j4` 把两个 gate 保持为 record-only pixel-baseline drift measurement:generated metadata 记录 `PngSha256`、`DriftMeasurement.Policy=record-only`、`PngHashPurpose=drift-evidence`、`StrictPixelBaselineEnforced=false`、`HostRuntimeDescription`、`OsDescription` 和 `ProcessArchitecture`,并明确 no strict pixel baseline enforcement。超出这条 canvas row 的 broad context-menu coverage、超出这条 tooltip popup 的广泛 popup coverage、超出列明 shell states 的广泛 language/theme variants、visual redesign、runtime behavior changes、public API changes、retained API removal 和 strict pixel baselines 仍不属于这个 gate;Phase 508 / GitHub #139 / `avalonia-node-map-2nu` 只新增一条有界的 View menu flyout manifest state,Phase 509 / GitHub #140 / `avalonia-node-map-0ff` 只新增一条有界的 host-command tooltip popup manifest state,Phase 510 / GitHub #141 / `avalonia-node-map-8lu` 只新增一条有界的 canvas context-menu manifest state,Phase 511 / GitHub #142 / `avalonia-node-map-9rq` 只新增两条有界的 `zh-CN` + `canonical-dark` shell-state rows,Phase 536 / GitHub #195 / `avalonia-node-map-uvd` 只新增一条有界 lasso screenshot state。 +CI 姿态:scene PNG gate 和 full-window shell visual gate 都已经接入 `AsterGraph.Demo.Tests`,所以正常 net9 validation lane 会执行它们。scene gate 验证确定性 artifact 生成、PNG 有效性、route metadata 和最小图片不变量;shell gate 还会在 full-window artifact 缺失、空白、尺寸过小、drawer state 错误、缺少预期 language/theme metadata、未覆盖预期 shell parts、Phase 508 flyout row 无法打开 `PART_ViewMenu` 并记录 required headers、Phase 509 popup row 无法打开 `PART_HostCommand_history.undo` 并记录 `Nothing to undo yet.`、Phase 510 context-menu row 无法打开 `PART_NodeCanvas` 并记录 `Add Node`、`Fit View` 和 `Reset View`、Phase 511 rows 无法为 closed Cookbook shell 和 runtime diagnostics drawer 记录 `zh-CN` + `canonical-dark` metadata,或 Phase 536 lasso row 无法打开 `PART_NodeCanvas`、设置 `NodeCanvasSelectionMode.Lasso`、记录 `full-window-shell-lasso-state`,并在 capture 时保持 `LassoSelectionMode_RendersTransientFeedbackPathOnlyDuringDrag` transient path 可见时失败;Phase 550 whiteboard primitive row 如果无法记录 `cookbook-whiteboard-primitive-screenshot-proof`、`shell-cookbook-whiteboard-primitive-screenshot-proof`、`PART_NodeCanvas` 和 `PART_CookbookWorkspaceRecipeContentPanel` 作为 proof-only route metadata 与 shell state,也会失败。Phase 512 / GitHub #143 / `avalonia-node-map-1j4` 把两个 gate 保持为 record-only pixel-baseline drift measurement:generated metadata 记录 `PngSha256`、`DriftMeasurement.Policy=record-only`、`PngHashPurpose=drift-evidence`、`StrictPixelBaselineEnforced=false`、`HostRuntimeDescription`、`OsDescription` 和 `ProcessArchitecture`,并明确 no strict pixel baseline enforcement。超出这条 canvas row 的 broad context-menu coverage、超出这条 tooltip popup 的广泛 popup coverage、超出列明 shell states 的广泛 language/theme variants、visual redesign、runtime behavior changes、public API changes、retained API removal 和 strict pixel baselines 仍不属于这个 gate;Phase 508 / GitHub #139 / `avalonia-node-map-2nu` 只新增一条有界的 View menu flyout manifest state,Phase 509 / GitHub #140 / `avalonia-node-map-0ff` 只新增一条有界的 host-command tooltip popup manifest state,Phase 510 / GitHub #141 / `avalonia-node-map-8lu` 只新增一条有界的 canvas context-menu manifest state,Phase 511 / GitHub #142 / `avalonia-node-map-9rq` 只新增两条有界的 `zh-CN` + `canonical-dark` shell-state rows,Phase 536 / GitHub #195 / `avalonia-node-map-uvd` 只新增一条有界 lasso screenshot state,Phase 550 / GitHub #223 / `avalonia-node-map-wjr` 只新增一组有界 whiteboard primitive proof route/state。 Phase 515 / GitHub #151 / `avalonia-node-map-t44` 基于 Phase 512 evidence 决定 strict pixel-baseline policy:strict single-hash enforcement 继续 deferred。已审查的证据是 two successful Phase 512 release-validation artifact sets,每组都在 `artifacts/test-results` 下包含 25 metadata.json files;每个 generated scene 和 shell artifact 都记录 `PngSha256`、`DriftMeasurement`、`Policy=record-only`、`PngHashPurpose=drift-evidence`、`StrictPixelBaselineEnforced=false`、`HostRuntimeDescription`、`OsDescription` 和 `ProcessArchitecture`。其中一条同路径 full-window shell popup artifact,`shell-cookbook-default-host-command-tooltip-popup`,在两次成功 Windows release-validation run 中产生了不同的 `PngSha256`,但 metadata 仍保持 record-only。这足以继续防守当前不变量,但不足以引入脆弱的 single-hash pass/fail gate。后续 pass/fail policy 的触发条件是:同一 route/state 集合在 supported CI hosts 上取得 repeatable no-drift evidence,或者先审查并落地一个明确的 host-keyed or tolerant comparator 和阈值。 diff --git a/docs/zh-CN/phase-0-reactflow-parity-audit.md b/docs/zh-CN/phase-0-reactflow-parity-audit.md index 8015c694..cdd37f7d 100644 --- a/docs/zh-CN/phase-0-reactflow-parity-audit.md +++ b/docs/zh-CN/phase-0-reactflow-parity-audit.md @@ -242,6 +242,10 @@ Phase 548 是 GitHub #219 / `avalonia-node-map-10p`,承接第一条 whiteboard Phase 549 是 GitHub #221 / `avalonia-node-map-3l6`,记录 whiteboard primitive persistence decision implementation gate。本 internal Core policy slice 记录 `WHITEBOARD_PRIMITIVE_PERSISTENCE_DECISION_GATE`:`GraphWhiteboardPrimitivePersistenceDecision` 为当前 whiteboard primitives 选择 separate annotation surface,并标记 `ExcludedFromCurrentGraphDocumentSchema`,同时继续把 `GraphDocumentCompatibility`、`GraphDocumentSerializer` 和 `CurrentSchemaVersion` 限定为 graph-document scope。该 contract 记录任何后续 GraphDocument schema change 前必须补齐 migration policy、compatibility tests、workspace persistence boundary tests、clipboard fragment boundary tests 和 screenshot artifact boundary tests。Phase 549 stacked after PR #220,不能早于 Phase 548 合并。它不授权 no GraphDocument schema change、no schema version bump、no workspace persistence behavior change、no public drawing API、no Avalonia pointer coordinator edits、no toolbar/tool activation、no eraser behavior、no screenshot manifest expansion、no Cookbook visual proof implementation、no UI redesign、no retained API removal 或 no full React Flow whiteboard parity。 +## Phase 550 更新 + +Phase 550 是 GitHub #223 / `avalonia-node-map-wjr`,记录 whiteboard primitive Cookbook screenshot implementation gate。本 proof-only Demo/Cookbook slice 记录 `WHITEBOARD_PRIMITIVE_COOKBOOK_SCREENSHOT_GATE`:`cookbook-whiteboard-primitive-screenshot-proof` 和 `shell-cookbook-whiteboard-primitive-screenshot-proof` 复用 `selection-marquee-workbench`,在 internal model、renderer adapter 和 persistence decision gates 之后提供带 route metadata、shell state 与 non-overlap evidence 的 whiteboard primitive visual proof。Phase 550 stacked after PR #222,不能早于 Phase 549 合并。它不授权 no public drawing API、no pointer coordinator、no toolbar、no eraser、no persisted whiteboard primitive state、no GraphDocument schema change、no renderer rewrite、no UI redesign、no retained API removal 或 no full React Flow whiteboard parity。 + ## Phase 489 更新 Phase 489 通过 PR #102 关闭 GitHub #101 / `avalonia-node-map-6sc`,完成 `perf/renderer-virtualization-spike` 分支上的 renderer virtualization design spike。本 slice 只做 docs/tests:先定义未来声明 ItemsRepeater/Skia-style renderer virtualization、background graph index 或扩大 graph-size claim 前必须满足的 proof contract。不做 public API change,也不做 runtime change。当前证据仍只支持 viewport-budgeted scene projection/rendering,不是真正的 renderer virtualization contract;`xlarge` 继续保持 telemetry-only。 @@ -573,6 +577,8 @@ Phase 548 记录 whiteboard primitive renderer adapter skeleton,通过 GitHub Phase 549 记录 whiteboard primitive persistence decision implementation gate,通过 GitHub #221 / `avalonia-node-map-3l6`。它只添加 internal `GraphWhiteboardPrimitivePersistenceDecision` policy contract:当前 whiteboard primitives 使用 separate annotation surface,保持 `ExcludedFromCurrentGraphDocumentSchema`,并且不修改 `GraphDocumentCompatibility`、`GraphDocumentSerializer`、`CurrentSchemaVersion`、workspace persistence、clipboard fragments、screenshot artifacts 或 public drawing APIs。 +Phase 550 记录 whiteboard primitive Cookbook screenshot implementation gate,通过 GitHub #223 / `avalonia-node-map-wjr`。它只添加 proof route/state pair:`cookbook-whiteboard-primitive-screenshot-proof` 和 `shell-cookbook-whiteboard-primitive-screenshot-proof`,并为现有 internal whiteboard primitive decisions 记录 route metadata、shell state 和 non-overlap visual proof;public drawing APIs、pointer coordinators、toolbar work、eraser behavior、persisted whiteboard primitive state、GraphDocument schema changes、renderer rewrites、UI redesign 和 full React Flow whiteboard parity 都保持 out of scope。 + | GitHub | Bead | 标题 | 优先级 | 可能 write set | 并行边界 | | --- | --- | --- | --- | --- | --- | | #193 | `avalonia-node-map-8l6` | Phase 535: refresh post-lasso visual feedback parity queue | P2 | parity roadmap docs 和 focused docs tests | Current docs/test queue refresh。它用 tracker-backed follow-ups 替换 stale Phase 534 current row,因此会阻塞下一批 implementation wave。 | @@ -590,7 +596,7 @@ Phase 549 记录 whiteboard primitive persistence decision implementation gate | #217 | `avalonia-node-map-rs0` | Phase 547: whiteboard primitive model skeleton | P2 | Core/Editor primitive model tests、internal model types 和 renderer-neutral contract docs | Stacked after PR #216;do not merge before Phase 546。本 slice 只添加 internal model skeleton,不得触碰 renderer adapters、persistence/schema、pointer coordinators、screenshot manifests、toolbar UX 或 eraser behavior。 | | #219 | `avalonia-node-map-10p` | Phase 548: whiteboard primitive renderer adapter skeleton | P2 | renderer adapter tests、scene snapshot projection proof 和 topmost primitive hit-testing evidence | Stacked after PR #218;do not merge before Phase 547。persistence/schema、Cookbook screenshot rows、pointer coordinators 和 toolbar activation 都保持 out of scope。 | | #221 | `avalonia-node-map-3l6` | Phase 549: whiteboard primitive persistence decision implementation gate | P3 | persistence decision tests、schema/surface choice、migration criteria 和 compatibility coverage | Stacked after PR #220;do not merge before Phase 548。本 slice 选择 separate annotation surface,并在任何 saved whiteboard primitive claim 前保持 GraphDocument schema/version/workspace persistence unchanged。 | -| TBD | TBD | Phase 550: whiteboard primitive Cookbook screenshot implementation gate | P3 | Cookbook screenshot implementation gate docs/tests、route metadata、shell state 和 non-overlap visual proof | model、renderer 与 persistence decisions 后的 candidate。任何 whiteboard visual parity claim 前必须先补 visual proof。 | +| #223 | `avalonia-node-map-wjr` | Phase 550: whiteboard primitive Cookbook screenshot implementation gate | P3 | Cookbook screenshot implementation gate docs/tests、route metadata、shell state 和 non-overlap visual proof | Stacked after PR #222;do not merge before Phase 549。本 slice 只添加 whiteboard primitive visual proof route/state pair,不做 public drawing API、pointer coordinator、toolbar、eraser、persisted whiteboard primitive state、GraphDocument schema change、renderer rewrite、UI redesign 或 full whiteboard parity claim。 | ## 推荐并行 Worktree 计划 @@ -706,4 +712,6 @@ Phase 549 记录 whiteboard primitive persistence decision implementation gate - Phase 546 是 GitHub #215 / `avalonia-node-map-0l9`;它记录 post-Phase-545 whiteboard implementation queue refresh 和 `WHITEBOARD_POST_GATE_QUEUE_REFRESH`,不做 runtime behavior changes、public API changes、production model/schema changes、renderer-layer changes、screenshot manifest expansion、pointer coordinator edits、UI redesign、drawing tool implementation、eraser behavior、toolbar work、retained API removal 或 full React Flow whiteboard parity。 - Phase 547 是 GitHub #217 / `avalonia-node-map-rs0`;它用 internal Core model types 和 focused Core/Editor contract tests 记录 `WHITEBOARD_PRIMITIVE_MODEL_SKELETON`。renderer adapters、persistence/schema、pointer coordinators、toolbar/tool activation、eraser behavior、screenshot manifests、Cookbook visual proof、public API claim expansion、retained API removal 和 full React Flow whiteboard parity 都保持 out of scope。 - Phase 548 是 GitHub #219 / `avalonia-node-map-10p`;它用 internal Core renderer-neutral projection 和 hit-testing tests 记录 `WHITEBOARD_PRIMITIVE_RENDERER_ADAPTER_SKELETON`。GraphDocument schema changes、public drawing API、Avalonia renderer rewrites、NodeCanvasConnectionSceneRenderer edits、pointer coordinators、toolbar/tool activation、eraser behavior、persistence/schema work、screenshot manifests、Cookbook visual proof、retained API removal 和 full React Flow whiteboard parity 都保持 out of scope。 -- Phase 478、Phase 484、Phase 490、Phase 491、Phase 492、Phase 493、Phase 494、Phase 495、Phase 497、Phase 498、Phase 499、Phase 500、Phase 501、Phase 502、Phase 503、Phase 504、Phase 505、Phase 506、Phase 507、Phase 508、Phase 509、Phase 510、Phase 511、Phase 512、Phase 513、Phase 520、Phase 521、Phase 522、Phase 523、Phase 524、Phase 525、Phase 526、Phase 527、Phase 528、Phase 529、Phase 535、Phase 539、Phase 540、Phase 541 和 Phase 546 都不修改产品代码;除非 focused test 证明存在具体 missing contract。 +- Phase 549 是 GitHub #221 / `avalonia-node-map-3l6`;它只用 internal `GraphWhiteboardPrimitivePersistenceDecision` policy contract 记录 `WHITEBOARD_PRIMITIVE_PERSISTENCE_DECISION_GATE`。GraphDocument schema changes、schema version bumps、workspace persistence behavior changes、public drawing APIs、pointer coordinators、toolbar/tool activation、eraser behavior、screenshot manifest expansion、UI redesign、retained API removal 和 full React Flow whiteboard parity 都保持 out of scope。 +- Phase 550 是 GitHub #223 / `avalonia-node-map-wjr`;它只用 `cookbook-whiteboard-primitive-screenshot-proof`、`shell-cookbook-whiteboard-primitive-screenshot-proof`、route metadata、shell state、non-overlap visual proof 和 `WHITEBOARD_PRIMITIVE_SCREENSHOT_GATE_OK` 记录 `WHITEBOARD_PRIMITIVE_COOKBOOK_SCREENSHOT_GATE`。Public drawing APIs、pointer coordinators、toolbar work、eraser behavior、persisted whiteboard primitive state、GraphDocument schema changes、renderer rewrites、UI redesign、retained API removal 和 full React Flow whiteboard parity 都保持 out of scope。 +- Phase 478、Phase 484、Phase 490、Phase 491、Phase 492、Phase 493、Phase 494、Phase 495、Phase 497、Phase 498、Phase 499、Phase 500、Phase 501、Phase 502、Phase 503、Phase 504、Phase 505、Phase 506、Phase 507、Phase 508、Phase 509、Phase 510、Phase 511、Phase 512、Phase 513、Phase 520、Phase 521、Phase 522、Phase 523、Phase 524、Phase 525、Phase 526、Phase 527、Phase 528、Phase 529、Phase 535、Phase 539、Phase 540、Phase 541、Phase 546 和 Phase 550 都不修改产品代码;除非 focused test 证明存在具体 missing contract。 diff --git a/src/AsterGraph.Demo/Cookbook/DemoCookbookCatalog.Recipes.cs b/src/AsterGraph.Demo/Cookbook/DemoCookbookCatalog.Recipes.cs index d639fc98..17d8e061 100644 --- a/src/AsterGraph.Demo/Cookbook/DemoCookbookCatalog.Recipes.cs +++ b/src/AsterGraph.Demo/Cookbook/DemoCookbookCatalog.Recipes.cs @@ -1362,6 +1362,91 @@ public static partial class DemoCookbookCatalog // Capture while the pointer drag is active so the transient lasso path is visible. """ ), + new DemoCookbookRecipe( + "whiteboard-primitive-screenshot-proof-route", + DemoCookbookRecipeCategory.Authoring, + "Whiteboard primitive screenshot proof route", + "Capture a bounded Cookbook and shell visual proof route for future whiteboard primitive examples without enabling whiteboard drawing behavior.", + [ + new DemoCookbookAnchor( + "Whiteboard primitive model skeleton", + "src/AsterGraph.Core/Models/GraphWhiteboardPrimitive.cs", + "GraphWhiteboardPrimitive"), + new DemoCookbookAnchor( + "Whiteboard renderer adapter skeleton", + "src/AsterGraph.Core/Models/GraphWhiteboardPrimitiveRendererAdapter.cs", + "GraphWhiteboardPrimitiveRendererAdapter"), + new DemoCookbookAnchor( + "Whiteboard persistence decision", + "src/AsterGraph.Core/Models/GraphWhiteboardPrimitivePersistenceDecision.cs", + "GraphWhiteboardPrimitivePersistenceDecision"), + ], + [ + new DemoCookbookAnchor( + "Selection marquee graph fixture", + "src/AsterGraph.Demo/DemoGraphFactory.cs", + "selection-marquee-workbench"), + new DemoCookbookAnchor( + "Whiteboard scene screenshot route", + "tests/AsterGraph.Demo.Tests/CookbookScreenshotGateRoutes.json", + "cookbook-whiteboard-primitive-screenshot-proof"), + new DemoCookbookAnchor( + "Whiteboard shell visual gate state", + "tests/AsterGraph.Demo.Tests/CookbookShellVisualGateStates.json", + "shell-cookbook-whiteboard-primitive-screenshot-proof"), + ], + [ + new DemoCookbookAnchor( + "Whiteboard primitive screenshot proof cookbook docs", + "docs/en/demo-cookbook.md", + "WHITEBOARD_PRIMITIVE_SCREENSHOT_GATE_OK"), + ], + [ + new DemoCookbookScenarioPoint( + DemoCookbookScenarioKind.GraphOperations, + "The route reuses selection-marquee-workbench so visual proof starts from a populated graph scene.", + "selection-marquee-workbench"), + new DemoCookbookScenarioPoint( + DemoCookbookScenarioKind.ValidationRuntimeOverlay, + "The shell visual gate captures PART_NodeCanvas and the Cookbook recipe panel for non-overlap evidence.", + "shell-cookbook-whiteboard-primitive-screenshot-proof"), + new DemoCookbookScenarioPoint( + DemoCookbookScenarioKind.SupportEvidence, + "The proof remains anchored to internal model, renderer, and persistence decisions instead of adding a whiteboard tool.", + "WHITEBOARD_PRIMITIVE_SCREENSHOT_GATE_OK"), + ], + [ + new DemoCookbookInteractionFacet( + DemoCookbookInteractionKind.Selection, + "The screenshot route reuses existing selection fixture state as the stable visual host scene.", + "selection-marquee-workbench"), + new DemoCookbookInteractionFacet( + DemoCookbookInteractionKind.LayoutReadability, + "The full-window shell capture keeps the node canvas and Cookbook route text visible for non-overlap review.", + "shell-cookbook-whiteboard-primitive-screenshot-proof"), + new DemoCookbookInteractionFacet( + DemoCookbookInteractionKind.Inspection, + "The generated metadata records route metadata, shell state, and required shell parts for evidence review.", + "cookbook-whiteboard-primitive-screenshot-proof"), + ], + [ + "WHITEBOARD_PRIMITIVE_SCREENSHOT_GATE_OK", + "GraphWhiteboardPrimitivePersistenceDecision", + "cookbook-whiteboard-primitive-screenshot-proof", + ], + new DemoCookbookRouteClarity( + "Whiteboard primitive screenshot proof route: launch `selection-marquee-workbench` and capture `cookbook-whiteboard-primitive-screenshot-proof` plus `shell-cookbook-whiteboard-primitive-screenshot-proof` metadata.", + "Supported seams remain internal `AsterGraph.Core` whiteboard primitive model, renderer adapter, and persistence decision contracts plus existing Demo screenshot gate manifests.", + "Demo cookbook provides proof-route plumbing only; Demo does not add a public drawing API, pointer coordinator, toolbar, eraser, persisted whiteboard primitive state, GraphDocument schema change, renderer rewrite, UI redesign, or full whiteboard parity."), + "Whiteboard primitive screenshot proof coverage is limited to route metadata, shell state, non-overlap visual proof, and existing internal whiteboard decisions; it does not add public drawing APIs, pointer coordinators, toolbar activation, eraser behavior, persisted whiteboard primitive state, GraphDocument schema changes, strict pixel baselines, retained API removal, or full whiteboard parity.", + CodeSample: """ + // Select the proof-only Cookbook route before capture. + const string routeId = "cookbook-whiteboard-primitive-screenshot-proof"; + const string shellStateId = "shell-cookbook-whiteboard-primitive-screenshot-proof"; + + // The route reuses selection-marquee-workbench; no whiteboard drawing tool is activated. + """ + ), new DemoCookbookRecipe( "interaction-keyboard-navigation-route", DemoCookbookRecipeCategory.Authoring, diff --git a/tests/AsterGraph.Demo.Tests/CookbookScreenshotGateRoutes.json b/tests/AsterGraph.Demo.Tests/CookbookScreenshotGateRoutes.json index 56b14ea5..a2d0efba 100644 --- a/tests/AsterGraph.Demo.Tests/CookbookScreenshotGateRoutes.json +++ b/tests/AsterGraph.Demo.Tests/CookbookScreenshotGateRoutes.json @@ -230,6 +230,27 @@ ], "outputFileName": "cookbook-interaction-lasso-screenshot-proof.png" }, + { + "id": "cookbook-whiteboard-primitive-screenshot-proof", + "recipeId": "whiteboard-primitive-screenshot-proof-route", + "scenario": "selection-marquee-workbench", + "hostGroup": "cookbook", + "language": "en", + "theme": "canonical-dark", + "viewportWidth": 1680, + "viewportHeight": 940, + "backgroundHex": "#09141C", + "scale": 1.0, + "minimumBytes": 4096, + "expectedDocumentTitle": "Selection Rectangle Fixture", + "minimumNodeCount": 8, + "minimumConnectionCount": 10, + "requiredNodeIds": [ + "select-clock", + "select-output" + ], + "outputFileName": "cookbook-whiteboard-primitive-screenshot-proof.png" + }, { "id": "cookbook-interaction-keyboard-navigation", "recipeId": "interaction-keyboard-navigation-route", diff --git a/tests/AsterGraph.Demo.Tests/CookbookShellVisualGateStates.json b/tests/AsterGraph.Demo.Tests/CookbookShellVisualGateStates.json index 482986c4..4865da33 100644 --- a/tests/AsterGraph.Demo.Tests/CookbookShellVisualGateStates.json +++ b/tests/AsterGraph.Demo.Tests/CookbookShellVisualGateStates.json @@ -120,6 +120,23 @@ ], "outputFileName": "shell-cookbook-lasso-screenshot-proof.png" }, + { + "id": "shell-cookbook-whiteboard-primitive-screenshot-proof", + "routeId": "cookbook-whiteboard-primitive-screenshot-proof", + "hostGroup": "cookbook", + "language": "en", + "theme": "canonical-dark", + "expectedPaneOpen": true, + "requiredShellParts": [ + "PART_HostMenu", + "PART_HostShellSplitView", + "PART_CookbookWorkspaceNavigationPanel", + "PART_NodeCanvas", + "PART_MainGraphEditorHost", + "PART_CookbookWorkspaceRecipeContentPanel" + ], + "outputFileName": "shell-cookbook-whiteboard-primitive-screenshot-proof.png" + }, { "id": "shell-cookbook-default-closed", "routeId": "cookbook-default-starter-host-ai-pipeline", diff --git a/tests/AsterGraph.Demo.Tests/DemoCookbookScreenshotGateTests.cs b/tests/AsterGraph.Demo.Tests/DemoCookbookScreenshotGateTests.cs index 57d35efa..59807b3e 100644 --- a/tests/AsterGraph.Demo.Tests/DemoCookbookScreenshotGateTests.cs +++ b/tests/AsterGraph.Demo.Tests/DemoCookbookScreenshotGateTests.cs @@ -291,6 +291,34 @@ public void CookbookScreenshotGate_IncludesPhase536LassoScreenshotProofRoute() Assert.Equal("shell-cookbook-lasso-screenshot-proof.png", shellState.OutputFileName); } + [Fact] + public void CookbookScreenshotGate_IncludesPhase550WhiteboardPrimitiveScreenshotProofRoute() + { + var routes = LoadRoutes(GetRepositoryRoot()); + var shellStates = LoadShellStates(GetRepositoryRoot()); + + var route = Assert.Single(routes, candidate => + string.Equals(candidate.Id, "cookbook-whiteboard-primitive-screenshot-proof", StringComparison.Ordinal)); + Assert.Equal("whiteboard-primitive-screenshot-proof-route", route.RecipeId); + Assert.Equal("selection-marquee-workbench", route.Scenario); + Assert.Equal("Selection Rectangle Fixture", route.ExpectedDocumentTitle); + Assert.True(route.MinimumNodeCount >= 2); + Assert.True(route.MinimumConnectionCount >= 1); + Assert.Contains("select-output", route.RequiredNodeIds, StringComparer.Ordinal); + Assert.Equal("cookbook-whiteboard-primitive-screenshot-proof.png", route.OutputFileName); + + var shellState = Assert.Single(shellStates, state => + string.Equals(state.Id, "shell-cookbook-whiteboard-primitive-screenshot-proof", StringComparison.Ordinal)); + Assert.Equal(route.Id, shellState.RouteId); + Assert.Equal("cookbook", shellState.HostGroup); + Assert.Equal("en", shellState.Language); + Assert.Equal("canonical-dark", shellState.Theme); + Assert.True(shellState.ExpectedPaneOpen); + Assert.Contains("PART_NodeCanvas", shellState.RequiredShellParts, StringComparer.Ordinal); + Assert.Contains("PART_CookbookWorkspaceRecipeContentPanel", shellState.RequiredShellParts, StringComparer.Ordinal); + Assert.Equal("shell-cookbook-whiteboard-primitive-screenshot-proof.png", shellState.OutputFileName); + } + [Fact] public void CookbookScreenshotGate_IncludesLifecycleFixtureBatchRoutes() { @@ -336,7 +364,7 @@ public void CookbookScreenshotGate_IncludesSelectedRuntimeClosedShellState() ], shellState.RequiredShellParts); Assert.Equal("shell-runtime-diagnostics-closed.png", shellState.OutputFileName); - Assert.Equal(11, shellStates.Count); + Assert.Equal(12, shellStates.Count); } [Fact] @@ -663,7 +691,7 @@ private static void CaptureShellVisualState( var imageSize = AssertPngArtifact(bytes, ShellMinimumBytes); Assert.True(imageSize.Width >= route.ViewportWidth); Assert.True(imageSize.Height >= route.ViewportHeight); - var pixelInspection = InspectNonBlankPng(imagePath); + var pixelInspection = InspectNonBlankPng(bytes, imagePath); Assert.True(pixelInspection.NonTransparentPixelCount > imageSize.Width * imageSize.Height / 4); Assert.True(pixelInspection.DistinctColorCount > 1); @@ -968,9 +996,9 @@ private static PngSize AssertPngArtifact(byte[] bytes, int minimumBytes) return new PngSize(ReadBigEndianInt32(bytes, 16), ReadBigEndianInt32(bytes, 20)); } - private static PngPixelInspection InspectNonBlankPng(string imagePath) + private static PngPixelInspection InspectNonBlankPng(byte[] bytes, string imagePath) { - using var bitmap = SKBitmap.Decode(imagePath); + using var bitmap = SKBitmap.Decode(bytes); Assert.NotNull(bitmap); Assert.True(bitmap.Width > 0); Assert.True(bitmap.Height > 0); diff --git a/tests/AsterGraph.Demo.Tests/ReactFlowParityRoadmapDocsTests.cs b/tests/AsterGraph.Demo.Tests/ReactFlowParityRoadmapDocsTests.cs index 0f51b988..87ddba2f 100644 --- a/tests/AsterGraph.Demo.Tests/ReactFlowParityRoadmapDocsTests.cs +++ b/tests/AsterGraph.Demo.Tests/ReactFlowParityRoadmapDocsTests.cs @@ -1470,6 +1470,42 @@ public void ParityRoadmapDocs_RecordPhase549WhiteboardPrimitivePersistenceDecisi Assert.Contains("Phase 549 记录 whiteboard primitive persistence decision implementation gate", chineseParity, StringComparison.Ordinal); } + [Fact] + public void ParityRoadmapDocs_RecordPhase550WhiteboardPrimitiveCookbookScreenshotGateInBothLocales() + { + var englishParity = ReadRepoFile("docs/en/phase-0-reactflow-parity-audit.md"); + var chineseParity = ReadRepoFile("docs/zh-CN/phase-0-reactflow-parity-audit.md"); + + foreach (var contents in new[] { englishParity, chineseParity }) + { + Assert.Contains("Phase 550", contents, StringComparison.Ordinal); + Assert.Contains("GitHub #223", contents, StringComparison.Ordinal); + Assert.Contains("avalonia-node-map-wjr", contents, StringComparison.Ordinal); + Assert.Contains("whiteboard primitive Cookbook screenshot implementation gate", contents, StringComparison.OrdinalIgnoreCase); + Assert.Contains("WHITEBOARD_PRIMITIVE_COOKBOOK_SCREENSHOT_GATE", contents, StringComparison.Ordinal); + Assert.Contains("cookbook-whiteboard-primitive-screenshot-proof", contents, StringComparison.Ordinal); + Assert.Contains("shell-cookbook-whiteboard-primitive-screenshot-proof", contents, StringComparison.Ordinal); + Assert.Contains("whiteboard primitive visual proof", contents, StringComparison.OrdinalIgnoreCase); + Assert.Contains("route metadata", contents, StringComparison.OrdinalIgnoreCase); + Assert.Contains("shell state", contents, StringComparison.OrdinalIgnoreCase); + Assert.Contains("non-overlap", contents, StringComparison.OrdinalIgnoreCase); + Assert.Contains("no public drawing API", contents, StringComparison.OrdinalIgnoreCase); + Assert.Contains("no pointer coordinator", contents, StringComparison.OrdinalIgnoreCase); + Assert.Contains("no toolbar", contents, StringComparison.OrdinalIgnoreCase); + Assert.Contains("no eraser", contents, StringComparison.OrdinalIgnoreCase); + Assert.Contains("no persisted whiteboard primitive state", contents, StringComparison.OrdinalIgnoreCase); + Assert.Contains("no GraphDocument schema change", contents, StringComparison.OrdinalIgnoreCase); + Assert.Contains("no full React Flow whiteboard parity", contents, StringComparison.OrdinalIgnoreCase); + Assert.DoesNotContain("| TBD | TBD | Phase 550: whiteboard primitive Cookbook screenshot implementation gate", contents, StringComparison.Ordinal); + Assert.DoesNotContain("whiteboard drawing is fully supported", contents, StringComparison.OrdinalIgnoreCase); + } + + AssertPostPhase545Queue(ExtractIssueWaveTable(englishParity)); + AssertPostPhase545Queue(ExtractIssueWaveTable(chineseParity)); + Assert.Contains("Phase 550 records the whiteboard primitive Cookbook screenshot implementation gate", englishParity, StringComparison.Ordinal); + Assert.Contains("Phase 550 记录 whiteboard primitive Cookbook screenshot implementation gate", chineseParity, StringComparison.Ordinal); + } + [Fact] public void ParityRoadmapDocs_RecordPhase501PostPhase500QueueRefreshInBothLocales() { @@ -1679,8 +1715,7 @@ private static void AssertPostPhase545Queue(string table) Assert.Contains("| #217 | `avalonia-node-map-rs0` | Phase 547: whiteboard primitive model skeleton", table, StringComparison.Ordinal); Assert.Contains("| #219 | `avalonia-node-map-10p` | Phase 548: whiteboard primitive renderer adapter skeleton", table, StringComparison.Ordinal); Assert.Contains("| #221 | `avalonia-node-map-3l6` | Phase 549: whiteboard primitive persistence decision implementation gate", table, StringComparison.Ordinal); - Assert.Contains("Phase 550: whiteboard primitive Cookbook screenshot implementation gate", table, StringComparison.Ordinal); - Assert.Contains("TBD", table, StringComparison.Ordinal); + Assert.Contains("| #223 | `avalonia-node-map-wjr` | Phase 550: whiteboard primitive Cookbook screenshot implementation gate", table, StringComparison.Ordinal); Assert.Contains("Core/Editor primitive model tests", table, StringComparison.OrdinalIgnoreCase); Assert.Contains("internal model types", table, StringComparison.OrdinalIgnoreCase); Assert.Contains("renderer-neutral contract docs", table, StringComparison.OrdinalIgnoreCase); @@ -1688,6 +1723,7 @@ private static void AssertPostPhase545Queue(string table) Assert.Contains("topmost primitive hit-testing evidence", table, StringComparison.OrdinalIgnoreCase); Assert.Contains("persistence decision tests", table, StringComparison.OrdinalIgnoreCase); Assert.Contains("Cookbook screenshot implementation gate", table, StringComparison.OrdinalIgnoreCase); + Assert.Contains("whiteboard primitive visual proof route", table, StringComparison.OrdinalIgnoreCase); Assert.Contains("Stacked after PR #214", table, StringComparison.OrdinalIgnoreCase); Assert.Contains("do not merge before Phase 545", table, StringComparison.OrdinalIgnoreCase); Assert.Contains("Stacked after PR #216", table, StringComparison.OrdinalIgnoreCase); @@ -1696,6 +1732,8 @@ private static void AssertPostPhase545Queue(string table) Assert.Contains("do not merge before Phase 547", table, StringComparison.OrdinalIgnoreCase); Assert.Contains("Stacked after PR #220", table, StringComparison.OrdinalIgnoreCase); Assert.Contains("do not merge before Phase 548", table, StringComparison.OrdinalIgnoreCase); + Assert.Contains("Stacked after PR #222", table, StringComparison.OrdinalIgnoreCase); + Assert.Contains("do not merge before Phase 549", table, StringComparison.OrdinalIgnoreCase); Assert.DoesNotContain("| #206 | `avalonia-node-map-b31` | Phase 542: whiteboard primitive core model contract gate | P2 | core/editor model tests, primitive identity/geometry/style contract docs, and API inventory | Future candidate", table, StringComparison.Ordinal); Assert.DoesNotContain("| #207 | `avalonia-node-map-aj8` | Phase 543: whiteboard renderer projection and hit-testing proof gate | P2 | renderer projection tests, hit-testing proof, and bounded Avalonia/editor evidence | Future candidate", table, StringComparison.Ordinal); Assert.DoesNotContain("| #208 | `avalonia-node-map-32n` | Phase 544: whiteboard primitive persistence schema policy gate | P3 | persistence/schema policy docs, migration criteria, and compatibility tests | Future candidate", table, StringComparison.Ordinal); @@ -1703,6 +1741,7 @@ private static void AssertPostPhase545Queue(string table) Assert.DoesNotContain("| TBD | TBD | Phase 547: whiteboard primitive model skeleton", table, StringComparison.Ordinal); Assert.DoesNotContain("| TBD | TBD | Phase 548: whiteboard primitive renderer adapter skeleton", table, StringComparison.Ordinal); Assert.DoesNotContain("| TBD | TBD | Phase 549: whiteboard primitive persistence decision implementation gate", table, StringComparison.Ordinal); + Assert.DoesNotContain("| TBD | TBD | Phase 550: whiteboard primitive Cookbook screenshot implementation gate", table, StringComparison.Ordinal); } private static void AssertBuiltInComponentMatrix(string table) diff --git a/tests/AsterGraph.Demo.Tests/ShellVisualCoveragePlanningDocsTests.cs b/tests/AsterGraph.Demo.Tests/ShellVisualCoveragePlanningDocsTests.cs index 16cb66c8..bca48a96 100644 --- a/tests/AsterGraph.Demo.Tests/ShellVisualCoveragePlanningDocsTests.cs +++ b/tests/AsterGraph.Demo.Tests/ShellVisualCoveragePlanningDocsTests.cs @@ -61,7 +61,7 @@ public void ShellVisualCoveragePlanningDocs_KeepPlanningSliceDistinctFromPhase50 { var manifestPath = Path.Combine(GetRepositoryRoot(), "tests/AsterGraph.Demo.Tests/CookbookShellVisualGateStates.json"); using var document = JsonDocument.Parse(File.ReadAllText(manifestPath)); - Assert.Equal(11, document.RootElement.GetArrayLength()); + Assert.Equal(12, document.RootElement.GetArrayLength()); Assert.Contains( document.RootElement.EnumerateArray(), state => @@ -74,7 +74,7 @@ public void ShellVisualCoveragePlanningDocs_RecordPhase509TooltipPopupRow() { var manifestPath = Path.Combine(GetRepositoryRoot(), "tests/AsterGraph.Demo.Tests/CookbookShellVisualGateStates.json"); using var document = JsonDocument.Parse(File.ReadAllText(manifestPath)); - Assert.Equal(11, document.RootElement.GetArrayLength()); + Assert.Equal(12, document.RootElement.GetArrayLength()); Assert.Contains( document.RootElement.EnumerateArray(), state => @@ -102,7 +102,7 @@ public void ShellVisualCoveragePlanningDocs_RecordPhase510CanvasContextMenuRow() { var manifestPath = Path.Combine(GetRepositoryRoot(), "tests/AsterGraph.Demo.Tests/CookbookShellVisualGateStates.json"); using var document = JsonDocument.Parse(File.ReadAllText(manifestPath)); - Assert.Equal(11, document.RootElement.GetArrayLength()); + Assert.Equal(12, document.RootElement.GetArrayLength()); Assert.Contains( document.RootElement.EnumerateArray(), state => @@ -132,7 +132,7 @@ public void ShellVisualCoveragePlanningDocs_RecordPhase511BoundedLanguageThemeRo { var manifestPath = Path.Combine(GetRepositoryRoot(), "tests/AsterGraph.Demo.Tests/CookbookShellVisualGateStates.json"); using var document = JsonDocument.Parse(File.ReadAllText(manifestPath)); - Assert.Equal(11, document.RootElement.GetArrayLength()); + Assert.Equal(12, document.RootElement.GetArrayLength()); Assert.Contains( document.RootElement.EnumerateArray(), state => @@ -194,7 +194,7 @@ public void ShellVisualCoveragePlanningDocs_RecordPhase536LassoScreenshotState() { var manifestPath = Path.Combine(GetRepositoryRoot(), "tests/AsterGraph.Demo.Tests/CookbookShellVisualGateStates.json"); using var document = JsonDocument.Parse(File.ReadAllText(manifestPath)); - Assert.Equal(11, document.RootElement.GetArrayLength()); + Assert.Equal(12, document.RootElement.GetArrayLength()); Assert.Contains( document.RootElement.EnumerateArray(), state =>