diff --git a/CHANGELOG.md b/CHANGELOG.md index 5efda7e8..67a62ae6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [6.2.0] + +Unpublished release candidate. Veritas Kanban 6.2.0 improves Settings persistence, keyboard and native macOS interactions, and large-board behavior. + +### Changed + +- Made macOS Settings a reusable window with shared settings content, synchronized changes, and a pending-save check before quitting. Browser Settings remains a modal (#1525, #1542). +- Added permission-aware Settings search and direct control links; added System appearance alongside Light and Dark; compacted secondary board filters on narrow screens (#1539–#1541). +- Loaded compact card records and rendered a measured window for large columns. Full task details load on demand, full-text search remains available, and dependency checks share one index (#1537). +- Updated SQLite notifications by affected row and moved filtering and pagination into repository queries. Extracted recovery scheduling, reconciliation and monitoring into one coordinator (#1536, #1543). + +### Fixed + +- Connected native menu commands to renderer actions, restored standard macOS menu commands, kept restored windows on available displays, and honored the title-bar double-click preference (#1524, #1529–#1531). +- Preserved pending Settings edits across section changes and closing; retained failed saves for retry and waited for persistence before confirming an import (#1525). +- Kept modified keys and IME composition out of board letter shortcuts. Keyboard selection follows visible order, brings tasks into view, and uses help derived from configured columns (#1526, #1533, #1534). +- Corrected primary-action, filter and code-token contrast; separated browser onboarding from native desktop diagnostics (#1532, #1535). +- Rejected empty or ambiguous task identifiers in CLI/MCP mutations; bounded API waits and retained structured errors. File notification writes preserve recoverable state and report malformed or unreadable data (#1527, #1528, #1538). + +### Compatibility + +- All maintained package versions move together to 6.2.0. REST API `v1` and existing full-task/summary clients remain compatible. This candidate adds no database migration. +- Existing explicit Light/Dark choices are retained. System appearance is an additional preference. Failed Settings writes need an explicit retry; closing the macOS Settings window keeps its pending edits alive. +- CLI/MCP task suffixes must identify exactly one task. Use the complete task ID when several matches exist. + ## [6.1.7] - 2026-09-04 Veritas Kanban 6.1.7 corrects desktop and compact-window layout, popout interactions, and task-editing failures found during the broader UI audit. diff --git a/README.md b/README.md index 496da85e..406f35dd 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,11 @@ Start with a visual Kanban board. Add CLI, MCP, OpenClaw, Squad Chat webhooks, w [![CI](https://github.com/BradGroux/veritas-kanban/actions/workflows/ci.yml/badge.svg)](https://github.com/BradGroux/veritas-kanban/actions/workflows/ci.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) -[![Version](https://img.shields.io/badge/version-6.1.7-blue.svg)](CHANGELOG.md) +[![Version](https://img.shields.io/badge/version-6.2.0-blue.svg)](CHANGELOG.md) [![TypeScript](https://img.shields.io/badge/TypeScript-6.0-blue.svg)](https://www.typescriptlang.org/) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md) -![Veritas Kanban 6.1.7 board, task workspace, Settings, and command palette tour](docs/assets/v6.1.7/board-to-workspace.gif) +![Veritas Kanban 6.2.0 candidate board-to-task interaction](docs/assets/v6.2.0/board-to-workspace.gif) > 🎬 [Watch the full demo video](https://bradgroux.github.io/veritas-kanban/demo/) @@ -167,15 +167,15 @@ When the board is working, use [Setup Paths](docs/SETUP-PATHS.md) to choose the Spawn autonomous coding agents on tasks when you choose to connect an agent runner. Track them in real-time with the multi-agent dashboard — status indicators, expandable agent cards, model attribution. Team roster manifests and workspace capability discovery route work to the right agent or trusted workspace before a run starts. Shared live run sessions let workspace members observe an active task run, co-drive with attributed messages, or fork a clean follow-up task without taking over the parent run. Squad Chat gives agents a shared local communication channel with system lifecycle events (spawned, completed, failed). Assign multiple agents per task, set permission levels (Intern/Specialist/Lead), and let them coordinate. -![Agent orchestration board](docs/assets/v6.1.7/board-overview.png) +![Agent orchestration board](docs/assets/v6.2.0/board-overview.png) -![Resizable Workbench panel](docs/assets/v6.1.7/workbench-panel.png) +![Resizable Workbench panel](docs/assets/v6.2.0/workbench-panel.png) Desktop Board Chat and Squad Chat open in one bounded, resizable right-side Workbench dock. The single orientation keeps the board, header, close control, and keyboard recovery paths reachable at every supported window size. -![Squad Chat coordination](docs/assets/v6.1.7/squad-chat.png) +![Squad Chat coordination](docs/assets/v6.2.0/squad-chat.png) ### 🧭 Provider And Cutover Operations @@ -185,7 +185,7 @@ The cutover guide documents a GitHub-backed operating model for Codex and Hermes Codex can run as the default first-class Veritas agent through local `codex exec`, SDK-backed sessions, GitHub-native `@codex` delegation, workflow-engine steps, review actions, Settings health checks, and MCP access to the board. Ollama Local, Ollama Cloud, and LM Studio Local profiles are optional routing targets for users who want local/server-hosted models or explicit cloud model execution. The docs include a roadmap, SOP, workflow examples, and an AGENTS template so provider-backed work can be started, tracked, reviewed, and released through the same Veritas lifecycle as other agents. -![Agent provider settings](docs/assets/v6.1.7/agent-providers.png) +![Agent provider settings](docs/assets/v6.2.0/agent-providers.png) ### 📊 Customizable Dashboard @@ -203,7 +203,7 @@ Define multi-step agent pipelines as version-controlled YAML. Sequential steps, Not just cards on a board. Tasks have dependency graphs with cycle detection, crash-recovery checkpointing (auto-sanitizes secrets), observational memory with importance scoring, time tracking, and full activity logs. Enforcement gates (review gates, delegation enforcement, auto-telemetry) add production guardrails — all optional, all toggleable. -![Progressive task workspace](docs/assets/v6.1.7/task-workspace.png) +![Progressive task workspace](docs/assets/v6.2.0/task-workspace.png) ### 🔀 Git-Native Development @@ -872,31 +872,31 @@ Packaged macOS release validation also requires candidate-bound native evidence.
Click to expand screenshots and GIFs -These 6.1.7 captures use release-safe dummy content against the current app surfaces. See the [v6 Visual Tour](docs/V6-VISUAL-TOUR.md) for the maintained media index and capture rules. +These 6.2.0 candidate captures use isolated public-safe tasks. Desktop images show the packaged Mac candidate; mobile images show the browser interface. See the [v6 Visual Tour](docs/V6-VISUAL-TOUR.md) for the maintained media index and capture rules. ### Desktop | Board and task-workspace tour | Current desktop board | | ---------------------------------------------------------------------------- | ------------------------------------------------------------- | -| ![6.1.7 board and workspace tour](docs/assets/v6.1.7/board-to-workspace.gif) | ![6.1.7 desktop board](docs/assets/v6.1.7/board-overview.png) | +| ![6.2.0 board and workspace tour](docs/assets/v6.2.0/board-to-workspace.gif) | ![6.2.0 desktop board](docs/assets/v6.2.0/board-overview.png) | | Workbench panel | Agent provider settings | | ---------------------------------------------------------------- | ---------------------------------------------------------------- | -| ![6.1.7 Workbench panel](docs/assets/v6.1.7/workbench-panel.png) | ![6.1.7 agent providers](docs/assets/v6.1.7/agent-providers.png) | +| ![6.2.0 Workbench panel](docs/assets/v6.2.0/workbench-panel.png) | ![6.2.0 agent providers](docs/assets/v6.2.0/agent-providers.png) | | Squad Chat coordination | Notification and reply adapters | | ------------------------------------------------------ | ---------------------------------------------------------------------------- | -| ![6.1.7 Squad Chat](docs/assets/v6.1.7/squad-chat.png) | ![6.1.7 notification adapters](docs/assets/v6.1.7/notification-adapters.png) | +| ![6.2.0 Squad Chat](docs/assets/v6.2.0/squad-chat.png) | ![6.2.0 notification adapters](docs/assets/v6.2.0/notification-adapters.png) | | Task workspace | Maintenance Center | | -------------------------------------------------------------- | ---------------------------------------------------------------------- | -| ![6.1.7 task workspace](docs/assets/v6.1.7/task-workspace.png) | ![6.1.7 Maintenance Center](docs/assets/v6.1.7/maintenance-center.png) | +| ![6.2.0 task workspace](docs/assets/v6.2.0/task-workspace.png) | ![6.2.0 Maintenance Center](docs/assets/v6.2.0/maintenance-center.png) | ### Mobile/PWA | Mobile board | Mobile board to workspace/settings flow | | ---------------------------------------------------------- | -------------------------------------------------------- | -| ![6.1.7 mobile board](docs/assets/v6.1.7/mobile-board.png) | ![6.1.7 mobile flow](docs/assets/v6.1.7/mobile-flow.gif) | +| ![6.2.0 mobile board](docs/assets/v6.2.0/mobile-board.png) | ![6.2.0 mobile flow](docs/assets/v6.2.0/mobile-flow.gif) |
diff --git a/assets/demo-overview.mp4 b/assets/demo-overview.mp4 index 483bcafa..d812927f 100644 Binary files a/assets/demo-overview.mp4 and b/assets/demo-overview.mp4 differ diff --git a/cli/package.json b/cli/package.json index 1057911a..fe7e7ee8 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@veritas-kanban/cli", - "version": "6.1.7", + "version": "6.2.0", "description": "CLI for Veritas Kanban task management", "type": "module", "bin": { diff --git a/desktop/package.json b/desktop/package.json index 76b781fa..d5c8c305 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@veritas-kanban/desktop", - "version": "6.1.7", + "version": "6.2.0", "private": true, "homepage": "https://github.com/BradGroux/veritas-kanban", "description": "Veritas Kanban native desktop shell", diff --git a/docs/AGENT-PROVIDERS.md b/docs/AGENT-PROVIDERS.md index e8d8f960..d32f21bb 100644 --- a/docs/AGENT-PROVIDERS.md +++ b/docs/AGENT-PROVIDERS.md @@ -2,7 +2,7 @@ Veritas works as a board without any agent runner. When you do enable agents, provider profiles are configured in **Settings -> Agents** and are stored in the same app config for the web app and the macOS desktop app. -![Agent provider settings](assets/v6.1.7/agent-providers.png) +![Agent provider settings](assets/v6.2.0/agent-providers.png) ## Defaults diff --git a/docs/DESKTOP-RELEASE.md b/docs/DESKTOP-RELEASE.md index e6da1067..aaaed610 100644 --- a/docs/DESKTOP-RELEASE.md +++ b/docs/DESKTOP-RELEASE.md @@ -1,5 +1,11 @@ # Veritas Kanban Desktop Release +> For the 6.2.0 candidate, retain local verification and exact package/media provenance. +> The owner authorized existing GitHub CI capacity without purchases or spending changes. +> Release and advisory publication are approved. Signing and publication still require verification. +> Installed-app replacement and public writing remain separate owner decisions. +> Follow the [current candidate gate](V6-GA-CHECKLIST.md#final-release-validation-commands). + This guide covers desktop packaging paths for macOS GA artifacts and Linux/Windows preview artifact scaffolding: unsigned PR artifacts, signed release artifacts, update metadata, and smoke testing. diff --git a/docs/FEATURES.md b/docs/FEATURES.md index 1dc4d426..15b85669 100644 --- a/docs/FEATURES.md +++ b/docs/FEATURES.md @@ -3,7 +3,7 @@ Complete feature reference for Veritas Kanban, including the v6 release surfaces. Every feature, every API endpoint, every configuration option. For a quick overview, see the [README](../README.md#-what-makes-veritas-kanban-different). -For current release screenshots and GIFs captured from 6.1.7, see the +For candidate screenshots and GIFs captured from 6.2.0, see the [v6 Visual Tour](V6-VISUAL-TOUR.md). For troubleshooting, see [TROUBLESHOOTING.md](TROUBLESHOOTING.md). @@ -89,15 +89,15 @@ For current release screenshots and GIFs captured from 6.1.7, see the The Kanban board is the central interface — a drag-and-drop workspace that reflects your project's state in real time. -![6.1.7 board to task-workspace tour](assets/v6.1.7/board-to-workspace.gif) +![6.2.0 board to task-workspace tour](assets/v6.2.0/board-to-workspace.gif) | Settings navigation | Board overview | | -------------------------------------------------------- | ------------------------------------------------ | -| ![6.1.7 settings](assets/v6.1.7/settings-navigation.png) | ![6.1.7 board](assets/v6.1.7/board-overview.png) | +| ![6.2.0 settings](assets/v6.2.0/settings-navigation.png) | ![6.2.0 board](assets/v6.2.0/board-overview.png) | | Workbench panel | Mobile/PWA board | | ----------------------------------------------------- | ----------------------------------------------------- | -| ![6.1.7 Workbench](assets/v6.1.7/workbench-panel.png) | ![6.1.7 mobile board](assets/v6.1.7/mobile-board.png) | +| ![6.2.0 Workbench](assets/v6.2.0/workbench-panel.png) | ![6.2.0 mobile board](assets/v6.2.0/mobile-board.png) | - **Kanban columns** — Four default columns for compatibility: To Do, In Progress, Blocked, Done; board columns and the default create status can be customized in Settings -> Board & Display - **Drag-and-drop** — Move tasks between configured columns with [@dnd-kit](https://dndkit.com/); reorder within columns; keyboard users press Space to pick up or drop, use spatial arrow-key movement across populated or empty columns, and press Escape to cancel; custom collision detection (pointerWithin + rectIntersection fallback) supports pointer and keyboard moves; tooltips are suppressed during drag; local state provides real-time column updates @@ -105,11 +105,11 @@ The Kanban board is the central interface — a drag-and-drop workspace that ref - **Task CRUD** — Create, read, update, and delete tasks through the UI or API - **Create task dialog** — Quick-create with title, type, priority, project, sprint, and description - ![Task workspace](assets/v6.1.7/task-workspace.png) + ![Task workspace](assets/v6.2.0/task-workspace.png) - **Progressive task workspace** — A quick drawer and full-width expanded presentation share Overview, Plan, Run, Results, and History navigation without losing the selected section, attempt, event, edits, scroll position, or board focus - ![Task workspace details](assets/v6.1.7/task-workspace.png) + ![Task workspace details](assets/v6.2.0/task-workspace.png) - **Task types** — Configurable type system with icons and color-coded card borders (code, research, content, automation, and custom types) - **Priority levels** — Low, medium, and high with visual indicators on cards @@ -280,9 +280,9 @@ Integrated git workflow from branch creation to merge. First-class support for autonomous coding agents. -![Task workspace demo](assets/v6.1.7/task-workspace.png) +![Task workspace demo](assets/v6.2.0/task-workspace.png) -![Agent provider settings](assets/v6.1.7/agent-providers.png) +![Agent provider settings](assets/v6.2.0/agent-providers.png) - **Agent orchestration** — Start, stop, and monitor AI agents on code tasks from the UI or API - **Multi-agent support** — Ships with Codex CLI, Codex SDK, Codex app-server, Codex Cloud, Hermes Agent, Claude Code, a generic ACP stdio provider, Amp, Copilot, Gemini, Ollama Local, Ollama Cloud, LM Studio Local, and Veritas profiles; add completely custom agents via Settings → Agents @@ -635,7 +635,7 @@ Real-time agent-to-agent communication channel for multi-agent collaboration. Sh | Threaded Squad Chat coordination | Human reply adapter settings | | -------------------------------------------------------------- | --------------------------------------------------------------------------------- | -| ![6.1.7 Squad Chat coordination](assets/v6.1.7/squad-chat.png) | ![6.1.7 notification and reply adapters](assets/v6.1.7/notification-adapters.png) | +| ![6.2.0 Squad Chat coordination](assets/v6.2.0/squad-chat.png) | ![6.2.0 notification and reply adapters](assets/v6.2.0/notification-adapters.png) | - **WebSocket-powered chat** — Messages broadcast in real time to all connected clients - **Resizable Workbench dock** — Board Chat and Squad Chat share one bounded right-side dock that isolates chat scrolling and keeps Close, Escape, Back, and Reset Layout recovery available @@ -2087,7 +2087,7 @@ Settings edits are batched across sections and continue saving when you switch t Modular settings system with focused, permission-aware sections. -![6.1.7 Maintenance Center settings surface](assets/v6.1.7/maintenance-center.png) +![6.2.0 Maintenance Center settings surface](assets/v6.2.0/maintenance-center.png) The Maintenance API and CLI include governed SQLite journal conversion: non-mutating previews, admin scheduling, restart-time exclusive execution, diff --git a/docs/GETTING-STARTED.md b/docs/GETTING-STARTED.md index 4bc68371..93045ab0 100644 --- a/docs/GETTING-STARTED.md +++ b/docs/GETTING-STARTED.md @@ -106,7 +106,7 @@ pnpm dev Web boots on **3000**, API on **3001**. First boot seeds demo tasks so you have something to look at. -![Dev stack running with public-safe 6.1.7 tasks](assets/v6.1.7/board-overview.png) +![Packaged 6.2.0 candidate showing the shared board UI with public-safe tasks](assets/v6.2.0/board-overview.png) ### 4. Run the in-app setup @@ -128,7 +128,7 @@ Visit [http://localhost:3000](http://localhost:3000) → follow the onboarding f 4. Hit **Create** and watch it appear in **Todo**. 5. Drag it to **In Progress** to feel the flow. -![Reviewing a task in the 6.1.7 progressive task workspace](assets/v6.1.7/task-workspace.png) +![Reviewing a task in the 6.2.0 progressive task workspace](assets/v6.2.0/task-workspace.png) > Need a clean slate? Remove the example tasks: `rm tasks/active/task_example_*.md` @@ -164,7 +164,7 @@ vk list --status todo CLI commands fully mirror the API and are the fastest way to script agent workflows. Write commands need `VK_API_KEY` unless localhost bypass grants an `agent` or `admin` role. -![Board to task-workspace tour with dummy 6.1.7 tasks](assets/v6.1.7/board-to-workspace.gif) +![Board to task-workspace tour with dummy 6.2.0 tasks](assets/v6.2.0/board-to-workspace.gif) --- @@ -201,7 +201,7 @@ This section is optional. Agents interact through HTTP + WebSocket; nothing is h - Check `.veritas-kanban/logs/agents.log` for run details - Confirm UI Agent Status indicator flips back to **Idle** -![Agent-ready task workspace](assets/v6.1.7/task-workspace.png) +![Agent-ready task workspace](assets/v6.2.0/task-workspace.png) > **Automation tip:** Keep a `prompts/` folder (see below) so agents get consistent instructions for sprint planning, reviews, research, etc. diff --git a/docs/V6-COMPATIBILITY-AND-RELEASE-POLICY.md b/docs/V6-COMPATIBILITY-AND-RELEASE-POLICY.md index 2d74fa68..36a62790 100644 --- a/docs/V6-COMPATIBILITY-AND-RELEASE-POLICY.md +++ b/docs/V6-COMPATIBILITY-AND-RELEASE-POLICY.md @@ -5,7 +5,7 @@ channels, and rollback limits. The machine-readable harness record at `GET /api/config/harness-compatibility` is authoritative for exact capability digests, fixture revisions, and the current host's live state. -Documentation freshness: 2026-09-03 for the verified Veritas Kanban 6.1.6 stable release. +Documentation freshness: 2026-09-07 for the 6.2.0 candidate. The latest published stable release remains 6.1.7. ## Harness Support Tiers @@ -23,23 +23,23 @@ are incompatible with v6. ## Compatibility Matrix -| Component | Supported v6 combination | Detection/evidence | Fail-closed boundary | -| -------------------------------------- | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | -| Server, web, shared, CLI, MCP, desktop | All release packages are exactly 6.1.6. | Package manifests, `/api/health.version`, `vk --version`, MCP metadata, desktop bundle/update metadata. | Mixed release packages are unsupported for publication. | -| Public API | REST API remains `v1` at `/api/v1`, with `/api` compatibility aliases where documented. | `X-API-Version`, OpenAPI/reference docs, CLI/MCP smoke. | Unknown API versions or incompatible auth fail before mutation. | -| Buzz Agent | Buzz v0.4.24 commit `710ed9fff57878a1d69f809b80a6ee0416c53fc4`; `buzz-agent 0.1.0`; ACP v1. | Exact initialize identity, capability digest, probe revision, composed Buzz fixtures. | Unknown build, `buzz-acp`, resume, HTTP/SSE MCP, or capability drift blocks. | -| Buzz relay integration | Buzz v0.4.24; NIP-11, NIP-29, NIP-42; optional NIP-43 membership. | Pinned relay compatibility evidence, signed query/event fixtures, mapping state. | Host/TLS drift, unsafe URL, bad signature, identity mismatch, replay, or disabled mapping blocks. | -| Grok Build | v0.2.111 build `94172f2aa4e5`; generic ACP v1. | Exact version/build and `x.ai` capability handshake. | Unknown version, approval bypass, leader/plugin/endpoint injection, or unsupported controls blocks. | -| Codex CLI | Explicit `codex-cli` adapter with exact runtime probe. | `codex --version`, `codex login status`, runtime manifest. | Missing adapter evidence or requested unsupported controls blocks. | -| Codex SDK | `@openai/codex-sdk 0.149.0`. | SDK import plus Codex authentication and runtime manifest. | Missing SDK/authentication or unsupported controls blocks. | -| Codex app-server | `codex-cli 0.145.0`, upstream commit `25af12f7e61572b0bc18ddb1008be543b91519b0`. | Generated v2 schemas, exact version, remote control disabled, deterministic and optional live smoke. | Schema drift, remote control, inherited plugins/apps/hooks/browser/computer tools, or unsandboxed shell method blocks. | -| Claude Code | `2.1.218 (Claude Code)`. | Version, auth status, agent discovery, stream fixtures, optional live smoke. | Permission bypass, inherited config/plugins, unsupported lifecycle request, bad stream, or missing authoritative result blocks. | -| GitHub Copilot CLI | v1.0.74 public-preview ACP; tag commit `2b809c84e87dbcc88f897cb4f3fb97c43b77af95`. | Version and ACP initialize handshake; authentication remains provider-managed. | Version drift, broad allow, remote/plugin/config injection, or unsupported controls blocks. | -| Hermes Agent | v2026.7.7.2 one-shot process adapter. | `hermes --version` and allowlisted boot authentication. | Resume/follow-up remains unsupported. | -| OpenClaw | v2026.6.11 gateway adapter. | Gateway health, runtime manifest, explicit operator tool policy. | Missing `sessions_spawn`/`sessions_send`, unknown evidence, or unsupported task controls blocks. | -| macOS desktop | Signed/notarized macOS 13 Ventura or later arm64 app with bundled 6.1.6 server/web. | Bundle version, signature, Gatekeeper, stapling, `/api/health.version`, update metadata. | Mixed bundle/runtime, failed readiness, signature, or metadata checks blocks stable publication. | -| Linux/Windows desktop | Unsigned preview artifacts only. | Cross-platform packaging workflows. | Not a supported stable install or update channel. | -| Desktop SQLite/profile | Existing v5.2.5 workspace upgraded in place after a complete backup. | Data/profile counts, integrity check, startup normalization, board/runtime smoke. | Competing writers, unsafe filesystem, failed migration, or missing recovery evidence blocks acceptance. | +| Component | Supported v6 combination | Detection/evidence | Fail-closed boundary | +| -------------------------------------- | ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | +| Server, web, shared, CLI, MCP, desktop | All candidate packages are exactly 6.2.0. | Package manifests, `/api/health.version`, `vk --version`, MCP metadata, desktop bundle/update metadata. | Mixed release packages are unsupported for publication. | +| Public API | REST API remains `v1` at `/api/v1`, with `/api` compatibility aliases where documented. | `X-API-Version`, OpenAPI/reference docs, CLI/MCP smoke. | Unknown API versions or incompatible auth fail before mutation. | +| Buzz Agent | Buzz v0.4.24 commit `710ed9fff57878a1d69f809b80a6ee0416c53fc4`; `buzz-agent 0.1.0`; ACP v1. | Exact initialize identity, capability digest, probe revision, composed Buzz fixtures. | Unknown build, `buzz-acp`, resume, HTTP/SSE MCP, or capability drift blocks. | +| Buzz relay integration | Buzz v0.4.24; NIP-11, NIP-29, NIP-42; optional NIP-43 membership. | Pinned relay compatibility evidence, signed query/event fixtures, mapping state. | Host/TLS drift, unsafe URL, bad signature, identity mismatch, replay, or disabled mapping blocks. | +| Grok Build | v0.2.111 build `94172f2aa4e5`; generic ACP v1. | Exact version/build and `x.ai` capability handshake. | Unknown version, approval bypass, leader/plugin/endpoint injection, or unsupported controls blocks. | +| Codex CLI | Explicit `codex-cli` adapter with exact runtime probe. | `codex --version`, `codex login status`, runtime manifest. | Missing adapter evidence or requested unsupported controls blocks. | +| Codex SDK | `@openai/codex-sdk 0.149.0`. | SDK import plus Codex authentication and runtime manifest. | Missing SDK/authentication or unsupported controls blocks. | +| Codex app-server | `codex-cli 0.145.0`, upstream commit `25af12f7e61572b0bc18ddb1008be543b91519b0`. | Generated v2 schemas, exact version, remote control disabled, deterministic and optional live smoke. | Schema drift, remote control, inherited plugins/apps/hooks/browser/computer tools, or unsandboxed shell method blocks. | +| Claude Code | `2.1.218 (Claude Code)`. | Version, auth status, agent discovery, stream fixtures, optional live smoke. | Permission bypass, inherited config/plugins, unsupported lifecycle request, bad stream, or missing authoritative result blocks. | +| GitHub Copilot CLI | v1.0.74 public-preview ACP; tag commit `2b809c84e87dbcc88f897cb4f3fb97c43b77af95`. | Version and ACP initialize handshake; authentication remains provider-managed. | Version drift, broad allow, remote/plugin/config injection, or unsupported controls blocks. | +| Hermes Agent | v2026.7.7.2 one-shot process adapter. | `hermes --version` and allowlisted boot authentication. | Resume/follow-up remains unsupported. | +| OpenClaw | v2026.6.11 gateway adapter. | Gateway health, runtime manifest, explicit operator tool policy. | Missing `sessions_spawn`/`sessions_send`, unknown evidence, or unsupported task controls blocks. | +| macOS desktop | Signed/notarized macOS 13 Ventura or later arm64 app with bundled 6.2.0 server/web for this candidate. | Bundle version, signature, Gatekeeper, stapling, `/api/health.version`, update metadata. | Mixed bundle/runtime, failed readiness, signature, or metadata checks blocks stable publication. | +| Linux/Windows desktop | Unsigned preview artifacts only. | Cross-platform packaging workflows. | Not a supported stable install or update channel. | +| Desktop SQLite/profile | Existing v5.2.5 workspace upgraded in place after a complete backup. | Data/profile counts, integrity check, startup normalization, board/runtime smoke. | Competing writers, unsafe filesystem, failed migration, or missing recovery evidence blocks acceptance. | ## Certification And Invalidation diff --git a/docs/V6-GA-CHECKLIST.md b/docs/V6-GA-CHECKLIST.md index ba2df0e3..849af1a5 100644 --- a/docs/V6-GA-CHECKLIST.md +++ b/docs/V6-GA-CHECKLIST.md @@ -1,13 +1,24 @@ # Veritas Kanban v6 GA Checklist -This checklist contains the active stable-release gate for Veritas Kanban -6.1.7 and retains the completed 6.1.6, 6.1.5, 6.1.4, 6.1.3, 6.1.2, 6.1.1, 6.1.0, and 6.0.2 evidence below. +This checklist contains the active candidate gate for Veritas Kanban +6.2.0 and retains the completed 6.1.7, 6.1.6, 6.1.5, 6.1.4, 6.1.3, 6.1.2, 6.1.1, 6.1.0, and 6.0.2 evidence below. Command results, platform details, workflow links, limitations, and artifact hashes belong in [v6 Release Candidate Evidence Packet](V6-RC-EVIDENCE-PACKET.md). -Documentation freshness: 2026-09-04 for the completed Veritas Kanban 6.1.7 release. +Documentation freshness: 2026-09-07 for the unpublished 6.2.0 candidate. -## 6.1.7 Release Gate +## 6.2.0 Candidate Gate + +- [x] Root, shared, server, web, CLI, MCP and desktop package versions are aligned at 6.2.0, with candidate release notes and compatibility guidance. +- [x] Complete the appropriate local integration verification. +- [x] Complete final delivery readback for all 25 findings: 24 implemented fixes and #1544 explicitly dispositioned as not reproducible. +- [x] Inspect the actual packaged candidate, all matched screenshots and complete GIF/video playback; retain revision and package hashes. +- [ ] Complete review and the repository delivery workflow using the owner-authorized existing CI capacity. +- [x] Obtain owner approval for the 6.2.0 release and advisory publication. +- [ ] Obtain separate approval for installed-app replacement and public writing. +- [ ] Verify signing, notarization, distributed assets and the installed application before calling 6.2.0 stable. + +## Historical 6.1.7 Release Gate - [x] macOS UI tracker #1389 and child issues #1378-#1388 are complete through focused merged work, the final signed candidate, refreshed documentation media, and the separately verified installed app. - [x] Root, shared, server, web, CLI, MCP, desktop, changelog, reviewed release source, and public documentation identify 6.1.7 without an API or database migration. @@ -156,54 +167,51 @@ Documentation freshness: 2026-09-04 for the completed Veritas Kanban 6.1.7 relea ## Final Release Validation Commands -Apply `ci:full` to the release pull request and keep it applied through the -final candidate synchronization. That single milestone runs the complete -workspace suite, critical-path coverage, unsigned desktop artifacts, and -Docker image contract. Run the following commands once from the clean 6.1.7 -release candidate at the supported Node floor and current supported Node: +The owner authorized the existing GitHub CI capacity for this candidate without a credit card, +capacity purchase, or spending-limit change. Release publication still requires separate approval. +Run verification locally and retain the actual command, revision and result. Group focused +checks by completed feature and run the assembled integration gate once; repeat only for a +relevant change, failure or unresolved concern. ```bash pnpm install --frozen-lockfile pnpm check:pnpm-settings pnpm check:security-artifacts -pnpm check:delivery-cadence -pnpm test:ci-scope +pnpm check:service-filesystem-boundary pnpm audit --prod --audit-level=high -pnpm audit:all pnpm check:gitleaks pnpm lint pnpm lint:budget -pnpm lint:report pnpm qa:mantine pnpm typecheck -pnpm build -pnpm test pnpm test:unit +pnpm desktop:test pnpm test:e2e pnpm smoke:cli-mcp -pnpm desktop:test -pnpm desktop:build -pnpm desktop:check:electron-artifacts pnpm desktop:test:readiness -pnpm desktop:dev:fresh -pnpm desktop:smoke:mac:local -pnpm desktop:package:mac:unsigned pnpm test:release-format -pnpm validate:release -- --version 6.1.7 --skip-build-output --source-only -pnpm validate:release -- --version 6.1.7 --native-evidence /absolute/path/evidence.json --native-app /absolute/path/veritas-kanban.app --docker-build +pnpm test:release-native +pnpm desktop:package:mac:unsigned +node scripts/native-ui/run.mjs /absolute/path/candidate.app /absolute/path/new-native-evidence +node --import tsx scripts/native-ui/board-performance.mjs /absolute/path/candidate.app /absolute/path/new-board-evidence +pnpm docs:capture-media /absolute/path/candidate.app /absolute/path/new-media-evidence +pnpm validate:release -- --version 6.2.0 --skip-build-output --source-only ``` -Source preflight is not release acceptance. Full validation requires the exact clean candidate's fresh native matrix and retained screenshots; signing, installed-app, documentation-media, and publication evidence remain separate gates. See [desktop release verification](DESKTOP-RELEASE.md#native-gate-before-macos-upload). +The packaging command includes the workspace build. Source preflight is not release +acceptance. After reviewing and committing the exact captured bytes, run the existing +media-publication verifier, then full release validation with the native report, exact app +and media publication manifest. See [desktop release verification](DESKTOP-RELEASE.md#native-gate-before-macos-upload). -Mount and inspect the unsigned DMG and ZIP, exercise the visible native -single-instance/reopen/clean-close/quit lifecycle with an isolated profile, and -run the production image as its non-root user against an isolated volume. -Record health, auth, SQLite, static-web, canonical-path, backup, integrity, -image-size, and clean-shutdown evidence. The same candidate must pass these -gates at Node 22.22.1 and the current supported Node runtime. +Mount and inspect the unsigned DMG and ZIP, exercise the native single-instance, reopen, +close and quit lifecycle using an isolated profile, and record the supported-runtime checks +actually performed. Signing, notarization, Docker distribution, installed-app replacement +and publication remain distinct evidence boundaries; do not infer them from local tests. ## Distribution And Post-Publication +Version 6.2.0 is not published. The following is retained historical evidence. + The 6.1.7 release is published and verified. Annotated tag `v6.1.7` peels to `6befd39cbc1264b18fa272d25bc64642f2b60383`; the exact live release body, signed/notarized assets, updater metadata, retained native matrix, diff --git a/docs/V6-RC-EVIDENCE-PACKET.md b/docs/V6-RC-EVIDENCE-PACKET.md index fce6946a..24071c45 100644 --- a/docs/V6-RC-EVIDENCE-PACKET.md +++ b/docs/V6-RC-EVIDENCE-PACKET.md @@ -1,6 +1,6 @@ # Veritas Kanban v6 Release Candidate Evidence Packet -This packet records the completed Veritas Kanban 6.1.7 macOS UI and interaction release and +This packet tracks the unpublished 6.2.0 candidate and the completed 6.1.7 macOS UI and interaction release, and retains historical evidence for the completed 6.1.6, 6.1.5, 6.1.4, 6.1.3, 6.1.2, 6.1.1, and 6.1.0 releases, the quarantined 6.0.0 prerelease, the 6.0.1 stabilization release, and the 6.0.2 desktop recovery hotfix. It separates merged implementation, deterministic conformance, local runtime proof, signed @@ -8,7 +8,23 @@ publication, and Homebrew availability. Veritas Kanban 6.1.7 is the active stable release. Do not use 6.0.0 for installation or upgrade validation. -Documentation freshness: 2026-09-04 for the completed Veritas Kanban 6.1.7 release. +Documentation freshness: 2026-09-07. Local native and media acceptance passed. Audit fixes are merged; release and distribution acceptance remain separate. + +## 6.2.0 candidate + +The candidate is prepared from the audit backlog delivered through individually reviewed public pull requests. No 6.2.0 release, installation replacement, or distribution update has occurred. The evidence below records the integrated delivery candidate. All 24 implemented audit fixes and the acceptance follow-ups are merged; #1544 is explicitly dispositioned as not reproducible. Tracker #1545 records the delivery readback. + +- Package versions: root, shared, server, web, CLI, MCP and desktop identify 6.2.0. +- Focused local checks cover the affected storage, Settings, keyboard, board, native bridge and recovery behavior. +- Verified build: `a2b8fbba0e2851d65611238624d8c9de347a2a27`. Whole-app SHA-256: `476a61cbc81f2a78b76657a87ccc98e2be645d2886d78e5df5fe51d7424dedcb`. The arm64 package was exercised on macOS 26.6.2 and is unsigned; it has not replaced an installed application. +- Local integration: server 3,709 passed / 5 skipped on Node 22.23.2; web 1,032 passed; CLI 90 passed; MCP 80 passed / 19 credential-gated skips; desktop 108 passed. Typecheck, build and lint passed with zero lint errors. All application contents match the retained passing candidate. The capture runner additionally synchronizes the renderer before reading the native frame and waits for the mobile General appearance control to load. Only the order of one Settings regression test changed during delivery; its 14-test file and 56 release-native contract tests passed again. The integrated delivery revision was rebuilt and repackaged, then passed fresh native, large-board, and media acceptance. +- The exact package passed all 144 required native cases and detected all six deliberate faults. Checks include native keyboard input, focus, light/dark and compact layouts, menu routing, modeless Settings persistence, window management and recovery. +- Packaged 100/1,000/5,000-task fixtures passed the recorded performance budgets. The 5,000-task journey also passed offscreen keyboard navigation, full-detail retrieval, status move, complete-description search, realtime updates, pointer dragging and reload persistence. +- All 14 maintained media assets, ten task-mode PNGs and the focused large-board image were captured from this build, inspected, and retained with original hashes. Both GIFs and the demo MP4 completed playback inspection. [Capture provenance](assets/v6.2.0/README.md) and the [comparison gallery](releases/v6.2.0-comparison.md) disclose baseline fixture and environment differences. +- Earlier Node 26.8.1 server runs had intermittent failures. Their cause remains unproven; passing supported-runtime checks do not establish remediation of #1544. +- The original external-tracker approval failure has not recurred in the instrumented full server run. Its original worker/order information was not retained. #1544 was explicitly dispositioned as not reproducible, with the evidence and limits recorded in the issue; no historical fix is claimed. +- The owner explicitly authorized existing GitHub CI capacity for delivery. No credit card, capacity purchase, spending-limit change, or repository-control change was made. Local verification remains the basis of the application acceptance evidence. +- The owner approved the 6.2.0 release and advisory publication. Signing/notarization and destination verification remain pending. Installed-app replacement and public writing remain separate owner decisions. ## 6.1.7 macOS UI and interaction release diff --git a/docs/V6-RELEASE-NOTES.md b/docs/V6-RELEASE-NOTES.md index 9dcc176f..f90015b9 100644 --- a/docs/V6-RELEASE-NOTES.md +++ b/docs/V6-RELEASE-NOTES.md @@ -1,8 +1,14 @@ -# Veritas Kanban 6.1.6 Release Notes +# Veritas Kanban 6.2.0 Candidate Release Notes + +The 6.2.0 candidate improves Settings persistence, native macOS interactions, keyboard navigation and large-board behavior. Read the [6.2.0 release source](releases/v6.2.0.md) for current changes and compatibility notes. Verification and publication remain separate stages in the [candidate evidence packet](V6-RC-EVIDENCE-PACKET.md). + +The latest published stable release remains 6.1.7. Veritas Kanban 6.0.0 remains quarantined. + +## Historical 6.1.6 release Veritas Kanban 6.1.6 is a focused desktop reliability and interface consistency release. It corrects chat and drag layout overflow, makes consecutive board moves reliable, polishes Settings and Command+K, removes the redundant desktop wordmark, unifies action and selected-navigation styling, refreshes maintained media, and updates vulnerable transitive dependencies. -> Veritas Kanban 6.0.0 remains a quarantined prerelease. Version 6.1.6 is the supported stable v6 release and requires macOS 13 Ventura or later on Apple silicon. +> Veritas Kanban 6.0.0 remains a quarantined prerelease. Version 6.1.6 was a supported stable v6 release and requires macOS 13 Ventura or later on Apple silicon. ## 6.1.6 Desktop Reliability Outcomes diff --git a/docs/V6-VISUAL-TOUR.md b/docs/V6-VISUAL-TOUR.md index 58878896..2b29d74d 100644 --- a/docs/V6-VISUAL-TOUR.md +++ b/docs/V6-VISUAL-TOUR.md @@ -1,13 +1,16 @@ # Veritas Kanban v6 Visual Tour -This tour presents release-safe 6.1.7 views of the board, progressive task +This tour presents release-safe 6.2.0 views of the board, progressive task workspace, grouped Settings, provider support, communication surfaces, Maintenance, Command+K, Workbench, and mobile layout. The historical [v5 Visual Tour](V5-VISUAL-TOUR.md) retains its own versioned media. -Documentation freshness: 2026-09-04 for the verified Veritas Kanban 6.1.7 stable -release. Every image below was generated from the integrated 6.1.7 source -with isolated dummy data. +Documentation freshness: 2026-09-07 for the unpublished 6.2.0 candidate. +Desktop media was captured from the packaged Mac application; mobile media was +captured in a browser at 390×844. All media uses isolated public-safe data. +See the [complete before/after comparison](releases/v6.2.0-comparison.md) and +[capture provenance](assets/v6.2.0/README.md). The signed 6.1.7 release remains +the current installation target until 6.2.0 is separately approved and released. ## Board And Task Workspace @@ -16,11 +19,11 @@ desktop shell, and Workbench affordance. Opening a task replaces the former peer-tab drawer with progressive Overview, Plan, Run, Results, and History modes. -![6.1.7 board overview with release-safe tasks](assets/v6.1.7/board-overview.png) +![6.2.0 board overview with release-safe tasks](assets/v6.2.0/board-overview.png) -![6.1.7 progressive task workspace](assets/v6.1.7/task-workspace.png) +![6.2.0 progressive task workspace](assets/v6.2.0/task-workspace.png) -![6.1.7 board, task workspace, grouped Settings, providers, and Command+K tour](assets/v6.1.7/board-to-workspace.gif) +![6.2.0 board-to-task interaction](assets/v6.2.0/board-to-workspace.gif) ## Grouped Settings And Command Palette @@ -28,9 +31,9 @@ Settings groups all twenty destinations under Core, Collaboration, Automation, Governance, and System while keeping transfer and danger-zone actions separate. Command+K uses the same current shell and remains bounded to the viewport. -![6.1.7 grouped Settings navigation](assets/v6.1.7/settings-navigation.png) +![6.2.0 grouped Settings navigation](assets/v6.2.0/settings-navigation.png) -![6.1.7 Command+K palette](assets/v6.1.7/command-palette.png) +![6.2.0 Command+K palette](assets/v6.2.0/command-palette.png) ## Provider Support @@ -43,7 +46,7 @@ diagnostics, dispatch, and telemetry. The release capture must use dummy profile names and contain no login state, environment values, provider output, private paths, or credentials. -![6.1.7 provider settings with current grouped navigation](assets/v6.1.7/agent-providers.png) +![6.2.0 provider settings with current grouped navigation](assets/v6.2.0/agent-providers.png) Expected visible behavior: @@ -62,7 +65,7 @@ URLs, public identity, environment-variable references, compatibility facets, one-channel mappings, definition preview/import, trigger rules, and bounded audit state. -![6.1.7 communication health, Buzz, and reply-adapter settings](assets/v6.1.7/notification-adapters.png) +![6.2.0 communication health, Buzz, and reply-adapter settings](assets/v6.2.0/notification-adapters.png) Expected visible behavior: @@ -97,22 +100,31 @@ Expected visible behavior: ## Workbench, Squad Chat, Maintenance, And Mobile -| Surface | Current 6.1.7 capture | +| Surface | Current 6.2.0 capture | | ------------------ | ----------------------------------------------------------------- | -| Workbench | ![Workbench](assets/v6.1.7/workbench-panel.png) | -| Squad Chat | ![Squad Chat](assets/v6.1.7/squad-chat.png) | -| Maintenance | ![Maintenance](assets/v6.1.7/maintenance-center.png) | -| Mobile board | ![Mobile board](assets/v6.1.7/mobile-board.png) | -| Mobile task | ![Mobile task workspace](assets/v6.1.7/mobile-task-workspace.png) | -| Mobile Settings | ![Mobile Settings](assets/v6.1.7/mobile-settings.png) | -| Mobile guided tour | ![Mobile flow](assets/v6.1.7/mobile-flow.gif) | +| Workbench | ![Workbench](assets/v6.2.0/workbench-panel.png) | +| Squad Chat | ![Squad Chat](assets/v6.2.0/squad-chat.png) | +| Maintenance | ![Maintenance](assets/v6.2.0/maintenance-center.png) | +| Mobile board | ![Mobile board](assets/v6.2.0/mobile-board.png) | +| Mobile task | ![Mobile task workspace](assets/v6.2.0/mobile-task-workspace.png) | +| Mobile Settings | ![Mobile Settings](assets/v6.2.0/mobile-settings.png) | +| Mobile guided tour | ![Mobile flow](assets/v6.2.0/mobile-flow.gif) | ## Capture Rules -Run `pnpm docs:capture-media` to recreate the maintained 6.1.7 PNG and GIF set -from an isolated Playwright runtime. The capture uses public dummy tasks, -cleans them after the run, and builds the GIFs from the reviewed screenshots. -It requires `ffmpeg` on `PATH`. +Follow the [documentation media contract](design/DOCUMENTATION-MEDIA-CONTRACT.md). +Run `pnpm docs:capture-media ` from a +clean candidate checkout on macOS. The output directory must not already exist. +The runner launches the actual packaged application in an isolated profile, +captures the mobile browser separately, and records real interactions at 8 fps. +It requires `ffmpeg` and the pinned Playwright Chromium installation. + +The original capture files and manifest remain outside the repository. After +visual inspection and complete GIF/MP4 playback, copy the exact bytes to the +versioned media paths and commit only documentation/media changes. Run +`pnpm docs:verify-media ` +from that clean documentation commit. This verifies original, committed and +working-copy hashes without recapturing or relabeling an older build. Before publication, retain only verified screenshots captured from an isolated runtime without private data. If a state cannot be safely produced, retain the diff --git a/docs/assets/demo-overview.mp4 b/docs/assets/demo-overview.mp4 index 483bcafa..d812927f 100644 Binary files a/docs/assets/demo-overview.mp4 and b/docs/assets/demo-overview.mp4 differ diff --git a/docs/assets/v6.2.0/README.md b/docs/assets/v6.2.0/README.md new file mode 100644 index 00000000..823f5952 --- /dev/null +++ b/docs/assets/v6.2.0/README.md @@ -0,0 +1,39 @@ +# 6.2.0 candidate media provenance + +These are reviewed local-candidate captures, not evidence of a published or signed release. + +- Build: `a2b8fbba0e2851d65611238624d8c9de347a2a27`; version: 6.2.0; bundle: `io.digitalmeld.veritas-kanban`. +- Whole-app SHA-256: `476a61cbc81f2a78b76657a87ccc98e2be645d2886d78e5df5fe51d7424dedcb`; macOS 26.6.2, Apple silicon, unsigned review package. +- Capture completed: 2026-09-08T00:35:55.632Z. Dark theme, reduced motion, isolated public-safe tasks. Native captures use the real package; mobile captures have no desktop bridge. +- Original JSON capture manifest: `media-public-620-4/evidence.json`, SHA-256 `bb92f226413b403639f0b6dcc5bbe9ef415fc1a4ea3302904bbd7f435de6d90d`. The original files, recording frames and manifest are retained outside public documentation under the [capture contract](../../design/DOCUMENTATION-MEDIA-CONTRACT.md). +- All 23 PNGs were individually inspected. The two new GIFs and retained baseline GIFs were played through complete cycles, and the MP4 reached its end without a playback error. Desktop and mobile recordings preserve the original input sequence: board, open task in Plan, select Plan, switch to Overview, close to board. Native captures omit the system cursor; visible focus rings remain. No synthetic cursor was added. + +Desktop maintained captures use 1700×760 content dimensions; mobile browser views use 390×844. Task-mode views use an actual 1180×900 native window and measured content dimensions. The original task-mode audit includes a frame/shadow and uses a different blocked showcase task with failed history; this candidate uses a To Do documentation task with no run. The older maintained captures used macOS 15.7.9. The 5,000-task comparison uses the same synthetic generator and 1360×900 content dimensions on the current host. The original maintained board has a collapsed left rail; the candidate shows the current expanded default. These differences are disclosed in the [gallery](../../releases/v6.2.0-comparison.md). + +| Asset | Capture boundary | Content dimensions | SHA-256 | +| -------------------------------------------------------- | ---------------- | ------------------ | ------------------------------------------------------------------ | +| [agent-providers.png](agent-providers.png) | packaged-macos | 1700×760 | `738dcb078ed9d04f392bdc61d388b677138412834261ecc07dce92352cb78990` | +| [board-overview.png](board-overview.png) | packaged-macos | 1700×760 | `04fdd6a0bd465ab6d05c60c51b3bed4deab1bbb10c4cbf5ce325c39ae9f1b338` | +| [board-to-workspace.gif](board-to-workspace.gif) | packaged-macos | 1700×760 | `ce440813e19cb05ea211407811622e75b7f835f3994a255990a5e815b1902bdc` | +| [command-palette.png](command-palette.png) | packaged-macos | 1700×760 | `db25eeca42d00a7b8056c99df58ea34822914f355833a0ac7db6debaedf5a700` | +| [maintenance-center.png](maintenance-center.png) | packaged-macos | 1700×760 | `b9a97426d7677917283d71c1055f2b87bb270770561beb0ce11c37e7065d65a7` | +| [mobile-board.png](mobile-board.png) | mobile-browser | 390×844 | `2f7be6ede22fc17da4d771e3e251ba620f126ee2f6b6f0cfc1b058b3e02fa7f0` | +| [mobile-flow.gif](mobile-flow.gif) | mobile-browser | 390×844 | `270017e341f9b03d1467b597391c5c1df0e40a0efbf3403f016608eb644ee163` | +| [mobile-settings.png](mobile-settings.png) | mobile-browser | 390×844 | `bb670a6ee3035beb37f8b143b1aabff81d5a51a2ba5767181383b842e7f26ccf` | +| [mobile-task-workspace.png](mobile-task-workspace.png) | mobile-browser | 390×844 | `7a55723b0ab0915c32d1e852d80a3dbe8c77026ef64336c947d019fcf2015b08` | +| [notification-adapters.png](notification-adapters.png) | packaged-macos | 1700×760 | `ff0ce568a5f7701a7ca63c0a0a54c1f61886f03443777e3bd17590f39793ae71` | +| [settings-navigation.png](settings-navigation.png) | packaged-macos | 1700×760 | `37061022142096d82227e692063687e157b62005fb622306e4b949bea351124f` | +| [squad-chat.png](squad-chat.png) | packaged-macos | 1700×760 | `06aa4e29fd285a2cab281a211843cd3ec1afd1561446f8903642d4c4bf1d91fe` | +| [task-workspace.png](task-workspace.png) | packaged-macos | 1700×760 | `022d92c0c1ef907fff22cb44a3c812c4ac54b5e2dfc99a3be4e73f43b3df9a32` | +| [workbench-panel.png](workbench-panel.png) | packaged-macos | 1700×760 | `b6f3de6615368e970d531e2666d324e94f8a1d1946fa633a6deb505d7c182334` | +| [task-drawer-overview.png](task-drawer-overview.png) | packaged-macos | 1180×900 | `c3792f8911f7313658421f75145729180148f771b47cfb63c646eda62f2df243` | +| [task-drawer-plan.png](task-drawer-plan.png) | packaged-macos | 1180×900 | `3879c43f95e90cfb40a3f56ef99478b15d2f25e0901f00c1a9092c4d0d967a4c` | +| [task-drawer-run.png](task-drawer-run.png) | packaged-macos | 1180×900 | `0eb3e06c6f8b0427e51ba76018028c1598dbd84efec54a48b413c1fb86948563` | +| [task-drawer-results.png](task-drawer-results.png) | packaged-macos | 1180×900 | `adfb2ed99bda4c5bdc36d585888e469770194d3de9b115b936b384b2b9d44027` | +| [task-drawer-history.png](task-drawer-history.png) | packaged-macos | 1180×900 | `ee445e7f5024928bce46451af5fc5cd86ab6e860cea7ee6cd8474139bb351169` | +| [task-expanded-overview.png](task-expanded-overview.png) | packaged-macos | 1180×900 | `faa98937bc33c7bd6581934e234b68b0b193e008ee29f801ad3cbc43656f4b22` | +| [task-expanded-plan.png](task-expanded-plan.png) | packaged-macos | 1180×900 | `0583ae2332857c7236fbe2ab6abf34a4f180462b419c639ee2f349eb1e0fc40d` | +| [task-expanded-run.png](task-expanded-run.png) | packaged-macos | 1180×900 | `1d2722305131bddb02c0c3a735714aac659149cdab525e1250548b7df2b2767f` | +| [task-expanded-results.png](task-expanded-results.png) | packaged-macos | 1180×900 | `e560f15b6e563ceebad24076ba872628e07c20ff6117d0781ead931c692c58cb` | +| [task-expanded-history.png](task-expanded-history.png) | packaged-macos | 1180×900 | `3ab524f5d0953b76c36c6443a9e126fc8fb86ba45d11c5ce5ee82c9bbbbeb8c8` | +| [board-5000.png](board-5000.png) | packaged-macos | 1360×900 | `2f1c3e9c230e5d14e19de61cc5121d5191faee0b7fe85d5a6d3cbfa676b4e3e4` | diff --git a/docs/assets/v6.2.0/agent-providers.png b/docs/assets/v6.2.0/agent-providers.png new file mode 100644 index 00000000..55b4accf Binary files /dev/null and b/docs/assets/v6.2.0/agent-providers.png differ diff --git a/docs/assets/v6.2.0/board-5000.png b/docs/assets/v6.2.0/board-5000.png new file mode 100644 index 00000000..86c87a75 Binary files /dev/null and b/docs/assets/v6.2.0/board-5000.png differ diff --git a/docs/assets/v6.2.0/board-overview.png b/docs/assets/v6.2.0/board-overview.png new file mode 100644 index 00000000..590ccb67 Binary files /dev/null and b/docs/assets/v6.2.0/board-overview.png differ diff --git a/docs/assets/v6.2.0/board-to-workspace.gif b/docs/assets/v6.2.0/board-to-workspace.gif new file mode 100644 index 00000000..2e56eec8 Binary files /dev/null and b/docs/assets/v6.2.0/board-to-workspace.gif differ diff --git a/docs/assets/v6.2.0/command-palette.png b/docs/assets/v6.2.0/command-palette.png new file mode 100644 index 00000000..7ce8d630 Binary files /dev/null and b/docs/assets/v6.2.0/command-palette.png differ diff --git a/docs/assets/v6.2.0/maintenance-center.png b/docs/assets/v6.2.0/maintenance-center.png new file mode 100644 index 00000000..3c50c253 Binary files /dev/null and b/docs/assets/v6.2.0/maintenance-center.png differ diff --git a/docs/assets/v6.2.0/mobile-board.png b/docs/assets/v6.2.0/mobile-board.png new file mode 100644 index 00000000..db0b9ce5 Binary files /dev/null and b/docs/assets/v6.2.0/mobile-board.png differ diff --git a/docs/assets/v6.2.0/mobile-flow.gif b/docs/assets/v6.2.0/mobile-flow.gif new file mode 100644 index 00000000..4ddf88c8 Binary files /dev/null and b/docs/assets/v6.2.0/mobile-flow.gif differ diff --git a/docs/assets/v6.2.0/mobile-settings.png b/docs/assets/v6.2.0/mobile-settings.png new file mode 100644 index 00000000..f337041f Binary files /dev/null and b/docs/assets/v6.2.0/mobile-settings.png differ diff --git a/docs/assets/v6.2.0/mobile-task-workspace.png b/docs/assets/v6.2.0/mobile-task-workspace.png new file mode 100644 index 00000000..f4a26bd5 Binary files /dev/null and b/docs/assets/v6.2.0/mobile-task-workspace.png differ diff --git a/docs/assets/v6.2.0/notification-adapters.png b/docs/assets/v6.2.0/notification-adapters.png new file mode 100644 index 00000000..6a787546 Binary files /dev/null and b/docs/assets/v6.2.0/notification-adapters.png differ diff --git a/docs/assets/v6.2.0/settings-navigation.png b/docs/assets/v6.2.0/settings-navigation.png new file mode 100644 index 00000000..472c6d74 Binary files /dev/null and b/docs/assets/v6.2.0/settings-navigation.png differ diff --git a/docs/assets/v6.2.0/squad-chat.png b/docs/assets/v6.2.0/squad-chat.png new file mode 100644 index 00000000..eda38c5b Binary files /dev/null and b/docs/assets/v6.2.0/squad-chat.png differ diff --git a/docs/assets/v6.2.0/task-drawer-history.png b/docs/assets/v6.2.0/task-drawer-history.png new file mode 100644 index 00000000..786ea3b2 Binary files /dev/null and b/docs/assets/v6.2.0/task-drawer-history.png differ diff --git a/docs/assets/v6.2.0/task-drawer-overview.png b/docs/assets/v6.2.0/task-drawer-overview.png new file mode 100644 index 00000000..11c5fc28 Binary files /dev/null and b/docs/assets/v6.2.0/task-drawer-overview.png differ diff --git a/docs/assets/v6.2.0/task-drawer-plan.png b/docs/assets/v6.2.0/task-drawer-plan.png new file mode 100644 index 00000000..73017ccb Binary files /dev/null and b/docs/assets/v6.2.0/task-drawer-plan.png differ diff --git a/docs/assets/v6.2.0/task-drawer-results.png b/docs/assets/v6.2.0/task-drawer-results.png new file mode 100644 index 00000000..9c360371 Binary files /dev/null and b/docs/assets/v6.2.0/task-drawer-results.png differ diff --git a/docs/assets/v6.2.0/task-drawer-run.png b/docs/assets/v6.2.0/task-drawer-run.png new file mode 100644 index 00000000..299b3929 Binary files /dev/null and b/docs/assets/v6.2.0/task-drawer-run.png differ diff --git a/docs/assets/v6.2.0/task-expanded-history.png b/docs/assets/v6.2.0/task-expanded-history.png new file mode 100644 index 00000000..bdf83f65 Binary files /dev/null and b/docs/assets/v6.2.0/task-expanded-history.png differ diff --git a/docs/assets/v6.2.0/task-expanded-overview.png b/docs/assets/v6.2.0/task-expanded-overview.png new file mode 100644 index 00000000..c34922af Binary files /dev/null and b/docs/assets/v6.2.0/task-expanded-overview.png differ diff --git a/docs/assets/v6.2.0/task-expanded-plan.png b/docs/assets/v6.2.0/task-expanded-plan.png new file mode 100644 index 00000000..007b8f8a Binary files /dev/null and b/docs/assets/v6.2.0/task-expanded-plan.png differ diff --git a/docs/assets/v6.2.0/task-expanded-results.png b/docs/assets/v6.2.0/task-expanded-results.png new file mode 100644 index 00000000..ad553633 Binary files /dev/null and b/docs/assets/v6.2.0/task-expanded-results.png differ diff --git a/docs/assets/v6.2.0/task-expanded-run.png b/docs/assets/v6.2.0/task-expanded-run.png new file mode 100644 index 00000000..17cc2e96 Binary files /dev/null and b/docs/assets/v6.2.0/task-expanded-run.png differ diff --git a/docs/assets/v6.2.0/task-workspace.png b/docs/assets/v6.2.0/task-workspace.png new file mode 100644 index 00000000..89265644 Binary files /dev/null and b/docs/assets/v6.2.0/task-workspace.png differ diff --git a/docs/assets/v6.2.0/workbench-panel.png b/docs/assets/v6.2.0/workbench-panel.png new file mode 100644 index 00000000..1aed1200 Binary files /dev/null and b/docs/assets/v6.2.0/workbench-panel.png differ diff --git a/docs/design/DOCUMENTATION-MEDIA-CONTRACT.md b/docs/design/DOCUMENTATION-MEDIA-CONTRACT.md index a3f8c321..da618f0b 100644 --- a/docs/design/DOCUMENTATION-MEDIA-CONTRACT.md +++ b/docs/design/DOCUMENTATION-MEDIA-CONTRACT.md @@ -6,6 +6,8 @@ Release media must show the candidate that will ship. Keep historical version di `scripts/docs-media/verify.mjs` owns the 14 maintained filenames. Every filename needs an explicit `keep`, `replace`, or `retire` decision and a reason. Both `keep` and `replace` require a current capture; `keep` is not permission to relabel old bytes. Retirement removes maintained references and cannot retire the required Board-to-workspace or mobile-flow interaction GIFs. +The runner also records ten `task--.png` comparisons in the same versioned directory. These remain separate from the maintained inventory in `taskModeAssets`. If any is published, the complete ten-image capture manifest is required. Each has the same candidate and file-hash checks, an actual 1180×900 native window, and measured content dimensions. The manifest records fixture and framing differences: native content capture excludes the frame and shadow retained in the original 6.1.7 task-mode audit. Do not describe unmatched data or host conditions as identical. + Desktop media comes from the actual packaged macOS application. Mobile media uses the supported 390×844 browser viewport. Do not inject a desktop bridge and call a browser screenshot native evidence. GIFs must record the named interaction in progress and visibly complete it; a slideshow made from still screenshots does not qualify. Use deterministic public-safe data. Inspect cropping, padding, focus, labels, active/hover state, theme accuracy, readable scale, and each GIF's playback. Check that maintained visuals contain no retired bottom chat dock, duplicate header controls, Advanced settings badges, or superseded task/template UI. @@ -32,4 +34,10 @@ Inspect the captured stills and play both GIFs and the demo MP4 through their co From the clean documentation commit, run `pnpm docs:verify-media /absolute/path/capture/evidence.json /absolute/path/veritas-kanban.app /absolute/path/capture/publication.json`. The new publication manifest must stay beside its original capture manifest and files. Verification checks that each published asset and both demo-video copies match the original captured bytes, including Git's committed blobs; ignored or uncommitted files cannot stand in for published media. Supply `publication.json` to full release validation. This command performs no new captures and refuses to overwrite an existing publication manifest. -Permitted post-build changes are regular, non-executable README/documentation Markdown, `docs/index.json`, the demo player at `docs/demo/index.html`, the 14 versioned maintained media files, and the two demo MP4 paths. Source, dependency, build, version, executable, or other changes require a new clean build and fresh captures. A documentation-only commit cannot justify rewriting an older manifest, changing recorded bytes, or reusing a different app package. Historical preparation/recapture reports remain diagnostic and cannot be promoted to publication evidence by editing their mode or identity. +Permitted post-build changes are regular, non-executable README/documentation Markdown, `docs/index.json`, `docs/index.html`, the demo player at `docs/demo/index.html`, the 14 versioned maintained media files, the ten explicitly named task-mode comparison PNGs with their complete capture evidence, the optional verified `board-5000.png`, and the two demo MP4 paths. Source, dependency, build, version, executable, or other changes require a new clean build and fresh captures. A documentation-only commit cannot justify rewriting an older manifest, changing recorded bytes, or reusing a different app package. Historical preparation/recapture reports remain diagnostic and cannot be promoted to publication evidence by editing their mode or identity. + +### Focused large-board comparison + +The optional `focusedBoardAssets` capture list permits only `board-5000.png`, at 1360×900 native content pixels. It has the same build, app digest, timestamp, byte-hash and committed-file requirements as the maintained captures. A published focused image requires its original capture entry. + +Run `node --import tsx ../scripts/native-ui/board-performance.mjs ` from `server/`. After that feature run passes, supply its `report.json` as the final argument to `pnpm docs:capture-media capture `. The capture command verifies the same candidate identity and original screenshot hash before incorporating the focused image. This supplements the fourteen maintained assets and ten task-mode views; it does not replace them. diff --git a/docs/features/squad-chat.md b/docs/features/squad-chat.md index 6ccd36a1..6e60db2a 100644 --- a/docs/features/squad-chat.md +++ b/docs/features/squad-chat.md @@ -10,16 +10,16 @@ Squad Chat stores and streams messages. It does not wake an external agent proce ## Demo Screenshots -These release-safe captures use dummy content from the current 6.1.7 app +These release-safe captures use dummy content from the local 6.2.0 candidate surfaces. -![Squad Chat coordination in Veritas Kanban 6.1.7](../assets/v6.1.7/squad-chat.png) +![Squad Chat coordination in Veritas Kanban 6.2.0](../assets/v6.2.0/squad-chat.png) The Workbench view shows threaded agent coordination with search, unread count, pinned decision state, acknowledgements, task/run chips, model attribution, a human reply, and system lifecycle events. -![Notification and reply adapter settings in Veritas Kanban 6.1.7](../assets/v6.1.7/notification-adapters.png) +![Notification and reply adapter settings in Veritas Kanban 6.2.0](../assets/v6.2.0/notification-adapters.png) The Notifications view shows local Squad Chat health, outbound webhook posture, inbound wake/reply health, payload redaction, and the Microsoft Teams adapter diff --git a/docs/index.html b/docs/index.html index 22c142ce..0c37246f 100644 --- a/docs/index.html +++ b/docs/index.html @@ -24,7 +24,7 @@ @@ -37,7 +37,7 @@ /> diff --git a/docs/releases/v6.2.0-comparison.md b/docs/releases/v6.2.0-comparison.md new file mode 100644 index 00000000..78cb2867 --- /dev/null +++ b/docs/releases/v6.2.0-comparison.md @@ -0,0 +1,219 @@ +# Veritas Kanban 6.1.7 → 6.2.0 visual comparison + +**Local review candidate; not a released build.** All original views have current equivalents. Every new still was inspected, both original and new GIFs were played through complete cycles, and the new MP4 played to completion without an error. The original images are verified against retained manifests. + +The original 6.1.7 capture used build `ed5094c6a5f9dd6958ceb952d8d018a40135bf33`, bundle `io.digitalmeld.veritas-kanban`, and packaged-app SHA-256 `a23df6443940aff4d9b5013b97ab753b986a63f3bb15dc0d3a8c6aaace866f94`. All fourteen maintained assets match the recovered capture manifest, and all ten task-mode images match their [retained manifest](../assets/v6.1.7/task-mode-audit/manifest.json). Original files remain unchanged. + +Desktop maintained captures use 1700×760 content dimensions; mobile browser captures use 390×844. The original task-mode audit used a 1180×900 native window and included its frame and shadow. The current capture records 1180×900 window content without the original frame/shadow. The original task-mode fixture, “Prototype AI triage,” was blocked with failed-run history; the new “Prepare the release candidate” fixture is To Do with no run. Differences in readiness, run results and history reflect those fixture states and do not establish a behavioral improvement. New captures use macOS 26.6.2; the original maintained captures used macOS 15.7.9. + +The [media capture contract](../design/DOCUMENTATION-MEDIA-CONTRACT.md) binds new images to the actual packaged candidate and checks original and committed file hashes. Visual review and full GIF playback are separate acceptance steps. + +Candidate build: `a2b8fbba0e2851d65611238624d8c9de347a2a27`; whole-app SHA-256: `476a61cbc81f2a78b76657a87ccc98e2be645d2886d78e5df5fe51d7424dedcb`. All new media comes from this candidate except the explicitly labeled mobile browser views. + +## Maintained views + +### Board overview + +The candidate shows the expanded navigation rail with text labels; the retained baseline has the rail collapsed. The same five tasks occupy the same columns. This default-layout difference changes the card width and is not a performance comparison. + +| 6.1.7 original | 6.2.0 candidate | +| --------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | +| [Open original](../assets/v6.1.7/board-overview.png)
![Board overview, 6.1.7](../assets/v6.1.7/board-overview.png) | [Open candidate](../assets/v6.2.0/board-overview.png)
![Board overview, 6.2.0](../assets/v6.2.0/board-overview.png) | + +### Board to workspace + +Both recordings use the same input sequence: board, open in Plan, select Plan, switch to Overview, close to board. Sampling is 8 fps with eight held frames after each completed stage; actual action duration can differ. The candidate uses the lighter task surface and background blur. + +| 6.1.7 original | 6.2.0 candidate | +| --------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | +| [Open original](../assets/v6.1.7/board-to-workspace.gif)
![Board to workspace, 6.1.7](../assets/v6.1.7/board-to-workspace.gif) | [Open candidate](../assets/v6.2.0/board-to-workspace.gif)
![Board to workspace, 6.2.0](../assets/v6.2.0/board-to-workspace.gif) | + +### Task workspace + +The task remains in Plan with the same description and status. The candidate uses a lighter workspace surface and blurs the board behind it; detail loading and focus behavior were checked separately. + +| 6.1.7 original | 6.2.0 candidate | +| --------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | +| [Open original](../assets/v6.1.7/task-workspace.png)
![Task workspace, 6.1.7](../assets/v6.1.7/task-workspace.png) | [Open candidate](../assets/v6.2.0/task-workspace.png)
![Task workspace, 6.2.0](../assets/v6.2.0/task-workspace.png) | + +### Settings navigation + +The baseline places Settings over the board. The candidate uses a separate modeless native window, adds search above the section content, and replaces the Dark mode switch with a System/Light/Dark appearance selector. Persistence was checked separately. + +| 6.1.7 original | 6.2.0 candidate | +| ------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- | +| [Open original](../assets/v6.1.7/settings-navigation.png)
![Settings navigation, 6.1.7](../assets/v6.1.7/settings-navigation.png) | [Open candidate](../assets/v6.2.0/settings-navigation.png)
![Settings navigation, 6.2.0](../assets/v6.2.0/settings-navigation.png) | + +### Agent providers + +The Agents controls remain in the same section within the new native Settings window. The wider surface exposes the provider list alongside persistent section navigation and search. + +| 6.1.7 original | 6.2.0 candidate | +| ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------- | +| [Open original](../assets/v6.1.7/agent-providers.png)
![Agent providers, 6.1.7](../assets/v6.1.7/agent-providers.png) | [Open candidate](../assets/v6.2.0/agent-providers.png)
![Agent providers, 6.2.0](../assets/v6.2.0/agent-providers.png) | + +### Maintenance + +Maintenance moves with Settings into its own native window. Storage and health values belong to each disposable capture profile; they are not a before/after operational measurement. + +| 6.1.7 original | 6.2.0 candidate | +| -------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | +| [Open original](../assets/v6.1.7/maintenance-center.png)
![Maintenance, 6.1.7](../assets/v6.1.7/maintenance-center.png) | [Open candidate](../assets/v6.2.0/maintenance-center.png)
![Maintenance, 6.2.0](../assets/v6.2.0/maintenance-center.png) | + +### Notifications + +Notifications remains grouped by health, adapters, delivery and preferences inside the separate Settings window. Fixture connections are disabled. Targeted notification storage updates were checked separately. + +| 6.1.7 original | 6.2.0 candidate | +| ---------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | +| [Open original](../assets/v6.1.7/notification-adapters.png)
![Notifications, 6.1.7](../assets/v6.1.7/notification-adapters.png) | [Open candidate](../assets/v6.2.0/notification-adapters.png)
![Notifications, 6.2.0](../assets/v6.2.0/notification-adapters.png) | + +### Command palette + +The command list keeps its bounded palette and visible keyboard guidance. The board behind it reflects the different navigation-rail state; native command delivery was verified separately. + +| 6.1.7 original | 6.2.0 candidate | +| ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------- | +| [Open original](../assets/v6.1.7/command-palette.png)
![Command palette, 6.1.7](../assets/v6.1.7/command-palette.png) | [Open candidate](../assets/v6.2.0/command-palette.png)
![Command palette, 6.2.0](../assets/v6.2.0/command-palette.png) | + +### Workbench + +Board Chat remains in the right dock and preserves the task board beside it. The same public-safe empty conversation is shown; the candidate has expanded navigation labels. + +| 6.1.7 original | 6.2.0 candidate | +| ------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------- | +| [Open original](../assets/v6.1.7/workbench-panel.png)
![Workbench, 6.1.7](../assets/v6.1.7/workbench-panel.png) | [Open candidate](../assets/v6.2.0/workbench-panel.png)
![Workbench, 6.2.0](../assets/v6.2.0/workbench-panel.png) | + +### Squad Chat + +Squad Chat remains selected inside the right dock, with the same empty conversation and Human sender. No external message was sent for this comparison. + +| 6.1.7 original | 6.2.0 candidate | +| --------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | +| [Open original](../assets/v6.1.7/squad-chat.png)
![Squad Chat, 6.1.7](../assets/v6.1.7/squad-chat.png) | [Open candidate](../assets/v6.2.0/squad-chat.png)
![Squad Chat, 6.2.0](../assets/v6.2.0/squad-chat.png) | + +### Mobile board + +The dedicated mobile board keeps stacked columns, direct status controls and bottom navigation. This view does not show the separate compact filter controls, which were checked in the responsive board workflow. + +| 6.1.7 original | 6.2.0 candidate | +| --------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | +| [Open original](../assets/v6.1.7/mobile-board.png)
![Mobile board, 6.1.7](../assets/v6.1.7/mobile-board.png) | [Open candidate](../assets/v6.2.0/mobile-board.png)
![Mobile board, 6.2.0](../assets/v6.2.0/mobile-board.png) | + +### Mobile task flow + +Both browser recordings open the same task in Plan, select Plan, switch to Overview and return to the board. The candidate updates the task surface while retaining the mobile mode selector. + +| 6.1.7 original | 6.2.0 candidate | +| ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | +| [Open original](../assets/v6.1.7/mobile-flow.gif)
![Mobile task flow, 6.1.7](../assets/v6.1.7/mobile-flow.gif) | [Open candidate](../assets/v6.2.0/mobile-flow.gif)
![Mobile task flow, 6.2.0](../assets/v6.2.0/mobile-flow.gif) | + +### Mobile task workspace + +At the same 390×844 browser viewport, Plan retains its mode and section selectors, description editor and status fields. The candidate changes the surface treatment without replacing the mobile navigation pattern. + +| 6.1.7 original | 6.2.0 candidate | +| ------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- | +| [Open original](../assets/v6.1.7/mobile-task-workspace.png)
![Mobile task workspace, 6.1.7](../assets/v6.1.7/mobile-task-workspace.png) | [Open candidate](../assets/v6.2.0/mobile-task-workspace.png)
![Mobile task workspace, 6.2.0](../assets/v6.2.0/mobile-task-workspace.png) | + +### Mobile Settings + +Browser Settings remains a dialog at 390×844. The candidate adds Settings search and the appearance selector; this is distinct from the modeless Mac Settings window. + +| 6.1.7 original | 6.2.0 candidate | +| ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------- | +| [Open original](../assets/v6.1.7/mobile-settings.png)
![Mobile Settings, 6.1.7](../assets/v6.1.7/mobile-settings.png) | [Open candidate](../assets/v6.2.0/mobile-settings.png)
![Mobile Settings, 6.2.0](../assets/v6.2.0/mobile-settings.png) | + +## Every task workspace mode + +The same task must stay mounted through Overview, Plan, Run, Results, and History, followed by the expanded presentation. The original task-mode fixture is separate from the maintained documentation fixture. + +### Drawer: Overview + +The active mode and task context remain visible in this presentation. Task content differs from the original showcase fixture; compare layout and navigation, not the displayed run outcome. + +| 6.1.7 original | 6.2.0 candidate | +| --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | +| [Open original](../assets/v6.1.7/task-mode-audit/drawer-overview.png)
![Drawer: Overview, 6.1.7](../assets/v6.1.7/task-mode-audit/drawer-overview.png) | [Open candidate](../assets/v6.2.0/task-drawer-overview.png)
![Drawer: Overview, 6.2.0](../assets/v6.2.0/task-drawer-overview.png) | + +### Drawer: Plan + +The active mode and task context remain visible in this presentation. Task content differs from the original showcase fixture; compare layout and navigation, not the displayed run outcome. + +| 6.1.7 original | 6.2.0 candidate | +| --------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | +| [Open original](../assets/v6.1.7/task-mode-audit/drawer-plan.png)
![Drawer: Plan, 6.1.7](../assets/v6.1.7/task-mode-audit/drawer-plan.png) | [Open candidate](../assets/v6.2.0/task-drawer-plan.png)
![Drawer: Plan, 6.2.0](../assets/v6.2.0/task-drawer-plan.png) | + +### Drawer: Run + +The active mode and task context remain visible in this presentation. Task content differs from the original showcase fixture; compare layout and navigation, not the displayed run outcome. + +| 6.1.7 original | 6.2.0 candidate | +| ------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------- | +| [Open original](../assets/v6.1.7/task-mode-audit/drawer-run.png)
![Drawer: Run, 6.1.7](../assets/v6.1.7/task-mode-audit/drawer-run.png) | [Open candidate](../assets/v6.2.0/task-drawer-run.png)
![Drawer: Run, 6.2.0](../assets/v6.2.0/task-drawer-run.png) | + +### Drawer: Results + +The active mode and task context remain visible in this presentation. Task content differs from the original showcase fixture; compare layout and navigation, not the displayed run outcome. + +| 6.1.7 original | 6.2.0 candidate | +| ------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------- | +| [Open original](../assets/v6.1.7/task-mode-audit/drawer-results.png)
![Drawer: Results, 6.1.7](../assets/v6.1.7/task-mode-audit/drawer-results.png) | [Open candidate](../assets/v6.2.0/task-drawer-results.png)
![Drawer: Results, 6.2.0](../assets/v6.2.0/task-drawer-results.png) | + +### Drawer: History + +The active mode and task context remain visible in this presentation. Task content differs from the original showcase fixture; compare layout and navigation, not the displayed run outcome. + +| 6.1.7 original | 6.2.0 candidate | +| ------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------- | +| [Open original](../assets/v6.1.7/task-mode-audit/drawer-history.png)
![Drawer: History, 6.1.7](../assets/v6.1.7/task-mode-audit/drawer-history.png) | [Open candidate](../assets/v6.2.0/task-drawer-history.png)
![Drawer: History, 6.2.0](../assets/v6.2.0/task-drawer-history.png) | + +### Expanded: Overview + +The active mode and task context remain visible in this presentation. Task content differs from the original showcase fixture; compare layout and navigation, not the displayed run outcome. + +| 6.1.7 original | 6.2.0 candidate | +| --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | +| [Open original](../assets/v6.1.7/task-mode-audit/expanded-overview.png)
![Expanded: Overview, 6.1.7](../assets/v6.1.7/task-mode-audit/expanded-overview.png) | [Open candidate](../assets/v6.2.0/task-expanded-overview.png)
![Expanded: Overview, 6.2.0](../assets/v6.2.0/task-expanded-overview.png) | + +### Expanded: Plan + +The active mode and task context remain visible in this presentation. Task content differs from the original showcase fixture; compare layout and navigation, not the displayed run outcome. + +| 6.1.7 original | 6.2.0 candidate | +| --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | +| [Open original](../assets/v6.1.7/task-mode-audit/expanded-plan.png)
![Expanded: Plan, 6.1.7](../assets/v6.1.7/task-mode-audit/expanded-plan.png) | [Open candidate](../assets/v6.2.0/task-expanded-plan.png)
![Expanded: Plan, 6.2.0](../assets/v6.2.0/task-expanded-plan.png) | + +### Expanded: Run + +The active mode and task context remain visible in this presentation. Task content differs from the original showcase fixture; compare layout and navigation, not the displayed run outcome. + +| 6.1.7 original | 6.2.0 candidate | +| ------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------- | +| [Open original](../assets/v6.1.7/task-mode-audit/expanded-run.png)
![Expanded: Run, 6.1.7](../assets/v6.1.7/task-mode-audit/expanded-run.png) | [Open candidate](../assets/v6.2.0/task-expanded-run.png)
![Expanded: Run, 6.2.0](../assets/v6.2.0/task-expanded-run.png) | + +### Expanded: Results + +The active mode and task context remain visible in this presentation. Task content differs from the original showcase fixture; compare layout and navigation, not the displayed run outcome. + +| 6.1.7 original | 6.2.0 candidate | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------- | +| [Open original](../assets/v6.1.7/task-mode-audit/expanded-results.png)
![Expanded: Results, 6.1.7](../assets/v6.1.7/task-mode-audit/expanded-results.png) | [Open candidate](../assets/v6.2.0/task-expanded-results.png)
![Expanded: Results, 6.2.0](../assets/v6.2.0/task-expanded-results.png) | + +### Expanded: History + +The active mode and task context remain visible in this presentation. Task content differs from the original showcase fixture; compare layout and navigation, not the displayed run outcome. + +| 6.1.7 original | 6.2.0 candidate | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------- | +| [Open original](../assets/v6.1.7/task-mode-audit/expanded-history.png)
![Expanded: History, 6.1.7](../assets/v6.1.7/task-mode-audit/expanded-history.png) | [Open candidate](../assets/v6.2.0/task-expanded-history.png)
![Expanded: History, 6.2.0](../assets/v6.2.0/task-expanded-history.png) | + +## Focused large-board comparison + +### Board with 5,000 tasks + +Both packages used the same synthetic task generator, 1360×900 content size, dark theme and this macOS 26.6.2 host. The candidate scrolls the selected offscreen task into view after keyboard movement; the baseline retained the first rows. Card truncation remains deliberate. The image shows selection and layout; performance and persistence claims come from the separately recorded packaged journey. + +| 6.1.7 original | 6.2.0 candidate | +| --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ | +| Original focused capture is retained in the portable comparison evidence; the historical repository directory is unchanged. | ![Large board, 6.2.0](../assets/v6.2.0/board-5000.png) | diff --git a/docs/releases/v6.2.0.md b/docs/releases/v6.2.0.md new file mode 100644 index 00000000..aa11d371 --- /dev/null +++ b/docs/releases/v6.2.0.md @@ -0,0 +1,31 @@ +Veritas Kanban 6.2.0 improves the everyday work of changing settings, navigating a board, and using the macOS application. + +Draft release notes for an unpublished candidate. Packaging, acceptance and publication status are recorded separately in the release candidate evidence packet. + +## What changed + +Settings keeps pending edits when you change sections or close the window. Failed saves remain available for retry, and importing a settings file reports success after the server saves it. On macOS, Settings opens in one reusable native window, so the board stays usable alongside it. Quitting waits for pending settings saves and asks you to return to Settings if a save fails. + +Settings search takes you to the relevant section and control. Appearance offers System, Light and Dark while retaining existing explicit preferences. On a phone, search and the active-filter count stay visible while secondary filters use a compact disclosure. + +Native New Task and Settings commands reach the application. Standard macOS Close, Hide, Quit, zoom and full-screen commands are available. Window restoration accounts for changed displays, and title-bar double-click follows the system preference. Narrowing a window closes background chat without dismissing the task above it. + +Keyboard navigation follows the visible board order and brings selected tasks into view. Shortcut help follows the configured columns. Modified commands and IME composition keep their normal behavior. Primary actions, selected filters and code labels have corrected contrast, and browser setup no longer presents unavailable desktop diagnostics. + +Large boards load compact card records and window long columns. Full descriptions and run details load when you open a task; search still checks the complete description. Task reads revalidate after changes, and moving a task remains valid in columns containing more than 1,000 cards. Notification updates change the affected SQLite rows, while file-backed notification storage preserves recoverable data on write failures. + +CLI and MCP commands reject empty or ambiguous task identifiers, enforce request deadlines, and preserve actionable API error information. Use a full task ID when a suffix matches more than one task. + +## Compatibility and upgrade + +The public REST API remains `v1`. All maintained packages must use 6.2.0 together. Existing full-task and summary APIs remain compatible, and this candidate adds no database migration. Retain a complete stopped-writer backup before an upgrade and keep it until the new runtime is accepted. + +The macOS application requires macOS 13 Ventura or later on Apple silicon. Follow the [install and upgrade guide](../V6-UPGRADE-INSTALL-ADMIN-GUIDE.md) after the signed release becomes available. An unsigned local candidate is for verification and does not establish stable distribution readiness. + +## Rollback + +Restore the complete previous application bundle and, when needed, its matching stopped-writer backup. Do not mix bundled package versions. A System appearance preference is new in 6.2.0; select an explicit Light or Dark preference before returning to a version that does not support it. + +See the [changelog](../../CHANGELOG.md) for user-visible changes and the [release candidate evidence packet](../V6-RC-EVIDENCE-PACKET.md) for the distinction between local verification, signing, installation and publication. + +See the [before/after gallery](v6.2.0-comparison.md) for all retained views and the [capture provenance](../assets/v6.2.0/README.md) for the verified local build. diff --git a/docs/testing/board-performance.md b/docs/testing/board-performance.md index 3f723880..59ff57a9 100644 --- a/docs/testing/board-performance.md +++ b/docs/testing/board-performance.md @@ -51,7 +51,7 @@ Candidate acceptance budgets, chosen before candidate measurements: Run from the repository root with a clean build-bound candidate and a new output directory: ```bash -node --import tsx scripts/native-ui/board-performance.mjs /absolute/path/candidate.app /absolute/path/new-board-evidence +pnpm --dir server exec node --import tsx ../scripts/native-ui/board-performance.mjs /absolute/path/candidate.app /absolute/path/new-board-evidence ``` The runner uses only an isolated synthetic profile, stops its packaged server before seeding, diff --git a/mcp/package.json b/mcp/package.json index cbe26e1b..659a36f6 100644 --- a/mcp/package.json +++ b/mcp/package.json @@ -1,6 +1,6 @@ { "name": "@veritas-kanban/mcp", - "version": "6.1.7", + "version": "6.2.0", "description": "MCP server for Veritas Kanban", "type": "module", "main": "./dist/index.js", diff --git a/package.json b/package.json index fb2685b3..16bb0a3e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "veritas-kanban", - "version": "6.1.7", + "version": "6.2.0", "private": true, "description": "Local-first task management and AI agent orchestration platform", "author": "Brad Groux ", diff --git a/scripts/docs-media/publication.mjs b/scripts/docs-media/publication.mjs index 80dbf64c..64124818 100644 --- a/scripts/docs-media/publication.mjs +++ b/scripts/docs-media/publication.mjs @@ -4,7 +4,12 @@ import { createHash } from 'node:crypto'; import { readFile, realpath } from 'node:fs/promises'; import path from 'node:path'; import { fileDigest } from '../native-ui/contract.mjs'; -import { maintainedAssets, verifyMediaEvidence } from './verify.mjs'; +import { + maintainedAssets, + taskModeAssets, + focusedBoardAssets, + verifyMediaEvidence, +} from './verify.mjs'; export const publicationSchema = 'documentation-media-publication/v1'; const sha = (value) => typeof value === 'string' && /^[a-f0-9]{40}$/.test(value); @@ -24,7 +29,9 @@ function publicationPath(file, version) { file === 'docs/index.html' || file === 'docs/demo/index.html' || ['assets/demo-overview.mp4', 'docs/assets/demo-overview.mp4'].includes(file) || - maintainedAssets.some((name) => file === `docs/assets/v${version}/${name}`) + [...maintainedAssets, ...taskModeAssets, ...focusedBoardAssets].some( + (name) => file === `docs/assets/v${version}/${name}` + ) ); } @@ -158,7 +165,42 @@ export async function verifyPublishedMedia({ maintainedContents, }); if (errors.length) return errors; - for (const asset of capture.assets) { + const committedTaskModes = taskModeAssets.filter((name) => + git( + root, + 'ls-tree', + expected.publicationCommit, + '--', + `docs/assets/v${expected.version}/${name}` + ).trim() + ); + if (committedTaskModes.length) + assert.deepEqual( + (capture.taskModeAssets ?? []).map((asset) => asset.name).sort(), + [...taskModeAssets].sort(), + 'Published task mode media requires the complete original capture manifest' + ); + for (const name of focusedBoardAssets) { + if ( + git( + root, + 'ls-tree', + expected.publicationCommit, + '--', + `docs/assets/v${expected.version}/${name}` + ).trim() + ) + assert.equal( + capture.focusedBoardAssets?.[0]?.name, + name, + 'Published focused board media requires original capture evidence' + ); + } + for (const asset of [ + ...capture.assets, + ...(capture.taskModeAssets ?? []), + ...(capture.focusedBoardAssets ?? []), + ]) { if (asset.decision === 'retire') continue; verifyCommittedFile(root, expected.publicationCommit, asset.path, asset.sha256); assert.equal( diff --git a/scripts/docs-media/publication.test.mjs b/scripts/docs-media/publication.test.mjs index fad4e1c8..d56a1056 100644 --- a/scripts/docs-media/publication.test.mjs +++ b/scripts/docs-media/publication.test.mjs @@ -253,3 +253,36 @@ test('a symlink cannot substitute an external original capture', async (t) => { await symlink(path.join(f.root, 'docs/assets/v6.1.7/board-overview.png'), file); assert((await verifyPublishedMedia(f.args)).some((error) => error.includes('symlink'))); }); + +test('task mode images cannot enter publication without their original capture manifest', async (t) => { + const f = await fixture(t); + await writeFile( + path.join(f.root, 'docs/assets/v6.1.7/task-drawer-overview.png'), + 'unrecorded image' + ); + f.git('add', '.'); + f.git('commit', '-qm', 'add unrecorded task view'); + f.expected.publicationCommit = f.git('rev-parse', 'HEAD'); + f.publication.publicationCommit = f.expected.publicationCommit; + await writeFile(f.evidencePath, JSON.stringify(f.publication)); + assert( + (await verifyPublishedMedia(f.args)).some((error) => + error.includes('complete original capture manifest') + ) + ); +}); + +test('focused board media cannot be published without original capture evidence', async (t) => { + const f = await fixture(t); + await writeFile(path.join(f.root, 'docs/assets/v6.1.7/board-5000.png'), 'unrecorded image'); + f.git('add', '.'); + f.git('commit', '-qm', 'add unrecorded board'); + f.expected.publicationCommit = f.git('rev-parse', 'HEAD'); + f.publication.publicationCommit = f.expected.publicationCommit; + await writeFile(f.evidencePath, JSON.stringify(f.publication)); + assert( + (await verifyPublishedMedia(f.args)).some((error) => + error.includes('original capture evidence') + ) + ); +}); diff --git a/scripts/docs-media/run.mjs b/scripts/docs-media/run.mjs index d2f560ff..cd23ff87 100644 --- a/scripts/docs-media/run.mjs +++ b/scripts/docs-media/run.mjs @@ -2,22 +2,22 @@ /* global window, document, innerWidth, innerHeight, getComputedStyle, requestAnimationFrame */ import assert from 'node:assert/strict'; import { spawnSync } from 'node:child_process'; -import { mkdir, readFile, realpath, writeFile } from 'node:fs/promises'; +import { copyFile, mkdir, readFile, realpath, writeFile } from 'node:fs/promises'; import path from 'node:path'; import os from 'node:os'; import { chromium, expect } from '@playwright/test'; import { nativeSettingsPage, closeNativeSettings } from '../native-ui/settings-window.mjs'; import { createNativeSession } from '../native-ui/session.mjs'; import { contentSizes, fileDigest, packageDigest } from '../native-ui/contract.mjs'; -import { maintainedAssets, mediaSchema, mediaEvidenceFailures } from './verify.mjs'; +import { maintainedAssets, taskModeAssets, mediaSchema, mediaEvidenceFailures } from './verify.mjs'; import { encodeInteraction, recordInteraction } from './record.mjs'; import { finalizeCapture } from './finalize.mjs'; const root = path.resolve(import.meta.dirname, '../..'); -const [appArgument, outputArgument, mode = 'capture'] = process.argv.slice(2); +const [appArgument, outputArgument, mode = 'capture', focusedBoardArgument] = process.argv.slice(2); assert( appArgument?.endsWith('.app') && outputArgument && mode === 'capture', - 'Usage: node scripts/docs-media/run.mjs [capture]' + 'Usage: node scripts/docs-media/run.mjs [capture] [board-report.json]' ); assert.equal(process.platform, 'darwin', 'Desktop documentation captures require macOS'); const output = path.join( @@ -50,6 +50,26 @@ const report = { startedAt: new Date().toISOString(), macOS: os.release(), assets: [], + fixture: { + source: 'scripts/docs-media/run.mjs', + kind: 'synthetic-public-safe', + taskTitle: 'Prepare the release candidate', + status: 'todo', + }, + taskModeComparison: { + baseline: 'docs/assets/v6.1.7/task-mode-audit/manifest.json', + matched: [ + 'native window 1180x900', + 'dark theme', + 'Overview/Plan/Run/Results/History', + 'drawer then expanded', + ], + unmatched: [ + 'Original showcase dataset is unavailable; uses the documentation fixture.', + 'Native content capture excludes the baseline window frame and shadow.', + 'Host OS differs from the baseline macOS 15.7.9.', + ], + }, }; const persist = () => writeFile(path.join(output, 'evidence.json'), JSON.stringify(report, null, 2) + '\n'); @@ -101,7 +121,7 @@ async function settle() { () => new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve))) ); } -async function capture() { +async function capture(taskMode = false) { if (mobile) { assert.deepEqual(await page.evaluate(() => [innerWidth, innerHeight]), [390, 844]); assert.equal(await page.evaluate(() => typeof window.veritasDesktop), 'undefined'); @@ -112,6 +132,9 @@ async function capture() { scaleFactor: 1, }; } + // DOM readiness and animation frames can precede Electron's last composited frame. + // Synchronize the renderer first; the published bytes still come from capturePage. + await page.screenshot({ fullPage: false }); const native = await app.evaluate(async ({ BrowserWindow, screen }, url) => { const win = BrowserWindow.getAllWindows().find((w) => w.webContents.getURL() === url); return { @@ -121,19 +144,28 @@ async function capture() { png: (await win.capturePage()).toPNG().toString('base64'), }; }, page.url()); - assert.equal(native.contentBounds.width, contentSizes.normal.width); - assert.equal(native.contentBounds.height, contentSizes.normal.height); + if (taskMode) { + assert.equal(native.bounds.width, 1180); + assert.equal(native.bounds.height, 900); + assert.equal(native.contentBounds.width, 1180); + assert(native.contentBounds.height >= 760 && native.contentBounds.height <= 900); + } else { + assert.equal(native.contentBounds.width, contentSizes.normal.width); + assert.equal(native.contentBounds.height, contentSizes.normal.height); + } return { bytes: Buffer.from(native.png, 'base64'), - width: contentSizes.normal.width, - height: contentSizes.normal.height, + width: native.contentBounds.width, + height: native.contentBounds.height, scaleFactor: native.scaleFactor, nativeWindow: { bounds: native.bounds, contentBounds: native.contentBounds }, }; } async function recordAsset(name, captured, method, recording) { const file = path.join(output, name); - report.assets.push({ + const taskMode = taskModeAssets.includes(name); + const destination = taskMode ? (report.taskModeAssets ??= []) : report.assets; + destination.push({ name, decision: 'replace', reason: 'Recaptured the converged interface from the candidate', @@ -149,6 +181,7 @@ async function recordAsset(name, captured, method, recording) { height: captured.height, scaleFactor: captured.scaleFactor, nativeWindow: captured.nativeWindow, + ...(taskMode ? { framing: 'native-content-without-window-frame' } : {}), method, capturedAt: new Date().toISOString(), }, @@ -159,7 +192,7 @@ async function recordAsset(name, captured, method, recording) { } async function still(name) { await settle(); - const captured = await capture(); + const captured = await capture(taskModeAssets.includes(name)); await writeFile(path.join(output, name), captured.bytes, { flag: 'wx' }); await recordAsset(name, captured, 'window-capture'); } @@ -350,8 +383,58 @@ try { await still('mobile-task-workspace.png'); await button('Close task workspace').click(); await button('Mobile settings').click(); - await expect(page.getByRole('dialog', { name: /^Settings(?: Board Only)?$/ })).toBeVisible(); + const mobileSettings = page.getByRole('dialog', { name: /^Settings(?: Board Only)?$/ }); + await expect(mobileSettings).toBeVisible(); + await expect(mobileSettings.getByRole('heading', { name: 'General', exact: true })).toBeVisible(); + await expect(mobileSettings.getByRole('combobox', { name: 'Appearance', exact: true })).toBeVisible(); await still('mobile-settings.png'); + page = boardPage; + mobile = false; + await openTask(); + // Match the retained task-mode audit's native window dimensions. Native + // capturePage excludes its frame/shadow; that framing difference is recorded. + await app.evaluate(({ BrowserWindow }, url) => { + const win = BrowserWindow.getAllWindows().find((w) => w.webContents.getURL() === url); + win.setSize(1180, 900); + }, page.url()); + for (const presentation of ['drawer', 'expanded']) { + if (presentation === 'expanded') await button('Expand task workspace').click(); + for (const name of ['Overview', 'Plan', 'Run', 'Results', 'History']) { + await workspaceMode(name); + await still(`task-${presentation}-${name.toLowerCase()}.png`); + } + } + await app.evaluate(({ BrowserWindow }, url) => { + const win = BrowserWindow.getAllWindows().find((w) => w.webContents.getURL() === url); + win.setContentSize(1700, 760); + }, page.url()); + await button('Close task workspace').click(); + if (focusedBoardArgument) { + const boardReportPath = await realpath(focusedBoardArgument); + const board = JSON.parse(await readFile(boardReportPath, 'utf8')); + assert.equal(board.status, 'passed', 'Focused board capture requires a passing feature run'); + for (const key of ['commit', 'version', 'packageDigest']) + assert.equal(board[key], report[key], `Focused board capture has a different ${key}`); + const entry = board.entries?.find((item) => item.count === 5000); + assert.deepEqual(entry?.failures, [], 'Focused board capture missed a performance budget'); + assert.equal(entry?.screenshot?.name, 'board-5000.png'); + const source = path.join(path.dirname(boardReportPath), entry.screenshot.name); + assert.equal(await realpath(source), source, 'Focused capture traverses a symlink'); + assert.equal( + await fileDigest(source), + entry.screenshot.sha256, + 'Focused capture bytes changed' + ); + await copyFile(source, path.join(output, entry.screenshot.name)); + report.focusedBoardAssets = [ + { + ...entry.screenshot, + decision: 'replace', + reason: 'Focused comparison of the matched 5000-task board fixture.', + path: `docs/assets/v${version}/${entry.screenshot.name}`, + }, + ]; + } report.assets.sort((a, b) => maintainedAssets.indexOf(a.name) - maintainedAssets.indexOf(b.name)); report.completedAt = new Date().toISOString(); report.status = 'captured'; diff --git a/scripts/docs-media/verify.mjs b/scripts/docs-media/verify.mjs index 16934fb2..93bf9d5c 100644 --- a/scripts/docs-media/verify.mjs +++ b/scripts/docs-media/verify.mjs @@ -20,6 +20,13 @@ export const maintainedAssets = [ 'workbench-panel.png', ]; +export const taskModeAssets = ['drawer', 'expanded'].flatMap((presentation) => + ['overview', 'plan', 'run', 'results', 'history'].map( + (mode) => `task-${presentation}-${mode}.png` + ) +); +export const focusedBoardAssets = ['board-5000.png']; + const digest = (value) => typeof value === 'string' && /^[a-f0-9]{64}$/.test(value); const text = (value) => typeof value === 'string' && value.trim().length > 0; @@ -75,14 +82,38 @@ export function mediaEvidenceFailures(report, expected, now = Date.now()) { new Set(assets.map((asset) => asset?.name)).size !== maintainedAssets.length ) errors.push('missing or duplicate maintained media decisions'); - for (const name of maintainedAssets) { - const asset = assets.find((item) => item?.name === name); + const supplemental = report.taskModeAssets ?? []; + const focused = report.focusedBoardAssets ?? []; + if (!Array.isArray(focused)) return [...errors, 'invalid focused board capture list']; + if ( + report.focusedBoardAssets !== undefined && + (focused.length !== 1 || focused[0]?.name !== focusedBoardAssets[0]) + ) + errors.push('missing or duplicate focused board capture'); + if (!Array.isArray(supplemental)) return [...errors, 'invalid task mode capture list']; + if ( + report.taskModeAssets !== undefined && + (supplemental.length !== taskModeAssets.length || + new Set(supplemental.map((asset) => asset?.name)).size !== taskModeAssets.length) + ) + errors.push('missing or duplicate task mode captures'); + const expectedNames = [ + ...maintainedAssets, + ...(report.taskModeAssets === undefined ? [] : taskModeAssets), + ...(report.focusedBoardAssets === undefined ? [] : focusedBoardAssets), + ]; + for (const name of expectedNames) { + const asset = [...assets, ...supplemental, ...focused].find((item) => item?.name === name); if (!asset || !['keep', 'replace', 'retire'].includes(asset.decision) || !text(asset.reason)) { errors.push(`${name}: missing explicit media decision and reason`); continue; } if (asset.decision === 'retire') { - if (name.endsWith('.gif')) + if ( + name.endsWith('.gif') || + taskModeAssets.includes(name) || + focusedBoardAssets.includes(name) + ) errors.push(`${name}: named interaction GIF must remain maintained`); if (asset.path !== undefined || asset.sha256 !== undefined || asset.capture !== undefined) errors.push(`${name}: retired media must not claim a current capture`); @@ -100,9 +131,25 @@ export function mediaEvidenceFailures(report, expected, now = Date.now()) { if (capture?.boundary !== boundary) errors.push(`${name}: wrong capture boundary`); if (boundary === 'packaged-macos' && capture?.packaged !== true) errors.push(`${name}: desktop capture did not use the packaged application`); - const viewport = - boundary === 'mobile-browser' ? { width: 390, height: 844 } : contentSizes.normal; - if (capture?.width !== viewport.width || capture?.height !== viewport.height) + const viewport = focusedBoardAssets.includes(name) + ? { width: 1360, height: 900 } + : boundary === 'mobile-browser' + ? { width: 390, height: 844 } + : contentSizes.normal; + const taskMode = taskModeAssets.includes(name); + if ( + taskMode && + (capture?.nativeWindow?.bounds?.width !== 1180 || + capture?.nativeWindow?.bounds?.height !== 900 || + capture?.width !== 1180 || + capture?.height < 760 || + capture?.height > 900 || + capture?.nativeWindow?.contentBounds?.width !== capture?.width || + capture?.nativeWindow?.contentBounds?.height !== capture?.height || + capture?.framing !== 'native-content-without-window-frame') + ) + errors.push(`${name}: missing matched task window geometry or explicit framing`); + if (!taskMode && (capture?.width !== viewport.width || capture?.height !== viewport.height)) errors.push( `${name}: wrong supported ${boundary === 'mobile-browser' ? 'mobile' : 'packaged macOS'} viewport` ); @@ -172,7 +219,11 @@ export async function verifyMediaEvidence({ evidencePath, root, expected, mainta } catch (error) { return [`missing candidate media directory: ${error.message}`]; } - for (const asset of report.assets) { + for (const asset of [ + ...report.assets, + ...(report.taskModeAssets ?? []), + ...(report.focusedBoardAssets ?? []), + ]) { if (asset.decision === 'retire') continue; try { const intendedFile = path.join(root, asset.path); diff --git a/scripts/docs-media/verify.test.mjs b/scripts/docs-media/verify.test.mjs index 3dab9057..f5692026 100644 --- a/scripts/docs-media/verify.test.mjs +++ b/scripts/docs-media/verify.test.mjs @@ -1,3 +1,4 @@ +/* global structuredClone */ import assert from 'node:assert/strict'; import { createHash } from 'node:crypto'; import { mkdtemp, mkdir, rm, symlink, writeFile } from 'node:fs/promises'; @@ -6,6 +7,7 @@ import path from 'node:path'; import test from 'node:test'; import { maintainedAssets, + taskModeAssets, mediaSchema, mediaEvidenceFailures, staleMediaReferences, @@ -223,3 +225,58 @@ test('missing maintained reference inventory cannot silently skip stale-referenc 'missing maintained documentation reference inventory', ]); }); + +test('task mode comparison binds every view to native geometry and candidate identity', () => { + const report = fixture(); + report.taskModeAssets = taskModeAssets.map((name) => ({ + ...structuredClone(report.assets[0]), + name, + path: `docs/assets/v6.1.7/${name}`, + capture: { + ...report.assets[0].capture, + width: 1180, + height: 872, + nativeWindow: { + bounds: { width: 1180, height: 900 }, + contentBounds: { width: 1180, height: 872 }, + }, + framing: 'native-content-without-window-frame', + }, + })); + assert.deepEqual(mediaEvidenceFailures(report, expected), []); + for (const mutate of [ + (r) => r.taskModeAssets.pop(), + (r) => (r.taskModeAssets[0].capture.commit = 'c'.repeat(40)), + (r) => (r.taskModeAssets[0].capture.nativeWindow.bounds.height = 760), + (r) => delete r.taskModeAssets[0].capture.framing, + (r) => (r.taskModeAssets[0].decision = 'retire'), + (r) => (r.taskModeAssets[0].path = 'docs/assets/v6.1.7/../escape.png'), + ]) { + const changed = structuredClone(report); + mutate(changed); + assert(mediaEvidenceFailures(changed, expected).length); + } +}); + +test('focused large-board capture requires the exact native viewport and build', () => { + const report = fixture(); + report.focusedBoardAssets = [ + { + ...structuredClone(report.assets[0]), + name: 'board-5000.png', + path: 'docs/assets/v6.1.7/board-5000.png', + capture: { ...report.assets[0].capture, width: 1360, height: 900 }, + }, + ]; + assert.deepEqual(mediaEvidenceFailures(report, expected), []); + for (const mutate of [ + (r) => r.focusedBoardAssets.push(r.focusedBoardAssets[0]), + (r) => (r.focusedBoardAssets[0].capture.packageDigest = 'c'.repeat(64)), + (r) => (r.focusedBoardAssets[0].capture.width = 1700), + (r) => (r.focusedBoardAssets[0].decision = 'retire'), + ]) { + const changed = structuredClone(report); + mutate(changed); + assert(mediaEvidenceFailures(changed, expected).length); + } +}); diff --git a/scripts/native-ui/board-performance.mjs b/scripts/native-ui/board-performance.mjs index 9c46a800..5d9aa5b9 100644 --- a/scripts/native-ui/board-performance.mjs +++ b/scripts/native-ui/board-performance.mjs @@ -6,6 +6,7 @@ import path from 'node:path'; import { mkdir, writeFile, readFile, realpath } from 'node:fs/promises'; import { pathToFileURL } from 'node:url'; import { performance } from 'node:perf_hooks'; +import { verifyLargeBoardJourney } from './large-board-journey.mjs'; const root = path.resolve(import.meta.dirname, '../..'); const { createNativeSession } = await import( pathToFileURL(`${root}/scripts/native-ui/session.mjs`) @@ -16,7 +17,9 @@ const { SqliteDatabase } = await import( const { SqliteTaskRepository } = await import( pathToFileURL(`${root}/server/src/storage/sqlite/task-repository.ts`) ); -const { packageDigest } = await import(pathToFileURL(`${root}/scripts/native-ui/contract.mjs`)); +const { packageDigest, fileDigest } = await import( + pathToFileURL(`${root}/scripts/native-ui/contract.mjs`) +); const require = createRequire(`${root}/package.json`); const { expect } = require('@playwright/test'); assert( @@ -174,6 +177,24 @@ try { }) ); await page.screenshot({ path: `${output}/board-${count}.png` }); + entry.screenshot = { + name: `board-${count}.png`, + sha256: await fileDigest(`${output}/board-${count}.png`), + capture: { + commit, + version, + packageDigest: report.packageDigest, + boundary: 'packaged-macos', + packaged: true, + method: 'window-capture', + capturedAt: new Date().toISOString(), + ...(await page.evaluate(() => ({ + width: window.innerWidth, + height: window.innerHeight, + scaleFactor: window.devicePixelRatio, + }))), + }, + }; entry.budgets = { loadRenderMs: count === 100 ? 1000 : count === 1000 ? 2000 : 5000, inputP95Ms: 100, @@ -190,6 +211,10 @@ try { if (responses.some((response) => !response.query.includes('view=board'))) entry.failures.push('Initial board requested full task records'); await persist(); + if (count === 5000) { + report.journey = await verifyLargeBoardJourney(page, count); + await persist(); + } } report.status = report.entries.some((entry) => entry.failures.length) ? 'failed' : 'passed'; if (report.status === 'failed') process.exitCode = 1; diff --git a/scripts/native-ui/contract.mjs b/scripts/native-ui/contract.mjs index 39a2c2bd..3c4f6917 100644 --- a/scripts/native-ui/contract.mjs +++ b/scripts/native-ui/contract.mjs @@ -73,21 +73,18 @@ export const requiredEntries = modes.flatMap((mode) => ); export function requiresOverlay(id) { const state = id.split('/')[1]; - return ( - state?.startsWith('settings-') || - [ - 'task-chat', - 'task-drawer', - 'task-expanded', - 'create-task', - 'create-template', - 'edit-template', - 'search', - 'command-palette', - 'preview', - 'confirmation', - ].includes(state) - ); + return [ + 'task-chat', + 'task-drawer', + 'task-expanded', + 'create-task', + 'create-template', + 'edit-template', + 'search', + 'command-palette', + 'preview', + 'confirmation', + ].includes(state); } // Expected production parts are keyed by the exercised surface, not whatever CSS @@ -96,7 +93,6 @@ export function requiredOverlayParts(id) { const state = id.split('/')[1]; if (['task-chat', 'task-drawer', 'task-expanded'].includes(state)) return ['task-header', 'task-body']; - if (state?.startsWith('settings-')) return ['header', 'body', 'scroll']; if (state === 'search') return ['header', 'body']; if (requiresOverlay(id) || ['clipped-modal', 'overlay-padding'].includes(state)) return ['header', 'body', 'scroll', 'footer']; @@ -325,6 +321,15 @@ export function evidenceFailures(report, expected, now = Date.now()) { !report.identity?.osVersion ) errors.push(`${id}: missing native environment`); + if ( + id.split('/')[1]?.startsWith('settings-') && + (entry.windowRole !== 'settings' || + native?.role !== 'settings' || + native?.visible !== true || + native?.modal !== false || + native?.settingsWindowCount !== 1) + ) + errors.push(`${id}: missing modeless native Settings window`); if (requiresOverlay(id) && !entry.geometry?.overlays?.length) errors.push(`${id}: missing required overlay`); for (const overlay of entry.geometry?.overlays ?? []) { diff --git a/scripts/native-ui/contract.test.mjs b/scripts/native-ui/contract.test.mjs index c5852b04..6d042734 100644 --- a/scripts/native-ui/contract.test.mjs +++ b/scripts/native-ui/contract.test.mjs @@ -76,7 +76,12 @@ function report() { status: 'passed', theme: mode.theme, screenshot: { path: `${id}.png`, sha256: digest }, + windowRole: id.split('/')[1]?.startsWith('settings-') ? 'settings' : 'board', nativeWindow: { + role: id.split('/')[1]?.startsWith('settings-') ? 'settings' : 'board', + visible: true, + modal: false, + settingsWindowCount: 1, bounds: { x: 0, y: 0, width: mode.width, height: mode.height }, contentBounds: { x: 0, y: 0, width: mode.width, height: mode.height }, scaleFactor: 2, @@ -368,7 +373,6 @@ test('required footer and compound scroll measurements cannot disappear', () => for (const [state, kind] of [ ['confirmation', 'footer'], ['create-task', 'scroll'], - ['settings-general', 'scroll'], ]) { const r = report(); const overlay = r.entries.find((entry) => entry.id === `light-normal/${state}`).geometry @@ -383,6 +387,11 @@ test('required footer and compound scroll measurements cannot disappear', () => }); test('missing overlay measurements and invalid native bounds fail closed', () => { for (const mutate of [ + (r) => delete r.entries.find((e) => e.id.endsWith('/settings-general')).nativeWindow.role, + (r) => (r.entries.find((e) => e.id.endsWith('/settings-general')).nativeWindow.modal = true), + (r) => + (r.entries.find((e) => e.id.endsWith('/settings-general')).nativeWindow.settingsWindowCount = + 2), (r) => (r.entries.find((e) => requiresOverlay(e.id)).geometry.overlays = []), (r) => (r.entries.find((e) => requiresOverlay(e.id)).geometry.overlays = [{ parts: [] }]), (r) => (r.entries[0].nativeWindow.bounds = {}), diff --git a/scripts/native-ui/large-board-journey.mjs b/scripts/native-ui/large-board-journey.mjs new file mode 100644 index 00000000..124d3985 --- /dev/null +++ b/scripts/native-ui/large-board-journey.mjs @@ -0,0 +1,120 @@ +import assert from 'node:assert/strict'; +import { expect } from '@playwright/test'; + +/** Exercise the real packaged board after the performance sample is recorded. */ +export async function verifyLargeBoardJourney(page, count) { + const column = (status) => page.locator(`[data-column-status="${status}"]`); + const card = (id) => page.locator(`[data-task-id="${id}"][role="article"]`); + const total = () => + page + .locator('.vk-column-status-count') + .evaluateAll((elements) => + elements.reduce((sum, element) => sum + Number(element.textContent), 0) + ); + const request = (method, route, body) => + page.evaluate( + async ({ method, route, body }) => { + const response = await fetch(route, { + method, + headers: { 'Content-Type': 'application/json' }, + ...(body ? { body: JSON.stringify(body) } : {}), + }); + if (!response.ok) throw new Error(`Fixture request failed: ${response.status}`); + const payload = await response.json(); + return payload.data ?? payload; + }, + { method, route, body } + ); + + await column('todo').locator('h2').click(); + await page.keyboard.press('Escape'); + // More than one rendered window: selection must scroll and remain mounted. + for (let index = 0; index < 65; index++) await page.keyboard.press('j'); + const id = 'task_scale_00256'; + await expect(card(id)).toHaveAttribute('data-selected', 'true'); + await expect(card(id)).toBeInViewport(); + const detailResponse = page.waitForResponse( + (response) => + new URL(response.url()).pathname === `/api/tasks/${id}` && + response.request().method() === 'GET' + ); + await page.keyboard.press('Enter'); + const detail = await detailResponse; + assert(detail.ok(), 'Full task detail failed after selecting an offscreen summary'); + const payload = await detail.json(); + assert.equal((payload.data ?? payload).description.length, 4096); + const panel = page.getByTestId('task-detail-panel'); + await expect(panel).toBeVisible(); + await expect(panel.getByRole('textbox', { name: 'Task title', exact: true })).toHaveValue( + 'Scale fixture 00256' + ); + await page.getByRole('button', { name: 'Close task workspace', exact: true }).click(); + await expect(panel).toBeHidden(); + + const moved = page.waitForResponse( + (response) => + new URL(response.url()).pathname === `/api/tasks/${id}/move` && + response.request().method() === 'POST' + ); + await page.keyboard.press('3'); + assert((await moved).ok(), 'Keyboard status move failed'); + await expect(column('blocked').locator(`[data-task-id="${id}"]`)).toBeInViewport(); + assert.equal((await request('GET', `/api/tasks/${id}`)).status, 'blocked'); + + const search = page.getByRole('textbox', { name: 'Search tasks', exact: true }); + // This text lives only in the full description, not the board summary. + const descriptionResponse = page.waitForResponse((response) => { + const url = new URL(response.url()); + return ( + url.pathname === '/api/tasks' && + url.searchParams.get('fields') === 'id' && + url.searchParams.get('search') === 'Public-safe board performance fixture' + ); + }); + await search.fill('Public-safe board performance fixture'); + const searchResponse = await descriptionResponse; + assert(searchResponse.ok(), 'Full-description search failed'); + const searchPayload = await searchResponse.json(); + assert.equal((searchPayload.data ?? searchPayload).length, count); + await expect.poll(total).toBe(count); + await search.fill('Scale fixture 00256'); + await expect.poll(total).toBe(1); + await expect(card(id)).toBeVisible(); + + // Direct API mutation performs no React Query invalidation in this renderer. + // The changed card must arrive through the production realtime path. + const title = 'Scale fixture 00256 realtime update'; + await request('PATCH', `/api/tasks/${id}`, { title }); + await expect(card(id).getByRole('heading', { name: title, exact: true })).toBeVisible(); + + const source = await card(id).boundingBox(); + const target = await column('todo').boundingBox(); + assert(source && target, 'Drag source or destination has no visible bounds'); + const dragged = page.waitForResponse( + (response) => + new URL(response.url()).pathname === `/api/tasks/${id}/move` && + response.request().method() === 'POST' + ); + await page.mouse.move(source.x + source.width / 2, source.y + 30); + await page.mouse.down(); + await page.mouse.move(source.x + source.width / 2 - 15, source.y + 35, { steps: 3 }); + await page.mouse.move(target.x + target.width / 2, target.y + 100, { steps: 20 }); + await page.mouse.up(); + assert((await dragged).ok(), 'Pointer drag did not commit'); + await expect(column('todo').locator(`[data-task-id="${id}"]`)).toBeVisible(); + assert.equal((await request('GET', `/api/tasks/${id}`)).status, 'todo'); + await search.fill(''); + await expect.poll(total).toBe(count); + await page.reload(); + await expect.poll(total).toBe(count); + assert.equal((await request('GET', `/api/tasks/${id}`)).title, title); + return { + offscreenKeyboardSelection: true, + fullDetail: true, + keyboardMove: true, + descriptionSearch: true, + realtimeUpdate: true, + pointerMove: true, + persistedAfterReload: true, + }; +} diff --git a/scripts/native-ui/run.mjs b/scripts/native-ui/run.mjs index 9bc6cb23..b44c7185 100644 --- a/scripts/native-ui/run.mjs +++ b/scripts/native-ui/run.mjs @@ -206,6 +206,13 @@ async function capture(entry) { const native = await app.evaluate(async ({ BrowserWindow, screen }, url) => { const window = BrowserWindow.getAllWindows().find((w) => w.webContents.getURL() === url); return { + role: new URL(url).searchParams.get('desktop-settings') === '1' ? 'settings' : 'board', + visible: window.isVisible(), + modal: window.isModal(), + settingsWindowCount: BrowserWindow.getAllWindows().filter( + (candidate) => + new URL(candidate.webContents.getURL()).searchParams.get('desktop-settings') === '1' + ).length, bounds: window.getBounds(), contentBounds: window.getContentBounds(), minimumSize: window.getMinimumSize(), diff --git a/server/package.json b/server/package.json index 02313804..00a3edd3 100644 --- a/server/package.json +++ b/server/package.json @@ -1,6 +1,6 @@ { "name": "@veritas-kanban/server", - "version": "6.1.7", + "version": "6.2.0", "private": true, "type": "module", "main": "./dist/index.js", diff --git a/shared/package.json b/shared/package.json index 7c036cba..229e25f7 100644 --- a/shared/package.json +++ b/shared/package.json @@ -1,6 +1,6 @@ { "name": "@veritas-kanban/shared", - "version": "6.1.7", + "version": "6.2.0", "private": true, "type": "module", "main": "./dist/index.js", diff --git a/web/package.json b/web/package.json index 01771729..aa7f4274 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "@veritas-kanban/web", - "version": "6.1.7", + "version": "6.2.0", "private": true, "type": "module", "scripts": { diff --git a/web/src/__tests__/KanbanBoard.test.tsx b/web/src/__tests__/KanbanBoard.test.tsx index 2e96f410..a800cfb1 100644 --- a/web/src/__tests__/KanbanBoard.test.tsx +++ b/web/src/__tests__/KanbanBoard.test.tsx @@ -46,7 +46,9 @@ function createFeatureSettings( } vi.mock('@/hooks/useTasks', () => ({ - useTasks: () => mockUseTasks(), + useBoardTasks: () => mockUseTasks(), + useBoardSearch: () => ({ data: [], isFetching: false, error: null, refetch: vi.fn() }), + useTask: (id: string) => ({ data: mockUseTasks().data?.find((task: Task) => task.id === id) }), useTasksByStatus: (tasks: Task[], columns?: Array<{ id: string }>) => { const result: Record = Object.fromEntries( (columns ?? [{ id: 'todo' }, { id: 'in-progress' }, { id: 'blocked' }, { id: 'done' }]).map( diff --git a/web/src/__tests__/task-detail-validation-mantine.test.tsx b/web/src/__tests__/task-detail-validation-mantine.test.tsx index f9b25e9c..a0c6cf1c 100644 --- a/web/src/__tests__/task-detail-validation-mantine.test.tsx +++ b/web/src/__tests__/task-detail-validation-mantine.test.tsx @@ -19,7 +19,7 @@ vi.mock('@/hooks/useTasks', async () => { const actual = await vi.importActual('@/hooks/useTasks'); return { ...actual, - useTasks: mocks.useTasks, + useBoardTasks: mocks.useTasks, useAddVerificationStep: () => ({ mutateAsync: mocks.addVerificationStep }), useUpdateVerificationStep: () => ({ mutateAsync: mocks.updateVerificationStep }), useDeleteVerificationStep: () => ({ mutateAsync: mocks.deleteVerificationStep }),