diff --git a/docs/plans/mvp.md b/docs/plans/mvp.md new file mode 100644 index 0000000..e8e3d1f --- /dev/null +++ b/docs/plans/mvp.md @@ -0,0 +1,64 @@ +# AgentOS.NET — the MVP + +**One sentence:** an AI software team that turns a backlog into reviewed, tested, merged pull requests — on the +team's own computers, with evidence on every change and governance a company can sign off. + +Everything below serves one loop. A feature that doesn't make this loop faster, safer or more visible waits. + +``` +Idea ──► spec + tickets with dependencies (Discovery: Requirement + Decomposition agents) + ──► Autopilot — the Autopilot Orchestrator agent decides what runs next, within fixed rules + ──► each ticket: a MAF workflow Coding → checks → Reviewer → Testing → merge + (red checks · conflicts · requested changes · failed acceptance → back to Coding, 3 rounds) + ──► Try the app — the latest version running on your computer + ──► Board · Autopilot log · Knowledge · Insights — what happened, why, and what it cost +``` + +## Who it is for (first) + +- **A tech lead / small team** in a .NET or web shop on GitHub: wants the backlog built without babysitting agents. +- **A non-technical product owner** on the same team: describes ideas in plain words, answers the agents' + questions, tries the app — never opens a terminal. + +## Why it wins + +1. **Runs on the team's own machines** — each member's Claude / Codex subscription through a paired computer, in a + scoped sandbox (own folder, allow-listed commands, no force-push, no credential reads). $0 server API cost. +2. **Nothing untested is merged** — a Reviewer agent and a Testing agent check every change, and post their evidence + on the pull request; failures already on main get their own tickets. +3. **Measured** — every agent call: tokens (fresh · cache read · cache write · output), latency, outcome; per agent, + per model, per run (Insights). +4. **It asks, it doesn't guess** — stuck work is retried with guidance or turned into one precise question on the + ticket; your reply is read back and acted on. + +**Proof:** VNChessAcademy (a multi-tenant teaching platform with a CCBridge xiangqi library) was built by this loop +on the lab — 25+ pull requests, most merged without a person touching the code. + +## In the MVP (the dock) + +| App | Role in the loop | +|---|---| +| **Home** | setup checklist (connect GitHub, connect a computer), recent activity | +| **Board** | Projects (Try the app) · New idea · Tasks · Computers · Autopilot · Knowledge · Activity · Files | +| **Insights** | runs, tokens, latency, cost (≈ API for subscription runs), budget | +| **Settings** | AI accounts, agent profiles, Autopilot (who decides, how a ticket runs), people, audit | + +**Outside the MVP** (kept, not in the dock): the Workflow studio and the 5-agent pipeline it runs, Terminal, the +Thorough engine, Azure DevOps boards (partial), the Components catalog. Reachable with Ctrl+K / Activities. + +## Before the first real users — enterprise gaps, in order + +1. **Onboarding without a terminal** — "Connect GitHub" through a GitHub App (repo picker, no PAT), and a + downloadable helper for "Connect this computer" (today it is `dotnet run`). +2. **Data isolation, stage 2** — an empty tenant on a connection sees *nothing* (today: everything), with an explicit + platform scope for system workers; then an external security review. +3. **Reliability** — cloud workers so work doesn't depend on a laptop being awake (plan: `cloud-workers-plan`); + resumable ticket workflows (checkpoints) across restarts. +4. **Deploy** — `azd up` to the customer's Azure, an operations guide, backups. +5. **Product shell** — landing page, docs site, licensing. + +## Definition of done (every change) + +Enterprise level, as CLAUDE.md's owner directives: tests at every layer, metrics, plain-language UI with an a11y pass, +docs true, verified in the running app — and for anything that touches persistence, identity or real services, on +the full stack / the lab. diff --git a/src/AgentOs.Web/Components/Pages/InsightsApp.razor b/src/AgentOs.Web/Components/Pages/InsightsApp.razor index c33c1fe..558cc69 100644 --- a/src/AgentOs.Web/Components/Pages/InsightsApp.razor +++ b/src/AgentOs.Web/Components/Pages/InsightsApp.razor @@ -22,7 +22,7 @@

Insights

-

Runs, agent utilisation and LLM cost at a glance — the control plane for the pipeline.

+

What your AI team did, how long it took and what it cost — every agent run, by agent and by model.

@@ -40,7 +40,7 @@
@(_summary is null ? "—" : Num(_summary.RunCount))
-
Pipeline runs
+
Agent runs
@SuccessLabel()
@@ -85,10 +85,10 @@ else if (_summary is null || _summary.CallCount == 0) + Hint="Start a task on the Board — every Coding, Reviewer, Testing and Discovery run shows up here with its tokens, time and cost." />
- - Open Workflow + + Open Board
@@ -141,7 +141,7 @@ else
}
-
Pipeline runs / day
+
Agent runs / day
} diff --git a/src/AgentOs.Web/Services/AppCatalog.cs b/src/AgentOs.Web/Services/AppCatalog.cs index 453ee41..d0e1b22 100644 --- a/src/AgentOs.Web/Services/AppCatalog.cs +++ b/src/AgentOs.Web/Services/AppCatalog.cs @@ -12,7 +12,8 @@ namespace AgentOs.Web.Services; /// The catalog of apps the AgentOS.NET shell can launch — built-ins plus any registered by plugins. public static class AppCatalog { - // Four apps are Pinned (the GNOME Dash): Home · Workflow · Board · Settings. Terminal and Files are tabs of + // Four apps are Pinned (the GNOME Dash) — the MVP loop (docs/plans/mvp.md): Home · Board · Insights · Settings. The + // Workflow studio (the 5-agent pipeline as a graph) is outside the MVP and stays one Activities / Ctrl+K search away. Terminal and Files are tabs of // Board (Log, Files) and stay launchable on their own from Activities. Everything else is unpinned — still launchable via Activities search / deep // links, and the admin/system surfaces are collected behind the Settings hub. Pinned defaults to // true on the DesktopApp record, so unpinned apps MUST say Pinned: false explicitly. @@ -23,9 +24,9 @@ public static class AppCatalog // standalone Overview + Cost admin windows are folded in here. // Home is the task-first landing (UX phase 2): describe a change → Run now; recent runs; setup checklist. new("home", "Home", "house", "Describe a change and run the agents — recent runs and setup", "Agents", 1000, 760, Pinned: true, ComponentType: typeof(HomeApp), Color: "#3584e4"), - new("insights", "Insights", "chart", "Runs, agent utilisation and LLM cost — analytics + budget", "Agents", 1180, 760, Pinned: false, ComponentType: typeof(InsightsApp), Color: "#1c71d8"), - new("workflow", "Workflow", "graph", "Build & run the agent pipeline as a visual graph", "Agents", 1320, 840, Pinned: true, ComponentType: typeof(OrchestrationStudio), Color: "#9141ac"), + new("workflow", "Workflow", "graph", "Build & run the agent pipeline as a visual graph", "Agents", 1320, 840, Pinned: false, ComponentType: typeof(OrchestrationStudio), Color: "#9141ac"), new("board", "Board", "git-pull-request", "Boards → tickets → AI sessions → PRs", "Agents", 1000, 660, Pinned: true, ComponentType: typeof(BoardApp), Color: "#2ec27e"), + new("insights", "Insights", "chart", "Runs, agent utilisation and LLM cost — analytics + budget", "Agents", 1180, 760, Pinned: true, ComponentType: typeof(InsightsApp), Color: "#1c71d8"), new("settings", "Settings", "gear", "Model keys, agents, governance, people and personal preferences", "System", 1060, 700, Pinned: true, ComponentType: typeof(SettingsHub), Color: "#5e5c64"), new("terminal", "Terminal", "terminal", "Live read-only tail of the runner's session feed", "Agents", 780, 520, Pinned: false, ComponentType: typeof(TerminalApp), Color: "#2b2b2e"), new("files", "Files", "folder", "Browse a connected repo's files (read-only)", "Agents", 880, 600, Pinned: false, ComponentType: typeof(FilesApp), Color: "#4a86d8"), diff --git a/tests/AgentOs.E2E.Tests/AgentOsUi/DesktopNavigation.cs b/tests/AgentOs.E2E.Tests/AgentOsUi/DesktopNavigation.cs new file mode 100644 index 0000000..d3d3301 --- /dev/null +++ b/tests/AgentOs.E2E.Tests/AgentOsUi/DesktopNavigation.cs @@ -0,0 +1,21 @@ +// Open any app the way a person does when it isn't in the dock: Ctrl+K search, pick the app. The dock holds only the +// MVP apps (Home · Board · Insights · Settings); everything else — e.g. the Workflow studio — is one search away. + +using System.Threading.Tasks; +using Microsoft.Playwright; + +namespace AgentOs.E2E.Tests.AgentOsUi; + +internal static class DesktopNavigation +{ + /// Open through the shell search and wait for its window to have focus. + public static async Task OpenAppAsync(this IPage page, string appName) + { + await page.Keyboard.PressAsync("Control+k"); + var input = page.Locator(".ko-search-input"); + await Assertions.Expect(input).ToBeFocusedAsync(); + await input.PressSequentiallyAsync(appName); + await page.Locator(".ko-row", new() { HasTextString = appName }).First.ClickAsync(); + await Assertions.Expect(page.Locator(".appwin.focused .appwin-title")).ToHaveTextAsync(appName); + } +} diff --git a/tests/AgentOs.E2E.Tests/AgentOsUi/LoginAndDesktopTests.cs b/tests/AgentOs.E2E.Tests/AgentOsUi/LoginAndDesktopTests.cs index 8f0723a..f4739dd 100644 --- a/tests/AgentOs.E2E.Tests/AgentOsUi/LoginAndDesktopTests.cs +++ b/tests/AgentOs.E2E.Tests/AgentOsUi/LoginAndDesktopTests.cs @@ -57,7 +57,7 @@ public async Task RightClick_OpensContextMenu_AtCursor_AndCloseOnOutsideClick() // Scenario 3: clicking a Desktop icon opens an AppFrame for the corresponding app key. [Theory] [InlineData("Home")] - [InlineData("Workflow")] + [InlineData("Insights")] [InlineData("Board")] [InlineData("Settings")] public async Task DockIcon_Click_OpensAppWindow(string title) diff --git a/tests/AgentOs.E2E.Tests/AgentOsUi/SettingsSearchTests.cs b/tests/AgentOs.E2E.Tests/AgentOsUi/SettingsSearchTests.cs index eeeb5a4..1d46cc1 100644 --- a/tests/AgentOs.E2E.Tests/AgentOsUi/SettingsSearchTests.cs +++ b/tests/AgentOs.E2E.Tests/AgentOsUi/SettingsSearchTests.cs @@ -60,7 +60,7 @@ public async Task CtrlK_OpensSearch_EnterRunsTheTopAction_WorkflowKeepsItsOwnCtr await Assertions.Expect(win.Locator(".prefs-h", new() { HasTextString = "Model keys" })).ToBeVisibleAsync(); // With the Workflow window focused, Ctrl+K is the builder's "Add a node" palette, not the shell search. - await _fx.Page.Locator(".dock-item[title=\"Workflow\"]").First.ClickAsync(); + await _fx.Page.OpenAppAsync("Workflow"); await Assertions.Expect(_fx.Page.Locator(".appwin.focused .syn-toolbar")).ToBeVisibleAsync(); // The builder registers its key handler after its first render — wait for it, not just for the toolbar. await _fx.Page.WaitForFunctionAsync("() => !!(window.agentosWorkflowKeys && window.agentosWorkflowKeys._handler)"); diff --git a/tests/AgentOs.E2E.Tests/AgentOsUi/WindowLifecycleTests.cs b/tests/AgentOs.E2E.Tests/AgentOsUi/WindowLifecycleTests.cs index fa39fb2..be49829 100644 --- a/tests/AgentOs.E2E.Tests/AgentOsUi/WindowLifecycleTests.cs +++ b/tests/AgentOs.E2E.Tests/AgentOsUi/WindowLifecycleTests.cs @@ -115,7 +115,7 @@ public async Task Dock_PinnedApps_ShowRunningDot_WhenOpen() await _fx.GotoDesktopAsync(); // 6 pinned apps in the Dash (GNOME Dash — no Start button). - foreach (var t in new[] { "Home", "Workflow", "Board", "Settings" }) + foreach (var t in new[] { "Home", "Board", "Insights", "Settings" }) { await Assertions.Expect(_fx.Page.Locator($".dock-item[title=\"{t}\"]")).ToBeVisibleAsync(); } diff --git a/tests/AgentOs.E2E.Tests/AgentOsUi/WorkflowAppRealAuthTests.cs b/tests/AgentOs.E2E.Tests/AgentOsUi/WorkflowAppRealAuthTests.cs index 69a6674..c3e4c7e 100644 --- a/tests/AgentOs.E2E.Tests/AgentOsUi/WorkflowAppRealAuthTests.cs +++ b/tests/AgentOs.E2E.Tests/AgentOsUi/WorkflowAppRealAuthTests.cs @@ -38,7 +38,7 @@ private async Task OpenWorkflowAsync() { await _fx.LoginAsync(); await EnsureCircuitConnectedAsync(); - await _fx.Page.Locator(".dock-item[title=\"Workflow\"]").First.ClickAsync(); + await _fx.Page.OpenAppAsync("Workflow"); var win = _fx.Page.Locator(".appwin.focused"); await Assertions.Expect(win.Locator(".appwin-title")).ToHaveTextAsync("Workflow"); // The studio graph dropdown is populated once the component's circuit is interactive — wait for it diff --git a/tests/AgentOs.E2E.Tests/AgentOsUi/WorkflowAppTests.cs b/tests/AgentOs.E2E.Tests/AgentOsUi/WorkflowAppTests.cs index cfe12f2..cb41e1b 100644 --- a/tests/AgentOs.E2E.Tests/AgentOsUi/WorkflowAppTests.cs +++ b/tests/AgentOs.E2E.Tests/AgentOsUi/WorkflowAppTests.cs @@ -24,7 +24,7 @@ private async Task OpenWorkflowAsync() { await _fx.GotoDesktopAsync(); await EnsureCircuitConnectedAsync(); - await _fx.Page.Locator(".dock-item[title=\"Workflow\"]").First.ClickAsync(); + await _fx.Page.OpenAppAsync("Workflow"); var win = _fx.Page.Locator(".appwin.focused"); await Assertions.Expect(win.Locator(".appwin-title")).ToHaveTextAsync("Workflow"); // The studio graph dropdown is populated once the component's circuit is interactive — wait for it