diff --git a/.archive/README.md b/.archive/README.md
new file mode 100644
index 000000000..a1d629c82
--- /dev/null
+++ b/.archive/README.md
@@ -0,0 +1,55 @@
+# .archive
+
+Code parked out of the live build but kept in-tree (and in git history). Excluded from `tsconfig.json` (`exclude: [".archive"]`), so nothing here is type-checked or bundled. Paths mirror their original `src/` location, so restoring is a reverse `git mv`.
+
+## Browser "design tokens" tab (`token-category`) — archived 2026-07-14
+
+The My Station browser primary sidebar was reduced to a **Sessions-only** variant (History and Design pills removed, pill header hidden — see `BrowserPrimarySidebar`'s `sessionsOnly` prop). The **Design** pill was the _only_ entry point for the "color / design tokens" viewer (`onOpenColorTokens` → `createColorTokensTab` → a `token-category` tab rendered by `TokenManagerPanel`). With that entry point gone, the whole `token-category` tab type became unreachable, so it was archived.
+
+**What moved here (self-contained to the feature):**
+
+- `src/modules/WorkStation/Browser/Panels/BrowserMainPane/content/TokenManagerContent/` — the token viewer panel (`TokenManagerPanel`)
+- `src/modules/WorkStation/Browser/Panels/BrowserMainPane/components/DesignFileBar/` — used only by `TokenManagerContent`
+- `src/modules/WorkStation/TabContent/renderers/tokenCategory.tsx` — the unified `token-category` renderer wrapper
+
+**What deliberately stayed live (shared with other features):**
+
+- `src/modules/WorkStation/Browser/hooks/useGlobalTokens.ts` — still used by the Browser sidebar's Design tab (`DesignTabGlobalTokens`), which remains in the **full** sidebar variant used by SessionReplay's "My Tabs" sidebar
+- `src/modules/WorkStation/Browser/Panels/BrowserPrimarySidebar/tabs/{HistoryTab.tsx,DesignTab/}` — still rendered by the full (non-`sessionsOnly`) sidebar variant
+- `src/modules/WorkStation/Browser/Panels/BrowserMainPane/{content/WebViewportContent,components/WebUrlBar}` — the live browser viewport + URL bar
+
+**Shared files edited in place** to sever the `token-category` branch:
+
+- `src/store/workstation/tabs/types.ts` — dropped `"token-category"` from the `WorkStationTabType` union and the `TOOL_TAB_TYPES` list
+- `src/store/workstation/tabHost.ts`, `src/store/workstation/tabs/tabFactory.ts` — removed its `→ "browser"` host mapping
+- `src/modules/WorkStation/TabContent/registry.ts`, `.../renderers/index.ts` — removed the renderer entry + barrel re-export
+- `src/store/workstation/browser/tabs/index.ts` — removed the `token-category` id-helpers, `createTokenCategoryTab` / `createColorTokensTab`, the `isShowingTokenCategoryAtom` / `tokenCategoryTabsAtom` atoms, `TokenCategoryData`, and its `BROWSER_TAB_TYPES` membership
+- `src/modules/WorkStation/Browser/BrowserLayout/{index.tsx,useBrowserLayoutState.ts}` — removed the `TokenManagerPanel` mount, `handleOpenColorTokens` / `handleOpenHistoryUrl`, and the `useGlobalTokens` auto-scan wiring
+
+**To restore:** reverse the `git mv`s above and revert the in-place edits (see the archival commit).
+
+**Note:** any browser tab of type `token-category` persisted in a user's saved workstation layout will no longer resolve to a renderer. This feature was reachable only via the removed Design pill, so that is expected.
+
+## WorkStation Database app — archived 2026-07-14
+
+The WorkStation "Database" app (the **Data** dock app, its tab types, renderers, and the `DatabaseManager` module) was removed from the live WorkStation. See `docs/workstation-unification/phase-2-host-hoist-plan.md` for the broader unification effort this is part of.
+
+**What moved here (self-contained to the app):**
+
+- `src/modules/WorkStation/DatabaseManager/` — the whole host module
+- `src/hooks/database/` — its hooks (`useSqliteDatabase`, `usePendingChanges`, `useQueryHistory`, `useDatabaseConnections`)
+- `src/store/workstation/tabs/factories/database.ts` — db tab factories/creators
+- `src/modules/WorkStation/TabContent/renderers/{table,query,schema,addConnection}.tsx` — the (placeholder) unified renderers
+- `src/modules/WorkStation/shared/StatusBar/DatabaseStatusBar.tsx`
+
+**What deliberately stayed live (shared with other features):**
+
+- `src/engines/DatabaseCore/` and `src/store/workstation/database/` — used by MainApp → Integrations → Databases, the CodeMirror SQL editor, and the Code Editor's SQLite file preview
+- `src/assets/databaseIcons/`, `src/hooks/workStation/database/` (Code Editor `.sqlite` preview)
+- Rust: `src-tauri/crates/db-browser` and `crates/db-clients` (the `db_*` / `db_sql_*` Tauri commands) — still invoked by the above. `crates/database` is the app's own persistence and is unrelated.
+
+**Shared files that were edited in place (not moved)** to sever the db branch: AppShell (`AppShellContent`, `index.tsx`, `useAppShellDerivedState`, `useMyStationDockSegments`), tab store (`tabHost`, `tabs/types`, `tabFactory`, `factories/index`, `tabs/index`), `dockFilter/atoms`, `TabContent/registry`, routes (`routeViewModeConfig`, `routeGroups`, router redirect, `componentMapping`), and `StatusBarRenderer` + `shared/StatusBar/index`.
+
+**To restore:** reverse the `git mv`s above, revert the in-place edits (see the archival commit), and remove `.archive` from `tsconfig.json`'s `exclude`.
+
+**Known harmless leftovers (intentional, to limit ripple):** the `db-table`/`db-query`/`db-schema` members of `WorkStationTabCategory` and the `"data"` slot in `StatusBarAppType` remain as unused union members; the `dockFilter.data` i18n key remains in `navigation.json` across locales.
diff --git a/src/hooks/database/index.ts b/.archive/src/hooks/database/index.ts
similarity index 100%
rename from src/hooks/database/index.ts
rename to .archive/src/hooks/database/index.ts
diff --git a/src/hooks/database/useDatabaseConnections.ts b/.archive/src/hooks/database/useDatabaseConnections.ts
similarity index 100%
rename from src/hooks/database/useDatabaseConnections.ts
rename to .archive/src/hooks/database/useDatabaseConnections.ts
diff --git a/src/hooks/database/usePendingChanges.ts b/.archive/src/hooks/database/usePendingChanges.ts
similarity index 100%
rename from src/hooks/database/usePendingChanges.ts
rename to .archive/src/hooks/database/usePendingChanges.ts
diff --git a/src/hooks/database/useQueryHistory.ts b/.archive/src/hooks/database/useQueryHistory.ts
similarity index 100%
rename from src/hooks/database/useQueryHistory.ts
rename to .archive/src/hooks/database/useQueryHistory.ts
diff --git a/src/hooks/database/useSqliteDatabase.ts b/.archive/src/hooks/database/useSqliteDatabase.ts
similarity index 100%
rename from src/hooks/database/useSqliteDatabase.ts
rename to .archive/src/hooks/database/useSqliteDatabase.ts
diff --git a/src/modules/WorkStation/Browser/Panels/BrowserMainPane/components/DesignFileBar/index.tsx b/.archive/src/modules/WorkStation/Browser/Panels/BrowserMainPane/components/DesignFileBar/index.tsx
similarity index 100%
rename from src/modules/WorkStation/Browser/Panels/BrowserMainPane/components/DesignFileBar/index.tsx
rename to .archive/src/modules/WorkStation/Browser/Panels/BrowserMainPane/components/DesignFileBar/index.tsx
diff --git a/src/modules/WorkStation/Browser/Panels/BrowserMainPane/content/TokenManagerContent/index.tsx b/.archive/src/modules/WorkStation/Browser/Panels/BrowserMainPane/content/TokenManagerContent/index.tsx
similarity index 100%
rename from src/modules/WorkStation/Browser/Panels/BrowserMainPane/content/TokenManagerContent/index.tsx
rename to .archive/src/modules/WorkStation/Browser/Panels/BrowserMainPane/content/TokenManagerContent/index.tsx
diff --git a/src/modules/WorkStation/DatabaseManager/DatabaseLayout/config.ts b/.archive/src/modules/WorkStation/DatabaseManager/DatabaseLayout/config.ts
similarity index 100%
rename from src/modules/WorkStation/DatabaseManager/DatabaseLayout/config.ts
rename to .archive/src/modules/WorkStation/DatabaseManager/DatabaseLayout/config.ts
diff --git a/src/modules/WorkStation/DatabaseManager/DatabaseLayout/index.tsx b/.archive/src/modules/WorkStation/DatabaseManager/DatabaseLayout/index.tsx
similarity index 100%
rename from src/modules/WorkStation/DatabaseManager/DatabaseLayout/index.tsx
rename to .archive/src/modules/WorkStation/DatabaseManager/DatabaseLayout/index.tsx
diff --git a/.archive/src/modules/WorkStation/DatabaseManager/DatabaseLayout/overlays/AddConnectionModal/AddConnectionFormField.tsx b/.archive/src/modules/WorkStation/DatabaseManager/DatabaseLayout/overlays/AddConnectionModal/AddConnectionFormField.tsx
new file mode 100644
index 000000000..c8ab451cd
--- /dev/null
+++ b/.archive/src/modules/WorkStation/DatabaseManager/DatabaseLayout/overlays/AddConnectionModal/AddConnectionFormField.tsx
@@ -0,0 +1,27 @@
+import React from "react";
+
+import Input, { InputProps } from "@src/components/Input";
+
+interface AddConnectionFormFieldProps extends Omit {
+ label: React.ReactNode;
+ onChange: (value: string) => void;
+ hint?: React.ReactNode;
+}
+
+export function AddConnectionFormField({
+ label,
+ onChange,
+ hint,
+ className,
+ ...inputProps
+}: AddConnectionFormFieldProps) {
+ return (
+
+ >
+ );
+});
diff --git a/src/modules/WorkStation/DatabaseManager/DatabaseLayout/overlays/AddConnectionModal/SqliteConnectionFields.tsx b/.archive/src/modules/WorkStation/DatabaseManager/DatabaseLayout/overlays/AddConnectionModal/SqliteConnectionFields.tsx
similarity index 100%
rename from src/modules/WorkStation/DatabaseManager/DatabaseLayout/overlays/AddConnectionModal/SqliteConnectionFields.tsx
rename to .archive/src/modules/WorkStation/DatabaseManager/DatabaseLayout/overlays/AddConnectionModal/SqliteConnectionFields.tsx
diff --git a/.archive/src/modules/WorkStation/DatabaseManager/DatabaseLayout/overlays/AddConnectionModal/SupabaseConnectionFields.tsx b/.archive/src/modules/WorkStation/DatabaseManager/DatabaseLayout/overlays/AddConnectionModal/SupabaseConnectionFields.tsx
new file mode 100644
index 000000000..b5c26c082
--- /dev/null
+++ b/.archive/src/modules/WorkStation/DatabaseManager/DatabaseLayout/overlays/AddConnectionModal/SupabaseConnectionFields.tsx
@@ -0,0 +1,54 @@
+import { memo } from "react";
+import { useTranslation } from "react-i18next";
+
+import { AddConnectionFormField } from "./AddConnectionFormField";
+
+export interface SupabaseConnectionFieldsProps {
+ supabaseUrl: string;
+ supabaseAccessToken: string;
+ onSupabaseUrlChange: (value: string) => void;
+ onSupabaseAccessTokenChange: (value: string) => void;
+}
+
+export const SupabaseConnectionFields = memo(function SupabaseConnectionFields({
+ supabaseUrl,
+ supabaseAccessToken,
+ onSupabaseUrlChange,
+ onSupabaseAccessTokenChange,
+}: SupabaseConnectionFieldsProps) {
+ const { t } = useTranslation();
+
+ return (
+ <>
+
+
+ {t("database.supabaseTokenHint")}{" "}
+
+ supabase.com/dashboard/account/tokens
+
+ >
+ }
+ />
+ >
+ );
+});
diff --git a/.archive/src/modules/WorkStation/DatabaseManager/DatabaseLayout/overlays/AddConnectionModal/TursoConnectionFields.tsx b/.archive/src/modules/WorkStation/DatabaseManager/DatabaseLayout/overlays/AddConnectionModal/TursoConnectionFields.tsx
new file mode 100644
index 000000000..5f0304ac3
--- /dev/null
+++ b/.archive/src/modules/WorkStation/DatabaseManager/DatabaseLayout/overlays/AddConnectionModal/TursoConnectionFields.tsx
@@ -0,0 +1,46 @@
+import { memo } from "react";
+import { useTranslation } from "react-i18next";
+
+import { AddConnectionFormField } from "./AddConnectionFormField";
+
+export interface TursoConnectionFieldsProps {
+ tursoUrl: string;
+ tursoToken: string;
+ onTursoUrlChange: (value: string) => void;
+ onTursoTokenChange: (value: string) => void;
+}
+
+export const TursoConnectionFields = memo(function TursoConnectionFields({
+ tursoUrl,
+ tursoToken,
+ onTursoUrlChange,
+ onTursoTokenChange,
+}: TursoConnectionFieldsProps) {
+ const { t } = useTranslation();
+
+ return (
+ <>
+
+
+ {t("database.authToken")}{" "}
+ ({t("optional")})
+ >
+ }
+ type="password"
+ value={tursoToken}
+ onChange={onTursoTokenChange}
+ placeholder="eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9..."
+ hint={t("database.tursoTokenHint")}
+ />
+ >
+ );
+});
diff --git a/src/modules/WorkStation/DatabaseManager/DatabaseLayout/overlays/AddConnectionModal/databaseTypeOptions.tsx b/.archive/src/modules/WorkStation/DatabaseManager/DatabaseLayout/overlays/AddConnectionModal/databaseTypeOptions.tsx
similarity index 100%
rename from src/modules/WorkStation/DatabaseManager/DatabaseLayout/overlays/AddConnectionModal/databaseTypeOptions.tsx
rename to .archive/src/modules/WorkStation/DatabaseManager/DatabaseLayout/overlays/AddConnectionModal/databaseTypeOptions.tsx
diff --git a/src/modules/WorkStation/DatabaseManager/DatabaseLayout/overlays/AddConnectionModal/formInputClass.ts b/.archive/src/modules/WorkStation/DatabaseManager/DatabaseLayout/overlays/AddConnectionModal/formInputClass.ts
similarity index 100%
rename from src/modules/WorkStation/DatabaseManager/DatabaseLayout/overlays/AddConnectionModal/formInputClass.ts
rename to .archive/src/modules/WorkStation/DatabaseManager/DatabaseLayout/overlays/AddConnectionModal/formInputClass.ts
diff --git a/src/modules/WorkStation/DatabaseManager/DatabaseLayout/overlays/AddConnectionModal/index.scss b/.archive/src/modules/WorkStation/DatabaseManager/DatabaseLayout/overlays/AddConnectionModal/index.scss
similarity index 100%
rename from src/modules/WorkStation/DatabaseManager/DatabaseLayout/overlays/AddConnectionModal/index.scss
rename to .archive/src/modules/WorkStation/DatabaseManager/DatabaseLayout/overlays/AddConnectionModal/index.scss
diff --git a/src/modules/WorkStation/DatabaseManager/DatabaseLayout/overlays/AddConnectionModal/index.tsx b/.archive/src/modules/WorkStation/DatabaseManager/DatabaseLayout/overlays/AddConnectionModal/index.tsx
similarity index 100%
rename from src/modules/WorkStation/DatabaseManager/DatabaseLayout/overlays/AddConnectionModal/index.tsx
rename to .archive/src/modules/WorkStation/DatabaseManager/DatabaseLayout/overlays/AddConnectionModal/index.tsx
diff --git a/src/modules/WorkStation/DatabaseManager/DatabaseLayout/overlays/AddConnectionModal/types.ts b/.archive/src/modules/WorkStation/DatabaseManager/DatabaseLayout/overlays/AddConnectionModal/types.ts
similarity index 100%
rename from src/modules/WorkStation/DatabaseManager/DatabaseLayout/overlays/AddConnectionModal/types.ts
rename to .archive/src/modules/WorkStation/DatabaseManager/DatabaseLayout/overlays/AddConnectionModal/types.ts
diff --git a/src/modules/WorkStation/DatabaseManager/Panels/DatabaseMainPane/components/DataGrid/ActionBar.tsx b/.archive/src/modules/WorkStation/DatabaseManager/Panels/DatabaseMainPane/components/DataGrid/ActionBar.tsx
similarity index 100%
rename from src/modules/WorkStation/DatabaseManager/Panels/DatabaseMainPane/components/DataGrid/ActionBar.tsx
rename to .archive/src/modules/WorkStation/DatabaseManager/Panels/DatabaseMainPane/components/DataGrid/ActionBar.tsx
diff --git a/src/modules/WorkStation/DatabaseManager/Panels/DatabaseMainPane/components/DataGrid/InlineEditCell.tsx b/.archive/src/modules/WorkStation/DatabaseManager/Panels/DatabaseMainPane/components/DataGrid/InlineEditCell.tsx
similarity index 100%
rename from src/modules/WorkStation/DatabaseManager/Panels/DatabaseMainPane/components/DataGrid/InlineEditCell.tsx
rename to .archive/src/modules/WorkStation/DatabaseManager/Panels/DatabaseMainPane/components/DataGrid/InlineEditCell.tsx
diff --git a/src/modules/WorkStation/DatabaseManager/Panels/DatabaseMainPane/components/DataGrid/InsertRowModal.tsx b/.archive/src/modules/WorkStation/DatabaseManager/Panels/DatabaseMainPane/components/DataGrid/InsertRowModal.tsx
similarity index 100%
rename from src/modules/WorkStation/DatabaseManager/Panels/DatabaseMainPane/components/DataGrid/InsertRowModal.tsx
rename to .archive/src/modules/WorkStation/DatabaseManager/Panels/DatabaseMainPane/components/DataGrid/InsertRowModal.tsx
diff --git a/src/modules/WorkStation/DatabaseManager/Panels/DatabaseMainPane/components/DataGrid/index.scss b/.archive/src/modules/WorkStation/DatabaseManager/Panels/DatabaseMainPane/components/DataGrid/index.scss
similarity index 100%
rename from src/modules/WorkStation/DatabaseManager/Panels/DatabaseMainPane/components/DataGrid/index.scss
rename to .archive/src/modules/WorkStation/DatabaseManager/Panels/DatabaseMainPane/components/DataGrid/index.scss
diff --git a/src/modules/WorkStation/DatabaseManager/Panels/DatabaseMainPane/components/DataGrid/index.tsx b/.archive/src/modules/WorkStation/DatabaseManager/Panels/DatabaseMainPane/components/DataGrid/index.tsx
similarity index 100%
rename from src/modules/WorkStation/DatabaseManager/Panels/DatabaseMainPane/components/DataGrid/index.tsx
rename to .archive/src/modules/WorkStation/DatabaseManager/Panels/DatabaseMainPane/components/DataGrid/index.tsx
diff --git a/src/modules/WorkStation/DatabaseManager/Panels/DatabaseMainPane/config.ts b/.archive/src/modules/WorkStation/DatabaseManager/Panels/DatabaseMainPane/config.ts
similarity index 100%
rename from src/modules/WorkStation/DatabaseManager/Panels/DatabaseMainPane/config.ts
rename to .archive/src/modules/WorkStation/DatabaseManager/Panels/DatabaseMainPane/config.ts
diff --git a/src/modules/WorkStation/DatabaseManager/Panels/DatabaseMainPane/index.tsx b/.archive/src/modules/WorkStation/DatabaseManager/Panels/DatabaseMainPane/index.tsx
similarity index 100%
rename from src/modules/WorkStation/DatabaseManager/Panels/DatabaseMainPane/index.tsx
rename to .archive/src/modules/WorkStation/DatabaseManager/Panels/DatabaseMainPane/index.tsx
diff --git a/src/modules/WorkStation/DatabaseManager/Panels/DatabaseMainPane/types.ts b/.archive/src/modules/WorkStation/DatabaseManager/Panels/DatabaseMainPane/types.ts
similarity index 100%
rename from src/modules/WorkStation/DatabaseManager/Panels/DatabaseMainPane/types.ts
rename to .archive/src/modules/WorkStation/DatabaseManager/Panels/DatabaseMainPane/types.ts
diff --git a/src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/config.ts b/.archive/src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/config.ts
similarity index 100%
rename from src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/config.ts
rename to .archive/src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/config.ts
diff --git a/src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/content/ConnectionsContent/components/AddedConnectionsList.tsx b/.archive/src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/content/ConnectionsContent/components/AddedConnectionsList.tsx
similarity index 100%
rename from src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/content/ConnectionsContent/components/AddedConnectionsList.tsx
rename to .archive/src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/content/ConnectionsContent/components/AddedConnectionsList.tsx
diff --git a/src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/content/ConnectionsContent/components/PendingConnectionsList.tsx b/.archive/src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/content/ConnectionsContent/components/PendingConnectionsList.tsx
similarity index 100%
rename from src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/content/ConnectionsContent/components/PendingConnectionsList.tsx
rename to .archive/src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/content/ConnectionsContent/components/PendingConnectionsList.tsx
diff --git a/src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/content/ConnectionsContent/index.tsx b/.archive/src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/content/ConnectionsContent/index.tsx
similarity index 100%
rename from src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/content/ConnectionsContent/index.tsx
rename to .archive/src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/content/ConnectionsContent/index.tsx
diff --git a/src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/content/QueryHistoryContent/index.tsx b/.archive/src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/content/QueryHistoryContent/index.tsx
similarity index 100%
rename from src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/content/QueryHistoryContent/index.tsx
rename to .archive/src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/content/QueryHistoryContent/index.tsx
diff --git a/src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/hooks/useDatabaseSidebarState.ts b/.archive/src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/hooks/useDatabaseSidebarState.ts
similarity index 100%
rename from src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/hooks/useDatabaseSidebarState.ts
rename to .archive/src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/hooks/useDatabaseSidebarState.ts
diff --git a/src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/index.tsx b/.archive/src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/index.tsx
similarity index 100%
rename from src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/index.tsx
rename to .archive/src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/index.tsx
diff --git a/src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/tabs/ConnectionsTab.tsx b/.archive/src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/tabs/ConnectionsTab.tsx
similarity index 100%
rename from src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/tabs/ConnectionsTab.tsx
rename to .archive/src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/tabs/ConnectionsTab.tsx
diff --git a/src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/tabs/QueryHistoryTab.tsx b/.archive/src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/tabs/QueryHistoryTab.tsx
similarity index 100%
rename from src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/tabs/QueryHistoryTab.tsx
rename to .archive/src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/tabs/QueryHistoryTab.tsx
diff --git a/src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/types.ts b/.archive/src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/types.ts
similarity index 100%
rename from src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/types.ts
rename to .archive/src/modules/WorkStation/DatabaseManager/Panels/DatabasePrimarySidebar/types.ts
diff --git a/src/modules/WorkStation/DatabaseManager/config.ts b/.archive/src/modules/WorkStation/DatabaseManager/config.ts
similarity index 100%
rename from src/modules/WorkStation/DatabaseManager/config.ts
rename to .archive/src/modules/WorkStation/DatabaseManager/config.ts
diff --git a/src/modules/WorkStation/DatabaseManager/index.tsx b/.archive/src/modules/WorkStation/DatabaseManager/index.tsx
similarity index 100%
rename from src/modules/WorkStation/DatabaseManager/index.tsx
rename to .archive/src/modules/WorkStation/DatabaseManager/index.tsx
diff --git a/src/modules/WorkStation/TabContent/renderers/addConnection.tsx b/.archive/src/modules/WorkStation/TabContent/renderers/addConnection.tsx
similarity index 100%
rename from src/modules/WorkStation/TabContent/renderers/addConnection.tsx
rename to .archive/src/modules/WorkStation/TabContent/renderers/addConnection.tsx
diff --git a/src/modules/WorkStation/TabContent/renderers/query.tsx b/.archive/src/modules/WorkStation/TabContent/renderers/query.tsx
similarity index 100%
rename from src/modules/WorkStation/TabContent/renderers/query.tsx
rename to .archive/src/modules/WorkStation/TabContent/renderers/query.tsx
diff --git a/src/modules/WorkStation/TabContent/renderers/schema.tsx b/.archive/src/modules/WorkStation/TabContent/renderers/schema.tsx
similarity index 100%
rename from src/modules/WorkStation/TabContent/renderers/schema.tsx
rename to .archive/src/modules/WorkStation/TabContent/renderers/schema.tsx
diff --git a/src/modules/WorkStation/TabContent/renderers/table.tsx b/.archive/src/modules/WorkStation/TabContent/renderers/table.tsx
similarity index 100%
rename from src/modules/WorkStation/TabContent/renderers/table.tsx
rename to .archive/src/modules/WorkStation/TabContent/renderers/table.tsx
diff --git a/src/modules/WorkStation/TabContent/renderers/tokenCategory.tsx b/.archive/src/modules/WorkStation/TabContent/renderers/tokenCategory.tsx
similarity index 100%
rename from src/modules/WorkStation/TabContent/renderers/tokenCategory.tsx
rename to .archive/src/modules/WorkStation/TabContent/renderers/tokenCategory.tsx
diff --git a/src/modules/WorkStation/shared/StatusBar/DatabaseStatusBar.tsx b/.archive/src/modules/WorkStation/shared/StatusBar/DatabaseStatusBar.tsx
similarity index 100%
rename from src/modules/WorkStation/shared/StatusBar/DatabaseStatusBar.tsx
rename to .archive/src/modules/WorkStation/shared/StatusBar/DatabaseStatusBar.tsx
diff --git a/src/store/workstation/tabs/factories/database.ts b/.archive/src/store/workstation/tabs/factories/database.ts
similarity index 100%
rename from src/store/workstation/tabs/factories/database.ts
rename to .archive/src/store/workstation/tabs/factories/database.ts
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 30f6b7257..a2a1df86c 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -4,16 +4,11 @@
## Test plan
-- [ ] I ran the checks relevant to the changed files, or explained why they were not run.
+-
-## Contributor checklist
+## Submit checklist
-- [ ] The PR title uses scoped Conventional Commits format, such as `feat(scope): summary` or `fix(scope): summary`.
-- [ ] All commits use scoped Conventional Commits format.
-- [ ] I did not skip pre-commit, pre-push, lint-staged, or other repository hooks.
-- [ ] The PR is scoped to one issue, feature, or fix.
-- [ ] I have signed the CLA if prompted by the CLA Assistant bot.
-- [ ] A human actively participated in the design and implementation process, and reviewed the contribution before submission.
-- [ ] I did not include secrets, private configuration, generated build output, or unrelated formatting changes.
-- [ ] I updated documentation for behavior, setup, or architecture changes where needed.
-- [ ] I updated all supported locale files for new UI text where needed.
+- [ ] The PR is focused and has a scoped Conventional Commits title, such as `feat(scope): summary` or `fix(scope): summary`.
+- [ ] I ran the relevant checks, or explained why they were not run.
+- [ ] No secrets, private config, generated output, or unrelated formatting changes are included.
+- [ ] Docs, screenshots, and locale updates are included when the change needs them.
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index eedcc907d..0a429fd72 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,8 +1,8 @@
-# CI — runs on every pull request to release / master.
+# CI — runs on every pull request to develop / release / master.
#
# Two parallel jobs:
# frontend — TypeScript typecheck + ESLint + Vitest unit tests
-# rust — cargo check + clippy (warnings-as-errors) + cargo test
+# rust — cargo check + advisory clippy + cargo test
#
# Mirrors the toolchain versions used in release.yaml (Node 20, pnpm 9,
# Rust stable, swatinem/rust-cache) so CI and release builds stay in sync.
@@ -12,6 +12,7 @@ name: "CI"
on:
pull_request:
branches:
+ - develop
- release
- master
@@ -45,12 +46,14 @@ jobs:
- name: Type check
run: pnpm typecheck
+ env:
+ NODE_OPTIONS: "--max-old-space-size=6144"
- name: Lint
run: pnpm lint
- name: Unit tests
- run: pnpm test --run
+ run: pnpm run test
# ── Rust ────────────────────────────────────────────────────────────────────
rust:
@@ -74,10 +77,17 @@ jobs:
working-directory: src-tauri
run: cargo check --workspace
- - name: cargo clippy
+ # The current develop baseline has pre-existing Clippy warnings across
+ # multiple crates. Keep Clippy visible and compilation-blocking without
+ # letting toolchain lint drift block every feature PR; restore
+ # `-- -D warnings` after the baseline cleanup lands.
+ - name: cargo clippy (advisory warnings)
working-directory: src-tauri
- run: cargo clippy --workspace -- -D warnings
+ run: cargo clippy --workspace
- name: cargo test
working-directory: src-tauri
- run: cargo test --workspace
+ # Several agent-core tests intentionally exercise process-global SQLite
+ # state. Keep the workspace suite deterministic until those fixtures are
+ # migrated to per-test connections.
+ run: cargo test --workspace -- --test-threads=1
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index a008ae3af..cc15bafc0 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -1,9 +1,22 @@
-# Build, sign, notarize, and release ORGII for macOS (Apple Silicon) and Windows (x64).
+# Build, sign, notarize, and release ORGII for macOS (Apple Silicon), Windows (x64), and Linux (x64).
#
# Trigger: push a tag matching v* (e.g. v1.1.0, v1.1.1).
# Release tags must be valid three-part SemVer because Tauri app/updater
# versions are written directly from the tag.
#
+# Update channels:
+# Stable — tag vX.Y.Z. Served via the GitHub "latest release" alias
+# (releases/latest/download/latest.json), which excludes prereleases.
+# Beta — tag vX.Y.Z-beta.N for the NEXT unreleased X.Y.Z (never a version
+# that already shipped, or SemVer would sort it below stable and the
+# updater would never offer it). Marked as a GitHub prerelease.
+# Windows beta builds ship NSIS only: WiX/MSI rejects SemVer prerelease
+# versions, and the updater uses the NSIS artifact anyway.
+# Every release (stable and beta) overwrites beta.json on the rolling
+# `updater` release when it is the highest version so far, so the beta
+# channel always points at the newest build of either kind and beta users
+# converge back onto stable once it overtakes the last beta.
+#
# Sidecar binaries (peekaboo, agent-browser, dugite/git) are NOT bundled in the
# .app. They are downloaded at first launch into ~/.orgii/bin/ by the app itself
# (post-notarized download strategy). This keeps the notarized bundle small and
@@ -168,14 +181,15 @@ jobs:
UPDATER_TAR=$(find "$BUNDLE_DIR/macos" -name "*.app.tar.gz" ! -name "*.sig" | head -1)
UPDATER_SIG=$(find "$BUNDLE_DIR/macos" -name "*.app.tar.gz.sig" | head -1)
- echo "updater_tar=$UPDATER_TAR" >> "$GITHUB_OUTPUT"
+ LATEST_UPDATER_TAR="ORG2-updater-mac-apple-silicon.app.tar.gz"
+ cp "$UPDATER_TAR" "$LATEST_UPDATER_TAR"
+ echo "updater_tar=$LATEST_UPDATER_TAR" >> "$GITHUB_OUTPUT"
echo "updater_sig=$UPDATER_SIG" >> "$GITHUB_OUTPUT"
- echo "updater_tar_name=$(basename "$UPDATER_TAR")" >> "$GITHUB_OUTPUT"
- echo "updater_sig_name=$(basename "$UPDATER_SIG")" >> "$GITHUB_OUTPUT"
+ echo "updater_tar_name=$LATEST_UPDATER_TAR" >> "$GITHUB_OUTPUT"
echo "=== Release artifacts ==="
echo "DMG: $DMG"
- echo "Updater tar: $UPDATER_TAR"
+ echo "Updater tar: $LATEST_UPDATER_TAR"
echo "Updater sig: $UPDATER_SIG"
# ── Generate updater manifest (latest.json) ───────────────
@@ -212,10 +226,8 @@ jobs:
prerelease: ${{ contains(github.ref_name, '-rc') || contains(github.ref_name, '-beta') || contains(github.ref_name, '-alpha') || contains(github.ref_name, '-nightly') }}
generate_release_notes: true
files: |
- ${{ steps.artifacts.outputs.dmg }}
${{ steps.artifacts.outputs.latest_dmg }}
${{ steps.artifacts.outputs.updater_tar }}
- ${{ steps.artifacts.outputs.updater_sig }}
latest.json
# ── Cleanup keychain ────────────────────────────────────────
@@ -256,6 +268,15 @@ jobs:
SEMVER="$FULL_VERSION"
echo "VERSION=$FULL_VERSION" >> "$GITHUB_ENV"
echo "SEMVER=$SEMVER" >> "$GITHUB_ENV"
+ # WiX/MSI rejects SemVer prerelease versions, so beta builds ship NSIS only.
+ # The updater's windows-x86_64 entry uses NSIS either way.
+ if [[ "$FULL_VERSION" == *-* ]]; then
+ echo "IS_PRERELEASE=true" >> "$GITHUB_ENV"
+ echo "WIN_BUNDLES=nsis" >> "$GITHUB_ENV"
+ else
+ echo "IS_PRERELEASE=false" >> "$GITHUB_ENV"
+ echo "WIN_BUNDLES=msi,nsis" >> "$GITHUB_ENV"
+ fi
sed -i "s/\"version\": \".*\"/\"version\": \"$SEMVER\"/" src-tauri/tauri.conf.json
sed -i "s/\"version\": \".*\"/\"version\": \"$FULL_VERSION\"/" package.json
@@ -291,7 +312,7 @@ jobs:
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
ORGII_DIAGNOSTICS_TOKEN: ${{ secrets.ORGII_DIAGNOSTICS_TOKEN }}
ORGII_APP_VERSION: ${{ env.SEMVER }}
- run: pnpm tauri build --target x86_64-pc-windows-msvc
+ run: pnpm tauri build --target x86_64-pc-windows-msvc --bundles ${{ env.WIN_BUNDLES }}
# ── Sign with Azure Trusted Signing ─────────────────────────
- name: Azure login (OIDC)
@@ -316,6 +337,36 @@ jobs:
timestamp-rfc3161: http://timestamp.acs.microsoft.com
timestamp-digest: SHA256
+ # ── Regenerate updater signatures after code signing ─────────
+ - name: Regenerate updater signatures
+ shell: bash
+ env:
+ TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
+ TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
+ run: |
+ BUNDLE_DIR="src-tauri/target/x86_64-pc-windows-msvc/release/bundle"
+ # The msi directory does not exist on prerelease builds (nsis-only).
+ # This step runs under bash -eo pipefail, so a bare find would kill
+ # the whole script the moment it exits non-zero.
+ MSI=$(find "$BUNDLE_DIR/msi" -name "*.msi" ! -name "*.zip" 2>/dev/null | head -1 || true)
+ NSIS=$(find "$BUNDLE_DIR/nsis" -name "*.exe" ! -name "*.zip" | head -1)
+
+ if [ ! -f "$NSIS" ]; then
+ echo "Missing Windows updater artifact: $NSIS" >&2
+ exit 1
+ fi
+ if [ "$IS_PRERELEASE" != "true" ] && [ ! -f "$MSI" ]; then
+ echo "Missing Windows updater artifact: $MSI" >&2
+ exit 1
+ fi
+
+ rm -f "$NSIS.sig"
+ pnpm tauri signer sign "$NSIS"
+ if [ -f "$MSI" ]; then
+ rm -f "$MSI.sig"
+ pnpm tauri signer sign "$MSI"
+ fi
+
# ── Gather artifacts ─────────────────────────────────────────
- name: Gather release artifacts
id: artifacts
@@ -323,43 +374,261 @@ jobs:
run: |
BUNDLE_DIR="src-tauri/target/x86_64-pc-windows-msvc/release/bundle"
- MSI=$(find "$BUNDLE_DIR/msi" -name "*.msi" ! -name "*.zip" | head -1)
- MSI_ZIP=$(find "$BUNDLE_DIR/msi" -name "*.msi.zip" | head -1)
- MSI_SIG=$(find "$BUNDLE_DIR/msi" -name "*.msi.zip.sig" | head -1)
+ # msi directory is absent on prerelease builds; see note in the
+ # signature-regeneration step about bash -eo pipefail.
+ MSI=$(find "$BUNDLE_DIR/msi" -name "*.msi" ! -name "*.zip" 2>/dev/null | head -1 || true)
+ MSI_SIG=$(find "$BUNDLE_DIR/msi" -name "*.msi.sig" 2>/dev/null | head -1 || true)
NSIS=$(find "$BUNDLE_DIR/nsis" -name "*.exe" ! -name "*.zip" | head -1)
- NSIS_ZIP=$(find "$BUNDLE_DIR/nsis" -name "*.nsis.zip" | head -1)
- NSIS_SIG=$(find "$BUNDLE_DIR/nsis" -name "*.nsis.zip.sig" | head -1)
+ NSIS_SIG=$(find "$BUNDLE_DIR/nsis" -name "*.exe.sig" | head -1)
+
+ for artifact in "$NSIS" "$NSIS_SIG"; do
+ if [ ! -f "$artifact" ]; then
+ echo "Missing Windows release artifact: $artifact" >&2
+ exit 1
+ fi
+ done
+
+ if [ "$IS_PRERELEASE" != "true" ]; then
+ for artifact in "$MSI" "$MSI_SIG"; do
+ if [ ! -f "$artifact" ]; then
+ echo "Missing Windows release artifact: $artifact" >&2
+ exit 1
+ fi
+ done
+ fi
- LATEST_MSI="ORG2-latest-windows-x64.msi"
LATEST_NSIS="ORG2-latest-windows-x64-setup.exe"
- cp "$MSI" "$LATEST_MSI"
cp "$NSIS" "$LATEST_NSIS"
- echo "msi=$MSI" >> "$GITHUB_OUTPUT"
- echo "msi_zip=$MSI_ZIP" >> "$GITHUB_OUTPUT"
- echo "msi_sig=$MSI_SIG" >> "$GITHUB_OUTPUT"
+ if [ -f "$MSI" ]; then
+ LATEST_MSI="ORG2-latest-windows-x64.msi"
+ cp "$MSI" "$LATEST_MSI"
+ echo "msi=$MSI" >> "$GITHUB_OUTPUT"
+ echo "msi_sig=$MSI_SIG" >> "$GITHUB_OUTPUT"
+ echo "latest_msi=$LATEST_MSI" >> "$GITHUB_OUTPUT"
+ fi
+
echo "nsis=$NSIS" >> "$GITHUB_OUTPUT"
- echo "nsis_zip=$NSIS_ZIP" >> "$GITHUB_OUTPUT"
echo "nsis_sig=$NSIS_SIG" >> "$GITHUB_OUTPUT"
- echo "latest_msi=$LATEST_MSI" >> "$GITHUB_OUTPUT"
echo "latest_nsis=$LATEST_NSIS" >> "$GITHUB_OUTPUT"
echo "=== Windows artifacts ==="
- echo "MSI: $MSI"
- echo "NSIS: $NSIS"
+ echo "MSI: ${MSI:-}"
+ echo "MSI sig: ${MSI_SIG:-}"
+ echo "NSIS: $NSIS"
+ echo "NSIS sig: $NSIS_SIG"
+
+ # ── Merge Windows updater entry into latest.json ─────────────
+ - name: Generate latest.json for Windows updater
+ env:
+ TAG: ${{ github.ref_name }}
+ GH_TOKEN: ${{ github.token }}
+ shell: bash
+ run: |
+ gh release download "$TAG" --pattern latest.json --output latest.json
+
+ SIGNATURE=$(cat "${{ steps.artifacts.outputs.nsis_sig }}")
+ NSIS_NAME="${{ steps.artifacts.outputs.latest_nsis }}"
+ DOWNLOAD_URL="https://github.com/${{ github.repository }}/releases/download/${TAG}/${NSIS_NAME}"
+
+ jq \
+ --arg signature "$SIGNATURE" \
+ --arg url "$DOWNLOAD_URL" \
+ '.platforms["windows-x86_64"] = { signature: $signature, url: $url }' \
+ latest.json > latest-with-windows.json
+
+ mv latest-with-windows.json latest.json
+
+ echo "=== latest.json with Windows updater ==="
+ cat latest.json
# ── Upload to existing GitHub Release ───────────────────────
- name: Upload Windows artifacts to release
uses: softprops/action-gh-release@v3
with:
draft: false
+ overwrite_files: true
prerelease: ${{ contains(github.ref_name, '-rc') || contains(github.ref_name, '-beta') || contains(github.ref_name, '-alpha') || contains(github.ref_name, '-nightly') }}
files: |
- ${{ steps.artifacts.outputs.msi }}
${{ steps.artifacts.outputs.latest_msi }}
- ${{ steps.artifacts.outputs.msi_zip }}
- ${{ steps.artifacts.outputs.msi_sig }}
- ${{ steps.artifacts.outputs.nsis }}
${{ steps.artifacts.outputs.latest_nsis }}
- ${{ steps.artifacts.outputs.nsis_zip }}
- ${{ steps.artifacts.outputs.nsis_sig }}
+ latest.json
+
+ # ── Linux x64 build ───────────────────────────────────────────────────
+ build-linux:
+ name: Build & Release (Linux x64)
+ runs-on: ubuntu-22.04
+ needs: build-windows
+ steps:
+ # ── Checkout ────────────────────────────────────────────────
+ - name: Checkout
+ uses: actions/checkout@v6
+ with:
+ fetch-depth: 0
+
+ # ── Stamp version from git tag ───────────────────────────────
+ - name: Set version from tag
+ run: |
+ FULL_VERSION="${GITHUB_REF_NAME#v}"
+ if ! [[ "$FULL_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.-]+)?$ ]]; then
+ echo "Release tag must be valid three-part SemVer: $GITHUB_REF_NAME" >&2
+ exit 1
+ fi
+ SEMVER="$FULL_VERSION"
+ echo "VERSION=$FULL_VERSION" >> "$GITHUB_ENV"
+ echo "SEMVER=$SEMVER" >> "$GITHUB_ENV"
+ sed -i "s/\"version\": \".*\"/\"version\": \"$SEMVER\"/" src-tauri/tauri.conf.json
+ sed -i "s/\"version\": \".*\"/\"version\": \"$FULL_VERSION\"/" package.json
+
+ # ── System dependencies ──────────────────────────────────────
+ - name: Install Linux build dependencies
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y \
+ libwebkit2gtk-4.1-dev \
+ libgtk-3-dev \
+ libayatana-appindicator3-dev \
+ librsvg2-dev \
+ patchelf
+
+ # ── Node.js + pnpm ──────────────────────────────────────────
+ - name: Setup pnpm
+ uses: pnpm/action-setup@v6
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v6
+ with:
+ node-version: "20"
+ cache: "pnpm"
+
+ - name: Install frontend dependencies
+ run: pnpm install --frozen-lockfile
+
+ # ── Rust ────────────────────────────────────────────────────
+ - name: Install Rust stable
+ uses: dtolnay/rust-toolchain@stable
+ with:
+ targets: x86_64-unknown-linux-gnu
+
+ - name: Rust cache
+ uses: swatinem/rust-cache@v2
+ with:
+ workspaces: "./src-tauri -> target"
+ shared-key: "release-linux-x64"
+
+ # ── Build Tauri app ──────────────────────────────────────────
+ - name: Build ORGII
+ env:
+ TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
+ TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
+ ORGII_DIAGNOSTICS_TOKEN: ${{ secrets.ORGII_DIAGNOSTICS_TOKEN }}
+ ORGII_APP_VERSION: ${{ env.SEMVER }}
+ run: pnpm tauri build --target x86_64-unknown-linux-gnu --bundles deb,appimage
+
+ # ── Gather artifacts ─────────────────────────────────────────
+ - name: Gather release artifacts
+ id: artifacts
+ run: |
+ BUNDLE_DIR="src-tauri/target/x86_64-unknown-linux-gnu/release/bundle"
+
+ DEB=$(find "$BUNDLE_DIR/deb" -name "*.deb" | head -1)
+ APPIMAGE=$(find "$BUNDLE_DIR/appimage" -name "*.AppImage" | head -1)
+ APPIMAGE_SIG=$(find "$BUNDLE_DIR/appimage" -name "*.AppImage.sig" | head -1)
+
+ for artifact in "$DEB" "$APPIMAGE" "$APPIMAGE_SIG"; do
+ if [ ! -f "$artifact" ]; then
+ echo "Missing Linux release artifact: $artifact" >&2
+ find "$BUNDLE_DIR" -maxdepth 3 -type f -print >&2
+ exit 1
+ fi
+ done
+
+ LATEST_DEB="ORG2-latest-linux-x64.deb"
+ LATEST_APPIMAGE="ORG2-latest-linux-x64.AppImage"
+ cp "$DEB" "$LATEST_DEB"
+ cp "$APPIMAGE" "$LATEST_APPIMAGE"
+
+ echo "appimage_sig=$APPIMAGE_SIG" >> "$GITHUB_OUTPUT"
+ echo "latest_deb=$LATEST_DEB" >> "$GITHUB_OUTPUT"
+ echo "latest_appimage=$LATEST_APPIMAGE" >> "$GITHUB_OUTPUT"
+ echo "latest_appimage_name=$LATEST_APPIMAGE" >> "$GITHUB_OUTPUT"
+
+ echo "=== Linux artifacts ==="
+ echo "DEB: $DEB"
+ echo "AppImage: $APPIMAGE"
+ echo "AppImage sig: $APPIMAGE_SIG"
+
+ # ── Merge Linux updater entry into latest.json ───────────────
+ - name: Generate latest.json for Linux updater
+ env:
+ TAG: ${{ github.ref_name }}
+ GH_TOKEN: ${{ github.token }}
+ run: |
+ gh release download "$TAG" --pattern latest.json --output latest.json
+
+ SIGNATURE=$(cat "${{ steps.artifacts.outputs.appimage_sig }}")
+ APPIMAGE_NAME="${{ steps.artifacts.outputs.latest_appimage_name }}"
+ DOWNLOAD_URL="https://github.com/${{ github.repository }}/releases/download/${TAG}/${APPIMAGE_NAME}"
+
+ jq \
+ --arg signature "$SIGNATURE" \
+ --arg url "$DOWNLOAD_URL" \
+ '.platforms["linux-x86_64"] = { signature: $signature, url: $url }' \
+ latest.json > latest-with-linux.json
+
+ mv latest-with-linux.json latest.json
+
+ echo "=== latest.json with Linux updater ==="
+ cat latest.json
+
+ # ── Upload to existing GitHub Release ───────────────────────
+ - name: Upload Linux artifacts to release
+ uses: softprops/action-gh-release@v3
+ with:
+ draft: false
+ overwrite_files: true
+ prerelease: ${{ contains(github.ref_name, '-rc') || contains(github.ref_name, '-beta') || contains(github.ref_name, '-alpha') || contains(github.ref_name, '-nightly') }}
+ files: |
+ ${{ steps.artifacts.outputs.latest_deb }}
+ ${{ steps.artifacts.outputs.latest_appimage }}
+ latest.json
+
+ # ── Publish beta channel manifest ─────────────────────────────
+ # The `updater` release is a rolling prerelease that hosts channel
+ # manifests at a stable URL. Every release (stable and beta) overwrites
+ # beta.json when it is the highest version so far, so the beta channel
+ # always tracks the newest build of either kind. The stable channel
+ # keeps using the releases/latest alias and needs no extra publishing.
+ - name: Publish beta channel manifest
+ env:
+ TAG: ${{ github.ref_name }}
+ GH_TOKEN: ${{ github.token }}
+ run: |
+ if ! gh release view updater >/dev/null 2>&1; then
+ gh release create updater \
+ --title "Updater channel manifests" \
+ --notes "Rolling release hosting update-channel manifests (beta.json). Do not delete: installed apps on the beta channel poll its assets." \
+ --prerelease \
+ --target "$GITHUB_SHA"
+ fi
+
+ NEW_VERSION=$(jq -r '.version' latest.json)
+ EXISTING_VERSION=$(gh release download updater --pattern beta.json --output - 2>/dev/null | jq -r '.version // empty' || true)
+ EXISTING_VERSION="${EXISTING_VERSION:-0.0.0}"
+
+ # Guard against regressing beta.json when an older tag is (re)built
+ # after a newer one already published (e.g. a stable hotfix behind
+ # the current beta). semver handles prerelease ordering correctly.
+ HIGHEST=$(npx --yes semver@7 "$NEW_VERSION" "$EXISTING_VERSION" | tail -1)
+ if [ -z "$HIGHEST" ]; then
+ echo "semver comparison produced no output (npx failure?); refusing to guess" >&2
+ exit 1
+ fi
+ if [ "$HIGHEST" != "$NEW_VERSION" ]; then
+ echo "Skipping beta.json: existing $EXISTING_VERSION > new $NEW_VERSION"
+ exit 0
+ fi
+
+ cp latest.json beta.json
+ gh release upload updater beta.json --clobber
+ echo "Published beta.json for $NEW_VERSION (was $EXISTING_VERSION)"
diff --git a/.gitignore b/.gitignore
index de5284d68..9531804e0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
node_modules/
+.pnpm-store/
/dist/
/build/
coverage/
@@ -135,3 +136,5 @@ code-server-bin/
BitFun/
archive/
**/.build/
+# Codex CLI local state (created when codex runs inside the repo)
+.codex/
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 000000000..84491962f
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,74 @@
+# AGENTS.md — Agent Skill Routing for ORGII
+
+This file orients Codex / orgii agents working in this repo. It tells you **which audit / methodology skill to invoke** for which kind of task, and what to deliver before declaring work done.
+
+> Cursor IDE users: live UI-feature delivery rules live in `.cursor/rules/ui-feature-workflow.mdc`. This file does **not** replace those — it's about skill routing for AI agents, not unit-test gates.
+
+This is **advisory**, not a hard contract. Use judgment based on PR size and risk.
+
+---
+
+## Skill Routing Table
+
+| Scenario | Skill to invoke | When |
+| -------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
+| Rust / TypeScript architecture, types, dead code, FSM, naming overload, wire protocol, init parity | `architecture-audit` | Before finalizing a refactor plan; before cleanup/unification PRs; when reviewing a domain rewrite |
+| Frontend UI consistency, design-system component usage, arbitrary Tailwind values, a11y basics, visual-pattern duplication | `frontend-ui-audit` | Before delivering a PR that touches `*.tsx` under `src/components/` or `src/modules/**/components/` (component refactors, UI cleanup batches) |
+| Both layers change together (e.g. "refactor module X") | Run both, emit **two independent reports** | Don't fold them; each skill has its own decision rules |
+| E2E test surface (Playwright / WebDriver), test stability | `e2e-testing` | When adding or repairing rendered E2E specs |
+
+Skills live at:
+
+- `~/.orgii/skills/architecture-audit/SKILL.md` (user-global)
+- `~/.orgii/skills/frontend-ui-audit/SKILL.md` (user-global)
+- `.orgii/skills/architecture-audit/SKILL.md` (workspace copy, if present)
+- `.orgii/skills/e2e-testing/SKILL.md` (workspace)
+
+If the skill block isn't already prefetched in your context, read its `SKILL.md` before acting on it.
+
+---
+
+## Default Delivery Flow
+
+### Touching `*.tsx` files (UI work)
+
+Before declaring a UI-touching task complete, ask:
+
+1. **Is this a single-file bug fix?** If yes, skip `frontend-ui-audit` (its own "When NOT To Use" rules out single bug fixes — noise-to-value ratio is too high).
+2. **Is this a component refactor, UI cleanup, or "should this use the design system?" question?** If yes, run `frontend-ui-audit` over the changed files and drop a report in `docs/frontend-ui-audit-YYYY-MM-DD/.md` using the skill's output format. Summarize fix / keep-with-reason / abstract counts in the delivery message so the user can see verdicts without opening the file.
+3. **Did you find a fix-candidate that spans multiple files?** Don't fix site-by-site silently. Surface it as a sweep candidate per the skill's `Systematic Sweep Discipline` section and let the user decide whether to land a config-level change.
+
+### Touching Rust / backend / type-level / cross-layer code
+
+Before finalizing a refactor plan, walk the 10-layer `architecture-audit` checklist (or at least the layers the change clearly touches). State which layers you covered and which you intentionally skipped.
+
+### Touching both
+
+Don't merge the reports. Two skills, two reports. Cross-reference in the delivery message if relevant.
+
+---
+
+## What This File Does NOT Do
+
+- It does **not** force every PR to produce an audit report. Single bug fixes, copy tweaks, hotfix patches → just ship.
+- It does **not** override the skills' own `When NOT To Use` rules.
+- It does **not** replace `.cursor/rules/ui-feature-workflow.mdc` for human/Cursor flow (unit tests + TEST_CASES.md + acceptance criteria). Those gates are about delivery quality; this routing is about which methodology to apply.
+- It does **not** mandate any commit-message format (commitlint handles that), any lint rule, or any pre-commit hook. Audit reports are docs, not gates.
+- It does **not** lock in skill content. If `~/.orgii/skills/*/SKILL.md` updates, this file's routing still applies — read the current SKILL.md, not your memory of it.
+
+---
+
+## Audit Report Conventions
+
+- **Location:** `docs/-YYYY-MM-DD/.md` (one date-stamped folder per audit batch, one file per audited component).
+- **Format:** follow the `## Output Format` section in the relevant skill verbatim — tables with Line / Element / Verdict / Reason / Suggested change columns.
+- **`keep with reason` rows MUST fill the Reason column.** That's the audit's value-add — preventing the next pass from re-flagging the same hit.
+- **Don't modify source code in an audit-only PR.** Audit and fix are separate concerns; mixing them makes review impossible.
+
+---
+
+## When You're Unsure
+
+- If you don't know which skill applies, **lean toward running `frontend-ui-audit` for UI changes and `architecture-audit` for type/control-flow changes**. Both being run when only one was needed costs nothing; missing one is a real gap.
+- If you're certain the user wants direct implementation and not an audit (e.g. "just fix this bug"), do that — don't insert an audit pass unprompted.
+- If the user asks "why didn't audit catch X?", check whether X is in scope for the skill they're invoking before assuming the audit failed. (`architecture-audit` is type/architecture, not UI consistency — see `frontend-ui-audit` for the latter.)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index c79e0064e..4f3b23b3a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -2,83 +2,56 @@
Thank you for helping improve ORGII. This project is a Tauri v2 desktop app built with React, TypeScript, webpack, and Rust.
-## Code of conduct
+## Quick path
-Be respectful, constructive, and specific. Assume good intent, but do not tolerate harassment, discrimination, or personal attacks. Keep issue and pull request discussions focused on the work.
+1. Search existing issues and pull requests first.
+2. For larger changes, open an issue or discussion before implementing.
+3. Set up the app, make a small focused change, and run the checks that match your files.
+4. Open a pull request with a clear summary, test plan, and passing CLA check.
-## Before you start
-
-- Search existing issues and pull requests before opening a new one.
-- For larger changes, open an issue or discussion first so maintainers can confirm the direction.
-- Keep pull requests small and focused. One fix or feature per PR is easiest to review.
-- Do not include secrets, private credentials, proprietary data, generated artifacts, or unrelated formatting changes.
-- If you use AI assistance, make sure a human actively participates in the design and implementation process and reviews the contribution before submission.
+Keep secrets, private data, generated artifacts, and unrelated formatting changes out of your PR. If you use AI assistance, a human must actively review the design and implementation before submission.
## Development setup
-Prerequisites:
+Install the required tools:
-| Tool | Required version | Install |
-| ------------------------------------ | ---------------------- | ------------------------------------------------------------------------------------------ |
-| [Node.js](https://nodejs.org/) | 20 or current LTS | `nvm install --lts` or download from nodejs.org |
-| [pnpm](https://pnpm.io/installation) | 9.15 | `npm install -g pnpm@9.15` |
-| [Rust toolchain](https://rustup.rs/) | 1.85.0 or later (MSRV) | `rustup toolchain install stable` |
-| Tauri system dependencies | — | Follow the [Tauri prerequisites guide](https://tauri.app/start/prerequisites/) for your OS |
-| [Python 3](https://www.python.org/) | any 3.x | Only needed for optional asset download scripts |
+| Tool | Version | Notes |
+| ------------------------------------ | ----------------- | ------------------------------------------------------------------------------------------- |
+| [Node.js](https://nodejs.org/) | 20 or current LTS | Use `nvm install --lts` or the Node.js installer. |
+| [pnpm](https://pnpm.io/installation) | 9.15 | Install with `npm install -g pnpm@9.15`. |
+| [Rust toolchain](https://rustup.rs/) | 1.85.0 or later | Use `rustup toolchain install stable`. |
+| Tauri system dependencies | Tauri v2 | Follow the [Tauri prerequisites guide](https://tauri.app/start/prerequisites/) for your OS. |
+| [Python 3](https://www.python.org/) | Any 3.x | Only needed for optional asset download scripts. |
From the repository root:
```bash
pnpm install
-```
-
-Copy `.env.example` to `.env` only when you need local configuration. `.env` is gitignored; never commit real secrets.
-
-Run the full desktop app:
-
-```bash
pnpm run tauri:dev
```
-Tauri starts the webpack dev server through its `beforeDevCommand`; contributors should use the Tauri scripts rather than launching the frontend shell independently.
-
-For fast desktop iteration against a built app bundle, use:
+Tauri starts the webpack dev server through its `beforeDevCommand`, so use the Tauri scripts for normal desktop development.
-```bash
-pnpm run tauri:build:fast
-```
-
-This is the fast iteration mode for validating local Tauri changes outside the dev server: it cleans only the app target for the local development profile, rebuilds the app bundle, and opens it immediately.
-
-## Useful checks
-
-Run the checks that match the files you changed before opening a PR.
+Copy `.env.example` to `.env` only when you need local configuration. `.env` is gitignored; never commit real secrets.
-Frontend:
+For fast desktop iteration against a built app bundle:
```bash
-pnpm run lint
-pnpm run test
-pnpm run check:circular
+pnpm run tauri:build:fast
```
-Rust/Tauri:
-
-```bash
-pnpm run cargo:check
-pnpm run cargo:clippy
-pnpm run cargo:test
-```
+## Run the right checks
-Targeted Rust module tests are available, for example:
+Run the checks that match the files you changed. If you cannot run a relevant check locally, explain why in the PR and include any partial verification you performed.
-```bash
-pnpm run cargo:test:agent_core
-pnpm run cargo:test:event_store
-pnpm run cargo:test:work_station
-```
+| Change area | Recommended checks |
+| ----------------------------- | ---------------------------------------------------------------------------------------------------------- |
+| Frontend / TypeScript | `pnpm run lint`, `pnpm run test`, `pnpm run check:circular` |
+| Rust / Tauri | `pnpm run cargo:check`, `pnpm run cargo:clippy`, `pnpm run cargo:test` |
+| Targeted Rust modules | `pnpm run cargo:test:agent_core`, `pnpm run cargo:test:event_store`, or `pnpm run cargo:test:work_station` |
+| Chat, session, or UI behavior | E2E tests in `tests/e2e`; see `tests/e2e/README.md` |
-Core UI end-to-end tests live in `tests/e2e` and use WebDriverIO with `tauri-webdriver-automation`. Run them after UI changes that can affect chat or session behavior:
+Core UI end-to-end tests use WebDriverIO with `tauri-webdriver-automation`:
```bash
cargo install tauri-webdriver-automation --locked
@@ -87,110 +60,78 @@ pnpm install
pnpm test
```
-See `tests/e2e/README.md` for account setup, isolated service runs, targeted specs, and scenario filters.
-
-If a check cannot be run locally, explain why in the pull request and include any partial verification you performed.
-
-## Project structure
-
-- `src/` — React, TypeScript, UI, stores, hooks, and frontend services
-- `src-tauri/` — Tauri shell and Rust backend
-- `docs/` — living architecture and feature documentation
-- `scripts/` — development, setup, maintenance, and build scripts
-- `tests/` — repository-level tests and test helpers
+## Project map
-Documentation is organized by domain under `docs/`. New or substantially changed features should include documentation when the behavior, architecture, or operating model is not obvious from the code. Use lowercase domain folders such as `docs/architecture/`, `docs/shared/`, `docs/workstation/`, and `docs/contributing/`; use `{subject}--MMDD.md` for date-stamped domain docs.
+| Path | Purpose |
+| ------------ | ----------------------------------------------------------- |
+| `src/` | React, TypeScript, UI, stores, hooks, and frontend services |
+| `src-tauri/` | Tauri shell and Rust backend |
+| `docs/` | Living architecture and feature documentation |
+| `scripts/` | Development, setup, maintenance, and build scripts |
+| `tests/` | Repository-level tests and test helpers |
-## Coding guidelines
+Add or update docs when behavior, architecture, setup, or user-visible behavior changes in a way that is not obvious from the code.
-Follow the repository rule files in `.cursor/rules/`, especially:
+## Coding expectations
-- `.cursor/rules/orgii-frontend.mdc` for frontend architecture, React, TypeScript, styling, state, i18n, and UI conventions.
-- `.cursor/rules/frontend-backend-alignment.mdc` for contracts between TypeScript and Tauri/Rust.
-- `.cursor/rules/rust-resource-lifecycle.mdc` and `.cursor/rules/cargo-cleanup.mdc` for Rust backend work.
-- The focused MDC files for terminology, tooltips, session rendering, and layout debugging when touching those areas.
+Use the repository rules in `.cursor/rules/` as the source of truth. The most common expectations are:
-In general, keep changes focused, use existing shared components and tokens, prefer typed values over hardcoded domain strings, propagate errors instead of silently swallowing them, update all supported locales for user-facing text, and remove dead code immediately.
+- Keep changes focused and remove dead code immediately.
+- Use existing shared components, hooks, stores, and design tokens.
+- Prefer typed constants and enums over hardcoded domain strings.
+- Let errors propagate instead of silently returning empty fallback data.
+- Update all supported locales when changing user-facing UI text.
+- Follow frontend/backend contract rules when a setting, command, or wire type crosses the TypeScript and Rust boundary.
-## Tests
+For deeper guidance, read:
-Add or update tests when changing behavior. Prefer focused tests that cover the changed module or component. For bug fixes, include a regression test when practical.
+- `.cursor/rules/orgii-frontend.mdc`
+- `.cursor/rules/frontend-backend-alignment.mdc`
+- `.cursor/rules/rust-resource-lifecycle.mdc`
+- `.cursor/rules/cargo-cleanup.mdc`
-## Commit and pull request format
+## Commits and pull requests
-Commit messages and pull request titles must use scoped Conventional Commits:
+Commit messages and PR titles must use scoped Conventional Commits:
```text
feat(scope): short imperative summary
fix(scope): short imperative summary
```
-Use the type that best matches the change. Common types include `feat`, `fix`, `chore`, `docs`, `style`, `test`, `refactor`, `perf`, `build`, `ci`, and `revert`. The scope should be lowercase kebab-case and should name the affected area, such as `git`, `settings`, `workstation`, `slash-menu`, or `contributing`.
+Use lowercase kebab-case scopes such as `git`, `settings`, `workstation`, `slash-menu`, or `contributing`. Common types include `feat`, `fix`, `chore`, `docs`, `style`, `test`, `refactor`, `perf`, `build`, `ci`, and `revert`.
-Every commit must include a proper message body unless the change is truly trivial. The body should explain why the change exists, summarize the important behavior or architecture changes, and mention notable verification or migration details. Do not leave commits with only a subject line or only automated hook metadata.
+Every non-trivial commit needs a body that explains why the change exists, summarizes important behavior or architecture changes, and mentions notable verification or migration details.
-The pre-commit hook appends a tamper-evidence trailer of the form
-`Pre-commit hook ran. Total eslint: N, total circular: N` to every commit
-it processes. The trailer is intentional: commits without it were created
-with `--no-verify`, `HUSKY=0`, or another bypass, and reviewers should
-treat such commits with extra scrutiny. Do not strip the trailer when
-amending or rewording.
-
-Examples:
+A `commit-msg` hook runs commitlint, and the pre-commit hook appends a tamper-evidence trailer:
```text
-feat(git): add remote authentication prompt
-
-Add an inline authentication prompt for remote operations so push and
-pull failures can recover without sending users to a terminal. The
-prompt accepts temporary tokens or stores them through the Git helper
-when the user opts in.
-
-fix(settings): preserve Git fetch preference
-
-Keep the fetch preference in the Git integration settings instead of
-resetting it when other network settings are edited. This avoids losing
-the user's chosen sync behavior when unrelated fields change.
-
-chore(contributing): document commit format
-
-Clarify that commits need a scoped Conventional Commit subject and a
-body that explains intent, not just automated hook metadata.
+Pre-commit hook ran. Total eslint: N, total circular: N
```
-A `commit-msg` git hook runs [commitlint](https://commitlint.js.org/) with `@commitlint/config-conventional` and the project's `commitlint.config.cjs` on every commit. The hook rejects subjects that are missing a type, exceed 72 characters, end with a period, use uppercase types or scopes, or otherwise fail the rules. Fix the message and commit again rather than bypassing the hook.
+Do not remove this trailer, and do not bypass hooks with `--no-verify`, `HUSKY=0`, or similar unless a maintainer explicitly asks you to do so for emergency recovery. If a hook fails, fix the issue and rerun the command normally.
+
+## Pull request checklist
-Pull request descriptions must include a clear summary and a test plan. If checks were not run, explain why and list any partial verification performed.
+Before requesting review, make sure the PR has:
-Do not skip pre-commit, pre-push, lint-staged, or other repository hooks. Do not use `--no-verify`, `HUSKY=0`, or equivalent bypasses unless a maintainer explicitly asks you to do so for an emergency recovery task. If a hook fails, fix the underlying issue and rerun the command normally.
+- A clear title, description, and test plan.
+- One focused issue, feature, or fix.
+- Relevant checks passing, or a note explaining what could not be run.
+- CLA Assistant passing, plus screenshots or docs when the change needs them.
-If you use a coding agent, the agent must read and follow this section before creating commits or pull requests. Agent-generated commits and PRs must use the same format, run the same checks, and must not bypass hooks.
+Also confirm that no secrets, local configuration, generated build output, or unrelated files are included.
## Contributor License Agreement
ORGII requires contributors to sign the repository Contributor License Agreement before a pull request can be merged. The agreement text is in [`docs/contributing/CLA.md`](docs/contributing/CLA.md).
-The repository uses GitHub CLA Assistant to collect signatures and report CLA status on pull requests. When you open your first PR, CLA Assistant will comment with a signing link if your GitHub account has not signed the current agreement.
+GitHub CLA Assistant will comment with a signing link on your first PR if your GitHub account has not signed the current agreement.
-Choose the signing path that matches your contribution:
+- Sign as an individual when the contribution is your own work and you are legally allowed to submit it.
+- Sign as a company only if you are authorized to bind that organization to the CLA.
-- **Individual contributor:** sign as yourself when the contribution is your own work and you are legally allowed to submit it.
-- **Corporate contributor:** sign on behalf of your employer or organization only if you are authorized to bind that entity to the CLA. If you are not authorized, sign only as an individual and submit only work you are permitted to contribute individually.
-
-Maintainers will not merge PRs until the CLA check passes. Corporate signatures may require additional maintainer review if the signing authority is unclear.
-
-## Pull request checklist
-
-Before requesting review, confirm that:
-
-- The PR has a clear title and description.
-- The change is scoped to one issue, feature, or fix.
-- The CLA Assistant check passes, or you have asked maintainers for help resolving the signature status.
-- Relevant lint, test, and cargo commands pass or are documented as not run.
-- UI changes include screenshots or screen recordings when useful.
-- New UI text has translations for all supported locales.
-- Documentation was added or updated when the change affects architecture, setup, or user-visible behavior.
-- No secrets, local configuration, generated build output, or unrelated files are included.
+Maintainers will not merge PRs until the CLA check passes.
## Security
@@ -198,6 +139,8 @@ Do not report security vulnerabilities in public issues. Use the repository's pr
Never commit API keys, signing keys, OAuth secrets, personal tokens, private logs, or user data. If you accidentally expose a secret, revoke it immediately and notify maintainers.
-## License
+## Code of conduct and license
+
+Be respectful, constructive, and specific. Keep issue and pull request discussions focused on the work.
-By contributing, you agree that your contributions are licensed under the repository license: AGPL-3.0-or-later and are submitted under the Contributor License Agreement in `docs/contributing/CLA.md`. See `LICENSE` for the full license text.
+By contributing, you agree that your contributions are licensed under AGPL-3.0-or-later and submitted under the Contributor License Agreement in `docs/contributing/CLA.md`. See `LICENSE` for the full license text.
diff --git a/README.md b/README.md
index 0016e3769..198119275 100644
--- a/README.md
+++ b/README.md
@@ -1,16 +1,6 @@
ORG-2
Open-source Cursor-style agent IDE — but built for reviewability, traceability, and creative freedom, not just faster coding.
@@ -41,9 +35,97 @@ It is not just another AI coding tool; it is an experiment in human/agent organi
ORG-II explores a different model: agents as persistent, observable colleagues inside a structured organization. Instead of stateless, hard-to-review AI IDE sessions, it introduces replayable agent execution, cross-session memory, AI blame, and a local-first Rust-based runtime so humans, agents, and teams can collaborate around shared context and aligned goals.
-## Key capabilities
+## Features
+
+
+
+
+
+### Manage sessions across 10+ apps & CLIs
+
+Load and manage agent sessions from all your tools in one place. Scan history, inspect subagents, and control each source without switching apps.
+
+
+
+
+
+
+
+
+
+### Review trajectories, not just PRs
+
+Form your team and share sessions across devices and teammates. Review the full agent trajectory, not only the resulting diff, and leave comments in context.
+
+
+
+
+
+
+
+
+
+### Tool calls, now as videos
+
+Replay the work as it happened. Messages, tool calls, file edits, and command output stay synchronized in one reviewable timeline.
+
+
+
+
+
+
+
+
+
+### AI blame, not just Git blame
+
+Do not stop at who changed a line. Trace it back to the agent sessions, tool calls, and decisions that drove the change.
+
+
+
+
+
+
+
+
+
+### Stay on track
+
+See how your time is spent across tasks and agent sessions. A daily activity timeline keeps duration, code changes, and priorities visible.
+
+
+
+
+
+
+
+
+
+### Full dev workspace
+
+Use the terminal, manage source control, trace Git history, and review pull requests without leaving your agent workspace.
+
+
+
+
+
+
+
+
+
+### Design Mode
+
+Inspect live pages in the native WebKit browser. Select an element and send its exact page context straight to the agent for a straightforward fix.
+
+
+
+
+
+
+
+
+## More capabilities
-- Long-running sessions with replayable execution traces for auditing, review, and debugging.
- Rust-based agents that work with your existing API keys and agent subscriptions.
- GUI, CLI, terminal, Git, browser, LSP, timeline, and database tooling.
- Cross-session memory, cross-agent knowledge sharing, and shared workspace state.
@@ -53,15 +135,50 @@ ORG-II explores a different model: agents as persistent, observable colleagues i
- Org-level alignment surfaces (issues/projects management) for coordinating humans, agents, goals, and accountability (WIP).
- Session collaboration and group issue workflows via self-hosted Supabase (WIP).
+## Supported CLI agents
+
+ORG-II includes built-in launch profiles for these coding agents, managed from the desktop TUI.
+
+
+
## Download
-Current build version: v1.1.3 (2026-06-25)
+Current build version: v1.1.24 (2026-07-16)
Download the latest ORGII desktop app with one click:
- [macOS Apple Silicon](https://github.com/yorgai/ORG2/releases/latest/download/ORG2-latest-mac-apple-silicon.dmg)
- [Windows x64 installer](https://github.com/yorgai/ORG2/releases/latest/download/ORG2-latest-windows-x64-setup.exe)
- [Windows x64 MSI](https://github.com/yorgai/ORG2/releases/latest/download/ORG2-latest-windows-x64.msi)
+- [Linux x64 AppImage](https://github.com/yorgai/ORG2/releases/latest/download/ORG2-latest-linux-x64.AppImage)
+- [Linux x64 DEB](https://github.com/yorgai/ORG2/releases/latest/download/ORG2-latest-linux-x64.deb)
- [All latest release assets](https://github.com/yorgai/ORG2/releases/latest)
The direct download links always resolve through GitHub's latest release pointer.
diff --git a/docs/architecture-audit-2026-07-08/source-control-repo-scope.md b/docs/architecture-audit-2026-07-08/source-control-repo-scope.md
new file mode 100644
index 000000000..15f4a93f6
--- /dev/null
+++ b/docs/architecture-audit-2026-07-08/source-control-repo-scope.md
@@ -0,0 +1,36 @@
+# Architecture Audit — Source Control Repo Scope
+
+**Date:** 2026-07-08
+**Scope:** Source Control issue/PR workstation atoms, sidebar/main-pane readers, issue detail tabs, ADE context collection.
+
+## Layers Covered
+
+| Layer | Verdict | Notes |
+| ------------------------------- | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
+| 1 Compilation correctness | pending verification | `npm run typecheck` and targeted lint are run after this report is staged. |
+| 2 Dead code / structural dedupe | pass with fix | Swept legacy global atom reads. Production readers now use `atomFamily`; legacy exports remain only as default-scope compatibility aliases. |
+| 3 Naming consistency | pass | New `workstationRepoScopeKey` names the repo/path fallback explicitly. |
+| 4 Semantic overloading | pass | "scope" is limited to workstation repo state, not UI filter scope. |
+| 5 Default branch analysis | pass with note | Fallback is `repo:` -> `path:` -> `default`; API calls keep their existing `"default"` repo id fallback separately. |
+| 6 Cross-domain leakage | pass | Repo-scoped state remains in workstation code-editor atoms; ADE collector only reads the active workspace scope. |
+| 7 New developer confusion | pass with fix | Default-scope legacy exports now alias the corresponding family atom instead of being independent atoms. |
+| 8 Wire protocol | not applicable | No serialized payload shape changed; repo id/path request arguments are preserved. |
+| 9 Init parity | pass | Sidebar, main pane, issue detail tab, Manage Issues handoff, and ADE collector all derive a scope key before reading/writing issue/PR state. |
+| 10 Resolver symmetry | pass with fix | Issue and PR state families use the same repo id/path fallback chain, and each family instance receives its own initial object. |
+
+## Sweeps
+
+| Sweep | Result |
+| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| Legacy global atom reads | No production reads remain outside compatibility exports and docs/test notes. |
+| Scope key propagation | `useWorkstationPr`, `useWorkstationIssues`, Source Control sidebar/main pane, issue detail tab, Manage Issues handoff, and ADE context collector all derive scoped atoms. |
+| API repo id fallback | Preserved existing `repoId ?? "default"` for backend calls while state scoping can still fall back to repo path. |
+
+## Fixes Landed From Audit
+
+- Converted legacy PR/issue callback/list exports to default-scope family aliases for consistent compatibility behavior.
+- Cloned family initial objects/arrays per scope to avoid shared initial references.
+
+## Residual Notes
+
+- `workstationSelectedPrAtom` still has a TODO referencing future atom-family migration, but it is a separate selected-PR detail surface and not part of this issue/PR list-state split.
diff --git a/docs/architecture-audit-2026-07-11/AgentToolsStrictSchemaNullable.md b/docs/architecture-audit-2026-07-11/AgentToolsStrictSchemaNullable.md
new file mode 100644
index 000000000..ce61dffe1
--- /dev/null
+++ b/docs/architecture-audit-2026-07-11/AgentToolsStrictSchemaNullable.md
@@ -0,0 +1,150 @@
+# Architecture Audit — agent tools strict-schema optional fields and todo snapshot deduplication
+
+**Date:** 2026-07-11
+**Auditor:** orgii session
+**Skill:** `.orgii/skills/architecture-audit/SKILL.md`
+**Scope (changed files):**
+
+- `src-tauri/crates/agent-core/src/core/providers/responses_common/types.rs`
+- `src-tauri/crates/agent-core/src/core/tools/registry.rs`
+- `src-tauri/crates/agent-core/src/core/tools/impls/coding/code_search.rs`
+- `src-tauri/crates/agent-core/src/core/tools/impls/coding/manage_todo.rs`
+- `src-tauri/crates/agent-core/src/core/tools/tests/registry_tests.rs`
+- `src-tauri/crates/agent-core/src/core/tools/tests/search_tool_tests.rs`
+- `src/engines/ChatPanel/ChatHistory/chatItemPipeline/pipeline.ts`
+- `src/engines/ChatPanel/ChatHistory/chatItemPipeline/__tests__/pipeline.test.ts`
+
+## What the change does (one theme)
+
+All eight files implement one strict-schema handling path plus its todo UI
+projection: preserve optional tool arguments when the OpenAI Responses API uses
+`strict: true`, then render only the latest snapshot in a consecutive run of
+todo updates.
+
+The Responses strict contract requires every property to be listed in `required`
+and to appear in the model's output. Previously, source-schema _optional_ fields
+either broke strict validation or were dropped. The change threads one invariant
+end-to-end:
+
+1. **Outbound schema (wire, `types.rs`):** `enforce_strict_schema` now, for every
+ property **not** in the source `required` set, calls the new
+ `make_schema_nullable` to rewrite it as `anyOf: [, {type: null}]`,
+ then marks all properties required. Optional → "required but nullable".
+2. **Inbound params (`registry.rs`):** `ToolRegistry::execute` now runs the new
+ `strip_optional_null_placeholders` over the model's arguments before invoking
+ the tool. For properties that were originally optional and are **not**
+ natively nullable, a literal `null` is deleted, restoring the "field omitted"
+ shape the tools expect. Recurses into nested objects and array items.
+3. **Tool-level validation (`code_search.rs`, `manage_todo.rs`):** nullable
+ optional search scope is treated as absent; nullable todo update fields mean
+ "leave unchanged". Empty titles, malformed strings, and invalid `blockedBy`
+ arrays are rejected instead of silently erasing or weakening a patch.
+4. **Frontend projection (`pipeline.ts`):** consecutive `manage_todo` events are
+ complete snapshots, so only the latest card is retained. A real intervening
+ activity remains a history boundary.
+5. **Tests:** cover schema nullability, registry cleanup, search scope fallback,
+ todo update parsing, status-count reconciliation, and snapshot boundaries.
+
+## Layers covered
+
+| Layer | Covered | Verdict |
+| --------------------------------- | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| 1 — Compilation correctness | yes | Deferred to quality gate (`cargo check` / `cargo test -p agent-core`). |
+| 2 — Dead code & dedup | yes | Removed the now test-only optional-content helper; one cross-module schema helper duplication remains intentionally local. |
+| 3 — Naming consistency | yes | Clean. |
+| 4 — Semantic overloading | yes | `required`/`nullable`/`optional` used consistently. |
+| 5 — Default branch analysis | yes | `_ => false` branches are correct. |
+| 8 — Wire protocol & serialization | yes (**primary**) | Round-trip parity holds; see analysis. |
+| 9 — Init/entry-point parity | yes | Production model-driven calls funnel through `ToolRegistry::execute`; direct debug/test calls do not consume provider placeholders. |
+| 6, 7, 10 | partially / n/a | Layer 6 (cross-domain leakage) n/a — code is generic schema plumbing; Layer 7 (new-dev clarity) good — doc comments explain intent; Layer 10 (resolver symmetry) n/a — no multi-field resolver. |
+
+## Layer 8 — Wire Protocol & Serialization (primary)
+
+This is exactly the layer this change lives in: it alters the JSON schema sent to
+the provider and the JSON params received back.
+
+**Round-trip parity invariant (verified by reading both sides):**
+
+- The outbound transform (`make_schema_nullable`) makes a field nullable **iff**
+ it is not in the source `required` set.
+- The inbound strip (`strip_optional_null_placeholders`) deletes a `null` **iff**
+ the field is not in the source `required` set **and** the property schema does
+ not natively accept null.
+- Both sides key off the **same source `required` set**, so the set of fields
+ that can carry a `null` placeholder outbound is exactly the set stripped
+ inbound. This symmetry is the correctness core — documented here so a future
+ edit to one side without the other is caught.
+
+**Critical detail — the strip reads the pre-strict schema.** `ToolRegistry::execute`
+passes `&tool.parameters()` (the original, non-strict schema) to the strip, not
+the `enforce_strict_schema`-mutated copy. That is correct: `enforce_strict_schema`
+is applied on a _clone_ at the provider boundary (`converter.rs:266`), never
+mutating `tool.parameters()`. So `originally_required` inside the strip genuinely
+reflects the source optionality. **Fragility note:** if someone ever makes
+`enforce_strict_schema` mutate the registered schema in place, the strip's
+`originally_required` would become the strict (all-required) set and it would stop
+stripping. The call site continues to pass a freshly generated source schema.
+
+**Explicitly-nullable fields are preserved:** `strip_optional_null_placeholders`
+uses `schema_accepts_null` so a field whose source schema is `anyOf:[T, null]`
+keeps its `null` value (the test `strict_schema_null_placeholders_restore_optional_omissions`
+asserts `explicit_null` survives while `repo_paths` is dropped). Good — "omitted"
+and "intentionally null" stay distinct.
+
+**Provider-agnostic strip:** the strip runs in `ToolRegistry::execute` for all
+providers, but only strict Responses/Codex providers generate the null
+placeholders. For non-strict providers there are no such placeholders, so the
+strip is a no-op in practice (and harmless if a model ever emitted a stray null
+for an optional non-nullable field). Acceptable.
+
+## Layer 9 — Entry-Point Parity
+
+Tool execution entry points:
+
+| Entry point | Applies `strip_optional_null_placeholders`? | Verdict |
+| ------------------------------------------------------------------------ | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
+| `ToolRegistry::execute` (production) | yes | correct |
+| `ToolRegistry::execute_action` → `self.execute` | yes (delegates) | correct |
+| `state/commands/session/debug/org_runtime.rs` direct `tool.execute(...)` | no (bypasses registry) | acceptable — debug endpoint constructs params in-code, not from a model, so no null placeholders arise. Flagged for awareness. |
+| test helpers calling `tool.execute(...)` directly | no | expected — tests exercise tool logic directly. |
+
+No production path constructs tool params from a model response and bypasses the
+registry strip. Parity holds for the model-driven path.
+
+## Layer 2 — Dead Code & Duplication
+
+- `make_schema_nullable` (types.rs) contains an `already_nullable` check that is
+ logically identical to `schema_accepts_null` (registry.rs): both answer "does
+ this schema permit `null`?" by inspecting `type` (string/array) and
+ `anyOf`/`oneOf` variants.
+ **Verdict: keep with reason (low-priority dedup candidate).** The two live in
+ different modules (`providers::responses_common` vs `tools`) with no existing
+ shared util between them; extracting a helper would create a new cross-module
+ dependency for ~10 lines. Note it so the next pass can promote it if a third
+ copy appears.
+- `optional_string_param` and `optional_index_array` centralize nullable update
+ parsing. The old `sanitize_optional_todo_content` helper was live only through
+ its own test after the extraction, so it was deleted.
+
+## Layer 5 — Default Branch Analysis
+
+- `schema_accepts_null` / `make_schema_nullable`: `match kind { String, Array, _ => false }`
+ — the `_` correctly covers non-type-descriptor JSON values (a schema `type`
+ that is neither a string nor an array of strings cannot declare null). Safe.
+- `optional_string_param`: absent and `null` explicitly mean "leave untouched";
+ strings are returned for field-specific validation; every other JSON type is
+ rejected. Correct and intentional.
+
+## Summary
+
+- **1 coherent strict-schema/todo feature** across 8 files; correctly split into
+ outbound schema, inbound cleanup, tool validation, and frontend projection
+ layers.
+- **Round-trip parity verified** — outbound nullable-set and inbound strip-set are
+ keyed off the same source `required` set.
+- **0 open fix candidates** after the focused cleanup.
+- **1 keep-with-reason flag:**
+ 1. `make_schema_nullable`'s `already_nullable` vs `schema_accepts_null`
+ duplication — low-priority dedup candidate, cross-module.
+- **1 parity note:** debug/test direct tool calls bypass the registry strip;
+ acceptable because they do not consume provider-generated params.
diff --git a/docs/architecture-audit-2026-07-11/WorktreeResolvePrBase.md b/docs/architecture-audit-2026-07-11/WorktreeResolvePrBase.md
new file mode 100644
index 000000000..4ec7cebc9
--- /dev/null
+++ b/docs/architecture-audit-2026-07-11/WorktreeResolvePrBase.md
@@ -0,0 +1,118 @@
+# Architecture Audit — `worktree_resolve_pr_base` (PR → git-resolvable base ref)
+
+**Date:** 2026-07-11
+**Scope:** New backend command `git::pr_base::worktree_resolve_pr_base` + its
+cross-layer wire contract to the frontend (`resolvePrWorktreeBase`,
+`WorktreeLaunchSource.resolvedBaseRef`, `getWorktreeFields`).
+**Auditor:** worktree PR-base resolution session.
+
+This change adds a Rust command + TS wiring. Per the routing rule, the layers
+that the change clearly touches are audited; layers with no surface are marked
+**skipped** with a one-line reason.
+
+---
+
+## Layer 1 — Compilation Correctness
+
+- `cargo test -p git pr_base` → compiles, **13/13 tests pass**.
+- `cargo check -p org2` (full app crate, exercises `generate_handler!` registration
+ of the new command) → **Finished, no errors**.
+- `pnpm typecheck` (`tsc --noEmit`) → **exit 0**.
+- No new clippy-visible patterns introduced (subprocess + thread drain mirror the
+ existing `bundle.rs` idiom). **Pass.**
+
+## Layer 2 — Dead Code & Structural Deduplication
+
+- Traced from the business entry point: FE `WorktreeSourceModal.handleConfirm`
+ → `resolvePrWorktreeBase` (`invoke("worktree_resolve_pr_base")`) → Rust
+ `worktree_resolve_pr_base` → `resolve_pr_base` → `resolve_pr_base_with` (pure)
+ → real git runner. Every new symbol is on a live path.
+- `resolve_pr_base_with` is generic over a runner so the pure logic is shared by
+ both the real command **and** the unit tests — no parallel test-only reimplementation.
+- Did **not** duplicate `git worktree` / fetch logic that already exists: the
+ resolver only fetches + rev-parses; worktree creation stays in the existing
+ `create_session_worktree` path (base ref forwarded via the existing `branch`
+ field). **Pass.**
+
+## Layer 3 — Naming Consistency
+
+- Command name `worktree_resolve_pr_base` matches the `git::*` snake_case Tauri
+ convention (`get_local_head_sha`, `merge_cloud_ref`); FE wrapper
+ `resolvePrWorktreeBase` matches the FE camelCase convention.
+- `PrBaseResolution` / `PrBaseSource` serialize camelCase (`baseRef`, `headSha`,
+ `branchNameOverride`, `compareBaseRef`, `source: "branch"|"pullRef"`) and the TS
+ `interface PrBaseResolution` mirrors it field-for-field. **Pass.**
+
+## Layer 4 — Semantic Overloading
+
+- `branch`: the launch payload field `SessionLaunchParams.branch` already means
+ "isolate base commit-ish" for worktree launches (documented in
+ `getWorktreeFields`). This change keeps that meaning — `resolvedBaseRef` (a SHA)
+ is fed through the same field. No new overload.
+- `baseBranch` vs `baseRef` vs `resolvedBaseRef` vs `compareBaseRef` are
+ deliberately distinct: `baseBranch` = human label / PR head branch name;
+ `resolvedBaseRef`/`baseRef` = concrete fetched head SHA (git-usable);
+ `compareBaseRef` = the diff-against ref (`refs/remotes//`). Each
+ documented at its definition. **Pass** (checked to avoid re-overloading "base").
+
+## Layer 5 — Default Branch Analysis
+
+- `normalize_remote`: `None`/blank → `origin`. Correct for all callers — the FE
+ only ever lists PRs from the `origin` GitHub remote, so `origin` is the right
+ default; an explicit remote is still honored.
+- `resolve_pr_base_with` fallback branch: branch-fetch failure only falls through
+ to `refs/pull//head` when `is_missing_remote_ref_error` matches; **any other
+ failure (auth/network) surfaces** rather than silently hitting the fallback
+ (test `surfaces_non_missing_ref_fetch_failure_without_fallback`). This is the
+ key default-safety property. **Pass.**
+
+## Layer 8 — Wire Protocol & Serialization Audit
+
+- Inspected the actual serialized contract, not just the structs:
+ - **Rust → FE (result):** `#[serde(rename_all = "camelCase")]` on
+ `PrBaseResolution` + `PrBaseSource`. Enum serializes as `"branch"` / `"pullRef"`
+ (unit variants → plain strings), matching the TS `type PrBaseSource =
+"branch" | "pullRef"`. `Option` → `string | null` (matches TS).
+ - **FE → Rust (args):** command uses `rename_all = "camelCase"`, so FE passes
+ `repoPath`, `prNumber`, `remote`, `headBranch`, `baseBranch`. Optional args are
+ sent as `?? null`, which Tauri maps to `Option::None`. Verified against the
+ `#[tauri::command(rename_all = "camelCase")]` signature.
+- No schema generator involved (no `schemars`); payload is a small fixed struct —
+ no bloat risk. **Pass.**
+
+## Layer 9 — Init Parity Across Entry Points
+
+- Single production entry point (`worktree_resolve_pr_base` Tauri command); the
+ unit tests drive the same pure core (`resolve_pr_base_with`) through a mock
+ runner, so test and production share identical branch/fallback logic. The only
+ step the test path omits is real subprocess execution (`run_git_capture_with_timeout`),
+ which is I/O, not business logic — an intentional, documented seam. **Pass.**
+
+## Layer 10 — Resolver Symmetry
+
+- `resolve_pr_base_with` resolves one primary output (`base_ref` = head SHA) via a
+ two-tier source chain: (1) `fetch `, (2) fallback
+ `fetch refs/pull//head`. Both tiers converge on the _same_
+ `rev_parse_fetch_head` reader — symmetric extraction, no field skips a source.
+- `branch_name_override` is populated on both tiers when a head branch is known
+ (test asserts fork PRs still surface the head branch as a label). `compare_base_ref`
+ is derived identically regardless of which fetch tier won. No asymmetry. **Pass.**
+
+## Layers skipped (no surface)
+
+- **Layer 6 (cross-domain leakage):** the new module is a self-contained git leaf;
+ no shared/core module gained a PR-specific field.
+- **Layer 7 (new-dev confusion):** covered implicitly by Layer 3/4 naming review;
+ all new public items carry doc comments explaining intent + the orca alignment.
+
+---
+
+## Summary
+
+- **All audited layers pass** (1,2,3,4,5,8,9,10); 6 & 7 skipped with reason.
+- Key safety properties verified by tests: fork fallback only on missing-ref,
+ auth/network errors surface, empty rev-parse errors, blank head branch → pull ref.
+- Wire contract confirmed symmetric on both directions (camelCase, enum strings,
+ `Option`↔`null`).
+- No dead code, no duplication of existing worktree/fetch logic, no new semantic
+ overload of `branch` / `base`.
diff --git a/docs/architecture-audit-2026-07-12/AgentOrgRecovery.md b/docs/architecture-audit-2026-07-12/AgentOrgRecovery.md
new file mode 100644
index 000000000..5664504e8
--- /dev/null
+++ b/docs/architecture-audit-2026-07-12/AgentOrgRecovery.md
@@ -0,0 +1,444 @@
+# Agent Org Recovery 与一致性加固:最终代码审计报告
+
+> 审计日期:2026-07-13
+> 分支:`fix/issue-272-agent-org-recovery-invariants`
+> 基线:`develop` / `ea8516092`
+> 范围:GitHub issue #272、相邻的 Agent Org 状态一致性、恢复、调度、任务原子性、CLI 能力边界和对应 UI 入口
+> 结论:依赖确认门、显式 Coordinator 指派、Planner 审批、生命周期收尾和 Pause/Resume 已通过连续真人 dev 验收;最终提交仍以本次全量检查与 Husky hooks 的实际结果为准。
+
+## 一句话结论
+
+这次把 Agent Org 从“看见像卡住了就尝试叫人、甚至按时间抢任务”,改成了“先读数据库做无副作用诊断,再按持久预算、真实任务资格和 run 状态执行恢复”。Run、Session、Task、Inbox/Wake 四种状态不再互相冒充,重复 wake 会合并,terminal/paused run 不会被后台门铃复活,任务完成与 run 收尾也不会再互相穿透。真人运行暴露出的“蓝色运行态反复闪、成员成果没有可靠交给下游、coordinator 晚一拍仍认为 blocked、所有人完成后 run 不自动收尾”也已经按四批追加修复;随后又根据真实日志修掉了更上游的根因:普通 Coordinator 消息不再被误判成用户接管 worker。第二次 Breaking Bad 真人运行继续暴露出两个新根因:Coordinator 把有先后关系的 Implement/Review/Test 当成并行任务,以及 duplicate Wake 虽被 scheduler 合并、却在数据库留下永远 queued 的新 intent。第三次 The Boys 真人运行证明底层依赖调度正确,但 Coordinator 的文字说明写着“等写作和检查完成”,结构化 `dependency_task_ids` 却只填写 Planner;本轮因此新增无副作用的依赖确认门,漏列当前 open task 时先返回 guidance,只有补齐依赖或明确确认并行后才允许落库。
+
+审计过程中还修复了相邻尾部问题:顶层 `org2` 编译路径错误、主动 shutdown 成员可能被 watchdog 复活、失败恢复曾直接唤醒 peer、损坏的 recovery deadline 会永久压住重试、启动恢复只扫描 500 个 run、CLI 保存错误没带 member_id、重复 Wake intent 不收口、旧 in-flight intent 不自愈、已完成 Run 重启后仍被通用策略暂停,以及 debug restart 与生产启动步骤漂移。最终设计进一步删除了失败后的 Worker 自动领取和自动 Wake,统一交回 Coordinator 明确指派。
+
+## 大白话:现在这套设计是什么
+
+| 代码名词 | 大白话 | 数据库里的真相 |
+| --------------- | ---------------------------------- | ---------------------------------------------- |
+| `AgentOrgRun` | 一次团队项目 | `agent_org_runs` |
+| `Session` | 某个成员当前是否在干活 | `agent_sessions` / 历史 CLI 的 `code_sessions` |
+| `Task` | 看板工单,记录归属、依赖和完成状态 | `agent_org_tasks` |
+| `AgentInbox` | 不会因为进程重启而丢失的信箱 | `agent_inbox` |
+| Wake | 门铃,只负责让成员起来读信/接活 | scheduler 中带幂等 key 的 turn |
+| Watchdog | 每分钟巡查的保安 | 纯分析 + 恢复执行器 |
+| Recovery budget | 保安的持久重试登记簿 | `agent_org_recovery_attempts` |
+
+```mermaid
+flowchart TD
+ DB["持久化快照 Run + Session + Task + Inbox + Budget"]
+ A["Recovery Analyzer 只读取,不写库、不 wake"]
+ P["Recovery Plan 同一轮可 wake、repair、reconcile"]
+ E["Recovery Executor 动作前重新检查 Run"]
+ W["Wake Dispatcher 确定性 key 合并重复门铃"]
+ C["Coordinator Notice 稳定指纹 + 1/5/15 分钟退避"]
+ F["Run Reconciler writer lock + Immediate transaction"]
+ N["No-op 暂停、终态、重复或工作已消失"]
+
+ DB --> A --> P --> E
+ E --> W
+ E --> C
+ E --> F
+ W -->|"Run 仍 Running 且有真实输入"| DB
+ W -->|"Paused / terminal / coalesced / no work"| N
+```
+
+核心原则是:
+
+1. `Idle session` 只表示员工此刻没执行 turn,不等于项目结束。
+2. `Running run` 只表示项目允许继续,不等于每个成员都健康。
+3. `Pending task` 不等于任何人都能接;依赖、eligibility、现有 owner 工作量都要满足。
+4. `Unread inbox` 是持久事实;wake 只是门铃,排队失败也不能把信当成已送达。
+5. 任何 task mutation 和 run finality 都要经过同一 writer lock,不能出现 terminal run 下面又长出 open task。
+
+## 真人运行诊断后的四批追加修复
+
+这四批不是换一套 Agent Org,也不是又加了两个 AI。它们是在现有 coordinator、member、task、inbox 之下补齐“门铃、交接单、完工回执、项目验收”四个机械环节。
+
+```mermaid
+flowchart LR
+ A["第 1 批:门铃防抖 Wake 不再自我循环"]
+ B["第 2 批:成果交接单 TaskOutput 持久保存"]
+ C["第 3 批:完工回执 TaskCompleted 通知 coordinator"]
+ D["第 4 批:项目验收 Finality 自动且原子"]
+
+ A --> B --> C --> D
+```
+
+### 第 1 批:切断 WakeNoop 自我循环
+
+真人测试时的“蓝色运行态闪一下、停一下、又闪一下”,根因不是模型在反复思考,而是后台门铃在一个特殊窗口里自我循环:用户正在直接和某个 member 对话时,`intervention` 会故意暂停它的组织信箱读取;旧 race guard 又看到信仍未读,于是再次 wake;这个空 turn 结束后仍未读,于是继续 wake。
+
+修复后:
+
+- `wake_one_member` 在排 scheduler 前检查 `AgentMemberInterventionStore`;直接用户对话期间返回 `DeferredIntervention`。
+- turn 结束后的 unread race guard 同时要求:run 仍为 Running、没有 intervention、确实有未读信。
+- 信仍保存在 `agent_inbox`,退出直接对话后再由正常路径读取,不会丢。
+
+```mermaid
+flowchart TD
+ U["用户正在直接和 Member 对话"] --> I["Intervention active"]
+ I --> W["后台发现未读信,申请 Wake"]
+ W --> D["DeferredIntervention"]
+ D --> K["信保留未读;不排空 turn;界面不闪"]
+ K --> R["Return to work 后正常 Wake + Drain"]
+```
+
+### 实机日志复核:把 Coordinator intervention 的源头一起修掉
+
+后来复查“龙之家族概括文”那次真实运行,确认闪烁时用户并没有继续给 Coordinator 发消息。准确时间线是:更早的一条普通 Coordinator 指令错误建立了 3 分钟 `intervention`;worker 随后给 Coordinator 写入未读信;旧系统一边禁止 intervention 中的 Coordinator 读信,一边又因未读信连续创建了 270 个顺序 Wake intent。也就是说,Wake 循环是后果,普通 Coordinator 消息被误分类才是更上游的根因。
+
+最终规则现在是:
+
+| 用户动作 | 系统含义 | 是否建立 intervention |
+| ------------------------------------------------------ | -------------------------------- | ----------------------------: |
+| 在 root/Coordinator 对话框正常下指令 | 正常指挥整个 Agent Org | 否 |
+| 切到 Planner/Implementer/Reviewer 等 worker 后直接聊天 | 用户临时接管这个 worker 的下一轮 | 是 |
+| 在 Group Chat 发消息或 @成员 | 组织内正常投递 | 否,并清除目标旧 intervention |
+| 点击 Return to work | 把 worker 交还给组织调度 | 清除 |
+
+```mermaid
+flowchart TD
+ U["用户提交消息"] --> B["Backend 读取 session 的真实 member_id"]
+ B --> C{"是 coordinator 吗?"}
+ C -->|"是"| N["普通组织指令;不建立 intervention"]
+ C -->|"否,是 worker"| I["建立 worker intervention"]
+ N --> D["Coordinator turn 可正常 drain worker inbox"]
+ I --> P["暂缓后台 drain,直到 Return to work 或 TTL"]
+```
+
+实现上不是只改一个 UI 判断:
+
+- 前端 direct-submit 和 queued-dispatch 只负责报告“这是用户直接输入”;
+- Backend 用持久 session 的 canonical `member_id` 做唯一分类,不能靠当前页面标题猜;
+- 通用 `agent_send_message` 不再从“有文本”反推 intervention,避免计划续跑或内部 continuation 被误判为用户接管;
+- Rust adapter 中重复写 intervention 的逻辑已删除,避免多层重复建立同一状态;
+- store 层永久禁止新建 Coordinator intervention;旧版本留下的 Coordinator intervention 在读取时自动清除,升级后不会继续挡信;
+- Wake 前置 gate 和 lifecycle race guard 仍保留,负责防住真正的 worker intervention,不再拿它们代替源头修复。
+
+### 第 2 批:给跨 Session 的成果一张持久交接单
+
+以前 task 只有 `pending / in_progress / completed`,只知道“做完了”,没有可靠保存“做出了什么”。Reviewer 或 implementer 不能安全读取另一个 session 的聊天历史,所以 coordinator 可能看到上游 completed,却仍然不知道下游应该拿什么继续。
+
+新增 `TaskOutput`:
+
+| 字段 | 大白话 |
+| ----------------------- | ---------------------------------------- |
+| `summary` | 一两句话的成果摘要 |
+| `content` | 可直接交给下游的正文(最多 20,000 字符) |
+| `artifact_ids` | 大文件或产物的持久引用 |
+| `produced_by_member_id` | 谁做的 |
+| `produced_at` | 什么时候做的,必须是 RFC3339 时间 |
+
+有下游依赖的上游任务,如果没有 `output`,系统不会接受 `completed`,而是返回明确 guidance。上游一旦合法完成,`TaskAssigned.dependency_outputs` 会把成果正文或 artifact 引用直接放进下游成员的真实 inbox turn;下游不需要猜 session id,也不需要翻别人的聊天记录。
+
+```mermaid
+flowchart LR
+ P["Producer Session"] -->|"task_update completed + output"| T["Task 持久状态"]
+ T -->|"dependency_outputs"| I["下游 Inbox"]
+ I -->|"Wake + Drain"| R["Reviewer / Implementer Session"]
+```
+
+### 第 3 批:把“成员空闲”和“任务完成”分成两封不同的回执
+
+`MemberIdle` 只表示“这个人这一轮结束了”,不能证明某张任务单已经完成。新增 system-only 的 `TaskCompleted`,明确告诉 coordinator:哪张 task、谁完成、输出摘要是什么、还剩多少 open task。
+
+- 只有 task 当前 owner 能把它设为 completed;coordinator 不能替 member 猜完成。
+- completed task 的状态是单调的,store 层禁止退回 pending/in_progress;修改需求必须新建 follow-up task。
+- 完成事务产生的 before/after `TaskMutationOutcome` 决定是否发一次回执,避免并发更新重复通知。
+- coordinator 收到回执后重新读持久 task board,再决定继续派工还是给用户最终答复。
+
+### 第 4 批:把 run 的结束改成真正的“项目验收”
+
+过去所有成员都回到 Idle 后,run 可能仍保持 Running;用户 pause/resume 会额外叫醒 coordinator,于是看起来像“暂停再恢复才突然知道做完了”。现在每个 Agent Org turn 结束都会尝试 `reconcile_run_finality`,但只有下面条件在同一个 SQLite `IMMEDIATE` transaction 内仍同时成立,才会写 Completed:
+
+1. task board 非空,且全部 task completed;
+2. root coordinator 和所有 worker 都已静止;
+3. 没有未读 inbox;
+4. 没有 active intervention;
+5. 没有 `optimistic / queued / running` 的未终结 turn intent;
+6. coordinator 的最后一个完整 turn 晚于最后一次 task 更新,证明它已经看过最新事实并有机会给用户最终答复。
+
+`reconcile_run_finality` 和所有 task mutation 共用 writer lock,所以“刚判定完成,另一个 turn 又创建 open task”的结果不可能写进数据库。
+
+前端新增 `runPhase`,用户不必再靠蓝色动画猜系统在做什么:
+
+| Phase | 界面含义 |
+| ----------------------------------------------------- | ---------------------------------------------------------- |
+| `coordinating` | coordinator 正在拆解/组织 |
+| `dispatching` | 有持久消息正在派送 |
+| `members_working` | 至少一个成员真正执行中或等待用户/额度 |
+| `waiting` | 有 open task,但当前无人执行;恢复系统会继续检查 |
+| `finalizing` | task 全部完成,正在等待最后消息排空和 coordinator 最终答复 |
+| `paused / completed / failed / cancelled / abandoned` | run 的持久状态 |
+
+### 四批追加审计发现并修正的尾部问题
+
+| 发现 | 为什么危险 | 最终修正 |
+| ------------------------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------ |
+| 旧 history 测试会把 completed task 重新打开 | 测试在鼓励已经禁止的非法生命周期 | 改成合法的 subject update 后再 release,仍覆盖 history 顺序 |
+| Finality 最初只检查 `queued` intent | `optimistic` 或状态同步窗口里的 `running` intent 可能被漏掉 | 改为任何未终结 intent 都阻止收尾,并覆盖三种状态 |
+| 新输出上限使用 UTF-8 byte 长度却写“字符” | 中文会被按 3 个 byte 计算,实际限制比文案小很多 | 所有新 output/dependency summary/content 限制统一按 Unicode 字符数 |
+| `produced_at` 只反序列化、不验证时间 | 直接 store 调用可留下无法审计的伪时间 | store 层强制 RFC3339,并加 malformed metadata 回归 |
+| UI 活跃数漏掉 `waiting_for_funds` | 后端认为成员工作中,界面却显示 active=0 | 前端 phase 和 active badge 使用同一状态集合 |
+
+## Breaking Bad 真人运行后的顺序派工与 Wake intent 修复
+
+### 1. 任务不再因“漏写 blocked_by”静默抢跑
+
+旧 `task_create` 把 `blocked_by=[]` 当成默认值。Coordinator 只要忘记填这个数组,Reviewer 和 Tester 就会立刻收到 `TaskAssigned`,即使任务描述明明写着“在概述和审核结果基础上”。现在工具协议强制每次创建任务明确选择:
+
+| 新字段 | 含义 | 何时派工 |
+| -------------------------------------------------------------- | ---------------------------- | --------------------------- |
+| `dispatch_policy="immediate"` | 当前信息已经足够、可独立开工 | 创建后立即派工 |
+| `dispatch_policy="after_dependencies"` + `dependency_task_ids` | 必须消费上游持久成果 | 所有上游 completed 后才派工 |
+
+`blocked_by` 仍是数据库里的底层表示,但不再暴露为 `task_create` 的易漏默认项。新协议还拒绝空依赖、未知 task id、self-cycle,以及 `immediate` 偷带 dependency id。最初使用 Rust tagged enum 会产生部分 LLM provider 不支持的 `oneOf`;审计测试捕获后,wire schema 改成扁平字符串 + 数组,在工具边界解析为 typed `TaskDispatchPolicy`。
+
+```mermaid
+flowchart LR
+ I["Implementer immediate"] -->|"completed + TaskOutput"| R["Reviewer after_dependencies"]
+ R -->|"completed + reviewed output"| T["Tester after_dependencies"]
+ T -->|"completed"| C["Coordinator final"]
+```
+
+三阶段回归测试证明:初始只有 Implementer 收到任务;Implementer 完成后 Reviewer 才收到且带上游 output;Reviewer 完成后 Tester 才收到且带审核 output。
+
+### 2. Scheduler 负责把每个 enqueue 决定写成终态
+
+旧路径会先写一条 `queued` intent,再调用 scheduler。scheduler 发现相同 `client_message_id` 时正确返回 duplicate,但新 intent id 没人更新,于是它永远停在 queued,阻止 `reconcile_run_finality`。现在新增两个精确终态:
+
+- `coalesced`:请求与已排队/运行的同一逻辑 turn 合并,本 intent 从未执行;
+- `rejected`:queue full/closed,本 intent 从未被接受。
+
+更新动作放进 Scheduler 本身,而不是要求 message、Wingman 或未来入口分别善后。`coalesced/rejected/stale` 都是 pre-durable terminal,不创建聊天 round,也不阻塞 Run 收尾。20 个并发相同 Wake 仍是 1 个 accepted、19 个 coalesced,但现在数据库 20 条 intent 全都有正确归宿。
+
+### 3. 升级后自动修旧数据,启动时先收尾再暂停
+
+进程重启后,内存 scheduler 已消失,因此旧 `optimistic/queued` 必然不可能再执行,启动时统一转为 `stale`;旧 `running` 转为 `failed`。实机数据库确认 Breaking Bad 的坏 intent `a7e69850-3f29-4e1c-a439-f35d1f53b659` 已从 `queued` 变为 `stale`。
+
+启动顺序现在是:intent 自愈 → 用 durable terminal marker 修正已结束 session → 其余 in-flight session abandoned → task failure disposition → 清 intervention → 原子完成所有 task 已 resolved 的 Run → 只暂停仍需继续的 Run。这样真正未完成的项目仍可恢复,而已经完成、只是被假 queued 卡住的项目不再要求用户手动 Resume 才收尾。debug `simulate-app-restart` 与生产入口使用相同七步顺序,并通过 pause/resume rendered E2E。
+
+## 实际改了什么
+
+### 1. Watchdog 从“边看边改”改为“先分析、后执行”
+
+- `inspect_stalled_run` 只读取 run、tasks、sessions、inbox 和 budget,生成可组合计划。
+- 一轮可以同时唤醒 Alice、让 Bob 接 ready task、把坏任务交给 coordinator,并尝试 reconcile。
+- 所有 `SessionStatus` 都被显式分类;没有用 `_ => false` 把未来状态静默吞掉。
+- 保留已决定的 E3 限制:任一 worker 处于 `Running`、`WaitingForUser` 或 `WaitingForFunds` 时,不做 peer 自动恢复;只允许发 stale/unsupported 的观察性 coordinator notice。
+- `Pending` 有 2 分钟 materialization grace;`Paused` 不自动 wake;Missing、Archived、历史 CLI transport 会升级给 coordinator。
+
+### 2. Wake 真正幂等,并把 Running 状态放回正确时刻
+
+- Agent Org wake 使用 `agent-org-wake:{run_id}:{member_id}`。
+- 20 个并发相同 wake 只接受一个 turn,其他返回 coalesced。
+- enqueue 前不再把 session 写成 Running;scheduler 真正执行 turn 时才更新。
+- 对 Agent Org wake,run 复核和 session→Running 在同一 writer lock + SQLite `IMMEDIATE` transaction 中完成。
+- wake 排队后 run 若变为 Paused/terminal,执行时直接 no-op,不 drain inbox,不调用 provider。
+- drain 后没有任何真实输入时返回空结果,不制造空 nudge,也不花一次 provider call。
+
+### 3. Recovery budget 持久化
+
+- 新增 `agent_org_recovery_attempts`,按 `(run, action_kind, target)` 保存指纹、次数和 UTC deadline。
+- 退避为 1/5/15 分钟,重启后仍然有效。
+- wake 只有真正 Enqueued 才消耗 member rewake attempt;coalesced 或 enqueue failure 不消耗。
+- member 成功 turn 后清除自己的预算;repair reason 指纹改变时 coordinator notice 自动获得新预算。
+- 非 Running run 的预算定期清理。
+- 损坏的 `next_allowed_at` 现在 fail-open 并告警;不会因为坏时间戳永久压住恢复,下一次成功动作会覆写为合法 UTC。
+
+### 4. Task eligibility 和显式指派逻辑统一
+
+> 2026-07-14 设计更新:后续真实运行证明自动领取会让任务责任边界变模糊,因此本节原有 claim 方案已被显式 Coordinator 指派取代。
+
+- Ownerless 只表示“当前没人负责”,不会触发 Worker 自动领取、自动 Wake 或执行模式切换。
+- Watchdog 看到 ready ownerless task 时只通知 Coordinator 明确选择 `owner_member_id`;Inbox drain 和 resume 不改变任务 owner。
+- `eligible_member_ids` 现在是 Coordinator 可选择的候选白名单,不是 Worker 的抢单许可。
+- 新建 ownerless pending task 必须有非空 eligibility;owner 和 eligibility 必须属于 launch snapshot roster。
+- `metadata` 必须是 object,保留字段类型和值会在 store 层再次校验,不能只相信 tool 层。
+
+### 5. 成员失败、shutdown 和 restart 使用明确 disposition
+
+成员异常失败:
+
+```text
+open task
+ -> owner=NULL, status=pending, metadata 保留
+ -> 不 wake failed owner,也不 wake eligible peer
+ -> coordinator 收到 awaiting_coordinator_assignment + task ids
+ -> coordinator 明确选择新的 owner 后,TaskAssigned 才能唤醒该成员
+```
+
+这条最终语义刻意不再区分“是否存在 peer”:eligibility 只是 Coordinator 的候选白名单,不是 Worker 的抢单许可。
+
+成员明确接受 shutdown:
+
+```text
+有合法 peer -> released_to_pool
+没有合法 peer -> owner=coordinator, status=pending
+```
+
+第二条是本轮审计补上的关键边界。主动 shutdown 是行政性停止,不应再被当成 provider failure 自动复活;交给 coordinator 后,任务仍合法、仍可见、但不会把已离开的成员叫回来。
+
+App restart 时,遗留 Running session 先转为 Abandoned,再走相同的 failure disposition,然后 run 转为 Paused,等待用户明确恢复。扫描不再只取前 500 个 Running run。
+
+### 6. Run finality 与 task mutation 原子化
+
+- create/update/delete/reassign/requeue/shutdown disposition 都在 transaction 内检查 parent run 必须是 Running。
+- `reconcile_if_terminal` 在统一 writer lock + `IMMEDIATE` transaction 内读取 run、root session、worker sessions 和全部 task 状态,再 CAS 写 terminal status。
+- 新增 reconcile 与并发 task_create 测试;合法结果只能是:reconcile 先赢、create 被拒绝,或 create 先赢、run 因 open task 进入 Abandoned。
+- update 返回事务内的 `TaskMutationOutcome`,side effect 依据 before/after transition 触发,避免并发更新重复发送 TaskAssigned。
+
+### 7. 删除 stale 自动抢任务
+
+- 删除按 15 分钟年龄自动清 owner 的 production path、debug endpoint、常量和测试。
+- Running session 的旧任务只会提醒 coordinator,不会被 watchdog 偷走。
+- 只有明确 Failed/Cancelled/Abandoned/Timeout、accepted shutdown 或 restart recovery 才能改变 ownership。
+- 无法解析的 task/session timestamp 会告警并生成 repair,不会永久静默。
+
+### 8. CLI Agent Org 能力边界变得诚实
+
+当前 CLI member 没有 production inbox drain、Agent Org task tools 和正确的 resume bridge,因此本 PR 不再假装它能运行:
+
+- 新建/更新 org 时拒绝 CLI coordinator/member。
+- launch 前再次 preflight,必须在创建 run/root session 前失败。
+- 错误包含 `member_id` 和 `cli:*` transport。
+- Agent Org 设置和 Session Creator 的成员选择器不再展示 CLI agent。
+- 已有旧定义仍能反序列化和打开,用户可删除不支持的 CLI member。
+- 删除了宣称 CLI Agent Org 已可运行的正向 E2E;保留历史数据解析和负向 validation 覆盖。
+
+## 用户现在看到的界面与行为
+
+| 场景 | 现在的界面/行为 |
+| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
+| 新建 Agent Org | coordinator 和 member 下拉只显示 Rust-native built-in/custom agents,不显示 CLI agents。 |
+| 编辑旧 CLI Agent Org | 旧数据仍能打开;CLI 选项不会继续提供,保存前需移除/替换不支持成员。 |
+| 启动含 CLI member 的旧 Org | 启动前明确报错,且不会留下半成品 run 或 root session。 |
+| Run 被暂停 | unread inbox 保留,后台 wake 不调用 provider;恢复后由正常 progress wake 继续。 |
+| Run 已结束 | task mutation 返回结构化不可变 guidance;排队中的 wake 执行时 no-op,不会复活成员。 |
+| 成员失败 | 看板任务按 eligibility 释放或保留;coordinator 收到含 disposition、eligible_member_ids、required_role 的恢复说明。 |
+| 成员主动 shutdown | 有 peer 时回池;无 peer 时交给 coordinator,不再自动复活已停止成员。 |
+| 多个来源同时 wake | 用户不会看到多个空 turn;scheduler 只执行一个,其余 coalesced。 |
+| 用户给 Coordinator 普通指令 | 不显示 intervention pin;Coordinator 仍能在该 turn 读取 worker 发来的未读信。 |
+| 用户直接切到 worker 聊天 | 只对该 worker 显示 intervention pin;后台信保留,Return to work 后继续。 |
+| 升级前留下 Coordinator intervention | 第一次读取时自动清除,不再要求用户 Pause/Resume 才恢复。 |
+| Running 成员长时间没更新 | 只提醒 coordinator,不自动清 owner。 |
+| App 重启 | 旧 intent 先收口;任务已全部 resolved 的 run 尝试原子完成;仍有工作/未读交接的 run 才暂停并保持看板可见。 |
+
+## 本轮代码审计发现并修复的问题
+
+| 优先级 | 发现 | 风险 | 修复 | 状态 |
+| ------ | --------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------- |
+| P0 | debug task seed 使用错误 crate 根路径 | `agent_core` check 通过但顶层 `org2` 无法编译 | 改为公开的 `agent_core::...` import,并补跑 `cargo check -p org2` | 已修复 |
+| P0 | 普通 Coordinator 指令被当成 worker takeover | 未读 worker 回执与 3 分钟 intervention 冲突,形成 270 个顺序 Wake intent 和蓝色闪烁 | Backend 按 canonical member_id 分类;Coordinator 永不建立 intervention;store 自愈旧记录;真实 UI 回归覆盖 | 已修复 |
+| P1 | 通用 `agent_send_message` 从“非空文本”推断 intervention | 自动 continuation 或内部程序化消息也可能被当成用户接管 worker | 只有 direct-submit / queued-dispatch 显式发 takeover 信号;generic command 不再推断;direct-member API 保留显式语义 | 已修复 |
+| P0 | accepted shutdown 的 sole-owner task 仍留给 Cancelled member | watchdog 会把主动停止成员当异常终止重试 | 无 peer 时把 owner 改为 coordinator,并写 `escalated_to_coordinator` history | 已修复 |
+| P1 | failure hook 按 eligibility 唤醒 peer | Worker 会在未经过 Coordinator 明确分配时收到任务 Wake | 删除失败后的 peer 自动领取/Wake;任务变成 ownerless pending,并向 Coordinator 报告具体 task ids | 已修复 |
+| P1 | recovery deadline 损坏时 budget probe 返回错误 | member 既不重试也不升级,可能永久静默 | 告警并 fail-open,成功动作覆写 deadline;新增测试 | 已修复 |
+| P1 | restart recovery 只扫 500 个 running run | 大量历史 run 时后面的遗留任务得不到 disposition | 使用 `usize::MAX`,SQL 安全 clamp 到 `i64::MAX` | 已修复 |
+| P2 | CLI 保存错误只显示成员名字 | 同名成员难定位,未满足 validation contract | 错误加入 `member_id=` 和 `cli:*` | 已修复 |
+| P2 | 全局 rustfmt/prettier 带入无关格式变化 | 增加 review 噪声 | 逐文件反向清除确认的 formatting-only diff | 已修复 |
+| P0 | Review/Test task 漏写 `blocked_by` 会静默当成可立即执行 | Reviewer/Tester 在没有上游产物时反复空跑,真实顺序与任务描述矛盾 | `task_create` 强制 `dispatch_policy`;依赖策略映射为底层 `blocked_by`;三阶段顺序测试 | 已修复 |
+| P0 | The Boys 实机中 Coordinator 文字说等待 Implement/Review,结构化依赖却只列 Planner | Scheduler 忠实执行错误图,Tester 在 Implementer 完成前先交付 | `after_dependencies` 漏列非传递 open task 时返回 `requires_dependency_confirmation` 且不落库;补齐依赖或显式 `allow_parallel_with_unlisted_open_tasks=true` 后才创建 | 已修复并经后续真人运行复验 |
+| P0 | scheduler 合并 duplicate Wake 后新 intent 仍为 queued | 所有人完成后 Run 永久无法 finality,用户必须 Pause/Resume | Scheduler 原子收口为 `coalesced`;queue full/closed 为 `rejected` | 已修复 |
+| P1 | 旧版本遗留 queued intent 升级后仍卡住 | 修复只对新数据有效,历史 Run 仍坏 | 启动时 queued/optimistic→stale、running→failed;实机坏行已自愈 | 已修复 |
+| P1 | 所有 task 已完成的 Running Run 在 restart 后仍被通用策略暂停 | 用户仍需手动 Resume 才完成 | 启动 pause sweep 前先走正常原子 finality,仅暂停仍需工作的 Run | 已修复 |
+| P1 | `simulate-app-restart` 与生产启动序列漂移 | rendered E2E 测到的是假流程 | debug endpoint 与生产统一为七步同序,并扩展 typed E2E result | 已修复 |
+| P1 | tagged dispatch enum 生成 LLM provider 不兼容的 `oneOf` | Coordinator 可能无法调用 task_create | wire schema 扁平化,边界内再解析为 typed enum;schema portability 测试 | 已修复 |
+
+审计结束时没有遗留本次范围内的 P0/P1/P2 actionable finding。
+
+## Architecture Audit:10 层检查
+
+| Layer | 检查内容 | 结论 |
+| -------------------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| 1. Compilation correctness | `org2`、`agent_core`、`e2e-test`、TypeScript、ESLint、Node syntax | 本次改动编译/类型/语法通过;Agent Core Clippy 有 36 条仓库既有基线,见验证章节。 |
+| 2. Dead code / duplication | 从 watchdog、resume、task tools、failure finalize、scheduler 业务入口正向追踪 | 删除 dead `find_available`、`blockers_resolved`、旧 stale-release path、旧 wake wrapper、自动 claim helper 和失真的 CLI 正向 E2E。Ownerless 统一进入 Coordinator repair。 |
+| 3. Naming consistency | 搜索 stale/release/wake/owner 旧名和注释 | stale 常量只表示 notice,不再暗示 release;failure 与 shutdown history 使用不同 disposition 名称。旧符号 sweep 为零。 |
+| 4. Semantic overloading | Run / Session / Task / Delivery / Budget 对照 | 五个状态源独立持久化。Idle 不代表 terminal,unread 不代表 wake accepted,Pending/eligible 不代表 Worker 可以自领。 |
+| 5. Default branches | 审查 `SessionStatus`、run status、wake outcome 和时间戳 fallback | Session recovery 显式穷举;DB/read error fail-closed;损坏 deadline/timestamp 不静默。E3 是具名且有测试的限制。 |
+| 6. Cross-domain leakage | Rust/CLI transport、core task store、UI picker | 历史 CLI 只在边界被识别,不再落入 Rust wake;shared picker 用 `hideCliAgents` 参数,不改变其他 CLI-only surface。 |
+| 7. New-developer clarity | 函数/事件/注释是否准确表达副作用 | Analyzer、executor、doorbell、disposition、finality transaction 的职责明确;shutdown 不再复用 failure 的 kept-owner 含义。 |
+| 8. Wire / serialization | scheduler response、task metadata、inbox payload、tool guidance | scheduler 的 duplicate/rejected 映射为持久终态;task dispatch schema 为 provider-compatible 扁平字段,边界内解析 typed policy;依赖遗漏用成功形态的 structured guidance 返回,不制造 trajectory error;typed metadata 在 tool + store 双层校验。 |
+| 9. Init parity | production setup、unit sandbox、debug seed、restart path | recovery schema 在 production 和测试初始化;production startup 与 `simulate-app-restart` 同为七步;debug seed 生成合法 eligibility;launch preflight 在 run 创建前执行。 |
+| 10. Resolver symmetry | member identity、assignment、run gate、session resolution | watchdog/resume/task/failure 都把 ownerless 解释为等待 Coordinator 指派;身份始终以 member_id 为主;wake 的 run_id 用 typed 参数,不从字符串反解析。 |
+
+## 状态所有权表
+
+| 状态维度 | 唯一真相源 | 允许从其他状态猜吗? |
+| ------------------- | ------------------------------ | -------------------- |
+| Run 是否允许继续 | `agent_org_runs.status` | 不允许 |
+| Member 是否正在执行 | `agent_sessions.status` | 不允许 |
+| Task 归属/完成/依赖 | `agent_org_tasks` | 不允许 |
+| 信是否未读 | `agent_inbox.read_at` | 不允许 |
+| Wake 是否已排队 | scheduler idempotency registry | 不允许 |
+| 自动恢复是否可再试 | `agent_org_recovery_attempts` | 不允许 |
+
+## 入口一致性矩阵
+
+| 入口 | Running gate | 显式 assignment | 持久 inbox | 幂等 wake | Budget |
+| ------------------------------ | ---------------------------------------------------------------------------: | --------------------------------: | -----------------: | --------------------------------: | ---------------------: |
+| Watchdog | analyzer + executor + turn start | ownerless 只 repair Coordinator | 是 | 是 | 是 |
+| Resume progress | dispatcher + turn start | 只投递已有 owner 的真实输入 | 是 | 是 | terminal retry 才计 |
+| Task create/update side effect | mutation transaction + dispatcher | Coordinator 写 owner 后才投递 | 是 | 是 | terminal retry 才计 |
+| Member failure finalize | task transaction + dispatcher | 清 owner,等待 Coordinator | coordinator notice | 是 | 不在 failure hook 消耗 |
+| Accepted shutdown | task transaction | peer pool / coordinator ownership | MemberTerminated | coordinator wake 合并 | 不复活 stopped member |
+| App restart | intent reconcile + failure disposition + resolved finality + remaining pause | 是 | 保留 | 旧队列收口;resume 后新 wake 生效 | 持久保留/按状态清理 |
+
+## 验证记录
+
+| 命令/检查 | 结果 |
+| -------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `cargo check -p org2` | 通过 |
+| `cargo check -p e2e-test` | 通过 |
+| Watchdog tests | 21 通过 |
+| Inbox drain tests | 30 通过 |
+| Lifecycle tests(单线程隔离) | 5 通过 |
+| Task store/tool/run finality/scheduler targeted tests | 通过;41 个 task tool 测试含三阶段依赖链、The Boys 同形漏依赖确认、明确并行保留,scheduler 含 20 并发 wake,另含 startup intent/finality 与 reconcile/create race |
+| `cargo test -p agent_core --lib -- --test-threads=1`(沙箱外) | **2951 通过,2 个既有基线失败** |
+| `pnpm typecheck` | 通过 |
+| `pnpm run lint` | 通过;修复 1 个本 diff 内的 Prettier 换行后全量复验通过 |
+| `pnpm run check:circular` | 通过;5073 files,零 circular dependency |
+| 两份修改的 `.mjs` 执行 `node --check` | 通过 |
+| 隔离 desktop/WebDriver `agent-org-recovery-ui.spec.mjs` | **2/2 真实渲染场景通过**;证明 Coordinator 普通指令不建立 intervention、可 drain Planner 未读信,同时 Planner 直接聊天仍建立 intervention |
+| 隔离 desktop/WebDriver `agent-org-pause-resume-ui.spec.mjs` | **8/8 真实渲染场景通过**;覆盖生产同序 restart simulation、暂停/恢复、历史 Run 自动恢复、成员/Coordinator 历史和侧栏 |
+| `session_persistence` 单线程全量 | **31/32 通过**;唯一失败是既有测试硬编码另一开发者 `/private/var/folders/10/.../Users_junyu/...` 临时路径,与本 diff 无关 |
+| `git diff --check` | 通过 |
+| stale/dead symbol sweep | 旧 stale release、旧 wake helper、旧 availability helper均为 0 命中 |
+
+完整 Rust suite 的两个已知失败:
+
+1. `core::session::turn::entry::tests::skill_slash_command_accepts_newline_after_name`:测试期待 bundled e2e-testing skill content,但本地返回原始 slash command;与 Agent Org 改动无关。
+2. `core::tools::search_tool_tests::repo_path_and_repo_paths_conflict_returns_error`:search tool 既有参数冲突行为与测试期待不一致;与本 PR 无关。
+
+`cargo clippy -p agent_core -p session_persistence --all-targets --no-deps -- -D warnings` 仍被仓库既有 Clippy 基线挡住,分布在 interaction、provider、memory、session launch 等旧代码;包含 `doc_lazy_continuation`、`too_many_arguments`、`clone_on_copy` 等。此次新增的 dispatch policy、scheduler intent 终态、startup recovery 和 parity 入口没有新增 Clippy 命中,按本 PR 约定未扩大为无关重构。
+
+本机已构建带 WebDriver automation 的最新桌面二进制,并在全新隔离 home 上运行 `agent-org-recovery-ui.spec.mjs`:失败成员恢复 UI、默认 Agent Org 启动、生产 run phase、Coordinator 普通指令读取 Planner inbox、worker-only intervention 共 2 个 rendered 场景全部通过。数据库证据中 Planner→Coordinator 的目标信从 unread 变为 read,同时 intervention 表只出现 `sde-planner`,没有 Coordinator 记录。未运行与本轮直接修复无关的整套 `agent-org-settings-ui.spec.mjs`。
+
+## 有意保留的设计边界
+
+- **E3 仍延后**:一个 worker active 时,暂不做 member-level peer recovery。代码注释和测试明确锁定,未来应作为独立设计升级。
+- **Eligibility 仍在 metadata JSON**:目前通过 typed tool + store invariant + SQL JSON1 加固;join table 规范化留给独立 schema PR。
+- **CLI Agent Org 是明确禁用,不是“半支持”**:完整支持需要 CLI inbox drain、Agent Org tools、resume bridge 和 production E2E。
+- **DispatchCategoryPalette 有两套相近 option-building 实现**:本次为保持行为一致同时加了 `hideCliAgents`;后续可单独抽共享 selector,避免 UI picker 规则双写。本 PR 不为此扩大重构范围。
+- **`WakeNoop` 是执行期结果**:request 侧能区分 Enqueued/Coalesced/Paused/Terminal/Unavailable/Failed;排队后工作消失由 processor no-op 并记录日志,当前没有再向最初 caller 反向传播异步 NoWork。
+
+## Commit-ready 判定
+
+自动化层面可以提交,理由:
+
+- 本次范围内的编译、类型、lint、targeted tests 和 diff hygiene 均通过。
+- 完整 Rust suite 中所有 Agent Org/Recovery/Task/Wake 测试通过。
+- 两个全量失败和 36 条 Agent Core Clippy 报告均为可复现的既有基线,未由本 diff 引入;workspace Clippy 还会先被未修改的 `cursor-bridge-app` `question_mark` 告警阻断。
+- 审计发现的 P0/P1/P2 已全部修复并复验。
+- 无关 untracked 文件 `.atomcode/`、`docs/PR-GUIDE-issue-194.md`、`docs/cli-agent-launch-args-plan-2026-07-04.md` 未被修改或纳入本次范围。
+
+但按仓库 `github-issue-fix-workflow`,The Boys 之后新增的依赖确认门属于可见调度行为变化,仍需用户在最新 dev 上做一次真人 Agent Org 验收。验收通过后才能把结论更新为最终 commit-ready 并执行 Husky commit。
+
+建议提交标题:
+
+```text
+fix(agent-org): harden recovery, wake delivery, and run finality
+```
+
+提交前还需真人确认依赖遗漏会被 Coordinator 修正或明确确认并行;两份审计文档应随代码一并纳入 commit,无关本地文件继续排除。
diff --git a/docs/architecture-audit-2026-07-12/GitProfiles.md b/docs/architecture-audit-2026-07-12/GitProfiles.md
new file mode 100644
index 000000000..8e69a858c
--- /dev/null
+++ b/docs/architecture-audit-2026-07-12/GitProfiles.md
@@ -0,0 +1,52 @@
+# Architecture Audit — Git Profiles
+
+Scope: reusable Git profile persistence and parsing in TypeScript, global Git config commands in Rust, and the Tauri command boundary.
+
+## Acceptance criteria
+
+- Multiple named identities persist locally and can be created, duplicated, edited, deleted, and selected.
+- Activating a profile writes `user.name`, `user.email`, optional `user.signingKey`, and `commit.gpgSign` at global scope.
+- Optional signing state from a previous profile cannot leak into the next profile.
+- Existing Git connection and preference behavior remains on the Connections tab.
+- Raw profile config supports exactly the identity fields the backend applies and rejects unsupported sections/keys.
+- TypeScript, focused tests, Git crate compilation, and full desktop command registration pass.
+
+## Ten-layer audit
+
+| Line | Element | Verdict | Reason | Suggested change |
+| ----------------------------------------------- | ----------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- |
+| `gitProfiles.ts:1`, `util.rs:502` | 1. Compilation correctness | pass | `npm run typecheck`, focused ESLint, `cargo check -p git`, and full `cargo check` pass. | None. |
+| `GitProfilesTab.tsx:61`, `util.rs:555` | 2. Dead code and structural deduplication | pass | Live path is tab mount → `get_git_global_profile` → editor state → `set_git_global_profile`. Existing config read/write helpers are reused; no parallel Git subprocess implementation was introduced. | None. |
+| `gitProfiles.ts:3`, `util.rs:505` | 3. Naming consistency | pass | `GitProfile` is the saved ORGII record; `GitGlobalProfile` is the wire/global-config shape; `GitUserIdentity` remains the effective repository identity lookup. Names distinguish persistence and resolution scopes. | None. |
+| `GitProfilesTab.tsx:39`, `util.rs:502` | 4. Semantic overloading | pass | “Profile” consistently means an author/signing identity. “Connection” remains GitHub authentication/repository access and is not reused for identity switching. | None. |
+| `util.rs:590` | 5. Default branch analysis | pass | Optional signing keys are explicitly written or unset. Commit signing is explicitly written as true or false. No catch-all branch silently retains the prior profile. | None. |
+| `gitProfiles.ts:1`, `util.rs:490` | 6. Cross-domain leakage | pass | UI persistence stays in the Git integration module; subprocess/config operations stay in the Git crate. GitHub connection records only supply email suggestions and do not own Git author identity. | None. |
+| `GitProfilesTab.tsx:163`, `util.rs:572` | 7. New-developer clarity | pass | “Activate” is the only path that mutates global Git config. Editing saved values only clears the active marker, making saved state versus applied state explicit. | None. |
+| `gitProfiles.ts:16`, `util.rs:504` | 8. Wire protocol and serialization | pass | The camel-cased frontend payload is intentionally mapped by Tauri to the snake-cased Rust struct fields. The payload contains four bounded scalar fields and no generated schema or hidden data. | None. |
+| `handler_list.inc:365`, `GitProfilesTab.tsx:61` | 9. Init parity | pass | There is one production read entry point and one production apply entry point, both registered in the canonical handler list. Empty local storage imports the same global shape later used for matching and applying. | None. |
+| `gitProfiles.ts:68`, `util.rs:557` | 10. Resolver symmetry | pass | Name, email, signing key, and signing toggle are read from global scope and applied to global scope. All four fields participate in active-profile matching. | None. |
+
+## Term overloading table
+
+| Term | Meaning | Owner | Verdict |
+| -------------- | ----------------------------------------------------------------- | -------------------------- | ------- |
+| Git connection | Authentication/repository access for a Git provider | Sync connections API | keep |
+| Git profile | Reusable author and commit-signing identity | Git Profiles settings | keep |
+| Global profile | Identity values currently written to global Git config | Git crate command boundary | keep |
+| Active profile | Saved profile whose every applied field matches global Git config | Git Profiles state | keep |
+
+## Resolver and entry-point matrices
+
+| Field | Read global | Persist saved profile | Compare active | Apply global | Clear when absent |
+| ------------ | ----------- | --------------------- | -------------- | ------------ | ----------------- |
+| Author name | yes | yes | yes | yes | required |
+| Email | yes | yes | yes | yes | required |
+| Signing key | yes | yes | yes | yes | yes |
+| Sign commits | yes | yes | yes | yes | explicit false |
+
+| Entry point | Validate | Blocking isolation | Shared helpers | Result surfaced |
+| ------------------------ | --------------------- | ------------------ | ------------------- | ---------------------- |
+| `get_git_global_profile` | Git output normalized | `spawn_blocking` | global read helper | typed Tauri result |
+| `set_git_global_profile` | name/email required | `spawn_blocking` | write/unset helpers | error or success toast |
+
+No systematic sweep candidates or deferred architecture fixes remain in this scope.
diff --git a/docs/architecture-audit-2026-07-12/KanbanNamingCleanup.md b/docs/architecture-audit-2026-07-12/KanbanNamingCleanup.md
new file mode 100644
index 000000000..ffc986fd2
--- /dev/null
+++ b/docs/architecture-audit-2026-07-12/KanbanNamingCleanup.md
@@ -0,0 +1,38 @@
+# Kanban naming cleanup — architecture audit
+
+## Acceptance criteria
+
+- [x] No retired management route or route metadata remains.
+- [x] No compatibility migration remains for the former management tab identities.
+- [x] Kanban actions, shortcuts, service methods, and ChatPanel entry points use one naming chain.
+- [x] Work Items sidebar IDs describe their actual navigation role.
+- [x] Route-only station mode, peek state, and focus state are deleted.
+- [x] Source and documentation filenames contain no retired product vocabulary outside immutable changelog history.
+- [x] TypeScript, focused lint/tests, locale parsing, and diff checks pass.
+
+## 10-layer audit
+
+| Layer | Coverage | Verdict | Evidence / reason |
+| --------------------------------------- | -------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| 1. Compilation correctness | Covered | pass | Full `tsc --noEmit` and targeted ESLint pass with zero errors. No Rust implementation was changed by this cleanup. |
+| 2. Dead code & structural deduplication | Covered | fix | Removed the dedicated route, route loader, app-mode branch, route-only station variant, peek/focus atoms, unused Workstation tab type, and obsolete translation keys. |
+| 3. Naming consistency | Covered | fix | Actions, shortcuts, services, tests, UI labels, storage keys, modules, and sidebar IDs consistently use Kanban, Work Items, or the neutral internal Work Management host. |
+| 4. Semantic overloading | Covered | fix | Kanban is the board/action name; Work Items is the expandable navigation group; Work Management is only the internal multi-section host boundary. |
+| 5. Default branch analysis | Covered | pass | Unknown or absent management sections resolve to Kanban; unknown routes resolve through the normal Workstation code fallback and no longer activate a hidden management mode. |
+| 6. Cross-domain concept leakage | Covered | fix | Shared AppShell and station-mode code no longer carries route-specific management state. The Work Management host owns only multi-section content coordination. |
+| 7. New-developer clarity | Covered | fix | `openKanbanTab`, `openKanbanChatPanelTabAtom`, `KANBAN_MENU_ITEM_ID`, and `WORK_ITEMS_*` expose intent without requiring knowledge of a retired product name. |
+| 8. Wire protocol & serialization | Covered | fix | No external wire protocol changed. The ChatPanel storage key is versioned to intentionally discard obsolete persisted tab identities instead of migrating them. |
+| 9. Init parity | Covered | pass | Shortcut, Spotlight, action system, Start Page, ChatPanel plus menu, and app sidebar all converge on the same Kanban service/atom path. |
+| 10. Resolver symmetry | Covered | pass | All four management sections use the same section-to-title, section-to-icon, tab activation, and sidebar-selection mappings. |
+
+## Term-overloading check
+
+| Term | Product meaning | Internal meaning | Verdict |
+| --------------- | ------------------------------------------------------ | ---------------------------------------------------------------- | ---------------- |
+| Kanban | Board destination and primary action | Default section of the management ChatPanel tab | aligned |
+| Work Items | Expandable navigation group and project/work-item list | Semantic sidebar ID namespace | aligned |
+| Work Management | Not displayed as product copy | Neutral host for Kanban, Projects, GitHub Issues, and GitHub PRs | keep with reason |
+
+## Systematic sweep
+
+The sweep covers casing variants, kebab/snake/camel identifiers, filenames, route strings, persisted type shims, action and shortcut IDs, translations, tests, comments, and audit documents. Historical changelog data is intentionally immutable and excluded from the live-reference criterion.
diff --git a/docs/architecture-audit-2026-07-12/OrgtrackUsageAnalyticsOptimization.md b/docs/architecture-audit-2026-07-12/OrgtrackUsageAnalyticsOptimization.md
new file mode 100644
index 000000000..163595a3b
--- /dev/null
+++ b/docs/architecture-audit-2026-07-12/OrgtrackUsageAnalyticsOptimization.md
@@ -0,0 +1,165 @@
+# Orgtrack Usage Analytics — Optimization Plan
+
+**Date:** 2026-07-12
+**Scope:** `orgtrack` usage/cost analytics — pricing accuracy (Tier 1) and
+parse/cache correctness (Tier 2).
+**Status:** Tier 1 (T1.1–T1.4) and Tier 2 (T2.1–T2.3) implemented in the working tree
+(uncommitted); `cargo check`, `typecheck`, and `lint` clean. Follow-ups below are parked.
+
+This plan targets two areas of the usage-analytics pipeline: turning token counts
+into trustworthy dollar figures, and hardening the parse/cache layer so those figures
+stay correct as source files change. It does **not** change the `.orgtrack` export,
+impact-indexing, or commit-linking subsystems.
+
+---
+
+## Current state (baseline)
+
+### Pricing
+
+- The only costing logic lives in `src-tauri/src/agent_sessions/unified_stats/accounting.rs`.
+ It uses four hardcoded per-Mtok rates (`DEFAULT_INPUT_COST_PER_MTOK = 3.0`,
+ `DEFAULT_OUTPUT_COST_PER_MTOK = 15.0`, `DEFAULT_CACHE_WRITE = 3.75`,
+ `DEFAULT_CACHE_READ = 0.30`).
+- `resolve_model_pricing` reads a `model_pricing` SQLite table
+ (`WHERE ?1 LIKE model_pattern ORDER BY length(model_pattern) DESC`), **but that table
+ is never created or populated anywhere in the codebase** — so every lookup falls
+ through to the four defaults regardless of model.
+- Costing reads only the native `session_token_usage` table. Imported/external session
+ tokens live in `imported_history_session_cache`, so those sessions almost always
+ report `cost_usd = 0`.
+- There is no real-vs-estimated cost distinction. `UsageSourceLabel::{Local, Pooling}`
+ is derived from `KeySource` and is a **label only**, not a cost decision.
+- Frontend: `$` cost is rendered **only** in the Sessions view (`session_usage_list`).
+ Other Usage overview, the Cursor panel, and the CLI panel show tokens with no cost,
+ even though `AggregateStats.total_cost_usd` exists on the backend.
+
+### Parse / cache correctness
+
+- Imported-history cache invalidation
+ (`crates/orgtrack-core/src/sources/imported_history/metadata.rs`) compares
+ `source_path + source_mtime_ms + source_size_bytes + source_fingerprint +
+parser_version`. This is solid for Claude/Codex (title-aware fingerprints) but
+ **Windsurf and OpenCode use a bare mtime string** as the fingerprint — a same-mtime
+ content change can be missed.
+- Cache signatures use millisecond mtime (`paths.rs::file_metadata_signature`), which
+ is coarse enough to miss rapid in-place edits.
+- SQLite-backed sources do not fold WAL/`-shm` sidecars into the change signature, so a
+ new session written but not yet checkpointed may not invalidate the cache.
+- The unified CLI scanner (`crates/orgtrack-core/src/sources/cli_session_db.rs`) uses
+ hand-rolled substring JSON extraction (`extract_json_string_field` / `extract_json_i64`)
+ rather than a real parser. Aider token counts are always `0` and message counts are a
+ rough `lines / 4` estimate.
+
+---
+
+## Tier 1 — Pricing accuracy
+
+**Goal:** every session that has tokens gets a trustworthy dollar figure, and the UI
+can show recorded vs estimated cost.
+
+### T1.1 — Bundled model-price catalog
+
+- Add a generated catalog data file (JSON) of per-model input/output/cache-read/
+ cache-write rates, plus a small loader.
+- Populate the `model_pricing` table from the bundled catalog on schema init (or query
+ the catalog directly and drop the dead table lookup).
+- Normalize model ids before lookup (case-fold, treat `.`/`-` equivalently, strip
+ date-pin and effort suffixes) so `claude-sonnet-4-5-20250101` and
+ `claude.sonnet.4.5` resolve to one rate.
+- Lookup order: exact id → normalized id → longest-prefix family fallback →
+ mid-range default. Local/self-hosted providers price at `$0`.
+- Owner: `accounting.rs`, a new `pricing_catalog` module, `crates/orgtrack-core` schema
+ init, plus the catalog data file.
+
+### T1.2 — Cost imported/external tokens
+
+- Extend costing so `imported_history_session_cache` tokens are priced through the same
+ catalog, not just `session_token_usage`. Imported sessions must stop reporting `$0`
+ purely because their tokens live in a different table.
+
+### T1.3 — Recorded vs estimated cost
+
+- Carry two cost figures per aggregate session: **recorded** (real metered spend) and
+ **estimated** (tokens × catalog list price). For subscription/own-key routes with no
+ metered cost, recorded is `$0` and estimated is the list-price figure.
+- Decide recorded-vs-estimated per route where the information exists (metered API key
+ vs subscription/OAuth login), rather than a cosmetic local/pooling label.
+- Expose both on `AggregateStats` / `UsageRecord` and the `orgtrack_*` command payloads.
+
+### T1.4 — Surface cost in every usage view (frontend)
+
+- Add a cost column and a `$` / tokens toggle to the Other Usage overview, Cursor panel,
+ and CLI panel (`src/modules/MainApp/DevRecord/views/OtherUsageView/*`).
+- For token-only sources, default the toggle to the estimate with an `ESTIMATED` marker,
+ matching the existing Sessions-view cost formatting.
+- Extend the frontend types (`src/api/tauri/orgtrackHistory/types.ts`) with the
+ recorded/estimated cost fields.
+
+---
+
+## Tier 2 — Parse / cache correctness
+
+**Goal:** cached rollups never go stale silently, and CLI parsing is robust.
+
+### T2.1 — Stronger cache fingerprints
+
+- Move cache signatures to nanosecond mtime granularity.
+- Fold SQLite WAL/`-shm` sidecars into the change signature for db-backed sources so a
+ not-yet-checkpointed write invalidates the cache.
+- Replace the bare-mtime fingerprints for Windsurf and OpenCode with content-aware
+ fingerprints (e.g. row counts / latest-updated markers), consistent with the
+ Claude/Codex approach.
+- Owner: `imported_history/metadata.rs`, `imported_history/cache.rs`, `paths.rs`,
+ `sources/windsurf/history.rs`, `sources/opencode/history.rs`.
+
+### T2.2 — Robust CLI parsing
+
+- Replace the hand-rolled substring JSON extraction in `cli_session_db.rs` with real
+ `serde_json` parsing per tool.
+- Fix Aider token accounting (currently always `0`) and replace the `lines / 4` message
+ estimate with an actual count where the format allows.
+
+### T2.3 — Token-accounting quirk verification
+
+- Verify per-source token accounting handles known edge cases without double-counting:
+ resumed/forked session overlap, cumulative-counter deltas with context-compaction
+ resets, and multi-record dedup. Document confirmed-correct vs fixed per source.
+
+---
+
+## Discovered follow-ups (parked)
+
+Surfaced during T2.3 verification and the pricing work; not fixed under this pass:
+
+- **FU1 (high) — Claude resume/fork double-count.** Claude sessions are cached with
+ `parent_session_id: None` and no cross-file message-`uuid` dedup, so `--resume`/fork
+ transcripts (which replay prior turns with the same usage into a new JSONL) are
+ counted twice. Pricing makes this visible in dollars — highest-value next fix.
+ Owner surface: `crates/orgtrack-core/src/sources/claude_code/history.rs`,
+ `.../imported_history/cache.rs`.
+- **FU2 — Codex compaction-reset undercount.** Last-wins on the cumulative
+ `total_token_usage` drops pre-compaction tokens if Codex resets after a context
+ compaction (undercount, not double-count).
+- **FU3 — Cursor `state.vscdb` sidecar folding.** The nanosecond/WAL fingerprint helper
+ (`sqlite_sidecar_signature`) exists and is adopted by OpenCode/Windsurf; Cursor's
+ `cursor_ide/db.rs` reader has not yet adopted it.
+- **FU4 — Per-session recorded cost in the detail command.** The single-session
+ `session_usage_summary` path has no route context, so it reports `recorded = $0` /
+ `estimated = list price`. Aggregate/heatmap/usage-list paths are route-aware.
+
+## Out of scope
+
+- `.orgtrack` on-disk export, reachability, git-blame, and sync-record generation.
+- Impact indexing and commit linking.
+- New source integrations (tracked separately).
+
+---
+
+## Verification
+
+- Backend: `pnpm cargo:check` and the relevant `cargo:test:*` targets must pass.
+- Frontend: `pnpm typecheck` and `pnpm lint` must pass; cost columns exercised against
+ real session data in the running app.
+- No source files under a user's tool directories are written — read-only guarantee is
+ preserved.
diff --git a/docs/architecture-audit-2026-07-12/SharedAccountDetails.md b/docs/architecture-audit-2026-07-12/SharedAccountDetails.md
new file mode 100644
index 000000000..8a44dbc0f
--- /dev/null
+++ b/docs/architecture-audit-2026-07-12/SharedAccountDetails.md
@@ -0,0 +1,47 @@
+# Shared Account Details Architecture Audit
+
+Scope: extracting Key Vault account details from the Settings ownership tree and consuming them from both Settings and the Chat launchpad.
+
+## Completion checklist
+
+- [x] TypeScript typecheck succeeds.
+- [x] ESLint succeeds for every touched TypeScript/TSX file.
+- [x] Existing Chat panel start-page tests succeed.
+- [x] One production implementation owns account detail, status, compatibility, and badge behavior.
+- [x] Shared modules do not import from `MainApp`.
+- [x] Chat defers detail code and detail-specific compatibility requests until selection.
+- [x] Old Settings import paths are compatibility re-exports rather than duplicate implementations.
+
+## Ten-layer findings
+
+| Line | Element | Verdict | Reason | Suggested change |
+| -------- | ------------------------ | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------- |
+| Layer 1 | Compilation correctness | pass | `npm run typecheck -- --pretty false`, scoped ESLint, and the focused Vitest suite all pass. | None. |
+| Layer 2 | Structural deduplication | pass | `AccountInlineDetails`, compatibility, badges, status colors, and layout primitives each have one implementation. Existing Settings paths only re-export them. | Remove compatibility re-exports only when all downstream imports have migrated. |
+| Layer 3 | Naming consistency | pass | The canonical name describes the rendered body (`AccountInlineDetails`); the former `AccountInlineStatusSection` survives only as a compatibility alias. | Prefer the canonical name in new code. |
+| Layer 4 | Semantic overloading | pass | `account` consistently means `KeyVaultAccount`; `status` is account readiness/health; `details` is presentation over the same account record. No term carries a second domain meaning. | None. |
+| Layer 5 | Default branches | pass | Selection explicitly toggles open/closed. Async completion clears only the matching account ID, preventing an older refresh from clearing a newer selection's loading state. Failed refresh retains cached account data. | None. |
+| Layer 6 | Cross-domain leakage | pass | `src/modules/shared/keyVault` contains no imports from `src/modules/MainApp`; Settings depends on shared code, not the reverse. | Keep this dependency direction. |
+| Layer 7 | New-developer clarity | pass | Layout primitives, account-domain components, and launchpad orchestration have separate owners and purpose-based names. | None. |
+| Layer 8 | Wire protocol | not applicable | No wire type or serialized payload changed. The launchpad calls the existing `refreshAccount(accountId, true)` boundary. | None. |
+| Layer 9 | Entry-point parity | pass | Settings and Chat render the same canonical detail component from the same `KeyVaultAccount` shape. Chat adds its surface-specific footer and on-demand refresh only. | Keep surface chrome outside the canonical body. |
+| Layer 10 | Resolver symmetry | pass | Both entry points use the same internal quota, overview, credential, badge, and compatibility resolvers because they share the component implementation. | None. |
+
+## Entry-point matrix
+
+| Entry point | Account source | Detail renderer | Compatibility resolver | Status renderer | Surface behavior |
+| ------------------ | ----------------------------- | ---------------------------------- | ------------------------------------ | ---------------------------------------------- | ------------------------------------------------- |
+| Key Vault Settings | `KeyVaultAccount` table row | Shared `AccountInlineDetails` | Shared `AccountCompatibilitySection` | Shared `AccountStatusIndicator` via action bar | Existing inline table expansion |
+| Chat launchpad | `useKeyVault().localAccounts` | Lazy shared `AccountInlineDetails` | Mounted only after selection | Shared `AccountStatusIndicator` in footer | Refresh on selection, inline expansion after tile |
+
+## Default and async branch matrix
+
+| Condition | Result |
+| ----------------------------------- | ----------------------------------------------------------------------- |
+| No selected account | No detail code mount and no compatibility request |
+| Select an account | Mark selected, refresh that account, dynamically import detail renderer |
+| Select the same account again | Collapse the detail surface |
+| Select B before A refresh completes | B remains loading; A's completion cannot clear B's loading state |
+| Refresh fails | Loading clears and the existing cached account remains available |
+
+No wire-payload, fallback-chain, FSM, or serialization changes were introduced, and no systematic issue class outside the extracted ownership boundary was found.
diff --git a/docs/architecture-audit-2026-07-12/WorkManagementGitHubLazyListsCache.md b/docs/architecture-audit-2026-07-12/WorkManagementGitHubLazyListsCache.md
new file mode 100644
index 000000000..610096b1c
--- /dev/null
+++ b/docs/architecture-audit-2026-07-12/WorkManagementGitHubLazyListsCache.md
@@ -0,0 +1,43 @@
+# Kanban GitHub lazy lists and cache — architecture audit
+
+## Acceptance criteria
+
+- Kanban fetches open pull requests on entry and fetches closed pull requests only when the Closed or Merged view is selected.
+- Closed pull-request results include both closed-unmerged and merged pull requests.
+- Issues, open PRs, and closed PRs reuse one shared global list cache rather than maintaining Workstation- and Ops-specific copies.
+- Fresh cache entries suppress repeat list requests for 10 minutes; manual refresh bypasses the TTL for the visible state.
+- Open and closed issue freshness are tracked independently so refreshing one state cannot extend the other state’s lifetime.
+- Rapid unmount/remount cycles share an in-flight request, and completed/rejected promises are removed without timers or subscriptions.
+- Cache hydration and writes are both LRU-bounded: four issue repositories, eight PR repo/state lists, and twenty PR details.
+- Kanban retains only one active page/query snapshot per GitHub scope and evicts it on access after 10 minutes.
+- Switching between Issues, PRs, and other Kanban sections restores the active page without retaining component trees.
+- Targeted ESLint, TypeScript, cache tests, query-state tests, and pagination tests pass.
+
+## Ten-layer audit
+
+| Layer | Coverage | Verdict | Evidence / reason |
+| ------------------------------------- | ----------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| 1. Compilation correctness | TypeScript and changed-file lint | pass | `tsc --noEmit`, targeted ESLint, and nine focused tests pass. Rust is unchanged. |
+| 2. Dead code / structural duplication | GitHub list-cache ownership | pass | The cache moved from a Workstation hook directory to `services/git`; My Station and Kanban now import the same live implementation. |
+| 3. Naming consistency | Cache, state, and view terminology | pass | `openPrs`, `closedPrs`, `openLoaded`, `closedLoaded`, and `OpsGitHubViewSnapshot` distinguish remote state, load lifecycle, and UI position. |
+| 4. Semantic overloading | “page”, “state”, “cache”, and “closed” | pass | Page means the 25-row client page; PR state means GitHub open/closed request state; Closed UI intentionally includes GitHub `merged` results returned by the closed endpoint. |
+| 5. Default branches | PR query-state resolution | pass | Open/null resolves only open, Closed/Merged resolves only closed, and explicit All resolves both; focused tests cover every branch. |
+| 6. Cross-domain leakage | Workstation ↔ Kanban | pass | Data caching is owned by the shared Git service. Ops-only query/page snapshots remain local to Kanban and do not leak UI concerns into the service. |
+| 7. New-developer clarity | Cache lifetime and cleanup | pass | Constants document the exact 10-minute TTL and LRU bounds; in-flight request cleanup and read-time snapshot expiry explain why no background timer is needed. |
+| 8. Wire protocol / serialization | GitHub PR list command and local persistence | pass | The existing `github_list_prs` payload remains `{ repoFullName, state, perPage }`; no Rust command or external schema changed. Bounded list caches continue using versioned localStorage keys. |
+| 9. Init parity | Open, Closed, Merged, All, refresh, and remount entry paths | pass | Every PR list state follows repo resolution → cache seed → TTL decision → coalesced request → cache update. Closed differs only by deliberate lazy activation. |
+| 10. Resolver symmetry | Open/closed issue and PR cache/fetch chains | pass | Issue sections have independent timestamps; both PR states use identical cache keying, staleness checks, request coalescing, network fallback, error fallback, loaded flags, and LRU persistence. |
+
+## Cache bounds and lifecycle
+
+| Cache | Key | Bound | Expiry / cleanup |
+| --------------- | ----------------------------------- | -----------------------------------------------: | -------------------------------------------------- |
+| Issues | repository path | 4 repositories; 200 rows per open/closed section | 10-minute access check; LRU on hydration and write |
+| PR lists | repository path + open/closed state | 8 lists; 100 rows per list | 10-minute access check; LRU on hydration and write |
+| PR details | repository + PR number | 20 details | 10-minute access check; memory-only LRU |
+| Ops view | issue/PR scope | exactly 2 snapshots; one current page/query each | 10-minute read-time eviction; memory-only |
+| In-flight lists | request kind + state + repository | active requests only | deleted in `finally` after resolve or reject |
+
+## Scoped-out layers
+
+No Rust, Tauri command signature, GitHub authentication, issue/PR mutation, database schema, session runtime, or queue lifecycle changed. The cache continues to treat GitHub as authoritative and is bypassed by explicit refresh.
diff --git a/docs/architecture-audit-2026-07-12/WorkManagementGitHubListUnification.md b/docs/architecture-audit-2026-07-12/WorkManagementGitHubListUnification.md
new file mode 100644
index 000000000..9bac6560d
--- /dev/null
+++ b/docs/architecture-audit-2026-07-12/WorkManagementGitHubListUnification.md
@@ -0,0 +1,58 @@
+# Kanban GitHub list unification — architecture audit
+
+## Acceptance criteria
+
+- [x] Issues and PRs use one list frame, row shell, summary component, and pager.
+- [x] Issue-only and PR-only metadata remain explicit at their call sites.
+- [x] Sidebar presets use the existing shared tree-row primitive.
+- [x] The Issues filter uses the same normal-section height allocation as Views.
+- [x] PRs do not create an empty second-level sidebar section.
+- [x] Open/create/refresh actions use one shared component in the search toolbar.
+- [x] The redundant search-row result count is removed.
+- [x] Pagination policy is a pure tested module.
+- [x] No parallel sidebar filter state is introduced; search text remains canonical.
+- [x] Issue and PR detail views hide the sidebar without mutating the saved collapse preference.
+- [x] TypeScript, targeted lint, tests, formatting, and whitespace checks pass.
+
+## Ten-layer audit
+
+| Layer | Coverage | Verdict |
+| ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
+| 1. Compilation correctness | Full frontend TypeScript check plus targeted ESLint. | Pass. |
+| 2. Dead code and structural deduplication | Traced the rendered path from `GitHubWorkItemsSurface` to both issue and PR rows. Removed the local hand-styled filter list, segmented-pill path, duplicate toolbar action markup, empty PR sidebar host, and auto-height global-section path; wired every new shared component into its applicable scopes. | Pass; no aspirational abstraction remains. |
+| 3. Naming consistency | Shared exports consistently use the `GitHubWorkItem*` prefix. Pagination helpers keep the same domain prefix. | Pass. |
+| 4. Semantic overloading | `work item` means a GitHub issue/PR only inside this local Kanban module; ORG2 project work items remain separate. `filter` in the sidebar is represented as a preset that writes the canonical GitHub search query. | Keep local naming; do not promote these types into the project-work-item domain. |
+| 5. Default branch analysis | Preset selection uses explicit assigned/authored/closed/all branches. Unknown keys do not mutate the query, and callers can only supply declared options. | Pass for the closed option set. |
+| 6. Cross-domain concept leakage | Shared components accept render slots and labels; they do not import issue or PR API models. `TreeRowBase` remains unaware of GitHub query semantics. | Pass. |
+| 7. New-developer confusion | `GitHubWorkItemRow`, `GitHubWorkItemSummary`, `GitHubWorkItemPagination`, `GitHubWorkItemSidebarFilters`, and `GitHubWorkItemToolbarActions` identify layout ownership directly. Domain metadata stays in `ManagedIssueRow` and `ManagedPrRow`; `onDetailViewChange` explicitly names the only surface-to-shell state projection. | Pass. |
+| 8. Wire protocol and serialization | No wire payload, API request shape, or serialization changed. Existing GitHub responses are only rendered and client-filtered. | Intentionally not applicable. |
+| 9. Init parity | No new entry point or initialization flow. Issue and PR surfaces continue through the same component entry point and scope prop. | Intentionally not applicable. |
+| 10. Resolver symmetry | No multi-source resolver changed. Repository and query resolution remain shared before scope-specific rendering. | Intentionally not applicable. |
+
+## Term overloading table
+
+| Term | Meaning here | Adjacent meaning | Decision |
+| --------- | ------------------------------------------------------ | ----------------------------------------------------------- | ------------------------------------------------------------- |
+| Work item | A list-renderable GitHub issue or PR. | An ORG2 project work item. | Keep the `GitHubWorkItem` prefix on every shared symbol. |
+| State | GitHub open/closed query state. | UI loading/detail state and ORG2 work-item lifecycle state. | Keep `GITHUB_QUERY_STATE` scoped to this module. |
+| Filter | A named sidebar preset that rewrites the search query. | Repository selector and free-text qualifiers. | Keep one canonical serialized query; do not add filter atoms. |
+
+## Default branch matrix
+
+| Preset | State | Assignee | Author |
+| -------------- | ------ | -------- | ------- |
+| Assigned to me | open | `@me` | cleared |
+| Created by me | open | cleared | `@me` |
+| Closed | closed | cleared | cleared |
+| All states | all | cleared | cleared |
+
+## Structural sweep
+
+- Searched the Kanban GitHub surface for `TabPill`, the old quick-filter arrays, and the local `FilterOptionList`; no obsolete path remains.
+- Both issue and PR rows render through `GitHubWorkItemRow`.
+- Both scopes render through `GitHubWorkItemListFrame` and `GitHubWorkItemPagination`.
+- Sidebar filter rendering uses `TreeRowBase`, matching the existing Views list rather than duplicating its Tailwind styling.
+- Issues alone publish second-level sidebar content, containing only shared tree rows with no custom inset wrapper and using the same `flexGrow: 1` section allocation as Views.
+- Both Issues and PRs render search-toolbar actions through `GitHubWorkItemToolbarActions`.
+- No search-row result-count label remains.
+- Detail openness has one source in `GitHubWorkItemsSurface`; the parent shell only derives sidebar collapse and toggle disabled state from it.
diff --git a/docs/architecture-audit-2026-07-12/WorkManagementSidebarRemoval.md b/docs/architecture-audit-2026-07-12/WorkManagementSidebarRemoval.md
new file mode 100644
index 000000000..a7e4f032b
--- /dev/null
+++ b/docs/architecture-audit-2026-07-12/WorkManagementSidebarRemoval.md
@@ -0,0 +1,38 @@
+# Kanban sidebar removal — architecture audit
+
+Scope: remove the nested Kanban primary-sidebar implementation after moving its destinations into the expandable app-sidebar item.
+
+## Acceptance criteria
+
+- [x] Kanban has no nested `WorkStationShell` or primary-sidebar configuration.
+- [x] No production reference remains to the removed sidebar component, width/collapse atoms, responsive helper, or focused hook.
+- [x] Kanban and Work Items destination selection has one source of truth: the existing internal Kanban section/project-view atoms.
+- [x] Chat-pane titles and icons derive from that same active section and expose only destination names.
+- [x] TypeScript compilation, targeted lint, and targeted navigation/Kanban tests pass.
+
+## 10-layer audit
+
+| Layer | Coverage | Verdict | Evidence / reason |
+| --------------------------------------- | -------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| 1. Compilation correctness | Covered | pass | Full `tsc --noEmit` passes after deleting the component, hook, atoms, and re-exports. No Rust was touched. |
+| 2. Dead code & structural deduplication | Covered | fix | Removed `WorkManagementSidebar`, responsive collapse helper/test, Ops-specific persisted width/collapse atoms, `useWorkManagementSidebarState`, and both re-export paths. A repository sweep confirms no remaining production references. |
+| 3. Naming consistency | Covered | fix | User-facing chat-pane names now match the navigation destinations: Kanban, Projects, GitHub Issues, and GitHub PRs. Internal `work-management` IDs remain stable implementation identifiers. |
+| 4. Semantic overloading | Covered | fix | Removed the visible product aliases from the chat pane. `work-management` is only the internal singleton host/type name, while each visible destination has one product label. |
+| 5. Default branch analysis | Covered | pass | Destination and tab-title mappings explicitly cover Kanban, Projects, GitHub Issues, and GitHub PRs. An absent or unknown section safely resolves to Kanban. |
+| 6. Cross-domain concept leakage | Covered | pass | Work Items navigation remains in the Workstation sidebar connector and Work Management module; shared Workstation panel state no longer carries route-only management state. |
+| 7. New-developer confusion | Covered | fix | Removed the misleading parallel `usePrimarySidebarState` / `useWorkManagementSidebarState` APIs. The expanded parent/child menu now expresses the visible hierarchy directly. |
+| 8. Wire protocol & serialization | Covered | fix | No network schema changed. The versioned ChatPanel storage key intentionally discards obsolete management-tab identities instead of migrating them. |
+| 9. Init parity | Not applicable | skipped | No initialization entry point or runtime registration path changed. |
+| 10. Resolver symmetry | Not applicable | skipped | No multi-source resolver or fallback chain changed. |
+
+## Term-overloading check
+
+| Term | Before | After | Verdict |
+| -------------- | ---------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | --------- |
+| Kanban sidebar | Could mean the global app-sidebar item or the nested resizable rail | User-facing navigation is Kanban plus expandable Work Items; no nested sidebar remains | clarified |
+| View | Mixed destination navigation with Kanban/List/Diary presentation modes | Kanban is top-level; Work Items expands to the existing project/work-item list and related destinations; presentation modes are in the 40px header | clarified |
+| Management tab | Previously displayed under multiple product aliases | The chat tab always displays its active destination: Kanban, Projects, GitHub Issues, or GitHub PRs | clarified |
+
+## Systematic sweep
+
+Searched for `WorkManagementSidebar`, `workManagementResponsiveLayout`, `useWorkManagementSidebarState`, `workStationWorkManagementSidebar`, `work_management_sidebar_width`, and `work_management_sidebar_collapsed`. All production definitions, imports, re-exports, tests, and persistence reads/writes were removed. Historical documentation was left intact as an audit trail.
diff --git a/docs/architecture-audit-2026-07-12/launchpad-manage-consolidation.md b/docs/architecture-audit-2026-07-12/launchpad-manage-consolidation.md
new file mode 100644
index 000000000..b0057cf3a
--- /dev/null
+++ b/docs/architecture-audit-2026-07-12/launchpad-manage-consolidation.md
@@ -0,0 +1,35 @@
+# Launchpad Manage consolidation architecture audit
+
+## Acceptance criteria
+
+- Explore is renamed to Manage in all supported locales.
+- The former workspace Dashboard renders only inside Launchpad Manage.
+- No standalone Dashboard ChatPanel tab type, creator atom, icon branch, or plus-menu entry remains.
+- Persisted Dashboard tabs migrate to Launchpad.
+- Folders Dashboard navigation focuses Launchpad Manage without duplicating a Launchpad tab.
+- Manage is lazy-loaded and unmounted when Work or Trends is selected.
+- Work actions reuse the original rounded pill geometry; tones remain on containers and icons remain neutral.
+- TypeScript, targeted ESLint, and focused state/UI tests pass.
+
+## Ten-layer audit
+
+| Layer | Coverage | Verdict | Evidence / reason |
+| ------------------------------------- | --------------------------------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| 1. Compilation correctness | TypeScript and changed-file lint | pass | `tsc --noEmit` and targeted ESLint complete successfully. Rust is untouched. |
+| 2. Dead code / structural duplication | Dashboard tab and render path | pass | Removed the `dashboard` ChatPanel tab variant, add-tab atom, icon/menu branches, display resolver case, and standalone `ChatPanelContent` render branch. |
+| 3. Naming consistency | Start-page tabs | pass | The canonical inner identities are now `work`, `manage`, and `heatmap`; user-facing labels are Work / Manage / Trends. |
+| 4. Semantic overloading | Dashboard vs. Launchpad | pass | Dashboard is retained only as a legacy navigation/migration term; workspace management UI has one live owner under Launchpad Manage. |
+| 5. Default branches | Initial tab and legacy navigation | pass | Launchpad defaults to Work. Legacy Dashboard navigation redirects to Manage, and persisted Dashboard tabs normalize to `start-page`. |
+| 6. Cross-domain leakage | Launchpad ↔ workspace management | keep with reason | `WorkspaceDashboardPanelView` remains a thin adapter over the shared launchpad module; ChatPanel owns only lazy hosting and inner-tab lifecycle. |
+| 7. New-developer clarity | Open/focus actions | pass | `openOrFocusChatPanelManageTabAtom` states its behavior and centralizes sidebar-to-Manage navigation. |
+| 8. Wire protocol / serialization | Local tab persistence | pass | No external protocol changes. LocalStorage normalization explicitly maps old `dashboard` and `launchpad` tab identities to Launchpad. |
+| 9. Init parity | Plus menu, new session, sidebar | pass | New session and Launchpad entry points reset to Work; Folders Dashboard focuses Manage; all paths use the same start-page state atom. |
+| 10. Resolver symmetry | Tab title, sidebar selection, inner tab | pass | Launchpad title resolution comes from `start-page`; folder Dashboard selection derives from `start-page + manage`; no Dashboard tab resolver remains. |
+
+## Lifecycle / memory boundary
+
+`WorkspaceDashboardPanelView` is declared with `React.lazy` and rendered only by the `manageTabActive` conditional. React unmounts it when the active inner tab changes, releasing its repo, key-vault, agent-catalog, container, and container-engine subscriptions plus local selection state. The loaded JavaScript chunk remains browser-cached, as expected, but the heavy live component graph is not retained.
+
+## Scoped-out layers
+
+No Rust, database, external wire protocol, session launch protocol, or container backend behavior changed. Workspace detail and legacy Explore surfaces remain available through their existing sidebar paths.
diff --git a/docs/architecture-audit-2026-07-12/work-management-chat-tab.md b/docs/architecture-audit-2026-07-12/work-management-chat-tab.md
new file mode 100644
index 000000000..8d95d6b0a
--- /dev/null
+++ b/docs/architecture-audit-2026-07-12/work-management-chat-tab.md
@@ -0,0 +1,48 @@
+# Kanban → ChatPanel tab migration architecture audit
+
+## Acceptance criteria
+
+- The Workstation no longer mounts a dedicated Kanban station view or tab bar.
+- Kanban is a singleton ChatPanel tab; Projects is an internal section of that tab.
+- The active management tab is the source of truth for its title, rendered content, and app-sidebar selection.
+- The active management tab also drives the outer Workstation sidebar highlight; no dedicated route or route-only station mode remains.
+- Every tab pill resolves from its canonical tab type or linked entity; surface-header and globally active-session titles cannot override another tab's identity.
+- Selecting a session in the Workstation sidebar focuses its existing session tab or creates and activates one; Launchpad cannot remain the active tab while session content opens.
+- The Kanban tab defaults to full-screen ChatPanel presentation on entry and restores the user's prior maximize state on exit when the user has not explicitly restored the Workstation.
+- The management tab keeps the top tab bar and maximize/Workstation toggle so full screen remains user-reversible, while suppressing the focused Workstation rail.
+- Launchpad names the Work / Explore / Trend start page; Dashboard names the workspace summary surface and uses an Info icon.
+- Every ChatPanel tab is closable; closing the final tab creates and activates the three-section Launchpad.
+- Closing Kanban disposes its transient creator, preview, replay-event, playback, and header state while retaining persisted user preferences.
+- The chat-tab storage key is versioned; stale management tabs are discarded instead of migrated.
+- The app sidebar owns Kanban and Work Items navigation; List and Diary remain presentation modes in the 40px content header.
+- Shortcut, Spotlight, action, Start Page, plus-menu, and sidebar entry points converge on one `openKanbanTab()` service path.
+- Removed Workstation tab types/renderers have zero remaining references.
+- Targeted ESLint, TypeScript, and tab-state tests pass.
+
+## Ten-layer audit
+
+| Layer | Coverage | Verdict | Evidence / reason |
+| ------------------------------------- | ------------------------------------------------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| 1. Compilation correctness | TypeScript + changed-file lint | pass | `pnpm exec tsc --noEmit --pretty false` and targeted ESLint complete with zero errors. Rust is untouched. |
+| 2. Dead code / structural duplication | Old station path, duplicate identity, retained UI state | pass | Deleted the retired management route, route-only station mode, peek/focus atoms, and Projects tab type; transient cleanup remains centralized in the canonical tab-close path. |
+| 3. Naming consistency | Chat tab and service names | pass | `start-page` is the Launchpad with Work / Explore / Trend; `dashboard` is the workspace summary; `work-management` owns both management sections under one visible tab identity. |
+| 4. Semantic overloading | `launchpad`, `dashboard`, `project`, `station`, `tab` | pass | Launchpad and Dashboard no longer label the same surface, surface-header context no longer doubles as tab identity, and Projects is explicitly an inner management section rather than another tab. |
+| 5. Default branches | Tab activation, presentation, and empty-tab fallback | pass | Every tab variant explicitly synchronizes its surface state; management/terminal tabs use Session only as a neutral underlying surface. Final close explicitly activates Launchpad. |
+| 6. Cross-domain leakage | ChatPanel ↔ Kanban | keep with reason | ChatPanel owns surface identity/presentation; Kanban continues to own its sidebar and management content. The shell lazy-load is an intentional host boundary, not duplicated domain logic. |
+| 7. New-developer clarity | Entry points and ownership | pass | `openKanbanChatPanelTabAtom`, `isChatPanelTabDefaultFullscreen`, and `openKanbanTab` distinguish entry defaults from enforced presentation. |
+| 8. Wire protocol / serialization | External payloads and local persistence | pass | No backend protocol changed. The versioned ChatPanel storage key intentionally drops obsolete management-tab identities; terminal tabs remain excluded from persistence. |
+| 9. Init parity | Shortcut, Spotlight, action, plus menu, sidebar | pass | All Kanban entry points converge on one tab atom; session rows focus linked tabs, while sidebar New Chat resets the draft and then creates and activates the localized Launchpad tab. |
+| 10. Resolver symmetry | Tab presentation and management selection | pass | Kanban and Projects follow the same activation chain; title, tab identity, content section, and outer sidebar highlight all resolve from the same active tab. |
+
+## Entry-point parity matrix
+
+| Entry point | Opens singleton tab | Makes chat visible | Defaults full screen | Workstation restorable | Selects section |
+| ----------------------------- | ------------------: | -----------------: | -------------------: | ---------------------: | --------------: |
+| Shortcut / Action / Spotlight | yes | yes | yes | yes | yes |
+| Start Page | yes | yes | yes | yes | yes |
+| ChatPanel `+` menu | yes | already visible | yes | yes | yes |
+| App sidebar | yes | already visible | yes | yes | yes |
+
+## Scoped-out layers
+
+No Rust, database, session initialization, external wire protocol, queue lifecycle, or resolver logic changed. Those skill checklist areas were inspected for applicability and intentionally skipped beyond the explicit Layer 8–10 statements above.
diff --git a/docs/architecture-audit-2026-07-12/work-management-github-consolidation.md b/docs/architecture-audit-2026-07-12/work-management-github-consolidation.md
new file mode 100644
index 000000000..96b20f1a0
--- /dev/null
+++ b/docs/architecture-audit-2026-07-12/work-management-github-consolidation.md
@@ -0,0 +1,38 @@
+# Kanban GitHub consolidation architecture audit
+
+## Acceptance criteria
+
+- Chat Pane has no Manage Issues action, render branch, navigation command, atom, or surface reducer state.
+- GitHub issue/PR ownership lives under `modules/MainApp/WorkManagement`.
+- Kanban exposes distinct typed `github-issues` and `github-prs` inner sections.
+- Sidebar selection, active content, and the singleton management tab resolve from the same `managementSection` value.
+- Repository/options/filters remain visible in a left sub-pane while results render on the right.
+- Selecting an issue or PR opens a second-level detail inside the right pane.
+- Issues and PRs share one data/filter implementation but fetch only the active scope.
+- TypeScript, targeted ESLint, focused tests, and whitespace checks pass.
+
+## Ten-layer audit
+
+| Layer | Coverage | Verdict | Evidence / reason |
+| ------------------------------------- | ----------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| 1. Compilation correctness | TypeScript and changed-file lint | pass | `pnpm typecheck` and targeted ESLint complete with zero errors. Rust is untouched. |
+| 2. Dead code / structural duplication | Former Chat Pane Manage Issues surface | pass | Removed the Chat Pane lazy render, start-page and plus-menu entries, content-state branch, navigation callback, atom, surface variant, reducer field/case, and adjacent Workstation visibility checks. A global sweep finds zero old symbol references. |
+| 3. Naming consistency | Surface and detail ownership | pass | The implementation is now `WorkManagement/GitHubWorkItemsSurface`; Chat-specific detail state/handlers and the Chat-specific storage key were renamed. |
+| 4. Semantic overloading | `Manage Issues`, Issues, PRs, Ops section | pass | The generic mixed Chat surface is replaced by explicit `GITHUB_ISSUES` and `GITHUB_PRS` section identities while sharing only implementation, not navigation meaning. |
+| 5. Default branches | Ops content selection | pass | Projects, GitHub Issues, and GitHub PRs are explicit branches; the default remains the established Ops task surface. No GitHub section silently falls through to Kanban. |
+| 6. Cross-domain leakage | Chat Panel ↔ Kanban | pass | The 2,000-line GitHub implementation moved out of `engines/ChatPanel` into the Kanban module. Shared issue detail and add-to-agent services remain imported at their existing reusable boundaries. |
+| 7. New-developer clarity | State and entry points | pass | Sidebar node kinds, home-tab constants, renderer branches, and the surface `scope` prop all name Issues and PRs directly. There is one visible owner. |
+| 8. Wire protocol / serialization | GitHub commands and local persistence | pass | Backend GitHub payloads are unchanged. Only the local repository-filter preference key changes to an Ops-owned name; no external serialized contract changes. |
+| 9. Init parity | Issues and PRs entry paths | pass | Both sidebar entries call `openKanbanChatPanelTabAtom`, activate the singleton management tab, publish through the Ops header host, resolve repositories identically, and mount the same scoped surface. |
+| 10. Resolver symmetry | Section, query, list, detail | pass | The active Ops `managementSection` drives sidebar selection and main content; `scope` initializes the matching search query, filters one item kind, fetches one API family, and resets incompatible detail state on change. |
+
+## Entry-point and ownership matrix
+
+| Entry point | Canonical tab | Inner section | Data scope | Detail host |
+| --------------------------- | ---------------- | --------------- | ----------- | ------------------ |
+| Ops sidebar → GitHub Issues | singleton Kanban | `github-issues` | issues only | right results pane |
+| Ops sidebar → GitHub PRs | singleton Kanban | `github-prs` | PRs only | right results pane |
+
+## Scoped-out layers
+
+No Rust, database schema, GitHub command payload, authentication, issue mutation semantics, session dispatch, or queue lifecycle behavior changed. The existing translation strings remain reusable content labels even though the live surface owner moved to Kanban.
diff --git a/docs/architecture-audit-2026-07-13/AgentOrgModernFamilyRecovery.md b/docs/architecture-audit-2026-07-13/AgentOrgModernFamilyRecovery.md
new file mode 100644
index 000000000..58214e46e
--- /dev/null
+++ b/docs/architecture-audit-2026-07-13/AgentOrgModernFamilyRecovery.md
@@ -0,0 +1,82 @@
+# Agent Org “Modern Family” 五批修复架构审计
+
+日期:2026-07-13
+分支:`fix/issue-272-agent-org-recovery-invariants`
+范围:用户实测中出现的根 Coordinator 错误进入 Plan、任务链创建不完整、消息绕过任务、Reviewer 尚在运行却提前收尾、暂停与空筛选红错。
+结论:五批修复已接入生产工具装配和持久化路径;本范围没有遗留的 P0/P1 问题。审计额外发现并修复了“任务图检查与落库之间可能插入一张并发任务”的窄竞态。
+
+## 验收标准与结果
+
+| 用户看到的问题 | 必须成立的新行为 | 结果 |
+| ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | ---- |
+| Group chat 要求用户切 Plan mode | 活跃 Agent Org 的根 Coordinator 不暴露通用 mode-switch 工具;计划由带 `execution_mode=plan` 的成员任务承载 | 通过 |
+| Plan / 写作 / Review 的卡片对不上 | Coordinator 可用一个 `task_graph_create` 原子写入完整动态依赖图;失败时零任务落库 | 通过 |
+| 创建 Review task 失败后,Coordinator 仍用聊天叫 Reviewer 干活 | 发给 Worker 的正式 plain 消息必须绑定真实、未完成、依赖已就绪且对收件人有权限的 `related_task_id` | 通过 |
+| Reviewer 还在 Running,Coordinator 已宣布全部完成 | `task_list.run_summary.completion_ready` 同时检查 Task、Session、Inbox、Turn intent、Intervention 和 Plan approval | 通过 |
+| `status=""` 形成红色工具失败;Pause 误伤未启动成员 | 空 status 当作未筛选;`OrgPause` 不把没有 live runtime 的惰性成员修成 Failed | 通过 |
+
+## 十层架构审计
+
+| Layer | Line / Element | Verdict | Reason | Suggested change |
+| ---------------- | ---------------------------------------------------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- |
+| 1 编译与验证 | Rust / TypeScript / task tests | keep | `cargo check -p org2`、typecheck、格式和 diff 检查通过;任务工具 58/58、task store 43/43、消息 14/14 通过。 | 无。 |
+| 1 编译与验证 | `cargo test -p agent_core --lib` | keep with reason | 2995/2997 通过;剩余 skill-content 与 search-tool 两项是本分支之前已存在且与 Agent Org 无关的基线。 | 另开基线清理,不混入本修复。 |
+| 1 编译与验证 | strict Clippy | keep with reason | 新增任务图、消息门和完成证书没有产生 Clippy 诊断;全 crate 仍被 Provider、Memory、Channel 等既有告警阻塞。 | 另开 Clippy cleanup PR。 |
+| 2 活代码/重复 | `task_dependency_closure` | fixed | 单任务、批量任务和 transaction-time recheck 曾各自可能定义“依赖覆盖”;现共享一个 closure 算法。 | 无。 |
+| 2 活代码/重复 | `task_graph_create` production/debug/test wiring | keep | 工具已接到 builtin metadata、policy、production overlay、debug runtime、test API、Rust/TS tool names 与前端事件识别,不是只在测试里存在。 | 无。 |
+| 3 命名 | `TaskGraphCreate` / `related_task_id` / `completion_ready` | keep | 名称分别表示“原子任务图”“消息所属任务”“完成证书”,没有用含糊的 `state` 或 `done`。 | 无。 |
+| 4 语义维度 | Run / Session / Task / Delivery / Approval | keep | Run 是否继续、成员是否正在工作、任务是否完成、消息是否已送达、计划是否获批分别保存;不再用 `open=0` 猜整个组织完成。 | 无。 |
+| 5 FSM 完整性 | `session_is_quiescent_for_completed_run` | keep | 每个 `SessionStatus` 显式分类;Running、Pending、Waiting、Paused 都会阻止完成证书。 | 新增状态时继续让编译器强制补齐 match。 |
+| 5 FSM 完整性 | task dispatch | keep | 只有依赖已完成的根任务收到 assignment;下游由真实 TaskCompleted 事件解锁。 | 无。 |
+| 6 跨域边界 | root Plan vs member Plan task | fixed | 通用 root mode switch 不再参与活跃 Agent Org 编排;用户显式以 Plan 启动普通 root session 的能力仍保留。 | 无。 |
+| 6 跨域边界 | Chat routing vs task authority | fixed | Hierarchy Mode 仍决定“谁能联系谁”;Task authority 决定“谁能给谁派活”。可聊天不等于可绕过任务。 | 无。 |
+| 7 新开发者理解 | Coordinator prompt / tool descriptions | fixed | Prompt 解释原子图、动态依赖、task-bound message、Plan task 和 completion certificate;非代码写作不会因工具存在就误入 GitHub issue workflow。 | 后续修改继续保留字符串契约测试。 |
+| 8 Wire / schema | Rust + TS `TASK_GRAPH_CREATE` | keep | 工具名、provider JSON schema、event extraction 与前端识别一致;schema compatibility 测试通过。 | 无。 |
+| 8 Wire / schema | recoverable guidance | keep | 依赖遗漏、plain 消息缺 task、空 after-dependencies 等返回结构化 guidance,不制造用户可见的红色失败卡。 | 长期可由 UI 渲染成专用提示卡。 |
+| 9 初始化一致性 | production overlay / debug runtime / test API | keep | Coordinator 才注册跨成员 graph tool;所有测试与调试入口使用同一实现。 | 无。 |
+| 10 Resolver 对称 | member owner / eligibility / recipient | keep | owner、eligible member 与 message recipient 都从 run roster 的稳定 member_id 解析;不接受 display name 猜测。 | 无。 |
+
+## 审计过程中额外修复的竞态
+
+原本工具会先读取当前开放任务,再决定新图是否需要依赖它们,然后进入 transaction 插入新图。极窄情况下,另一条执行流可以刚好在两步之间新增任务:新图本身仍是完整的,但它会漏掉刚出现的开放工作。
+
+现在 store 在真正写入的同一笔 SQLite IMMEDIATE transaction 里再检查一次:
+
+```mermaid
+flowchart LR
+ A["工具预检查\n给模型友好 guidance"] --> B["取得统一 writer lock"]
+ B --> C["IMMEDIATE transaction\n重读现有 open tasks"]
+ C -->|"依赖覆盖完整"| D["验证整张图与环"]
+ D --> E["一次写入 tasks + history"]
+ C -->|"发现并发新增的遗漏任务"| F["整笔回滚\n返回重新确认 guidance"]
+```
+
+回归测试确认:事务内发现遗漏时,数据库只保留原来的任务,不会留下半张新图。
+
+## 最终流程
+
+```mermaid
+flowchart TD
+ U["用户在 Group chat 提交目标"] --> C["Coordinator 保持 Build\n设计动态工作图"]
+ C --> G["task_graph_create\n一次写入 Plan / Write / Review / Final 等实际需要的节点"]
+ G --> R["只唤醒依赖已满足的根任务"]
+ R --> M["Member 完成自己的 Task\n写 durable output"]
+ M --> N["后端解锁真正依赖它的下游 Task"]
+ N --> M
+ M --> S["Coordinator 调用 task_list"]
+ S --> Q{"completion_ready?"}
+ Q -->|"false"| W["按 blocker 等待真实事件\n不假完成、不空 Wake"]
+ W --> S
+ Q -->|"true"| F["向用户输出最终结果"]
+```
+
+## 明确保留的边界
+
+1. 依赖链不是写死的 `Planner → Implementer → Reviewer → Tester`。Coordinator 根据本次请求动态生成;无依赖的任务可以并行,消费上游产物的任务必须声明依赖。
+2. `HierarchyMode::Soft` 没有被删除。它管理通信可达性,不授予跨成员任务修改权。
+3. `completion_ready` 是给 Coordinator 的持久化完成证书,不是新的大模型,也不取代 Coordinator 的判断和最终表达。
+4. 本报告不背书当前 dirty worktree 中 Git、Key Vault、Provider、其他 UI 等无关改动;本轮没有修改 `*.tsx`,因此没有新增 frontend-ui-audit 报告。
+
+## 最终判断
+
+这五批把 Agent Org 从“模型靠聊天和局部看板猜下一步”收紧为“Coordinator 设计任务图,数据库保证任务与依赖,消息只能补充上下文,完成必须拿到多维证书”。Modern Family 实测暴露的三条错误捷径——根 Plan、消息绕任务、Reviewer 未完先收尾——都已在代码边界被阻断,而不是只靠提示词劝模型别犯错。
diff --git a/docs/architecture-audit-2026-07-13/AgentOrgPlannerApproval.md b/docs/architecture-audit-2026-07-13/AgentOrgPlannerApproval.md
new file mode 100644
index 000000000..ffbeac8b0
--- /dev/null
+++ b/docs/architecture-audit-2026-07-13/AgentOrgPlannerApproval.md
@@ -0,0 +1,89 @@
+# Agent Org Planner、审批与 Wake 加固架构审计
+
+日期:2026-07-13
+范围:#272 恢复链路,以及本轮五批 Planner 任务、动态依赖、审批、Wake 和 Group chat 状态投影。
+结论:本范围内没有遗留的 P0/P1 架构问题;审计发现的三组“数据库写了一半”风险已经改成单事务。工作区中其余 Git、Key Vault、Provider 等改动不属于本报告,也没有被本轮修改或背书。
+
+## 验收结果
+
+| 检查 | 结果 | 说明 |
+| ----------------------- | --------------------------- | -------------------------------------------------------------------------------------- |
+| Rust 编译 | 通过 | `cargo check -p org2` |
+| TypeScript | 通过 | `npm run typecheck` |
+| Agent Org approval 单测 | 11/11 | 包含审批、退回、编辑、暂停、自动审批与事务回滚 |
+| `create_plan` 单测 | 7/7 | 包含空标题、空内容、错误 session 与动态说明 |
+| Agent Org 真实桌面 E2E | 通过 | task-driven Plan → 审批 → 下游解锁 |
+| `agent_core` 全量单测 | 2984 通过,2 个既有基线失败 | 沙箱外运行后端口测试正常;剩余两项是无关的 skill 内容与 search 参数冲突基线 |
+| Clippy | 项目基线未清零 | 全依赖被既有 dependency warning 阻塞;`--no-deps` 有既有警告。本轮新增的两条警告已修掉 |
+| Diff 空白检查 | 通过 | `git diff --check` |
+
+## 十层审计
+
+| Layer | Line / Element | Verdict | Reason | Suggested change |
+| -------------------- | --------------------------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------- |
+| 1 编译 | `src-tauri` / frontend | keep with reason | Rust 与 TypeScript 均编译;本轮新增代码没有留下项目 warning。全仓 Clippy 仍受既有基线影响。 | 本 PR 不顺手清理无关 Clippy 基线,单独建 cleanup PR。 |
+| 2 死代码与重复 | `agent_org_plan_approvals.rs:256,286,391` | fixed | Coordinator 请求、自动批准、退回反馈原先存在各写各的风险;现在共享事务 helper。 | 无。 |
+| 2 死代码与重复 | `agent_inbox.rs:714` | fixed | 新增 `insert_in_tx`,审批和 Inbox 不再各开一个 transaction。 | 无。 |
+| 2 死代码与重复 | legacy `ExecModeSetRequest` reader | keep with reason | 当前生产端不再生成这种消息,但仍需读取旧数据库历史行;保留的是反序列化兼容,不是第二套调度机制。 | 等明确放弃旧本地 DB 兼容时再删除。 |
+| 3 命名 | `agent_org_tasks::TaskExecutionMode` | keep | 名字明确表示“这张任务卡下一次应以 Plan 还是 Build 执行”,不会和整个 session 的通用执行模式混淆。 | 无。 |
+| 3 命名 | `AgentOrgPlanInboxDelivery` | keep | 类型明确表达“审批状态与哪封持久信件一起提交”,没有用模糊的 `Result`/`Context`。 | 无。 |
+| 4 语义维度 | Run / Session / Task / Approval / Delivery | keep | 五个状态分开保存:run 是否继续、session 是否工作、task 是否完成、plan 是否获批、inbox/wake 是否送达,互不冒充。 | 继续要求新 UI 标明自己显示的是哪一维。 |
+| 4 语义维度 | `org_tasks.rs:118` `AwaitingPlanApproval` | keep | 这是从 durable approval 投影出的界面阶段,不会把 Run 改成 Paused,也不会把 Task 假装 Completed。 | 无。 |
+| 5 默认分支 | `PlanApprovalPolicy` | keep | `Coordinator`、`User`、`Automatic` 三种策略在创建与提示词中显式匹配;没有 `_ => automatic` 之类危险兜底。 | 新增策略时让编译器强制所有 match 补齐。 |
+| 5 默认分支 | historical missing `execution_mode` | keep with reason | 旧 task / inbox 行缺字段时按 Build 读取,只用于历史兼容;新 `task_create` 强制显式传值。 | 保持“旧读宽松、新写严格”。 |
+| 6 跨域泄漏 | Agent Org approval vs top-level Plan approval | keep | 两者没有复用同一状态:单 agent 的 Build/Skip 机制保持原样;Agent Org 审批绑定 run + member + source task。 | 无。 |
+| 7 新开发者理解 | `create_plan.rs:168` tool description | fixed | 工具说明现在写清:必须绑定 owned in-progress Plan task;提交后停止;批准负责完成任务;不会制造 fake Build turn。 | 无。 |
+| 7 新开发者理解 | `section_builders.rs:680-768` | fixed | Coordinator 的 prompt 同时解释 dynamic dependency、dispatch policy、execution mode 和审批结果。 | 后续提示词变更继续有字符串契约测试。 |
+| 8 Wire/序列化 | `TaskAssigned.execution_mode` | keep | 扁平 typed field,历史缺失默认 Build;成员从真实 assignment 决定 Plan/Build。 | 无。 |
+| 8 Wire/序列化 | `task_create` schema | fixed | `dispatch_policy` 和 `execution_mode` 都是 required;依赖确认返回结构化 guidance,不制造 trajectory-visible tool failure。 | 无。 |
+| 8 Wire/序列化 | Plan 内容边界 | keep | 审批保存完整 markdown 与 artifact path;注入 Inbox/TaskOutput 的文本有总量边界,避免无界 prompt 膨胀。 | 后续可加 payload-size telemetry,但不是正确性前提。 |
+| 9 Init parity | setup / test env / debug Agent Org fixtures | fixed | 新审批表在 production setup、测试 DB 与 E2E 初始化一致;遗漏的 `plan_approval_policy` fixture 已补齐,`cargo check -p org2` 能抓住。 | 新增 OrgDefinition 字段时继续编译所有 desktop/E2E target。 |
+| 9 Init parity | `external_import/tests.rs` 全局 HOME guard | fixed | 全量并行测试曾能在 lifecycle 建表过程中切走 `ORGII_HOME`,造成“no such table”假失败;所有本地 HOME guard 现先取得 workspace canonical lock。 | 新增 env-mutating test 必须使用 `test_helpers::test_env`。 |
+| 10 Resolver symmetry | owner / member / task / execution mode | keep | `create_plan` 从 runtime member identity 找到同 run 的 owned in-progress Plan task;ownerless task 不参与 mode prepeek,也不会由成员自领。 | 无。 |
+| 10 Resolver symmetry | explicit assignment | fixed | Watchdog、resume、Inbox drain 和 task side effect 都把 ownerless 解释为“等待 Coordinator 指派”;只有真实 `TaskAssigned` 决定 Worker 的下一次执行模式。 | 无。 |
+
+## 事务审计发现并已修复的问题
+
+| 问题类 | 旧风险 | 现在 |
+| -------------------- | --------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
+| 退回计划 | approval 先变 `changes_requested`,写 Planner Inbox 失败后 Planner 永远收不到意见 | approval 状态与反馈信同一 SQLite transaction;任一步失败全部回滚 |
+| Coordinator 审批请求 | 可能有 pending approval,却没有给 Coordinator 的请求信 | approval revision 与请求信同事务创建 |
+| Automatic 审批 | 可能 approval 已 approved,但 source Plan task 仍 in_progress | approval 创建、批准、TaskOutput 与 Plan task 完成在同一 transaction |
+
+对应测试会主动删除 `agent_inbox` 表制造失败,确认数据库不会留下“半成功”状态。这不是只测 happy path。
+
+## 状态与事件边界
+
+```mermaid
+flowchart LR
+ T["Plan Task\nin_progress"] --> P["create_plan\n写 approval revision"]
+ P --> C{"审批策略"}
+ C -->|Coordinator| I["给 Coordinator 一封持久请求信"]
+ C -->|User| U["Group chat 显示审批卡"]
+ C -->|Automatic| A["同事务自动批准"]
+ I --> D{"批准或退回"}
+ U --> D
+ D -->|退回| F["同事务保留 Task + 写 Planner 反馈信"]
+ F --> T
+ D -->|批准| O["同事务写 TaskOutput + 完成 Plan Task"]
+ A --> O
+ O --> N["只解锁真实依赖它的下游任务"]
+```
+
+## Wake 审计结论
+
+- 保留 #272 的恢复能力:未读 Inbox、刚解锁且已有 owner 的任务、审批反馈和受预算控制的真实恢复输入仍可触发 Wake;ownerless task 只提醒 Coordinator 明确指派。
+- 删除“只因还有一个未完成任务就每分钟叫醒”的假输入。
+- 等待用户审批时没有可消费输入,所以不调用模型、不闪烁、不消耗 recovery attempt。
+- Coordinator 或 member 真有未读持久信时仍会 Wake;Wake 只是门铃,Inbox row 才是事实。
+- 同一 member 的并发 Wake 使用确定性 key 合并;只有 Scheduler 真开始 turn 才把 session 写成 Running。
+
+## 保留项与明确边界
+
+1. SQLite 与计划 markdown 文件不能形成跨文件系统的真正原子 transaction。正常写文件失败会报错并保持 DB 未批准;进程在极窄窗口硬崩溃时,DB 中保存的 `plan_content` 仍是权威内容,文件可重建。这是已记录的边界,不是双真相。
+2. 历史 `ExecModeSetRequest` 只读兼容暂留;新代码没有 producer。
+3. 此报告只审计 Agent Org 范围;当前 worktree 的无关用户改动没有被格式化、删除或纳入结论。
+
+## 最终判断
+
+这轮设计已经从“Coordinator 用消息临时命令成员切模式”变为“任务本身携带执行契约”。Planner 的 Plan 模式、审批等待、退回反馈、批准完成和依赖解锁都有持久状态与事务边界;Wake 只负责把真实事件送进 turn,不再负责猜测工作。范围内没有需要阻止提交的审计发现。
diff --git a/docs/architecture-audit-2026-07-13/AppUpdater.md b/docs/architecture-audit-2026-07-13/AppUpdater.md
new file mode 100644
index 000000000..3ab8b09de
--- /dev/null
+++ b/docs/architecture-audit-2026-07-13/AppUpdater.md
@@ -0,0 +1,92 @@
+# Architecture Audit — AppUpdater
+
+**Scope:** `src/scaffold/AppUpdater/`, `general.autoUpdateEnabled`, and the General settings entry
+**Date:** 2026-07-13
+**Auditor:** Codex
+
+## Acceptance criteria
+
+- [x] One lifecycle owner for check, download, and install state
+- [x] One scheduler for startup, interval, foreground, and online triggers
+- [x] Automatic updates default on and can be disabled through persisted settings
+- [x] Startup can install and relaunch; active-use checks silently pre-download
+- [x] Manual checks and installs remain available when automation is disabled
+- [x] Check throttling, force bypass, coalescing, cache failure semantics, progress throttling, install deduplication, and scheduler cleanup are tested
+- [x] Targeted ESLint and updater Vitest suite pass
+
+## 10-layer audit
+
+### Layer 1 — Compilation correctness
+
+- Targeted ESLint passes for every changed TypeScript/TSX file.
+- Updater lifecycle tests pass (12/12); the settings-default test also passes.
+- Full `tsc --noEmit` reaches one pre-existing error at `src/engines/ChatPanel/InputArea/components/ContextInfoButton.tsx:468` (`string | undefined` passed where `string` is required); no updater diagnostic is emitted.
+- Settings UI parity remains red on the `develop` baseline because eight existing `housekeeper.*` keys are not covered by the manifest. The new `general.autoUpdateEnabled` key is covered by the General-section prefix.
+
+### Layer 2 — Dead code and structural deduplication
+
+- Traced automatic entry point: `AppDeferredServices` → `AppUpdater` → `AppUpdaterScheduler` → `runAutomaticUpdate` → `AppUpdaterCoordinator`.
+- Traced manual entry points from Settings, Spotlight/ActionSystem, Sidebar, and ChatPanel.
+- Removed parallel module-level throttle/check/install state. Jotai atoms are projections of coordinator state.
+- Production updater calls now have one owner: `check`, `download`, `install`, and `downloadAndInstall` are invoked only by the coordinator.
+
+### Layer 3 — Naming consistency
+
+- `autoUpdateEnabled` consistently means the persisted user preference.
+- `AutomaticUpdateReason` distinguishes startup, interval, foreground, and online scheduling.
+- Documentation now matches the options-object API and current two-hour interval.
+
+### Layer 4 — Semantic overloading
+
+| Term | Meaning | Verdict |
+| ---------------- | ---------------------------- | -------------------------------------------------------------------- |
+| update | Tauri `Update` resource | Keep; concrete external type |
+| automatic update | Configured scheduling policy | Keep; represented by `autoUpdateEnabled` and `AutomaticUpdateReason` |
+| install | Apply a downloaded package | Keep; distinct from download and relaunch phases |
+
+No conflicting domain meanings remain inside the updater module.
+
+### Layer 5 — Default branch analysis
+
+- `general.autoUpdateEnabled` defaults to `true` in the canonical settings registry and the derived atom also uses `true` as a defensive fallback.
+- Public check defaults remain silent and throttled (`notify: false`, `force: false`).
+- Download-event handling is exhaustive over Tauri's `Started | Progress | Finished` union; there is no unsafe catch-all branch.
+
+### Layer 6 — Cross-domain leakage
+
+- Scheduling and updater resources remain under `scaffold/AppUpdater`.
+- The platform settings atom only adapts the central settings domain; it does not own updater lifecycle state.
+- General Settings only binds the preference to existing design-system controls.
+
+### Layer 7 — New-developer confusion test
+
+- Coordinator, scheduler, and settings preference have separate purpose-based names.
+- Comments explain why active-use automation downloads without installing: Windows installation can terminate the app.
+- The state model and entry points are documented in the component guide.
+
+### Layer 8 — Wire protocol and serialization
+
+- The only persisted wire value is `general.autoUpdateEnabled: boolean`; it is validated by the canonical Zod registry and written through `updateSettingAtom`.
+- The updater release request and signed package protocol remain owned by pinned `@tauri-apps/plugin-updater` 2.9.0; this change adds no custom payload or schema generation.
+- Tauri's `Update` resource is closed when replaced or explicitly cleared to avoid stale native resources.
+
+### Layer 9 — Init parity
+
+| Entry point | Checks setting | Fresh check | Throttle/dedupe | Download | Install | Relaunch |
+| ------------------- | -------------: | -----------------: | --------------: | --------: | ------: | -------: |
+| Startup automatic | Yes | Yes | Yes | Yes | Yes | Yes |
+| Two-hour interval | Yes | Yes | Yes | Yes | No | No |
+| Foreground / online | Yes | If throttle allows | Yes | Yes | No | No |
+| Manual check | No | Yes | Yes | No | No | No |
+| Manual install | No | If cache empty | Yes | If needed | Yes | Yes |
+
+Manual paths intentionally ignore the automation preference so users can update on demand after disabling background behavior.
+
+### Layer 10 — Resolver symmetry
+
+No multi-field fallback resolver was introduced. Both schema default and atom fallback resolve the single automatic-update preference to `true`; there is no asymmetric source chain.
+
+## Systematic sweep
+
+- Swept all updater imports and call sites with `checkForAppUpdates`, `installAvailableAppUpdate`, `useAvailableAppUpdate`, `check`, `download`, `install`, and `downloadAndInstall`.
+- No second production scheduler, updater transport caller, or install-state writer remains.
diff --git a/docs/architecture-audit-2026-07-13/GeminiCliRemoval.md b/docs/architecture-audit-2026-07-13/GeminiCliRemoval.md
new file mode 100644
index 000000000..037dc86bf
--- /dev/null
+++ b/docs/architecture-audit-2026-07-13/GeminiCliRemoval.md
@@ -0,0 +1,62 @@
+# Architecture audit: Gemini CLI removal
+
+## Acceptance criteria
+
+- Gemini CLI is not registered, detected, installed, launched, parsed, proxied, authenticated, imported, or displayed.
+- Gemini CLI-specific Code Assist OAuth and native-provider code is deleted.
+- Gemini API-key support remains available as `gemini_api`.
+- Antigravity remains a separate provider and uses only its documented CLI,
+ authentication, configuration, and migration contracts.
+- A persisted legacy `gemini_cli` credential cannot corrupt the key vault and is discarded on the next vault write.
+
+## Ten-layer audit
+
+| Layer | Coverage | Result |
+| ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
+| 1. Compilation correctness | Rust workspace consumers, frontend types, locale JSON, E2E JavaScript syntax | Pass. Affected Rust crates and the application compile; TypeScript reports no errors. |
+| 2. Dead code and structural deduplication | Detectors, OAuth adapter, native Code Assist provider, parser, runner setup, proxy routes, commands, UI setup flows | Removed rather than left behind as unreachable branches. Antigravity uses a separate minimal plain-text parser. |
+| 3. Naming consistency | `ModelType`, CLI registry, binary IDs, validation schemas, icons, translations, tests | Active Gemini CLI names are gone. The sole runtime string is a tombstone used only to retire old persisted credentials. |
+| 4. Semantic overloading | Gemini CLI OAuth versus Gemini API-key provider and Antigravity | Separated. `gemini_api` remains an API provider; Antigravity uses its own keyring-backed CLI contract. |
+| 5. Default branch analysis | enum matches, provider fallback, parser dispatch, auto-detect dispatch, setup routing | No default branch silently routes a removed Gemini CLI value to another provider. |
+| 6. Cross-domain concept leakage | key vault, runner, storage housekeeping, external import, skill discovery, UI, E2E | Removed Gemini CLI assumptions from every affected domain. |
+| 7. New-developer confusion | registry and setup surfaces | There is now one supported Gemini concept in product code: the Gemini API provider. Historical changelog entries remain historical. |
+| 8. Wire protocol and serialization | Rust enum, TypeScript schemas, RPC commands, persisted credentials | Removed the live wire value. Added a read-time tombstone filter so old rows are ignored and cleaned up safely. |
+| 9. Init parity | main sessions, side queries, fallback providers, goal loop, post-turn processing, subagents | Removed the Code Assist session-id/project initialization path from all provider construction entry points. |
+| 10. Resolver symmetry | CLI registry ↔ binary resolver ↔ launch profile ↔ command builder ↔ parser | Gemini CLI was removed from every resolver stage. Antigravity resolves symmetrically to `agy --print` and its plain-text parser. |
+
+## Systematic sweeps
+
+| Sweep | Verdict | Notes |
+| ----------------------------------------------- | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `GeminiCli` / `gemini_cli` | Pass | Remaining occurrences are limited to the persisted-data retirement filter and its test. |
+| `gemini-cli` / “Gemini CLI” | Pass | Remaining product occurrences are historical changelog text only. |
+| `.gemini`, Gemini OAuth/token environment names | Pass | Removed from active detection, auth, storage, skill discovery, and setup code. |
+| Gemini API | Keep with reason | API-key provider support is explicitly outside the removal scope. |
+| Antigravity | Adapt independently | Uses `agy`, self-managed keyring/browser authentication, documented config paths, and plain-text print mode; no Gemini OAuth internals are shared. |
+
+## Gemini-to-Antigravity transition follow-up
+
+| Capability | Decision | Reason |
+| ------------------------------ | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
+| First-launch account migration | Reuse through `agy` | Antigravity itself detects legacy profiles and migrates active tokens into the OS keyring. ORGII must not copy or persist those tokens. |
+| Local and SSH login | Delegate to `agy` | Antigravity owns browser sign-in, remote authorization URLs, authorization codes, logout, and keyring cleanup. |
+| Non-interactive prompt | Implement natively | ORGII launches `agy --print `, with documented `--model`, `--add-dir`, and `--conversation` flags. |
+| Output parsing | New Antigravity parser | `--print` returns plain text; Gemini CLI's `stream-json` event parser is incompatible. |
+| Permission mode | Keep Antigravity flag | Full-permission mode uses the documented `--dangerously-skip-permissions` option. |
+| Workspace context | Keep native locations | Antigravity reads `GEMINI.md`, `AGENTS.md`, and workspace `.agents/skills`. |
+| Global skills | Add new path | Discover `~/.gemini/antigravity-cli/skills`; do not restore legacy `~/.gemini/skills`. |
+| Config discovery | Update paths | Track Antigravity settings, keybindings, and MCP files under the documented `.gemini/antigravity-cli` and `.gemini/config` locations. |
+| ACP | Mark unavailable | The published CLI exposes a TUI and print mode, not an ACP transport. |
+
+## Verification
+
+- `cargo check -p key_vault -p integrations -p agent_cli -p agent_core -p org2`
+- `npm run typecheck`
+- Locale JSON validation with `jq`
+- Targeted key-store, key-extractor, registry, CLI resolver, and provider-factory tests
+- Antigravity command-builder and plain-text parser tests
+- Skill-scanner tests for the updated discovery set
+- Legacy credential retirement regression test
+- E2E JavaScript syntax checks and `git diff --check`
+
+The broader `agent_core` and `key_vault` suites also ran. Their unrelated pre-existing failures are recorded in the delivery summary; all targeted removal tests pass.
diff --git a/docs/architecture-audit-2026-07-14/AgentOrgFinalAudit.md b/docs/architecture-audit-2026-07-14/AgentOrgFinalAudit.md
new file mode 100644
index 000000000..ecef4b47b
--- /dev/null
+++ b/docs/architecture-audit-2026-07-14/AgentOrgFinalAudit.md
@@ -0,0 +1,78 @@
+# Agent Org #272 最终全量架构审计
+
+日期:2026-07-14
+基线:`develop`
+分支:`fix/issue-272-agent-org-recovery-invariants`
+范围:从 `develop` 分出后,本分支涉及 Agent Org Run、Task、Inbox、Wake、Watchdog、成员生命周期、Planner 审批、任务权限、桌面端投影和 E2E 的全部差异。
+
+## 最终结论
+
+本分支范围内没有遗留的 P0/P1/P2 正确性问题,可以提交。最终审计发现并处理了两类收尾问题:
+
+1. 删除 25 个与 #272 无关的纯格式化或旧 Clippy 修写,避免把 Git、Key Vault、Provider、Session Memory 等无关代码混进提交。
+2. 将本次新增的 `member_view_from_parts` 十参数接口收束为一个 identity 参数对象,消除本分支唯一新增的 Clippy 告警。
+
+最终设计坚持:Run、Session、Task、Approval、Inbox/Delivery 是五套不同状态;Coordinator 负责明确派工,Worker 不能自动抢 ownerless task;Watchdog 只恢复真实可处理事件;Plan 审批是持久状态,不靠空 Wake 或模型猜测推进。
+
+## 十层审计
+
+| Layer | Line / Element | Verdict | Reason | Suggested change |
+| ------------------ | ------------------------------------------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- |
+| 1 编译与警告 | Rust desktop / E2E / frontend | keep with reason | `org2`、`e2e-test`、TypeScript、ESLint 均通过;本分支新增 Clippy 告警为 0。全仓严格 Clippy 仍有 develop 基线告警。 | 基线告警另开 cleanup PR,不能混入 #272。 |
+| 2 死代码与重复 | auto-claim、`find_available`、旧 blocker helpers | fixed | Worker 自动抢 ownerless task 的旧入口和失去调用者的 helper 已清除;全仓 sweep 没有第二套 auto-claim。 | 无。 |
+| 3 命名与职责 | `RecoveryPlan` / analyzer / executor / wake outcome | keep | analyzer 只读并给出计划,executor 执行副作用,outcome 明确区分 queued、coalesced、paused、terminal、no-work。 | 无。 |
+| 4 状态维度 | Run / Session / Task / Approval / Delivery | keep | 各自持久化、各自转换;Idle 不代表 Run 结束,Pending 不代表可抢,Wake 不代表消息已消费。 | 新功能继续避免用一个状态推断另一个状态。 |
+| 5 FSM 与默认分支 | Session status / approval policy / run finality | keep | 关键状态显式分类;终态、Paused、等待审批和历史兼容路径都有明确语义,没有危险的“其他都继续”。 | 新增 enum variant 时依赖编译器强制补齐。 |
+| 6 跨域边界 | hierarchy message routing vs task authority | fixed | Hierarchy Mode 只控制成员之间能否通信;Task authority 独立,Coordinator 管全局,Worker 只能推进自己的任务。 | 无。 |
+| 7 新开发者可理解性 | prompt、tool schema、typed guidance | fixed | Coordinator 的派工、动态依赖、Planner 执行模式、审批责任和 ownerless 处理均写入提示词与 schema;可修正输入返回 guidance 而非红色失败卡。 | 提示词契约继续保留字符串测试。 |
+| 8 Wire / 数据边界 | typed inbox payload、task mutation outcome、reserved metadata | fixed | 新写入严格校验 roster、eligibility、owner 和依赖;历史坏数据仍可读并交给 Watchdog 升级;side effect 使用事务内 outcome。 | eligibility join table 可在后续性能 PR 评估。 |
+| 9 初始化一致性 | production / test / debug / E2E DB | fixed | recovery budget、plan approval 等表在各初始化路径一致;CLI Agent Org 在创建和 launch preflight 都明确拒绝。 | CLI parity 完整实现前不要重新开放。 |
+| 10 Resolver 对称性 | run/member/session/task identity | keep | member id、session id、root session 和 run id 的解析方向一致;任务展示与 Inbox 显示优先使用 member identity,不用共享 agent type 冒充成员。 | 无。 |
+
+## 关键不变量复核
+
+| 不变量 | 结果 |
+| --------------------------------------------------------- | ------------------------------------------------------------------------- |
+| 非 Running 的 Run 不能再创建、更新、删除、认领或重派 Task | 通过,Task mutation 与 reconcile 共用 writer lock + immediate transaction |
+| Run 收尾与并发 Task create 不能同时成功 | 通过,并发测试锁定只有一个可串行化结果 |
+| 同一成员的并发 Wake 不得制造多个空 turn | 通过,确定性 idempotency key 合并请求 |
+| Wake 只有真正被 scheduler 接受才消耗恢复预算 | 通过,coalesced / rejected / paused 不计 attempt |
+| scheduler 真正开始 turn 前 Session 不得伪装 Running | 通过,状态更新已移至实际执行边界 |
+| Paused / terminal Run 的排队 Wake 不得复活 provider | 通过,执行前重新读取 Run 并 fail closed |
+| Worker 不能自动认领 ownerless task | 通过,ownerless 只通知 Coordinator 明确指派 |
+| Worker 不能修改其他成员的任务状态 | 通过,Coordinator 全局权限与 Worker 自有任务权限分离 |
+| Worker 输出后忘记完成 Task 不能永久卡住 | 通过,有限自动修正;失败后通知 Coordinator,不做无限 Wake |
+| 等待 Plan 审批时不得每分钟 Wake 或闪烁 | 通过,pending approval 是明确 quiet state |
+| 失败成员任务重排不能丢 metadata 或制造半写入 | 通过,Task 与 history event 同事务 |
+| Task 完成和 dependency unblock 不能重复发 TaskAssigned | 通过,side effect 依赖事务内 mutation outcome |
+
+## 验证结果
+
+| 检查 | 结果 | 说明 |
+| ---------------------------------------------------- | ---------------- | ----------------------------------------------------------------- |
+| `pnpm typecheck` | 通过 | TypeScript 类型检查无错误 |
+| `pnpm run lint` | 通过 | ESLint 无错误 |
+| `pnpm run check:circular` | 通过 | 5,077 文件,无循环依赖 |
+| 目标前端单测 | 25/25 | Group chat、Kanban、Task outcome |
+| 前端全量单测 | 4,071/4,076 | 5 个失败均位于未修改的 develop 基线模块 |
+| `cargo check -p org2` | 通过 | 桌面端完整编译 |
+| `cargo check -p e2e-test` | 通过 | Rust E2E target 编译 |
+| `cargo test -p agent_core --lib -- --test-threads=1` | 2,984/2,986 | 沙箱外运行;剩余 2 个是未修改的 skill-content 与 search-tool 基线 |
+| Agent Org / Recovery / Approval / Lifecycle tests | 全部通过 | 包含并发、暂停、恢复、ownerless、审批与事务回滚 |
+| changed Rust `rustfmt --check` | 通过 | 只检查本分支修改文件,不递归污染旧基线 |
+| changed E2E `node --check` | 通过 | 变更的 `.mjs` 语法通过 |
+| `git diff --check` | 通过 | 无空白错误 |
+| `cargo clippy --all-targets -- -D warnings` | develop 基线阻断 | 5 个无关 crate 旧告警;本分支未修改这些文件 |
+| Agent Core strict Clippy | develop 基线阻断 | 本次唯一新增告警已修;剩余均可在 develop 原代码复现 |
+
+## 全量测试中明确不属于本分支的失败
+
+- 前端:Housekeeper settings manifest、Cursor external-history、planning-meta 旧断言、editUtils null 处理。
+- Rust:内置 `e2e-testing` skill 内容断言、search tool 的 `repo_path` / `repo_paths` 冲突断言。
+- Clippy:`orgtrack-core`、`cursor-bridge-app` 以及 Agent Core 旧模块的既有 lint debt。
+
+这些文件没有被本分支修改。审计没有为追求“全绿数字”而把它们顺手改进来,避免扩大 #272 的评审范围。
+
+## 提交判断
+
+结论:**可提交**。本次范围的架构不变量、生产路径、事务边界、恢复策略、权限模型和 UI 投影均有实现与测试对应;未发现多余的目标外代码或会阻止提交的回归。
diff --git a/docs/architecture-audit-2026-07-14/CanvasSessionState.md b/docs/architecture-audit-2026-07-14/CanvasSessionState.md
new file mode 100644
index 000000000..32b26af8b
--- /dev/null
+++ b/docs/architecture-audit-2026-07-14/CanvasSessionState.md
@@ -0,0 +1,89 @@
+# Architecture Audit — Canvas session state
+
+**Scope:** `canvasPreviewAtom`, `useCanvasForTurn`, and Canvas consumers in Chat, SessionCore, and WorkStation
+**Date:** 2026-07-14
+**Auditor:** Codex
+
+## Acceptance criteria
+
+- [x] Session matching is defined once for all Canvas-derived UI state.
+- [x] Dismiss and clear operations cannot mutate another session's entry.
+- [x] Inline card, latest-canvas shortcut, pinned pill, and Simulator-open state have explicit ownership rules.
+- [x] The compatibility hook and duplicated test-only implementations are removed.
+- [x] Production consumers use the canonical hook or the small store-level transition helpers.
+- [x] TypeScript, targeted ESLint, and Canvas lifecycle tests pass.
+
+## 10-layer audit
+
+### Layer 1 — Compilation correctness
+
+- `pnpm typecheck` passes.
+- Targeted ESLint passes for all changed Canvas TypeScript and TSX files.
+- Canvas lifecycle and hook suites pass (53 tests).
+
+### Layer 2 — Dead code and structural deduplication
+
+- Removed `useCanvasPreviewForSession`, which only relayed a subset of `useCanvasForTurn`.
+- Replaced test-local copies of session matching and dismiss behavior with exported pure helpers used by production.
+- Swept remaining `canvasPreviewAtom` reads. Direct access remains only in integration owners that write jump/simulator state; Chat, pinned actions, and the WorkStation renderer use the canonical session-scoped hook.
+
+### Layer 3 — Naming consistency
+
+- `latestPayload` means the newest matching payload even after dismissal.
+- `payload` means the payload still eligible for inline rendering.
+- `isDismissed`, `openedInSimulator`, and `allowsLatestCanvasShortcut` name distinct UI decisions instead of overloading one visibility flag.
+
+### Layer 4 — Semantic overloading
+
+| Term | Meaning | Verdict |
+| --------------- | ---------------------------------------- | ------------------------------------------------- |
+| latest payload | Matching session's stored Canvas payload | Keep; may remain available after inline dismissal |
+| visible payload | Payload eligible for the inline card | Keep as `payload`; derived from dismissal state |
+| clear | Remove the matching session's entry | Keep; distinct from soft dismiss |
+
+No term drives more than one state transition.
+
+### Layer 5 — Default branch analysis
+
+- Missing or mismatched session IDs derive an empty snapshot and leave mutations unchanged.
+- `allowsLatestCanvasShortcut` defaults to allowed when no matching global entry exists, so another session cannot suppress the current session's event-store fallback.
+- Dismiss and clear helpers use explicit guards rather than catch-all mutation branches.
+
+### Layer 6 — Cross-domain leakage
+
+- Store-level helpers contain only session matching and immutable transitions.
+- The hook owns Chat-specific shortcut eligibility.
+- WorkStation tab closure and Simulator jump behavior remain in their existing UI integration layers.
+
+### Layer 7 — New-developer confusion test
+
+- The snapshot interface documents the difference between stored, visible, dismissed, and Simulator-open state.
+- Callers consume named snapshot fields rather than reconstructing conditions from the raw atom.
+- The deleted compatibility shim no longer creates two apparent public APIs for the same state.
+
+### Layer 8 — Wire protocol and serialization
+
+- No wire payload or persisted schema changes. Canvas state remains an in-memory Jotai entry containing the existing `CanvasInlinePayload`.
+
+### Layer 9 — Init and entry-point parity
+
+| Consumer | Read path | Mutation path |
+| ----------------------- | ----------------------------------------- | ------------------------------------ |
+| Streaming `ChatVariant` | `useCanvasForTurn().snapshot.payload` | none |
+| `ChatView` shortcut | `latestPayload` plus shortcut eligibility | existing Simulator jump action |
+| `PinnedActionsBar` | `snapshot.isDismissed` | session-scoped clear |
+| WorkStation Canvas tab | `snapshot.latestPayload` | session-scoped clear plus tab close |
+| New-turn sync | none | shared session-scoped dismiss helper |
+
+All entry points use the same session predicate.
+
+### Layer 10 — Resolver symmetry
+
+- `latestPayload`, dismissal, and Simulator-open state all resolve from the same matching entry.
+- Both mutation helpers apply the same session guard. There is no field-specific fallback chain.
+
+## Systematic sweep
+
+- Swept `canvasPreviewAtom`, `useCanvasPreviewForSession`, `cardDismissed`, and `openedInSimulator` usages.
+- No remaining compatibility-hook caller or duplicated session-scoped mutation was found.
+- The remaining direct atom integrations intentionally own Simulator selection or backend-stream state writes and are not duplicate Chat presentation paths.
diff --git a/docs/architecture-audit-2026-07-14/SessionProvenance.md b/docs/architecture-audit-2026-07-14/SessionProvenance.md
new file mode 100644
index 000000000..f56231312
--- /dev/null
+++ b/docs/architecture-audit-2026-07-14/SessionProvenance.md
@@ -0,0 +1,100 @@
+# Architecture Audit: Session Provenance
+
+Scope: every file changed by the Session Provenance PR, including the extracted
+protocol, canonical Orgtrack records, hook CLI and installers, SQLite storage,
+historical reconciliation, RPC schemas, Session Blame UI, sidebar reveal, all
+locales, documentation, and rendered E2E coverage. Unrelated key-vault changes
+were explicitly removed from the final upstream diff.
+
+| Layer | Area inspected | Verdict | Reason | Suggested change |
+| ---------------------------- | ------------------------------------------------------------------------------------------------ | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
+| 1. Compilation | Rust packages, desktop app, TypeScript boundary, E2E specs | keep with reason | `cargo check -p org2` passes. Current focused suites pass: `orgtrack_protocol` 6 unit/integration tests plus its package-boundary test, `agent_cli` 20, `orgtrack_core` 189, app `orgtrack::` 14, and 22 focused frontend tests. Changed frontend files pass ESLint. Full TypeScript check reaches only the pre-existing untouched `ContextInfoButton.tsx:468` error. Strict clippy reaches only five pre-existing warnings outside this PR after all changed-code warnings were removed. | Resolve the repository's unrelated TypeScript and clippy debt separately. |
+| 2. Dead code / deduplication | Protocol types, classifiers, provider parsing, reconciliation, RPC and UI types | keep with reason | Removed unused lookup DTOs, stale translations, speculative `Reference` action and `Transcript` capture method, and an unused attribution field. Live hooks and historical import now share `resource_interaction` action/path/patch classification. Historical import calls the existing Claude/Codex/Cursor session list/load paths and normalized `ActivityChunk`; it does not introduce a second transcript parser. | Keep new provider support behind the existing provider readers and shared classifier. |
+| 3. Naming | `ResourceInteractionEnvelopeV1`, `SessionActorLifecycleEnvelopeV1`, Session Provenance, locators | keep with reason | Names describe two different facts: resource activity and actor lifecycle. They avoid the ambiguous “file touches” label and do not overload actor, session, transcript, source DB, and Orgtrack store identities. | Add a new version only for an incompatible wire change; do not rename fields in v1. |
+| 4. Semantic overloading | Session/root/actor IDs, action, precision, capture method, source/store paths | keep with reason | Canonical session ID, provider session ID, parent session ID, and actor ID remain separate. Action has six supported values; capture method has three. Attribution precision records whether actor ownership was direct, correlated, or session-only. Original provider data locations are read-only source locators; the Orgtrack DB/spool are writable store locators and are never emitted in privacy-filtered envelopes. | Preserve the distinction when the collector is extracted into a standalone package. |
+| 5. Default branches | Unknown tools/events, incomplete hook configs, invalid inbox records, missing transcripts | keep with reason | Unknown file-capable tools fail closed rather than fabricating an action. Hook installation checks the complete structural event/matcher set instead of trusting a marker. Invalid inbox files are quarantined. Transcript navigation is enabled only when a real transcript file exists. | Add explicit mappings and fixtures when a provider adds a new tool/event family. |
+| 6. Cross-domain leakage | `orgtrack-protocol`, `orgtrack-core`, `agent-cli`, desktop orchestration, frontend | keep with reason | The protocol crate depends only on serialization/schema concerns and contains no Tauri, SQLite, filesystem, ORG2, or provider implementation. `orgtrack-core` owns canonical classification, `agent-cli` owns vendor config/spooling, the desktop app owns persistence/reconciliation, and the frontend consumes projections. | The later cloud/submodule extraction can move these packages without moving My Station UI policy. |
+| 7. New-developer test | Module layout, docs, operational boundaries | keep with reason | Live ingestion remains in `session_provenance.rs`; historical scheduling/checkpoints/provider reuse were split into `historical_backfill.rs`. The protocol README, package RFC, and `docs/session-provenance.md` explain storage, privacy, hooks, source/store paths, upgrades, and extraction. The provider E2E is now a focused spec rather than being hidden inside the Diff-tab spec. | Keep E2E responsibilities split by observable product contract. |
+| 8. Wire / serialization | Vendor JSON to v1 envelopes to SQLite JSON to Zod RPC | keep with reason | Both resource and actor-lifecycle envelopes are strictly decoded. JSON Schemas enumerate the six actions, outcomes, and precision values. Negative tests reject prompts, commands, output, content, diffs, identities, source DB paths, and store paths. Deterministic observation IDs include capture method and actor so exact and reconciled facts cannot collide. | Add checked-in upstream payload fixtures when providers publish stable fixtures. |
+| 9. Initialization parity | Hook subprocess, desktop drain, native events, historical import, test harness | keep with reason | Hook subprocesses validate and atomically spool but never open the desktop DB. Desktop drain validates before persistence. Native events use the same canonical store. Historical import uses durable fingerprints and immediately requeries after terminal backfill. User preference is written before provider mutation, and returned state reflects actual installation; an unselect therefore removes a hook on the normal path and reports per-platform failure honestly. | A standalone collector must pass this same entry-point matrix before cutover. |
+| 10. Resolver symmetry | Repo/workspace/file/session/actor resolution and sidebar replay | keep with reason | Live and historical paths share canonical provider prefixes and file-resource resolution. Claude actor IDs resolve to the same child identities produced by existing importers; Codex/Cursor promote transcript origins only after locating a real file. Session Blame folds participants whose effective replay target equals the root into the root aggregate; distinct children exact-load, reveal the correct time group, expand, select, scroll, and replay independently. Provider first-page refreshes preserve exact-loaded child rows, while disabling a provider still removes them. | Keep labels presentation-only; navigation must continue using canonical IDs and transcript proof. |
+
+## Systematic sweeps
+
+- Compared every PR path against `upstream/develop`, including generated schemas,
+ all 13 locale files, Rust package manifests, frontend RPC types, hooks, state,
+ tests, reports, and E2E documentation.
+- Searched canonical session-ID construction across Claude Code, Codex, Cursor,
+ native ORG2, historical import, SQLite queries, RPC output, and sidebar state.
+- Searched all current file-capable event/tool mappings and verified that live and
+ backfill paths converge on the same classifier.
+- Exercised every supported user-level hook configuration and verified install,
+ remount/readback, uninstall restoration, malformed-config reporting, and
+ per-platform status behavior. Cursor completeness includes `postToolUse`,
+ `subagentStart`, and `subagentStop`.
+- Removed unrelated key-vault compatibility changes from the final upstream diff
+ instead of coupling account migration behavior to Session Provenance.
+
+## Term overloading table
+
+| Term | Existing meanings found | Resolution |
+| -------------------- | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
+| interaction outcome | Agent-core response/cancel/timeout state; Orgtrack resource success/failure state | The protocol uses `ResourceInteractionOutcome`; agent-core retains its domain-local `InteractionOutcome`. |
+| session ID | Provider session ID; ORG2 canonical root session ID; actor/child session identity | Separate source, canonical, parent, and actor fields; labels are never identity. |
+| original DB path | Provider history input; normalized Orgtrack destination | Read-only `SourceLocator` and writable `StoreLocator`; neither appears in an event envelope. |
+| actor lifecycle | An agent/subagent starting or stopping | `SessionActorLifecycleEnvelopeV1`; it proves hierarchy and transcript identity but does not pretend that a file interaction occurred. |
+| resource interaction | A read/write/search/list/create/delete operation on a normalized resource | `ResourceInteractionEnvelopeV1`; it may point at an actor established by lifecycle data. |
+
+## Initialization parity matrix
+
+| Entry point | Normalize provider payload | Validate v1 | Atomic spool | Desktop DB | Shared resolver |
+| ------------------------------------ | ------------------------------------------ | --------------------- | ----------------------------------------------- | --------------------------- | --------------- |
+| Claude/Codex/Cursor hook subprocess | yes | yes | yes | no | desktop drain |
+| Desktop inbox drain | already normalized | yes | consumes published files; rejects invalid files | yes | yes |
+| ORG2 native event | typed native record | not a wire round-trip | no | yes | yes |
+| Historical transcript reconciliation | existing provider reader + `ActivityChunk` | not a wire event | no | yes; fingerprint checkpoint | yes |
+| Protocol golden/schema tests | not applicable | yes | no | no | not applicable |
+
+## Rendered and live evidence
+
+The real hook-settings test passed in an isolated home using a real pointer: it
+enabled Codex, remounted the settings panel, reread the installed status, then
+restored the original state. This verifies that the switch is stateful rather
+than visual-only.
+
+The focused provider E2E passed end to end with the locally authenticated Claude
+Code, Codex, and Cursor CLIs. Each provider read and edited the same isolated
+file through its real tools; production hooks emitted privacy-filtered records;
+the desktop drained them into SQLite; historical backfill reconciled transcripts;
+and My Station rendered Session Blame. It proved root and subagent transcript
+identity, distinct root/child replay, sidebar group expansion/selection/scroll,
+and absence of content sentinels from captured metadata.
+
+An audit rerun initially exposed a race where a provider first-page refresh
+could remove an exact-hydrated Codex child after the transcript had already
+opened. The loader now preserves child rows across ordinary page replacement,
+but not across provider disable. A focused regression test covers both branches,
+and a fresh real-provider E2E then passed the previously failing child
+expand/select/scroll assertion.
+
+The independent native ORG2 Diff scenario was also launched, but its `before
+all` stopped before product interaction because the isolated account database
+contained no Codex account satisfying `gpt-5.5 + session token + Rust-agent
+support`. Claude candidates were absent as well. This is recorded as an
+environment/credential fixture blocker, not a passing product assertion and not
+a Session Provenance regression.
+
+## Session Blame to sidebar contract
+
+| Boundary | Verified behavior |
+| ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| RPC | `sessionIds?: string[]` is additive, Zod-validated, and exact-match; `session-1` cannot match `session-10`. |
+| Cache | Canonical `session_id` has an idempotent index; explicit historical navigation can hydrate its requested root/child even when presentation filters hide it, without changing the user's source preferences. |
+| State | Reveal requests use monotonically increasing IDs, clear conditionally, uncollapse the sidebar, clear search, and open the containing time group. |
+| DOM | Canonical session ID is the row identity; scrolling occurs once per reveal request. |
+| Transcript | Root and child are independently hydrated and selected; actor navigation is clickable only with a real transcript path. |
+
+The implementation therefore supports the claimed chain:
+`session -> actor/subagent -> resource interaction -> proven transcript replay`.
+When a provider does not expose enough evidence, the record remains visible at
+session-only precision instead of inventing subagent ownership.
diff --git a/docs/architecture-audit-2026-07-14/WarpImportedHistory.md b/docs/architecture-audit-2026-07-14/WarpImportedHistory.md
new file mode 100644
index 000000000..e9177a271
--- /dev/null
+++ b/docs/architecture-audit-2026-07-14/WarpImportedHistory.md
@@ -0,0 +1,40 @@
+# Architecture audit: Warp imported history
+
+## Acceptance criteria
+
+- Detect Warp's local database on supported desktop platforms.
+- Import list metadata and replayable user, assistant, reasoning, model, and tool events without mutating Warp data.
+- Reuse the shared imported-history cache, aggregation, recent-path, sidebar, replay, and source-rescan pipelines.
+- Keep Warp Agent history distinct from the separate Warp CLI/TUI integration.
+
+## Ten-layer audit
+
+| Layer | Coverage | Result |
+| ----------------------------------------- | ---------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| 1. Compilation correctness | Core crate, Tauri command registration, TypeScript source registry and filters | Targeted Rust and frontend tests pass. The complete `org2` desktop crate compiles. |
+| 2. Dead code and structural deduplication | Database discovery, Kanban source mapping, imported cache | One shared Warp path resolver feeds both detection and import. One shared source→Kanban map replaces two former copies. |
+| 3. Naming consistency | `warp`, `warpapp-`, `SOURCE_WARP`, command names, icon ID | Source IDs and prefixes are consistent across Rust, Tauri, TypeScript, tests, and docs. `warpapp-` identifies imported app history; it is not a CLI-agent ID. |
+| 4. Semantic overloading | Warp conversation IDs, ORGII session IDs, Warp terminal sessions | Source IDs remain raw conversation IDs in cache keys; only ORGII-facing IDs receive `warpapp-`. Terminal restoration data is not presented as Agent conversation history. |
+| 5. Default branch analysis | Unknown protobuf messages/tools, missing summary, missing timestamps | Unknown tools preserve raw names/payloads. Missing optional data uses explicit fallback order; missing schema or malformed tasks returns an empty/safe result. |
+| 6. Cross-domain concept leakage | Warp parser versus shared import infrastructure | Warp schema/protobuf knowledge is isolated under `sources/warp`; generic cache/query/replay contracts remain source-neutral. |
+| 7. New-developer confusion | Module docs, storage note, constants | The storage schema, paths, mappings, fallbacks, privacy limits, and #331 boundary are documented in `docs/architecture/warp-imported-history.md`. |
+| 8. Wire protocol and serialization | Official Warp protobuf descriptor, JSON projection, Tauri payloads | Uses Warp's pinned published descriptor rather than a hand-copied proto. Outputs existing `ActivityChunk` and imported-session wire types. Fixture tests exercise protobuf encode/decode and SQLite rows. |
+| 9. Init parity | Sidebar list, replay, recent paths, source stats, rescan aggregation, spotlight, Kanban filter | Every existing imported-history entry point has a Warp registration. No separate partial initialization path was introduced. |
+| 10. Resolver symmetry | Detect path → source cache → list/replay command → frontend descriptor/filter | The same source ID/prefix/path candidates resolve end to end, with tests for paths, prefix round-trip, registry lookup, replayability, and filter mapping. |
+
+## Deliberately skipped
+
+| Area | Reason |
+| -------------------------------------------- | ------------------------------------------------------------- |
+| Warp CLI process launch and live TUI capture | Out of scope for #366; tracked by #331. |
+| Cloud-history API | No local-import contract and would change privacy/auth scope. |
+| Mutation or migration of `warp.sqlite` | Imported history is strictly read-only. |
+
+## Verification
+
+- Six Rust fixture/schema/path/cache tests under `sources::warp::history::tests`.
+- Frontend registry, replayability, session-dispatch, pagination, and Kanban mapping tests.
+- `cargo check -p org2`.
+- ESLint over every changed TypeScript/TSX file.
+- Full TypeScript checking reaches one unrelated pre-existing error in `ContextInfoButton.tsx:468`; no Warp file reports a type error.
+- `git diff --check`.
diff --git a/docs/architecture-audit-2026-07-15/AgentBlameApiRemoval.md b/docs/architecture-audit-2026-07-15/AgentBlameApiRemoval.md
new file mode 100644
index 000000000..e054c7915
--- /dev/null
+++ b/docs/architecture-audit-2026-07-15/AgentBlameApiRemoval.md
@@ -0,0 +1,49 @@
+# Agent Blame API removal architecture audit
+
+**Scope:** End-to-end removal of the five RPCs formerly consumed only by `AgentBlamePanelView`: scan start, scan status, scan cancel, index read, and file-session lookup.
+
+## Acceptance criteria
+
+- [x] No frontend wrapper, RPC procedure, Zod input/output schema, Tauri registration, or Rust command remains for the five removed APIs.
+- [x] No API-only Rust helper, projection type, test, cancel marker, or options relay remains.
+- [x] Shared orgtrack export, sync, index generation, and file timeline behavior remains available to its live callers.
+- [x] TypeScript typecheck and targeted ESLint pass.
+- [x] Rust formatting, library compilation, and focused orgtrack tests pass.
+
+## Removed call chains
+
+| Frontend wrapper | Tauri command | Rust/API-only implementation removed |
+| ---------------------------- | ------------------------------- | -------------------------------------------------------------------------- |
+| `startOrgtrackScan` | `orgtrack_scan_start` | Background scan launcher and public `OrgtrackScanOptions` relay |
+| `getOrgtrackScanStatus` | `orgtrack_scan_status` | Scan-progress reader endpoint |
+| `cancelOrgtrackScan` | `orgtrack_scan_cancel` | Cancel request helper, cancel-marker path, and cancellation guards |
+| `getOrgtrackIndex` | `orgtrack_get_index` | Standalone index reader endpoint |
+| `lookupOrgtrackFileSessions` | `orgtrack_lookup_file_sessions` | File-session aggregation projection, projection types, and projection test |
+
+## Ten-layer audit
+
+| Layer | Coverage | Verdict |
+| ----: | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| 1 | Compilation correctness | `tsc --noEmit`, targeted ESLint, `rustfmt --check`, and `cargo check --lib` pass. The focused orgtrack test filter passes 5/5 tests. Strict Clippy was attempted and is blocked by six pre-existing findings in untouched orgtrack-core files; none point to this change. |
+| 2 | Dead code and structural deduplication | Traced each frontend entry point through RPC registration and Tauri dispatch. Removed the orphaned background launcher/status/cancel helpers, standalone index reader, file-session projection/types/test, cancel path, and the now-single-caller options relay. |
+| 3 | Naming consistency | Repository-wide sweeps return zero hits for all five frontend wrapper names and command strings. Remaining `orgtrack` index/timeline names belong to live export, sync, and editor-timeline flows. |
+| 4 | Semantic overloading | `scan` now refers only to synchronous export progress/checkpoint internals; it no longer also denotes a user-controlled background RPC lifecycle. `index` remains the generated repo-sync projection and sync response type. |
+| 5 | Default branches | Deleted the API defaults for `resume`, `rebuild`, and trajectory scan start. The remaining synchronous export path directly expresses its established resume behavior instead of routing fixed values through an options struct. No new catch-all branch was added. |
+| 6 | Cross-domain leakage | Agent Blame-specific API controls no longer leak into the shared lineage RPC surface or repo-sync public types. Shared orgtrack primitives required by other features were retained. |
+| 7 | New-developer clarity | There is no longer a public scan-control API without a UI owner, and no `OrgtrackScanOptions` type suggesting configurable callers that do not exist. |
+| 8 | Wire protocol | The five Tauri IPC command registrations and their Zod wire contracts were removed together. No HTTP, WebSocket, or external serialized payload changed. |
+| 9 | Initialization parity | The removed commands were query/control endpoints, not app/session initialization entry points. The live `orgtrack_initialize` and `orgtrack_export` entry points still share `export_orgtrack`. |
+| 10 | Resolver symmetry | No multi-source resolver or fallback chain is involved in the removed call paths. The surviving export path has one direct tier input and one checkpoint source. |
+
+## Intentionally retained live surfaces
+
+- `orgtrack_initialize` and `orgtrack_export` for synchronous metadata export.
+- `orgtrack_sync_core_repo` and `OrgtrackIndex` for repo synchronization.
+- `orgtrack_get_file_timeline` for editor timeline attribution.
+- Internal scan progress/checkpoint structures used while producing exports.
+
+No compatibility shim or deprecated alias was added for the removed commands.
+
+## Existing verification debt
+
+`cargo clippy --lib -- -D warnings` currently fails on six unrelated pre-existing findings in `canonical.rs`, `privacy/mod.rs`, and imported-history source parsers. They were left untouched to keep this removal scoped and avoid mixing an unrelated cleanup into the API deletion.
diff --git a/docs/architecture-audit-2026-07-15/SpotlightSelectorContextMenus.md b/docs/architecture-audit-2026-07-15/SpotlightSelectorContextMenus.md
new file mode 100644
index 000000000..b6b806679
--- /dev/null
+++ b/docs/architecture-audit-2026-07-15/SpotlightSelectorContextMenus.md
@@ -0,0 +1,36 @@
+# Architecture Audit: Spotlight Selector Context Menus
+
+## Acceptance criteria
+
+- [x] One shared context-menu renderer owns native menu creation and clipboard
+ failure handling.
+- [x] Selector builders declare copy values without importing UI transport.
+- [x] Branches expose name only; worktrees and workspace-path rows expose name
+ and path.
+- [x] Filesystem-backed rows expose Reveal through the existing cross-platform
+ opener API and label resolver; branch rows do not invent a path action.
+- [x] Repository paths are normalized at their domain adapter boundary.
+- [x] Every new production type/property is consumed by a live call path.
+
+## Ten-layer review
+
+| Layer | Coverage | Verdict |
+| --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- |
+| 1. Compilation correctness | Changed files pass ESLint and focused Vitest coverage. The full repository typecheck reports only the unrelated pre-existing `ContextInfoButton.tsx:468` error. | No changed-file error found. |
+| 2. Dead code and structural duplication | Traced item builders → `SpotlightItemData.contextMenuCopy` → `SpotlightItemRow` → native Tauri menu → shared `copyText` / `revealItemInDir`. | One live path; no per-palette menu duplication. |
+| 3. Naming consistency | `contextMenuCopy.name/path`, `Copy Name`, and `Copy Path` describe the copied values consistently. | Keep. |
+| 4. Semantic overloading | Branch name, worktree label, workspace name, and filesystem path remain distinct fields. | Keep. |
+| 5. Default-branch analysis | Menu entries are included explicitly only when their corresponding value exists; no catch-all invents a path. | Keep. |
+| 6. Cross-domain leakage | Palette builders own domain-to-copy-value mapping; the shared row knows only optional name/path strings. | Keep. |
+| 7. New-developer clarity | The item data shows exactly which values a row exposes. Native-menu and clipboard concerns remain centralized. | Keep. |
+| 8. Wire protocol | No application wire contract changed. Tauri's existing native-menu, opener, and clipboard APIs are reused. | No custom serialization to audit. |
+| 9. Init parity | No initialization path changed; every Spotlight row renderer receives the same optional data contract. | Not applicable beyond call-path trace. |
+| 10. Resolver symmetry | No resolver or fallback chain changed. The saved-workspace primary/first-folder choice is one documented single-field rule. | Not applicable. |
+
+## Call path
+
+`selector item builder` → `contextMenuCopy` → row `contextmenu` event → native
+Tauri menu → shared `copyText` browser/Tauri/textarea fallback chain or
+cross-platform `revealItemInDir`.
+
+No architecture fix candidates remain in the audited scope.
diff --git a/docs/architecture-audit-2026-07-15/WorktreePalette.md b/docs/architecture-audit-2026-07-15/WorktreePalette.md
new file mode 100644
index 000000000..482279f62
--- /dev/null
+++ b/docs/architecture-audit-2026-07-15/WorktreePalette.md
@@ -0,0 +1,39 @@
+# Architecture Audit: WorktreePalette CRUD Flow
+
+## Scope and acceptance criteria
+
+- `src/scaffold/GlobalSpotlight/palettes/BranchPalette/index.tsx`
+- `src/scaffold/GlobalSpotlight/palettes/BranchPalette/types.ts`
+- `src/scaffold/GlobalSpotlight/index.tsx`
+
+- [x] One typed source of truth controls switch/remove mode.
+- [x] The embedding shell only mirrors mode for footer presentation.
+- [x] Create, remove, refresh, and switch use the existing worktree API/cache
+ path rather than adding parallel transport logic.
+- [x] Main and active worktrees cannot enter the remove action path.
+- [x] Changed TypeScript files pass ESLint and Prettier.
+- [ ] Repository-wide `tsc --noEmit` is clean; currently blocked by the
+ unrelated pre-existing `ContextInfoButton.tsx:468` type error.
+
+## Ten-layer review
+
+| Layer | Coverage | Verdict |
+| --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- |
+| 1. Compilation correctness | Changed-file ESLint and Prettier pass. Repository typecheck reports only the unrelated `ContextInfoButton.tsx:468` error. | No changed-file issue found. |
+| 2. Dead code and structural duplication | Traced `WorktreePalette` → `GlobalSpotlight.handleRemoveWorktree` → existing `removeGitWorktree`, plus `refreshWorktreeMap` for list invalidation. The new mode type and callback are both wired into production. | Keep. No duplicate API path introduced. |
+| 3. Naming consistency | `WorktreePaletteMode` uses explicit `switch` and `remove` values; callbacks retain the existing `onRemoveWorktree` contract. | Keep. |
+| 4. Semantic overloading | `remove` consistently means removing a linked checkout while preserving its branch; branch deletion remains a separate action and handler. | Keep. |
+| 5. Default-branch analysis | The two-state union is handled with explicit equality checks; there is no catch-all that assigns future modes switch semantics. | Keep. |
+| 6. Cross-domain leakage | Worktree mode stays within the Worktree palette/types. The outer Spotlight state mirrors it only to choose footer chrome. | Keep. |
+| 7. New-developer clarity | Mode, handlers, protected-row filter, and cache refresh names state intent directly. | Keep. |
+| 8. Wire protocol | No wire type or payload changed. Removal continues to send the existing `worktree_path`/`force` DELETE payload. | Intentionally skipped payload dump; transport is unchanged. |
+| 9. Init parity | No initialization path changed. The only production Worktree palette call site supplies the existing create/select callbacks and the shared remove callback. | Not applicable beyond call-site trace. |
+| 10. Resolver symmetry | No multi-field resolver or fallback chain changed. | Not applicable. |
+
+## Call path
+
+`WorktreePalette row` → `onRemoveWorktree(skipRefresh)` →
+`GlobalSpotlight.handleRemoveWorktree` → `removeGitWorktree` →
+`refreshWorktreeMap` → subscribed `useWorktreeEntries` rows.
+
+No architecture fix candidates remain in the audited scope.
diff --git a/docs/architecture-audit-2026-07-15/session-file-metadata.md b/docs/architecture-audit-2026-07-15/session-file-metadata.md
new file mode 100644
index 000000000..1afc58655
--- /dev/null
+++ b/docs/architecture-audit-2026-07-15/session-file-metadata.md
@@ -0,0 +1,121 @@
+# Architecture Audit — Orgtrack Round Metadata
+
+**Scope:** Issues #387 and #388: per-round resource/development metadata, whole-session edit impact, and Kanban file search.
+
+## Completion criteria
+
+- [x] One Orgtrack projector owns per-round read/search/write/create/delete/rename observations.
+- [x] The same projector owns modified-file line stats and development artifacts (commits/PRs).
+- [x] ORG2, Claude Code, Codex, Cursor, and other normalized providers enter through the same tool metadata boundary.
+- [x] `session_turns` is a rebuildable ORG2 read cache, not the semantic owner.
+- [x] Historical rows rebuild lazily through a versioned index; existing DBs keep working.
+- [x] Session, turn, and actor/execution-thread identities are not conflated.
+- [x] The chat footer renders resource observations and edit/development metadata.
+- [x] Kanban file search uses the whole-session edit projection without parsing transcripts per keystroke.
+- [x] Session Blame pages by complete root-session groups and refreshes from a durable SQLite revision.
+- [x] Historical backfill ownership/progress survives restarts without retaining an in-memory job registry.
+- [x] The chat loads metadata only for rendered turns and removes atoms when those turns leave the view.
+
+## Ownership and extraction boundary
+
+| Layer | Owns | Does not own |
+| ------------------------ | ------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
+| `orgtrack-protocol` | Stable action/outcome/envelope vocabulary | Provider payload parsing, SQLite, UI |
+| `orgtrack-core` | Provider adapters, resource extraction, `TurnMetadataAccumulator`, Git artifact recognition | ORG2 database paths, Tauri commands, React |
+| `session-persistence` | Versioned `session_turns` materialized cache and lazy rebuild | Tool-name constants, provider-specific result parsing, Git semantics |
+| app `session_provenance` | stdin/inbox/SQLite/filesystem adapters and actor lifecycle wiring | Round aggregation rules |
+| frontend | Validated display and navigation | Raw transcript aggregation |
+
+Moving Orgtrack to a future repository/submodule therefore requires changing Cargo dependency locations and supplying host adapters; the protocol/projector does not depend on the ORG2 app crate.
+
+## Incremental memory and freshness model
+
+| Concern | Durable source of truth | Bounded in-memory state |
+| ------------------------ | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
+| File-history freshness | Per-resource SQLite revision advanced by interaction insert/delete triggers | Current visible page plus one numeric revision; no process-wide history cache |
+| Session Blame pagination | SQLite query pages root sessions, then returns all interactions for those roots | 30 root sessions by default (100 hard maximum); children never split from their root |
+| Historical backfill | SQLite job row with owner, token, status, progress, error, and update time | One process-owner UUID; transcript batches are released after projection |
+| Per-round chat metadata | Versioned `session_turns` rows | Only currently rendered turn atoms; stale/session-unmounted atoms are explicitly removed |
+| Live invalidation | Revision remains authoritative across every writer and restart | A payload-free Tauri event accelerates refresh; visible-only 5 s revision probes recover missed events |
+
+Every inbox consumer emits the same invalidation after a successful drain. This prevents a query from consuming a hook envelope before the periodic drain loop can broadcast it. The event is only a hint: the frontend rechecks the SQLite revision before replacing a page, and an ordering change during “load more” restarts from page zero.
+
+## Identity semantics
+
+| Field | Meaning | Source |
+| ------------ | ------------------------------------------- | --------------------------------------------------------- |
+| `session_id` | Durable conversation/session | Provider canonical session identity |
+| `turn_id` | User-message-bounded conversational round | Latest non-synthetic user-message id |
+| `actor_id` | Root agent/subagent identity | Hook lifecycle or reconciled actor mapping |
+| `thread_id` | Provider execution thread/process dimension | Preserved on normalized events; never reused as `turn_id` |
+
+Native ORG2 associates completed tool calls with the nearest preceding real user message in the in-memory production event store. Reconciled histories infer the same boundary from normalized `user_message` chunks. A provider thread id may identify an execution lane or subagent and is intentionally not promoted to a conversational round.
+
+## Provider coverage
+
+| Capture surface | Providers | Projection path |
+| -------------------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
+| Managed hooks | Claude Code, Codex, Cursor, Qwen Code, Factory Droid, Trae, OpenCode, Windsurf, Kimi, Antigravity, ZCode | hook adapter → privacy-safe `ResourceInteractionEnvelopeV1` |
+| Imported history | Claude Code, Codex, Cursor, OpenCode, Windsurf, WorkBuddy, Trae, Cline, Warp, ZCode | existing provider loader → normalized `ActivityChunk` → Orgtrack resource projector |
+| Native ORG2 | Rust-agent event pipeline | merged production tool event → Orgtrack interaction store; turn cache → `TurnMetadataAccumulator` |
+| Cloud collaboration replay | Authorized ORG2 team-session event cache | checkout-safe path remap → normalized `ActivityChunk` → Orgtrack interaction store |
+
+Hook-only providers gain live provenance immediately. Providers with imported-history loaders also gain lazy historical projection. Adding a future provider means implementing an adapter/loader to the normalized boundary, not adding another turn metadata implementation.
+
+## Production call-chain trace
+
+| Entry point | Path | Result |
+| ----------------- | ------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
+| Live native tool | production event merge → nearest user-message turn → `persist_native_event_interactions` | Canonical session/turn/actor/resource fact |
+| External hook | provider hook → `hook_adapter` → privacy-safe spool → bounded drain | Canonical live resource fact without raw content/query/output |
+| Historical round | existing provider loader/event cache → normalized tool metadata → `TurnMetadataAccumulator` | Lazy read/search/edit/Git metadata |
+| Cloud replay | authorized event cache → owner/viewer checkout remap → user-message round boundary | Exact-owner resource facts without persisting the owner's path |
+| Session aggregate | `load_turn_index` → fold unique modified paths and line totals | Final edit impact and Kanban search input |
+| Chat UI | validated RPC → per-turn atom → `TurnMetadataFooter` | Read/search paths, edits, commits, and PRs |
+| Open file history | SQLite revision probe → root-session page → Session Blame | Incremental refresh without a resident process-wide cache |
+
+## Ten-layer audit
+
+| Layer | Verdict | Evidence / decision |
+| ------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| 1. Compilation correctness | Pass | Rust check and 1,187 Rust tests, TypeScript typecheck, 5,127 Vitest tests, full ESLint, targeted Clippy, and two rendered desktop E2E scenarios passed. |
+| 2. Dead code / structural duplication | Pass | Removed the unbounded file-interaction read path and the process-wide backfill `HashMap`; one paged store query, one durable job table, and existing provider loaders remain. |
+| 3. Naming consistency | Pass | `TurnMetadata` names the UI/cache projection; `ResourceInteraction` names protocol facts; `modifiedFiles` remains the edit-only review subset. |
+| 4. Semantic overloading | Pass | Session, turn, actor, and thread meanings are documented and enforced; the former `thread_id → turn_id` assignment was removed. |
+| 5. Default branches | Pass | Malformed JSON is tolerated, unknown tools are skipped, failed writes do not claim modifications, missed events recover through revision polling, and prior-process jobs are reclaimed without racing a live owner. |
+| 6. Cross-domain leakage | Pass | Provider/tool/Git semantics live in `orgtrack-core`; `session-persistence` calls one provider-neutral accumulator; filesystem/SQLite concerns remain host adapters. |
+| 7. New-developer clarity | Pass | Module docs and the ownership/provider/identity tables identify the one extension point and why the cache is rebuildable. |
+| 8. Wire protocol / serialization | Pass | Rust serde camelCase, Zod, and TS interfaces agree on revision/page fields and the optional bounded turn-id request (500 maximum). |
+| 9. Init parity | Pass | Fresh and legacy DBs both gain parent identity, revisions, triggers, seeded revision rows, indexes, and durable job storage in safe migration order. |
+| 10. Resolver symmetry | Pass | Live hooks, native events, cloud replay, and imported histories converge on Orgtrack rules; provider discovery and transcript parsing continue to reuse existing loaders. |
+
+## Systematic sweeps
+
+| Issue class | Sweep | Outcome |
+| -------------------------- | ------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------- |
+| Duplicate provider parsing | Searched provider loaders, hook adapters, and turn-cache code | Existing loaders/adapters are reused; no new transcript reader was introduced. |
+| Duplicate round projection | Searched file/Git accumulators and host tool-name constants | One `TurnMetadataAccumulator` remains in `orgtrack-core`. |
+| Identity conflation | Searched `turn_id` assignments from `thread_id` | Native and reconciled paths now derive turns from user-message boundaries. |
+| Schema parity | Checked create/ALTER/insert/select/Rust/Zod/TS shapes | All include `resource_interactions_json`; v10 rebuilds historical rows lazily. |
+| Localization | Parsed every locale JSON and compared the new feature keys | All 13 locales include read/search/failure labels. |
+| Unbounded reads/state | Searched file history queries, backfill registries, and turn atoms | Root pages are bounded, job state is durable, and invisible turn atoms are evicted. |
+| Invalidation consumers | Searched every hook-inbox drain call and interaction writer | Every drain broadcasts; native/collaboration writers broadcast; revision remains authoritative. |
+
+## Final verdict
+
+No blocking architecture finding remains. Orgtrack owns the reusable protocol, provider-neutral projection, paged store contract, and durable metadata; ORG2 owns host adapters and disposable UI state. A future extraction is repository packaging/versioning plus host wiring, not a domain redesign. Runtime memory no longer grows with indexed session count: persisted history/job/turn data remains on disk and the open surfaces keep bounded pages only.
+
+## Verification
+
+| Gate | Result |
+| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| Rust app tests | Pass: `cargo test --lib --no-fail-fast` — 936 tests |
+| Rust crate tests | Pass: `orgtrack_core` 230 tests + `session_persistence` 21 tests |
+| Rust compilation | Pass: `cargo check` |
+| Rust lint | Pass for changed crates with `cargo clippy ... --all-targets --no-deps -D warnings`; whole workspace remains blocked by unrelated existing lint debt in `integrations`, `system-services`, and `key-vault` |
+| Frontend types | Pass: `npm run typecheck` |
+| Frontend lint | Pass: `npm run lint` |
+| Frontend unit tests | Pass: 444 files / 5,127 tests |
+| Session Blame E2E | Pass: isolated macOS Tauri/WebDriver with real Claude Code 2.1.210, Codex 0.144.1, and Cursor Agent 2026.07.09-a3815c0 hooks; live refresh, distinct transcripts, and sidebar reveal verified |
+| Round metadata E2E | Pass: isolated macOS Tauri/WebDriver `turn-metadata` scenario against the real Tauri command and SQLite cache |
+| Localization | Pass: all 13 session locale JSON files parse and contain the new keys |
diff --git a/docs/architecture-audit-2026-07-16/CloudOrgManagementTab.md b/docs/architecture-audit-2026-07-16/CloudOrgManagementTab.md
new file mode 100644
index 000000000..63c7b185d
--- /dev/null
+++ b/docs/architecture-audit-2026-07-16/CloudOrgManagementTab.md
@@ -0,0 +1,43 @@
+# Cloud org management tab architecture audit
+
+Scope: the typed `cloud-org` chat tab, all production entry points that open it, activation/close behavior, and the managed-org switcher that updates its payload.
+
+## Acceptance criteria
+
+- Org management never borrows the Launchpad tab identity.
+- At most one org-management chat tab exists.
+- Switching organizations updates that tab in place.
+- Switching away and back restores the selected organization.
+- Leaving, deleting, or losing access to the selected organization closes the stale management tab.
+- Sidebar management and post-create navigation use the same open/focus atom.
+- TypeScript, focused unit tests, lint, and cloud i18n parity pass.
+
+## Term overloading sweep
+
+| Term | Meaning in this change | Verdict |
+| ------------------ | ----------------------------------------------------- | ----------------------------------------------------------------------------------- |
+| Launchpad | The `start-page` tab hosting Work / Manage / Trend | pass — no longer labels cloud organization management |
+| Manage ORG | The singleton managed-cloud organization settings tab | pass — one visible identity and one open/focus action |
+| Manage | Launchpad's workspace-management inner section | keep with reason — separate from cloud-org settings and still owned by `start-page` |
+| Organization / org | The selected managed-cloud organization payload | pass — stored as `ChatPanelSelectedCloudOrg`, not inferred from a title |
+
+## Ten-layer audit
+
+| Layer | Area inspected | Verdict | Reason / evidence | Suggested change |
+| ----: | ------------------------------------ | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- |
+| 1 | Compilation correctness | pass | `npm run typecheck`, focused ESLint, and 30 focused Vitest assertions pass. | None. |
+| 2 | Dead code and structural duplication | pass | Sidebar manage, cloud-org creation, the in-page switcher, activation, and stale-org close all converge on the canonical chat-tab atoms; no parallel tab constructor remains. | None. |
+| 3 | Naming consistency | pass | `cloud-org`, `openCloudOrgManagementInChatPanelTabAtom`, and `closeCloudOrgManagementChatPanelTabAtom` consistently describe tab identity and lifecycle. | None. |
+| 4 | Semantic overloading | pass | Launchpad remains only `start-page`; cloud settings render under a distinct `cloud-org` variant titled `Manage ORG`. | None. |
+| 5 | Default branches | pass | Tab display and activation explicitly handle `cloud-org`; no catch-all maps it to session, workspace, or Launchpad behavior. | None. |
+| 6 | Cross-domain leakage | pass | The generic tab store holds only a typed org identifier; cloud fetching and management remain in `Org2Cloud` / `CloudOrgPanelView`. | None. |
+| 7 | New-developer clarity | pass | The tab payload documents restoration semantics, and the open atom documents singleton switching behavior. | None. |
+| 8 | Wire protocol / serialization | not applicable | No backend request, RPC payload, or external schema changed. The local tab store already starts fresh on app restart. | None. |
+| 9 | Entry-point parity | pass | Sidebar Manage ORG, post-create navigation, and the header org switcher all call `openCloudOrgManagementInChatPanelTabAtom`; leave/delete/roster loss use the matching close action. | None. |
+| 10 | Resolver symmetry | pass | The explicit `cloud-org` display branch resolves the localized Manage ORG label, while activation resolves the selected organization from the typed `cloudOrg` payload regardless of entry point. | None. |
+
+## Systematic sweep
+
+- Searched every `CHAT_PANEL_SURFACE_KIND.CLOUD_ORG` production navigation site; no UI entry point bypasses the dedicated tab constructor.
+- Searched every `ChatPanelTab` type/display/activation branch; the new variant has explicit title, icon, activation, singleton-normalization, and close handling.
+- Existing rendered cloud-org specs were updated to click the visible General / Repo scopes / Members tabs before asserting or mutating their content.
diff --git a/docs/architecture-audit-2026-07-17/ChatHistory.md b/docs/architecture-audit-2026-07-17/ChatHistory.md
new file mode 100644
index 000000000..fee14b610
--- /dev/null
+++ b/docs/architecture-audit-2026-07-17/ChatHistory.md
@@ -0,0 +1,52 @@
+# ChatHistory architecture audit
+
+Scope: the `ChatHistory` public component, its extracted projection, navigation, viewport, item-action, planning-indicator, and render-view modules, plus the existing state/search/pagination hooks they compose.
+
+## Acceptance criteria
+
+- `ChatHistory/index.tsx` is a small composition boundary rather than a state, derivation, effect, and rendering monolith.
+- Public props and default behavior remain compatible.
+- Raw history projection, visible turn pagination, navigation, viewport effects, item actions, and JSX each have one clear owner.
+- The extracted hooks are used by the production entry point and do not create circular imports.
+- Tail-turn collapse and conversation-page mapping have direct characterization tests.
+- TypeScript, focused lint, the complete ChatHistory test surface, and the circular-dependency check pass; unrelated full-suite failures are identified explicitly.
+
+## Term overloading sweep
+
+| Term | Meaning in this change | Verdict |
+| ---------------- | ------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
+| history | The raw event list supplied to `ChatHistory` | keep with reason — the existing public prop remains stable |
+| projected groups | Grouped render records after filtering and display projection | pass — explicitly owned by `useChatHistoryProjectionModel` |
+| turn page | The visible slice of projected groups selected by turn pagination | pass — kept distinct from browser/history-page navigation |
+| active group | The clamped group used for minimap and overview selection | pass — derived in the navigation controller instead of mirrored by a repair effect |
+| item actions | Edit, checkpoint restore, regenerate, and answer callbacks for rendered history items | pass — `useChatHistoryItemActions` avoids collision with the existing history-actions context |
+
+## Ten-layer audit
+
+| Layer | Area inspected | Verdict | Reason / evidence | Suggested change |
+| ----: | ------------------------------------ | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
+| 1 | Compilation correctness | pass in scope | TypeScript and focused ESLint pass for every extracted module. The 21-file ChatHistory suite passes all 294 tests. The repository-wide run reaches 469 passing files but is blocked by a modified websocket schema test outside this scope and two dependency-level `ERR_REQUIRE_ESM` errors. | Resolve the websocket/schema and test-environment failures in their owning change. |
+| 2 | Dead code and structural duplication | pass | `index.tsx` imports and invokes every extracted production hook; the render view consumes their returned models. No parallel copy of the former orchestration remains. | None. |
+| 3 | Naming consistency | pass after fix | The initially ambiguous action-hook name was changed to `useChatHistoryItemActions`, separating item callbacks from the existing `useChatHistoryActions` context hook. | None. |
+| 4 | Semantic overloading | pass | Raw history, projected groups, visible turn pages, and external history-page navigation are represented by separate names and owners. | None. |
+| 5 | Default branches | pass | Existing defaults remain explicit in `ChatHistory.types.ts` and the entry point: standard grouping, collapsed groups, and inert external-navigation contracts. | None. |
+| 6 | Cross-domain leakage | pass | Planning atom reads are isolated in `PlanningIndicatorBridge`; the view receives render data and callbacks without reaching into projection or viewport state. Agent-org data remains an explicit view prop. | None. |
+| 7 | New-developer clarity | pass | The 206-line entry point reads top-to-bottom as state, projection, navigation, viewport, actions, and view composition. Each extracted filename describes its responsibility. | None. |
+| 8 | Wire protocol / serialization | not applicable | No network payload, persistence shape, schema, or serialization boundary changed. | None. |
+| 9 | Entry-point parity | not applicable | `ChatHistory` has one public production entry point; this refactor did not add alternate initialization paths. | None. |
+| 10 | Resolver symmetry | not applicable | No multi-source configuration or identity resolver changed. | None. |
+
+## Systematic sweep
+
+- Traced the public `ChatHistory` entry point through each new hook and into `ChatHistoryView`; every new production abstraction is on that call chain.
+- Searched the hook barrel and direct imports for the action-hook naming class; the item-action hook is now distinct from the existing context action hook.
+- Ran the repository circular-dependency check across 5,286 files; it reported no circular dependency.
+- Kept public prop exports at the package entry point while moving their definitions to `ChatHistory.types.ts`, avoiding duplicate prop contracts.
+- Ran the repository-wide test suite: 469 files / 5,247 tests passed; `src/api/realtime/websocket/schemas.test.ts` has one out-of-scope failure in an independently modified file, and the runner reports two `html-encoding-sniffer` / `@exodus/bytes` module-format errors.
+
+## Summary
+
+- **fixes applied: 1** — resolved the action-hook naming collision.
+- **abstractions introduced: 7** — projection, navigation, viewport, item actions, tail collapse, planning bridge, and render-only view.
+- **kept with reason: 3** — public prop names/defaults, the existing hook barrel, and view-local `useGroupHeaderRenderer` composition.
+- **not applicable layers: 3** — wire protocol, entry-point parity, and resolver symmetry.
diff --git a/docs/architecture-audit-2026-07-17/CliVersionScan.md b/docs/architecture-audit-2026-07-17/CliVersionScan.md
new file mode 100644
index 000000000..ab1b9ad35
--- /dev/null
+++ b/docs/architecture-audit-2026-07-17/CliVersionScan.md
@@ -0,0 +1,41 @@
+# CLI Version Scan Architecture Audit
+
+## Acceptance criteria
+
+- [x] A version scan starts only after the user selects a CLI in Session Creator.
+- [x] Each actual scan probes the exact resolved executable with `--version` and queries its read-only official release source.
+- [x] A fresh per-CLI observation is reused for twelve hours; there is no timer, scan-all path, or background polling loop.
+- [x] Version scanning reads no credentials, invokes no updater, and writes nothing to Key Vault.
+- [x] Session creation remains available when installed/latest version detection fails.
+- [x] The inline warning appears only when both versions are comparable and the installed version is older.
+- [x] Chat history preserves every CLI error, including repeated errors and errors preceding a recovered final reply.
+
+## 10-layer review
+
+| Layer | Scope checked | Verdict | Evidence |
+| -------------------------- | -------------------------------------------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------- |
+| 1. Compilation | integrations resolver, key-vault command, Tauri handler, RPC/Zod, React | pass | `cargo check -p org2 --bin org2` and `pnpm typecheck` |
+| 2. Dead code / duplication | version parsing, status comparison, old repeated-error UI | pass | installed parsing is resolver-owned; latest parsing/status is command-owned; repeat-count field/rendering removed |
+| 3. Naming | scan vs update; installed/latest/current/outdated/unknown | pass | public names describe observations and never imply mutation |
+| 4. Semantic overloading | unavailable source, network failure, unparseable version, older version | pass | only a comparable older version maps to `outdated`; all other indeterminate cases map to `unknown` |
+| 5. Default branches | missing binary, missing release source, timeout, endpoint error, cache hit | pass | bounded diagnostics are returned without blocking session creation |
+| 6. Cross-domain leakage | Key Vault and credential environment | pass | scanner receives an agent type and resolved binary only; snapshot contains version metadata only |
+| 7. New-developer confusion | cache ownership and scan trigger | pass | comments identify the twelve-hour backend/frontend caches and selection-only trigger |
+| 8. Wire protocol | Rust snapshot ↔ Zod ↔ typed service/hook | pass | field names and enum values match exactly across the boundary |
+| 9. Init parity | first selection, re-render, concurrent calls, forced future refresh | pass | frontend deduplicates promises; backend is authoritative for the twelve-hour cache |
+| 10. Resolver symmetry | discovered binary vs launched binary | pass | version probe uses the centralized resolved executable path, not a new PATH lookup |
+
+## Default-branch analysis
+
+| State | Result | Session impact |
+| -------------------------------------- | ------------------------------------- | -------------------------------------------------- |
+| installed < latest | `outdated` | warning shown; launch remains enabled |
+| installed = latest | `current` | no warning |
+| installed > latest | `current` | no false warning for prerelease/newer local builds |
+| installed/latest missing or unparsable | `unknown` | no warning; CLI errors remain visible in Chat |
+| release endpoint fails/times out | `unknown` with bounded diagnostic | no warning; no retry loop |
+| fresh cached snapshot | returned without process/network work | no added CPU/network churn |
+
+## Findings
+
+No unresolved architecture finding remains in this scope. Three CLIs currently have no stable read-only latest-version endpoint; they still report the installed version and remain `unknown` rather than invoking a self-updater or fabricating a latest version. Kiro and Antigravity use the version field from their official read-only release manifests.
diff --git a/docs/architecture-audit-2026-07-18/SharedSessionPolling.md b/docs/architecture-audit-2026-07-18/SharedSessionPolling.md
new file mode 100644
index 000000000..32e108024
--- /dev/null
+++ b/docs/architecture-audit-2026-07-18/SharedSessionPolling.md
@@ -0,0 +1,121 @@
+# Shared-session polling architecture audit
+
+Scope: managed-cloud shared sessions (`Org2Cloud` + the surviving `TeamCollaboration` project bridge), including the root sync/realtime entry points, comments, remote-session listings, project/work-item sync, comment-task execution, and the API-call hotspot UI shown in the supplied capture.
+
+## Outcome
+
+The dominant problem is not a collection of independent timers. It is one coarse Realtime invalidation that behaves like event-driven polling: every `org_change_signals` row update fans out into roster, entitlement, repo-scope, project/work-item, comment-task, remote-session, and open-comment-thread reads. Several of those reads then multiply across every org or across duplicate mounted consumers.
+
+Implementation update: the client-side multipliers identified here are now fixed. Realtime inbound requests are per-org and delta-preserving, reconnect is the only full-listing path, waiting no longer starts a second pass, ordinary signals no longer read the org roster, and sharing-policy recovery is a per-org entitlement read capped at once per minute. Remote-session/comment requests share keyed deduplication, optimistic comment mutations notify peers without invalidating themselves, Realtime-only project pulls skip the outbox drain, roster changes reload only members, and address-comment completion is event-driven with a 60-second deadman check. The remaining coarse behavior requires a server wire-protocol change: `org_change_signals` still lacks a plane, entity/session hint, origin, and monotonic per-plane revision.
+
+The screenshot is consistent with this call graph:
+
+- 9 `cloud_list_org_collab_state`, 9 `project_collab_outbox_drain`, and 9 `cloud_list_comment_tasks` calls form one repeated sync-cycle cluster.
+- 18 `cloud_list_session_comments` calls are consistent with two simultaneously mounted `useSessionComments` consumers for the same session. The second forced subscriber queues a second fetch behind the first.
+- 16 `list_my_orgs` calls versus 9 serialized sync cycles are consistent with Realtime callbacks starting roster reads without the sync engine's coalescing.
+- The panel says **9 likely polling** but renders only six cards because `PanelContent.tsx` uses `hotspots.slice(0, 6)`. The exact identities of the hidden three are not recoverable from the PNG. Source-derived candidates are `get_entitlement_state`, `cloud_list_org_sessions`, and `project_collab_apply_remote` when a non-empty full listing is repeatedly applied.
+
+```mermaid
+flowchart LR
+ S["org_change_signals update"] --> R["refetchOrgs"]
+ R --> O["list_my_orgs"]
+ R --> E["get_entitlement_state × every org"]
+ S --> I["invalidateOrgInbound(orgId)"]
+ I --> F["global inbound pass"]
+ F --> Q["cloud_get_org_repo_scopes"]
+ F --> P["cloud_list_org_collab_state × every org"]
+ P --> D["project_collab_outbox_drain × every org"]
+ F --> T["cloud_list_comment_tasks × every org"]
+ S --> L["remote-session version bump"]
+ L --> LS["cloud_list_org_sessions × mounted consumer"]
+ S --> C["org-wide comments version bump"]
+ C --> LC["cloud_list_session_comments × open session × duplicate subscriber"]
+```
+
+## Acceptance criteria for a polling fix
+
+- One ordinary server mutation causes at most one coalesced delta pull for the affected org and affected plane.
+- A normal Realtime event does not clear full-listing latches; only initial connect/reconnect or an explicit revocation-reconciliation event requests a full listing.
+- A change in org A does not pull project or comment-task state for org B.
+- Project/work-item/comment/session changes do not refetch `list_my_orgs`, entitlement, members, or repo scopes.
+- One comments invalidation causes at most one listing for the affected session regardless of mounted surfaces; an already-patched local mutation causes no local listing.
+- One remote-session invalidation causes at most one listing per org regardless of sidebar/panel consumers.
+- Attaching a waiter to an active sync pass does not mark that pass dirty or schedule another pass.
+- An empty project outbox is not drained on every inbound pull.
+- Long-running address-comment runs observe authoritative session-finality events; status polling is fallback-only.
+- The hotspot UI can reveal all groups that contribute to its “likely polling” count.
+
+## Finding sweep
+
+| Priority | Line / element | Verdict | Reason | Suggested change |
+| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| P0 | `useOrg2CloudRealtime.ts:215-233` — `org_change_signals` `onChange` | fix | One untyped org-wide event triggers roster refetch, entitlement hydration, full inbound sync, remote-session refresh, every open comment thread, and task-runner work. A session push, project update, work-item update, task heartbeat, or comment edit all pay for unrelated planes. | Put plane/revision/session metadata on the signal (or split signals by plane). Coalesce by `(orgId, plane)` and dispatch only the affected consumers. Include an origin/revision so a client can cheaply ignore or acknowledge its own already-applied write. |
+| P0 | `org2CloudSyncEngine.ts:521-536, 909-961` — `forceInboundNextPass` | fix | The caller supplies `orgId`, but the engine stores one global boolean. The next inbound pass loops over **all orgs**, so one org's event pulls every org's project/work-item and task planes. | Replace the boolean with pending per-org plane masks, e.g. `Map>`; consume only requested orgs/planes. Reserve an explicit all-org mode for reconnect recovery. |
+| P0 | `org2CloudSyncEngine.ts:523-527, 1070-1078, 1145-1153` — full-listing latch reset | fix | Every ordinary signal deletes the affected org's full-listing latches. The next project and task reads therefore omit their persisted cursors and fetch complete listings, defeating the delta design documented in those methods. | Keep cursors/latches for ordinary change events. Request `full` only on initial subscribe/reconnect, membership/visibility revocation, or a signal that explicitly says absence reconciliation is required. |
+| P0 | `useOrg2CloudRealtime.ts:220`; `org2CloudOrgsAtom.ts:308-366` — roster/entitlement refresh | fix | Every broad change signal invokes `list_my_orgs`; every successful refetch then launches `get_entitlement_state` once per org. Calls are not single-flight, so bursts still spend network work even when older responses are discarded by request epochs. | Refetch roster only on membership/org lifecycle events. Give roster and entitlement separate cached stores, single-flight per key, TTL/backoff, and explicit invalidations. Do not hydrate entitlements as a side effect of every roster read. |
+| P0 | `SessionCommentsHeaderExtras.tsx:50-58`; `SessionCommentsContext.tsx:202-216`; `org2CloudSessionCommentsAtom.ts:460-483, 538-542` — duplicate comments subscribers | fix | The active shared session mounts two `useSessionComments` instances for the same key. On a forced signal the first claims the fetch; the second sees `loading` and records `queue_force`, guaranteeing a second sequential listing even though both observed the same signal generation. This matches the screenshot's 18 comment calls versus 9 sync-cycle calls. | Move invalidation consumption into a module-level keyed request coordinator, deduped by signal generation. Alternatively make the header consume a shared session-level store/provider bridge instead of owning a second fetching hook. Queue a second force only when its server revision is newer than the in-flight request's revision. |
+| P1 | `org2CloudCommentsBus.ts:59-70`; `org2CloudSessionCommentsAtom.ts:651-729`; `SessionCommentsContext.tsx:256-270` — local broadcasts | fix | Add/edit/delete/resolve already patch the local atom from the mutation result, but `broadcastCommentsChanged` also bumps the local signal and forces a listing. Task creation explicitly calls `refresh()` and then bumps the same signal. With two consumers this commonly becomes two follow-up listings, followed later by the durable org signal. | Split “notify peers” from “invalidate this client.” Optimistically patched callers should broadcast to peers without bumping local fetch state. Callers lacking a returned row should perform one keyed local refresh, not both `refresh()` and local broadcast invalidation. |
+| P1 | `org2CloudRemoteSessionsAtom.ts:122-183`; `cloudSessionsSection.tsx:159`; `CloudSessionsSection.tsx:37` — per-hook request locks | fix | Sidebar and cloud-management session sections can mount for the same org. Each hook instance owns its own in-flight set and last-version map, so both may issue `cloud_list_org_sessions` for one invalidation. The broad signal also bumps this list for project/task/comment-only changes. | Store the in-flight promise and processed revision at module/store scope by org. Invalidate this list only for session/share/access changes. Reuse the returned server session row after local push/share mutations where possible. |
+| P1 | `useOrg2CloudRealtime.ts:220-224`; `org2CloudSyncEngine.ts:474-509, 521-536` — invalidate then wait | fix | `invalidateOrgInbound()` starts `runSyncPass()` immediately. The following `runSyncPassAndWaitForDrain()` calls `runSyncPass()` again; because a pass is active, it sets `passDirty`, guaranteeing a follow-up pass merely to attach a waiter. The same pattern appears in `resumeOrg(); runSyncPassAndWaitForDrain()` callers. | Expose one request API that records intent and optionally returns a drain promise, e.g. `requestSync(intent, { wait: true })`. Attaching a waiter must not itself set dirty. Dirty should mean new unconsumed work only. |
+| P1 | `ProjectSyncChannel.ts:115-119, 142-152` — unconditional outbox drain | fix | Every project pull calls `pushOutbox`, which invokes `project_collab_outbox_drain` even when no local mutation exists. The screenshot's drain count exactly tracks project-state pulls. | Separate inbound apply from outbound drain. Maintain a per-org dirty/pending signal from local mutations and Rust retry deadlines; drain only when dirty/retry is due. The low-frequency safety pass may retain a bounded pending probe. |
+| P1 | `ProjectSyncChannel.ts:121-139`; `org2CloudSyncEngine.ts:1070-1093` — repeated full remote apply | fix | Resetting the full-listing latch means the same non-empty project/work-item set is repeatedly sent through `project_collab_apply_remote`; Rust may no-op it, but IPC, decoding, and field comparisons are still paid. `notifyDataChanged` can then schedule another projects pass when anything applies. | Preserve delta cursors on normal events and include a server revision gate before IPC. Keep the existing `entities.length === 0` skip; add “revision already applied” identity/no-op protection before calling the bridge. |
+| P2 | `CloudOrgPanelView/index.tsx:254-330` — roster-version effect | fix | A teammate membership change makes the panel refetch entitlement, full member roster, and repo scopes together. Only the member roster is necessarily stale; the other calls can appear as hidden hotspots while the panel is open. | Split the panel loaders by data owner and invalidation key. Membership version refreshes members; plan/policy refreshes entitlement; scope-policy version refreshes scopes. Back them with shared per-org caches. |
+| P2 | `addressCommentsRun.ts:388-400` — 4-second status poll | fix | While an address-comments run is active, `SessionService.getStatus` runs 15 times/minute for up to 15 minutes (up to 225 status calls), despite the app already having session lifecycle/event infrastructure. This is not in the six visible cloud RPC cards but is shared-session-specific polling. | Await the authoritative turn/session terminal event or lifecycle store with a generation guard. Retain a much slower deadman poll only for dropped-event recovery and preserve the 15-minute deadline. |
+| P2 | `router/index.tsx:26-35`; `org2CloudSyncEngine.ts:412-430`; `useOrg2CloudRealtime.ts:167-180, 235-243` — startup/reconnect burst | fix | Root startup independently starts an immediate sync pass, a roster subscribe compensation read, and one full inbound invalidation per org. Near-simultaneous subscriptions are serialized but can leave dirty follow-ups and duplicate complete listings. | Add a bootstrap barrier: load roster, establish subscriptions, then issue one all-org full reconciliation generation. Reconnects should coalesce true-edge status events across channels into one bounded recovery pass. |
+| P3 | `PanelContent.tsx:60-64, 77-79` — hotspot visibility | fix | The heading counts every likely-polling group but the UI renders only the first six, which is why the capture says 9 while showing 6. This hides lower-rate multipliers from the exact workflow used to diagnose them. | Add “show all”/expand behavior or render all likely-polling rows before non-polling rows. Preserve the six-card compact default only when the count is six or fewer. |
+| Keep | `org2CloudSyncEngine.ts:151-198, 544-573` — recurring safety chain | keep with reason | One 60-second visible timer, stretched to five minutes while hidden, is a reasonable outbound safety net. Inbound work is already gated to five minutes and scopes/events to ten minutes. The timer is not the source of the 4.5-9/min rates; the coarse invalidation bypass is. | Keep the single chain, but make every pass consume precise dirty/revision state. Consider moving the ordinary visible safety cadence to five minutes once event-driven outbound coverage is proven. |
+| Keep | `commentTaskRunner.ts:388-416`; `org2CloudCommentTasksClient.ts:40-44` — lease heartbeat | keep with reason | A 60-second heartbeat renews a 15-minute fenced lease and carries bounded progress only while a real task is running. It is protocol maintenance, not idle polling. | Keep. Ensure heartbeats do not invalidate unrelated planes; their task-plane signal should update only that task/org. |
+| Keep | `org2CloudRealtimeClient.ts:258-315` — presence retry | keep with reason | The one-second retry is failure-driven, coalesced to the latest payload, and constrained by a connection-wide five-calls/30-second scheduler. It is not an always-on loop. | Keep; expose retry counts in push/timer diagnostics if it becomes noisy. |
+| Keep | `org2CloudOrgsAtom.ts:197-285` — initial roster retry | keep with reason | The 2/5/10/20-second retry sequence is bounded and exists only after initial-load failure. The excessive roster traffic comes from unbounded broad-signal refetches, not this recovery sequence. | Keep the bounded recovery, but share its single-flight/backoff state with imperative roster refreshes. |
+
+## Pre-fix trigger / call matrix
+
+| Trigger | Current remote/IPC effects | Excess mechanism |
+| -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
+| Any `org_change_signals` change | `list_my_orgs`; `get_entitlement_state × orgs`; scopes for invalidated org; project/task pulls for all orgs; outbox drains for all orgs; remote-session listing for mounted active org; comment listings for every mounted open session in org | Coarse signal, global boolean, full-latch reset, per-hook locks, no roster single-flight |
+| Local shared-session event write | 3-second debounced sync; session metadata/events push; server signal echoes back into the full fan-out above | Legitimate outbound write causes broad self-invalidation |
+| Local project/work-item mutation | 1.5-second project pass; project pull + outbox drain/push; server signal; possible remote-apply `orgii-data-changed` follow-up | Push and pull are inseparable; empty drains and one accepted echo pass |
+| Local comment mutation | Mutation RPC; local atom patch; local forced listing; peer broadcast; durable org signal; another forced listing | Local notification is conflated with peer invalidation; duplicate subscribers queue a second force |
+| Realtime reconnect/startup | Roster read plus one full invalidation per org plus independent bootstrap sync | No connection-generation bootstrap barrier |
+| Address-comments execution | `session_get_status` every 4 seconds; task lease heartbeat every 60 seconds when applicable | Status completion is polled rather than observed; heartbeat is intentional |
+
+## Term-overloading sweep
+
+| Term | Current meanings | Verdict |
+| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
+| `org_change_signals` | Session rows, comments, tasks, projects, work items, sharing policy, and possibly scope/entitlement changes | fix — one name/payload cannot express which consumer is stale |
+| `invalidateOrgInbound(orgId)` | Clear backoff, clear full-listing state, clear scope TTL, force all-org inbound work, and start execution | fix — invalidation intent and scheduling are overloaded |
+| `runSyncPass` | Session outbound push, repo-scope hydration, project pull, project outbox push, comment-task pull, reconciliation, and retry | keep only behind plane-specific intent; the current entry point is too broad for every trigger |
+| `force` comments refresh | A genuinely newer server revision, a duplicate subscriber observing the same signal, an explicit user refresh, or a mutation caller requesting reconciliation | fix — carry a revision/reason so equal work dedupes and newer work queues |
+| “polling” in DevTools | Fixed timers and repeated event-driven fetch loops are both classified from repeated automatic calls | keep with reason — the monitor correctly exposes the user-visible cost even when the trigger is Realtime rather than a timer |
+
+## Ten-layer audit
+
+| Layer | Area inspected | Verdict | Reason / evidence | Suggested change |
+| ----: | ---------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| 1 | Compilation correctness | pass | Full TypeScript check passes. Six focused suites pass: 139 tests, including scoped/delta Realtime pulls, pull-only project-channel coverage, and event-driven status completion. | Keep the focused call-budget assertions as regression gates. |
+| 2 | Dead code / structural duplication | fix | Two comments fetch owners and two possible remote-session fetch owners exist for the same keys; push/pull scheduling also has parallel “invalidate then wait” entry points. | Centralize keyed request ownership and sync intent scheduling. |
+| 3 | Naming consistency | fix | `invalidateOrgInbound(orgId)` sounds org-scoped but sets a global inbound flag and clears unrelated caches/latches. | Name intent explicitly: org set, plane mask, delta/full mode, wait policy. |
+| 4 | Semantic overloading | fix | One change signal and one sync pass carry several unrelated domains. `force` cannot distinguish a newer revision from a duplicate observer. | Use typed plane/revision/origin signals and reasoned fetch requests. |
+| 5 | Default-branch analysis | fix | With no plane discriminator, the implicit default is “refresh everything.” With no revision discriminator, the default forced in-flight behavior is “queue another fetch.” These defaults are unsafe under new signal producers or new consumers. | Make exhaustive plane handling mandatory and ignore equal/older revisions. |
+| 6 | Cross-domain leakage | fix | The Realtime React hook directly orchestrates roster, entitlement, sync engine, sidebar sessions, comments, and task execution. Transport delivery knows product-domain refresh policy. | Route transport frames into a domain-neutral invalidation coordinator; each domain consumes its own typed revision. |
+| 7 | New-developer clarity | fix | Comments claim the engine has the only recurring pull and header atom sharing prevents duplicate fetches, but event-driven refetches and forced queueing violate the practical guarantee. | Document call budgets and ownership invariants next to the coordinator; replace comments that describe timer count with end-to-end request-count guarantees. |
+| 8 | Wire protocol / serialization | fix | `org_change_signals` carries insufficient invalidation information: the client has only org identity, not plane/session/entity revision or origin. That forces full RPC reads to discover what changed. | Extend the signal row/payload or use per-plane signal rows with monotonic revisions and optional entity/session hints. Inspect the actual Realtime payload in an integration test. |
+| 9 | Entry-point parity | fix | Bootstrap timer, visibility resume, local event activity, local project mutations, Realtime, reconnect compensation, and user policy changes all enter the same pass with different intent, and some callers start then separately wait. | One `requestSync(intent)` entry point should merge intent monotonically and provide optional drain completion without scheduling duplicate work. |
+| 10 | Resolver symmetry | fix | The method accepts one org, clears only that org's latches, then resolves work through an all-org boolean. Comments resolve a session key but also listen to an org-wide wildcard. Scope, project, task, and session refresh dimensions are asymmetric. | Carry org and plane identity through every stage; resolve only targeted keys, with an explicit all-org/all-plane recovery mode. |
+
+## Implementation status
+
+1. Done client-side: coalescing per-org sync intent, delta-preserving ordinary invalidations, full reconnect recovery, and one drainable request API.
+2. Partly done: roster was removed from broad signals and inbound work is org-scoped. Server-side plane/revision/origin metadata remains the highest-value follow-up.
+3. Done: comments dedupe by signal generation, remote-session locks/version watermarks are store-shared, and peer-only comment broadcasts avoid local read-after-write fetches.
+4. Done: Realtime-only project pulls do not probe the outbox; address-comment status uses the session store with a sparse recovery check.
+5. Partly done: focused request-count regressions and all likely-polling DevTools cards are present. A dual-instance end-to-end call-budget assertion remains recommended.
+
+## Verification performed
+
+- Systematic source sweep for `setInterval`, recursive `setTimeout`, polling/status loops, Realtime invalidations, forced refreshes, sync-pass entry points, and all cloud RPC names across `Org2Cloud` and `TeamCollaboration`.
+- Forward call-chain trace from the router root and `org_change_signals` callback through roster, sync, project bridge, remote-session, comments, and task-runner consumers.
+- Mount trace confirming the duplicate comments consumers and the possible duplicate remote-session consumers.
+- Full typecheck: `pnpm typecheck` — passed.
+- Focused tests: `pnpm vitest run src/features/Org2Cloud/org2CloudSyncEngine.test.ts src/features/Org2Cloud/org2CloudSessionCommentsAtom.test.ts src/features/Org2Cloud/addressCommentsRun.test.ts src/features/Org2Cloud/org2CloudOrgsAtom.test.ts src/features/TeamCollaboration/engine/ProjectSyncChannel.test.ts src/modules/shared/DevTools/APICallPanel/components/PanelContent.test.ts` — 139/139 passed.
diff --git a/docs/architecture/agentsview-lessons-orgtrack-session-analytics--0624.md b/docs/architecture/agentsview-lessons-orgtrack-session-analytics--0624.md
index 501c75329..15d70f422 100644
--- a/docs/architecture/agentsview-lessons-orgtrack-session-analytics--0624.md
+++ b/docs/architecture/agentsview-lessons-orgtrack-session-analytics--0624.md
@@ -9,7 +9,7 @@ status: active
Integrate the useful parts of `agentsview` into ORGII without creating a second
session analytics system. ORGII should keep `orgtrack` and the existing
`unified_stats` Tauri API as the shared pipeline for session discovery,
-activity, token accounting, cost reporting, Dev Record, ops control, and chat
+activity, token accounting, cost reporting, Dev Record, Kanban, and chat
start-page analytics.
## What agentsview does well
@@ -92,7 +92,7 @@ Dev Record’s shared `HeatmapGrid`. The card displays:
This keeps the heatmap visible at the point where users decide what work to do
next.
-### Dev Record / ops-control improvements
+### Dev Record / work-management improvements
Dev Record session rows now use computed cache-aware cost instead of hardcoded
zero-cost placeholders. Expanded per-round rows show cache write/read token
diff --git a/docs/architecture/managed-cloud-collaboration.md b/docs/architecture/managed-cloud-collaboration.md
new file mode 100644
index 000000000..dbd48bbfb
--- /dev/null
+++ b/docs/architecture/managed-cloud-collaboration.md
@@ -0,0 +1,201 @@
+# Managed Cloud Collaboration
+
+This document is the canonical design for ORGII managed-cloud collaboration.
+It replaces the dated implementation audits and E2E run reports that were
+useful while the feature was being built but are not part of the maintained
+product contract.
+
+## Product model
+
+ORGII has three collaboration scopes with deliberately different semantics:
+
+1. **Personal** is local/private. It never exposes a cloud organization's
+ roster and cannot be used as a destination for direct member sharing.
+2. **Organization** is the durable team boundary. Membership, roles, sharing
+ policy, Projects, Work Items, comments, and direct session grants are
+ authorized by the managed backend.
+3. **Link capability** is an explicit guest path. Creating a link is separate
+ from sharing with an organization member and the link can expire or be
+ revoked independently.
+
+Selecting an organization member creates a direct grant; it does not generate
+a link. The recipient sees that session in **Shared directly with me** without
+copying a URL. Link generation remains an explicit action and always exposes a
+Copy control.
+
+An imported shared session or local Codex/Claude/Cursor history is immutable
+at its source. The user may inspect and comment where cloud authorization
+exists. On the first attempt to continue the conversation, ORGII asks for a
+local repository/workspace with the same Git remote plus the local account and
+model, then creates a writable ORGII-owned fork and sends the message there.
+Cancelling the picker preserves the unsent message.
+
+## Ownership and authorization
+
+- The backend is authoritative for cloud organization membership, roles,
+ policies, grants, invite state, ownership transfer, and deletion.
+- Local aliases connect a cloud organization to local project storage but do
+ not redefine cloud identity or authorization.
+- A direct grant, organization visibility, and a link capability are separate
+ authorization concepts. Code must not infer one from another.
+- Revocation and deletion invalidate local caches immediately through
+ Realtime. Polling is recovery-only.
+- The last-owner and role-transition invariants are server transactions, not
+ UI conventions.
+
+## Data planes
+
+### Durable entities
+
+Projects and Work Items use local-first persistence plus a durable SQLite
+outbox. Each mutation has one typed entity operation, a stable entity ID,
+organization scope, and an expected remote version. The sync engine:
+
+1. commits the local mutation and outbox record atomically;
+2. pushes eligible operations in order;
+3. applies optimistic concurrency control (OCC) at the backend;
+4. pulls/invalidate on Realtime signals;
+5. resolves or surfaces conflicts deterministically; and
+6. retries at the recorded eligibility time, including while the app is
+ hidden.
+
+Remote tombstones are permanent convergence operations. User-initiated local
+deletion may remain recoverable, so the sync worker uses a distinct purge path.
+Deleting a Project also deletes its child Work Items; children must never be
+silently converted into standalone items by an FK default.
+
+### Comments and agent tasks
+
+Session comments are durable cloud rows. Replies retain their thread root,
+edits and deletes converge live, and status is a typed tri-state value. An
+`@agent` mention is stored as structured task intent and rendered as a pill,
+not inferred later from plain text. The Address Comments action operates on an
+explicit selection and links agent output back to the originating comment.
+Top-level comments have exactly one scope: no event anchor means a session
+note applying to the session as a whole; an event anchor means a round comment.
+Address Comments groups both scopes, selects both by default, permits
+scope-level selection, and carries the scope into the agent briefing.
+
+### Presence
+
+Presence is ephemeral awareness, never the source of truth for membership,
+locks, or durable edits. Only the active organization publishes tracking
+state. Inactive channels may listen, and connection-wide updates are
+coalesced to stay within transport limits. Leaving an organization untracks
+before disposing its channel.
+
+### Execution locks
+
+A Work Item execution lock identifies the active session and role. Start,
+retry, cancel, and lock-holder UX all use the same Work Item orchestrator in
+detail views and ChatPanel. Lock release is serialized as explicit JSON
+`null`; an omitted field means "unchanged", not "clear".
+
+## Create with AI
+
+Create with AI uses `builtin:work-item-manager` by default and follows one
+durable-draft invariant:
+
+1. Before launch, the UI allocates a cloud-aware Work Item ID and writes one
+ draft in the selected Project or organization-scoped standalone store.
+2. The launched session is durably linked to that draft.
+3. The Work Item Manager receives a volatile system section containing the
+ exact `short_id` and Project scope. It updates the linked draft instead of
+ creating a duplicate unless the user explicitly asks for multiple items.
+4. `project_slug` is omitted for standalone Work Items; a fake Personal
+ Workspace Project is never invented.
+5. The session link survives every update and is visible from both the Work
+ Item and session surfaces.
+
+The Work Item Manager may research with read-only tools and mutate Projects or
+Work Items through their typed management tools. It cannot edit repository
+files or run shell commands.
+
+## Client boundaries
+
+- `org2CloudClient` owns authenticated HTTP transport and token refresh.
+- Entity-specific clients own wire shapes for organizations, sessions,
+ shares, comments/tasks, Projects, and Work Items.
+- `org2CloudRealtimeClient` owns subscription lifecycle and reconnect
+ behavior.
+- `org2CloudSyncEngine` owns durable project-plane convergence and retry.
+- Jotai atoms expose UI state; they do not become alternate persistence or
+ authorization layers.
+- Rust owns local project/work-item transactions, session persistence,
+ execution locks, agent tools, and desktop commands.
+
+There is one endpoint snapshot per authenticated operation. A token refresh
+must not silently move an in-flight request to a different endpoint. Unknown
+credential/provider rows are preserved when an older build updates a known
+account so account refresh cannot destroy forward-version data.
+
+## Desktop instance isolation
+
+Instance profiles are created at build time. Instance `N` has an independent:
+
+- product name and bundle identifier;
+- deep-link schemes;
+- ORGII home and WebKit storage;
+- IDE server port (`13846 + N`); and
+- local managed-cloud proxy port (`17887 + N`).
+
+The supported commands are:
+
+```sh
+pnpm run tauri:build:fast
+open src-tauri/target/dev-build/bundle/macos/ORG2.app
+
+pnpm run tauri:build:fast -- --instance 2
+pnpm run tauri:open:instance -- --instance 2
+```
+
+Copying and patching an already-built app bundle is not supported because it
+can leave the frontend, schemes, ports, and data home with different identity.
+
+## Backend migration contract
+
+Backend RPC/schema changes live in `orgii-cloud-infra` and must be deployed
+before a desktop release that calls them. Migrations are dated, idempotent, and
+must be run against local Supabase twice before production rollout. In
+particular, production must include the Work Item lock-release delta that
+writes `executionLock: null`; the desktop cannot compensate for an old RPC
+that removes the key.
+
+Desktop code must not auto-run production SQL or mutate production
+organizations during tests. Production migration is an explicit operator
+step with the infra repository's migration history as the source of truth.
+
+## Verification contract
+
+Changes to this feature are complete only when the affected layers pass their
+own gates. A skipped scenario is never reported as a pass.
+
+- TypeScript: typecheck plus focused unit tests for changed clients, atoms,
+ reducers, filters, clipboard, and UI state machines.
+- Rust: focused crate tests for changed persistence, tools, session launch,
+ locks, and sync code.
+- Local cloud: Auth/PostgREST/Realtime/RPC assertions against disposable users.
+- Rendered single-instance UI: signed-out/in, organization scope, sharing,
+ import/fork, comments/tasks, presence policy, Projects, Work Items, and real
+ provider execution.
+- Rendered dual-instance UI: isolation, invite/join, direct share, link share,
+ revoke, comments, Project/Work Item convergence, lock ownership, offline OCC,
+ roles, leave/remove/reactivate, ownership transfer, and typed deletion.
+- Create with AI: a real provider must update the single linked draft through
+ the rendered composer, preserve the session link, and create no duplicate.
+- Packaging: build and launch main plus Instance 2 concurrently and verify
+ listener ownership and visible identity.
+
+OAuth-live runs use an explicitly selected real account. Tests must never fall
+back to an unrelated account merely because it is available.
+
+## Explicit non-goals
+
+- Presence is not a durable collaborative document protocol.
+- Typed business entities are not converted wholesale to CRDTs. A future rich
+ document body may use a per-artifact CRDT behind a narrow interface while
+ metadata, ACLs, Work Item transitions, tombstones, and OCC stay typed.
+- Guest links do not grant organization membership.
+- Import does not make remote history writable.
+- Browser OAuth callback allow-list verification and production migration are
+ external release gates; deterministic local JWT tests do not prove them.
diff --git a/docs/architecture/warp-imported-history.md b/docs/architecture/warp-imported-history.md
new file mode 100644
index 000000000..9b92919dd
--- /dev/null
+++ b/docs/architecture/warp-imported-history.md
@@ -0,0 +1,77 @@
+# Warp imported history
+
+ORGII imports Warp's locally stored Agent conversations as read-only external history. It does not launch Warp, mutate Warp data, or attempt to download cloud-only conversations.
+
+## Source contract
+
+| Field | Value |
+| --------------------- | --------------------------------- |
+| Source ID | `warp` |
+| ORGII session prefix | `warpapp-` |
+| Store kind | SQLite |
+| Database | `warp.sqlite` |
+| Conversation metadata | `agent_conversations` |
+| Transcript payload | `agent_tasks.task` protobuf blobs |
+| Protobuf message | `warp.multi_agent.v1.Task` |
+
+The importer uses Warp's published descriptor at revision [`2d0e8dd`](https://github.com/warpdotdev/warp-proto-apis/tree/2d0e8ddf5a946a663f7e0952144ccbced0068a81) through `warp_multi_agent_api`. Dynamic protobuf decoding avoids a parallel hand-maintained schema and preserves compatibility with fields the importer does not yet interpret.
+
+## Database discovery
+
+Candidates are tried in a stable order and deduplicated. The first existing database is opened with SQLite read-only flags.
+
+| Platform/channel | Candidate |
+| ---------------- | -------------------------------------------------------------------------------------------------------------- |
+| macOS Stable | `~/Library/Group Containers/2BBY89MBSN.dev.warp/Library/Application Support/dev.warp.Warp-Stable/warp.sqlite` |
+| macOS Preview | `~/Library/Group Containers/2BBY89MBSN.dev.warp/Library/Application Support/dev.warp.Warp-Preview/warp.sqlite` |
+| macOS legacy | `~/Library/Application Support/dev.warp.Warp-{Stable,Preview}/warp.sqlite` |
+| Linux | `${XDG_STATE_HOME:-~/.local/state}/warp-terminal/warp.sqlite` |
+| Windows | `%LOCALAPPDATA%/warp/Warp/data/warp.sqlite` |
+
+The same candidate function is shared by import and Data Sources detection so discovery cannot drift.
+
+Executable detection recognizes the current `oz`/`oz-preview` names, the deprecated `warp-cli` name, and Linux's `warp-terminal` desktop launcher. This only improves the Data Sources inventory; it does not add a live CLI runner.
+
+## Mapping into ORGII
+
+| Warp event | ORGII replay chunk |
+| -------------------------------------- | -------------------------------------- |
+| `userQuery` | user message |
+| `agentReasoning` | thinking |
+| `agentOutput` | assistant message |
+| `modelUsed` | session model metadata |
+| `toolCall` + matching `toolCallResult` | one tool-call chunk with paired output |
+
+Known tools map to ORGII's canonical shell, file-read, file-edit, code-search, and glob operations. Unknown tools retain a snake-case name and their complete JSON payload rather than being discarded. `applyFileDiffs` also contributes touched-file and line-impact statistics.
+
+Messages from every task in a conversation are merged by protobuf timestamp, then task/message position. Missing timestamps fall back to the conversation's `last_modified_at`.
+
+## Metadata fallbacks
+
+| Field | Resolution order |
+| -------------------- | ------------------------------------------------------------------------------------------------------------------- |
+| Title | summary title → root-task description → summary initial query → first user query → agent name → `Warp conversation` |
+| Model | latest `modelUsed` event → latest non-empty usage model |
+| Repository path | summary initial working directory |
+| Created/updated time | earliest/latest message timestamp → conversation `last_modified_at` |
+| Parent | `parent_conversation_id`, wrapped with `warpapp-` |
+
+Remote child conversations and unlisted automatic code-diff conversations are retained in the cache input set but are not listed as primary sessions. Conversations with no decodable replay chunks are also hidden.
+
+## Cache and compatibility
+
+The shared imported-history cache fingerprints conversation JSON, optional summary JSON, modification time, task count/bytes, and SQLite WAL/SHM sidecars. Parser version changes can invalidate cached rows. Databases that predate the optional `summary` column are supported; missing tables and malformed protobuf records degrade to an empty result instead of crashing the source scan.
+
+## Privacy and limitations
+
+- Reads are local and read-only.
+- ORGII only sees conversations present in the local `warp.sqlite` database.
+- Warp can store/sync some data remotely depending on user settings; cloud-only history is outside this importer.
+- This implements issue #366 (history import), not the separate Warp CLI/TUI integration tracked by #331.
+
+## References
+
+- [Warp: interacting with agents](https://docs.warp.dev/agent-platform/local-agents/interacting-with-agents)
+- [Warp: session restoration and database locations](https://docs.warp.dev/terminal/sessions/session-restoration)
+- [Warp repository migration code](https://github.com/warpdotdev/Warp/blob/main/warp-repository/src/migration.rs)
+- [Warp protobuf APIs](https://github.com/warpdotdev/warp-proto-apis)
diff --git a/docs/assets/feature-wall/ai-blame.gif b/docs/assets/feature-wall/ai-blame.gif
new file mode 100644
index 000000000..81a5cadce
Binary files /dev/null and b/docs/assets/feature-wall/ai-blame.gif differ
diff --git a/docs/assets/feature-wall/design-mode.gif b/docs/assets/feature-wall/design-mode.gif
new file mode 100644
index 000000000..2e7f51a1e
Binary files /dev/null and b/docs/assets/feature-wall/design-mode.gif differ
diff --git a/docs/assets/feature-wall/development-workspace.gif b/docs/assets/feature-wall/development-workspace.gif
new file mode 100644
index 000000000..97133f17a
Binary files /dev/null and b/docs/assets/feature-wall/development-workspace.gif differ
diff --git a/docs/assets/feature-wall/replay.gif b/docs/assets/feature-wall/replay.gif
new file mode 100644
index 000000000..e777da7c6
Binary files /dev/null and b/docs/assets/feature-wall/replay.gif differ
diff --git a/docs/assets/feature-wall/session-sources.png b/docs/assets/feature-wall/session-sources.png
new file mode 100644
index 000000000..98e4502ad
Binary files /dev/null and b/docs/assets/feature-wall/session-sources.png differ
diff --git a/docs/assets/feature-wall/team-trajectory-review.png b/docs/assets/feature-wall/team-trajectory-review.png
new file mode 100644
index 000000000..de4466ced
Binary files /dev/null and b/docs/assets/feature-wall/team-trajectory-review.png differ
diff --git a/docs/assets/feature-wall/work-diary.png b/docs/assets/feature-wall/work-diary.png
new file mode 100644
index 000000000..47ae3b241
Binary files /dev/null and b/docs/assets/feature-wall/work-diary.png differ
diff --git a/docs/assets/session-provenance/runtime-hooks.png b/docs/assets/session-provenance/runtime-hooks.png
new file mode 100644
index 000000000..e37b2b563
Binary files /dev/null and b/docs/assets/session-provenance/runtime-hooks.png differ
diff --git a/docs/assets/session-provenance/session-blame.png b/docs/assets/session-provenance/session-blame.png
new file mode 100644
index 000000000..d61220840
Binary files /dev/null and b/docs/assets/session-provenance/session-blame.png differ
diff --git a/docs/audit-2026-06-10/naming-collisions.md b/docs/audit-2026-06-10/naming-collisions.md
index 6d7853108..14e7fecfd 100644
--- a/docs/audit-2026-06-10/naming-collisions.md
+++ b/docs/audit-2026-06-10/naming-collisions.md
@@ -114,7 +114,7 @@ BE:
### `mode`(3 套状态机)
1. `agentExecMode`(build / ask / plan / debug / review / wingman)
-2. `stationMode`(workstation surface mode:agent-station / my-station / ops-control 等)
+2. `stationMode`(workstation surface mode:agent-station / my-station / work-management 等)
3. `chatPanelContentModeAtom`(哪个 content view 显示)
### `pill`(3 个 UI primitive)
diff --git a/docs/contributing/wiki/Architecture-Overview.md b/docs/contributing/wiki/Architecture-Overview.md
index 038072fdc..cbaef832c 100644
--- a/docs/contributing/wiki/Architecture-Overview.md
+++ b/docs/contributing/wiki/Architecture-Overview.md
@@ -93,7 +93,7 @@ The core of the backend. Each CLI agent type has its own:
- **Platform adapter** — spawns the agent process, manages its stdin/stdout/stderr.
- **Output parser** — converts raw agent output into structured events (tool calls, messages, file edits).
-Supported CLI agents: Cursor, Claude Code, Codex, Copilot, Gemini CLI, Kiro, OpenCode.
+Supported CLI agents: Cursor, Claude Code, Codex, Copilot, Antigravity, Kiro, OpenCode.
### `crates/agent-core/`
diff --git a/docs/contributing/wiki/Sessions.md b/docs/contributing/wiki/Sessions.md
index 13eea5c4d..b25d93f44 100644
--- a/docs/contributing/wiki/Sessions.md
+++ b/docs/contributing/wiki/Sessions.md
@@ -6,11 +6,11 @@ A **session** is a single running agent conversation. ORGII manages multiple con
Every session belongs to one of three dispatch categories:
-| Category | What it is |
-| ------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
-| `cli_agent` | An external CLI coding-agent process (Cursor, Claude Code, Codex, Gemini CLI, Copilot, Kiro, OpenCode, …) spawned and managed by ORGII |
-| `rust_agent` | ORGII's built-in Rust-native agent — the SDE Agent or OS Agent |
-| `cursor_ide` | A read-only view of a Cursor IDE chat imported into ORGII (no ORGII-side process) |
+| Category | What it is |
+| ------------ | --------------------------------------------------------------------------------------------------------------------------------------- |
+| `cli_agent` | An external CLI coding-agent process (Cursor, Claude Code, Codex, Antigravity, Copilot, Kiro, OpenCode, …) spawned and managed by ORGII |
+| `rust_agent` | ORGII's built-in Rust-native agent — the SDE Agent or OS Agent |
+| `cursor_ide` | A read-only view of a Cursor IDE chat imported into ORGII (no ORGII-side process) |
## CLI agents
@@ -21,7 +21,7 @@ When you select `cli_agent`, ORGII spawns the chosen CLI tool as a subprocess, p
| Cursor | `cursor_cli` |
| Claude Code | `claude_code` |
| Codex | `codex` |
-| Gemini CLI | `gemini_cli` |
+| Antigravity | `antigravity` |
| GitHub Copilot | `copilot` |
| Kiro | `kiro` |
| OpenCode | `opencode` |
diff --git a/docs/cursor-ide-metadata.md b/docs/cursor-ide-metadata.md
new file mode 100644
index 000000000..1a4053a9a
--- /dev/null
+++ b/docs/cursor-ide-metadata.md
@@ -0,0 +1,82 @@
+# Cursor IDE session metadata
+
+Reference for the metadata ORGII imports from Cursor's local store, what each
+field is, and where it comes from. Verified against a real Cursor install
+(2026‑07).
+
+## Where Cursor keeps things
+
+Modern Cursor uses a three‑tier layout under
+`~/Library/Application Support/Cursor/User/globalStorage/`
+(`%APPDATA%\Roaming\Cursor\...` on Windows, `~/.config/Cursor/...` on Linux):
+
+| Store | What it holds | ORGII uses it for |
+| -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------- |
+| `conversation-search.db` | Lightweight **index**: one indexed row per conversation (`id`, `title`, `updated_at`, `is_archived`, `root_fingerprint`), `conversations_recency` index. | **Discovery + change detection.** A cheap indexed read, no blob parsing. |
+| `state.vscdb` | The **content**: `cursorDiskKV` key/value table with `composerData:` (session metadata) and `bubbleId::` (messages). Can be multiple GB. | Point‑lookup `composerData:` for changed sessions; lazy bubble reads when a session is opened. |
+| `~/.cursor/chats///store.db` | Newer per‑session blob store (agent‑mode subset only). | Not used — the main history is in `state.vscdb`. |
+
+The `conversation-search.db` `id` **is** the composerId, so
+`composerData:` is a fast primary‑key lookup.
+
+## The pipeline (`sources/cursor_ide`)
+
+```
+conversation-search.db ──► discover_from_index() (indexed SELECT, ~14 ms for 1656 rows)
+ │ updated_at + root_fingerprint = change signature
+ ▼
+changed_records_from_conn() ──► only genuinely-changed sessions
+ │
+ ▼
+state.vscdb: composerData: ──► cache_input_from_raw() (parse the changed few)
+ ▼
+imported_history_session_cache ──► CursorIdeSessionRow ──► SessionAggregateRecord ──► frontend Session
+```
+
+This is the **same incremental model the file‑based sources use** (claude_code,
+codex, cline, trae…): discovery is cheap, and only changed sessions are
+re‑parsed. There is no per‑restart full scan of `state.vscdb`, and no separate
+on‑hover fetch — every field below rides in the session row.
+
+## `composerData` fields
+
+### Captured today (surfaced on the session)
+
+| Field in `composerData` | Session field | Notes |
+| ------------------------------------------------------------------------------ | ----------------------------------- | -------------------------------------------------------------- |
+| `name` | `name` | Session title. |
+| `createdAt` | `created_at` | |
+| last bubble `createdAt` / `lastUpdatedAt`; index `updated_at` wins for recency | `updated_at` | Sort key. |
+| `status` | `status` | `completed` / `aborted` / … |
+| `unifiedMode` | (metadata) | `agent` / `edit` / `ask`. |
+| `isAgentic` | (metadata) | |
+| `modelConfig.modelName` | `model` | e.g. `claude-opus-4-8`, `gpt-5.5`. |
+| `contextTokensUsed` | `input_tokens` | Cursor records a single total (no in/out split). |
+| `totalLinesAdded` / `totalLinesRemoved` | `lines_added` / `lines_removed` | |
+| `filesChangedCount` | `files_changed` | |
+| `originalFileStates` (keys with an edit marker) + newly‑created | `touched_files` | Files the session edited. |
+| `trackedGitRepos[0].repoPath` (fallback `workspaceIdentifier.uri.fsPath`) | `repo_path` (+ derived `repo_name`) | The repo the chat ran in. Populated ~66/80 of recent sessions. |
+| `trackedGitRepos[0].branches[0].branchName` | `branch` | Branch at the time. |
+| parent `subagentComposerIds` + child `subagentInfo.parentComposerId` | `parent_session_id` / `listable` | Child is nested under its parent in the sidebar. |
+
+### Present but not captured
+
+- **`subtitle`** — Cursor's one‑line change summary (e.g. "Edited index.tsx,
+ index.tsx"). Redundant with `touched_files`; threading it through the shared
+ `SessionAggregateRecord` (no `Default`, ~24 constructors) wasn't worth it.
+- **`context.selectedCommits` / `context.selectedPullRequests` /
+ `context.gitPRDiffSelections`** — commits/PRs a user _attached to the chat as
+ context_. First‑class fields, but empty in practice (0/80 sampled). These are
+ inputs, not outputs — Cursor does not record commits the session produced.
+- `promptTokenBreakdown` (per‑category token usage), `fullConversationHeadersOnly`
+ (bubble list — read lazily for replay), and ~25 UI/worktree state booleans.
+
+## What is _not_ available anywhere in Cursor's store
+
+- **Commits produced by the session** — not recorded. `trackedGitRepos` tracks
+ repo + branch, not a commit list.
+- **Linked pull requests** — only the (usually empty) attach‑as‑context slots.
+
+Correlating a session to the commits/PRs it produced would require joining by
+repo + time window against `git log` / the GitHub API — a separate concern from
+this importer.
diff --git a/docs/external-history-loader-metadata.md b/docs/external-history-loader-metadata.md
new file mode 100644
index 000000000..3cd4d607f
--- /dev/null
+++ b/docs/external-history-loader-metadata.md
@@ -0,0 +1,301 @@
+# External history loader metadata matrix
+
+本文档记录 ORGII 当前从各个外部 AI session loader 读取并写入统一 cache 的
+metadata。它的主要用途是:
+
+1. 判断一个 loader 能否支持 file → AI session 的 blame 查询;
+2. 区分“源数据没有”与“源数据存在、但 loader 尚未归一化”;
+3. 修改 loader 时同步更新 capability matrix。
+
+最后按代码验证:**2026-07-14**。
+
+## Scope
+
+这里覆盖 `imported_history` 当前注册的 8 个 external loaders:
+
+- Claude Code (`claude_code`)
+- Codex (`codex_app`)
+- Cursor (`cursor_ide`)
+- OpenCode (`opencode`)
+- Windsurf (`windsurf`)
+- WorkBuddy / CodeBuddy (`workbuddy`)
+- Trae (`trae`)
+- Cline (`cline`)
+
+`orgii_cli_sessions` 和 `orgii_rust_agents` 是 ORGII 自有 session source,不属于
+external history loader,因此不在此表中。
+
+## Unified cache schema
+
+所有 loader 最终写入 `ImportedHistoryCacheInput` /
+`imported_history_session_cache`。统一字段分成四组:
+
+| 类别 | 字段 |
+| ----------------------- | ------------------------------------------------------------------------------------------------------------------------- |
+| Identity | `source`, `source_session_id`, `session_id` |
+| Source/change detection | `source_path`, `source_record_key`, `source_mtime_ms`, `source_size_bytes`, `source_fingerprint`, `parser_version` |
+| Session | `name`, `created_at_ms`, `updated_at_ms`, `model`, `input_tokens`, `output_tokens`, `repo_path`, `branch` |
+| Impact/hierarchy | `files_changed`, `lines_added`, `lines_removed`, `touched_files`, `listable`, `parent_session_id`, `source_metadata_json` |
+
+注意:统一 cache 当前没有 recorded cost、estimated cost、commit 或 pull request 字段。
+价格估算属于下游计算,不是 loader 原始 metadata。
+
+## Capability matrix
+
+图例:
+
+- ✅:loader 当前会写入统一 cache。
+- ◐:会写入,但值的语义或精度有限。
+- 🟡:源 transcript/tool data 中可推导,但当前 loader 没有写入。
+- ❓:产品能力存在,但当前读取的本地存储尚未验证出稳定映射。
+- —:当前没有可靠来源或不支持。
+
+所有 loader 都会写入 session id、name、created/updated time 和 source provenance;
+下表只比较有差异的能力。
+
+| Loader | Repo path | Branch | Model | Token split | Touched files | `+/-` lines | Parent/subagent | Source-specific metadata |
+| ----------- | ------------------------- | ----------------- | ------------------------------------ | -------------------------------------- | ----------------------------- | ------------------------------------------ | -------------------------------- | ------------------------ |
+| Claude Code | ✅ `cwd` | ✅ `gitBranch` | ✅ | ✅ input/output,input 含 cache tokens | ✅ | ✅ structured patch;旧记录启发式 fallback | ✅ sidechain → parent | — |
+| Codex | ✅ turn `cwd` | — | ✅ | ✅ input/output | ✅ | ✅ successful `patch_apply_end` | ✅ subagent thread → parent | — |
+| Cursor | ✅ tracked repo/workspace | ✅ tracked branch | ✅ | ◐ 单一 `contextTokensUsed` 写入 input | ✅ `originalFileStates` | ✅ Cursor 汇总计数 | ✅ composer child → parent | ✅ status、agentic、mode |
+| OpenCode | ✅ `session.directory` | — | ✅ | ✅ 含 reasoning/cache token 分类 | ✅ edit tool parts | ◐ tool 参数/diff 启发式 | ✅ `parent_id` child → parent | — |
+| Windsurf | ✅ tracked repo/workspace | ✅ tracked branch | ✅ | ◐ 单一 context token total 写入 input | ✅ tool-former edit data | ◐ tool 参数/diff 启发式 | ✅ `subagentInfo` child → parent | — |
+| WorkBuddy | ✅ `cwd`/`project` | ✅ `gitBranch` | ✅ | ✅ input/output,含 cache tokens | ✅ edit tool 参数 | ◐ edit 参数启发式计数 | ✅ subagent path → parent | — |
+| Trae | ◐ 从 project slug 还原 | — | ◐ 实际为 agent label,不是 LLM model | — | — | — | — | ✅ agent、current、order |
+| Cline | ✅ `workspaceRoot`/`cwd` | — | ✅ model,fallback provider | ✅ input/output | ✅ child/root edit transcript | ◐ old/new tool 参数启发式 | ✅ `sessions.db` child → parent | — |
+
+## Direct metadata vs loader-derived metadata
+
+这里的“直接”特指:源存储已经有稳定字段或结构化 map,loader 只需要读取、改名或加
+session prefix。“计算”表示源里没有最终的 session-level cache 字段,需要 ORGII 遍历
+event/tool records、筛选、聚合或解析 diff 后生成。“启发式”表示计算输入是 requested tool
+arguments,不保证等于最终成功写入磁盘的结果。
+
+### AI blame and subagent provenance
+
+| Loader | `touched_files` 来源 | `lines_added/removed` 来源 | Parent/subagent 来源 |
+| ----------- | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | ----------------------------------------------------------------- |
+| Claude Code | **结构化计算**:聚合 `toolUseResult.filePath`;旧记录从 Edit/MultiEdit/Write path 聚合 | **结构化计算**:统计 `structuredPatch`;旧记录为 old/new 参数启发式 | **直接 metadata**:`isSidechain` + parent `sessionId` |
+| Codex | **直接 structured event**:成功 `patch_apply_end.changes` 的 map keys | **结构化计算**:解析每个 change 的 `unified_diff` | **直接 metadata**:`parent_thread_id` / thread-spawn parent |
+| Cursor | **直接 metadata + filter**:`originalFileStates` map keys | **直接 metadata**:`totalLinesAdded` / `totalLinesRemoved` | **直接 metadata**:`subagentComposerIds` + `parentComposerId` |
+| OpenCode | **需要计算**:筛选当前 session 的 write/edit/patch parts,聚合 input path | **计算/启发式**:有 patch 时解析 diff,否则统计 old/new/content tool args | **直接 metadata**:`session.parent_id`;loader 只做循环/孤儿校验 |
+| Windsurf | **需要计算**:筛选当前 composer 的 edit/write tool-former records,聚合 params path | **计算/启发式**:解析 patch 或统计 before/after content | **直接 metadata**:`subagentInfo.parentComposerId` |
+| WorkBuddy | **需要计算**:筛选 child/root JSONL 中的 edit/write/apply-patch calls,聚合 path | **启发式计算**:统计 old/new/content/edits tool args | **路径推导**:`/subagents/agent-*.jsonl`;child id 为直接 |
+| Trae | **没有可靠来源** | **没有可靠来源** | **没有可靠来源** |
+| Cline | **需要计算**:从每个 DB row 自己的 transcript 聚合 `editor.path` | **启发式计算**:统计 `old_text` / `new_text`,失败 result 不计入 | **直接 DB metadata**:`is_subagent` + `parent_session_id` |
+
+因此,只有 Cursor 已经在 session/composer metadata 中同时提供 file/line 汇总;Codex
+提供 authoritative applied-event file map,但仍需解析 diff 计算行数。Claude Code 有
+authoritative structured patch,但需要跨 tool results 聚合成 session totals。OpenCode、
+Windsurf、WorkBuddy、Cline 都没有现成的 session-level touched-file list,必须由 loader
+从各自的 tool records 计算。
+
+### General session metadata provenance
+
+| Loader | 源里直接存在 | ORGII 需要计算/归一化 | 启发式或缺失 |
+| ----------- | ---------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ |
+| Claude Code | session id、timestamp、`cwd`、`gitBranch`、model、usage、title/summary、sidechain fields | title fallback chain、token category 求和、turn/tool records 聚合 | 旧 edit 记录的 line totals |
+| Codex | thread id、timestamp、turn `cwd`/model、`total_token_usage`、parent thread、patch result | title fallback chain、选择最新 total usage、patch diff 聚合 | branch 缺失;旧 rollout 使用 tool-call fallback |
+| Cursor | composer name/time/status/model/mode、context token total、repo/branch、impact totals、subagent fields | workspace fallback、URI → path、过滤真正有 edit marker 的 file states | input/output token split 缺失 |
+| OpenCode | `session` 表的 id/title/directory/model/times/token columns/`parent_id`,以及 part tool state | model JSON 解析、reasoning/cache token 分类求和、part impact 聚合、container/mirror 校验 | 无 patch 时 line totals 是 tool-argument heuristic |
+| Windsurf | composer name/time/status/model/context total/repo/branch、`subagentInfo`、bubble tool-former data | workspace fallback、tool params/result 归一化、composer impact 聚合 | input/output split 缺失;无 diff 时 line totals 启发式 |
+| WorkBuddy | JSONL timestamp/model/usage/`cwd`/project/branch、child 内嵌 `sessionId` | title fallback、兼容多种 token 字段并求和、tool impact 聚合、从目录布局推导 parent | line totals 是 requested args heuristic |
+| Trae | summary topic/time、agent id、current/order | 从 project slug 尽力还原 repo path、把 agent id 转成显示 label | model/tokens/branch/impact/parent 缺失 |
+| Cline | DB session/parent/status/time/model/workspace/messages path,sidecar title/prompt/usage,transcript events | DB → sidecar → transcript fallback chain、aggregate usage fallback、每个 root/child transcript 的 impact | branch 缺失;line totals 是 editor args heuristic |
+
+## Loader details
+
+### Claude Code
+
+主要来源:`~/.claude/projects/**/*.jsonl`,并读取相邻 session title index。
+
+当前归一化:
+
+- Name:custom title → AI title → summary/index title → first prompt → id。
+- Time:transcript timestamps,缺失时退回文件 mtime。
+- Workspace:`cwd`、`gitBranch`。
+- Usage:assistant message usage;input 包含普通、cache-read、cache-creation
+ tokens,output 单独累计。
+- Impact:优先使用 `toolUseResult.structuredPatch` 的结构化 diff;旧记录没有
+ structured patch 时,退回 Edit/MultiEdit/Write 参数启发式。
+- Hierarchy:`isSidechain=true` 且 `sessionId` 指向另一个 session 时,写入
+ `parent_session_id`。因此 sidebar 可按主 session 折叠 subagent。
+
+AI blame 状态:**可直接使用 `touched_files`**。新格式的 line stats 接近实际
+applied diff;旧格式 fallback 只代表工具参数中的文本行数。
+
+### Codex
+
+主要来源:`~/.codex/sessions/**/*.jsonl`,标题从 `session_index.jsonl` 或
+session metadata 读取。
+
+当前归一化:
+
+- Name:session index/thread name → session metadata title → first prompt → id。
+- Time、model、repo path:rollout timestamp 和 turn context (`cwd`, `model`)。
+- Usage:最新 `total_token_usage.input_tokens/output_tokens`。
+- Impact:优先读取成功的 `patch_apply_end.changes[path].unified_diff`;这能覆盖
+ `apply_patch`、exec 包装的 patch 等路径。旧 rollout 退回 apply-patch tool-call
+ 解析。
+- Hierarchy:从 subagent `session_meta` 的 `parent_thread_id`、
+ `source.subagent.thread_spawn.parent_thread_id` 等字段解析 parent。
+
+AI blame 状态:**可直接使用 `touched_files`**,且当前是各 loader 中较强的
+authoritative applied-patch 信号。当前没有 branch metadata。
+
+### Cursor
+
+主要来源:`conversation-search.db` 负责 discovery/change detection,
+`state.vscdb` 的 `composerData:` 负责 metadata,bubble 在打开 session 时懒加载。
+更完整的存储说明见 [Cursor IDE session metadata](./cursor-ide-metadata.md)。
+
+当前归一化:
+
+- Name/time/status/model/mode:composer metadata;index `updated_at` 是排序时的
+ authoritative recency。
+- Workspace:`trackedGitRepos[0]`,fallback 到 `workspaceIdentifier`。
+- Usage:`contextTokensUsed` 是单一总数,统一写入 `input_tokens`,没有可靠的
+ input/output split。
+- Impact:`totalLinesAdded`、`totalLinesRemoved`、`filesChangedCount`;
+ `touched_files` 来自 `originalFileStates` 中带 edit marker 或 newly-created 的文件。
+- Hierarchy:主 composer 的 `subagentComposerIds` 用于发现 child,child 的
+ `subagentInfo.parentComposerId` 用于最终归属。child 不进入 root list,由 sidebar
+ 的通用 child-session flow 折叠显示。
+- Extra:`source_metadata_json` 保存 `status`、`isAgentic`、`unifiedMode`。
+
+AI blame 状态:**可直接使用 `touched_files`**。Line/file totals 是 Cursor 自己的
+session 汇总;`touched_files.len()` 不应被假定永远等于 `filesChangedCount`。
+
+### OpenCode
+
+主要来源:OpenCode 的 `opencode.db`,读取 `session`、message 和 part 表。
+
+当前归一化:
+
+- Name/time/model/repo:`session.title`、`time_created/time_updated`、model JSON、
+ `directory`。
+- Usage:input 加上 cache read/write;output 加上 reasoning tokens。
+- Hierarchy:读取 `parent_id`,但当前逻辑只将有效 container parent/mirror 关系
+ 映射成 `parent_session_id`,并处理循环、缺失 parent 和 ORGII-managed mirror。
+- Impact:从当前 session 自己的 write/edit/patch/apply-patch tool parts 提取路径;
+ patch 文本优先统计 unified diff,否则以 old/new/content 参数估算行数。失败的 edit
+ 不计入。
+
+AI blame 状态:**可直接使用 `touched_files`**。`parent_id` 对应的 child run 使用自己
+的 part stream 计算 impact,因此不会把 child 修改重复归到 parent。
+
+### Windsurf
+
+主要来源:Windsurf `User/globalStorage/state.vscdb` 的 composer/bubble 数据。
+
+当前归一化:
+
+- Name/time/status/model、repo、branch 和单一 `contextTokensUsed`。
+- Hierarchy:`subagentInfo.parentComposerId` 写入 `parent_session_id`;child 不进入根
+ list,由 sidebar 的通用 child-session flow 折叠显示。
+- Impact:从每个 composer 自己的 edit/write/apply-patch `toolFormerData` 提取 path;
+ 可获得 before/after content 时估算行数,并忽略失败状态。
+
+AI blame 状态:**可直接使用 `touched_files`**。Impact 按 composer 计算,subagent
+修改保留在 child row。
+
+### WorkBuddy / CodeBuddy
+
+主要来源:`~/.workbuddy/{projects,sessions,history.jsonl}`、
+`~/.codebuddy/...` 和 CodeBuddyExtension JSONL。
+
+当前归一化:
+
+- Name:AI title/display → first user prompt → file stem。
+- Time/model/repo/branch:JSONL timestamp、message model、`cwd`/`project`、
+ `gitBranch`。
+- Usage:兼容 input/output、prompt/completion 和 cache token 字段。
+- Impact:识别 Edit、MultiEdit、Write、edit_file、write_file、apply_patch 等 tool,
+ 但只有参数包含结构化 path 字段时才收集文件;line totals 通过
+ old/new/content/edits 文本行数估算。
+- Hierarchy:已观察到 `/subagents/agent-*.jsonl` 布局,目录可提供
+ parent id,child JSONL 自身有独立 `sessionId`。Discovery 会导入这些 `agent-*`
+ 文件,优先以 child 内嵌 `sessionId` 作为 source id,并将目录 parent id 标准化为
+ `parent_session_id`。
+
+AI blame 状态:**可直接使用 `touched_files`**。文件集合通常可靠;line totals 是
+requested tool arguments 的启发式统计,不等同于成功 applied diff。Child transcript
+独立计算,不会与 parent 混合。
+
+### Trae
+
+主要来源:`~/.trae-cn/memory/projects/**/session_memory_*.jsonl` 和
+`~/.trae/memory/projects/**`。明文文件只有 turn summary;完整 transcript 位于
+SQLCipher 加密的 `ModularData/ai-agent/database.db`,当前不解密。
+
+当前归一化:
+
+- Name:`topics.md` 中的 session topic → first summary intent → id。
+- Time:`message_summary_time`;creation time 缺失时还可从 Mongo ObjectId-style
+ session id 回退。
+- Repo:从 project directory slug 尽力还原 filesystem path。
+- “Model”:从 VS Code `state.vscdb` index 读取 agent id,并转换为类似
+ `Solo Agent` 的 label;它不是底层 LLM model。
+- Extra:`source_metadata_json` 保存 agent、是否 current、Trae list order。
+- Tokens、branch、impact、parent 均为空。
+
+AI blame 状态:**没有可靠结构化信号**。Summary 的 `actions` 可能偶尔提到文件,
+但属于自然语言,不能作为稳定的 blame index。要获得可靠文件列表,需要解密完整 DB、
+找到新的明文事件源,或通过 repo/time correlation 另行推断。
+
+### Cline
+
+主要来源:`~/.cline/data/db/sessions.db` 作为 discovery/hierarchy index,按每行的
+`messages_path` 读取 root 或 child transcript;旧安装没有 DB 时,fallback 到
+`~/.cline/data/sessions//.messages.json`。Root 仍读取同目录 `.json`
+sidecar。
+
+当前归一化:
+
+- Name:sidecar title → DB `metadata_json.title` → sidecar/DB prompt → first user text → id。
+- Time/model/repo/usage:优先 sidecar/transcript,并以 DB 的 started/updated、model、
+ provider、workspace/cwd、aggregate usage 补缺。
+- Hierarchy:DB 的 `is_subagent=1` + `parent_session_id` 直接建立 child relation。
+- Impact:每个 DB row 指向自己的 transcript;从 `editor` old/new/path 参数生成
+ `touched_files` 和启发式行数。
+- Branch 为空。
+
+AI blame 状态:**可直接使用 `touched_files`**。已用真实 Cline spawn 验证:root 与
+`__agent_` child 是两行独立 session,child 有独立 `messages_path` 并明确
+指向 root;因此 subagent-level blame 不依赖 tool-name 推断。
+
+## AI blame readiness and next work
+
+如果 blame 的最小定义是“给定文件,列出修改过它的 AI sessions”,当前优先级为:
+
+1. **Ready**:Claude Code、Codex、Cursor、OpenCode、Windsurf、WorkBuddy、Cline。
+2. **Blocked on reliable source data**:Trae。
+
+这里的 Ready 表示 loader 已经写入可查询的 `touched_files`,不代表历史记录覆盖率
+或路径格式已经在所有版本、所有 edit tool 上达到 100%。
+
+Subagent/session hierarchy 是另一条独立能力轴:
+
+1. **已写入 parent relation**:Claude Code、Codex、Cursor、OpenCode、Windsurf、
+ WorkBuddy、Cline。
+2. **当前没有可靠信号**:Trae。
+
+索引层应该以标准化后的 `touched_files` 为唯一查询接口,而不是让 blame feature
+重新理解每种 transcript。每个新 collector 至少需要:
+
+1. 只记录实际 edit/write/patch 操作,避免把 read/search 路径算作修改;
+2. 路径相对 `repo_path` 归一化,并处理 URI、绝对路径和平台分隔符;
+3. 对 session 内文件去重;
+4. 区分 authoritative applied diff 与 tool-argument heuristic;
+5. bump 对应 loader 的 metadata parser version,让旧 cache 自动重建;
+6. 添加 fixture/unit test,覆盖 modified、created、deleted、rename 和 failed edit。
+
+## Maintenance rule
+
+修改任何 external loader 的 `session_meta_to_cache_input`、Cursor 的
+`cache_input_from_raw`,或新增 `ImportedHistoryCacheInput` 字段时,应在同一个 PR 中更新:
+
+1. 本文 capability matrix;
+2. 对应 loader detail;
+3. AI blame readiness 分组;
+4. parser version 和 metadata/cache tests(如果字段会改变已有 cache row)。
diff --git a/docs/frontend-ui-audit-2026-07-01/SourceControlScopeToolbar.md b/docs/frontend-ui-audit-2026-07-01/SourceControlScopeToolbar.md
new file mode 100644
index 000000000..d4b3c9631
--- /dev/null
+++ b/docs/frontend-ui-audit-2026-07-01/SourceControlScopeToolbar.md
@@ -0,0 +1,151 @@
+# Frontend UI Audit — SourceControlScopeToolbar
+
+**File:** `src/modules/WorkStation/CodeEditor/Panels/EditorPrimarySidebar/tabs/SourceControlScopeToolbar.tsx` (355 LOC)
+**Date:** 2026-07-01
+**Auditor:** Cursor agent (frontend-ui-audit skill)
+
+## D1 — Raw HTML vs Design System
+
+| Line | Element | Verdict | Reason | Suggested change |
+| ---- | ------------------------ | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------- |
+| 119 | `