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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Extracted from `mystira-workspace/apps/devhub` (March 2026). Not yet onboarded t

## Stack

- **Frontend**: React 18 + TypeScript 5 + Vite 5 + Tailwind CSS (in `app/`)
- **Frontend**: React 19 + TypeScript 5 + Vite 8 + Tailwind CSS 4 (in `app/`)
- **Desktop**: Tauri 2.0 + Rust (in `app/src-tauri/`)
- **CLI sidecar**: .NET 9 — `Deck.CLI/`
- **Services**: .NET 9 — `Deck.Services/`
Expand All @@ -37,7 +37,8 @@ cargo test # Rust tests

- The Tauri frontend (`app/src/`) uses a VSCode-style layout (`VSCodeLayout` component)
- Navigation is via `VIEWS` constants in `app/src/types/constants.ts`
- Adding a new panel: add a `VIEWS` entry, add an `ACTIVITY_BAR_ITEMS` entry in `App.tsx`, add a route in `AppContent.tsx`
- Adding a new panel: add a `VIEWS` entry, add an `ACTIVITY_BAR_ITEMS` entry in `App.tsx`, add a route in `AppContent.tsx`, add a block in `AppSidebar.tsx`
- Panels are governed by `docs/adr/` — read [ADR-0002](docs/adr/0002-deck-as-panel-host-for-org-wide-tenants.md) first. The shell stays generic; a panel owns its own coupling, consumes external contracts, embeds no domain logic, and must not compute a number another repo owns
- The `.NET CLI` sidecar communicates via JSON stdin/stdout through Tauri's `shell` plugin
- `deck-contracts` (Rust) defines shared types between the Tauri backend and .NET sidecar

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Deck is a Tauri desktop application that provides a unified operations interface
| Layer | Tech |
|-------|------|
| Desktop shell | Tauri 2.0 + Rust |
| Frontend | React 18 + TypeScript 5 + Vite 5 + Tailwind CSS |
| Frontend | React 19 + TypeScript 5 + Vite 8 + Tailwind CSS 4 |
| CLI sidecar | .NET 9 (`Deck.CLI`) |
| Services layer | .NET 9 (`Deck.Services`) |
| Shared contracts | Rust crate (`deck-contracts`) |
Expand Down
8 changes: 4 additions & 4 deletions app/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -717,16 +717,16 @@ const response = await invoke("my_feature", { param: "value" });

### Frontend

- **React 18** - UI framework
- **React 19** - UI framework
- **TypeScript 5** - Type safety
- **Vite 5** - Build tool and dev server
- **TailwindCSS 3** - Utility-first CSS
- **Vite 8** - Build tool and dev server
- **TailwindCSS 4** - Utility-first CSS
- **Monaco Editor** - Bicep file viewing
- **Tauri API** - Desktop functionality

### Backend

- **Tauri 1.5** - Cross-platform desktop framework
- **Tauri 2.0** - Cross-platform desktop framework
- **Rust** - Native performance
- **Tokio** - Async runtime
- **Serde** - JSON serialization
Expand Down
4 changes: 2 additions & 2 deletions app/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ This document outlines the architecture for a standalone React/Tauri desktop app

### Frontend

- **React 18** - UI framework
- **React 19** - UI framework
- **TypeScript** - Type safety
- **TanStack Query (React Query)** - API state management
- **Zustand** - Client-side state management
Expand All @@ -47,7 +47,7 @@ This document outlines the architecture for a standalone React/Tauri desktop app

### Backend (Tauri)

- **Tauri 1.5+** - Desktop app framework
- **Tauri 2.0** - Desktop app framework
- **Rust** - Backend runtime for Tauri
- **tokio** - Async runtime
- **reqwest** - HTTP client for API calls
Expand Down
22 changes: 18 additions & 4 deletions docs/adr/0001-deck-as-panel-host-for-mystira-ops-tooling.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,25 @@

## Status

Proposed
**Accepted**

Generalised — not superseded — by [ADR-0002](0002-deck-as-panel-host-for-org-wide-tenants.md),
which lifts this decision from *Mystira ops tooling* to *org-wide tenants*. This ADR remains the
record of the Mystira tenant and of the evidence that produced the panel-host shape; ADR-0002 does
not restate it.

## Date

2026-07-11 (proposed). Re-grounded against `dev` @ `bc0d1c5` on 2026-08-10 before commit — the
evidence in §Context was refreshed for PR #3 (Camera panel) and PR #5 (CI); the decision itself is
unchanged and still awaiting ratification.
2026-07-11 (proposed) → **2026-08-12 (accepted)**.

Re-grounded against `dev` @ `bc0d1c5` on 2026-08-10 before commit — the evidence in §Context was
refreshed for PR #3 (Camera panel) and PR #5 (CI); the decision itself was unchanged.

Ratified 2026-08-12 as part of the decision to host a harness workbench panel in deck (baton task
`e650d56c`, org-meta `docs/agent-optimization/07-workbench-investigation.md` §2.6). That panel
would otherwise have been the **third** tenant built on an unratified foundational decision. The
decision text is accepted as written; the scope it was written against is generalised by ADR-0002
rather than stretched here.

## Context

Expand Down Expand Up @@ -152,6 +164,8 @@ This establishes `docs/adr/NNNN-*.md` as deck's ADR home for future decisions.

## References

- [`docs/adr/0002-deck-as-panel-host-for-org-wide-tenants.md`](0002-deck-as-panel-host-for-org-wide-tenants.md)
— generalises this decision to org-wide tenants; read it before adding a **non-Mystira** panel.
- `docs/specs/deck-ops-cockpit.md` — deck ops cockpit technical spec (this ADR's companion).
- `mystira-workspace .agents/roadmaps/video-generation/design-ecosystem-provisioning.md` — deck's
ecosystem role (Models panel + sluice hybrid routing; §1 fit-with-what-deck-already-is).
Expand Down
176 changes: 176 additions & 0 deletions docs/adr/0002-deck-as-panel-host-for-org-wide-tenants.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
# ADR-0002: deck as panel-host for org-wide tenants

## Status

**Accepted**

Generalises [ADR-0001](0001-deck-as-panel-host-for-mystira-ops-tooling.md). Does **not** supersede
it: 0001 remains Accepted and remains the record of the Mystira tenant.

## Date

2026-08-12

## Context

ADR-0001 settled that deck is a generic panel-host and that Mystira ops tooling is hosted as
bounded panel modules on top of it. Its *decision text* is written in general terms — "deck is a
generic panel-host"; "Panels consume external contracts, they do not embed domain logic — deck is a
viewer and a trigger" — but everything around that text is Mystira. The title says "for Mystira ops
tooling". The entire Context is R13: the half-wired Cosmos Explorer / Migration Manager extraction,
the story-generator Batch API, Mystira's spend in docket. Every alternative was weighed against
Mystira surfaces.

A **harness workbench** panel has now been accepted for deck (baton task `e650d56c`; org-meta
`docs/agent-optimization/07-workbench-investigation.md` §2.4, and the spike in
[phoenixvc/org-meta#66](https://github.com/phoenixvc/org-meta/pull/66)). It renders run ledgers,
`HandoffEnvelope` composition, and authority containment over work happening across the whole
workspace. It is not Mystira. It is org-wide.

Two facts sharpen the decision:

- **A non-Mystira tenant already shipped.** The Camera panel (PR #3) added a panel plus its own
backend by *using* the four-file recipe rather than changing it. So an org-agnostic tenant on the
panel-host shape is demonstrated, not hypothetical — but it was never written down as a decision,
which is why ADR-0001's Mystira framing still reads as the whole story.
- **ADR-0001 names the failure mode this ADR exists to avoid.** Its Consequences list "coupling
creep" as the top risk and calls the generic-shell claim "a discipline, not a mechanism". Quietly
reading "Mystira ops tooling" as covering a harness panel would be exactly that creep — applied
to the ADR text rather than to the code, which is worse, because the ADR is the instrument that
is supposed to detect it.

The question this ADR settles: **does deck host tenants beyond Mystira, and under what rules?**

Alternatives considered:

1. **Let the harness panel land under ADR-0001 unamended.** Rejected — coupling creep, per above.
The ADR would then say one thing and be used for another, and every later reader would have to
guess which reading is load-bearing.
2. **Broaden ADR-0001 in place** — retitle it, rewrite its Context to "org tenants" with Mystira as
the first instance, and record the amendment. Rejected. ADR-0001's Context *is* its evidence: the
interrupted extraction, the three R13 facets, the specific alternatives those facts ruled out.
That evidence was true of a moment and produced the panel-host shape. Generalising it away leaves
an ADR whose Context no longer explains its own Decision, and loses the record of *why* deck is a
panel-host at all. An ADR records a decision in its context; the context is not the disposable
part.
3. **A second ADR that generalises panel-host to org-wide tenants and cites 0001 as the specific
case.** Chosen. The Mystira narrative survives intact as history, the general rule is stated once
in its own right, and the third tenant lands on a rule rather than on a stretch.

## Decision

**deck is a panel-host for org-wide tenants. ADR-0001's decision binds for every tenant, not only
Mystira.**

A **tenant** is a bounded set of panels serving one domain, added through deck's existing four-file
recipe (a `VIEWS` entry + `ACTIVITY_BAR_ITEMS` + an `AppContent` route + an `AppSidebar` block) plus
whatever backend module it needs. Tenants as of this ADR: the generic shell's own panels (Dashboard,
Service Manager, Infrastructure), **Camera** (PR #3), **Mystira ops** (ADR-0001), and the
**harness workbench** (org-meta doc 07).

The rules, carried over from ADR-0001 and now stated at org scope:

- **The shell is the generic asset.** `VSCodeLayout`, the activity bar / `VIEWS` routing, and the
Rust exec primitives (`Command::new`, argv-only) carry no tenant semantics and stay reusable by
any phoenixvc project. Tenant-specific strings and types live inside the tenant's own panel and
backend files; the four-file recipe (`VIEWS`, `ACTIVITY_BAR_ITEMS`, `AppContent` route,
`AppSidebar` block) defines the shared touch-points a tenant registers into.
- **The `cli.rs` bridge is currently tenant-specific.** Unlike the generic shell primitives above,
the `execute_devhub_cli` function and `Mystira.DevHub.CLI` executable references in `cli.rs` and
`helpers.rs` are coupled to the Mystira tenant, not tenant-neutral.
- **Panels consume external contracts; they do not embed domain logic.** deck is a viewer and a
trigger. This was written for Mystira and holds unchanged for every tenant.
- **Secrets never enter the repo** — session or machine-local only, per deck's `security.md`.
- **A tenant is omissible.** Removing it means removing its entries from the four registration
points (`VIEWS`, `ACTIVITY_BAR_ITEMS`, `AppContent` route, `AppSidebar` block), without modifying
generic shell code.

Two rules this ADR adds, because org-wide tenants surface problems a single-org tenant did not:

- **deck must not compute a number another repo owns.** Where a value has an owner in the
ecosystem's ownership map, deck renders it and the owner derives it. For the harness tenant that
means baton owns `RunRecord`, leases, usage, and score *computation*; deck owns score *display*,
envelope composition, and panel layout (doc 07 §2.5). Generalised: **one store, many readers —
deck is always a reader.** A second client computing the same quantity produces two numbers with
no way to adjudicate between them.
- **deck does not become an execution substrate.** It invokes external CLIs through the existing
argv-only exec primitives, in the caller's own worktree. It mints no run identity, holds no lease,
and makes no model call outside sluice. This is ADR-0001's "viewer and a trigger" made precise for
tenants that touch the harness, and it is the constraint under which doc 05 §4's prohibition on
re-installing an IDE agent stack does not apply to this panel (doc 07 §2.4).

## Consequences

### Positive

- **The Mystira narrative stays intact as history.** No retro-editing of an ADR to cover a case its
author did not have in front of them.
- **The third tenant lands on a stated rule, not a stretched one.** The generic-shell claim is
testable against a written general rule instead of against an inference from a Mystira document.
- **Ownership is now explicit.** ADR-0001 implied it ("consume external contracts"); this ADR names
the failure mode — two clients, two numbers, no adjudication — so it can be caught in review.

### Negative / Costs

- **Two ADRs to read.** A reader who finds only 0001 will conclude deck is Mystira-shaped.
Mitigation: 0001's Status and References now point forward to this ADR.
- **Multi-tenant panel selection remains unsolved, and is deliberately deferred a second time.**
All `VIEWS` are still compiled in. This was re-reviewed at ratification and the deferral is judged
still correct: adding one more compiled-in view costs nothing today, and a panel registry (config
schema, load path, conditional routing, tests) would have no beneficiary yet. **This ADR does not
build a panel registry.**

It does fix the trigger, because a deferral without an expiry condition is just an omission. Doc
07 §2.6 argues a third tenant makes config-driven selection "urgent rather than later"; that is
not quite the right axis. Tenant *count* is not the cost — all current tenants share one operator,
so a third is a busier activity bar, not a multi-tenancy problem. Build panel selection when
either holds:
1. a **second org** adopts deck and would inherit tenants it does not want, or
2. a tenant must be **hidden from an operator who has the others** (permissions, or an unwanted
upstream dependency).

Until one of those is true, the deferral stands and each new tenant is one more compiled-in view.
- **deck is ungoverned.** Not retort-onboarded, no `.agentkit`, no `HarnessManifest`; doc 03 §3.4
places it in Tier D. Hosting a panel that renders other repos' runs is deck as *instrument* rather
than as *subject*, so this is not blocking — but it is the same dogfooding gap doc 03 §3.3 flags
for docket, and this ADR names it rather than waving it through.
- **More upstreams to track.** Each tenant binds deck to another repo's contract (sluice endpoints,
docket tool names, the Batch API, now baton's run model). ADR-0001 accepted this for Mystira; it
compounds per tenant.

### Boundary summary

| Layer | Generic (org-agnostic) | Tenant-coupled (panel modules) |
| --- | --- | --- |
| Shell / layout / routing | ✅ `VSCodeLayout`, activity bar, `VIEWS` mechanism | — |
| Exec primitives | ✅ `Command::new` argv-only | — |
| Sidecar bridge | — | ✅ `cli.rs`, `Mystira.DevHub.CLI` coupling |
| Panel selection / visibility | ⏸ deferred — all `VIEWS` compiled in (see trigger above) | — |
| Mystira ops | — | ✅ ADR-0001 — Cosmos/Migration, cost/ops, batch monitor |
| Camera | — | ✅ PR #3 — local viewer + its own backend |
| Harness workbench | — | ✅ doc 07 — run ledger, envelopes, containment (baton owns the numbers) |

## Notes on placement

ADR-0001 §"Notes on placement" established `docs/adr/NNNN-*.md` as deck's ADR home, 4-digit
numbering, narrative shape (Status / Context / Decision / Consequences / References) rather than a
weighted decision matrix. This ADR follows that convention unchanged and takes **`0002`**.

The choice of a new ADR over an in-place amendment to 0001 is recorded above as Alternative 2 rather
than left implicit, because "just widen the existing ADR" is the cheap move and its cost — losing
the evidence that justified the original decision — is not visible at the moment it is taken.

## References

- [`docs/adr/0001-deck-as-panel-host-for-mystira-ops-tooling.md`](0001-deck-as-panel-host-for-mystira-ops-tooling.md)
— the specific case this ADR generalises; the Mystira tenant and the evidence for the panel-host
shape.
- `docs/specs/deck-ops-cockpit.md` — the Mystira tenant's technical spec (ADR-0001's companion).
- `phoenixvc/org-meta docs/agent-optimization/07-workbench-investigation.md` — the harness workbench
investigation. §2.4 placement and the never-executes-in-process constraint; §2.5 the
baton-owns-the-number split; §2.6 the risks of choosing deck (including this ADR's own trigger);
§2.7 the `retort-plugins` asset and §5.2 the spike that established it hosts.
- [phoenixvc/org-meta#66](https://github.com/phoenixvc/org-meta/pull/66) — the spike informing the
placement decision.
- baton task `e650d56c-d98f-4049-b875-30a12ba8870c` (project `b024b02f-3274-42eb-9596-531335eb76ac`)
— the decision to accept deck as host for the harness workbench panel.
2 changes: 1 addition & 1 deletion docs/specs/deck-ops-cockpit.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ gracefully when its upstream is only specced (not yet built).

### 1.1 The shell and the panel-host recipe

deck is a Tauri 2.0 + React 18/TS5/Vite5/Tailwind app (`app/`) over a Rust backend
deck is a Tauri 2.0 + React 19/TS5/Vite8/Tailwind 4 app (`app/`) over a Rust backend
(`app/src-tauri/`), a .NET 9 CLI sidecar (`Deck.CLI/` + `Deck.Services/`), and two Rust crates —
`crates/deck-contracts/` (shared Rust↔TS contracts) and `crates/deck-camera/` (a deck-owned local
RTSP transport, added in PR #3). Adding a panel is a fixed four-file recipe (confirmed in
Expand Down
Loading