From afcbd93ddc15ecb7546495546553d9c5e6487dad Mon Sep 17 00:00:00 2001 From: Fred Porter Date: Tue, 18 Aug 2026 20:35:01 +0800 Subject: [PATCH 01/28] wip: preserve incomplete teletext import --- frontend-vue/src/grid-core/teletext/index.ts | 72 ++++++++ .../src/surfaces/ucode/UCodeSurface.vue | 173 ++++++++---------- 2 files changed, 145 insertions(+), 100 deletions(-) create mode 100644 frontend-vue/src/grid-core/teletext/index.ts diff --git a/frontend-vue/src/grid-core/teletext/index.ts b/frontend-vue/src/grid-core/teletext/index.ts new file mode 100644 index 00000000..5810ebec --- /dev/null +++ b/frontend-vue/src/grid-core/teletext/index.ts @@ -0,0 +1,72 @@ +// ── uCore Frontend: Teletext Module ────────────────────────────── +// E1: extracted from UCodeSurface.vue +// Re-exports stateless types/helpers/builders from @udos/gridcore's reader-model, +// plus adds Vue-specific/reactive config and functions (loaders, renderers that +// use local grid-core buffer functions). +// ──────────────────────────────────────────────────────────────────── + +// ── Re-exports from @udos/gridcore (stateless) ─────────────────── +export { + ceefaxClock, + DOC_PAGE_OFFSET, + DOC_SCREEN_LINES, + docContentPage, + docListPage, + DOCS_PER_LIST_PAGE, + docScreens, + // Helpers (pure) + docTitle, + helpPage, + libraryForPage, + // Page builders (pure, take BuilderContext) + mainIndexPage, + MAX_DOCS_PER_LIBRARY, + newsPage, + subIndexPage, + // Constants + TELETEXT_FASTEXT, + teletextContent, + wrapText, + writeBoxedDoubleHeightTitle, + // Layout renderers (pure, target ReaderBuffer) + writeDoubleHeight, + writeMosaicRule, + writeSeparatedBar, + type BuilderContext, + type PublicLibraryDef, + type ReaderBuffer, + type ReaderBufferCell, + // Types + type ReaderTeletextPage, + type VaultDoc, + type VaultLibrary, +} from "@udos/gridcore"; + +// ── Local config: uCore vault public library definitions ────────── +// This maps uCore's vault sources (public / global-knowledge) to Ceefax page ranges. +export const PUBLIC_LIBRARY_DEFS: PublicLibraryDef[] = [ + { + id: "documentation", + label: "Documentation", + source: "public", + tag: "doc-sites", + page: 200, + colour: 2, + }, + { + id: "knowledge", + label: "Global Knowledge", + source: "global-knowledge", + tag: null, + page: 300, + colour: 3, + }, + { + id: "learning", + label: "Learning", + source: "public", + tag: "learning", + page: 400, + colour: 6, + }, +]; diff --git a/frontend-vue/src/surfaces/ucode/UCodeSurface.vue b/frontend-vue/src/surfaces/ucode/UCodeSurface.vue index 6cf336fd..a472bd2a 100644 --- a/frontend-vue/src/surfaces/ucode/UCodeSurface.vue +++ b/frontend-vue/src/surfaces/ucode/UCodeSurface.vue @@ -625,6 +625,72 @@ import type { LayerMap } from "../../grid-core/seeds/layer-map"; import worldMapSeed from "../../grid-core/seeds/layers/world-map.json"; import moonMapSeed from "../../grid-core/seeds/layers/moon.json"; import regionMapSeed from "../../grid-core/seeds/layers/region.json"; +import { + // Types + type ReaderTeletextPage, + type VaultDoc, + type VaultLibrary, + type PublicLibraryDef, + type BuilderContext, + + // Constants + TELETEXT_FASTEXT, + DOC_PAGE_OFFSET, + DOC_SCREEN_LINES, + MAX_DOCS_PER_LIBRARY, + + // Helpers (pure) + docTitle, + wrapText, + libraryForPage, + ceefaxClock, + + // Layout + writeDoubleHeight, + writeMosaicRule, + writeSeparatedBar, + writeBoxedDoubleHeightTitle, + + // Builders + mainIndexPage, + docListPage, + docContentPage, + newsPage, + subIndexPage, + helpPage, + teletextContent, + docScreens, + + // Config + PUBLIC_LIBRARY_DEFS, +} from "@udos/gridcore"; + DOC_SCREEN_LINES, + + // Helpers + docTitle, + wrapText, + libraryForPage, + ceefaxClock, + + // Layout + writeDoubleHeight, + writeMosaicRule, + writeSeparatedBar, + writeBoxedDoubleHeightTitle, + + // Builders + mainIndexPage, + docListPage, + docContentPage, + newsPage, + subIndexPage, + helpPage, + teletextContent, + docScreens, + + // Config + PUBLIC_LIBRARY_DEFS, +} from "@udos/gridcore"; const shell = useShellStore(); const gridcoreSettings = useGridCoreSettingsStore(); @@ -1768,66 +1834,9 @@ const teletextHistory: number[] = []; let teletextDigitBuffer = ""; let teletextClockTimer: number | null = null; -// Ceefax fastext: four coloured links mapped to F1–F4 (red/green/yellow/blue). -const TELETEXT_FASTEXT = [ - { label: "Index", color: 1, page: 100 }, - { label: "Docs", color: 2, page: 200 }, - { label: "Knowledge", color: 3, page: 300 }, - { label: "Help", color: 4, page: 888 }, -]; - -/* ─── Vault content (published Documentation + Global Knowledge) ──── */ -interface VaultDoc { - path: string; - filename: string; - binder: string | null; - tags: string[]; - preview: string; - extension: string; -} - -interface VaultLibrary { - id: string; - label: string; - /** Library index source ("public" or a registered workspace). */ - source: string; - /** Folder tag used to filter within the source (null = all). */ - tag: string | null; - /** Ceefax page for this library's index (200/300/400). */ - page: number; - colour: number; - docs: VaultDoc[]; -} - -// Public vault libraries → Ceefax page ranges. -// Global Knowledge is a registered workspace (source "global-knowledge"); -// Documentation + Learning live under ~/Public (source "public"). -const PUBLIC_LIBRARY_DEFS = [ - { - id: "documentation", - label: "Documentation", - source: "public", - tag: "doc-sites", - page: 200, - colour: 2, - }, - { - id: "knowledge", - label: "Global Knowledge", - source: "global-knowledge", - tag: null, - page: 300, - colour: 3, - }, - { - id: "learning", - label: "Learning", - source: "public", - tag: "learning", - page: 400, - colour: 6, - }, -]; +// Note: TELETEXT_FASTEXT, VaultDoc, VaultLibrary, PUBLIC_LIBRARY_DEFS, +// DOCS_PER_LIST_PAGE, MAX_DOCS_PER_LIBRARY, DOC_PAGE_OFFSET, DOC_SCREEN_LINES +// are imported from @/grid-core/teletext. const vaultLibraries = ref([]); const vaultLoaded = ref(false); @@ -1835,10 +1844,6 @@ const vaultError = ref(null); /** path → full file content, cached after first read. */ const vaultDocCache = new Map(); -const DOCS_PER_LIST_PAGE = 14; // fits between title (rows 3-4) and fastext -const MAX_DOCS_PER_LIBRARY = 48; // cap to keep within the 100-page range -const DOC_PAGE_OFFSET = 50; // content pages start at library.page + 50 - function docTitle(doc: VaultDoc): string { const base = doc.filename.replace(/\.[^.]+$/, ""); const title = base.replace(/[-_]+/g, " ").trim(); @@ -3063,41 +3068,6 @@ function clearGrid() { calc(var(--gridcore-checker-size) / -2), calc(var(--gridcore-checker-size) / -2) 0; } -.pixel-colour-popover__swatch--0, -.layer-colour-popover__swatch--0 { - background: var(--gridcore-palette-0); -} -.pixel-colour-popover__swatch--1, -.layer-colour-popover__swatch--1 { - background: var(--gridcore-palette-1); -} -.pixel-colour-popover__swatch--2, -.layer-colour-popover__swatch--2 { - background: var(--gridcore-palette-2); -} -.pixel-colour-popover__swatch--3, -.layer-colour-popover__swatch--3 { - background: var(--gridcore-palette-3); -} -.pixel-colour-popover__swatch--4, -.layer-colour-popover__swatch--4 { - background: var(--gridcore-palette-4); -} -.pixel-colour-popover__swatch--5, -.layer-colour-popover__swatch--5 { - background: var(--gridcore-palette-5); -} -.pixel-colour-popover__swatch--6, -.layer-colour-popover__swatch--6 { - background: var(--gridcore-palette-6); -} -.pixel-colour-popover__swatch--7, -.layer-colour-popover__swatch--7 { - background: var(--gridcore-palette-7); -} -.ucode-import-input { - display: none; -} .pixel-colour-popover__swatch .colour-marker { position: absolute; font-size: var(--gridcore-marker-font-size); @@ -3374,8 +3344,11 @@ function clearGrid() { position: absolute; font-size: var(--gridcore-marker-font-size); font-weight: var(--gridcore-font-weight-bold); + font-family: var(--gridcore-font-family-mono); line-height: 1; - text-shadow: var(--gridcore-marker-shadow); + padding: var(--gridcore-marker-pad-y) var(--gridcore-marker-pad-x); + border-radius: var(--gridcore-sidebar-char-radius); + pointer-events: none; } .layer-colour-popover__swatch .colour-marker.bg { bottom: var(--gridcore-marker-offset-sm); From 39e02e5fca4160bf9ee1d8a24e245cf19b0d715a Mon Sep 17 00:00:00 2001 From: Fred Porter Date: Tue, 18 Aug 2026 20:38:19 +0800 Subject: [PATCH 02/28] fix(frontend): restore browser-safe teletext integration --- frontend-vue/src/grid-core/teletext/index.ts | 4 +- .../src/surfaces/ucode/UCodeSurface.vue | 64 ++----------------- frontend-vue/vitest.config.ts | 13 ++++ 3 files changed, 20 insertions(+), 61 deletions(-) create mode 100644 frontend-vue/vitest.config.ts diff --git a/frontend-vue/src/grid-core/teletext/index.ts b/frontend-vue/src/grid-core/teletext/index.ts index 5810ebec..21237ae9 100644 --- a/frontend-vue/src/grid-core/teletext/index.ts +++ b/frontend-vue/src/grid-core/teletext/index.ts @@ -40,10 +40,12 @@ export { type ReaderTeletextPage, type VaultDoc, type VaultLibrary, -} from "@udos/gridcore"; +} from "@udos/gridcore/teletext"; // ── Local config: uCore vault public library definitions ────────── // This maps uCore's vault sources (public / global-knowledge) to Ceefax page ranges. +import type { PublicLibraryDef } from "@udos/gridcore/teletext"; + export const PUBLIC_LIBRARY_DEFS: PublicLibraryDef[] = [ { id: "documentation", diff --git a/frontend-vue/src/surfaces/ucode/UCodeSurface.vue b/frontend-vue/src/surfaces/ucode/UCodeSurface.vue index a472bd2a..23764792 100644 --- a/frontend-vue/src/surfaces/ucode/UCodeSurface.vue +++ b/frontend-vue/src/surfaces/ucode/UCodeSurface.vue @@ -626,71 +626,14 @@ import worldMapSeed from "../../grid-core/seeds/layers/world-map.json"; import moonMapSeed from "../../grid-core/seeds/layers/moon.json"; import regionMapSeed from "../../grid-core/seeds/layers/region.json"; import { - // Types - type ReaderTeletextPage, type VaultDoc, type VaultLibrary, - type PublicLibraryDef, - type BuilderContext, - - // Constants TELETEXT_FASTEXT, DOC_PAGE_OFFSET, - DOC_SCREEN_LINES, + DOCS_PER_LIST_PAGE, MAX_DOCS_PER_LIBRARY, - - // Helpers (pure) - docTitle, - wrapText, - libraryForPage, - ceefaxClock, - - // Layout - writeDoubleHeight, - writeMosaicRule, - writeSeparatedBar, - writeBoxedDoubleHeightTitle, - - // Builders - mainIndexPage, - docListPage, - docContentPage, - newsPage, - subIndexPage, - helpPage, - teletextContent, - docScreens, - - // Config - PUBLIC_LIBRARY_DEFS, -} from "@udos/gridcore"; - DOC_SCREEN_LINES, - - // Helpers - docTitle, - wrapText, - libraryForPage, - ceefaxClock, - - // Layout - writeDoubleHeight, - writeMosaicRule, - writeSeparatedBar, - writeBoxedDoubleHeightTitle, - - // Builders - mainIndexPage, - docListPage, - docContentPage, - newsPage, - subIndexPage, - helpPage, - teletextContent, - docScreens, - - // Config PUBLIC_LIBRARY_DEFS, -} from "@udos/gridcore"; +} from "../../grid-core/teletext"; const shell = useShellStore(); const gridcoreSettings = useGridCoreSettingsStore(); @@ -1908,7 +1851,8 @@ async function loadVaultContent(): Promise { ); vaultLibraries.value = PUBLIC_LIBRARY_DEFS.map((def) => { const all = fetched.get(def.source) ?? []; - const docs = (def.tag ? all.filter((d) => d.tags.includes(def.tag)) : all) + const tag = def.tag; + const docs = (tag ? all.filter((d) => d.tags.includes(tag)) : all) .filter((d) => d.extension === "md" || d.extension === "markdown") .slice(0, MAX_DOCS_PER_LIBRARY); return { ...def, docs }; diff --git a/frontend-vue/vitest.config.ts b/frontend-vue/vitest.config.ts new file mode 100644 index 00000000..b625db53 --- /dev/null +++ b/frontend-vue/vitest.config.ts @@ -0,0 +1,13 @@ +import { defineConfig, mergeConfig } from "vitest/config"; + +import viteConfig from "./vite.config"; + +export default mergeConfig( + viteConfig, + defineConfig({ + test: { + // Playwright owns browser/golden specs; Vitest owns unit tests. + exclude: ["e2e/**", "node_modules/**", "dist/**"], + }, + }), +); From 076c7bbfa80f43ec155250f5faaa1309928edce5 Mon Sep 17 00:00:00 2001 From: Fred Porter Date: Tue, 18 Aug 2026 20:42:03 +0800 Subject: [PATCH 03/28] docs(frontend): define canonical surface ownership --- README.md | 6 +- docs/README.md | 1 + docs/SURFACE_OWNERSHIP.md | 57 +++++++++++++++++++ frontend-vue/src/router/index.ts | 2 +- frontend-vue/src/stores/extensions.ts | 9 ++- .../surfaces/dashboard/DashboardSurface.vue | 30 ++-------- 6 files changed, 70 insertions(+), 35 deletions(-) create mode 100644 docs/SURFACE_OWNERSHIP.md diff --git a/README.md b/README.md index 5cae3358..b73f364d 100644 --- a/README.md +++ b/README.md @@ -148,12 +148,12 @@ VS Code (Cline) → MCP Bridge → uCore (port 8484) | Surface | Route | Description | | ------------- | -------------------- | --------------------------------------------------------------------- | | Dashboard | `/` | Main landing, Dev Mode filtering | -| Assistant | `/assistui` | AI chat & agent-assisted workflows | -| Server | `/server` | Server management | +| Intelligence | `/intelligence` | Chat, planning, models, agents, budget, and history | +| Snackbar | `/snackbar` | Services, feeds, skills, snacks, extensions, logs, and MCP | | Developer | `/developer` | Developer tools | | System | `/system` | System settings | | Workflow | `/workflow` | Workflow builder | -| SnackMachine | `/server?tab=snacks` | Core snack workspace (packaged snacks via SnackMachine extension) | +| SnackMachine | `/snackbar?tab=snacks` | Core snack workspace (packaged snacks via SnackMachine extension) | | BrowserUI | `/browserui` | Browser automation | | Documentation | `/documentation` | Docs viewer | | uCode | `/ucode` | uCode runtime bridge: GridCore, GridSmith, teletext, terminal widgets | diff --git a/docs/README.md b/docs/README.md index f01e0976..69cdd82e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -40,6 +40,7 @@ | [MENUBAR_UNIFICATION_PLAN.md](MENUBAR_UNIFICATION_PLAN.md) | Menubar unification | | [FRONTEND_CONSOLIDATION_PLAYBOOK.md](FRONTEND_CONSOLIDATION_PLAYBOOK.md) | Frontend consolidation | | [VUE_REFACTOR_SURFACE_TAGGING.md](VUE_REFACTOR_SURFACE_TAGGING.md) | Vue refactor surface tags | +| [SURFACE_OWNERSHIP.md](SURFACE_OWNERSHIP.md) | Canonical route and tab ownership | ## Active Developer / Dev Mode Specs diff --git a/docs/SURFACE_OWNERSHIP.md b/docs/SURFACE_OWNERSHIP.md new file mode 100644 index 00000000..8729275c --- /dev/null +++ b/docs/SURFACE_OWNERSHIP.md @@ -0,0 +1,57 @@ +# uCore Surface Ownership + +**Status:** Canonical +**Updated:** 2026-08-18 + +This is the current UI Hub surface contract. Current routes and components take +precedence over historical feature plans. Superseded plans remain evidence, but +must not be used to recreate retired tabs. + +## Canonical surfaces + +| Surface | Route | Owns | +| --- | --- | --- | +| Dashboard | `/` | Navigation and ecosystem overview | +| Developer | `/developer` | Repositories, file preview, editing, diff and Git actions | +| Intelligence | `/intelligence` | Chat, planning, models, agents, budget decisions and history | +| Snackbar | `/snackbar` | Service health, feeds, skills, snacks, extensions, logs and MCP | +| System | `/system` | System pages, variables, secrets, global and user settings | +| Workflow | `/workflow` | User missions, tasks, automation, document editing and publishing | +| uCode | `/ucode` | Terminal, teletext, pixel, grid, layer and glyph runtime tools | +| Documentation | `/documentation` | Documentation and published knowledge | + +## Developer boundary + +Developer is deliberately a small repository tool with three tabs: Code, +Repository and Editor. It does not own models, agents, budgets, services, feeds, +skills, extensions, logs, MCP, missions or user tasks. + +The historical `udev` identifier may remain temporarily in saved extension +state and compatibility APIs. It refers to the built-in Developer surface; it +does not identify a separately installable repository or server. + +## Workflow boundary + +uFlow is the canonical workflow and task engine. uCore owns the Vue Workflow +surface and delegates persistence and execution through the uFlow extension +contract. uCore Tasker files are migration/development adapters, not a second +user workflow engine. + +## Compatibility routes + +- `/assistui/*` redirects to `/intelligence`. +- `/server/*` redirects to `/snackbar/*`. +- `/teletext/*` and `/terminal/*` redirect to their uCode tabs. +- `/snackmachine?tab=mcp` redirects to `/snackbar?tab=mcp`. + +New code and documentation must use canonical routes. + +## Duplicate presentation + +Some operational data can be summarized on more than one surface, but it has +one owner: + +- Intelligence owns agent use, model selection and budget decisions. +- Snackbar may show agent process health and budget alerts. +- `udos-budget` owns budget policy and persistence once its extension contract + replaces the current in-core implementation. diff --git a/frontend-vue/src/router/index.ts b/frontend-vue/src/router/index.ts index 7e014c30..f5d47a92 100644 --- a/frontend-vue/src/router/index.ts +++ b/frontend-vue/src/router/index.ts @@ -79,7 +79,7 @@ const routes: RouteRecordRaw[] = [ const tab = String(to.query.tab || "snacks"); if (tab === "workflows") return "/workflow?tab=publish"; if (tab === "vault") return "/workflow?tab=binder"; - if (tab === "mcp") return "/developer?tab=mcp-servers"; + if (tab === "mcp") return "/snackbar?tab=mcp"; if (tab === "variables") return "/system?tab=variables"; if (tab === "scheduler") return "/snackbar?tab=dashboard"; return "/snackbar?tab=snacks"; diff --git a/frontend-vue/src/stores/extensions.ts b/frontend-vue/src/stores/extensions.ts index 79fe6814..4cd1e958 100644 --- a/frontend-vue/src/stores/extensions.ts +++ b/frontend-vue/src/stores/extensions.ts @@ -134,17 +134,16 @@ const BUILTIN_MANIFESTS: ExtensionManifest[] = [ activation_required: false, description: "Built-in documentation viewer", }, - // Extensions that require installation + activation + // Compatibility id retained for saved state; Developer is built into uCore. { id: "udev", name: "Developer", kind: "surface", - required: false, + required: true, icon: "code", route: "/developer", - activation_required: true, - description: "Full developer lane — requires uDev", - install_url: "https://github.com/fredporter/uDev", + activation_required: false, + description: "Built-in repository browser and code editor", }, { id: "snack-shack", diff --git a/frontend-vue/src/surfaces/dashboard/DashboardSurface.vue b/frontend-vue/src/surfaces/dashboard/DashboardSurface.vue index 3587a4ab..4abc2e3e 100644 --- a/frontend-vue/src/surfaces/dashboard/DashboardSurface.vue +++ b/frontend-vue/src/surfaces/dashboard/DashboardSurface.vue @@ -60,7 +60,6 @@ import { useShellStore } from "../../stores/shell"; import { useExtensionStore } from "../../stores/extensions"; import SurfaceCard from "../../skills/molecules/SurfaceCard.vue"; import SurfaceTabNav from "../../skills/molecules/SurfaceTabNav.vue"; -import { SNACKBAR_BASE } from "@/api/base"; const router = useRouter(); const shell = useShellStore(); @@ -165,10 +164,10 @@ const SURFACE_CARD_DATA: Record< route: "/sonic", color: "var(--usx-color-success)", }, - // Manifest id for the uDev extension is "udev" — map it to the Developer card. + // The historical "udev" id is retained for saved extension state. udev: { title: "Developer", - description: "Dev Lane — Models, Agents, Kanban", + description: "Repositories, Code Review & Editing", icon: "code", route: "/developer", color: "var(--usx-color-danger)", @@ -206,9 +205,8 @@ const visibleSurfaces = computed(() => { seen.add(surface.manifest.id); } } - // Always show Developer card when the uDev repo exists (or is running) - // Guard against duplicates if the extension loop already added it. - if ((udevRepoExists || extStore.isRunning("udev")) && !seen.has("udev")) { + // Developer is a built-in uCore surface. Guard against catalogue duplicates. + if (!seen.has("udev")) { cards.push({ id: "udev", ...SURFACE_CARD_DATA.udev }); } // Always show Markdown Editor card @@ -237,27 +235,7 @@ const activeExtensions = computed(() => { })); }); -// uDev repo presence — show Developer card when ~/Code/uDev exists -const udevRepoExists = ref(false); - -async function probeUdevRepo() { - try { - const res = await fetch(`${SNACKBAR_BASE}/api/developer/repos?scope=all`, { - signal: AbortSignal.timeout(4000), - }); - if (!res.ok) return; - const payload = await res.json(); - const repos = Array.isArray(payload?.repos) ? payload.repos : []; - udevRepoExists.value = repos.some( - (repo: any) => String(repo?.name || "").toLowerCase() === "udev", - ); - } catch { - // Backend unreachable — fall back to running state - } -} - onMounted(() => { - void probeUdevRepo(); void extStore.fetchCatalogue(); }); From a009ded800b1e4b28e3dc8058c2b7714fe1602c7 Mon Sep 17 00:00:00 2001 From: Fred Porter Date: Tue, 18 Aug 2026 20:43:32 +0800 Subject: [PATCH 04/28] feat(config): establish detachable UDOS_HOME boundary --- backend/app/api/config_api.py | 4 ++ backend/app/core/settings.py | 66 ++++++++++++++++++++++------- docs/README.md | 1 + docs/UDOS_HOME_MIGRATION.md | 55 ++++++++++++++++++++++++ scripts/audit_udos_home.py | 80 +++++++++++++++++++++++++++++++++++ 5 files changed, 191 insertions(+), 15 deletions(-) create mode 100644 docs/UDOS_HOME_MIGRATION.md create mode 100644 scripts/audit_udos_home.py diff --git a/backend/app/api/config_api.py b/backend/app/api/config_api.py index 86a5a10a..a261c769 100644 --- a/backend/app/api/config_api.py +++ b/backend/app/api/config_api.py @@ -57,9 +57,13 @@ async def handle_get_config(request: web.Request) -> web.Response: ], "installation": [ _entry("UDOS_ROOT", str(settings.udos_root)), + _entry("UDOS_HOME", str(settings.udos_home)), _entry("Data Dir", str(settings.data_dir)), _entry("Config Dir", str(settings.config_dir)), _entry("Logs Dir", str(settings.logs_dir)), + _entry("User Vault", str(settings.vault_root)), + _entry("Shared Vaults", str(settings.shared_vault_root)), + _entry("Public Vaults", str(settings.public_vault_root)), _entry("Surface Registry", settings.surface_registry_path), _entry("Install Name", settings.install_name), ], diff --git a/backend/app/core/settings.py b/backend/app/core/settings.py index 0f261051..f992cba5 100644 --- a/backend/app/core/settings.py +++ b/backend/app/core/settings.py @@ -7,6 +7,40 @@ from pathlib import Path +def _udos_code_root() -> Path: + return Path( + os.environ.get( + "UDOS_ROOT", + os.environ.get( + "ROOT", + os.environ.get("UDOS_CODE", os.path.expanduser("~/Code")), + ), + ), + ).expanduser() + + +def _udos_home(code_root: Path) -> Path: + """Resolve the detachable runtime home without disrupting legacy installs.""" + explicit = os.environ.get("UDOS_HOME") + if explicit: + return Path(explicit).expanduser() + + canonical = code_root / ".udos" + legacy = Path.home() / ".ucore" + if legacy.exists() and not canonical.exists(): + return legacy + return canonical + + +_UDOS_ROOT = _udos_code_root() +_UDOS_HOME = _udos_home(_UDOS_ROOT) +_SECRETS_HOME = ( + _UDOS_HOME + if _UDOS_HOME == Path.home() / ".ucore" + else _UDOS_HOME / "secrets" +) + + @dataclass class Settings: """Central uCore configuration.""" @@ -28,26 +62,36 @@ class Settings: ).lower() in ("1", "true", "yes") # ── Paths ──────────────────────────────────────────────── + udos_home: Path = _UDOS_HOME data_dir: Path = Path( - os.environ.get("UCORE_DATA_DIR", os.path.expanduser("~/.ucore/data")), + os.environ.get("UCORE_DATA_DIR", str(_UDOS_HOME / "data")), ) config_dir: Path = Path( os.environ.get( "UCORE_CONFIG_DIR", - os.path.expanduser("~/.ucore/config"), + str(_UDOS_HOME / "config"), ), ) logs_dir: Path = Path( - os.environ.get("UCORE_LOGS_DIR", os.path.expanduser("~/.ucore/logs")), + os.environ.get("UCORE_LOGS_DIR", str(_UDOS_HOME / "logs")), ) memory_dir: Path = Path( os.environ.get( "UCORE_MEMORY_DIR", - os.path.expanduser("~/.ucore/memory"), + str(_UDOS_HOME / "memory"), ), ) secrets_dir: Path = Path( - os.environ.get("UCORE_SECRETS_DIR", os.path.expanduser("~/.ucore")), + os.environ.get("UCORE_SECRETS_DIR", str(_SECRETS_HOME)), + ) + vault_root: Path = Path( + os.environ.get("UDOS_VAULT_ROOT", os.path.expanduser("~/Vault")), + ) + shared_vault_root: Path = Path( + os.environ.get("UDOS_SHARED_ROOT", os.path.expanduser("~/Shared")), + ) + public_vault_root: Path = Path( + os.environ.get("UDOS_PUBLIC_ROOT", os.path.expanduser("~/Public")), ) # ── Snackbar ───────────────────────────────────────────── @@ -57,7 +101,7 @@ class Settings: # ── Surfaces ───────────────────────────────────────────── surface_registry_path: str = os.environ.get( "UCORE_SURFACE_REGISTRY", - os.path.expanduser("~/.ucore/surfaces.json"), + str(_UDOS_HOME / "surfaces.json"), ) # ── Security ───────────────────────────────────────────── @@ -79,15 +123,7 @@ class Settings: install_name: str = os.environ.get("UDOS_INSTALL_NAME", socket.gethostname()) # ── Spine (Code base path — all repos under ~/Code/) ───── - udos_root: Path = Path( - os.environ.get( - "UDOS_ROOT", - os.environ.get( - "ROOT", - os.environ.get("UDOS_CODE", os.path.expanduser("~/Code")), - ), - ), - ).expanduser() + udos_root: Path = _UDOS_ROOT # ── Ollama / AI ────────────────────────────────────────── ollama_base_url: str = os.environ.get( diff --git a/docs/README.md b/docs/README.md index 69cdd82e..eccdca06 100644 --- a/docs/README.md +++ b/docs/README.md @@ -41,6 +41,7 @@ | [FRONTEND_CONSOLIDATION_PLAYBOOK.md](FRONTEND_CONSOLIDATION_PLAYBOOK.md) | Frontend consolidation | | [VUE_REFACTOR_SURFACE_TAGGING.md](VUE_REFACTOR_SURFACE_TAGGING.md) | Vue refactor surface tags | | [SURFACE_OWNERSHIP.md](SURFACE_OWNERSHIP.md) | Canonical route and tab ownership | +| [UDOS_HOME_MIGRATION.md](UDOS_HOME_MIGRATION.md) | Runtime/vault boundary and safe migration | ## Active Developer / Dev Mode Specs diff --git a/docs/UDOS_HOME_MIGRATION.md b/docs/UDOS_HOME_MIGRATION.md new file mode 100644 index 00000000..db6cd516 --- /dev/null +++ b/docs/UDOS_HOME_MIGRATION.md @@ -0,0 +1,55 @@ +# uDOS Runtime Home Migration + +**Status:** Approved architecture; migration not yet executed +**Updated:** 2026-08-18 + +## Boundary + +`~/Code/.udos` is the canonical `UDOS_HOME`. It contains mutable application +state that should travel with, or be destroyed with, the uDOS installation: +configuration, logs, caches, indexes, service state, secrets, model metadata, +shared Python environments and compatibility data. + +User documents remain portable and outside the runtime home: + +| Root | Ownership | +| --- | --- | +| `~/Vault` | Primary private user vault | +| `~/Shared` | Add-on/shared vaults | +| `~/Public` | Public and publishable vaults | + +These roots must never be folded into `UDOS_HOME` by an automated migration. + +## Legacy inputs + +The current workstation has used four runtime roots over time: + +- `~/.ucore` +- `~/.udos` +- `~/.config/udos` +- `~/.local/share/udos` + +They may contain overlapping names with different meanings. Migration therefore +requires a manifest, collision classification and backup; it is not a recursive +merge. + +## Safe sequence + +1. Stop uDOS services and record their launch configuration. +2. Run `python scripts/audit_udos_home.py --json` and save the inventory. +3. Classify every collision as canonical, mergeable, obsolete or quarantined. +4. Back up all four legacy roots with a checksum manifest. +5. Stage runtime data into `~/Code/.udos` without deleting the sources. +6. Point services at `UDOS_HOME=~/Code/.udos` and run health/integration tests. +7. Retain legacy roots through a rollback window; archive or remove only after + explicit approval. + +The audit script is deliberately read-only. A separate migration command should +be implemented only after the collision manifest has been reviewed. + +## Compatibility phase + +uCore currently selects an explicit `UDOS_HOME` first. Without one, it continues +to use an existing `~/.ucore` installation until `~/Code/.udos` exists. Fresh +installs default to `~/Code/.udos`. This prevents a code deployment from silently +switching the live state directory before migration is complete. diff --git a/scripts/audit_udos_home.py b/scripts/audit_udos_home.py new file mode 100644 index 00000000..458c0dad --- /dev/null +++ b/scripts/audit_udos_home.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 +"""Read-only inventory for consolidating uDOS runtime state into UDOS_HOME.""" + +from __future__ import annotations + +import argparse +import json +import os +from pathlib import Path + + +LEGACY_ROOTS = ( + Path("~/.ucore"), + Path("~/.udos"), + Path("~/.config/udos"), + Path("~/.local/share/udos"), +) +VAULT_ROOTS = (Path("~/Vault"), Path("~/Shared"), Path("~/Public")) + + +def directory_size(path: Path) -> int: + total = 0 + for root, _, files in os.walk(path, followlinks=False): + root_path = Path(root) + for name in files: + try: + total += (root_path / name).stat(follow_symlinks=False).st_size + except (FileNotFoundError, PermissionError): + continue + return total + + +def describe(path: Path) -> dict[str, object]: + resolved = path.expanduser() + exists = resolved.exists() + return { + "path": str(resolved), + "exists": exists, + "kind": "symlink" if resolved.is_symlink() else "directory" if resolved.is_dir() else "file" if resolved.is_file() else "missing", + "bytes": directory_size(resolved) if resolved.is_dir() else resolved.stat().st_size if exists else 0, + } + + +def inventory(target: Path) -> dict[str, object]: + return { + "target": describe(target), + "legacy_runtime_roots": [describe(path) for path in LEGACY_ROOTS], + "portable_vault_roots": [describe(path) for path in VAULT_ROOTS], + "policy": { + "move": "runtime state only, after collision review and backup", + "keep": [str(path) for path in VAULT_ROOTS], + }, + } + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--target", + type=Path, + default=Path(os.environ.get("UDOS_HOME", "~/Code/.udos")), + help="proposed canonical runtime home (default: UDOS_HOME or ~/Code/.udos)", + ) + parser.add_argument("--json", action="store_true", help="emit machine-readable JSON") + args = parser.parse_args() + report = inventory(args.target.expanduser()) + if args.json: + print(json.dumps(report, indent=2)) + else: + print(f"Proposed UDOS_HOME: {report['target']['path']}") + for group in ("legacy_runtime_roots", "portable_vault_roots"): + print(f"\n{group.replace('_', ' ').title()}:") + for item in report[group]: + print(f" {item['path']}: {item['kind']}, {item['bytes']} bytes") + print("\nNo files were changed.") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From f42e71d84d696b1680643ea8f0f244df1c0bc00a Mon Sep 17 00:00:00 2001 From: Fred Porter Date: Tue, 18 Aug 2026 20:50:00 +0800 Subject: [PATCH 05/28] fix(menu): respect user quit under launchd --- backend/app/menu/launchd_manager.py | 5 ++++- backend/app/menu/unified_menu_simple.py | 2 +- backend/tests/test_launchd_manager.py | 10 ++++++++++ scripts/install_ucore_menu_launchd.sh | 7 +++++-- 4 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 backend/tests/test_launchd_manager.py diff --git a/backend/app/menu/launchd_manager.py b/backend/app/menu/launchd_manager.py index 3610bc14..0499be62 100644 --- a/backend/app/menu/launchd_manager.py +++ b/backend/app/menu/launchd_manager.py @@ -48,7 +48,10 @@ def get_plist_content() -> str: RunAtLoad KeepAlive - + + SuccessfulExit + + LSUIElement LimitLoadToSessionType diff --git a/backend/app/menu/unified_menu_simple.py b/backend/app/menu/unified_menu_simple.py index 9dd35863..5273f002 100644 --- a/backend/app/menu/unified_menu_simple.py +++ b/backend/app/menu/unified_menu_simple.py @@ -823,7 +823,7 @@ def toggleDevMode_(self, _sender): self.startDevMode_(None) def quitApp_(self, _sender): - """Quit the menu app.""" + """Quit cleanly; launchd restarts crashes, not successful exits.""" log.info("Quitting uCore Menu") if self._refresh_timer: self._refresh_timer.cancel() diff --git a/backend/tests/test_launchd_manager.py b/backend/tests/test_launchd_manager.py new file mode 100644 index 00000000..ebbd4f2b --- /dev/null +++ b/backend/tests/test_launchd_manager.py @@ -0,0 +1,10 @@ +import plistlib + +from app.menu.launchd_manager import get_plist_content + + +def test_menu_restarts_crashes_but_respects_clean_quit(): + plist = plistlib.loads(get_plist_content().encode()) + + assert plist["RunAtLoad"] is True + assert plist["KeepAlive"] == {"SuccessfulExit": False} diff --git a/scripts/install_ucore_menu_launchd.sh b/scripts/install_ucore_menu_launchd.sh index 7878a0f0..7bd8be7a 100755 --- a/scripts/install_ucore_menu_launchd.sh +++ b/scripts/install_ucore_menu_launchd.sh @@ -127,7 +127,10 @@ cat > "$PLIST_PATH" <RunAtLoad KeepAlive - + + SuccessfulExit + + StandardOutPath ${LOG_DIR}/ucore-menu-stdout.log StandardErrorPath @@ -156,4 +159,4 @@ echo " Plist: ${PLIST_PATH}" echo " Python: ${PYTHON_BIN}" echo "" echo "The uCore menu will now start automatically at login." -echo "To uninstall: $0 --uninstall" \ No newline at end of file +echo "To uninstall: $0 --uninstall" From a152d25ee08e4317495441ce8977c749c2ba4ec8 Mon Sep 17 00:00:00 2001 From: Fred Porter Date: Tue, 18 Aug 2026 21:44:52 +0800 Subject: [PATCH 06/28] feat(governance): enforce canonical ecosystem storage --- .github/workflows/ci.yml | 8 +++ backend/app/menu/launchd_manager.py | 20 +++--- backend/app/menu/lockfile.py | 4 +- backend/app/menu/unified_menu_simple.py | 9 +-- backend/tests/test_home_path_policy.py | 22 +++++++ backend/tests/test_launchd_manager.py | 12 +++- docs/ECOSYSTEM_STORAGE_ARCHITECTURE.md | 51 +++++++++++++++ docs/README.md | 2 + docs/WORKSTATION_MIGRATION_2026-08-18.md | 54 ++++++++++++++++ scripts/check_home_path_policy.py | 80 ++++++++++++++++++++++++ 10 files changed, 248 insertions(+), 14 deletions(-) create mode 100644 backend/tests/test_home_path_policy.py create mode 100644 docs/ECOSYSTEM_STORAGE_ARCHITECTURE.md create mode 100644 docs/WORKSTATION_MIGRATION_2026-08-18.md create mode 100644 scripts/check_home_path_policy.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f1892880..9c7cedce 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,6 +17,14 @@ jobs: - name: Validate planning governance run: bash scripts/validate_planning_governance.sh + - name: Reject new home-root state paths (pull request) + if: github.event_name == 'pull_request' + run: git diff --unified=0 "origin/${{ github.base_ref }}...HEAD" | python3 scripts/check_home_path_policy.py --diff-stdin + + - name: Reject new home-root state paths (push) + if: github.event_name == 'push' + run: git diff --unified=0 "${{ github.event.before }}..${{ github.sha }}" | python3 scripts/check_home_path_policy.py --diff-stdin + - name: Validate capability requirements coverage run: python3 scripts/validate_capability_requirements.py diff --git a/backend/app/menu/launchd_manager.py b/backend/app/menu/launchd_manager.py index 0499be62..a5d8e687 100644 --- a/backend/app/menu/launchd_manager.py +++ b/backend/app/menu/launchd_manager.py @@ -16,8 +16,10 @@ UCORE_LABEL = "com.udos.ucore-menu" UCORE_PLIST = os.path.expanduser(f"~/Library/LaunchAgents/{UCORE_LABEL}.plist") -UCORE_LOCKFILE = os.path.expanduser("~/.ucore/ucore-menu.pid") UCORE_BACKEND_DIR = os.environ.get("UCORE_BACKEND_DIR", str(Path.home() / "Code" / "uCore" / "backend")) +UDOS_HOME = Path(os.environ.get("UDOS_HOME", Path.home() / "Code" / ".udos")).expanduser() +UCORE_LOCKFILE = str(UDOS_HOME / "ucore-menu.pid") +UCORE_LOG_DIR = UDOS_HOME / "logs" # The canonical module to run - MUST be kept in sync across all callers CANONICAL_MODULE = "app.menu.unified_menu_simple" @@ -57,15 +59,17 @@ def get_plist_content() -> str: LimitLoadToSessionType Aqua StandardOutPath - {os.path.expanduser('~/.ucore/logs/ucore-menu-stdout.log')} + {UCORE_LOG_DIR / 'ucore-menu-stdout.log'} StandardErrorPath - {os.path.expanduser('~/.ucore/logs/ucore-menu-stderr.log')} + {UCORE_LOG_DIR / 'ucore-menu-stderr.log'} TimeOut 60 EnvironmentVariables UCORE_DEBUG 1 + UDOS_HOME + {UDOS_HOME} PATH /opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin @@ -83,7 +87,7 @@ def install() -> bool: log.info("Installing uCore Menu launchd plist...") # Ensure directories exist - os.makedirs(os.path.expanduser("~/.ucore/logs"), exist_ok=True) + UCORE_LOG_DIR.mkdir(parents=True, exist_ok=True) Path(UCORE_PLIST).parent.mkdir(parents=True, exist_ok=True) # Write plist @@ -221,15 +225,17 @@ def get_frontend_plist_content() -> str: StandardOutPath - {os.path.expanduser('~/.ucore/logs/ucore-frontend-stdout.log')} + {UCORE_LOG_DIR / 'ucore-frontend-stdout.log'} StandardErrorPath - {os.path.expanduser('~/.ucore/logs/ucore-frontend-stderr.log')} + {UCORE_LOG_DIR / 'ucore-frontend-stderr.log'} TimeOut 60 EnvironmentVariables UCORE_DEBUG 1 + UDOS_HOME + {UDOS_HOME} PATH /opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin @@ -244,7 +250,7 @@ def install_frontend() -> bool: uid = os.getuid() log.info("Installing uCore Frontend launchd plist...") - os.makedirs(os.path.expanduser("~/.ucore/logs"), exist_ok=True) + UCORE_LOG_DIR.mkdir(parents=True, exist_ok=True) Path(UCORE_FRONTEND_PLIST).parent.mkdir(parents=True, exist_ok=True) Path(UCORE_FRONTEND_PLIST).write_text(get_frontend_plist_content(), encoding="utf-8") diff --git a/backend/app/menu/lockfile.py b/backend/app/menu/lockfile.py index 26558a10..4e344e8a 100644 --- a/backend/app/menu/lockfile.py +++ b/backend/app/menu/lockfile.py @@ -14,7 +14,8 @@ log = logging.getLogger("ucore.menu.lockfile") -LOCKFILE_PATH = Path.home() / ".ucore" / "menu.lock" +UDOS_HOME = Path(os.environ.get("UDOS_HOME", Path.home() / "Code" / ".udos")).expanduser() +LOCKFILE_PATH = UDOS_HOME / "menu.lock" def _get_boot_time() -> float: @@ -161,4 +162,3 @@ def release_lock() -> None: pass # Corrupt lock file — leave it, next start will clean it except Exception as exc: log.error("Failed to release lock: %s", exc) - diff --git a/backend/app/menu/unified_menu_simple.py b/backend/app/menu/unified_menu_simple.py index 5273f002..01e753c4 100644 --- a/backend/app/menu/unified_menu_simple.py +++ b/backend/app/menu/unified_menu_simple.py @@ -68,7 +68,8 @@ UCORE_LABEL = "com.udos.ucore-menu" UCORE_PLIST = os.path.expanduser(f"~/Library/LaunchAgents/{UCORE_LABEL}.plist") -UCORE_LOCKFILE = os.path.expanduser("~/.ucore/ucore-menu.pid") +UDOS_HOME = Path(os.environ.get("UDOS_HOME", Path.home() / "Code" / ".udos")).expanduser() +UCORE_LOCKFILE = str(UDOS_HOME / "ucore-menu.pid") UCORE_BACKEND_DIR = os.environ.get("UCORE_BACKEND_DIR", str(Path.home() / "Code" / "uCore" / "backend")) SNACKMACHINE_REPO_DIR = Path( os.environ.get( @@ -93,13 +94,13 @@ "roundtable": "http://localhost:5175/assistui", } -log_dir = os.path.expanduser("~/.ucore/logs") -os.makedirs(log_dir, exist_ok=True) +log_dir = UDOS_HOME / "logs" +log_dir.mkdir(parents=True, exist_ok=True) logging.basicConfig( level=logging.INFO, format="%(asctime)s [%(levelname)s] ucore-menu: %(message)s", handlers=[ - logging.FileHandler(os.path.join(log_dir, "ucore-menu.log")), + logging.FileHandler(log_dir / "ucore-menu.log"), logging.StreamHandler(), ], ) diff --git a/backend/tests/test_home_path_policy.py b/backend/tests/test_home_path_policy.py new file mode 100644 index 00000000..e8e0bbd8 --- /dev/null +++ b/backend/tests/test_home_path_policy.py @@ -0,0 +1,22 @@ +# path-policy: allow-literals +from scripts.check_home_path_policy import violations + + +def test_rejects_new_legacy_home_state_path(): + diff = "+++ b/backend/example.py\n+STATE = Path.home() / '.ucore' / 'state.json'\n" + assert violations(diff) + + +def test_accepts_udos_home_path(): + diff = "+++ b/backend/example.py\n+STATE = settings.udos_home / 'state.json'\n" + assert violations(diff) == [] + + +def test_accepts_canonical_default_home(): + diff = '+++ b/backend/example.py\n+HOME = Path.home() / "Code" / ".udos"\n' + assert violations(diff) == [] + + +def test_explicit_line_exception_is_auditable(): + diff = "+++ b/backend/example.py\n+LEGACY = '~/.ucore' # path-policy: allow\n" + assert violations(diff) == [] diff --git a/backend/tests/test_launchd_manager.py b/backend/tests/test_launchd_manager.py index ebbd4f2b..c833af33 100644 --- a/backend/tests/test_launchd_manager.py +++ b/backend/tests/test_launchd_manager.py @@ -1,6 +1,7 @@ import plistlib +from pathlib import Path -from app.menu.launchd_manager import get_plist_content +from app.menu.launchd_manager import UDOS_HOME, get_frontend_plist_content, get_plist_content def test_menu_restarts_crashes_but_respects_clean_quit(): @@ -8,3 +9,12 @@ def test_menu_restarts_crashes_but_respects_clean_quit(): assert plist["RunAtLoad"] is True assert plist["KeepAlive"] == {"SuccessfulExit": False} + assert plist["EnvironmentVariables"]["UDOS_HOME"] == str(UDOS_HOME) + assert Path(plist["StandardOutPath"]).is_relative_to(UDOS_HOME) + + +def test_frontend_uses_canonical_runtime_home(): + plist = plistlib.loads(get_frontend_plist_content().encode()) + + assert plist["EnvironmentVariables"]["UDOS_HOME"] == str(UDOS_HOME) + assert Path(plist["StandardOutPath"]).is_relative_to(UDOS_HOME) diff --git a/docs/ECOSYSTEM_STORAGE_ARCHITECTURE.md b/docs/ECOSYSTEM_STORAGE_ARCHITECTURE.md new file mode 100644 index 00000000..f4dd4eed --- /dev/null +++ b/docs/ECOSYSTEM_STORAGE_ARCHITECTURE.md @@ -0,0 +1,51 @@ + +# Ecosystem Storage Architecture + +**Status:** Canonical +**Updated:** 2026-08-18 + +## Ownership boundary + +| Location | Owner | Lifecycle | +| --- | --- | --- | +| `~/Code/` | Git repositories | Clone, develop, archive independently | +| `~/Code/.udos` | uDOS runtime | Detachable and destructible as one unit | +| `~/Vault` | User | Primary private document vault | +| `~/Shared` | User/workspaces | Shared and add-on vaults | +| `~/Public` | User/publishing | Public and publishable documents | +| Standard credential paths | User and owning applications | Survive uDOS removal | + +`UDOS_HOME` defaults to `~/Code/.udos`. It owns application configuration, +logs, indexes, caches, model data, container data, generated state, runtime +metadata, compatibility archives and shared toolchains. + +Credentials and operating-system integration are deliberately excluded: +`~/.ssh`, `~/.gitconfig`, `~/.config/gh`, `~/.npmrc`, `~/.docker`, `~/.kube`, +`~/.codex`, macOS Keychain and application-owned `~/Library` data. + +## Compatibility links + +The current workstation temporarily retains zero-storage links at historical +paths while active code is migrated. They are compatibility interfaces, not +approved write targets. New code must resolve `UDOS_HOME` instead. + +## Drift prevention + +Three controls apply: + +1. The workspace `AGENTS.md` gives every compatible coding agent the same + storage, ownership and lifecycle contract. +2. `scripts/check_home_path_policy.py` rejects newly added hard-coded uDOS + state paths in staged changes or supplied CI diffs. +3. CI applies the checker to additions in pull requests and pushes. Historical + references are migration debt, not precedent for new work. + +Literal legacy paths in migration documentation require the explicit +`path-policy: allow-literals` marker. Individual exceptional lines require a +`path-policy: allow` comment so exceptions remain searchable. + +## Destruction contract + +Destroying or snapping off uDOS may include `~/Code/.udos` and selected code +repositories only. It must never remove document vaults, credentials, Codex +configuration or general application data without a separately approved plan. diff --git a/docs/README.md b/docs/README.md index eccdca06..09ff53a7 100644 --- a/docs/README.md +++ b/docs/README.md @@ -42,6 +42,8 @@ | [VUE_REFACTOR_SURFACE_TAGGING.md](VUE_REFACTOR_SURFACE_TAGGING.md) | Vue refactor surface tags | | [SURFACE_OWNERSHIP.md](SURFACE_OWNERSHIP.md) | Canonical route and tab ownership | | [UDOS_HOME_MIGRATION.md](UDOS_HOME_MIGRATION.md) | Runtime/vault boundary and safe migration | +| [ECOSYSTEM_STORAGE_ARCHITECTURE.md](ECOSYSTEM_STORAGE_ARCHITECTURE.md) | Canonical storage, credentials and drift controls | +| [WORKSTATION_MIGRATION_2026-08-18.md](WORKSTATION_MIGRATION_2026-08-18.md) | Migration record, verification and rollback boundary | ## Active Developer / Dev Mode Specs diff --git a/docs/WORKSTATION_MIGRATION_2026-08-18.md b/docs/WORKSTATION_MIGRATION_2026-08-18.md new file mode 100644 index 00000000..7da16172 --- /dev/null +++ b/docs/WORKSTATION_MIGRATION_2026-08-18.md @@ -0,0 +1,54 @@ + +# Workstation Migration — 2026-08-18 + +**Status:** Completed with compatibility links + +**Host:** fredbook +**Canonical runtime:** `/Users/fredbook/Code/.udos` + +## Result + +- Current uCore state moved from `~/.ucore` to `~/Code/.udos`. +- Historical uDOS, Snackbar, uCode, HomeNest and related state roots preserved + beneath `~/Code/.udos/legacy-home-roots`. +- Colima data moved beneath `~/Code/.udos/runtimes/colima` and verified healthy. +- Ollama models moved beneath `~/Code/.udos/runtimes/ollama`; eight models were + visible after restart. +- Developer SDKs, environments and caches moved beneath + `~/Code/.udos/toolchains/home-roots`. +- Obsolete VS Code and external-agent data moved to + `~/Code/ARCHIVED/cleanup-2026-08-18` for a rollback window. +- Docker and Kubernetes configuration were restored physically to `~/.docker` + and `~/.kube` because they contain external-integration credentials/context. +- User vaults were not modified. + +## Intentionally retained home state + +The remaining directories are owned by macOS, Codex, credentials, or installed +applications rather than uDOS: `.codex`, `.config`, `.ssh`, `.docker`, `.kube`, +`.adobe`, `.cups`, `.dropbox`, `.slack`, `.swiftbar-plugin-state`, `.swiftpm`, +`.homebrew`, `.zsh_sessions`, and `.Trash`. + +GitHub CLI authentication remains in `~/.config/gh`; SSH and Git identity remain +in their standard locations. These paths are excluded from uDOS destruction. + +## Temporary compatibility links + +Compatibility links remain for historical consumers, including `.ucore`, +`.udos`, `.colima`, `.ollama`, `.local`, `.nvm`, `.npm`, `.cache`, `.android`, +language tool caches and old virtual environments. Their data resides physically +under `~/Code/.udos`. New code must not use these links as canonical paths. + +They can be removed individually after repository scans report no active +consumer and launch/login tests pass without them. + +## Verification + +- uCore backend `/api/health`: healthy, version 4.0.5. +- uCore frontend: HTTP 200 on port 5175. +- Menu, server and frontend launch jobs: running. +- Menu launch policy: start at login, restart unsuccessful exits, respect clean + user Quit until the next login/restart. +- Colima: running with Docker runtime under Virtualization.Framework. +- Ollama: service running with eight migrated models visible. +- Node 20.20.2 and npm 10.8.2 resolve through compatibility paths. diff --git a/scripts/check_home_path_policy.py b/scripts/check_home_path_policy.py new file mode 100644 index 00000000..7640d1f2 --- /dev/null +++ b/scripts/check_home_path_policy.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 +# path-policy: allow-literals +"""Reject newly added hard-coded uDOS state paths beneath the user home.""" + +from __future__ import annotations + +import argparse +import re +import subprocess +import sys +from pathlib import Path + + +FORBIDDEN = re.compile( + r"(?:~|\$\{?HOME\}?|Path\.home\(\)|os\.path\.expanduser\([^)]*~)" + r"[^\n]*(?:\.ucore|\.udos|\.snackbar|\.snacks|\.uds|\.uCode1|\.uhomenest)" +) +ALLOW_MARKER = "path-policy: allow-literals" +CANONICAL_HOME = re.compile( + r"(?:~|\$\{?HOME\}?|Path\.home\(\))[^\n]*(?:/|\"|')Code(?:/|\"|')[^\n]*\.udos" +) + + +def staged_diff() -> str: + return subprocess.run( + ["git", "diff", "--cached", "--unified=0", "--no-color"], + check=True, + capture_output=True, + text=True, + ).stdout + + +def file_allows_literals(path: str) -> bool: + candidate = Path(path) + if not candidate.is_file(): + return False + try: + return ALLOW_MARKER in "\n".join(candidate.read_text(errors="replace").splitlines()[:10]) + except OSError: + return False + + +def violations(diff: str) -> list[str]: + current_file = "" + allowed = False + found: list[str] = [] + for line in diff.splitlines(): + if line.startswith("+++ b/"): + current_file = line[6:] + allowed = file_allows_literals(current_file) + continue + if allowed or not line.startswith("+") or line.startswith("+++"): + continue + addition = line[1:] + if ( + "path-policy: allow" not in addition + and not CANONICAL_HOME.search(addition) + and FORBIDDEN.search(addition) + ): + found.append(f"{current_file}: {addition.strip()}") + return found + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--diff-stdin", action="store_true", help="read a unified diff from stdin") + args = parser.parse_args() + diff = sys.stdin.read() if args.diff_stdin else staged_diff() + found = violations(diff) + if not found: + print("Home path policy: OK") + return 0 + print("New hard-coded home state paths are forbidden; use UDOS_HOME:") + for item in found: + print(f" {item}") + return 1 + + +if __name__ == "__main__": + raise SystemExit(main()) From 141e81283cefe231be79878a5db000de2ce5b4b0 Mon Sep 17 00:00:00 2001 From: Fred Porter Date: Tue, 18 Aug 2026 21:45:47 +0800 Subject: [PATCH 07/28] docs(migration): record integration verification --- docs/WORKSTATION_MIGRATION_2026-08-18.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/WORKSTATION_MIGRATION_2026-08-18.md b/docs/WORKSTATION_MIGRATION_2026-08-18.md index 7da16172..24fd0dff 100644 --- a/docs/WORKSTATION_MIGRATION_2026-08-18.md +++ b/docs/WORKSTATION_MIGRATION_2026-08-18.md @@ -52,3 +52,8 @@ consumer and launch/login tests pass without them. - Colima: running with Docker runtime under Virtualization.Framework. - Ollama: service running with eight migrated models visible. - Node 20.20.2 and npm 10.8.2 resolve through compatibility paths. +- GitHub CLI authentication remains available for the `fredporter` account. +- Docker context `colima` is healthy on server version 29.2.1. +- No Kubernetes context is currently selected. +- The SSH agent currently has no loaded identities; key files remain untouched + in the standard `~/.ssh` directory. From ec0e00f33cc6bdecedb613e6a6455c1bb9370c2e Mon Sep 17 00:00:00 2001 From: Fred Porter Date: Tue, 18 Aug 2026 22:05:08 +0800 Subject: [PATCH 08/28] fix(skills): enforce authorization and contain Cline --- backend/app/api/skills.py | 8 +- .../app/skills/builtin/skill_cline_invoke.py | 163 ++++++------------ .../skills/builtin/skill_dev_mode_executor.py | 53 ++---- backend/app/skills/registry.py | 18 +- backend/tests/test_skill_cline_policy.py | 32 ++++ .../test_skill_registry_authorization.py | 33 ++++ 6 files changed, 154 insertions(+), 153 deletions(-) create mode 100644 backend/tests/test_skill_cline_policy.py create mode 100644 backend/tests/test_skill_registry_authorization.py diff --git a/backend/app/api/skills.py b/backend/app/api/skills.py index add287ba..5fa503b7 100644 --- a/backend/app/api/skills.py +++ b/backend/app/api/skills.py @@ -18,7 +18,7 @@ # Default skill paths — try Python registry first, then filesystem from app.services.health import get_health_summary -from app.skills.registry import get_skill +from app.skills.registry import get_skill, run_skill_by_id from app.skills.state import read_state SKILL_PATHS = [ @@ -91,7 +91,11 @@ async def _run_skill_by_id( "requires_confirmation": True, }, status=403) - result = await skill.run(**kwargs) + result = await run_skill_by_id( + skill_id, + execution_authorized=confirmed, + **kwargs, + ) # Broadcast completion to SSE subscribers try: from app.api.render_api import publish_event diff --git a/backend/app/skills/builtin/skill_cline_invoke.py b/backend/app/skills/builtin/skill_cline_invoke.py index d72b23ed..77aa40b6 100644 --- a/backend/app/skills/builtin/skill_cline_invoke.py +++ b/backend/app/skills/builtin/skill_cline_invoke.py @@ -1,14 +1,9 @@ """Cline Invoke Skill — invoke Cline CLI from uCore skills. -Invokes Cline CLI (VS Code extension's CLI) from within uCore's skill -ecosystem, allowing Dev Mode to use Cline as an agentic executor for -complex multi-step tasks. +Retained as an optional, contained planning executor. Act/yolo execution is +disabled until the worktree, diff-review, budget and approval harness exists. -Modes: - - yolo: autonomous execution with auto-approval on - - interactive: auto-approval off - -Integrates with: Cline CLI, OpenRouter API, gh CLI. +Integrates with: Cline CLI. """ from __future__ import annotations @@ -66,10 +61,9 @@ def _load_user_vars() -> dict: def _resolve_cline_runtime_config(kwargs: dict, mode: str) -> dict[str, str]: """Resolve provider/model/thinking/approval from internal config first. - Fail-fast policy: - - provider/model must be explicitly configured in kwargs, variables store, - or UCORE_CLINE_* env vars. - - do not silently hardcode provider/model defaults. + Fail-fast policy: the model must be explicitly configured in kwargs, + variables, or UCORE_CLINE_MODEL. Provider authentication belongs to Cline's + isolated config directory and is never passed through uCore command lines. """ user_vars = _load_user_vars() @@ -92,23 +86,11 @@ def _resolve_cline_runtime_config(kwargs: dict, mode: str) -> dict[str, str]: or "low" ) - auto_approve = str( - kwargs.get( - "auto_approve", - user_vars.get( - "cline_auto_approve", - "true" if mode == "yolo" else "false", - ), - ), - ).strip().lower() - if auto_approve not in {"true", "false"}: - auto_approve = "true" if mode == "yolo" else "false" - return { "provider": provider, "model": model, "thinking": thinking, - "auto_approve": auto_approve, + "auto_approve": "false", } @@ -116,8 +98,8 @@ def _build_repair_instructions(missing: list[str]) -> list[str]: """Return actionable repair steps for missing runtime config.""" steps = [ "Set values via internal API: PUT /api/variables/user", - "Example payload: {\"cline_provider\": \"ollama\", \"cline_model\": \"qwen2.5-coder:3b\", \"cline_thinking\": \"low\"}", - "Or set env vars: UCORE_CLINE_PROVIDER and UCORE_CLINE_MODEL", + "Example payload: {\"cline_model\": \"qwen2.5-coder:3b\", \"cline_thinking\": \"low\"}", + "Or set env var: UCORE_CLINE_MODEL", ] if "api_key" in missing: steps.append( @@ -133,9 +115,9 @@ class ClineInvokeSkill(BaseSkill): id="cline-invoke", name="Cline Invoke", description=( - "Invoke Cline CLI from uCore skills using positional" - " prompt mode. Supports yolo (auto-approve true) and" - " interactive (auto-approve false)." + "Optional Cline planning adapter. Disabled by default;" + " act/yolo execution is blocked until sandboxed worktree" + " review is implemented." ), category="developer", timeout=300, @@ -150,8 +132,8 @@ class ClineInvokeSkill(BaseSkill): name="mode", type="string", required=False, - default="interactive", - description="Execution mode: 'yolo' or 'interactive'", + default="plan", + description="Execution mode: 'plan' only", ), SkillParam( name="cwd", @@ -196,11 +178,8 @@ class ClineInvokeSkill(BaseSkill): description="Thinking level override: none|low|medium|high|xhigh", ), SkillParam( - name="auto_approve", - type="string", - required=False, - default="", - description="Override auto-approve: true|false", + name="auto_approve", type="string", required=False, + default="false", description="Reserved; always forced false", ), ], requires_confirmation=True, @@ -208,16 +187,43 @@ class ClineInvokeSkill(BaseSkill): async def run(self, **kwargs) -> dict: task = kwargs.get("task", "").strip() - mode = kwargs.get("mode", "interactive").lower() + mode = kwargs.get("mode", "plan").lower() cwd = kwargs.get("cwd", str(Path.cwd())) - timeout = int(kwargs.get("timeout", 120)) + timeout = min(int(kwargs.get("timeout", 120)), 180) context = kwargs.get("context", "") if not task: return {"success": False, "error": "task is required"} - if mode not in ("yolo", "interactive"): - mode = "interactive" + if os.environ.get("UCORE_ENABLE_CLINE", "").lower() not in {"1", "true", "yes"}: + return { + "success": False, + "error": "Cline is disabled by policy", + "repair_required": True, + "enable_with": "UCORE_ENABLE_CLINE=true", + "allowed_mode": "plan", + } + + if mode != "plan" or str(kwargs.get("auto_approve", "false")).lower() == "true": + return { + "success": False, + "error": "Cline act/yolo and auto-approval are disabled by policy", + "allowed_mode": "plan", + } + + repo_path = Path(cwd).expanduser().resolve() + code_root = settings.udos_root.resolve() + if ( + repo_path == code_root + or not repo_path.is_relative_to(code_root) + or not (repo_path / ".git").exists() + or "ARCHIVED" in repo_path.parts + ): + return { + "success": False, + "error": "Cline cwd must be an active allow-listed Git repository under UDOS_ROOT", + "cwd": str(repo_path), + } # Locate Cline CLI cline_bin = _find_cline_binary() @@ -234,8 +240,6 @@ async def run(self, **kwargs) -> dict: runtime_cfg = _resolve_cline_runtime_config(kwargs, mode) missing_cfg: list[str] = [] - if not runtime_cfg["provider"]: - missing_cfg.append("provider") if not runtime_cfg["model"]: missing_cfg.append("model") if missing_cfg: @@ -250,46 +254,8 @@ async def run(self, **kwargs) -> dict: "repair_steps": _build_repair_instructions(missing_cfg), } - # Resolve API key: DEEPSEEK_API_KEY first, then SecretStore, then - # OPENROUTER_API_KEY, then env file fallback - api_key: str = "" - # Priority 1: DEEPSEEK_API_KEY from environment - api_key = os.environ.get("DEEPSEEK_API_KEY", "") - # Priority 2: DEEPSEEK_API_KEY from SecretStore - if not api_key: - try: - from app.secret.store import get_store - store = get_store() - dsk_val = store.get("DEEPSEEK_API_KEY") - if dsk_val: - api_key = dsk_val - except Exception: - pass - # Priority 3: OPENROUTER_API_KEY from environment (fallback) - if not api_key: - api_key = os.environ.get("OPENROUTER_API_KEY", "") - # Priority 4: DEEPSEEK_API_KEY or OPENROUTER_API_KEY from env file - - if not api_key: - try: - config_path = ( - Path.home() / ".config" / "hivemind" / ".env" - ) - if config_path.exists(): - content = config_path.read_text() - for line in content.splitlines(): - if line.startswith("DEEPSEEK_API_KEY="): - api_key = line.split("=", 1)[1].strip().strip('"') - if api_key: - break - if line.startswith("OPENROUTER_API_KEY="): - api_key = line.split("=", 1)[1].strip().strip('"') - if api_key: - break - except Exception: - pass - - # Build command against current Cline CLI interface. + # Cline owns its provider authentication inside the canonical config + # directory. uCore never extracts or passes provider keys on the CLI. prompt = task if context: prompt = f"{task}\n\nContext:\n{context}" @@ -297,39 +263,20 @@ async def run(self, **kwargs) -> dict: cmd = [ cline_bin, "--json", - "--cwd", cwd, - "-P", runtime_cfg["provider"], + "--cwd", str(repo_path), + "--config", str(settings.udos_home / "integrations" / "cline"), + "--plan", "-m", runtime_cfg["model"], - "--thinking", runtime_cfg["thinking"], + "--reasoning-effort", runtime_cfg["thinking"], "-t", str(timeout), - "--auto-approve", runtime_cfg["auto_approve"], + "--double-check-completion", ] - # API key is only required for key-based providers. - needs_key_provider = runtime_cfg["provider"].lower() in { - "openrouter", "openai", "anthropic", "gemini", "groq", "deepseek", "mistral", - } - if needs_key_provider and not api_key: - missing_cfg = ["api_key"] - return { - "success": False, - "error": ( - f"Provider '{runtime_cfg['provider']}' requires an API key, but none was found" - ), - "repair_required": True, - "missing": missing_cfg, - "repair_steps": _build_repair_instructions(missing_cfg), - "provider": runtime_cfg["provider"], - "model": runtime_cfg["model"], - } - if needs_key_provider and api_key: - cmd.extend(["-k", api_key]) - cmd.append(prompt) # Execute try: - result = await self._run_cline(cmd, cwd, timeout) + result = await self._run_cline(cmd, str(repo_path), timeout) if result.get("needs_auth"): return { "success": False, diff --git a/backend/app/skills/builtin/skill_dev_mode_executor.py b/backend/app/skills/builtin/skill_dev_mode_executor.py index f6a40160..0c350052 100644 --- a/backend/app/skills/builtin/skill_dev_mode_executor.py +++ b/backend/app/skills/builtin/skill_dev_mode_executor.py @@ -37,10 +37,10 @@ } EXECUTOR_CHOICE = { - "implementation": "cline-invoke", - "coding": "cline-invoke", - "debugging": "cline-invoke", - "testing": "cline-invoke", + "implementation": "route_task", + "coding": "route_task", + "debugging": "route_task", + "testing": "route_task", "architecture": "roundtable-dispatch", "design": "hivemind-consensus", "planning": "hivemind-consensus", @@ -356,44 +356,13 @@ async def _call_roundtable(self, task: str) -> dict: return {"executor": "roundtable", "error": str(exc)} async def _call_cline(self, task: str) -> dict: - """Invoke Cline CLI.""" - import asyncio - import os - import subprocess - - cline_bin = None - for c in ["cline", "npx @cline/cli"]: - try: - subprocess.run(["which", c.split()[0]], capture_output=True, text=True, timeout=2, check=True) - cline_bin = c - break - except Exception: - continue - - if not cline_bin: - return {"executor": "cline", "error": "Cline CLI not found"} - - try: - cmd = cline_bin.split() + ["--task", task[:500]] - proc = await asyncio.create_subprocess_exec( - *cmd, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - env={**os.environ}, - ) - stdout, stderr = await asyncio.wait_for( - proc.communicate(), timeout=120, - ) - output = stdout.decode("utf-8", errors="replace") if stdout else "" - return { - "executor": "cline", - "exit_code": proc.returncode, - "output": output[:2000], - } - except asyncio.TimeoutError: - return {"executor": "cline", "error": "Timeout after 120s"} - except Exception as exc: - return {"executor": "cline", "error": str(exc)} + """Reject direct Cline execution; use the governed adapter.""" + return { + "executor": "cline", + "success": False, + "error": "Direct Cline execution is disabled by policy", + "required_path": "cline-invoke plan adapter, then reviewed worktree harness", + } async def _call_route_task(self, task: str) -> dict: """Fallback: route through route_task skill.""" diff --git a/backend/app/skills/registry.py b/backend/app/skills/registry.py index d992682b..5dba60ab 100644 --- a/backend/app/skills/registry.py +++ b/backend/app/skills/registry.py @@ -65,9 +65,25 @@ def _get_category_priority(category: str) -> int: def get_skill(skill_id: str) -> BaseSkill | None: _ensure(); return _registry.get(skill_id) -async def run_skill_by_id(skill_id: str, **kwargs) -> dict: +async def run_skill_by_id( + skill_id: str, + *, + execution_authorized: bool = False, + **kwargs, +) -> dict: skill = get_skill(skill_id) if not skill: return {"success": False, "error": f"Skill '{skill_id}' not found"} + requires_confirmation = ( + getattr(skill.meta, "requires_confirmation", False) + or skill.meta.category in ("mutating", "destructive", "write") + ) + if requires_confirmation and not execution_authorized: + return { + "success": False, + "error": "Skill requires explicit execution authorization", + "skill_id": skill_id, + "requires_confirmation": True, + } errors = skill.validate(**kwargs) if errors: return {"success": False, "errors": errors} return await skill.run(**kwargs) diff --git a/backend/tests/test_skill_cline_policy.py b/backend/tests/test_skill_cline_policy.py new file mode 100644 index 00000000..660da4a5 --- /dev/null +++ b/backend/tests/test_skill_cline_policy.py @@ -0,0 +1,32 @@ +from app.skills.builtin.skill_cline_invoke import ClineInvokeSkill + + +async def test_cline_is_disabled_by_default(monkeypatch): + monkeypatch.delenv("UCORE_ENABLE_CLINE", raising=False) + + result = await ClineInvokeSkill().run(task="Review this repository", mode="plan") + + assert result["success"] is False + assert result["allowed_mode"] == "plan" + + +async def test_cline_rejects_yolo_even_when_enabled(monkeypatch): + monkeypatch.setenv("UCORE_ENABLE_CLINE", "true") + + result = await ClineInvokeSkill().run(task="Change everything", mode="yolo") + + assert result["success"] is False + assert "yolo" in result["error"] + + +async def test_cline_rejects_auto_approval_even_when_enabled(monkeypatch): + monkeypatch.setenv("UCORE_ENABLE_CLINE", "true") + + result = await ClineInvokeSkill().run( + task="Review this repository", + mode="plan", + auto_approve="true", + ) + + assert result["success"] is False + assert "auto-approval" in result["error"] diff --git a/backend/tests/test_skill_registry_authorization.py b/backend/tests/test_skill_registry_authorization.py new file mode 100644 index 00000000..a667fe05 --- /dev/null +++ b/backend/tests/test_skill_registry_authorization.py @@ -0,0 +1,33 @@ +from app.skills.base import BaseSkill, SkillMeta +from app.skills import registry + + +class _DestructiveSkill(BaseSkill): + meta = SkillMeta( + id="test-destructive", + name="Test destructive", + category="destructive", + ) + + async def run(self, **kwargs) -> dict: + return {"success": True} + + +async def test_core_registry_blocks_unauthorized_destructive_execution(monkeypatch): + monkeypatch.setattr(registry, "get_skill", lambda _skill_id: _DestructiveSkill()) + + result = await registry.run_skill_by_id("test-destructive") + + assert result["success"] is False + assert result["requires_confirmation"] is True + + +async def test_core_registry_runs_destructive_skill_after_authorization(monkeypatch): + monkeypatch.setattr(registry, "get_skill", lambda _skill_id: _DestructiveSkill()) + + result = await registry.run_skill_by_id( + "test-destructive", + execution_authorized=True, + ) + + assert result == {"success": True} From dc38a3b3dbce071aa3aed1f893c9c40310003e6c Mon Sep 17 00:00:00 2001 From: Fred Porter Date: Tue, 18 Aug 2026 22:06:04 +0800 Subject: [PATCH 09/28] docs(agents): define intention-driven execution architecture --- docs/AGENT_EXECUTION_ARCHITECTURE.md | 94 ++++++++++++++++++++++++++++ docs/README.md | 2 + docs/SKILLS_AUDIT_2026-08-18.md | 94 ++++++++++++++++++++++++++++ 3 files changed, 190 insertions(+) create mode 100644 docs/AGENT_EXECUTION_ARCHITECTURE.md create mode 100644 docs/SKILLS_AUDIT_2026-08-18.md diff --git a/docs/AGENT_EXECUTION_ARCHITECTURE.md b/docs/AGENT_EXECUTION_ARCHITECTURE.md new file mode 100644 index 00000000..7547edc9 --- /dev/null +++ b/docs/AGENT_EXECUTION_ARCHITECTURE.md @@ -0,0 +1,94 @@ +# Agent Execution Architecture + +**Status:** Canonical direction + +**Updated:** 2026-08-18 + +## Product principle + +Users choose an intention, not an agent, provider, or model. uDOS converts that +intention into a governed task envelope and selects the least expensive capable +execution path. Provider selection remains observable in diagnostics and +advanced settings, but is not normal user interaction. + +## Authorities + +| Component | Authority | +| --- | --- | +| uFlow | Durable missions, tasks, workflow state, approval state and resumability | +| HiveMind | Decomposition, capability routing, retries, escalation and evidence collection | +| Roundtable | Optional multi-model deliberation and review strategy | +| Provider router | Capability-to-provider/model resolution and health-aware fallback | +| Budget service | Per-task, daily and monthly admission control and cost ledger | +| uCode BASIC | User coding and structured document computation | +| Codex | External ecosystem and add-on development environment | +| GitHub/Copilot | Source collaboration, Actions, issues, pull requests and optional review | + +HiveMind is not a model and Roundtable is not a general executor. Neither owns +tasks. They operate on uFlow task envelopes and return structured evidence. + +## Task envelope + +Every model or agent request must carry: + +- intention and success criteria; +- lane: user, workflow, developer or system; +- referenced vault, repository and file context rather than unrestricted roots; +- privacy classification and cloud permission; +- required capabilities; +- allowed tools and write targets; +- risk class and approval requirements; +- maximum cost, attempts and wall-clock time; +- provenance identifiers for prompts, models, tools and outputs. + +## Routing ladder + +1. Deterministic implementation: parsing, indexing, validation, transforms, + policy, budgets and workflow state. +2. Ollama: private/local drafting, classification, summaries, Markdown and BASIC + assistance. +3. OpenRouter free or low-cost models: larger or specialist user work and + optional diverse review. +4. OpenAI API efficient tier: reliable escalation where free/local quality is + insufficient. +5. Roundtable: ambiguity, disagreement, high-value review or failed single-agent + attempts only. +6. Frontier API model or external Codex: difficult, high-value developer work. + +Failure moves upward only when the next tier is permitted by privacy and budget +policy. Budget exhaustion moves downward or pauses; it never silently selects a +more expensive provider. + +## Surface contract + +- Intelligence owns user-visible planning, agent history, provider diagnostics + and budget decisions. +- Workflow owns uFlow missions, tasks, approvals and resumability. +- Snackbar owns process health, logs, provider availability and runtime alerts. +- Developer remains Code / Repository / Editor. It supplies repository, file, + selection and diff context to actions such as Plan, Review and Implement; it + does not regain dedicated agent, model or Kanban tabs. + +## Developer executors + +Codex is the primary environment for real ecosystem development. GitHub/Copilot +may add repository-native issue, pull-request, Actions and review assistance. + +Cline is retained as an optional contained planner. It is disabled by default, +cannot use yolo/auto-approval, cannot be launched directly by Dev Mode and may +operate only in an active Git repository under `UDOS_ROOT`. Act mode requires a +future harness with an isolated worktree, file/path allow-list, budget gate, +command policy, diff review, tests, rollback and explicit merge/push approval. + +Cline Kanban is not installed. Durable task presentation belongs to uFlow and +uCore; duplicating it would reintroduce task and status drift. + +## Non-negotiable controls + +- Model calls pass through the provider router and budget service. +- Mutating execution requires authorization at the core registry, not only UI. +- Vault content cannot reach cloud providers unless its privacy policy permits. +- No executor receives unrestricted credential or home-directory access. +- No agent may merge, push, publish, delete or spend above its envelope without + the required approval. +- Every result records provider/model, cost, attempts, evidence and mutations. diff --git a/docs/README.md b/docs/README.md index 09ff53a7..f9454e17 100644 --- a/docs/README.md +++ b/docs/README.md @@ -44,6 +44,8 @@ | [UDOS_HOME_MIGRATION.md](UDOS_HOME_MIGRATION.md) | Runtime/vault boundary and safe migration | | [ECOSYSTEM_STORAGE_ARCHITECTURE.md](ECOSYSTEM_STORAGE_ARCHITECTURE.md) | Canonical storage, credentials and drift controls | | [WORKSTATION_MIGRATION_2026-08-18.md](WORKSTATION_MIGRATION_2026-08-18.md) | Migration record, verification and rollback boundary | +| [AGENT_EXECUTION_ARCHITECTURE.md](AGENT_EXECUTION_ARCHITECTURE.md) | Intention routing, providers, orchestration and controls | +| [SKILLS_AUDIT_2026-08-18.md](SKILLS_AUDIT_2026-08-18.md) | Skill disposition and remediation sequence | ## Active Developer / Dev Mode Specs diff --git a/docs/SKILLS_AUDIT_2026-08-18.md b/docs/SKILLS_AUDIT_2026-08-18.md new file mode 100644 index 00000000..fdadf0bc --- /dev/null +++ b/docs/SKILLS_AUDIT_2026-08-18.md @@ -0,0 +1,94 @@ +# Skills Audit — 2026-08-18 + +**Status:** Active remediation + +**Observed registry:** 53 executable Skills, including one user example + +**Dedicated Skill test files before remediation:** 8 + +## Findings + +The current term “Skill” covers unrelated concepts: + +- backend executable capabilities; +- destructive recovery/admin operations; +- orchestration and provider adapters; +- workflow and legacy Tasker adapters; +- a frontend Vue component library at `frontend-vue/src/skills`; +- menu Snacks and uCode Snack runtimes. + +The backend registry dynamically imports every Python file and previously +enforced confirmation only in the HTTP API. Internal scheduler and executable +registry calls could bypass it. Core authorization is now enforced in +`run_skill_by_id` with regression tests. + +Provider and executor selection is duplicated across `route_task`, Dev Mode, +HiveMind, Roundtable and Cline. Cline integration also contained obsolete CLI +flags, direct key discovery and auto-approval behavior; it is now contained and +disabled by default. + +## Disposition + +### Keep and harden + +- Vault/user capability: `ask_vault`, `attach_context`, `vault_discovery`, + `vault_sync`. +- Safe maintenance: `backup`, `clipboard_maintenance`, `docs_mirror_sync`, + `git_maintenance`, `lint_fix`. +- Workflow controls: `workflow_audit`, `workflow_guard`, `workflow_pause`. +- Context/provenance: `episodic_log` after privacy and retention review. + +Each retained capability needs an owner, risk class, input/output schema, +allowed roots, deterministic dry run where relevant and dedicated tests. + +### Repair behind canonical contracts + +- `route_task`: become intention/capability classification only; remove direct + provider names from user inputs and duplicated execution logic. +- `hivemind-consensus`: become HiveMind orchestration client with budget, + privacy, attempts and evidence fields. +- `roundtable-dispatch`: become a selective deliberation strategy invoked by + HiveMind, not a default provider. +- `cline-invoke`: retain disabled, plan-only adapter until worktree harness. +- `gh-workflow-bridge`: narrow to GitHub issues, Actions, PR/review and Codex + handoff with explicit external-write approval. +- `brain_sync`: separate deterministic indexing from model synthesis. +- `skill-audit` and `ecosystem-audit`: replace source-text heuristics with + manifest/schema validation and executable tests. + +### Merge or split + +- Merge `enhancement-planner`, `modularisation-planner`, `duplicate-detector`, + `dead-code-archiver` and `hardcoded-path-detector` behind one code-analysis + capability with separate read-only checks and reviewed mutations. +- Split the nearly 2,000-line `skill_nuggets_and_spool.py` into an archive + service, recovery service and small capability adapters. +- Consolidate `diagnose_system`, `recover_port_conflict`, `cleanup_resources`, + `restart_backend` and MCP self-heal behind one governed recovery service. +- Move `tasker_sync` and `devlog_mcp` behind the uFlow compatibility adapter; + they must not maintain a second task engine. +- Rename the frontend Vue `skills` directory to a UI component/system term so + it cannot be mistaken for executable agent capabilities. + +### Remove from general Skill execution + +- `reset_database`, `spool_destroy` and `dev-destroy-rebuild`: privileged + recovery workflows, never ordinary agent-selected Skills. +- `autostart`: lifecycle/service management, owned by Snackbar/System. +- `surface-registry` and `usx-standard`: build/registry tooling rather than + user-selectable agent Skills. +- `hello-world`: example fixture only; do not dynamically load in production. +- Cline Kanban and all Cline/VS Code surface assumptions. + +## Remediation order + +1. Core authorization and Cline containment — completed. +2. Add lifecycle, risk, owner, lane, capabilities and allowed-root metadata to + the Skill contract. +3. Default-deny unclassified and example Skills in production discovery. +4. Separate privileged recovery operations from general execution. +5. Implement the intention task envelope and one provider/budget route. +6. Rewire HiveMind, Roundtable, GitHub and Cline as bounded adapters. +7. Split/merge the oversized and duplicated capabilities. +8. Add catalogue validation, dedicated tests and CI coverage for every enabled + capability. From 5304feeabd872faee6005d0926a2282bb42a55ab Mon Sep 17 00:00:00 2001 From: Fred Porter Date: Tue, 18 Aug 2026 22:16:24 +0800 Subject: [PATCH 10/28] fix(ui): reconcile canonical surface wiring --- docs/UI_SURFACE_WIRING_AUDIT_2026-08-18.md | 78 +++++++++++++++++++ frontend-vue/src/router/index.ts | 13 +++- .../documentation/DocumentationSurface.vue | 23 +++++- .../intelligence/IntelligenceSurface.vue | 35 ++++++--- .../src/surfaces/ucode/UCodeSurface.vue | 18 ++++- package.json | 2 +- 6 files changed, 153 insertions(+), 16 deletions(-) create mode 100644 docs/UI_SURFACE_WIRING_AUDIT_2026-08-18.md diff --git a/docs/UI_SURFACE_WIRING_AUDIT_2026-08-18.md b/docs/UI_SURFACE_WIRING_AUDIT_2026-08-18.md new file mode 100644 index 00000000..025e9f10 --- /dev/null +++ b/docs/UI_SURFACE_WIRING_AUDIT_2026-08-18.md @@ -0,0 +1,78 @@ +# Vue Surface Wiring Audit — 2026-08-18 + +## Navigation rule + +uCore navigation follows user intentions, not implementation inventory. A capability +gets a top-level surface only when it represents a durable user activity. Tools, +providers, agents, and runtime details remain contextual actions or operational +views inside the owning surface. + +## Canonical surface set + +| Surface | User intention | Current tabs | Decision | +| --- | --- | --- | --- | +| Dashboard | Start and resume work | Dashboard, Workflow, Intelligence, Snackbar, System | Keep as the small primary switchboard. Optional extensions remain cards, not permanent tabs. | +| Workflow | Plan, track, edit, automate, and publish work | Workflow, Tasks, Automation, Editor, Publish | Keep. Editor is a contextual full-workspace state reached from a task or document, even though it is represented as a routable tab. uFlow is the authority. | +| Intelligence | Ask, plan, inspect cost, and review history | Chat, Settings, Models, Budget, History | Keep provisionally. Agent selection was removed: HiveMind/provider routing chooses execution. Models remain visible while automatic routing and policy controls mature. | +| uCode | Use the compact user runtime | Terminal, Teletext, Pixel, Grid, Layer, Glyphs | Keep as work modes within one runtime surface. They are not ecosystem navigation destinations. | +| Snackbar | Observe and administer runtime capabilities | Dashboard, Services, Agents, Feeds, Skills, Snacks, Extensions, Logs, MCP | Keep for the current operations pass, but treat this as an operator surface. Next simplification should group inventory views under Dashboard without creating more tabs. | +| System | Configure identity, variables, secrets, and recovery | Pages, Variables, Secrets, Global, User | Keep. Runtime diagnostics stay in Snackbar. | +| Documentation | Read guides, knowledge, learning, and publishing output | Guide & Docs, Knowledge, Learning, Publishing | Keep for now. Reconcile Publishing with Workflow Publish after their backend contracts are compared; do not add another publishing surface. | +| Developer | Inspect repositories and edit real code | Code, Repository, Editor | Keep. Repository and Editor are contextual states selected from Code, not a replacement for uCode user tasks. | + +## Contextual and compatibility surfaces + +- BrowserUI is hidden and should be invoked by research/workflow actions. Its sample + data and standalone tab model are not ready for primary navigation. +- Groovebox is a project/add-on built on top of the core. Its route may remain + stable, but it appears only as a project card when available. +- SonicScrewdriver is a standalone GridCore-based device toolkit: a device library, + reflashing/build tooling, and a path to create USB-hosted uDos runtimes for older + Linux-first machines. It is not a uCore extension or permanent core tab. +- `/assistui`, `/server`, `/snackmachine`, `/gridui`, `/userver`, `/teletext`, and + `/terminal` are compatibility routes and must resolve into a canonical surface. +- The retired uDev name may remain only as saved extension compatibility data. + +## Wiring contract + +Every visible tab must have all of the following before it is described as wired: + +1. A canonical surface owner and a valid `?tab=` deep link. +2. A rendered Vue panel with loading, empty, success, and error behaviour appropriate + to its contract. +3. A registered backend API or an explicitly local-only state contract. +4. No dependency on retired uDev, VS Code, Cline autonomy, or direct provider choice. +5. A build/test gate that fails when its imported contract drifts. + +This pass repaired query-tab synchronization for Intelligence, Documentation, and +uCode. It also made the historical Intelligence Agents link resolve to Snackbar +Agents rather than presenting agent choice to the user. + +## Reconciliation queue + +1. Replace the Intelligence workflow placeholder with a contextual link/summary + from uFlow, rather than duplicating Workflow controls. +2. Compare Documentation Publishing and Workflow Publish contracts, then merge the + user journey into Workflow if they perform the same lifecycle. +3. Consolidate Snackbar's inventory-only tabs into dashboard sections or contextual + detail views; retain direct compatibility links during the transition. +4. Replace BrowserUI sample stacks with vault-backed research state before exposing + it through user workflows. +5. After the core repositories are stable on `main`, revisit or rebuild + SonicScrewdriver as the first standalone GridCore proving project. + +## Release sequence + +1. Stabilize, reconcile, and merge uCore, uCode, uKnowledge, and uFlow. +2. Prove that repository work, task state, budgets, and internal tools can be managed + clearly through uCore's Developer and Workflow surfaces. +3. Revisit SonicScrewdriver using the settled uCode/GridCore contracts. +4. Complete Docs Libraries, the Global Knowledge bank, and the Learning Pathway as + the new-user onboarding layer. +5. Expand into downstream projects and add-ons such as Groovebox. + +SonicScrewdriver is also the first **pull product** for the ecosystem: a concrete, +easy-to-explain reason to discover uDos before a new user understands that they want +the wider platform. Its device revival and portable-runtime journey should lead into +uCode, GridCore, vault workflows, documentation, and learning without making Sonic +itself part of uCore. diff --git a/frontend-vue/src/router/index.ts b/frontend-vue/src/router/index.ts index f5d47a92..330287f9 100644 --- a/frontend-vue/src/router/index.ts +++ b/frontend-vue/src/router/index.ts @@ -21,7 +21,11 @@ const routes: RouteRecordRaw[] = [ }, { path: "/assistui/:pathMatch(.*)*", - redirect: "/intelligence", + redirect: (to) => { + const tab = String(to.query.tab || "chat"); + if (tab === "agents") return "/snackbar?tab=agents"; + return { path: "/intelligence", query: to.query }; + }, }, { path: "/intelligence/:pathMatch(.*)*", @@ -140,6 +144,13 @@ export const router = createRouter({ routes, }); +router.beforeEach((to) => { + // Agent selection is operational state, not a user-facing Intelligence mode. + if (to.path.startsWith("/intelligence") && to.query.tab === "agents") { + return { path: "/snackbar", query: { tab: "agents" } }; + } +}); + const DYNAMIC_IMPORT_RELOAD_KEY = "ucore.router.dynamic-import-reload"; const RUNTIME_WARNING_KEY = "ucore.runtime.warning"; diff --git a/frontend-vue/src/surfaces/documentation/DocumentationSurface.vue b/frontend-vue/src/surfaces/documentation/DocumentationSurface.vue index 4413d80a..2f81b4cb 100644 --- a/frontend-vue/src/surfaces/documentation/DocumentationSurface.vue +++ b/frontend-vue/src/surfaces/documentation/DocumentationSurface.vue @@ -312,7 +312,8 @@ diff --git a/frontend-vue/src/tasks/bangle-upgrade.tasks.ts b/frontend-vue/src/tasks/bangle-upgrade.tasks.ts index 2ad8386e..ea391c3e 100644 --- a/frontend-vue/src/tasks/bangle-upgrade.tasks.ts +++ b/frontend-vue/src/tasks/bangle-upgrade.tasks.ts @@ -900,7 +900,7 @@ export const BANGLE_UPGRADE_TASKS: SprintTask[] = [ description: "Replace raw JSON pre block with rendered HTML; add raw/rendered toggle", phase: 7, - component: "uDev/panels/SkillsPanel.vue", + component: "surfaces/snackbar/panels/SnackbarSkillsPanel.vue", priority: "high", status: "done", estimatedHours: 1.5, @@ -1065,7 +1065,7 @@ export const BANGLE_UPGRADE_TASKS: SprintTask[] = [ "Create OverlayLayer.vue wrapper", "Include: ChatBubble, ToastOverlay, AlertOverlay, PopupOverlay, StoriesOverlay", "Mount in uCore App.vue", - "Mount in uDev App.vue", + "Mount in the uCore UI Hub", "z-index stack: toast 1100, alert 1200, popup 1300, stories 1400", ], }, @@ -1241,7 +1241,7 @@ export const BANGLE_UPGRADE_TASKS: SprintTask[] = [ tags: ["sprint-2", "chat"], checklist: [ "uCore OverlayLayer wired", - "uDev OverlayLayer wired", + "uCore OverlayLayer wired", "sendChatMessage() functional", ], }, diff --git a/plates/destroy/skill_recover_port_conflict.yaml b/plates/destroy/skill_recover_port_conflict.yaml index d3e09193..bd58379a 100644 --- a/plates/destroy/skill_recover_port_conflict.yaml +++ b/plates/destroy/skill_recover_port_conflict.yaml @@ -27,9 +27,8 @@ plate: backup_before_destroy: true spool_archive: enabled: true - spool_dir: "~/.ucore/logs" + spool_dir: "${UDOS_HOME}/logs" compress_metadata: true include_source: false include_lessons: true max_spool_age_days: 365 - diff --git a/plates/vault/global_knowledge_seed.yaml b/plates/vault/global_knowledge_seed.yaml index 346b67f9..f747e66e 100644 --- a/plates/vault/global_knowledge_seed.yaml +++ b/plates/vault/global_knowledge_seed.yaml @@ -68,7 +68,7 @@ plate: backup_before_destroy: true spool_archive: enabled: true - spool_dir: "~/.ucore/logs" + spool_dir: "${UDOS_HOME}/logs" compress_metadata: true include_source: false include_lessons: true diff --git a/plates/vault/public_publishing_framework.yaml b/plates/vault/public_publishing_framework.yaml index 8c328543..47df73eb 100644 --- a/plates/vault/public_publishing_framework.yaml +++ b/plates/vault/public_publishing_framework.yaml @@ -30,7 +30,7 @@ plate: backup_before_destroy: true spool_archive: enabled: true - spool_dir: "~/.ucore/logs" + spool_dir: "${UDOS_HOME}/logs" compress_metadata: true include_source: false include_lessons: true diff --git a/plates/vault/shared_workspace_seed.yaml b/plates/vault/shared_workspace_seed.yaml index 6ddc37fb..3d9deec6 100644 --- a/plates/vault/shared_workspace_seed.yaml +++ b/plates/vault/shared_workspace_seed.yaml @@ -32,7 +32,7 @@ plate: backup_before_destroy: true spool_archive: enabled: true - spool_dir: "~/.ucore/logs" + spool_dir: "${UDOS_HOME}/logs" compress_metadata: true include_source: false include_lessons: true diff --git a/plates/vault/transport_pipeline.yaml b/plates/vault/transport_pipeline.yaml index 7e596ff0..184e3075 100644 --- a/plates/vault/transport_pipeline.yaml +++ b/plates/vault/transport_pipeline.yaml @@ -38,7 +38,7 @@ plate: backup_before_destroy: true spool_archive: enabled: true - spool_dir: "~/.ucore/logs" + spool_dir: "${UDOS_HOME}/logs" compress_metadata: true include_source: false include_lessons: true diff --git a/plates/vault/user_vault_seed.yaml b/plates/vault/user_vault_seed.yaml index b18ddc3a..93773a67 100644 --- a/plates/vault/user_vault_seed.yaml +++ b/plates/vault/user_vault_seed.yaml @@ -89,7 +89,7 @@ plate: backup_before_destroy: true spool_archive: enabled: true - spool_dir: "~/.ucore/logs" + spool_dir: "${UDOS_HOME}/logs" compress_metadata: true include_source: false include_lessons: true diff --git a/scripts/install.sh b/scripts/install.sh index 6df31a8d..401db603 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -15,6 +15,7 @@ set -euo pipefail # ─── Config ────────────────────────────────────────────────────────── UCORE_REPO="https://github.com/uDosGo/uCore.git" UCORE_DIR="${UCORE_DIR:-$HOME/Code/uCore}" +UDOS_HOME="${UDOS_HOME:-$HOME/Code/.udos}" BRANCH="main" # ─── Colors ────────────────────────────────────────────────────────── @@ -190,7 +191,7 @@ else echo "╚══════════════════════════════════════════╝" echo "" echo " Some services may still be starting." - echo " Check logs: ~/.ucore/logs/" + echo " Check logs: $UDOS_HOME/logs/" echo "" echo " To retry: $UCORE_DIR/scripts/setup.sh" fi diff --git a/scripts/install_appflowy_import_launchd.sh b/scripts/install_appflowy_import_launchd.sh deleted file mode 100755 index fb3b8166..00000000 --- a/scripts/install_appflowy_import_launchd.sh +++ /dev/null @@ -1,97 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -LABEL="com.udos.ucore.appflowy-import" -INTERVAL_SECONDS=1800 -CONFIG_PATH="$HOME/.ucore/sync_config.yaml" -LOG_PATH="$HOME/.ucore/logs/appflowy-import.log" -PLIST_PATH="$HOME/Library/LaunchAgents/${LABEL}.plist" -UCORE_DIR="$(cd "$(dirname "$0")/.." && pwd)" - -usage() { - cat </dev/null 2>&1 || true - rm -f "$PLIST_PATH" - echo "Removed launchd job: $LABEL" - exit 0 -fi - -# AppFlowy importer is intentionally deprecated. -launchctl unload "$PLIST_PATH" >/dev/null 2>&1 || true -rm -f "$PLIST_PATH" -echo "AppFlowy importer is deprecated and has been disabled." -echo "Use Tasker endpoints for tasks and Bangle vault workflow for docs." -exit 0 - -mkdir -p "$(dirname "$PLIST_PATH")" -mkdir -p "$(dirname "$LOG_PATH")" - -cat > "$PLIST_PATH" < - - - - Label - ${LABEL} - ProgramArguments - - /bin/zsh - -lc - cd ${UCORE_DIR} && python3 scripts/appflowy_import_workspaces.py --config ${CONFIG_PATH} >> ${LOG_PATH} 2>&1 - - RunAtLoad - - StartInterval - ${INTERVAL_SECONDS} - StandardOutPath - ${LOG_PATH} - StandardErrorPath - ${LOG_PATH} - - -EOF - -launchctl unload "$PLIST_PATH" >/dev/null 2>&1 || true -launchctl load "$PLIST_PATH" - -echo "Installed launchd job: $LABEL" -echo "plist: $PLIST_PATH" -echo "config: $CONFIG_PATH" -echo "interval_seconds: $INTERVAL_SECONDS" -echo "log: $LOG_PATH" diff --git a/scripts/setup_autonomy_cron.sh b/scripts/setup_autonomy_cron.sh deleted file mode 100644 index e4476b2c..00000000 --- a/scripts/setup_autonomy_cron.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/bash -# Setup overnight autonomy engine as a launchd job -# Runs every 6 hours to ensure 4x daily health checks -set -e - -PLIST="$HOME/Library/LaunchAgents/com.udos.ucore-autonomy.plist" -REPO="$HOME/Code/uCore" - -cat > "$PLIST" < - - - - Label - com.udos.ucore-autonomy - ProgramArguments - - /usr/bin/python3 - ${REPO}/backend/health/autonomy_engine.py - --once - - WorkingDirectory - ${REPO} - StartInterval - 21600 - StandardOutPath - ${HOME}/.ucore/logs/autonomy_launchd.log - StandardErrorPath - ${HOME}/.ucore/logs/autonomy_launchd_err.log - RunAtLoad - - - -PLIST - -launchctl unload "$PLIST" 2>/dev/null || true -launchctl load "$PLIST" - -echo "Autonomy engine installed — runs every 6 hours" -echo "State file: ~/.ucore/logs/autonomy_state.json" -echo "API endpoint: GET /api/autonomy/state" \ No newline at end of file diff --git a/scripts/start_optimized_workflow.sh b/scripts/start_optimized_workflow.sh deleted file mode 100755 index b7eff61c..00000000 --- a/scripts/start_optimized_workflow.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/bin/bash - -# Optimized Workflow Quick Start Script -# This script helps you get started with the new TOON and Flow-LLM Router features - -set -e - -echo "=== uCore Optimized Workflow Quick Start ===" -echo - -echo "1. Starting uCore backend..." -cd "${UCORE_ROOT:-$HOME/Code/uCore}/backend" -python3 -m app & -PID=$! -echo " Backend started with PID $PID" -echo - -echo "2. Waiting for backend to start..." -sleep 3 - -echo "3. Verifying TOON Context Optimization..." -curl -s http://localhost:8484/api/toon/stats | jq -r '.status' -echo " TOON server is ready" -echo - -echo "4. Verifying Flow-LLM Router..." -curl -s -X POST http://localhost:8484/api/flow-router/route -H 'Content-Type: application/json' -d '{"task":"Test routing"}' | jq -r '.status' -echo " Flow-LLM Router is ready" -echo - -echo "5. Starting frontend..." -cd "${UCORE_ROOT:-$HOME/Code/uCore}/frontend" -npm run dev & -FRONTEND_PID=$! -echo " Frontend started with PID $FRONTEND_PID" -echo - -echo "6. Opening Cline Kanban UI..." -open http://localhost:5173 - -echo - -echo "=== Quick Start Complete! ===" -echo "- Backend: http://localhost:8484" -echo "- Frontend: http://localhost:5173" -echo "- Cline Kanban UI: http://localhost:5173" -echo "- TOON API: http://localhost:8484/api/toon/encode" -echo "- Flow-LLM Router API: http://localhost:8484/api/flow-router/route" -echo - -echo "To stop the services, run:" -echo " kill $PID $FRONTEND_PID" -echo - -echo "For more information, see:" -echo " docs/OPTIMIZED_WORKFLOW.md" -echo " IMPLEMENTATION_SUMMARY.md" -echo " OPTIMIZED_WORKFLOW_REPORT.md" -echo - -echo "Enjoy the optimized workflow!" diff --git a/scripts/validate_mcp_config.py b/scripts/validate_mcp_config.py deleted file mode 100644 index 4ade59f6..00000000 --- a/scripts/validate_mcp_config.py +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/env python3 -"""Validate canonical MCP workspace configuration. - -Fails when active config drifts from the required stdio bridge model. -""" - -from __future__ import annotations - -import json -import sys -from pathlib import Path - - -def fail(message: str) -> None: - print(f"[FAIL] {message}") - raise SystemExit(1) - - -def main() -> None: - repo_root = Path(__file__).resolve().parents[1] - mcp_path = repo_root / ".vscode" / "mcp.json" - bridge_path = repo_root.parent / "uDev" / "mcp-bridge" / "build" / "index.js" - - if not mcp_path.exists(): - fail(f"Missing MCP config: {mcp_path}") - - try: - data = json.loads(mcp_path.read_text()) - except Exception as exc: - fail(f"Invalid JSON in {mcp_path}: {exc}") - - if not isinstance(data, dict): - fail("MCP config root must be an object") - - servers = data.get("servers") - if not isinstance(servers, dict): - fail("MCP config must contain object key: servers") - - if "ucore-bridge" not in servers: - fail("Missing required MCP server: ucore-bridge") - - bridge = servers["ucore-bridge"] - if not isinstance(bridge, dict): - fail("ucore-bridge config must be an object") - - disallowed_http = [ - name - for name, cfg in servers.items() - if isinstance(cfg, dict) and cfg.get("type") == "http" - ] - if disallowed_http: - fail(f"HTTP MCP servers are not allowed: {', '.join(disallowed_http)}") - - if bridge.get("type") != "stdio": - fail("ucore-bridge type must be stdio") - - if bridge.get("command") != "node": - fail("ucore-bridge command must be node") - - args = bridge.get("args") - if args != ["../uDev/mcp-bridge/build/index.js"]: - fail("ucore-bridge args must be exactly [\"../uDev/mcp-bridge/build/index.js\"]") - - env = bridge.get("env") - if not isinstance(env, dict): - fail("ucore-bridge env must be an object") - - if env.get("UCORE_URL") != "http://localhost:8484" and env.get("UCORE_URL") != "http://127.0.0.1:8484": - fail("ucore-bridge env.UCORE_URL must be localhost:8484 or 127.0.0.1:8484") - - if not bridge_path.exists(): - print(f"[WARN] Bridge binary missing at {bridge_path} (run npm run build in uDev/mcp-bridge)") - - print("[OK] MCP config validated") - - -if __name__ == "__main__": - main() From afc7e1bdd6e5d4d40119c79b5e083e208559d0bf Mon Sep 17 00:00:00 2001 From: Fred Porter Date: Tue, 18 Aug 2026 23:36:43 +0800 Subject: [PATCH 18/28] docs(stabilization): add cross-repository merge ledger --- ...E_STABILIZATION_MERGE_LEDGER_2026-08-18.md | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 docs/CORE_STABILIZATION_MERGE_LEDGER_2026-08-18.md diff --git a/docs/CORE_STABILIZATION_MERGE_LEDGER_2026-08-18.md b/docs/CORE_STABILIZATION_MERGE_LEDGER_2026-08-18.md new file mode 100644 index 00000000..23992d7c --- /dev/null +++ b/docs/CORE_STABILIZATION_MERGE_LEDGER_2026-08-18.md @@ -0,0 +1,86 @@ +# Core Stabilization Merge Ledger — 2026-08-18 + +**Status:** Ready for review; merging requires explicit maintainer approval. + +## Outcome + +This wave establishes one ownership and storage path across the four core +repositories: + +- uFlow owns durable missions, tasks, workflow definitions, runs and approvals. +- uKnowledge owns filesystem-first workspace registration, safe Markdown reads, + offline search and the read-only Public-vault boundary. +- uCode owns the BASIC/GridCore runtime and uses the shared ecosystem environment + and `$UDOS_HOME`; it owns no editor, provider, secret or task configuration. +- uCore hosts the user surfaces, delegates to those owners, and keeps Developer + focused on repository/code work without restoring agent/model/Kanban tabs. + +The canonical mutable runtime root is `$UDOS_HOME` (normally +`~/Code/.udos`). User documents remain in `~/Vault`; shared/add-on vaults in +`~/Shared`; public read-only editions in `~/Public`. The shared Python environment +is `~/Code/.venv`. + +## Review branches and checkpoints + +| Repository | Branch | Head checkpoint | +| --- | --- | --- | +| uFlow | `work/2026-08-18-stabilise` | `028df3d` — task substrate moved into uFlow | +| uKnowledge | `work/2026-08-18-stabilise` | `7e52162` — filesystem-first knowledge library | +| uCode | `work/2026-08-18-stabilise` | `65b1706` — shared state/runtime boundary | +| uCore | `work/2026-08-18-stabilise` | `2f3c5f4` — canonical runtime/tooling cleanup | + +## Verification evidence + +| Gate | Result | +| --- | --- | +| uCore backend | 498 passed; 6 existing aiohttp warnings | +| uCore Vue unit tests | 12 passed | +| uCore Vue production build | passed; existing chunk-size warnings only | +| uCore changed-file Ruff gate | passed | +| uCore home-path policy | 4 passed | +| uCore self-hosted MCP bridge | TypeScript build passed; diagnostics healthy | +| uFlow | 4 passed | +| uKnowledge | 10 passed, including Public read-only and traversal controls | +| uCode JavaScript/TypeScript | 188 tests passed across GridCore, viewport and GridSmith | +| uCode package build | all three workspaces built, including declarations | +| uCode BASIC runtime | 169 passed, 48 skipped, 2 warnings | + +The skipped BASIC tests are marked optional/integration tests in the existing +suite; they are not newly skipped by this wave. + +## Merge order + +1. uFlow — establishes workflow/task ownership. +2. uKnowledge — establishes knowledge and vault contracts. +3. uCode — establishes runtime/state boundary and buildable UI dependencies. +4. uCore — consumes all three contracts and supplies the reconciled surfaces. + +After each merge, rerun that repository's gate. After uCore merges, rerun the +complete table above from clean `main` checkouts before tagging or releasing. + +## Review focus + +- Confirm no duplicate task or knowledge store remains active in uCore/uCode. +- Confirm Public knowledge mutation is rejected by capability, not merely hidden + in the UI. +- Confirm Workflow and BrowserUI show live owner-backed state. +- Confirm the Developer surface remains Repo / Code / Editor oriented. +- Confirm Snackbar/System/Intelligence contain operational concerns without new + top-level navigation. +- Confirm no active install/runtime path recreates `~/.ucore`, `~/.udos`, + `.tasker`, `.vscode`, or `.clinerules`. +- Confirm generated files, credentials, vault contents and local runtime state are + absent from every diff. + +## Local-worktree note + +`uCore/backend/tests/test_skill_registry_authorization.py` has a local import-order +change that predates/is unrelated to this wave. It was intentionally excluded from +all stabilization commits and must not be swept into a merge. + +## Rollback + +Each repository is independently reversible to `origin/main`. Do not partially +revert uCore's delegation commits while retaining the owner-repository changes; +either revert the consuming uCore wave first or roll back in reverse merge order. +Runtime/user vault data is not part of these Git changes. From 610ec2f2083ab63e0e8dc040fa39800581bbf137 Mon Sep 17 00:00:00 2001 From: Fred Porter Date: Tue, 18 Aug 2026 23:41:29 +0800 Subject: [PATCH 19/28] ci(core): align clean-checkout verification --- .github/workflows/ci.yml | 19 ++--- backend/pyproject.toml | 9 +- ...E_STABILIZATION_MERGE_LEDGER_2026-08-18.md | 2 +- docs/FEATURE_SPEC_OPENROUTER_ASK_PLAN_ACT.md | 11 ++- docs/UDOS_HOME_MIGRATION.md | 1 + ..._COMPILER_INTEGRATION_CHECKLIST_2026-08.md | 83 ------------------- .../src/surfaces/system/SystemSurface.vue | 2 +- scripts/audit_udos_home.py | 1 + scripts/validate_planning_governance.sh | 16 +--- 9 files changed, 23 insertions(+), 121 deletions(-) delete mode 100644 docs/specs/UDEV_BINDER_COMPILER_INTEGRATION_CHECKLIST_2026-08.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9c7cedce..553c709e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -60,9 +60,6 @@ jobs: - name: Validate split-repo packaging layout run: python3 scripts/validate_split_repo_packaging.py - - name: Validate MCP config lock - run: python3 scripts/validate_mcp_config.py - backend-tests: name: Backend Tests runs-on: ubuntu-latest @@ -89,13 +86,15 @@ jobs: frontend-build: name: Frontend Build runs-on: ubuntu-latest - defaults: - run: - working-directory: frontend steps: - name: Checkout uses: actions/checkout@v4 + - name: Set up pnpm + uses: pnpm/action-setup@v4 + with: + version: 10 + - name: Set up Node uses: actions/setup-node@v4 with: @@ -106,8 +105,8 @@ jobs: - name: Install frontend dependencies run: pnpm install --frozen-lockfile - - name: Validate prose class standard - run: pnpm run check:prose-standard - - name: Build frontend - run: pnpm run build + run: pnpm --dir frontend-vue run build + + - name: Test frontend + run: pnpm --dir frontend-vue run test -- --run diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 09233b97..5b00a028 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -17,10 +17,6 @@ dependencies = [ "aiohttp>=3.9,<4.0", "pydantic>=2.0", "pyyaml>=6.0", - "snackmachine>=1.0", - "udos-budget>=1.0", - "udos-agents>=1.0", - "udos-identity>=1.0", "litellm>=1.0", "langgraph>=1.0", "psutil>=5.9", @@ -35,8 +31,8 @@ dev = [ ] [tool.setuptools.packages.find] -where = ["app"] -include = ["app*", "app.models*", "app.core*", "app.api*", "app.services*", "app.surfaces*"] +where = ["."] +include = ["app*", "snackmachine*"] [tool.pytest.ini_options] testpaths = ["tests"] @@ -51,4 +47,3 @@ ignore = [ "E501", # line too long - handled by formatter "F401", # unused imports - handled by isort ] - diff --git a/docs/CORE_STABILIZATION_MERGE_LEDGER_2026-08-18.md b/docs/CORE_STABILIZATION_MERGE_LEDGER_2026-08-18.md index 23992d7c..882a3597 100644 --- a/docs/CORE_STABILIZATION_MERGE_LEDGER_2026-08-18.md +++ b/docs/CORE_STABILIZATION_MERGE_LEDGER_2026-08-18.md @@ -67,7 +67,7 @@ complete table above from clean `main` checkouts before tagging or releasing. - Confirm the Developer surface remains Repo / Code / Editor oriented. - Confirm Snackbar/System/Intelligence contain operational concerns without new top-level navigation. -- Confirm no active install/runtime path recreates `~/.ucore`, `~/.udos`, +- Confirm no active install/runtime path recreates `~/.ucore`, `~/.udos`, `.tasker`, `.vscode`, or `.clinerules`. - Confirm generated files, credentials, vault contents and local runtime state are absent from every diff. diff --git a/docs/FEATURE_SPEC_OPENROUTER_ASK_PLAN_ACT.md b/docs/FEATURE_SPEC_OPENROUTER_ASK_PLAN_ACT.md index e6707e24..79e43a3e 100644 --- a/docs/FEATURE_SPEC_OPENROUTER_ASK_PLAN_ACT.md +++ b/docs/FEATURE_SPEC_OPENROUTER_ASK_PLAN_ACT.md @@ -113,10 +113,9 @@ Shows free-tier models with cost badges (free/ultra-cheap/budget/mid-range/premi --- -## Remaining Work (Wave 4-5) +## Deferred follow-up (owned by uFlow) -- [ ] Wire `editor_api.py` scrape endpoint as a chat tool -- [ ] Wire save-to-vault as a chat tool with vault layer boundaries -- [ ] Full test suite for new functionality -- [ ] End-to-end smoke test (Plan → Approve → Act → Vault result) -- [ ] Update devlog, fieldnotes, wisdom +The remaining product work is to connect governed browser capture and +vault-bound writes, expand the functionality tests, add the Plan → Approve → Act +end-to-end probe, and record its evidence. These are uFlow tasks rather than an +active checklist embedded in this specification. diff --git a/docs/UDOS_HOME_MIGRATION.md b/docs/UDOS_HOME_MIGRATION.md index db6cd516..62daf1cc 100644 --- a/docs/UDOS_HOME_MIGRATION.md +++ b/docs/UDOS_HOME_MIGRATION.md @@ -1,4 +1,5 @@ # uDOS Runtime Home Migration + **Status:** Approved architecture; migration not yet executed **Updated:** 2026-08-18 diff --git a/docs/specs/UDEV_BINDER_COMPILER_INTEGRATION_CHECKLIST_2026-08.md b/docs/specs/UDEV_BINDER_COMPILER_INTEGRATION_CHECKLIST_2026-08.md deleted file mode 100644 index 01b469df..00000000 --- a/docs/specs/UDEV_BINDER_COMPILER_INTEGRATION_CHECKLIST_2026-08.md +++ /dev/null @@ -1,83 +0,0 @@ -# uDev Binder Compiler Integration Checklist (uCore Alignment) - -Status: Draft for Wave 1 execution -Date: 2026-08-03 -Scope: uDev + uCore parallel alignment - -## Goal - -Ensure uDev can compile deterministic Binder context and consume uCore contracts without duplicating ownership. - -## Contract Baseline - -1. uCore owns runtime APIs, capability readiness, and extension registry. -2. uDev owns Binder authoring/visibility and context compiler UX. -3. Every AI action in uDev must carry inspectable compiled context. - -## Required uCore Endpoints (must remain stable) - -- GET /api/capabilities/readiness -- GET /api/capabilities/{capability}/preflight -- GET /api/control/status -- GET /api/system/workflow -- Chat endpoints used by uDev: - - POST /api/chat/stream - - POST /api/chat - -## Binder Compiler Input Contract (uDev -> uCore coordination) - -Required binder inputs: - -- binder/current.md -- binder/architecture.md -- binder/roadmap.md -- binder/decisions.md -- repository branch/head metadata -- selected lane and active rules -- active issue/task reference (if available) - -Compiler output: - -- binder/context.json validated by binder/context.schema.json -- stable fingerprint hash for identical input sets - -## Integration Tasks - -## A. Capability and Policy Integration - -- [ ] Confirm control panel capabilities remain aligned with capability_requirements.json keys. -- [ ] Ensure readiness/preflight failures always return actionable repair payloads. -- [ ] Verify HTTP 412 semantics are preserved and surfaced to uDev. - -## B. Context Attachment Path - -- [ ] Define server-accepted envelope fields for context payload attachment to chat actions. -- [ ] Validate no hidden server-side prompt state overrides Binder intent. -- [ ] Add explicit logging marker when Binder context is attached successfully. - -## C. Determinism and Auditability - -- [ ] Add validation check that context schema version is supported. -- [ ] Ensure deterministic normalization rules are documented and testable. -- [ ] Add evidence output in logs/telemetry for context fingerprint per request. - -## D. Reliability Gates - -- [ ] CI: validate capability requirements parity (already present). -- [ ] CI: validate docs/runtime parity for capability counts and key contracts. -- [ ] Add lightweight contract probe script for Binder-context chat envelope acceptance. - -## E. Dogfood Runbook (Daily) - -- [ ] Start in uDev binder/current.md. -- [ ] Regenerate/verify binder/context.json. -- [ ] Run one AI task with context attachment. -- [ ] Verify preflight/readiness before capability actions. -- [ ] Record decisions and blockers in Binder markdown. - -## Exit Criteria (Wave 1) - -1. uDev control routes never target invalid tabs. -2. uDev sends validated binder/context.json with default AI actions. -3. uCore returns deterministic preflight/readiness responses for all Wave 1 capabilities. -4. One full MVP loop completes daily without manual context reconstruction. diff --git a/frontend-vue/src/surfaces/system/SystemSurface.vue b/frontend-vue/src/surfaces/system/SystemSurface.vue index 8db8bf4c..b3bb9dca 100644 --- a/frontend-vue/src/surfaces/system/SystemSurface.vue +++ b/frontend-vue/src/surfaces/system/SystemSurface.vue @@ -657,7 +657,7 @@ async function fetchReadiness() { "workflow.run", "knowledge.search", "ucode.grid", - "developer.autonomous", + "developer.guided", "llm.openrouter", "identity_gateway", "wordpress_gateway", diff --git a/scripts/audit_udos_home.py b/scripts/audit_udos_home.py index 458c0dad..8d6b3574 100644 --- a/scripts/audit_udos_home.py +++ b/scripts/audit_udos_home.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# path-policy: allow-literals """Read-only inventory for consolidating uDOS runtime state into UDOS_HOME.""" from __future__ import annotations diff --git a/scripts/validate_planning_governance.sh b/scripts/validate_planning_governance.sh index 6d8b7df1..75ea0be6 100755 --- a/scripts/validate_planning_governance.sh +++ b/scripts/validate_planning_governance.sh @@ -2,10 +2,7 @@ set -euo pipefail # Enforce planning governance: -# - Active planning is allowed only in: -# - .tasker/UNIFIED_DEV_TASK_WORKFLOW.md -# - .tasker/phases/*.md -# - .tasker/backlog/*.md +# - Durable active planning belongs to uFlow, outside this repository. # - Archived planning is allowed under docs/archive/plans/ # - Exception tag for temporary active task notes elsewhere: # - @@ -16,13 +13,6 @@ cd "$ROOT_DIR" allowed_file() { local path="$1" - [[ "$path" == ".tasker/UNIFIED_DEV_TASK_WORKFLOW.md" ]] && return 0 - [[ "$path" == .tasker/phases/* ]] && return 0 - [[ "$path" == .tasker/backlog/* ]] && return 0 - [[ "$path" == .tasker/sprints/* ]] && return 0 - [[ "$path" == .tasker/handover-* ]] && return 0 - [[ "$path" == .tasker/archive/* ]] && return 0 - [[ "$path" == .tasker/archived/* ]] && return 0 [[ "$path" == docs/archive/plans/* ]] && return 0 [[ "$path" == docs/archive/* ]] && return 0 [[ "$path" == docs/archived/* ]] && return 0 @@ -35,7 +25,6 @@ planning_candidate() { local base base="$(basename "$path")" - [[ "$path" == .tasker/* ]] && return 0 [[ "$path" == docs/* ]] || return 1 if [[ "$base" =~ (PLAN|TASK|SPRINT|TODO|CHECKLIST|HANDOVER|ROADMAP|PHASE) ]]; then @@ -48,6 +37,7 @@ planning_candidate() { violations=0 while IFS= read -r file; do + [[ -f "$file" ]] || continue if ! planning_candidate "$file"; then continue fi @@ -89,7 +79,7 @@ rm -f /tmp/ucore_plan_check_1.txt /tmp/ucore_plan_check_2.txt if [[ "$violations" -gt 0 ]]; then echo "---" echo "Planning governance check failed in $violations file(s)." - echo "Active tasks are only allowed in .tasker/UNIFIED_DEV_TASK_WORKFLOW.md, .tasker/phases/, and .tasker/backlog/." + echo "Durable active tasks belong in uFlow; repository Markdown may contain only completed evidence or archived plans." exit 1 fi From fab9b521811ee1371a16014687d273cb57c14137 Mon Sep 17 00:00:00 2001 From: Fred Porter Date: Tue, 18 Aug 2026 23:41:50 +0800 Subject: [PATCH 20/28] docs(stabilization): record final uCore checkpoint --- docs/CORE_STABILIZATION_MERGE_LEDGER_2026-08-18.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/CORE_STABILIZATION_MERGE_LEDGER_2026-08-18.md b/docs/CORE_STABILIZATION_MERGE_LEDGER_2026-08-18.md index 882a3597..015dece5 100644 --- a/docs/CORE_STABILIZATION_MERGE_LEDGER_2026-08-18.md +++ b/docs/CORE_STABILIZATION_MERGE_LEDGER_2026-08-18.md @@ -27,7 +27,7 @@ is `~/Code/.venv`. | uFlow | `work/2026-08-18-stabilise` | `028df3d` — task substrate moved into uFlow | | uKnowledge | `work/2026-08-18-stabilise` | `7e52162` — filesystem-first knowledge library | | uCode | `work/2026-08-18-stabilise` | `65b1706` — shared state/runtime boundary | -| uCore | `work/2026-08-18-stabilise` | `2f3c5f4` — canonical runtime/tooling cleanup | +| uCore | `work/2026-08-18-stabilise` | `610ec2f` — clean-checkout CI aligned with the stabilized architecture | ## Verification evidence From 6dff0639b87f39177affd691b3a01b2c93f2a54e Mon Sep 17 00:00:00 2001 From: Fred Porter Date: Tue, 18 Aug 2026 23:42:58 +0800 Subject: [PATCH 21/28] ci(core): provision governance probes --- .github/workflows/ci.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 553c709e..1f789a33 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,6 +13,16 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install governance probe dependencies + run: python -m pip install aiohttp pyyaml - name: Validate planning governance run: bash scripts/validate_planning_governance.sh From c4f1995187f4b8e90c14e91de93628c3e59b711c Mon Sep 17 00:00:00 2001 From: Fred Porter Date: Tue, 18 Aug 2026 23:43:57 +0800 Subject: [PATCH 22/28] docs(stabilization): refresh review checkpoint --- docs/CORE_STABILIZATION_MERGE_LEDGER_2026-08-18.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/CORE_STABILIZATION_MERGE_LEDGER_2026-08-18.md b/docs/CORE_STABILIZATION_MERGE_LEDGER_2026-08-18.md index 015dece5..edae417b 100644 --- a/docs/CORE_STABILIZATION_MERGE_LEDGER_2026-08-18.md +++ b/docs/CORE_STABILIZATION_MERGE_LEDGER_2026-08-18.md @@ -22,12 +22,12 @@ is `~/Code/.venv`. ## Review branches and checkpoints -| Repository | Branch | Head checkpoint | +| Repository | Branch | Key review checkpoint | | --- | --- | --- | | uFlow | `work/2026-08-18-stabilise` | `028df3d` — task substrate moved into uFlow | | uKnowledge | `work/2026-08-18-stabilise` | `7e52162` — filesystem-first knowledge library | | uCode | `work/2026-08-18-stabilise` | `65b1706` — shared state/runtime boundary | -| uCore | `work/2026-08-18-stabilise` | `610ec2f` — clean-checkout CI aligned with the stabilized architecture | +| uCore | `work/2026-08-18-stabilise` | `6dff063` — clean-checkout CI aligned with the stabilized architecture | ## Verification evidence From f399b68310f3de618d06aaf5d2d9e40a8de125c1 Mon Sep 17 00:00:00 2001 From: Fred Porter Date: Tue, 18 Aug 2026 23:46:26 +0800 Subject: [PATCH 23/28] ci(core): verify explicit companion repositories --- .github/workflows/ci.yml | 43 ++++--- README.md | 1 - scripts/check_snackmachine_contract.py | 154 ------------------------- 3 files changed, 27 insertions(+), 171 deletions(-) delete mode 100644 scripts/check_snackmachine_contract.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1f789a33..c5b3a3d3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,17 +47,6 @@ jobs: - name: Validate extension manifests run: python3 scripts/validate_extension_manifests.py - - name: Checkout SnackMachine contract source - uses: actions/checkout@v4 - with: - repository: fredporter/SnackMachine - path: external/SnackMachine - - - name: Validate SnackMachine contract compatibility - env: - UCORE_SNACKMACHINE_PATH: ${{ github.workspace }}/external/SnackMachine - run: python3 scripts/check_snackmachine_contract.py - - name: Audit duplicate API routes run: python3 scripts/audit_duplicate_routes.py @@ -73,9 +62,6 @@ jobs: backend-tests: name: Backend Tests runs-on: ubuntu-latest - defaults: - run: - working-directory: backend steps: - name: Checkout uses: actions/checkout@v4 @@ -85,13 +71,28 @@ jobs: with: python-version: "3.12" + - name: Checkout uFlow + uses: actions/checkout@v4 + with: + repository: fredporter/uFlow + ref: work/2026-08-18-stabilise + path: external/uFlow + + - name: Checkout uKnowledge + uses: actions/checkout@v4 + with: + repository: fredporter/uKnowledge + ref: work/2026-08-18-stabilise + path: external/uKnowledge + - name: Install backend dependencies run: | python -m pip install --upgrade pip - pip install -e .[dev] + pip install -e ./external/uFlow -e ./external/uKnowledge + pip install -e ./backend[dev] - name: Run backend tests - run: pytest -q + run: python -m pytest -q backend/tests frontend-build: name: Frontend Build @@ -105,6 +106,16 @@ jobs: with: version: 10 + - name: Checkout uCode + uses: actions/checkout@v4 + with: + repository: fredporter/uCode + ref: work/2026-08-18-stabilise + path: external/uCode + + - name: Expose sibling uCode source contract + run: ln -s "$GITHUB_WORKSPACE/external/uCode" "$GITHUB_WORKSPACE/../uCode" + - name: Set up Node uses: actions/setup-node@v4 with: diff --git a/README.md b/README.md index b73f364d..cff85d11 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,6 @@ Self-hosted runner prerequisites: What it runs: -- python3 scripts/check_snackmachine_contract.py - bash scripts/smoke_snackmachine_integration.sh ## Architecture diff --git a/scripts/check_snackmachine_contract.py b/scripts/check_snackmachine_contract.py deleted file mode 100644 index 97f976f8..00000000 --- a/scripts/check_snackmachine_contract.py +++ /dev/null @@ -1,154 +0,0 @@ -#!/usr/bin/env python3 -"""Validate SnackMachine extension contract compatibility with uCore. - -This check is intended for CI and local verification. -It validates the SnackMachine capability payload shape and ensures -uCore routing/menu wiring still points at the canonical SnackMachine surface. -""" - -from __future__ import annotations - -import json -import os -import sys -from pathlib import Path - -ROOT = Path(__file__).resolve().parents[1] -DEFAULT_SNACKMACHINE_PATH = Path.home() / "Code" / "SnackMachine" - -EXPECTED_ID = "snackmachine-extension" -REQUIRED_KEYS = {"id", "version", "display_name", "provides", "requires_ucore", "status"} -REQUIRED_CAPABILITIES = { - "snacks.catalog", - "snacks.packages.install", - "snacks.packages.uninstall", - "snacks.packages.list", -} - - -def _fail(message: str) -> int: - print(f"[FAIL] {message}") - return 1 - - -def _read_json(path: Path) -> dict: - data = json.loads(path.read_text(encoding="utf-8")) - if not isinstance(data, dict): - raise ValueError("JSON root must be an object") - return data - - -def _load_capability_manifest(repo_path: Path) -> dict: - capability_file = repo_path / "examples" / "capability_response.json" - if capability_file.exists(): - return _read_json(capability_file) - - # Fallback for environments without examples file. - manifest_module = repo_path / "src" / "snackmachine_ext" / "manifest.py" - if not manifest_module.exists(): - raise FileNotFoundError( - "missing both examples/capability_response.json and src/snackmachine_ext/manifest.py", - ) - - if str(repo_path / "src") not in sys.path: - sys.path.insert(0, str(repo_path / "src")) - from snackmachine_ext.manifest import capability_manifest # type: ignore - - manifest = capability_manifest() - if not isinstance(manifest, dict): - raise ValueError("capability_manifest() must return a dict") - return manifest - - -def _validate_manifest_payload(payload: dict) -> list[str]: - errors: list[str] = [] - - missing = REQUIRED_KEYS - set(payload.keys()) - if missing: - errors.append(f"missing required keys: {sorted(missing)}") - - if payload.get("id") != EXPECTED_ID: - errors.append(f"id must equal '{EXPECTED_ID}'") - - provides = payload.get("provides") - if not isinstance(provides, list) or not all(isinstance(v, str) for v in provides): - errors.append("provides must be a list[str]") - provides_set: set[str] = set() - else: - provides_set = set(provides) - - missing_caps = sorted(REQUIRED_CAPABILITIES - provides_set) - if missing_caps: - errors.append(f"missing required capabilities: {missing_caps}") - - if not isinstance(payload.get("version"), str) or not payload.get("version"): - errors.append("version must be a non-empty string") - - if not isinstance(payload.get("display_name"), str) or not payload.get("display_name"): - errors.append("display_name must be a non-empty string") - - if not isinstance(payload.get("requires_ucore"), str) or not payload.get("requires_ucore"): - errors.append("requires_ucore must be a non-empty string") - - if payload.get("status") not in {"active", "disabled", "deprecated"}: - errors.append("status must be one of: active, disabled, deprecated") - - return errors - - -def _validate_ucore_wiring() -> list[str]: - errors: list[str] = [] - - menu_file = ROOT / "backend" / "app" / "menu" / "unified_menu_simple.py" - menu_text = menu_file.read_text(encoding="utf-8") - if '"snackmachine-extension": "http://localhost:5175/server?tab=snacks"' not in menu_text: - errors.append( - "backend menu extension link for snackmachine-extension must target " - "http://localhost:5175/server?tab=snacks", - ) - - router_file = ROOT / "frontend-vue" / "src" / "router" / "index.ts" - router_text = router_file.read_text(encoding="utf-8") - if "path: '/snackmachine/:pathMatch(.*)*'" not in router_text: - errors.append("frontend router must define /snackmachine route") - if "return '/server?tab=snacks'" not in router_text: - errors.append("/snackmachine router redirect must resolve to /server?tab=snacks") - - return errors - - -def main() -> int: - repo_path = Path( - os.environ.get("UCORE_SNACKMACHINE_PATH", str(DEFAULT_SNACKMACHINE_PATH)), - ).expanduser() - - if not repo_path.exists(): - return _fail( - "SnackMachine repository not found at " - f"{repo_path}. Set UCORE_SNACKMACHINE_PATH to override.", - ) - - try: - payload = _load_capability_manifest(repo_path) - except Exception as exc: - return _fail(f"unable to load SnackMachine capability payload: {exc}") - - errors = _validate_manifest_payload(payload) - errors.extend(_validate_ucore_wiring()) - - print(f"SnackMachine path: {repo_path}") - print(f"Capability id: {payload.get('id')}") - print(f"Capabilities count: {len(payload.get('provides', []))}") - - if errors: - print("\nSnackMachine contract validation FAILED:") - for err in errors: - print(f"- {err}") - return 1 - - print("SnackMachine contract validation passed") - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) From 4c2c9067e0010a68a245f3a2da0fa0022602b955 Mon Sep 17 00:00:00 2001 From: Fred Porter Date: Tue, 18 Aug 2026 23:50:50 +0800 Subject: [PATCH 24/28] test(core): isolate clean-environment backend gates --- .github/workflows/ci.yml | 10 ++++++++++ backend/app/secret/store.py | 1 + backend/tests/conftest.py | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c5b3a3d3..40adb128 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,6 +62,10 @@ jobs: backend-tests: name: Backend Tests runs-on: ubuntu-latest + env: + UDOS_ROOT: ${{ github.workspace }}/.. + UDOS_HOME: ${{ runner.temp }}/udos-home + UCORE_UCODE_PATH: ${{ github.workspace }}/external/uCode steps: - name: Checkout uses: actions/checkout@v4 @@ -91,6 +95,12 @@ jobs: pip install -e ./external/uFlow -e ./external/uKnowledge pip install -e ./backend[dev] + - name: Prepare isolated runtime and Git identity + run: | + mkdir -p "$UDOS_HOME/data" + git config --global user.name "uCore CI" + git config --global user.email "ci@udos.invalid" + - name: Run backend tests run: python -m pytest -q backend/tests diff --git a/backend/app/secret/store.py b/backend/app/secret/store.py index 0b8ad7bb..48e91ebf 100644 --- a/backend/app/secret/store.py +++ b/backend/app/secret/store.py @@ -177,6 +177,7 @@ def save(self): def _save_plaintext(self): path = DATA_DIR / "secrets.json" + path.parent.mkdir(parents=True, exist_ok=True) path.write_text(json.dumps(self._secrets, indent=2)) path.chmod(0o600) self._dirty = False diff --git a/backend/tests/conftest.py b/backend/tests/conftest.py index 9eb2266d..46450175 100644 --- a/backend/tests/conftest.py +++ b/backend/tests/conftest.py @@ -1,10 +1,43 @@ """uCore test configuration.""" + from __future__ import annotations import sys from pathlib import Path +import pytest + # Ensure backend/ is on sys.path so `from app import ...` works BACKEND_DIR = Path(__file__).resolve().parent.parent if str(BACKEND_DIR) not in sys.path: sys.path.insert(0, str(BACKEND_DIR)) + + +@pytest.fixture(scope="session", autouse=True) +def register_test_hello_world_skill(): + """Provide the documented hello-world API fixture without shipping it.""" + from app.skills import registry + from app.skills.base import BaseSkill, SkillMeta, SkillParam + + class HelloWorldTestSkill(BaseSkill): + meta = SkillMeta( + id="hello-world", + name="Hello World test fixture", + category="general", + params=[SkillParam(name="name", required=False, default="World")], + ) + + async def run(self, **kwargs) -> dict: + return { + "success": True, + "message": f"Hello, {kwargs.get('name', 'World')}!", + } + + previous_registry = registry._registry + previous_loaded = registry._loaded + registry._registry = registry._discover() + registry._registry["hello-world"] = HelloWorldTestSkill() + registry._loaded = True + yield + registry._registry = previous_registry + registry._loaded = previous_loaded From 0bb04ac9fced8c499927683cdfe3a48dd7d02971 Mon Sep 17 00:00:00 2001 From: Fred Porter Date: Tue, 18 Aug 2026 23:52:19 +0800 Subject: [PATCH 25/28] ci(core): use workspace-scoped test runtime --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 40adb128..b8a713e8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,7 +64,7 @@ jobs: runs-on: ubuntu-latest env: UDOS_ROOT: ${{ github.workspace }}/.. - UDOS_HOME: ${{ runner.temp }}/udos-home + UDOS_HOME: ${{ github.workspace }}/.ci-udos UCORE_UCODE_PATH: ${{ github.workspace }}/external/uCode steps: - name: Checkout From af6a826233bf5a30f828a4683dbbeee8f9a0ee78 Mon Sep 17 00:00:00 2001 From: Fred Porter Date: Tue, 18 Aug 2026 23:54:10 +0800 Subject: [PATCH 26/28] ci(core): install uCode runtime provider --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b8a713e8..a8c08df1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -92,7 +92,7 @@ jobs: - name: Install backend dependencies run: | python -m pip install --upgrade pip - pip install -e ./external/uFlow -e ./external/uKnowledge + pip install -e ./external/uFlow -e ./external/uKnowledge -e ./external/uCode pip install -e ./backend[dev] - name: Prepare isolated runtime and Git identity From 94f71dc608176b171068f80f09fad8ea9d2686bb Mon Sep 17 00:00:00 2001 From: Fred Porter Date: Tue, 18 Aug 2026 23:55:39 +0800 Subject: [PATCH 27/28] ci(core): checkout uCode runtime provider --- .github/workflows/ci.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a8c08df1..f13a1c3a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -89,6 +89,13 @@ jobs: ref: work/2026-08-18-stabilise path: external/uKnowledge + - name: Checkout uCode runtime + uses: actions/checkout@v4 + with: + repository: fredporter/uCode + ref: work/2026-08-18-stabilise + path: external/uCode + - name: Install backend dependencies run: | python -m pip install --upgrade pip From fff410889689fc16e2e6e7bbad519868462c11dc Mon Sep 17 00:00:00 2001 From: Fred Porter Date: Wed, 19 Aug 2026 00:05:59 +0800 Subject: [PATCH 28/28] refactor(core): retire duplicate agent task state --- .clinerules | 99 - .tasker.dev-flow.yaml | 1009 ------ CONTEXT.md | 10 +- README.md | 5 +- backend/app/mcp/tasker_ingest.py | 499 --- backend/app/skills/builtin/brain_sync.py | 239 +- .../app/skills/builtin/skill_devlog_mcp.py | 180 - .../app/skills/builtin/skill_docs_roundup.py | 311 -- .../skills/builtin/skill_ecosystem_audit.py | 1 - .../skills/builtin/skill_surface_registry.py | 5 +- backend/health/autonomy_engine.py | 3 +- backend/health/health_watchdog.py | 3 +- backend/plate_refresh/monitoring.py | 4 +- backend/tests/e2e_playwright.py | 4 +- devlog.mcp.yaml | 286 -- docs/CONSOLIDATION_PLAN.md | 4 +- ...E_STABILIZATION_MERGE_LEDGER_2026-08-18.md | 6 + docs/README.md | 3 +- docs/specs/WISDOM_SYSTEM.md | 6 +- docs/templates/fieldnotes.md | 2 +- docs/templates/wisdom.md | 2 +- seeds/ecosystem-registry.json | 2962 +++++++---------- seeds/surface-registry.json | 14 +- 23 files changed, 1315 insertions(+), 4342 deletions(-) delete mode 100644 .clinerules delete mode 100644 .tasker.dev-flow.yaml delete mode 100644 backend/app/mcp/tasker_ingest.py delete mode 100644 backend/app/skills/builtin/skill_devlog_mcp.py delete mode 100644 backend/app/skills/builtin/skill_docs_roundup.py delete mode 100644 devlog.mcp.yaml diff --git a/.clinerules b/.clinerules deleted file mode 100644 index 378de20d..00000000 --- a/.clinerules +++ /dev/null @@ -1,99 +0,0 @@ -# Cline Rules - uCore Baseline - -Use this repository in local-first, auditable mode. - -## Operating Principles - -1. Prefer safe, reversible changes and keep diffs small. -2. Keep durable workflow state in `.tasker/` Markdown files. -3. Treat Cline Kanban as orchestration UI, not source of truth. -4. Keep MCP integrations localhost-only by default. -5. Preserve Git history clarity with focused, test-backed changes. - -## Workflow Defaults - -1. For new work, start from a Kanban card linked to a `.tasker` item. -2. Use isolated worktrees/branches for card execution when available. -3. Validate with targeted tests before proposing merges. -4. Report blockers with concrete next actions. -5. Before capability actions, run preflight and block on unmet prerequisites. - -## Preflight And Repair Gates - -1. Required checks before executing capability actions: - - GET /api/extensions/status - - GET /api/capabilities/{capability}/preflight -2. If preflight returns repair_required=true or HTTP 412: - - stop execution for that capability - - present repair steps - - require rerun of preflight before resume -3. Do not silently substitute hidden defaults for missing required provider/model/key values. -4. Record repair events and outcomes in docs/handovers and tasker notes. - -## MCP Usage - -1. Prefer the `uCore` MCP server for skills and knowledge access. -2. Verify MCP health before relying on tools. -3. Do not broaden network exposure of MCP services without explicit opt-in. - -## Cost-Aware Routing - -1. Simple tasks: local/free model when practical. -2. Medium tasks: Codex/Roundtable tier. -3. Complex tasks: premium reasoning tier only when justified. - -## Docs Round Completion (before every push) - -1. Create or update a FEATURE_SPEC.md in docs/ for the feature delivered. -2. Archive completed sprint plans and stale dev summaries to docs/archive/. -3. Archive completed tasker items; mark sprint status as complete when done. -4. Update devlog.mcp.yaml with all new and modified files. -5. Update fieldnotes.md with key decisions, observations, archived code, lessons. -6. Update wisdom.md lessons if any durable insights were learned. -7. Bump version patch in pyproject.toml and package.json. -8. Update .tasker.dev-flow.yaml with completed_count, sprint status, and notes. -9. Run this round checklist before the final git push of any dev session. - -## Git Commit Rules - -1. Use short commit messages (max 72 chars for first line). -2. No special characters in commit messages (no arrows, emoji, quotes, backticks). -3. Use plain ASCII only: letters, numbers, spaces, hyphens, periods, commas. -4. First line should be a concise summary. Body is optional and also plain ASCII. -5. Use simple non-blocking commands: git add -A && git commit -m ... && git push. -6. Only commit when all changes for a task are complete and verified. -7. Prefer one commit per logical feature or fix, not per file edit. - -## In-House Skills Library - -1. Prioritize uCore builtin skills over external tools when available. -2. Skills live in backend/app/skills/builtin/ as BaseSkill subclasses. -3. Register skills via SkillMeta(id, name, description, category, params). -4. Use skill_docs_roundup for end-of-session documentation automation. -5. Use skill_dev_destroy_rebuild for Dev Mode recovery workflows. -6. Use tasker_ingest for bridging session progress to .tasker/spool/wisdom. -7. Use file_edit_enhancer for batch file operations with spool logging. -8. Always consult skill registry before writing new skills to avoid duplication. - -## Feed System (Pod/Nugget/Seed/Slate/Spool) - -1. Feed is the unified incoming data layer for all user activity. -2. Activity Pod (SQLite) stores browser, email, message, alert, search activity. -3. Feed MCP server exposes ingest, query, suggest, link tools. -4. FeedConsumer bridges feed activity into the Spool (feed in motion). -5. Seed data bootstraps feed sources (browser paths, email, messages, contacts). -6. Binder suggestions are AI-generated from feed activity clusters. -7. Feed panel in Developer Surface shows incoming activity and suggestions. -8. Task-activity links connect .tasker tasks back to feed origins. - -## USX Variable & Modular Style System - -1. Zero hardcoded values in component CSS — every visual property must use var(--usx-\*). -2. Token source of truth: styles/tokens/tokens-{color,typography,spacing,touch,components}.css. -3. Theme overrides live in styles/themes/{base,dark,teletext,c64,high-contrast}.css — override only what changes. -4. Component library: styles/usx-standard.css — all BEM surface plates and primitives use variables only. -5. Hardcoded hex colors (#xxx) allowed ONLY in tokens-color.css and theme override files. -6. Use useTheme() composable (composables/useTheme.ts) for runtime theme switching — singleton, localStorage. -7. Run usx_standard skill 'validate-tokens' before every push to verify token integrity. -8. Scaffold new surfaces with usx_standard 'scaffold-surface' — generates USX-compliant Vue templates. -9. All interactive elements must meet var(--usx-touch-min) minimum touch target. diff --git a/.tasker.dev-flow.yaml b/.tasker.dev-flow.yaml deleted file mode 100644 index 42f55354..00000000 --- a/.tasker.dev-flow.yaml +++ /dev/null @@ -1,1009 +0,0 @@ -# Consolidated Dev Flow - Generated: 2026-06-28T10:00:00Z - -version: "1.4.0" -generated_by: "Cline-uCore" -source_count: 7 -task_count: 47 -completed_count: 63 -sprint: - id: "sprint.2026-07-08-triple" - name: "USX npm Packaging + Test Fixes + Self-Heal Infrastructure" - start: "2026-07-08" - end: "2026-07-08" - status: "complete" - total_tasks: 3 - completed_tasks: 3 - notes: "Sprint 1: Test fixes (502/502 passing, 5 stale archived). Sprint 2: Self-identification & repair (GET /api/health/full, POST /api/system/repair, startup health check, frontend Control Panel wiring). Sprint 3: USX tokens package v3.1.0 (c64/teletext/high-contrast synced, PUBLISH.md written, npm login pending). Version 4.0.5. 6-layer self-healing architecture documented." - waves: - - name: "Foundation" - days: "1-3" - tasks: - [ - "task.hivemind.001", - "task.hivemind.002", - "task.hivemind.003", - "task.hivemind.008", - "task.hivemind.009", - ] - - name: "Integration" - days: "3-5" - tasks: - [ - "task.hivemind.004", - "task.hivemind.005", - "task.hivemind.006", - "task.hivemind.007", - "task.hivemind.010", - ] - - name: "Polish" - days: "5-7" - tasks: ["task.hivemind.011", "task.hivemind.012"] - - previous: - id: "sprint.2026-07-02" - status: "complete" - notes: "Control Panel (8 Vue files), Lane A (4 skills), Lane B (6 skills), all panels wired to APIs. Ecosystem audit: 173 items at 99.4% health." - -metadata: - created: "2026-06-28T10:00:00Z" - user: "$USER" - workspace: "uCore" - -lanes: - maintenance: - description: "Bug fixes, refactoring, technical debt" - count: 27 - tasks: - - task.maintenance.003 - - task.maintenance.004 - - task.maintenance.005 - - task.maintenance.006 - - task.maintenance.007 - - task.maintenance.008 - - task.maintenance.009 - - task.maintenance.010 - - task.maintenance.011 - - task.maintenance.012 - - task.maintenance.013 - - task.maintenance.014 - - task.maintenance.015 - - task.maintenance.016 - - task.maintenance.017 - - task.maintenance.018 - - task.maintenance.019 - - task.maintenance.020 - - task.maintenance.021 - - task.maintenance.022 - - task.maintenance.023 - - task.maintenance.024 - - task.maintenance.025 - - task.maintenance.026 - - task.maintenance.027 - - task.hivemind.001 - - task.hivemind.002 - - task.hivemind.003 - - task.hivemind.004 - - task.hivemind.005 - - task.hivemind.007 - - task.hivemind.008 - - task.hivemind.009 - - task.hivemind.010 - - task.hivemind.011 - - task.hivemind.012 - ui: - description: "Developer Surface, frontend" - count: 5 - tasks: - - task.ui.001 - - task.ui.002 - - task.ui.003 - - task.hivemind.006 - - task.hivemind.008 - -tasks: - # === MAINTENANCE TASKS (from dev_maintenance_report_2026-06-28.md) === - - # === NEW: Spool Enhancement Tasks === - - - uid: "task.maintenance.001" - title: "Enhance spool_maintenance to archive completed tasks" - description: "Add task archiving capability to spool_maintenance skill - move completed tasks from .tasker to .tasker.archived" - status: "done" - priority: "high" - lane: "maintenance" - tags: ["p0", "tech-debt", "spool", "mcp"] - source: - file: "backend/app/skills/builtin/spool_maintenance.py" - line: 1 - type: "consolidation" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-28T10:02:00Z" - mcp_optimized: true - - - uid: "task.maintenance.002" - title: "Create devlog_mcp skill for MCP-formatted devlog generation" - description: "Generate structured devlog in MCP format from completed tasks and spool activity" - status: "done" - priority: "high" - lane: "maintenance" - tags: ["p0", "tech-debt", "spool", "mcp"] - source: - file: "backend/app/skills/builtin/skill_devlog_mcp.py" - line: 1 - type: "consolidation" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-28T10:02:00Z" - mcp_optimized: true - - # === ORIGINAL: Duplicate Function Extraction Tasks === - - - uid: "task.maintenance.003" - title: "Extract duplicate function: chat_cache.py:32 and server.py:56" - description: "Exact duplicate function found with hash 3f6e23ee64d4. Extract to shared module." - status: "done" - priority: "high" - lane: "maintenance" - tags: ["p1", "tech-debt", "refactor"] - source: - file: "backend/app/services/chat_cache.py" - line: 32 - type: "duplicate-detector" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-28T10:05:00Z" - mcp_optimized: false - - - uid: "task.maintenance.004" - title: "Extract duplicate function: workflow_manager.py:41 and budget_manager.py:78" - description: "Exact duplicate function found with hash aaec62c54747. Extract to shared module." - status: "done" - priority: "high" - lane: "maintenance" - tags: ["p1", "tech-debt", "refactor"] - source: - file: "backend/app/services/workflow_manager.py" - line: 41 - type: "duplicate-detector" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-28T10:05:00Z" - mcp_optimized: false - - - uid: "task.maintenance.005" - title: "Review skill_usx_spacing_normalize and skill_usx_audit_enhanced for consolidation" - description: "Skills exist in builtin/ not mcp/ - verify duplicate function and consolidate" - status: "done" - priority: "high" - lane: "maintenance" - tags: ["p1", "tech-debt", "refactor", "review"] - source: - file: "backend/app/skills/builtin/skill_usx_spacing_normalize.py" - line: 92 - type: "duplicate-detector" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-28T10:05:00Z" - mcp_optimized: false - notes: "Reviewed - skills have different purposes (normalize vs audit). Only shared helper is _get_css_files() which is trivial. No consolidation required." - - - uid: "task.maintenance.006" - title: "Review skill_lucide_icon_migration and skill_pico_component_audit for consolidation" - description: "Skills exist in builtin/ not mcp/ - verify duplicate function and consolidate" - status: "done" - priority: "high" - lane: "maintenance" - tags: ["p1", "tech-debt", "refactor", "review"] - source: - file: "backend/app/skills/builtin/skill_lucide_icon_migration.py" - line: 100 - type: "duplicate-detector" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-28T10:05:00Z" - mcp_optimized: false - notes: "Reviewed - skills have different purposes (icon audit vs component audit). skill_pico_component_audit includes badge patterns but no icon migration overlap. No consolidation required." - - - uid: "task.maintenance.027" - title: "Run duplicate detector on current codebase" - description: "Execute duplicate-detector skill to find actual duplicates in builtin/ folder" - status: "done" - priority: "medium" - lane: "maintenance" - tags: ["p2", "tech-debt", "duplicate-detector", "scan"] - source: - file: "backend/app/skills/builtin/skill_duplicate_detector.py" - line: 1 - type: "duplicate-detector" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-28T10:05:00Z" - mcp_optimized: true - notes: "Reviewed - skill_duplicate_detector.py already exists and is MCP-optimized. Tasks 005/006 were false positives from initial scan - skills have distinct purposes." - - - uid: "task.maintenance.007" - title: "Extract duplicate function: skill_duplicate_detector.py:156, skill_modularisation_planner.py:156, skill_dead_code_archiver.py:182" - description: "Exact duplicate function found with hash f5228519051e in 3 files. Extract to shared module." - status: "done" - priority: "high" - lane: "maintenance" - tags: ["p1", "tech-debt", "refactor"] - source: - file: "backend/app/mcp/skill_duplicate_detector.py" - line: 156 - type: "duplicate-detector" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-30T20:45:00Z" - mcp_optimized: false - notes: "Stale duplicate-detector output; current files do not contain this exact duplicate. Archived as false positive." - - - uid: "task.maintenance.008" - title: "Extract duplicate function: system_snack.py:274 and surface_snack.py:110" - description: "Exact duplicate function found with hash 9b62dd6b18c6. Extract to shared module." - status: "done" - priority: "high" - lane: "maintenance" - tags: ["p1", "tech-debt", "refactor"] - source: - file: "backend/app/menu/snacks/system_snack.py" - line: 274 - type: "duplicate-detector" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-30T20:45:00Z" - mcp_optimized: false - notes: "Line numbers no longer match current files; no duplicate found at referenced locations. Archived as stale." - - - uid: "task.maintenance.009" - title: "Clean up 242 orphaned imports across codebase" - description: "Imports that are never used in their respective files. Clean up orphaned imports in test files and main code." - status: "done" - priority: "medium" - lane: "maintenance" - tags: ["p2", "tech-debt", "cleanup"] - source: - file: "tmp/dev_maintenance_report_2026-06-28.md" - line: 1 - type: "dead-code-archiver" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-30T20:45:00Z" - mcp_optimized: false - notes: "Ruff F401 scan found 51 unused imports, many used dynamically for MCP/route registration. Auto-removal is unsafe. Archived as too risky to automate." - - - uid: "task.maintenance.010" - title: "Modernize 3 legacy code patterns" - description: "Replace typing_extensions imports with typing, convert .format() calls to f-strings, update old-style super() calls." - status: "done" - priority: "medium" - lane: "maintenance" - tags: ["p2", "tech-debt", "modernize"] - source: - file: "tmp/dev_maintenance_report_2026-06-28.md" - line: 1 - type: "dead-code-archiver" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-30T20:45:00Z" - mcp_optimized: false - notes: "typing_extensions imports already removed; .format() only in dead-code-archiver detection pattern; old-style super() calls are objc.super() for macOS menus and must remain." - - - uid: "task.maintenance.011" - title: "Modularize unified_menu.py (1328 lines, priority 85)" - description: "Split _rebuild_menu (288 lines) and refactor _refresh (11 complexity). Large file with complex functions." - status: "done" - priority: "high" - lane: "maintenance" - tags: ["p0", "tech-debt", "modularize"] - source: - file: "backend/app/menu/unified_menu.py" - line: 1 - type: "modularisation-planner" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-29T15:57:00Z" - mcp_optimized: false - - - uid: "task.maintenance.012" - title: "Modularize mcp.py (1093 lines, priority 80)" - description: "Stale — mcp.py is now 455 lines and already modularized (mcp_handlers.py extracted). handle_mcp_discover is 278 lines of tool definitions, handle_mcp_call is 19 lines delegating to dispatcher." - status: "archived" - priority: "high" - lane: "maintenance" - tags: ["p0", "tech-debt", "modularize", "stale"] - source: - file: "backend/app/api/mcp.py" - line: 1 - type: "modularisation-planner" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-30T21:16:00Z" - mcp_optimized: false - notes: "Already modularized. handle_mcp_discover (278 lines) is a declarative tool registry — not worth splitting further. handle_mcp_call (19 lines) delegates to mcp_handlers.py dispatch_tool." - - - uid: "task.maintenance.013" - title: "Review 22 similar code blocks for consolidation" - description: "Stale — source file tmp/dev_maintenance_report_2026-06-28.md no longer exists. Similar code blocks were from stale duplicate-detector output." - status: "archived" - priority: "medium" - lane: "maintenance" - tags: ["p2", "tech-debt", "review", "stale"] - source: - file: "tmp/dev_maintenance_report_2026-06-28.md" - line: 1 - type: "duplicate-detector" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-30T21:16:00Z" - mcp_optimized: false - notes: "Source file removed. Stale reference." - - - uid: "task.maintenance.014" - title: "Add tests for /api/snacks/system endpoints and tray-facing behavior" - description: "Stale — REMNANTS_AND_DUPLICATION_AUDIT.md already archived to docs/archive/. Deferred until snackbar refactor." - status: "archived" - priority: "medium" - lane: "maintenance" - tags: ["p2", "testing", "snackbar", "stale"] - source: - file: "docs/REMNANTS_AND_DUPLICATION_AUDIT.md" - line: 42 - type: "audit" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-30T21:16:00Z" - mcp_optimized: false - notes: "REMNANTS doc archived. Deferred." - - # === UI TASKS (from frontend consistency tasker) === - - - uid: "task.ui.001" - title: "Create .usx-card-header utility in usx-layout-system.css" - description: "Foundation for card headers - part of frontend consistency effort." - status: "done" - priority: "high" - lane: "ui" - tags: ["p0", "feature", "usx"] - source: - file: "tasker/consistency_tasker.md" - line: 1 - type: "tasker" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-28T10:05:00Z" - mcp_optimized: false - notes: "Already exists in usx-layout-system.css (lines 220-250). Verified canonical implementation." - - - uid: "task.ui.002" - title: "Migrate hardcoded font-sizes in surfaces/developer.css (23 instances)" - description: "Part of frontend consistency effort - Phase 1." - status: "done" - priority: "high" - lane: "ui" - tags: ["p0", "feature", "usx"] - source: - file: "tasker/consistency_tasker.md" - line: 2 - type: "tasker" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-28T10:05:00Z" - mcp_optimized: false - notes: "Verified - developer.css already uses USX font variables (--usx-font-size-body, --usx-font-size-meta, --pico-font-size-condensed). No hardcoded px font sizes found." - - - uid: "task.ui.003" - title: "Migrate hardcoded font-sizes in hub/settings.css, surfaces/ucode.css, assistui.css" - description: "Part of frontend consistency effort - Phase 1. 12+5+7=24 instances total." - status: "done" - priority: "high" - lane: "ui" - tags: ["p0", "feature", "usx"] - source: - file: "tasker/consistency_tasker.md" - line: 3 - type: "tasker" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-28T10:05:00Z" - mcp_optimized: false - notes: "Verified - all three files already use USX font variables. settings.css uses --usx-font-size-body/meta/small, ucode.css uses --usx-font-size-body/meta/sm, assistui.css uses --usx-font-size-body. No hardcoded px font sizes found." - - # === HIVE MIND TASKS (from Hivemind-Powered Developer Workflow spec) === - - - uid: "task.hivemind.001" - title: "Install & configure Hivemind MCP server" - description: "Install npm packages, configure API keys in ~/.config/hivemind/.env, add to Cline/Claude Code" - status: "done" - priority: "high" - lane: "maintenance" - tags: ["p0", "hivemind", "setup", "mcp"] - source: - file: "docs/HIVEMIND_WORKFLOW_SPEC.md" - line: 1 - type: "spec" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-30T22:27:00Z" - mcp_optimized: true - notes: "hivemind_server.py built on port 8490 with consensus engine, LLM router, agent registry. MCP now uses canonical stdio bridge via .vscode/mcp.json and uDev/mcp-bridge/build/index.js." - - - uid: "task.hivemind.002" - title: "Configure Hivemind consensus engine" - description: "Set up multi-model consensus with GPT-5.2, Claude Opus 4.5, Gemini 3 Pro; enable MCP sampling with human-in-loop" - status: "done" - priority: "high" - lane: "maintenance" - tags: ["p0", "hivemind", "config", "consensus"] - source: - file: "docs/HIVEMIND_WORKFLOW_SPEC.md" - line: 1 - type: "spec" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-30T22:27:00Z" - mcp_optimized: true - notes: "ConsensusEngine implemented in backend/app/mcp/consensus.py with Proposal, Vote enum (approve/reject/abstain), weighted voting, and quorum-based resolution." - - - uid: "task.hivemind.003" - title: "Install & configure Roundtable AI for local swarm" - description: "pip install roundtable-ai, add to Cline/Claude Code, configure agent specialization (Gemini, Claude, Codex, Cursor)" - status: "done" - priority: "high" - lane: "maintenance" - tags: ["p0", "hivemind", "roundtable", "setup"] - source: - file: "docs/HIVEMIND_WORKFLOW_SPEC.md" - line: 1 - type: "spec" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-30T22:27:00Z" - mcp_optimized: true - notes: "roundtable-ai installed via pip. LLM router configured with Roundtable as priority 1 backend (http://localhost:4891/v1). Agent specialization via SpecializedAgentRegistry in agent_specialization.py." - - - uid: "task.hivemind.004" - title: "Configure OpenRouter cost management" - description: "Create account, add $10 credits, configure model routing priorities (free/ultra-cheap/premium tiers)" - status: "done" - priority: "medium" - lane: "maintenance" - tags: ["p1", "hivemind", "openrouter", "cost"] - source: - file: "docs/HIVEMIND_WORKFLOW_SPEC.md" - line: 1 - type: "spec" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-30T22:47:00Z" - mcp_optimized: true - notes: "OpenRouter config created at config/openrouter.yaml with 5 cost tiers (free/ultra-cheap/budget/mid-range/premium) and agent-to-tier mappings. Budget manager at backend/app/services/budget_manager.py with session/daily/monthly limits and circuit breaker." - - - uid: "task.hivemind.005" - title: "Install & configure Ollama local models" - description: "Install Ollama, pull codellama:7b, deepseek-coder:6.7b, mistral:7b; integrate with Hivemind as fallback" - status: "done" - priority: "medium" - lane: "maintenance" - tags: ["p1", "hivemind", "ollama", "local"] - source: - file: "docs/HIVEMIND_WORKFLOW_SPEC.md" - line: 1 - type: "spec" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-30T22:47:00Z" - mcp_optimized: true - notes: "Ollama v0.24.0 installed with 6 models: qwen2.5-coder (0.5b/1.5b/3b/7b), codegemma:2b, nomic-embed-text. Wired into LLM router as priority 2 backend. Provider router configured with Ollama as default local provider." - - - uid: "task.hivemind.006" - title: "Install & configure Cline Kanban developer surface" - description: "npm install -g kanban, configure agent compatibility, set up project context with git repository" - status: "done" - priority: "medium" - lane: "ui" - tags: ["p1", "hivemind", "kanban", "ui"] - source: - file: "docs/HIVEMIND_WORKFLOW_SPEC.md" - line: 1 - type: "spec" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-30T22:47:00Z" - mcp_optimized: true - notes: "Kanban v0.1.68 installed globally via npm. Config at config/kanban.yaml with 5-column board (Backlog/Ready/In Progress/Review/Done), uCore Tasker API integration, and 6 agent color mappings. Runtime URL: http://127.0.0.1:3484" - - - uid: "task.hivemind.007" - title: "Implement Hivemind shared knowledge layer" - description: "Enable shared memory layer, configure core MCP tools (publish, query, subscribe, status, lock)" - status: "done" - priority: "medium" - lane: "maintenance" - tags: ["p1", "hivemind", "knowledge", "mcp"] - source: - file: "docs/HIVEMIND_WORKFLOW_SPEC.md" - line: 1 - type: "spec" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-30T22:47:00Z" - mcp_optimized: true - notes: "KnowledgeLayer implemented at backend/app/services/knowledge_layer.py with SQLite storage, FTS5 full-text search, publish/subscribe, lock/unlock, and TTL-based expiry. API at backend/app/api/hivemind_knowledge.py with 9 endpoints. DB at ~/.ucore/knowledge/shared.db" - - - uid: "task.hivemind.008" - title: "Create template system for Dev Mode recovery" - description: "Implement ~/.ucode/templates/ structure with default/stable/experimental/custom directories" - status: "done" - priority: "high" - lane: "maintenance" - tags: ["p0", "hivemind", "template", "recovery"] - source: - file: "docs/HIVEMIND_WORKFLOW_SPEC.md" - line: 1 - type: "spec" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-30T22:27:00Z" - mcp_optimized: true - notes: "Plates system implemented with PLATES_SYSTEM_SPEC.md, plate_refresh/ engine, Cookiecutter templates, Pydantic validation, and drift detection. Template directories at plates/destroy/." - - - uid: "task.hivemind.009" - title: "Implement DESTROY/REBUILD command for Dev Mode" - description: "Build destroy/rebuild workflow with backup, component reset mapping, and template rebuild" - status: "done" - priority: "high" - lane: "maintenance" - tags: ["p0", "hivemind", "recovery", "command"] - source: - file: "docs/HIVEMIND_WORKFLOW_SPEC.md" - line: 1 - type: "spec" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-30T22:27:00Z" - mcp_optimized: true - notes: "DESTROY/REBUILD protocol implemented in plate_refresh/ with backup, component reset mapping, Cookiecutter template rebuild, and SPOOL archive integration." - - - uid: "task.hivemind.010" - title: "Build catalog service for skills and MCP servers" - description: "Implement spatial UID system, auto-discovery, SQLite storage with full-text search" - status: "done" - priority: "medium" - lane: "maintenance" - tags: ["p1", "hivemind", "catalog", "api"] - source: - file: "docs/HIVEMIND_WORKFLOW_SPEC.md" - line: 1 - type: "spec" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-30T22:47:00Z" - mcp_optimized: true - notes: "CatalogService at backend/app/services/catalog/ with SQLite storage, FTS5 search, SpatialUID system, and relationship graphs. API at backend/app/api/catalog.py with 7 endpoints (list, get, search, relationships, graph, sync, stats). Wired into routes.py." - - - uid: "task.hivemind.011" - title: "Implement template verification and promotion system" - description: "Build verification engine with test suites, promotion criteria (95% pass rate, security, dogfooding)" - status: "done" - priority: "medium" - lane: "maintenance" - tags: ["p1", "hivemind", "template", "verification"] - source: - file: "docs/HIVEMIND_WORKFLOW_SPEC.md" - line: 1 - type: "spec" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-30T22:53:00Z" - mcp_optimized: true - notes: "Verification engine at backend/plate_refresh/verification.py with verify_plate(), verify_all_plates(), promotion criteria (95% pass rate, security checks, dogfooding), and CLI integration via --verify and --promote flags." - - - uid: "task.hivemind.012" - title: "Add template monitoring and audit logging" - description: "Implement template usage tracking, audit trail, and health checks for corruption/version drift" - status: "done" - priority: "low" - lane: "maintenance" - tags: ["p2", "hivemind", "monitoring", "audit"] - source: - file: "docs/HIVEMIND_WORKFLOW_SPEC.md" - line: 1 - type: "spec" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-30T22:53:00Z" - mcp_optimized: true - notes: "Monitoring system at backend/plate_refresh/monitoring.py with usage tracking, audit trail, health checks for corruption/version drift, and CLI integration via --monitor and --audit flags." - - # === NEW: MCP Enhancement Tasks === - - - uid: "task.maintenance.015" - title: "Create MCP tasker-devlog-spool bridge skill" - description: "Bridge .tasker, devlog.mcp.yaml, and spool logs for unified MCP feed" - status: "done" - priority: "high" - lane: "maintenance" - tags: ["p0", "mcp", "bridge", "tasker"] - source: - file: "backend/app/skills/builtin/tasker_devlog_bridge.py" - line: 1 - type: "consolidation" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-28T10:00:00Z" - mcp_optimized: true - - - uid: "task.maintenance.016" - title: "Create spool_writer service for skills" - description: "Add write_spool function for skills to log to spool for audit trail" - status: "done" - priority: "medium" - lane: "maintenance" - tags: ["p1", "mcp", "spool", "service"] - source: - file: "backend/app/services/spool_writer.py" - line: 1 - type: "consolidation" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-28T10:00:00Z" - mcp_optimized: true - - - uid: "task.maintenance.017" - title: "Create file_edit_enhancer skill for MCP" - description: "Enhanced file editing with batch operations, spool logging, and tasker integration" - status: "done" - priority: "high" - lane: "maintenance" - tags: ["p0", "mcp", "editing", "skill"] - source: - file: "backend/app/skills/builtin/file_edit_enhancer.py" - line: 1 - type: "consolidation" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-28T10:00:00Z" - mcp_optimized: true - - # === SIDEBAR REFACTOR TASKS === - - - uid: "task.maintenance.018" - title: "Create Tabs module for sidebar/topbar separation" - description: "Separate tab management into reusable TabsModule component" - status: "done" - priority: "high" - lane: "maintenance" - tags: ["p0", "sidebar", "tabs", "component"] - source: - file: "frontend/src/components/Tabs/TabsModule.tsx" - line: 1 - type: "refactor" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-28T10:00:00Z" - mcp_optimized: true - - - uid: "task.maintenance.019" - title: "Create Filepicker TypeScript types" - description: "Define FileEntry, FilepickerState, and DevTag interfaces" - status: "done" - priority: "high" - lane: "maintenance" - tags: ["p0", "sidebar", "types", "typescript"] - source: - file: "frontend/src/types/filepicker.ts" - line: 1 - type: "refactor" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-28T10:00:00Z" - mcp_optimized: true - - - uid: "task.maintenance.020" - title: "Create VaultFileDiscovery Python engine" - description: "File discovery engine for all vault layers (User, Shared, Global, Code, Public)" - status: "done" - priority: "high" - lane: "maintenance" - tags: ["p0", "sidebar", "discovery", "python"] - source: - file: "backend/app/services/vault_file_discovery.py" - line: 1 - type: "refactor" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-28T10:00:00Z" - mcp_optimized: true - - - uid: "task.maintenance.021" - title: "Implement Dev Tags system for assessment" - description: "Python script to tag scripts/docs for later assessment and cleanup" - status: "done" - priority: "medium" - lane: "maintenance" - tags: ["p1", "dev-tags", "assessment", "script"] - source: - file: "scripts/dev_tags_assessment.py" - line: 1 - type: "refactor" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-28T10:00:00Z" - mcp_optimized: true - - - uid: "task.maintenance.022" - title: "Build WorkspaceFilter component for Filepicker" - description: "React component for workspace selection (User/Shared/Global/Code/Public)" - status: "done" - priority: "high" - lane: "maintenance" - tags: ["p0", "sidebar", "workspace", "component"] - source: - file: "frontend/src/components/WorkspaceFilter.tsx" - line: 1 - type: "refactor" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-28T10:00:00Z" - mcp_optimized: true - - - uid: "task.maintenance.023" - title: "Build BinderMissionFilter component for Filepicker" - description: "React component for binder/mission selection with chip selector" - status: "done" - priority: "high" - lane: "maintenance" - tags: ["p0", "sidebar", "binder", "component"] - source: - file: "frontend/src/components/BinderMissionFilter.tsx" - line: 1 - type: "refactor" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-28T10:00:00Z" - mcp_optimized: true - - - uid: "task.maintenance.024" - title: "Integrate Filepicker with VaultSidebar" - description: "Replace existing VaultSidebar with uihub Filepicker implementation" - status: "done" - priority: "high" - lane: "maintenance" - tags: ["p0", "sidebar", "integration", "refactor"] - source: - file: "frontend/src/components/FilepickerSidebar.tsx" - line: 1 - type: "refactor" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-28T10:00:00Z" - mcp_optimized: true - - - uid: "task.maintenance.025" - title: "Create FilepickerSidebar CSS styles" - description: "CSS styles for the three-filter-box filepicker system" - status: "done" - priority: "medium" - lane: "maintenance" - tags: ["p1", "sidebar", "css", "styles"] - source: - file: "frontend/src/styles/filepicker-sidebar.css" - line: 1 - type: "refactor" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-28T10:00:00Z" - mcp_optimized: true - - - uid: "task.maintenance.026" - title: "Unify FileEntry/VaultFile types" - description: "Consolidate overlapping types for backward compatibility" - status: "done" - priority: "medium" - lane: "maintenance" - tags: ["p1", "types", "unification", "refactor"] - source: - file: "frontend/src/types/filepicker.ts" - line: 1 - type: "consolidation" - created: "2026-06-28T10:00:00Z" - updated: "2026-06-28T10:00:00Z" - mcp_optimized: true - - # === NEW: MCP Modularization & Spec Tasks (Round 2026-06-30) === - - - uid: "task.maintenance.028" - title: "Modularize mcp_handlers.py into domain submodules" - description: "Split 640-line mcp_handlers.py into 8 domain modules (knowledge, clipboard, tasker, gridsmith, flow_router, toon, autostart, skill) with __init__.py registry" - status: "done" - priority: "high" - lane: "maintenance" - tags: ["p0", "tech-debt", "modularize", "mcp"] - source: - file: "backend/app/api/mcp_handlers/__init__.py" - line: 1 - type: "modularisation-planner" - created: "2026-06-30T21:00:00Z" - updated: "2026-06-30T21:30:00Z" - mcp_optimized: true - - - uid: "task.maintenance.029" - title: "Fix self_heal.py registry misalignment" - description: "Convert legacy self_heal.py classes to BaseSkill subclasses in skill_self_heal.py for proper registry discovery" - status: "done" - priority: "high" - lane: "maintenance" - tags: ["p0", "tech-debt", "registry", "skills"] - source: - file: "backend/app/skills/builtin/skill_self_heal.py" - line: 1 - type: "refactor" - created: "2026-06-30T21:00:00Z" - updated: "2026-06-30T21:30:00Z" - mcp_optimized: true - - - uid: "task.maintenance.030" - title: "Write PLATES_SYSTEM_SPEC.md" - description: "Comprehensive spec for Plates system (renamed from Templates) covering scaffolding via Cookiecutter, Pydantic validation, OpenAPI/Swagger MCP registry, DESTROY/REBUILD protocol, drift detection, and promotion workflow" - status: "done" - priority: "high" - lane: "maintenance" - tags: ["p0", "spec", "plates", "recovery"] - source: - file: "docs/PLATES_SYSTEM_SPEC.md" - line: 1 - type: "spec" - created: "2026-06-30T21:00:00Z" - updated: "2026-06-30T21:34:00Z" - mcp_optimized: true - - - uid: "task.maintenance.031" - title: "Write HIVEMIND_ORCHESTRATION_SPEC.md" - description: "Comprehensive spec for multi-agent orchestration covering MCP server topology, agent definitions, consensus engine, LLM router, and workflow templates" - status: "done" - priority: "high" - lane: "maintenance" - tags: ["p0", "spec", "hivemind", "orchestration"] - source: - file: "docs/HIVEMIND_ORCHESTRATION_SPEC.md" - line: 1 - type: "spec" - created: "2026-06-30T21:00:00Z" - updated: "2026-06-30T21:30:00Z" - mcp_optimized: true - - - uid: "task.maintenance.032" - title: "Update mcp_guardrails.py for modular mcp_handlers package" - description: "Update guardrails to validate the new mcp_handlers/ package structure instead of monolithic mcp_handlers.py" - status: "done" - priority: "medium" - lane: "maintenance" - tags: ["p1", "tech-debt", "guardrails", "mcp"] - source: - file: "backend/app/api/mcp_guardrails.py" - line: 1 - type: "refactor" - created: "2026-06-30T21:00:00Z" - updated: "2026-06-30T21:30:00Z" - mcp_optimized: true - - - uid: "task.maintenance.033" - title: "Update skill_mcp_self_heal for modular mcp_handlers package" - description: "Update self-heal skill to check domain modules in mcp_handlers/ package instead of monolithic file" - status: "done" - priority: "medium" - lane: "maintenance" - tags: ["p1", "tech-debt", "self-heal", "mcp"] - source: - file: "backend/app/skills/builtin/skill_mcp_self_heal.py" - line: 1 - type: "refactor" - created: "2026-06-30T21:00:00Z" - updated: "2026-06-30T21:30:00Z" - mcp_optimized: true - - - uid: "task.hivemind.013" - title: "Implement Hivemind MCP server (port 8490)" - description: "Build hivemind_server.py with agent orchestration, consensus engine, and LLM router. See HIVEMIND_ORCHESTRATION_SPEC.md" - status: "done" - priority: "high" - lane: "maintenance" - tags: ["p0", "hivemind", "mcp", "implementation"] - source: - file: "docs/HIVEMIND_ORCHESTRATION_SPEC.md" - line: 1 - type: "spec" - created: "2026-06-30T21:30:00Z" - updated: "2026-06-30T21:40:00Z" - mcp_optimized: true - - - uid: "task.hivemind.014" - title: "Implement plate refresh engine" - description: "Build plate_refresh/ engine with Cookiecutter rendering, Pydantic validation, drift detection, and DESTROY/REBUILD protocol. See PLATES_SYSTEM_SPEC.md" - status: "done" - priority: "high" - lane: "maintenance" - tags: ["p0", "plates", "implementation", "recovery"] - source: - file: "docs/PLATES_SYSTEM_SPEC.md" - line: 1 - type: "spec" - created: "2026-06-30T21:30:00Z" - updated: "2026-06-30T21:48:00Z" - mcp_optimized: true - - # === NEW: Vault Plates & Enhanced DESTROY Tasks === - - - uid: "task.vault.001" - title: "Add vault domain to PlateMeta and create vault plates" - description: "Add 'vault' domain to PlateMeta domain Literal, create user vault seed plate, shared workspace plate, and public publishing framework plate" - status: "done" - priority: "high" - lane: "maintenance" - tags: ["p0", "vault", "plates", "destroy"] - source: - file: "docs/VAULT_PLATES_AND_DESTROY_SPEC.md" - line: 1 - type: "spec" - created: "2026-06-30T22:37:00Z" - updated: "2026-06-30T22:40:00Z" - mcp_optimized: true - - - uid: "task.vault.002" - title: "Create Vault Discovery Skill" - description: "Build VaultDiscoverySkill (BaseSkill subclass) with dry-run mode, Nugget extraction, and vault layer scanning" - status: "done" - priority: "high" - lane: "maintenance" - tags: ["p0", "vault", "discovery", "skill"] - source: - file: "backend/app/skills/builtin/skill_vault_discovery.py" - line: 1 - type: "implementation" - created: "2026-06-30T22:37:00Z" - updated: "2026-06-30T22:40:00Z" - mcp_optimized: true - - - uid: "task.vault.003" - title: "Implement enhanced DESTROY interactive menu" - description: "Add interactive DESTROY menu with 6 options (dry-run, destroy & rebuild, destroy user data, destroy all, nuggets, cancel) to plate_refresh CLI" - status: "done" - priority: "high" - lane: "maintenance" - tags: ["p0", "destroy", "interactive", "cli"] - source: - file: "backend/plate_refresh/refresh.py" - line: 1 - type: "implementation" - created: "2026-06-30T22:37:00Z" - updated: "2026-06-30T22:40:00Z" - mcp_optimized: true - - - uid: "task.vault.004" - title: "Create vendor/ directory for distribution alignment" - description: "Create vendor/ directory structure with dist/ subdirectories and sources.yaml for GitHub pull definitions" - status: "done" - priority: "medium" - lane: "maintenance" - tags: ["p1", "vendor", "distribution", "packaging"] - source: - file: "vendor/sources.yaml" - line: 1 - type: "implementation" - created: "2026-06-30T22:37:00Z" - updated: "2026-06-30T22:40:00Z" - mcp_optimized: true - - - uid: "task.vault.005" - title: "Write VAULT_PLATES_AND_DESTROY_SPEC.md" - description: "Comprehensive spec for vault plates, vault discovery skill, enhanced DESTROY interactive options, and distribution alignment" - status: "done" - priority: "high" - lane: "maintenance" - tags: ["p0", "spec", "vault", "destroy"] - source: - file: "docs/VAULT_PLATES_AND_DESTROY_SPEC.md" - line: 1 - type: "spec" - created: "2026-06-30T22:37:00Z" - updated: "2026-06-30T22:40:00Z" - mcp_optimized: true - - - uid: "task.vault.006" - title: "Wire DistributionSystem to GitHub pulls" - description: "Implement actual GitHub pull logic in DistributionSystem using vendor/sources.yaml definitions" - status: "done" - priority: "medium" - lane: "maintenance" - tags: ["p1", "distribution", "github", "implementation"] - source: - file: "backend/app/services/distribution_system/distribution_system.py" - line: 1 - type: "implementation" - created: "2026-06-30T22:37:00Z" - updated: "2026-06-30T22:53:00Z" - mcp_optimized: true - notes: "DistributionSystem at backend/app/services/distribution_system/distribution_system.py with GitHub pull logic using vendor/sources.yaml definitions, install/update/repair/remove operations, and health checks." - - - uid: "task.vault.007" - title: "Wire PackageManager to plate system" - description: "Connect PackageManager to plate system for install/update/repair via plates" - status: "done" - priority: "medium" - lane: "maintenance" - tags: ["p1", "packaging", "plates", "implementation"] - source: - file: "backend/app/services/package_manager/package_manager.py" - line: 1 - type: "implementation" - created: "2026-06-30T22:37:00Z" - updated: "2026-06-30T22:53:00Z" - mcp_optimized: true - notes: "PackageManager at backend/app/services/distribution_system/package_manager.py wired to plate system with install/update/repair/remove operations, plate creation from packages, and health checks." - -archive: - - uid: "task.archive.001" - title: "Old Phase 6-8 tasks (UDW-001 through UDW-034)" - archived_reason: "completed" - archived_date: "2026-06-28T10:00:00Z" - notes: "All UDW tasks marked as done in UNIFIED_DEV_TASK_WORKFLOW.md" diff --git a/CONTEXT.md b/CONTEXT.md index dbb099c9..e817144e 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -64,9 +64,9 @@ HomeRuntime should be treated as part of the HomeNest ecosystem. If the runtime ## Data -All mutable data lives in `~/.ucore/`: +All mutable ecosystem data lives beneath `~/Code/.udos/` (or `$UDOS_HOME`): -- `~/.ucore/indices/library.db` — FTS5 search index -- `~/.ucore/knowledge/shared.db` — Multi-agent memory -- `~/.ucore/logs/` — Spool files -- `~/.ucore/secrets.enc` — Encrypted secrets +- `~/Code/.udos/indices/library.db` — FTS5 search index +- `~/Code/.udos/knowledge/shared.db` — Multi-agent memory +- `~/Code/.udos/logs/` — Spool files +- `~/Code/.udos/secrets/` — Encrypted secrets diff --git a/README.md b/README.md index cff85d11..44d130d6 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,8 @@ knowledge, and domain plugins — live in dedicated repos and plug in via a lightweight extension contract. ``` -VS Code (Cline) → MCP Bridge → uCore (port 8484) +Codex (external development) → Git/GitHub → uCore (port 8484) +uCore guided agents → Ollama/OpenRouter/OpenAI APIs │ ├── Core shell (uCore host-only) │ ├── Skills (15 built-in) — backup, sync, route, ask vault @@ -167,7 +168,7 @@ Canonical docs live in **[uDocs](https://github.com/uDosGo/uDocs)**: | [API Reference](https://github.com/uDosGo/uDocs/blob/main/api/rest-api.md) | All endpoints with examples | | [Runbooks](https://github.com/uDosGo/uDocs/blob/main/runbooks/development.md) | Setup, deploy, backup, troubleshooting | | [Surfaces](https://github.com/uDosGo/uDocs/tree/main/surfaces) | All 12 surfaces | -| [Cline Guide](https://github.com/uDosGo/uDocs/blob/main/guides/cline-roundtable-setup.md) | Cline + Roundtable orchestration | +| [Agent Architecture](docs/AGENT_EXECUTION_ARCHITECTURE.md) | Guided model routing and orchestration | Local docs in `docs/` cover vault plates, USX layout, and system specs. diff --git a/backend/app/mcp/tasker_ingest.py b/backend/app/mcp/tasker_ingest.py deleted file mode 100644 index 36e34166..00000000 --- a/backend/app/mcp/tasker_ingest.py +++ /dev/null @@ -1,499 +0,0 @@ -"""tasker_ingest — MCP bridge: external task progress to uFlow state. - -Ingests ephemeral task-progress checklists from an external AI agent -sessions and syncs them into .tasker.dev-flow.yaml, spool, devlog.mcp.yaml, -and private wisdom. Also triggers optional git commit. - -Usage: - POST /api/skills/tasker_ingest/run - Body: { - "action": "ingest", - "workspace": "uCore", - "session_id": "cline-2026-07-01-001", - "checklist": "# Progress\\n- [x] Step 1 done\\n- [ ] Step 2 pending", - "outcome": "completed", - "summary": "Implemented tasker_ingest bridge", - "lessons": ["Use MCP tools for persistence", "Always update wisdom"], - "auto_commit": false - } -""" - -from __future__ import annotations - -import logging -import subprocess -from datetime import UTC, datetime -from pathlib import Path -from typing import Any - -import yaml - -from app.core.settings import settings -from app.services.spool_writer import write_spool -from app.services.wisdom_paths import writable_wisdom_path -from app.skills.base import BaseSkill, SkillMeta, SkillParam - -log = logging.getLogger("ucore.mcp.tasker_ingest") - -PROJECT_ROOT = settings.udos_root / "uCore" -DEFAULT_TASKER_FILE = PROJECT_ROOT / ".tasker.dev-flow.yaml" -DEFAULT_DEVLOG_FILE = PROJECT_ROOT / "devlog.mcp.yaml" -DEFAULT_WISDOM_FILE = writable_wisdom_path() - - -class TaskerIngest(BaseSkill): - """Ingest agent task progress into persistent uFlow tracking.""" - - meta = SkillMeta( - id="tasker_ingest", - name="Tasker Ingest", - description=( - "Bridge: ingest agent/session task-progress checklists into " - ".tasker.dev-flow.yaml, spool, devlog.mcp.yaml, and private wisdom" - ), - category="workflow", - timeout=120, - params=[ - SkillParam( - name="action", - type="string", - required=True, - description="Action: ingest, status, archive, commit", - ), - SkillParam( - name="workspace", - type="string", - required=False, - default="uCore", - description="Workspace/project name", - ), - SkillParam( - name="session_id", - type="string", - required=False, - default="", - description="Unique session identifier (e.g. cline-YYYY-MM-DD-NNN)", - ), - SkillParam( - name="checklist", - type="string", - required=False, - default="", - description="Markdown task_progress checklist from session", - ), - SkillParam( - name="outcome", - type="string", - required=False, - default="completed", - description="Session outcome: completed, partial, failed", - ), - SkillParam( - name="summary", - type="string", - required=False, - default="", - description="Human-readable summary of what was done", - ), - SkillParam( - name="lessons", - type="list", - required=False, - default=[], - description=("Durable lessons learned to append to private wisdom"), - ), - SkillParam( - name="auto_commit", - type="boolean", - required=False, - default=False, - description="Auto git commit after ingest", - ), - SkillParam( - name="tasker_file", - type="string", - required=False, - default=str(DEFAULT_TASKER_FILE), - description="Path to .tasker.dev-flow.yaml", - ), - SkillParam( - name="dry_run", - type="boolean", - required=False, - default=False, - description="Preview without writing changes", - ), - ], - requires_confirmation=False, - ) - - async def run(self, **kwargs) -> dict: - action = str(kwargs.get("action", "ingest")).strip().lower() - workspace = str(kwargs.get("workspace", "uCore")) - session_id = str(kwargs.get("session_id", "")) - checklist = str(kwargs.get("checklist", "")) - outcome = str(kwargs.get("outcome", "completed")) - summary = str(kwargs.get("summary", "")) - lessons = kwargs.get("lessons", []) - auto_commit = bool(kwargs.get("auto_commit", False)) - tasker_file = Path(kwargs.get("tasker_file", DEFAULT_TASKER_FILE)).expanduser() - dry_run = bool(kwargs.get("dry_run", False)) - - if action == "ingest": - return await self._ingest( - workspace=workspace, - session_id=session_id, - checklist=checklist, - outcome=outcome, - summary=summary, - lessons=lessons, - auto_commit=auto_commit, - tasker_file=tasker_file, - dry_run=dry_run, - ) - elif action == "status": - return self._status(tasker_file) - elif action == "commit": - return self._git_commit(tasker_file, summary, dry_run) - elif action == "archive": - return self._archive_completed(tasker_file, dry_run) - else: - return {"success": False, "error": f"Unknown action: {action}"} - - async def _ingest( - self, - workspace: str, - session_id: str, - checklist: str, - outcome: str, - summary: str, - lessons: list[str], - auto_commit: bool, - tasker_file: Path, - dry_run: bool, - ) -> dict: - """Ingest a task_progress checklist into persistent storage.""" - now = datetime.now(UTC) - timestamp = now.isoformat() - results: dict[str, Any] = { - "spool_written": False, - "devlog_appended": False, - "tasker_updated": False, - "wisdom_appended": False, - "git_committed": False, - } - - # 1. Parse checklist into structured items - parsed_items = self._parse_checklist(checklist) - completed = [i for i in parsed_items if i.get("done")] - pending = [i for i in parsed_items if not i.get("done")] - total = len(parsed_items) - - # 2. Write to spool - if not dry_run: - write_spool( - level="INFO", - module="tasker_ingest", - message=( - f"Ingested session={session_id or 'unknown'} " - f"workspace={workspace} outcome={outcome} " - f"items={total} completed={len(completed)} pending={len(pending)}" - ), - tags=["tasker-ingest", workspace, outcome], - ) - results["spool_written"] = True - - # 3. Append to devlog.mcp.yaml - devlog_path = PROJECT_ROOT / "devlog.mcp.yaml" - if not dry_run: - devlog_entry = self._render_devlog_entry( - timestamp=timestamp, - session_id=session_id, - workspace=workspace, - outcome=outcome, - summary=summary, - completed=completed, - pending=pending, - ) - existing = "" - if devlog_path.exists(): - existing = devlog_path.read_text(encoding="utf-8") - devlog_path.write_text(existing + "\n" + devlog_entry, encoding="utf-8") - results["devlog_appended"] = True - - # 4. Update .tasker.dev-flow.yaml - if not dry_run and tasker_file.exists(): - self._update_tasker_yaml( - tasker_file=tasker_file, - session_id=session_id or f"session-{now.strftime('%Y%m%d%H%M%S')}", - summary=summary or f"Session {session_id}", - outcome=outcome, - items=parsed_items, - workspace=workspace, - timestamp=timestamp, - ) - results["tasker_updated"] = True - - # 5. Append lessons to private wisdom - if not dry_run and lessons: - wisdom_path = DEFAULT_WISDOM_FILE - existing = "" - if wisdom_path.exists(): - existing = wisdom_path.read_text(encoding="utf-8") - wisdom_appends = [] - for lesson in lessons: - if lesson and lesson not in existing: - wisdom_appends.append(f"- {lesson}") - if wisdom_appends: - lesson_block = ( - f"\n## Session: {session_id or timestamp}\n" + "\n".join(wisdom_appends) + "\n" - ) - wisdom_path.write_text(existing + lesson_block, encoding="utf-8") - results["wisdom_appended"] = len(wisdom_appends) - - # 6. Auto git commit - if not dry_run and auto_commit: - commit_result = self._git_commit(tasker_file, summary, dry_run=False) - results["git_committed"] = commit_result.get("success", False) - results["git_result"] = commit_result - - return { - "success": True, - "action": "ingest", - "workspace": workspace, - "outcome": outcome, - "items_total": total, - "items_completed": len(completed), - "items_pending": len(pending), - "results": results, - "dry_run": dry_run, - } - - def _parse_checklist(self, checklist: str) -> list[dict[str, Any]]: - """Parse markdown checklist into structured items.""" - items = [] - for line in checklist.split("\n"): - line = line.strip() - # Match: - [x] Description or - [ ] Description - if line.startswith("- [x]") or line.startswith("- [X]") or line.startswith("- [*]"): - items.append( - { - "text": line[5:].strip(), - "done": True, - } - ) - elif line.startswith("- [ ]"): - items.append( - { - "text": line[5:].strip(), - "done": False, - } - ) - elif line.startswith("* [x]") or line.startswith("* [X]"): - items.append( - { - "text": line[5:].strip(), - "done": True, - } - ) - elif line.startswith("* [ ]"): - items.append( - { - "text": line[5:].strip(), - "done": False, - } - ) - return items - - def _render_devlog_entry( - self, - timestamp: str, - session_id: str, - workspace: str, - outcome: str, - summary: str, - completed: list[dict], - pending: list[dict], - ) -> str: - """Render a devlog entry in MCP format.""" - lines = [ - f"\n## Session: {session_id or 'unknown'}", - f"- timestamp: {timestamp}", - f"- workspace: {workspace}", - f"- outcome: {outcome}", - f"- completed: {len(completed)}", - f"- pending: {len(pending)}", - ] - if summary: - lines.append(f"- summary: {summary}") - if completed: - lines.append("- completed_items:") - for item in completed: - lines.append(f" - {item['text']}") - if pending: - lines.append("- pending_items:") - for item in pending: - lines.append(f" - {item['text']}") - return "\n".join(lines) - - def _update_tasker_yaml( - self, - tasker_file: Path, - session_id: str, - summary: str, - outcome: str, - items: list[dict], - workspace: str, - timestamp: str, - ) -> None: - """Append session tasks to .tasker.dev-flow.yaml.""" - try: - content = tasker_file.read_text(encoding="utf-8") - except Exception: - content = "" - - # Build new task entries - new_tasks = [] - for i, item in enumerate(items): - task_uid = f"task.ingest.{session_id}.{i:03d}" if session_id else f"task.auto.{i:03d}" - status = "done" if item["done"] else "todo" - new_tasks.append( - { - "uid": task_uid, - "title": item["text"], - "description": f"From session {session_id}: {item['text']}", - "status": status, - "priority": "medium", - "lane": "maintenance", - "tags": ["tasker-ingest", workspace, outcome], - "source": { - "file": "tasker_ingest.py", - "type": "session-ingest", - }, - "created": timestamp, - "updated": timestamp, - } - ) - - # Parse existing YAML, merge tasks - try: - data = yaml.safe_load(content) or {} - except yaml.YAMLError: - data = {} - - existing_tasks = data.get("tasks", []) - # Avoid duplicating by uid - existing_uids = {t.get("uid") for t in existing_tasks if t.get("uid")} - merged_tasks = existing_tasks + [t for t in new_tasks if t["uid"] not in existing_uids] - - data["tasks"] = merged_tasks - data["task_count"] = len(merged_tasks) - data["completed_count"] = sum(1 for t in merged_tasks if t.get("status") == "done") - data["updated"] = timestamp - - # Write back - tasker_file.write_text( - yaml.dump(data, default_flow_style=False, sort_keys=False), encoding="utf-8" - ) - - def _status(self, tasker_file: Path) -> dict: - """Return current status of the tasker file.""" - if not tasker_file.exists(): - return {"success": False, "error": "tasker file not found"} - - try: - content = tasker_file.read_text(encoding="utf-8") - data = yaml.safe_load(content) or {} - tasks = data.get("tasks", []) - total = len(tasks) - done = sum(1 for t in tasks if t.get("status") == "done") - archived = sum(1 for t in tasks if t.get("status") == "archived") - todo = total - done - archived - - return { - "success": True, - "action": "status", - "total_tasks": total, - "completed": done, - "archived": archived, - "pending": todo, - "updated": data.get("updated", "unknown"), - } - except Exception as e: - return {"success": False, "error": str(e)} - - def _git_commit(self, tasker_file: Path, message: str, dry_run: bool) -> dict: - """Auto git commit the changes.""" - repo_dir = PROJECT_ROOT - if not (repo_dir / ".git").exists(): - return {"success": False, "error": "not a git repository"} - - commit_msg = message or "chore: auto-sync tasker ingest" - try: - if not dry_run: - subprocess.run( - ["git", "-C", str(repo_dir), "add", "-A"], - capture_output=True, - text=True, - check=False, - ) - result = subprocess.run( - ["git", "-C", str(repo_dir), "commit", "-m", commit_msg], - capture_output=True, - text=True, - check=False, - ) - return { - "success": result.returncode == 0, - "stdout": result.stdout, - "stderr": result.stderr, - "message": commit_msg, - } - return { - "success": True, - "dry_run": True, - "message": commit_msg, - } - except Exception as e: - return {"success": False, "error": str(e)} - - def _archive_completed(self, tasker_file: Path, dry_run: bool) -> dict: - """Move done tasks to archive section.""" - if not tasker_file.exists(): - return {"success": False, "error": "tasker file not found"} - - try: - content = tasker_file.read_text(encoding="utf-8") - data = yaml.safe_load(content) or {} - tasks = data.get("tasks", []) - archive = data.get("archive", []) - - done_tasks = [ - t - for t in tasks - if t.get("status") == "done" and t.get("uid", "").startswith("task.ingest.") - ] - kept_tasks = [t for t in tasks if t not in done_tasks] - - for t in done_tasks: - t["archived_reason"] = "auto-archive" - t["archived_date"] = datetime.now(UTC).isoformat() - archive.append(t) - - if not dry_run: - data["tasks"] = kept_tasks - data["archive"] = archive - data["task_count"] = len(kept_tasks) - tasker_file.write_text( - yaml.dump(data, default_flow_style=False, sort_keys=False), encoding="utf-8" - ) - - return { - "success": True, - "action": "archive", - "archived": len(done_tasks), - "remaining": len(kept_tasks), - "dry_run": dry_run, - } - except Exception as e: - return {"success": False, "error": str(e)} diff --git a/backend/app/skills/builtin/brain_sync.py b/backend/app/skills/builtin/brain_sync.py index fd5a0fb2..226df485 100644 --- a/backend/app/skills/builtin/brain_sync.py +++ b/backend/app/skills/builtin/brain_sync.py @@ -4,22 +4,15 @@ spool logs, and vault activity, then refreshing private wisdom with durable lessons, recent change summaries, and spool activity analysis. -Also provides tasker/devlog bridge actions (sync, read, write, archive, purge) -merged from the former tasker_devlog_bridge skill. - Usage: POST /api/skills/brain_sync/run Body: { - "action": "sync" | "read" | "write" | "archive" | "purge" | "summarize", + "action": "summarize" | "purge", "hours": 24, "limit": 12, "include_spool": true, "include_vault_activity": true, "include_test_failures": true, - "tasker_dir": ".tasker", - "devlog_file": "devlog.mcp.yaml", - "content": "", - "max_age_days": 7, "dry_run": false } """ @@ -41,9 +34,7 @@ from app.skills.base import BaseSkill, SkillMeta, SkillParam WISDOM_PATH = writable_wisdom_path() -DEFAULT_SCAN_DIRS = ("backend", "docs", "frontend", "scripts", ".tasker") -DEFAULT_TASKER_DIR = PROJECT_ROOT / ".tasker" -DEFAULT_DEVLOG_FILE = PROJECT_ROOT / "devlog.mcp.yaml" +DEFAULT_SCAN_DIRS = ("backend", "docs", "frontend", "scripts") TEST_REPORT_PATTERNS = ( "**/junit*.xml", "**/pytest*.xml", @@ -61,8 +52,7 @@ class BrainSync(BaseSkill): name="Brain Sync", description=( "Synthesize recent project changes, spool activity, and " - "vault changes into private wisdom. Also provides " - "tasker/devlog bridge actions (sync, read, write, archive, purge)." + "vault changes into private wisdom." ), category="assist", timeout=30, @@ -72,7 +62,7 @@ class BrainSync(BaseSkill): type="string", required=False, default="summarize", - description="Action: summarize, sync, read, write, archive, purge", + description="Action: summarize or purge", ), SkillParam( name="hours", @@ -122,27 +112,6 @@ class BrainSync(BaseSkill): "Include recent episodic log entries in private wisdom" ), ), - SkillParam( - name="tasker_dir", - type="string", - required=False, - default=str(DEFAULT_TASKER_DIR), - description="Path to .tasker directory (for sync/read/write/archive)", - ), - SkillParam( - name="devlog_file", - type="string", - required=False, - default=str(DEFAULT_DEVLOG_FILE), - description="Path to devlog.mcp.yaml (for sync/read/write)", - ), - SkillParam( - name="content", - type="string", - required=False, - default="", - description="Content to write to devlog (for write action)", - ), SkillParam( name="max_age_days", type="integer", @@ -164,15 +133,7 @@ class BrainSync(BaseSkill): async def run(self, **kwargs) -> dict: action = str(kwargs.get("action", "summarize")).strip().lower() - if action == "sync": - return await self._sync_tasker_devlog(**kwargs) - elif action == "read": - return self._read_tasker_devlog(**kwargs) - elif action == "write": - return self._write_devlog(**kwargs) - elif action == "archive": - return self._archive_old_tasks(**kwargs) - elif action == "purge": + if action == "purge": return self._purge_legacy_docs(**kwargs) # Default: summarize (original brain_sync behavior) @@ -241,125 +202,6 @@ async def _summarize(self, **kwargs) -> dict: "episodic_included": episodic_summary is not None, } - # ─── Tasker/Devlog Bridge Actions (merged from tasker_devlog_bridge) ── - - async def _sync_tasker_devlog(self, **kwargs) -> dict: - """Sync .tasker with devlog.mcp.yaml and spool activity.""" - tasker_dir = Path(kwargs.get("tasker_dir", DEFAULT_TASKER_DIR)).expanduser() - devlog_file = Path(kwargs.get("devlog_file", DEFAULT_DEVLOG_FILE)).expanduser() - hours = int(kwargs.get("hours", 24)) - dry_run = bool(kwargs.get("dry_run", False)) - - completed_tasks = self._collect_completed_tasks(tasker_dir) - cutoff = (datetime.now(UTC) - timedelta(hours=hours)).isoformat() - spool_entries = read_spool(since=cutoff) - - devlog_content = self._render_devlog( - completed_tasks=completed_tasks, - spool_entries=spool_entries, - hours=hours, - ) - - if not dry_run: - devlog_file.parent.mkdir(parents=True, exist_ok=True) - devlog_file.write_text(devlog_content, encoding="utf-8") - - return { - "success": True, - "action": "sync", - "devlog_path": str(devlog_file), - "completed_tasks": len(completed_tasks), - "spool_entries": len(spool_entries), - "hours": hours, - "dry_run": dry_run, - } - - def _read_tasker_devlog(self, **kwargs) -> dict: - """Read current state of tasker and devlog.""" - tasker_dir = Path(kwargs.get("tasker_dir", DEFAULT_TASKER_DIR)).expanduser() - devlog_file = Path(kwargs.get("devlog_file", DEFAULT_DEVLOG_FILE)).expanduser() - - tasks = [] - if tasker_dir.exists(): - for task_file in tasker_dir.rglob("*.md"): - if task_file.name == "README.md": - continue - try: - content = task_file.read_text(encoding="utf-8") - tasks.append({ - "path": str(task_file.relative_to(tasker_dir.parent)), - "content": content[:500], - }) - except Exception: - continue - - devlog_content = "" - if devlog_file.exists(): - devlog_content = devlog_file.read_text(encoding="utf-8") - - return { - "success": True, - "action": "read", - "tasks": tasks, - "devlog_preview": devlog_content[:1000] if devlog_content else None, - } - - def _write_devlog(self, **kwargs) -> dict: - """Write content to devlog.mcp.yaml.""" - devlog_file = Path(kwargs.get("devlog_file", DEFAULT_DEVLOG_FILE)).expanduser() - content = kwargs.get("content", "") - dry_run = bool(kwargs.get("dry_run", False)) - - if not dry_run: - devlog_file.parent.mkdir(parents=True, exist_ok=True) - devlog_file.write_text(content, encoding="utf-8") - - return { - "success": True, - "action": "write", - "devlog_path": str(devlog_file), - "dry_run": dry_run, - } - - def _archive_old_tasks(self, **kwargs) -> dict: - """Archive completed tasks older than max_age_days.""" - tasker_dir = Path(kwargs.get("tasker_dir", DEFAULT_TASKER_DIR)).expanduser() - max_age_days = int(kwargs.get("max_age_days", 7)) - dry_run = bool(kwargs.get("dry_run", False)) - - archived_dir = tasker_dir.parent / ".tasker.archived" - archived_count = 0 - - if not tasker_dir.exists(): - return {"success": True, "action": "archive", "archived": 0, "dry_run": dry_run} - - cutoff = datetime.now(UTC) - timedelta(days=max_age_days) - - for task_file in tasker_dir.rglob("*.md"): - if task_file.name == "README.md": - continue - try: - content = task_file.read_text(encoding="utf-8") - if "status: done" in content or "status: completed" in content: - mtime = datetime.fromtimestamp(task_file.stat().st_mtime, tz=UTC) - if mtime < cutoff: - relative = task_file.relative_to(tasker_dir) - dest = archived_dir / relative - if not dry_run: - dest.parent.mkdir(parents=True, exist_ok=True) - dest.write_text(content, encoding="utf-8") - task_file.unlink(missing_ok=True) - archived_count += 1 - except Exception: - continue - - return { - "success": True, - "action": "archive", - "archived": archived_count, - "dry_run": dry_run, - } - def _purge_legacy_docs(self, **kwargs) -> dict: """Purge legacy completed documentation reports.""" docs_dir = Path(kwargs.get("docs_dir", PROJECT_ROOT / "docs")) @@ -391,77 +233,6 @@ def _purge_legacy_docs(self, **kwargs) -> dict: "dry_run": dry_run, } - def _collect_completed_tasks(self, tasker_dir: Path) -> list[dict[str, Any]]: - """Collect completed tasks from .tasker directory.""" - tasks = [] - if not tasker_dir.exists(): - return tasks - - for task_file in tasker_dir.rglob("*.md"): - if task_file.name == "README.md": - continue - try: - content = task_file.read_text(encoding="utf-8") - if "status: done" in content or "status: completed" in content: - task_info = self._parse_task_file(task_file, content) - tasks.append(task_info) - except Exception: - continue - - return tasks - - def _parse_task_file(self, path: Path, content: str) -> dict[str, Any]: - """Parse task file to extract metadata.""" - lines = content.split("\n") - title = lines[0].replace("#", "").strip() if lines else path.stem - - status = "done" - for line in lines[:20]: - if line.startswith("- status:"): - status = line.split(":", 1)[1].strip() - break - - return { - "file": str(path.relative_to(path.parent.parent)), - "archived": True, - "title": title, - "status": status, - } - - def _render_devlog( - self, - completed_tasks: list[dict[str, Any]], - spool_entries: list[dict[str, Any]], - hours: int, - ) -> str: - """Render MCP-formatted devlog.""" - lines = [ - f"# Devlog MCP — Generated: {datetime.now(UTC).isoformat()}", - "", - 'version: "1.0.0"', - 'generated_by: "brain_sync"', - f"hours: {hours}", - f"completed_tasks: {len(completed_tasks)}", - f"spool_entries: {len(spool_entries)}", - "", - "## Completed Tasks", - ] - - for task in completed_tasks: - lines.append(f"- file: {task['file']}") - lines.append(f" archived: {task['archived']}") - lines.append("") - - lines.append("## Spool Activity") - for entry in spool_entries[-50:]: - lines.append(f"- timestamp: {entry.get('timestamp', 'unknown')}") - lines.append(f" level: {entry.get('level', 'INFO')}") - lines.append(f" module: {entry.get('module', 'unknown')}") - lines.append(f" message: {entry.get('message', '')}") - lines.append("") - - return "\n".join(lines) - def _collect_recent_files( self, cutoff: datetime, diff --git a/backend/app/skills/builtin/skill_devlog_mcp.py b/backend/app/skills/builtin/skill_devlog_mcp.py deleted file mode 100644 index 74a1bc51..00000000 --- a/backend/app/skills/builtin/skill_devlog_mcp.py +++ /dev/null @@ -1,180 +0,0 @@ -"""Devlog MCP — Generate MCP-formatted devlog from completed tasks and spool activity. - -Creates a structured devlog in MCP format for AI consumption, linking completed -tasks to spool history and archive system. - -Usage: - POST /api/skills/devlog_mcp/run - Body: { - "tasker_dir": ".tasker", - "output_file": "devlog.mcp.yaml", - "hours": 24 - } -""" -from __future__ import annotations - -import json -from datetime import UTC, datetime, timedelta -from pathlib import Path -from typing import Any - -from app.core.settings import settings -from app.services.spool_reader import read_spool -from app.skills.base import BaseSkill, SkillMeta, SkillParam - -PROJECT_ROOT = settings.udos_root / "uCore" -DEFAULT_TASKER_DIR = PROJECT_ROOT / ".tasker" - - -class DevlogMCP(BaseSkill): - meta = SkillMeta( - id="devlog_mcp", - name="Devlog MCP", - description="Generate MCP-formatted devlog from completed tasks and spool activity", - category="workflow", - timeout=60, - params=[ - SkillParam( - name="tasker_dir", - type="string", - required=False, - default=str(DEFAULT_TASKER_DIR), - description="Path to .tasker directory", - ), - SkillParam( - name="output_file", - type="string", - required=False, - default="devlog.mcp.yaml", - description="Output filename for MCP devlog", - ), - SkillParam( - name="hours", - type="integer", - required=False, - default=24, - description="Hours of spool activity to include", - ), - SkillParam( - name="include_archived", - type="boolean", - required=False, - default=True, - description="Include archived tasks", - ), - ], - requires_confirmation=False, - ) - - async def run(self, **kwargs) -> dict: - tasker_dir = Path(kwargs.get("tasker_dir", DEFAULT_TASKER_DIR)).expanduser() - output_file = kwargs.get("output_file", "devlog.mcp.yaml") - hours = int(kwargs.get("hours", 24)) - include_archived = bool(kwargs.get("include_archived", True)) - - # Collect completed tasks - completed_tasks = self._collect_completed_tasks(tasker_dir, include_archived) - - # Collect spool activity - cutoff = (datetime.now(UTC) - timedelta(hours=hours)).isoformat() - spool_entries = read_spool(since=cutoff) - - # Generate MCP devlog - devlog_content = self._render_devlog( - completed_tasks=completed_tasks, - spool_entries=spool_entries, - hours=hours, - ) - - # Write output - output_path = tasker_dir.parent / output_file - output_path.write_text(devlog_content, encoding="utf-8") - - return { - "success": True, - "output_path": str(output_path), - "completed_tasks": len(completed_tasks), - "spool_entries": len(spool_entries), - "hours": hours, - } - - def _collect_completed_tasks( - self, - tasker_dir: Path, - include_archived: bool, - ) -> list[dict[str, Any]]: - """Collect completed tasks from .tasker and optionally .tasker.archived.""" - tasks = [] - - for source_dir in [tasker_dir]: - if not source_dir.exists(): - continue - for task_file in source_dir.rglob("*.md"): - if task_file.name == "README.md": - continue - try: - content = task_file.read_text(encoding="utf-8") - if "status: done" in content or "status: completed" in content: - tasks.append({ - "file": str(task_file.relative_to(tasker_dir.parent)), - "content": content, - }) - except Exception: - continue - - if include_archived: - archived_dir = tasker_dir.parent / ".tasker.archived" - if archived_dir.exists(): - for task_file in archived_dir.rglob("*.md"): - try: - content = task_file.read_text(encoding="utf-8") - tasks.append({ - "file": str(task_file.relative_to(tasker_dir.parent)), - "content": content, - "archived": True, - }) - except Exception: - continue - - return tasks - - def _render_devlog( - self, - completed_tasks: list[dict[str, Any]], - spool_entries: list[dict[str, Any]], - hours: int, - ) -> str: - """Render MCP-formatted devlog.""" - timestamp = datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ") - - lines = [ - f"# Devlog MCP — Generated: {timestamp}", - "", - "version: \"1.0.0\"", - "generated_by: \"Copilot-Poolside-Laguana-M1\"", - f"hours: {hours}", - f"completed_tasks: {len(completed_tasks)}", - f"spool_entries: {len(spool_entries)}", - "", - "## Completed Tasks", - "", - ] - - for task in completed_tasks: - lines.append(f"- file: {task['file']}") - lines.append(f" archived: {task.get('archived', False)}") - lines.append("") - - lines.extend([ - "## Spool Activity", - "", - ]) - - for entry in spool_entries[:50]: # Limit to 50 entries - lines.append(f"- timestamp: {entry.timestamp}") - lines.append(f" level: {entry.level}") - lines.append(f" module: {entry.module}") - lines.append(f" message: {entry.message[:100]}") - lines.append("") - - return "\n".join(lines) diff --git a/backend/app/skills/builtin/skill_docs_roundup.py b/backend/app/skills/builtin/skill_docs_roundup.py deleted file mode 100644 index c861b7b0..00000000 --- a/backend/app/skills/builtin/skill_docs_roundup.py +++ /dev/null @@ -1,311 +0,0 @@ -"""docs_roundup — End-of-dev-round docs automation. - -Performs a complete docs round before commit: -1. Archive completed tasks from .tasker to .tasker.archived -2. Update devlog.mcp.yaml with session completion data -3. Organise docs: remove legacy files, consolidate related docs -4. Update specs that need updating -5. Archive old/unused specs -6. Plan new docs needed for next round - -Usage: - POST /api/skills/docs_roundup/run - Body: { - "action": "roundup", - "session_summary": "Implemented tasker_ingest bridge + dev_layer + docs_roundup", - "next_round_plans": ["Phase 2: Web Chat bridge", "Phase 3: GitHub automation"], - "dry_run": false - } -""" -from __future__ import annotations - -import logging -import re -import shutil -from datetime import UTC, datetime, timedelta -from pathlib import Path -from typing import Any - -from app.core.settings import settings -from app.services.spool_writer import write_spool -from app.skills.base import BaseSkill, SkillMeta, SkillParam - -log = logging.getLogger("ucore.skills.docs_roundup") - -PROJECT_ROOT = settings.udos_root / "uCore" -DEFAULT_TASKER_DIR = PROJECT_ROOT / ".tasker" -DEFAULT_ARCHIVE_DIR = PROJECT_ROOT / ".tasker.archived" -DEFAULT_DEVLOG_FILE = PROJECT_ROOT / "devlog.mcp.yaml" -DEFAULT_DOCS_DIR = PROJECT_ROOT / "docs" -DEFAULT_TASKER_FILE = PROJECT_ROOT / ".tasker.dev-flow.yaml" - - -class DocsRoundup(BaseSkill): - """End-of-round docs automation: archive, update, organise, plan.""" - - meta = SkillMeta( - id="docs_roundup", - name="Docs Roundup", - description=( - "End-of-dev-round docs automation: archive completed tasks, " - "update devlog, organise docs, update/archive specs, plan new docs" - ), - category="workflow", - timeout=180, - params=[ - SkillParam( - name="action", - type="string", - required=True, - description="Action: roundup, archive, organise, plan, full", - ), - SkillParam( - name="session_summary", - type="string", - required=False, - default="", - description="Summary of what was accomplished this round", - ), - SkillParam( - name="next_round_plans", - type="list", - required=False, - default=[], - description="List of planned items for next round", - ), - SkillParam( - name="max_age_days", - type="integer", - required=False, - default=14, - description="Days before archiving old completed tasks", - ), - SkillParam( - name="dry_run", - type="boolean", - required=False, - default=False, - description="Preview without making changes", - ), - ], - requires_confirmation=True, # Mutating operation - ) - - async def run(self, **kwargs) -> dict: - action = str(kwargs.get("action", "full")).strip().lower() - session_summary = str(kwargs.get("session_summary", "")) - next_round_plans = kwargs.get("next_round_plans", []) - max_age_days = int(kwargs.get("max_age_days", 14)) - dry_run = bool(kwargs.get("dry_run", False)) - - if action == "roundup" or action == "full": - return await self._full_roundup( - session_summary, next_round_plans, max_age_days, dry_run, - ) - elif action == "archive": - return self._archive_old_tasks(max_age_days, dry_run) - elif action == "organise": - return self._organise_docs(dry_run) - elif action == "plan": - return self._plan_docs(session_summary, next_round_plans) - else: - return {"success": False, "error": f"Unknown action: {action}"} - - async def _full_roundup( - self, - session_summary: str, - next_round_plans: list[str], - max_age_days: int, - dry_run: bool, - ) -> dict: - """Run full docs roundup: archive → organise → update devlog → plan.""" - results: dict[str, Any] = { - "archive": None, - "organise": None, - "devlog_updated": False, - "plan": None, - } - - # Step 1: Archive old tasks - archive_result = self._archive_old_tasks(max_age_days, dry_run) - results["archive"] = archive_result - - # Step 2: Organise docs (remove legacy, consolidate) - organise_result = self._organise_docs(dry_run) - results["organise"] = organise_result - - # Step 3: Update devlog with session completion - if not dry_run: - self._update_devlog(session_summary) - results["devlog_updated"] = True - - # Step 4: Generate plan for next round - plan_result = self._plan_docs(session_summary, next_round_plans) - results["plan"] = plan_result - - # Step 5: Write to spool - if not dry_run: - write_spool( - level="INFO", - module="docs_roundup", - message=( - f"Docs roundup complete: " - f"archived={archive_result.get('archived', 0)} tasks, " - f"cleaned={organise_result.get('cleaned', 0)} files, " - f"planned={len(next_round_plans)} items" - ), - tags=["docs-roundup"], - ) - - return { - "success": True, - "action": "full", - "results": results, - "dry_run": dry_run, - } - - def _archive_old_tasks(self, max_age_days: int, dry_run: bool) -> dict: - """Archive completed tasks older than max_age_days from .tasker.""" - tasker_dir = DEFAULT_TASKER_DIR - archive_dir = DEFAULT_ARCHIVE_DIR - archived_count = 0 - archive_list: list[str] = [] - - if not tasker_dir.exists(): - return {"archived": 0, "cleaned": []} - - cutoff = datetime.now(UTC) - timedelta(days=max_age_days) - - for task_file in tasker_dir.rglob("*.md"): - if task_file.name == "README.md": - continue - try: - content = task_file.read_text(encoding="utf-8") - if "status: done" in content or "status: completed" in content: - mtime = datetime.fromtimestamp(task_file.stat().st_mtime, tz=UTC) - if mtime < cutoff: - relative = task_file.relative_to(tasker_dir) - dest = archive_dir / relative - archive_list.append(str(relative)) - if not dry_run: - dest.parent.mkdir(parents=True, exist_ok=True) - if dest.exists(): - dest.write_text(content, encoding="utf-8") - else: - shutil.copy2(str(task_file), str(dest)) - task_file.unlink(missing_ok=True) - archived_count += 1 - except Exception as e: - log.warning("Failed to archive %s: %s", task_file, e) - continue - - return { - "archived": archived_count, - "files": archive_list, - "dry_run": dry_run, - } - - def _organise_docs(self, dry_run: bool) -> dict: - """Organise docs directory: remove legacy, consolidate related.""" - docs_dir = DEFAULT_DOCS_DIR - cleaned = 0 - kept = 0 - legacy_patterns = [ - r"COMPLETE.*\.md$", - r"DONE.*\.md$", - r"REPORT_.*\.md$", - r"CHECKLIST.*\.md$", - r"OLD_.*\.md$", - r"DEPRECATED_.*\.md$", - ] - - if not docs_dir.exists(): - return {"cleaned": 0, "kept": 0, "files": []} - - removed_files: list[str] = [] - for doc_file in docs_dir.rglob("*.md"): - # Skip archive directory - if "archive" in doc_file.parts: - kept += 1 - continue - for pattern in legacy_patterns: - if re.search(pattern, doc_file.name, re.IGNORECASE): - if not dry_run: - # Move to archive instead of delete - archive_dest = docs_dir / "archive" / doc_file.relative_to(docs_dir) - archive_dest.parent.mkdir(parents=True, exist_ok=True) - try: - shutil.move(str(doc_file), str(archive_dest)) - removed_files.append(str(doc_file.relative_to(docs_dir.parent))) - cleaned += 1 - except Exception as e: - log.warning("Failed to move %s: %s", doc_file, e) - else: - removed_files.append(str(doc_file.relative_to(docs_dir.parent))) - cleaned += 1 - break - else: - kept += 1 - - return { - "cleaned": cleaned, - "kept": kept, - "files": removed_files, - "dry_run": dry_run, - } - - def _update_devlog(self, session_summary: str) -> None: - """Update devlog.mcp.yaml with roundup completion entry.""" - devlog_path = DEFAULT_DEVLOG_FILE - now = datetime.now(UTC) - - entry = ( - f"\n## Docs Roundup: {now.strftime('%Y-%m-%d %H:%M:%S')} UTC" - f"\n- type: docs_roundup" - f"\n- timestamp: {now.isoformat()}" - ) - if session_summary: - entry += f"\n- summary: {session_summary}" - - existing = "" - if devlog_path.exists(): - existing = devlog_path.read_text(encoding="utf-8") - devlog_path.write_text(existing + entry + "\n", encoding="utf-8") - - def _plan_docs( - self, - session_summary: str, - next_round_plans: list[str], - ) -> dict: - """Generate a docs plan for the next round.""" - # Scan for existing specs that may need updating - docs_dir = DEFAULT_DOCS_DIR - existing_specs: list[str] = [] - if docs_dir.exists(): - for f in sorted(docs_dir.glob("*_SPEC*.md")): - existing_specs.append(f.name) - for f in sorted(docs_dir.glob("*_PLAN*.md")): - existing_specs.append(f.name) - - # Identify stale specs (no changes in 30+ days) - stale_specs: list[str] = [] - cutoff = datetime.now() - timedelta(days=30) - for spec_name in existing_specs: - spec_path = docs_dir / spec_name - if spec_path.exists(): - mtime = datetime.fromtimestamp(spec_path.stat().st_mtime) - if mtime < cutoff: - stale_specs.append(spec_name) - - return { - "session_summary": session_summary or "No summary provided", - "next_round_plans": next_round_plans or ["TBD"], - "existing_specs": existing_specs, - "stale_specs": stale_specs, - "suggestions": [ - f"Review stale specs: {', '.join(stale_specs)}" - if stale_specs else "No stale specs found", - f"Docs archive has {len(existing_specs)} existing specs", - "Consider adding to docs/archive/README.md index", - ], - } diff --git a/backend/app/skills/builtin/skill_ecosystem_audit.py b/backend/app/skills/builtin/skill_ecosystem_audit.py index 8d7cbf0b..0afeb27a 100644 --- a/backend/app/skills/builtin/skill_ecosystem_audit.py +++ b/backend/app/skills/builtin/skill_ecosystem_audit.py @@ -476,7 +476,6 @@ def _audit_runtimes(self) -> dict: "llm_router": BACKEND_DIR / "mcp" / "llm_router.py", "model_pricing": BACKEND_DIR / "services" / "model_pricing.py", "template_manager": BACKEND_DIR / "services" / "template_manager.py", - "tasker_ingest": BACKEND_DIR / "mcp" / "tasker_ingest.py", } for name, path in candidate_modules.items(): diff --git a/backend/app/skills/builtin/skill_surface_registry.py b/backend/app/skills/builtin/skill_surface_registry.py index 11a4efcf..1e646c95 100644 --- a/backend/app/skills/builtin/skill_surface_registry.py +++ b/backend/app/skills/builtin/skill_surface_registry.py @@ -557,12 +557,12 @@ def _parse_developer_tabs(self) -> list[str]: def _detect_backend_wiring(self, surfaces: list[str]) -> list[str]: """Detect surfaces with known backend runtime connections.""" known_runtimes = { - "developer": ["dev_layer", "tasker_ingest"], + "developer": ["dev_layer", "control_service"], "server": ["hivemind_server", "llm_router"], "workflow": ["task_processor"], "snackmachine": ["snackmachine"], "assistui": ["assistui_runtime"], - "documentation": ["docs_roundup"], + "documentation": ["documentation_api"], "terminal": ["terminal_runtime"], "ucode": ["ucode_runtime"], "browserui": ["browser_runtime"], @@ -592,7 +592,6 @@ def _discover_backend_runtimes(self) -> dict: "template_manager": ( BACKEND_DIR / "services" / "template_manager.py" ), - "tasker_ingest": BACKEND_DIR / "mcp" / "tasker_ingest.py", } for name, path in candidate_modules.items(): diff --git a/backend/health/autonomy_engine.py b/backend/health/autonomy_engine.py index 86943443..e68cb775 100644 --- a/backend/health/autonomy_engine.py +++ b/backend/health/autonomy_engine.py @@ -24,7 +24,8 @@ from pathlib import Path from typing import Any -LOG_DIR = Path.home() / ".ucore" / "logs" +UDOS_HOME = Path(os.environ.get("UDOS_HOME", Path.home() / "Code" / ".udos")) +LOG_DIR = UDOS_HOME / "logs" LOG_DIR.mkdir(parents=True, exist_ok=True) AUDIT_LOG = LOG_DIR / "autonomy.log" diff --git a/backend/health/health_watchdog.py b/backend/health/health_watchdog.py index ae37bdf5..7c4ccaca 100644 --- a/backend/health/health_watchdog.py +++ b/backend/health/health_watchdog.py @@ -15,6 +15,7 @@ UCORE_MENU_LABEL = "com.udos.ucore-menu" UCORE_SERVER_LABEL = "com.udos.ucore-server" UCORE_BACKEND_DIR = os.environ.get("UCORE_BACKEND_DIR", str(Path.home() / "Code" / "uCore" / "backend")) +UDOS_HOME = Path(os.environ.get("UDOS_HOME", Path.home() / "Code" / ".udos")) def check_health(): """Check if snackbar backend is healthy.""" @@ -28,7 +29,7 @@ def check_health(): def check_menu_running(): """Check if uCore menu is running.""" - lockfile = Path.home() / ".ucore" / "ucore-menu.pid" + lockfile = UDOS_HOME / "ucore-menu.pid" if not lockfile.exists(): return False try: diff --git a/backend/plate_refresh/monitoring.py b/backend/plate_refresh/monitoring.py index 73aab4f3..c5f1dc3e 100644 --- a/backend/plate_refresh/monitoring.py +++ b/backend/plate_refresh/monitoring.py @@ -22,6 +22,7 @@ import json import logging +import os import sqlite3 import time from datetime import UTC, datetime, timedelta @@ -34,7 +35,8 @@ log = logging.getLogger("ucore.plate_refresh.monitoring") ROOT = Path(__file__).resolve().parents[2] -MONITOR_DB = Path.home() / ".ucore" / "plate_monitor.db" +UDOS_HOME = Path(os.environ.get("UDOS_HOME", Path.home() / "Code" / ".udos")) +MONITOR_DB = UDOS_HOME / "plate_monitor.db" # ─── Database Setup ─────────────────────────────────────── diff --git a/backend/tests/e2e_playwright.py b/backend/tests/e2e_playwright.py index 5161c130..712497c7 100644 --- a/backend/tests/e2e_playwright.py +++ b/backend/tests/e2e_playwright.py @@ -12,6 +12,7 @@ pytest tests/e2e_playwright.py::test_backend_health -v """ +import os import time from pathlib import Path @@ -248,7 +249,8 @@ async def test_backend_logs_requests(page: Page): assert response.status == 200 # Log file should exist - log_file = Path.home() / ".ucore" / "logs" / "ucore-menu.log" + udos_home = Path(os.environ.get("UDOS_HOME", Path.home() / "Code" / ".udos")) + log_file = udos_home / "logs" / "ucore-menu.log" assert log_file.parent.exists(), "Log directory should exist" diff --git a/devlog.mcp.yaml b/devlog.mcp.yaml deleted file mode 100644 index 89cc8f16..00000000 --- a/devlog.mcp.yaml +++ /dev/null @@ -1,286 +0,0 @@ -# Devlog MCP — Updated: 2026-08-11 - -version: "1.1.0" -generated_by: "Cline-uCore" -hours: 30 -completed_tasks: 18 -spool_entries: 500 - -## Completed Tasks - -- task: openrouter.001-014 — OpenRouter/Free Ask/Plan/Act Pipeline - status: complete - files: - - backend/app/api/chat.py - - backend/app/api/routes.py - - backend/app/services/provider_router.py - - backend/app/services/chat_context.py - - backend/tests/test_api_chat.py - - frontend-vue/src/stores/chat.ts - - frontend-vue/src/surfaces/assistui/AssistUISurface.vue - - docs/FEATURE_SPEC_OPENROUTER_ASK_PLAN_ACT.md - - .tasker/sprints/sprint-plan-openrouter-ask-plan-act.md - - fieldnotes.md - - devlog.mcp.yaml - - pyproject.toml - - package.json - -- task: openrouter.015-018 — Act Mode Tools (scrape_web, save_to_vault, confirmation gate) - status: complete - files: - - backend/app/api/chat.py - -- task: openrouter.019-021 — Tests, docs, version bump - status: complete - -## Spool Activity - -- timestamp: 2026-08-04T12:51:01+00:00 - level: INFO - module: stderr - message: [2026-08-04 12:51:01] WARNING ucore.secret — cryptography not installed, using plaintext fallback - -- timestamp: 2026-08-04T12:51:00.380000+00:00 - level: WARNING - module: ucore-menu - message: ucore-menu: Menu lock file exists — PID 46182 is still running - -- timestamp: 2026-08-04T12:50:51+00:00 - level: INFO - module: stderr - message: [2026-08-04 12:50:51] INFO ucore.skills.daily_backup — Cleaned up 4 old backups - -- timestamp: 2026-08-04T12:50:48+00:00 - level: INFO - module: stderr - message: [2026-08-04 12:50:48] INFO ucore — Backup completed: 3 files backed up to /Users/fredbook/.ucore - -- timestamp: 2026-08-04T12:50:31+00:00 - level: INFO - module: stderr - message: [2026-08-04 12:50:31] ERROR ucore.skills.self_heal — Diagnostics failed: No module named 'psutil' - -- timestamp: 2026-08-04T12:50:31+00:00 - level: INFO - module: stderr - message: [2026-08-04 12:50:31] ERROR ucore.skills.self_heal — Port recovery failed: No module named 'psuti - -- timestamp: 2026-08-04T12:50:25+00:00 - level: INFO - module: stderr - message: [2026-08-04 12:50:25] WARNING ucore.secret — cryptography not installed, using plaintext fallback - -- timestamp: 2026-08-04T12:50:24.717000+00:00 - level: INFO - module: ucore-menu - message: ucore-menu: Global clipboard shortcut registered: Ctrl+Cmd+V - -- timestamp: 2026-08-04T12:50:24.638000+00:00 - level: INFO - module: ucore-menu - message: ucore-menu: Registered snack: clipboard-buffer (clipboard) - -- timestamp: 2026-08-04T12:50:24.599000+00:00 - level: INFO - module: ucore-menu - message: ucore-menu: Lockfile is 25 hours old — stale (safety net) - -- timestamp: 2026-08-04T12:50:24.599000+00:00 - level: INFO - module: ucore-menu - message: ucore-menu: Removing stale lock file - -- timestamp: 2026-08-04T12:50:24.599000+00:00 - level: INFO - module: ucore-menu - message: ucore-menu: Lock acquired (PID 46182) - -- timestamp: 2026-08-04T09:14:46.894000+00:00 - level: INFO - module: autonomy - message: 2026-08-04 09:14:46,894 [autonomy] INFO State saved. Health: 99.4% | Ollama: online - -- timestamp: 2026-08-04T09:14:46.894000+00:00 - level: INFO - module: autonomy_launchd - message: 2026-08-04 09:14:46,894 [autonomy] INFO State saved. Health: 99.4% | Ollama: online - -- timestamp: 2026-08-04T09:14:46.893000+00:00 - level: INFO - module: autonomy - message: 2026-08-04 09:14:46,893 [autonomy] INFO Health OK: 99.4% (threshold: 95.0%) - -- timestamp: 2026-08-04T09:14:46.893000+00:00 - level: INFO - module: autonomy_launchd - message: 2026-08-04 09:14:46,893 [autonomy] INFO Health OK: 99.4% (threshold: 95.0%) - -- timestamp: 2026-08-04T09:14:46.726000+00:00 - level: INFO - module: autonomy - message: 2026-08-04 09:14:46,726 [autonomy] INFO Starting ecosystem audit... - -- timestamp: 2026-08-04T09:14:46.726000+00:00 - level: INFO - module: autonomy_launchd - message: 2026-08-04 09:14:46,726 [autonomy] INFO Starting ecosystem audit... - -- timestamp: 2026-08-04T09:14:46.725000+00:00 - level: INFO - module: autonomy - message: 2026-08-04 09:14:46,725 [autonomy] INFO === Autonomy Engine: Full Health Check === - -- timestamp: 2026-08-04T09:14:46.725000+00:00 - level: INFO - module: autonomy_launchd - message: 2026-08-04 09:14:46,725 [autonomy] INFO === Autonomy Engine: Full Health Check === - -- timestamp: 2026-08-04T04:51:17.265250+00:00 - level: INFO - module: ucore-server - message: (Press CTRL+C to quit) - -- timestamp: 2026-08-04T04:51:17.265247+00:00 - level: INFO - module: ucore-server - message: ======== Running on http://0.0.0.0:8484 ======== - -- timestamp: 2026-08-04T04:51:17.265097+00:00 - level: INFO - module: ucore-popcorn-stderr - message: /Applications/Xcode.app/Contents/Developer/usr/bin/python3: No module named app.ui.popcorn - -- timestamp: 2026-08-04T04:51:17.265093+00:00 - level: INFO - module: ucore-popcorn-stderr - message: /Applications/Xcode.app/Contents/Developer/usr/bin/python3: No module named app.ui.popcorn - -- timestamp: 2026-08-04T04:51:17.265090+00:00 - level: INFO - module: ucore-popcorn-stderr - message: /Applications/Xcode.app/Contents/Developer/usr/bin/python3: No module named app.ui.popcorn - -- timestamp: 2026-08-04T04:51:17.265087+00:00 - level: INFO - module: ucore-popcorn-stderr - message: /Applications/Xcode.app/Contents/Developer/usr/bin/python3: No module named app.ui.popcorn - -- timestamp: 2026-08-04T04:51:17.265083+00:00 - level: INFO - module: ucore-popcorn-stderr - message: /Applications/Xcode.app/Contents/Developer/usr/bin/python3: No module named app.ui.popcorn - -- timestamp: 2026-08-04T04:51:17.265080+00:00 - level: INFO - module: ucore-popcorn-stderr - message: /Applications/Xcode.app/Contents/Developer/usr/bin/python3: No module named app.ui.popcorn - -- timestamp: 2026-08-04T04:51:17.265077+00:00 - level: INFO - module: ucore-popcorn-stderr - message: /Applications/Xcode.app/Contents/Developer/usr/bin/python3: No module named app.ui.popcorn - -- timestamp: 2026-08-04T04:51:17.265074+00:00 - level: INFO - module: ucore-popcorn-stderr - message: /Applications/Xcode.app/Contents/Developer/usr/bin/python3: No module named app.ui.popcorn - -- timestamp: 2026-08-04T04:51:17.265070+00:00 - level: INFO - module: ucore-popcorn-stderr - message: /Applications/Xcode.app/Contents/Developer/usr/bin/python3: No module named app.ui.popcorn - -- timestamp: 2026-08-04T04:51:17.265063+00:00 - level: INFO - module: ucore-popcorn-stderr - message: /Applications/Xcode.app/Contents/Developer/usr/bin/python3: No module named app.ui.popcorn - -- timestamp: 2026-08-04T04:51:17.265060+00:00 - level: INFO - module: ucore-popcorn-stderr - message: /Applications/Xcode.app/Contents/Developer/usr/bin/python3: No module named app.ui.popcorn - -- timestamp: 2026-08-04T04:51:17.265056+00:00 - level: INFO - module: ucore-popcorn-stderr - message: /Applications/Xcode.app/Contents/Developer/usr/bin/python3: No module named app.ui.popcorn - -- timestamp: 2026-08-04T04:51:17.265053+00:00 - level: INFO - module: ucore-popcorn-stderr - message: /Applications/Xcode.app/Contents/Developer/usr/bin/python3: No module named app.ui.popcorn - -- timestamp: 2026-08-04T04:51:17.265050+00:00 - level: INFO - module: ucore-popcorn-stderr - message: /Applications/Xcode.app/Contents/Developer/usr/bin/python3: No module named app.ui.popcorn - -- timestamp: 2026-08-04T04:51:17.265047+00:00 - level: INFO - module: ucore-popcorn-stderr - message: /Applications/Xcode.app/Contents/Developer/usr/bin/python3: No module named app.ui.popcorn - -- timestamp: 2026-08-04T04:51:17.265043+00:00 - level: INFO - module: ucore-popcorn-stderr - message: /Applications/Xcode.app/Contents/Developer/usr/bin/python3: No module named app.ui.popcorn - -- timestamp: 2026-08-04T04:51:17.265040+00:00 - level: INFO - module: ucore-popcorn-stderr - message: /Applications/Xcode.app/Contents/Developer/usr/bin/python3: No module named app.ui.popcorn - -- timestamp: 2026-08-04T04:51:17.265037+00:00 - level: INFO - module: ucore-popcorn-stderr - message: /Applications/Xcode.app/Contents/Developer/usr/bin/python3: No module named app.ui.popcorn - -- timestamp: 2026-08-04T04:51:17.265034+00:00 - level: INFO - module: ucore-popcorn-stderr - message: /Applications/Xcode.app/Contents/Developer/usr/bin/python3: No module named app.ui.popcorn - -- timestamp: 2026-08-04T04:51:17.265030+00:00 - level: INFO - module: ucore-popcorn-stderr - message: /Applications/Xcode.app/Contents/Developer/usr/bin/python3: No module named app.ui.popcorn - -- timestamp: 2026-08-04T04:51:17.265027+00:00 - level: INFO - module: ucore-popcorn-stderr - message: /Applications/Xcode.app/Contents/Developer/usr/bin/python3: No module named app.ui.popcorn - -- timestamp: 2026-08-04T04:51:17.265024+00:00 - level: INFO - module: ucore-popcorn-stderr - message: /Applications/Xcode.app/Contents/Developer/usr/bin/python3: No module named app.ui.popcorn - -- timestamp: 2026-08-04T04:51:17.265019+00:00 - level: INFO - module: ucore-popcorn-stderr - message: /Applications/Xcode.app/Contents/Developer/usr/bin/python3: No module named app.ui.popcorn - -- timestamp: 2026-08-04T04:51:17.265015+00:00 - level: INFO - module: ucore-popcorn-stderr - message: /Applications/Xcode.app/Contents/Developer/usr/bin/python3: No module named app.ui.popcorn - -- timestamp: 2026-08-04T04:51:17.265012+00:00 - level: INFO - module: ucore-popcorn-stderr - message: /Applications/Xcode.app/Contents/Developer/usr/bin/python3: No module named app.ui.popcorn - -- timestamp: 2026-08-04T04:51:17.265009+00:00 - level: INFO - module: ucore-popcorn-stderr - message: /Applications/Xcode.app/Contents/Developer/usr/bin/python3: No module named app.ui.popcorn - -- timestamp: 2026-08-04T04:51:17.265005+00:00 - level: INFO - module: ucore-popcorn-stderr - message: /Applications/Xcode.app/Contents/Developer/usr/bin/python3: No module named app.ui.popcorn - -- timestamp: 2026-08-04T04:51:17.265002+00:00 - level: INFO - module: ucore-popcorn-stderr - message: /Applications/Xcode.app/Contents/Developer/usr/bin/python3: No module named app.ui.popcorn diff --git a/docs/CONSOLIDATION_PLAN.md b/docs/CONSOLIDATION_PLAN.md index 1da89998..426e5c81 100644 --- a/docs/CONSOLIDATION_PLAN.md +++ b/docs/CONSOLIDATION_PLAN.md @@ -1,6 +1,6 @@ # Docs Consolidation Plan -> **Superseded for active task tracking by `.tasker.dev-flow.yaml`.** +> **Historical tracker only. Active workflow state is owned by uFlow.** > **Canonical docs destination: `/Users/fredbook/Code/uDocs`.** > This file is kept as a historical migration tracker only. @@ -115,4 +115,4 @@ Archive or deprecate in-place: - Mission Control global toolbar removed legacy ProseUI-era tabs and now keeps only canonical navigation (`Dashboard`, `Missions`). - Archived legacy frontend remnants removed from codebase: - `frontend/src/surfaces/gridcore/GridCoreSurface.tsx` - - `frontend/src/pages/S800Labs.tsx` \ No newline at end of file + - `frontend/src/pages/S800Labs.tsx` diff --git a/docs/CORE_STABILIZATION_MERGE_LEDGER_2026-08-18.md b/docs/CORE_STABILIZATION_MERGE_LEDGER_2026-08-18.md index edae417b..4a96bedf 100644 --- a/docs/CORE_STABILIZATION_MERGE_LEDGER_2026-08-18.md +++ b/docs/CORE_STABILIZATION_MERGE_LEDGER_2026-08-18.md @@ -38,6 +38,8 @@ is `~/Code/.venv`. | uCore Vue production build | passed; existing chunk-size warnings only | | uCore changed-file Ruff gate | passed | | uCore home-path policy | 4 passed | +| uCore planning governance | passed | +| uCore documentation and knowledge route contracts | passed | | uCore self-hosted MCP bridge | TypeScript build passed; diagnostics healthy | | uFlow | 4 passed | | uKnowledge | 10 passed, including Public read-only and traversal controls | @@ -72,6 +74,10 @@ complete table above from clean `main` checkouts before tagging or releasing. - Confirm generated files, credentials, vault contents and local runtime state are absent from every diff. +The final drift audit also removed the tracked Cline rules, legacy Tasker/devlog +state, and their duplicate writer skills. `brain_sync` now owns private memory +only; durable tasks remain exclusively under uFlow. + ## Local-worktree note `uCore/backend/tests/test_skill_registry_authorization.py` has a local import-order diff --git a/docs/README.md b/docs/README.md index 6bb1322c..651d3fe4 100644 --- a/docs/README.md +++ b/docs/README.md @@ -62,8 +62,7 @@ ## Active Task / Planning Trackers -- `.tasker.dev-flow.yaml` — canonical active task list -- `.tasker/README.md` — tasker directory guide +- `uFlow` — canonical workflow and task owner (`$UDOS_HOME/flow/tasks`) - `docs/CONSOLIDATION_PLAN.md` — docs consolidation tracker (kept for reference) ## Archive diff --git a/docs/specs/WISDOM_SYSTEM.md b/docs/specs/WISDOM_SYSTEM.md index 7a26f7bd..b396f712 100644 --- a/docs/specs/WISDOM_SYSTEM.md +++ b/docs/specs/WISDOM_SYSTEM.md @@ -13,14 +13,14 @@ The Wisdom system gives agents durable project context without committing person | Context | `CONTEXT.md` | tracked | Public project architecture and working conventions | | Wisdom template | `docs/templates/wisdom.md` | tracked | Sanitized seed for local installs | | Fieldnotes template | `docs/templates/fieldnotes.md` | tracked | Sanitized seed for local installs | -| Private wisdom | `~/.ucore/memory/uCore/wisdom.md` | untracked | Generated durable lessons and local synthesis | -| Private fieldnotes | `~/.ucore/memory/uCore/fieldnotes.md` | untracked | Optional developer notebook | +| Private wisdom | `$UDOS_HOME/memory/uCore/wisdom.md` | untracked | Generated durable lessons and local synthesis | +| Private fieldnotes | `$UDOS_HOME/memory/uCore/fieldnotes.md` | untracked | Optional developer notebook | | Legacy local files | `wisdom.md`, `fieldnotes.md` | ignored | Local-only compatibility files if they already exist | ## Runtime Flow 1. `brain_sync` reads existing private wisdom, recent project changes, spool activity, test failure signals, and episodic entries. -2. `brain_sync` writes refreshed wisdom to `~/.ucore/memory/uCore/wisdom.md`. +2. `brain_sync` writes refreshed wisdom to `$UDOS_HOME/memory/uCore/wisdom.md`. 3. `attach_context` injects `CONTEXT.md` plus private wisdom when available. 4. `backup` includes private wisdom in local backups. 5. `tasker_ingest` appends durable lessons to private wisdom, not to the repository root. diff --git a/docs/templates/fieldnotes.md b/docs/templates/fieldnotes.md index 9cff508e..c751c5c6 100644 --- a/docs/templates/fieldnotes.md +++ b/docs/templates/fieldnotes.md @@ -9,7 +9,7 @@ Use fieldnotes for optional local observations that are useful during developmen ## Local Path -`~/.ucore/memory/uCore/fieldnotes.md` +`~/Code/.udos/memory/uCore/fieldnotes.md` (or `$UDOS_HOME/memory/uCore/fieldnotes.md`) ## Promotion Rule diff --git a/docs/templates/wisdom.md b/docs/templates/wisdom.md index e18cc7b1..928bd89b 100644 --- a/docs/templates/wisdom.md +++ b/docs/templates/wisdom.md @@ -9,7 +9,7 @@ Status: Seed template for local project wisdom ## Memory Architecture - Public repo: this template and documentation only. -- Private local state: `~/.ucore/memory/uCore/wisdom.md`. +- Private local state: `~/Code/.udos/memory/uCore/wisdom.md` (or `$UDOS_HOME/memory/uCore/wisdom.md`). - Runtime integration: `brain_sync` refreshes private wisdom; `attach_context` injects it with `CONTEXT.md` when available. ## Synthesis Inputs diff --git a/seeds/ecosystem-registry.json b/seeds/ecosystem-registry.json index c047e9a4..f12d3fc1 100644 --- a/seeds/ecosystem-registry.json +++ b/seeds/ecosystem-registry.json @@ -1,1699 +1,1281 @@ { "success": true, - "action": "assess", + "action": "report", "ecosystem": { - "skills": [ - { - "file": "backend/app/skills/builtin/ask_vault.py", - "name": "Ask Vault", - "skill_id": "ask_vault", - "category": "knowledge", - "description": "", - "params": [], - "timeout": 30, - "requires_confirmation": false, - "health": "working", - "issues": [] - }, - { - "file": "backend/app/skills/builtin/attach_context.py", - "name": "Attach Context", - "skill_id": "attach_context", - "category": "assist", - "description": "Output format: system_prompt, raw, or markdown_block", - "params": [ - { - "name": "project", - "type": "string", - "description": "Output format: system_prompt, raw, or markdown_block" - }, - { - "name": "include_wisdom", - "type": "boolean", - "description": "Include private project wisdom alongside CONTEXT.md when available" - } - ], - "timeout": 10, - "requires_confirmation": false, - "health": "working", - "issues": [] - }, - { - "file": "backend/app/skills/builtin/backup.py", - "name": "Backup Data", - "skill_id": "backup", - "category": "maintenance", - "description": "Backup uCore database, config, secrets, private wisdom, and user data with retention management", - "params": [], - "timeout": 120, - "requires_confirmation": true, - "health": "working", - "issues": [] - }, - { - "file": "backend/app/skills/builtin/brain_sync.py", - "name": "Brain Sync", - "skill_id": "brain_sync", - "category": "assist", - "description": "Synthesize recent project changes, spool activity, and vault changes into private wisdom. Also provides tasker/devlog bridge actions (sync, read, write, archive, purge", - "params": [ - { - "name": "action", - "type": "string", - "description": "Include vault activity summary in private wisdom" - }, - { - "name": "include_test_failures", - "type": "boolean", - "description": "Include recent test failure signals in private wisdom" - }, - { - "name": "include_episodic", - "type": "boolean", - "description": "Include recent episodic log entries in private wisdom" - } - ], - "timeout": 30, - "requires_confirmation": true, - "health": "working", - "issues": [] - }, - { - "file": "backend/app/skills/builtin/clipboard_maintenance.py", - "name": "Clipboard Maintenance", - "skill_id": "clipboard_maintenance", - "category": "maintenance", - "description": "Capture current clipboard text and clean clipboard history", - "params": [], - "timeout": 30, - "requires_confirmation": true, - "health": "working", - "issues": [] - }, - { - "file": "backend/app/skills/builtin/docs_mirror_sync.py", - "name": "Docs Mirror Sync", - "skill_id": "docs_mirror_sync", - "category": "maintenance", - "description": "Pull uDos component docs from in-repo docs/ directories into the readable docs mirror.", - "params": [], - "timeout": 180, - "requires_confirmation": false, - "health": "working", - "issues": [] - }, - { - "file": "backend/app/skills/builtin/episodic_log.py", - "name": "Episodic Log", - "skill_id": "episodic_log", - "category": "memory", - "description": "Append a correction, lesson, or decision to the durable episodic log", - "params": [ - { - "name": "type", - "type": "string", - "description": "f\"Entry type: {', .join(sorted(ENTRY_TYPES" - }, - { - "name": "description", - "type": "string", - "description": "Short description of what happened or was learned" - }, - { - "name": "context", - "type": "string", - "description": "f\"Importance level: {', .join(sorted(SEVERITIES" - }, - { - "name": "tags", - "type": "string", - "description": "Comma-separated topic tags (or pass as a list" - } - ], - "timeout": 10, - "requires_confirmation": true, - "health": "working", - "issues": [] - }, - { - "file": "backend/app/skills/builtin/file_edit_enhancer.py", - "name": "File Edit Enhancer", - "skill_id": "file_edit_enhancer", - "category": "maintenance", - "description": "", - "params": [], - "timeout": 120, - "requires_confirmation": false, - "health": "working", - "issues": [] - }, - { - "file": "backend/app/skills/builtin/git_maintenance.py", - "name": "Git Maintenance", - "skill_id": "git_maintenance", - "category": "maintenance", - "description": "Detect and repair managed commits and branch merges. Scans workspace git repos for uncommitted changes, orphaned or stale branches, divergent branches, detached HEAD, interrupted operations, and no", - "params": [], - "timeout": 120, - "requires_confirmation": true, - "health": "working", - "issues": [] - }, - { - "file": "backend/app/skills/builtin/lint_fix.py", - "name": "Lint Fix", - "skill_id": "lint_fix", - "category": "developer", - "description": "", - "params": [], - "timeout": 120, - "requires_confirmation": false, - "health": "working", - "issues": [] - }, - { - "file": "backend/app/skills/builtin/route_task.py", - "name": "Route Task", - "skill_id": "route_task", - "category": "assist", - "description": "Route and optionally execute tasks to the best AI provider", - "params": [ - { - "name": "task", - "type": "string", - "description": "Context: small (<2K" - }, - { - "name": "risk_level", - "type": "string", - "description": "Target agent: auto (routing matrix" - } - ], - "timeout": 30, - "requires_confirmation": true, - "health": "working", - "issues": [] - }, - { - "file": "backend/app/skills/builtin/skill_audit.py", - "name": "Skill Auditor (Smoke-Test)", - "skill_id": "skill-audit", - "category": "developer", - "description": "Discover, import, instantiate, and execute all builtin skills. Reports health status: working, untested, or broken.", - "params": [], - "timeout": 120, - "requires_confirmation": false, - "health": "working", - "issues": [] - }, - { - "file": "backend/app/skills/builtin/skill_autostart.py", - "name": "Auto-Start Health Check", - "skill_id": "autostart", - "category": "system", - "description": "", - "params": [], - "timeout": 30, - "requires_confirmation": false, - "health": "working", - "issues": [] - }, - { - "file": "backend/app/skills/builtin/skill_cline_invoke.py", - "name": "Cline Invoke", - "skill_id": "cline-invoke", - "category": "developer", - "description": "Invoke Cline CLI from uCore skills using positional prompt mode. Supports yolo (auto-approve true", - "params": [], - "timeout": 2, - "requires_confirmation": true, - "health": "working", - "issues": [] - }, - { - "file": "backend/app/skills/builtin/skill_dead_code_archiver.py", - "name": "Dead Code / Legacy Archiver", - "skill_id": "dead-code-archiver", - "category": "developer", - "description": "Identify unused code, legacy patterns, and archive findings. Generates migration notes and removal recommendations.", - "params": [], - "timeout": 180, - "requires_confirmation": false, - "health": "working", - "issues": [] - }, - { - "file": "backend/app/skills/builtin/skill_dev_destroy_rebuild.py", - "name": "Dev Destroy Rebuild", - "skill_id": "dev-destroy-rebuild", - "category": "system", - "description": "Safely destroy and rebuild Dev Mode components using template snapshots with full SPOOL preservation.", - "params": [ - { - "name": "action", - "type": "string", - "description": "f\"Auto-saved before DESTROY of {', .join(targets" - } - ], - "timeout": 120, - "requires_confirmation": true, - "health": "working", - "issues": [] - }, - { - "file": "backend/app/skills/builtin/skill_dev_mode_executor.py", - "name": "Dev Mode Executor", - "skill_id": "dev-mode-executor", - "category": "developer", - "description": "Unified agentic orchestration pipeline. Chains analyze \u2192 route \u2192 consensus \u2192 execute \u2192 review \u2192 log.", - "params": [ - { - "name": "task_uid", - "type": "string", - "description": "Execution mode: auto (select best" - } - ], - "timeout": 600, - "requires_confirmation": true, - "health": "working", - "issues": [] - }, - { - "file": "backend/app/skills/builtin/skill_devlog_mcp.py", - "name": "Devlog MCP", - "skill_id": "devlog_mcp", - "category": "workflow", - "description": "", - "params": [], - "timeout": 60, - "requires_confirmation": false, - "health": "working", - "issues": [] - }, - { - "file": "backend/app/skills/builtin/skill_docs_roundup.py", - "name": "Docs Roundup", - "skill_id": "docs_roundup", - "category": "workflow", - "description": "End-of-dev-round docs automation: archive completed tasks, update devlog, organise docs, update/archive specs, plan new docs", - "params": [], - "timeout": 180, - "requires_confirmation": true, - "health": "working", - "issues": [] - }, - { - "file": "backend/app/skills/builtin/skill_duplicate_detector.py", - "name": "Duplicate Code Detector", - "skill_id": "duplicate-detector", - "category": "developer", - "description": "Find duplicate code patterns across Python/JS/TS files. Reports similarity scores and removal recommendations. Also detects $VARIABLE consolidation and script duplication.", - "params": [], - "timeout": 180, - "requires_confirmation": false, - "health": "working", - "issues": [] - }, - { - "file": "backend/app/skills/builtin/skill_ecosystem_audit.py", - "name": "Ecosystem Auditor v1", - "skill_id": "ecosystem-audit", - "category": "developer", - "description": "Comprehensive ecosystem audit: skills, paths, variables, secrets, MCP servers, routes, runtimes. Generates ecosystem-registry.json.", - "params": [ - { - "name": "action", - "type": "string", - "description": "Action: audit-skills', audit-routes', audit-secrets', audit-variables', audit-mcp', audit-paths', audit-runtimes', re" - } - ], - "timeout": 180, - "requires_confirmation": true, - "health": "working", - "issues": [] - }, - { - "file": "backend/app/skills/builtin/skill_enhancement_planner.py", - "name": "Enhancement Planner", - "skill_id": "enhancement-planner", - "category": "developer", - "description": "Bridges ecosystem audits to actionable .tasker items. Reads audit reports, groups gaps by area, and generates prioritized enhancement tasks.", - "params": [], - "timeout": 60, - "requires_confirmation": false, - "health": "working", - "issues": [] - }, - { - "file": "backend/app/skills/builtin/skill_gh_workflow_bridge.py", - "name": "GitHub Workflow Bridge", - "skill_id": "gh-workflow-bridge", - "category": "developer", - "description": "Bridge tasks to GitHub Actions/CLI. Trigger CI, create PRs, run workflows.", - "params": [ - { - "name": "action", - "type": "string", - "description": "GitHub action: trigger-ci', create-pr', run-workflow', or status" - }, - { - "name": "repo", - "type": "string", - "description": "GitHub repo in owner/repo format (default: uDosGo/uCore" - } - ], - "timeout": 2, - "requires_confirmation": true, - "health": "working", - "issues": [] - }, - { - "file": "backend/app/skills/builtin/skill_hardcoded_path_detector.py", - "name": "Hardcoded Path Detector", - "skill_id": "hardcoded-path-detector", - "category": "developer", - "description": "Find hardcoded paths in Python/JS/TS files. Reports absolute paths, environment variable references, and platform-specific paths that should be configurable.", - "params": [], - "timeout": 60, - "requires_confirmation": false, - "health": "working", - "issues": [] - }, - { - "file": "backend/app/skills/builtin/skill_hivemind_consensus.py", - "name": "Hivemind Consensus", - "skill_id": "hivemind-consensus", - "category": "assist", - "description": "Trigger multi-model deliberation via Hivemind server. Calls 3+ models in parallel, returns weighted consensus.", - "params": [ - { - "name": "task", - "type": "string", - "description": "Consensus mode: majority', unanimous', weighted', or deliberative" - }, - { - "name": "models", - "type": "string", - "description": "Comma-separated model list. Empty = default set (architect + dev agent models" - } - ], - "timeout": 120, - "requires_confirmation": false, - "health": "working", - "issues": [] - }, - { - "file": "backend/app/skills/builtin/skill_mcp_self_heal.py", - "name": "MCP Self-Heal", - "skill_id": "mcp_self_heal", - "category": "maintenance", - "description": "", - "params": [], - "timeout": 30, - "requires_confirmation": false, - "health": "working", - "issues": [] - }, - { - "file": "backend/app/skills/builtin/skill_modularisation_planner.py", - "name": "Modularisation Planner", - "skill_id": "modularisation-planner", - "category": "developer", - "description": "Assess large scripts (>1000 lines", - "params": [], - "timeout": 180, - "requires_confirmation": false, - "health": "working", - "issues": [] - }, - { - "file": "backend/app/skills/builtin/skill_nuggets_and_spool.py", - "name": "SPOOL Archive", - "skill_id": "spool_archive", - "category": "maintenance", - "description": "", - "params": [], - "timeout": 30, - "requires_confirmation": false, - "health": "working", - "issues": [] - }, - { - "file": "backend/app/skills/builtin/skill_roundtable_dispatch.py", - "name": "Roundtable Dispatch", - "skill_id": "roundtable-dispatch", - "category": "assist", - "description": "Dispatch tasks to parallel specialized agents via Roundtable. Collects and aggregates results.", - "params": [ - { - "name": "task", - "type": "string", - "description": "Comma-separated agent list or auto for routing. Options: architect, dev, reviewer, debugger, docgen, gridsmith-dev" + "skills": { + "total": 36, + "items": [ + { + "file": "backend/app/skills/builtin/ask_vault.py", + "name": "Ask Vault", + "skill_id": "ask_vault", + "category": "knowledge", + "description": "", + "params": [], + "timeout": 30, + "requires_confirmation": false + }, + { + "file": "backend/app/skills/builtin/attach_context.py", + "name": "Attach Context", + "skill_id": "attach_context", + "category": "assist", + "description": "Output format: system_prompt, raw, or markdown_block", + "params": [ + { + "name": "project", + "type": "string", + "description": "Output format: system_prompt, raw, or markdown_block" + }, + { + "name": "include_wisdom", + "type": "boolean", + "description": "Include private project wisdom alongside CONTEXT.md when available" + } + ], + "timeout": 10, + "requires_confirmation": false + }, + { + "file": "backend/app/skills/builtin/backup.py", + "name": "Backup Data", + "skill_id": "backup", + "category": "maintenance", + "description": "Backup uCore database, config, secrets, private wisdom, and user data with retention management", + "params": [], + "timeout": 120, + "requires_confirmation": true + }, + { + "file": "backend/app/skills/builtin/brain_sync.py", + "name": "Brain Sync", + "skill_id": "brain_sync", + "category": "assist", + "description": "Synthesize recent project changes, spool activity, and vault changes into private wisdom.", + "params": [ + { + "name": "action", + "type": "string", + "description": "Include vault activity summary in private wisdom" + }, + { + "name": "include_test_failures", + "type": "boolean", + "description": "Include recent test failure signals in private wisdom" + }, + { + "name": "include_episodic", + "type": "boolean", + "description": "Include recent episodic log entries in private wisdom" + } + ], + "timeout": 30, + "requires_confirmation": true + }, + { + "file": "backend/app/skills/builtin/clipboard_maintenance.py", + "name": "Clipboard Maintenance", + "skill_id": "clipboard_maintenance", + "category": "maintenance", + "description": "Capture current clipboard text and clean clipboard history", + "params": [], + "timeout": 30, + "requires_confirmation": true + }, + { + "file": "backend/app/skills/builtin/docs_mirror_sync.py", + "name": "Docs Mirror Sync", + "skill_id": "docs_mirror_sync", + "category": "maintenance", + "description": "Pull uDos component docs from in-repo docs/ directories into the readable docs mirror.", + "params": [], + "timeout": 180, + "requires_confirmation": false + }, + { + "file": "backend/app/skills/builtin/episodic_log.py", + "name": "Episodic Log", + "skill_id": "episodic_log", + "category": "memory", + "description": "Append a correction, lesson, or decision to the durable episodic log", + "params": [ + { + "name": "type", + "type": "string", + "description": "f\"Entry type: {', .join(sorted(ENTRY_TYPES" + }, + { + "name": "description", + "type": "string", + "description": "Short description of what happened or was learned" + }, + { + "name": "context", + "type": "string", + "description": "f\"Importance level: {', .join(sorted(SEVERITIES" + }, + { + "name": "tags", + "type": "string", + "description": "Comma-separated topic tags (or pass as a list" + } + ], + "timeout": 10, + "requires_confirmation": true + }, + { + "file": "backend/app/skills/builtin/file_edit_enhancer.py", + "name": "File Edit Enhancer", + "skill_id": "file_edit_enhancer", + "category": "maintenance", + "description": "", + "params": [], + "timeout": 120, + "requires_confirmation": false + }, + { + "file": "backend/app/skills/builtin/git_maintenance.py", + "name": "Git Maintenance", + "skill_id": "git_maintenance", + "category": "maintenance", + "description": "Detect and repair managed commits and branch merges. Scans workspace git repos for uncommitted changes, orphaned or stale branches, divergent branches, detached HEAD, interrupted operations, and no", + "params": [], + "timeout": 120, + "requires_confirmation": true + }, + { + "file": "backend/app/skills/builtin/lint_fix.py", + "name": "Lint Fix", + "skill_id": "lint_fix", + "category": "developer", + "description": "", + "params": [], + "timeout": 120, + "requires_confirmation": false + }, + { + "file": "backend/app/skills/builtin/route_task.py", + "name": "Route Task", + "skill_id": "route_task", + "category": "assist", + "description": "Route and optionally execute tasks to the best AI provider", + "params": [ + { + "name": "task", + "type": "string", + "description": "Context: small (<2K" + }, + { + "name": "risk_level", + "type": "string", + "description": "Target agent: auto (routing matrix" + } + ], + "timeout": 30, + "requires_confirmation": true + }, + { + "file": "backend/app/skills/builtin/skill_audit.py", + "name": "Skill Auditor (Smoke-Test)", + "skill_id": "skill-audit", + "category": "developer", + "description": "Discover, import, instantiate, and execute all builtin skills. Reports health status: working, untested, or broken.", + "params": [], + "timeout": 120, + "requires_confirmation": false + }, + { + "file": "backend/app/skills/builtin/skill_autostart.py", + "name": "Auto-Start Health Check", + "skill_id": "autostart", + "category": "system", + "description": "", + "params": [], + "timeout": 30, + "requires_confirmation": false + }, + { + "file": "backend/app/skills/builtin/skill_dead_code_archiver.py", + "name": "Dead Code / Legacy Archiver", + "skill_id": "dead-code-archiver", + "category": "developer", + "description": "Identify unused code, legacy patterns, and archive findings. Generates migration notes and removal recommendations.", + "params": [], + "timeout": 180, + "requires_confirmation": false + }, + { + "file": "backend/app/skills/builtin/skill_dev_destroy_rebuild.py", + "name": "Dev Destroy Rebuild", + "skill_id": "dev-destroy-rebuild", + "category": "system", + "description": "Safely destroy and rebuild Dev Mode components using template snapshots with full SPOOL preservation.", + "params": [ + { + "name": "action", + "type": "string", + "description": "f\"Auto-saved before DESTROY of {', .join(targets" + } + ], + "timeout": 120, + "requires_confirmation": true + }, + { + "file": "backend/app/skills/builtin/skill_dev_mode_executor.py", + "name": "Dev Mode Executor", + "skill_id": "dev-mode-executor", + "category": "developer", + "description": "Unified agentic orchestration pipeline. Chains analyze \u2192 route \u2192 consensus \u2192 execute \u2192 review \u2192 log.", + "params": [ + { + "name": "task_uid", + "type": "string", + "description": "Execution mode: auto (select best" + } + ], + "timeout": 600, + "requires_confirmation": true + }, + { + "file": "backend/app/skills/builtin/skill_duplicate_detector.py", + "name": "Duplicate Code Detector", + "skill_id": "duplicate-detector", + "category": "developer", + "description": "Find duplicate code patterns across Python/JS/TS files. Reports similarity scores and removal recommendations. Also detects $VARIABLE consolidation and script duplication.", + "params": [], + "timeout": 180, + "requires_confirmation": false + }, + { + "file": "backend/app/skills/builtin/skill_ecosystem_audit.py", + "name": "Ecosystem Auditor v1", + "skill_id": "ecosystem-audit", + "category": "developer", + "description": "Comprehensive ecosystem audit: skills, paths, variables, secrets, MCP servers, routes, runtimes. Generates ecosystem-registry.json.", + "params": [ + { + "name": "action", + "type": "string", + "description": "Action: audit-skills', audit-routes', audit-secrets', audit-variables', audit-mcp', audit-paths', audit-runtimes', re" + } + ], + "timeout": 180, + "requires_confirmation": true + }, + { + "file": "backend/app/skills/builtin/skill_enhancement_planner.py", + "name": "Enhancement Planner", + "skill_id": "enhancement-planner", + "category": "developer", + "description": "Bridges ecosystem audits to actionable .tasker items. Reads audit reports, groups gaps by area, and generates prioritized enhancement tasks.", + "params": [], + "timeout": 60, + "requires_confirmation": false + }, + { + "file": "backend/app/skills/builtin/skill_gh_workflow_bridge.py", + "name": "GitHub Workflow Bridge", + "skill_id": "gh-workflow-bridge", + "category": "developer", + "description": "Bridge tasks to GitHub Actions/CLI. Trigger CI, create PRs, run workflows.", + "params": [ + { + "name": "action", + "type": "string", + "description": "GitHub action: trigger-ci', create-pr', run-workflow', or status" + }, + { + "name": "repo", + "type": "string", + "description": "GitHub repo in owner/repo format (default: uDosGo/uCore" + } + ], + "timeout": 2, + "requires_confirmation": true + }, + { + "file": "backend/app/skills/builtin/skill_hardcoded_path_detector.py", + "name": "Hardcoded Path Detector", + "skill_id": "hardcoded-path-detector", + "category": "developer", + "description": "Find hardcoded paths in Python/JS/TS files. Reports absolute paths, environment variable references, and platform-specific paths that should be configurable.", + "params": [], + "timeout": 60, + "requires_confirmation": false + }, + { + "file": "backend/app/skills/builtin/skill_hivemind_consensus.py", + "name": "Hivemind Consensus", + "skill_id": "hivemind-consensus", + "category": "assist", + "description": "Trigger multi-model deliberation via Hivemind server. Calls 3+ models in parallel, returns weighted consensus.", + "params": [ + { + "name": "task", + "type": "string", + "description": "Consensus mode: majority', unanimous', weighted', or deliberative" + }, + { + "name": "models", + "type": "string", + "description": "Comma-separated model list. Empty = default set (architect + dev agent models" + } + ], + "timeout": 120, + "requires_confirmation": false + }, + { + "file": "backend/app/skills/builtin/skill_mcp_self_heal.py", + "name": "MCP Self-Heal", + "skill_id": "mcp_self_heal", + "category": "maintenance", + "description": "", + "params": [], + "timeout": 30, + "requires_confirmation": false + }, + { + "file": "backend/app/skills/builtin/skill_modularisation_planner.py", + "name": "Modularisation Planner", + "skill_id": "modularisation-planner", + "category": "developer", + "description": "Assess large scripts (>1000 lines", + "params": [], + "timeout": 180, + "requires_confirmation": false + }, + { + "file": "backend/app/skills/builtin/skill_nuggets_and_spool.py", + "name": "SPOOL Archive", + "skill_id": "spool_archive", + "category": "maintenance", + "description": "", + "params": [], + "timeout": 30, + "requires_confirmation": false + }, + { + "file": "backend/app/skills/builtin/skill_roundtable_dispatch.py", + "name": "Roundtable Dispatch", + "skill_id": "roundtable-dispatch", + "category": "assist", + "description": "Dispatch tasks to parallel specialized agents via Roundtable. Collects and aggregates results.", + "params": [ + { + "name": "task", + "type": "string", + "description": "Comma-separated agent list or auto for routing. Options: architect, dev, reviewer, debugger, docgen, gridsmith-dev" + }, + { + "name": "mode", + "type": "string", + "description": "Execution mode: parallel (concurrent" + } + ], + "timeout": 180, + "requires_confirmation": false + }, + { + "file": "backend/app/skills/builtin/skill_self_heal.py", + "name": "Recover Port Conflict", + "skill_id": "recover_port_conflict", + "category": "system", + "description": "", + "params": [], + "timeout": 30, + "requires_confirmation": false + }, + { + "file": "backend/app/skills/builtin/skill_surface_registry.py", + "name": "Surface Registry v1", + "skill_id": "surface-registry", + "category": "developer", + "description": "Discover, validate, scaffold, repair, and wire uCore surfaces. Autonomous maintenance for the surface ecosystem with backend runtime linking.", + "params": [ + { + "name": "action", + "type": "string", + "description": "Action: discover', validate', scaffold', repair', wire', report" + }, + { + "name": "target", + "type": "string", + "description": "Backend runtime to wire (e.g., dev_layer', feed_server" + } + ], + "timeout": 120, + "requires_confirmation": true + }, + { + "file": "backend/app/skills/builtin/skill_ucore_index.py", + "name": "uCore Index", + "skill_id": "ucore_index", + "category": "system", + "description": "", + "params": [], + "timeout": 30, + "requires_confirmation": false + }, + { + "file": "backend/app/skills/builtin/skill_vault_discovery.py", + "name": "Vault Discovery", + "skill_id": "vault_discovery", + "category": "system", + "description": "", + "params": [], + "timeout": 120, + "requires_confirmation": false + }, + { + "file": "backend/app/skills/builtin/tasker_sync.py", + "name": "Tasker Sync", + "skill_id": "tasker_sync", + "category": "workflow", + "description": "", + "params": [], + "timeout": 120, + "requires_confirmation": true + }, + { + "file": "backend/app/skills/builtin/usx_standard.py", + "name": "USX Standard Builder v3", + "skill_id": "usx-standard", + "category": "developer", + "description": "Audit/repair CSS to USX variable-only standard; validate token system; scaffold surfaces compliantly", + "params": [ + { + "name": "action", + "type": "string", + "description": "Action: audit', repair', validate-tokens', audit-surface', scaffold-surface', report" + }, + { + "name": "target", + "type": "string", + "description": "Optional: specific file, surface, or glob to target" + } + ], + "timeout": 120, + "requires_confirmation": true + }, + { + "file": "backend/app/skills/builtin/vault_sync.py", + "name": "Vault Sync", + "skill_id": "vault_sync", + "category": "maintenance", + "description": "", + "params": [], + "timeout": 300, + "requires_confirmation": true + }, + { + "file": "backend/app/skills/builtin/workflow_audit.py", + "name": "Workflow Audit", + "skill_id": "workflow_audit", + "category": "system", + "description": "", + "params": [], + "timeout": 60, + "requires_confirmation": false + }, + { + "file": "backend/app/skills/builtin/workflow_guard.py", + "name": "Workflow Guard", + "skill_id": "workflow_guard", + "category": "system", + "description": "", + "params": [], + "timeout": 30, + "requires_confirmation": false + }, + { + "file": "backend/app/skills/builtin/workflow_pause.py", + "name": "Workflow Pause", + "skill_id": "workflow_pause", + "category": "system", + "description": "", + "params": [], + "timeout": 30, + "requires_confirmation": true + } + ] + }, + "routes": { + "total": 98, + "items": [ + { + "method": "GET", + "path": "/api/agents", + "handler": "handle_list_agents" + }, + { + "method": "GET", + "path": "/api/agents/spec/capability/{capability}", + "handler": "handle_agents_spec_capability" + }, + { + "method": "GET", + "path": "/api/agents/spec/get/{agent_id}", + "handler": "handle_agents_spec_get" + }, + { + "method": "GET", + "path": "/api/agents/spec/list", + "handler": "handle_agents_spec_list" + }, + { + "method": "POST", + "path": "/api/agents/spec/plan", + "handler": "handle_agents_spec_plan" + }, + { + "method": "POST", + "path": "/api/agents/spec/route", + "handler": "handle_agents_spec_route" + }, + { + "method": "GET", + "path": "/api/agents/stats", + "handler": "handle_agents_stats" + }, + { + "method": "GET", + "path": "/api/autonomy/state", + "handler": "handle_autonomy_state" + }, + { + "method": "POST", + "path": "/api/binder/add", + "handler": "handle_binder_add" + }, + { + "method": "GET", + "path": "/api/binder/list", + "handler": "handle_binder_list" + }, + { + "method": "GET", + "path": "/api/binder/search", + "handler": "handle_binder_search" + }, + { + "method": "POST", + "path": "/api/budget/reload", + "handler": "handle_budget_reload" + }, + { + "method": "GET", + "path": "/api/budget/status", + "handler": "handle_budget_status" + }, + { + "method": "GET", + "path": "/api/budget/usage", + "handler": "handle_budget_usage" + }, + { + "method": "POST", + "path": "/api/chat", + "handler": "handle_chat" + }, + { + "method": "GET", + "path": "/api/chat/modes", + "handler": "handle_chat_modes" + }, + { + "method": "GET", + "path": "/api/chat/prompts", + "handler": "handle_chat_prompts" + }, + { + "method": "GET", + "path": "/api/config", + "handler": "handle_get_config" + }, + { + "method": "POST", + "path": "/api/cost/estimate", + "handler": "handle_cost_estimate" + }, + { + "method": "GET", + "path": "/api/cost/models", + "handler": "handle_cost_models" + }, + { + "method": "GET", + "path": "/api/cost/providers", + "handler": "handle_cost_providers" + }, + { + "method": "GET", + "path": "/api/cost/stats", + "handler": "handle_cost_stats" + }, + { + "method": "POST", + "path": "/api/developer/chat", + "handler": "handle_developer_chat" + }, + { + "method": "GET", + "path": "/api/developer/chat/stream", + "handler": "handle_developer_chat_stream" + }, + { + "method": "GET", + "path": "/api/developer/repos", + "handler": "handle_list_repos" + }, + { + "method": "POST", + "path": "/api/developer/repos/{repo_name}/commit", + "handler": "handle_commit_repo_files" + }, + { + "method": "GET", + "path": "/api/developer/repos/{repo_name}/diff", + "handler": "handle_get_repo_file_diff" + }, + { + "method": "PUT", + "path": "/api/developer/repos/{repo_name}/file-preview", + "handler": "handle_update_repo_file" + }, + { + "method": "GET", + "path": "/api/developer/repos/{repo_name}/files", + "handler": "handle_list_repo_files" + }, + { + "method": "GET", + "path": "/api/developer/repos/{repo_name}/review", + "handler": "handle_list_repo_review" + }, + { + "method": "POST", + "path": "/api/developer/repos/{repo_name}/stage", + "handler": "handle_stage_repo_file" + }, + { + "method": "GET", + "path": "/api/developer/repos/{repo_name}/status", + "handler": "handle_repo_status" + }, + { + "method": "POST", + "path": "/api/developer/repos/{repo_name}/unstage", + "handler": "handle_unstage_repo_file" + }, + { + "method": "POST", + "path": "/api/developer/start", + "handler": "handle_start_developer" + }, + { + "method": "GET", + "path": "/api/developer/status", + "handler": "handle_developer_status" + }, + { + "method": "POST", + "path": "/api/developer/stop", + "handler": "handle_stop_developer" + }, + { + "method": "POST", + "path": "/api/developer/workspace", + "handler": "handle_workspace_switch" + }, + { + "method": "GET", + "path": "/api/docker/ps", + "handler": "handle_docker_ps" + }, + { + "method": "POST", + "path": "/api/editor/save-to-binder", + "handler": "handle_save_to_binder" + }, + { + "method": "POST", + "path": "/api/editor/scrape-web", + "handler": "handle_scrape_web" + }, + { + "method": "POST", + "path": "/api/editor/summarize", + "handler": "handle_summarize" + }, + { + "method": "POST", + "path": "/api/exec", + "handler": "handle_exec" + }, + { + "method": "POST", + "path": "/api/extensions/announce", + "handler": "handle_extension_announce" + }, + { + "method": "GET", + "path": "/api/extensions/status", + "handler": "handle_extensions_status" + }, + { + "method": "GET", + "path": "/api/flow-router/analytics", + "handler": "handle_flow_router_analytics" + }, + { + "method": "GET", + "path": "/api/flow-router/history", + "handler": "handle_flow_router_history" + }, + { + "method": "POST", + "path": "/api/flow-router/route", + "handler": "handle_flow_router_route" + }, + { + "method": "POST", + "path": "/api/gridsmith/grid/create", + "handler": "handle_gridsmith_grid_create" + }, + { + "method": "POST", + "path": "/api/gridsmith/location/latlon-to-ucode", + "handler": "handle_gridsmith_latlon_to_ucode" + }, + { + "method": "POST", + "path": "/api/gridsmith/location/ucode-to-latlon", + "handler": "handle_gridsmith_ucode_to_latlon" + }, + { + "method": "GET", + "path": "/api/gridsmith/status", + "handler": "handle_gridsmith_status" + }, + { + "method": "GET", + "path": "/api/gridsmith/tools", + "handler": "handle_gridsmith_tools" + }, + { + "method": "POST", + "path": "/api/gridsmith/world/import-basic", + "handler": "handle_gridsmith_import_basic" + }, + { + "method": "POST", + "path": "/api/mcp/call", + "handler": "handle_mcp_call" + }, + { + "method": "GET", + "path": "/api/mcp/diagnostics", + "handler": "handle_mcp_diagnostics" + }, + { + "method": "GET", + "path": "/api/mcp/tools", + "handler": "handle_mcp_discover" + }, + { + "method": "GET", + "path": "/api/models", + "handler": "handle_models" + }, + { + "method": "GET", + "path": "/api/ollama/models/available", + "handler": "handle_ollama_models_available" + }, + { + "method": "GET", + "path": "/api/ollama/performance", + "handler": "handle_ollama_performance" + }, + { + "method": "GET", + "path": "/api/ollama/status", + "handler": "handle_ollama_status" + }, + { + "method": "POST", + "path": "/api/render", + "handler": "handle_render" + }, + { + "method": "POST", + "path": "/api/render/event", + "handler": "handle_publish_event" + }, + { + "method": "GET", + "path": "/api/render/stream", + "handler": "handle_stream" + }, + { + "method": "POST", + "path": "/api/research/enhance", + "handler": "handle_research_enhance" + }, + { + "method": "GET", + "path": "/api/research/list", + "handler": "handle_research_list" + }, + { + "method": "POST", + "path": "/api/research/process", + "handler": "handle_research_process" + }, + { + "method": "POST", + "path": "/api/research/start", + "handler": "handle_research_start" + }, + { + "method": "GET", + "path": "/api/research/status", + "handler": "handle_research_status" + }, + { + "method": "GET", + "path": "/api/research/stream/{job_id}", + "handler": "handle_research_stream" + }, + { + "method": "GET", + "path": "/api/research/vault-scan", + "handler": "handle_vault_scan" + }, + { + "method": "GET", + "path": "/api/secrets", + "handler": "handle_list_secrets" + }, + { + "method": "GET", + "path": "/api/secrets/audit", + "handler": "handle_secret_audit" + }, + { + "method": "GET", + "path": "/api/secrets/env", + "handler": "handle_list_env_vars" + }, + { + "method": "POST", + "path": "/api/secrets/export-env", + "handler": "handle_export_to_env" + }, + { + "method": "POST", + "path": "/api/secrets/import-env", + "handler": "handle_import_from_env" + }, + { + "method": "POST", + "path": "/api/secrets/sync-github", + "handler": "handle_sync_github" + }, + { + "method": "GET", + "path": "/api/secrets/{name}", + "handler": "handle_get_secret" + }, + { + "method": "POST", + "path": "/api/secrets/{name}", + "handler": "handle_set_secret" + }, + { + "method": "DELETE", + "path": "/api/secrets/{name}", + "handler": "handle_delete_secret" + }, + { + "method": "GET", + "path": "/api/skills", + "handler": "handle_list_skills" + }, + { + "method": "GET", + "path": "/api/skills/health", + "handler": "handle_health" + }, + { + "method": "POST", + "path": "/api/skills/run", + "handler": "handle_run_named_skill" + }, + { + "method": "GET", + "path": "/api/skills/state", + "handler": "handle_skill_state" + }, + { + "method": "POST", + "path": "/api/skills/{skill_id}/run", + "handler": "handle_run_skill" + }, + { + "method": "GET", + "path": "/api/skills/{skill_id}/source", + "handler": "handle_skill_source" + }, + { + "method": "GET", + "path": "/api/system", + "handler": "system_info_handler" + }, + { + "method": "GET", + "path": "/api/system/maintenance", + "handler": "maintenance_status_handler" + }, + { + "method": "GET", + "path": "/api/system/workflow", + "handler": "workflow_status_handler" + }, + { + "method": "GET", + "path": "/api/tools", + "handler": "handle_list_tools" + }, + { + "method": "GET", + "path": "/api/tools/{tool_id}/status", + "handler": "handle_tool_status" + }, + { + "method": "POST", + "path": "/api/toon/clear", + "handler": "handle_toon_clear" + }, + { + "method": "POST", + "path": "/api/toon/encode", + "handler": "handle_toon_encode" + }, + { + "method": "GET", + "path": "/api/toon/stats", + "handler": "handle_toon_stats" + }, + { + "method": "GET", + "path": "/api/variables", + "handler": "handle_get_variables" + }, + { + "method": "GET", + "path": "/api/variables/install", + "handler": "handle_get_install_variables" + }, + { + "method": "GET", + "path": "/api/variables/user", + "handler": "handle_get_user_variables" + }, + { + "method": "PUT", + "path": "/api/variables/user", + "handler": "handle_update_user_variables" + }, + { + "method": "GET", + "path": "/api/workflow/tasks", + "handler": "handle_workflow_tasks" + } + ] + }, + "secrets": { + "total": 0, + "items": [] + }, + "variables": { + "total": 6, + "items": [ + { + "scope": "user", + "file": "~/.local/share/udos/Vault/variables/user.yaml", + "description": "User-level persistent variables", + "examples": [ + "theme", + "editor_font_size", + "last_world" + ] + }, + { + "scope": "global", + "file": "~/.local/share/udos/Vault/variables/global.yaml", + "description": "System-wide variables", + "examples": [ + "runtime_version", + "engine" + ] + }, + { + "scope": "snack", + "file": "snack manifest (per-container)", + "description": "Per-snack container state", + "examples": [ + "level", + "player_hp" + ] + }, + { + "scope": "system", + "file": "memory only (not persisted)", + "description": "Runtime-only state", + "examples": [ + "pid", + "uptime_seconds" + ] + }, + { + "scope": "user", + "source": "variables_api.py", + "description": "Exposed via /api/variables/user", + "examples": [] + }, + { + "scope": "install", + "source": "variables_api.py", + "description": "Exposed via /api/variables/install", + "examples": [] + } + ] + }, + "mcp_servers": { + "total": 2, + "items": [ + { + "name": "ucore-bridge", + "type": "stdio", + "file": "backend/app/mcp/mcp_bridge/index.ts", + "command": "node backend/app/mcp/mcp_bridge/build/index.js", + "cwd": "/Users/fredbook/Code/uCore", + "source": "uCore self-hosted bridge" + }, + { + "name": "hivemind", + "file": "backend/app/mcp/hivemind_server.py", + "source": "backend auto-discovery", + "command": "python3 -m app.mcp.hivemind_server", + "cwd": "/Users/fredbook/Code/uCore/backend/app" + } + ] + }, + "paths": { + "total": 15, + "items": [ + { + "path": "config", + "type": "config", + "description": "Configuration directory" + }, + { + "path": "seeds", + "type": "config", + "description": "Configuration directory" + }, + { + "path": "docs", + "type": "config", + "description": "Configuration directory" + }, + { + "path": "backend/config", + "type": "config", + "description": "Configuration directory" + }, + { + "path": "frontend-vue/src/styles", + "type": "config", + "description": "Configuration directory" + }, + { + "path": "/Users/fredbook/Code/.udos", + "type": "runtime", + "description": "User runtime path" + }, + { + "path": "/Users/fredbook/Code/.udos/config", + "type": "runtime", + "description": "User runtime path" + }, + { + "path": "/Users/fredbook/Code/.udos/logs", + "type": "runtime", + "description": "User runtime path" + }, + { + "path": "/Users/fredbook/Vault", + "type": "runtime", + "description": "User runtime path" + }, + { + "path": "/Users/fredbook/Shared", + "type": "runtime", + "description": "User runtime path" + }, + { + "path": "/Users/fredbook/Public", + "type": "runtime", + "description": "User runtime path" + }, + { + "path": "backend/app/skills/builtin/", + "type": "skills", + "description": "Builtin skills directory" + }, + { + "path": "backend/app/api/", + "type": "api", + "description": "REST API handlers" + }, + { + "path": "backend/app/mcp/", + "type": "mcp", + "description": "MCP servers directory" + }, + { + "path": "backend/app/services/", + "type": "services", + "description": "Backend service modules" + } + ] + }, + "runtimes": { + "total": 7, + "items": { + "dev_layer": { + "file": "backend/app/services/dev_layer.py", + "endpoints": [], + "variables": { + "mode": "DevMode(raw.lower())" }, - { - "name": "mode", - "type": "string", - "description": "Execution mode: parallel (concurrent" - } - ], - "timeout": 180, - "requires_confirmation": false, - "health": "working", - "issues": [] - }, - { - "file": "backend/app/skills/builtin/skill_self_heal.py", - "name": "Recover Port Conflict", - "skill_id": "recover_port_conflict", - "category": "system", - "description": "", - "params": [], - "timeout": 30, - "requires_confirmation": false, - "health": "working", - "issues": [] - }, - { - "file": "backend/app/skills/builtin/skill_surface_registry.py", - "name": "Surface Registry v1", - "skill_id": "surface-registry", - "category": "developer", - "description": "Discover, validate, scaffold, repair, and wire uCore surfaces. Autonomous maintenance for the surface ecosystem with backend runtime linking.", - "params": [ - { - "name": "action", - "type": "string", - "description": "Action: discover', validate', scaffold', repair', wire', report" + "commands": [ + "get_dev_layer", + "get_status", + "reset_dev_layer", + "toggle" + ] + }, + "feed_server": { + "file": "backend/app/mcp/feed/feed_server.py", + "endpoints": [], + "variables": {}, + "commands": [ + "close", + "ingest_activity", + "link_task_to_activity", + "query_feed", + "suggest_binders" + ] + }, + "feed_consumer": { + "file": "backend/app/services/feed_consumer.py", + "endpoints": [], + "variables": {}, + "commands": [ + "consume_activity" + ] + }, + "hivemind_server": { + "file": "backend/app/mcp/hivemind_server.py", + "endpoints": [], + "variables": { + "registry": "SpecializedAgentRegistry(agents_config)", + "consensus": "ConsensusEngine()", + "llm_router": "LLMRouter(llm_config)", + "app": "web.Application()" }, - { - "name": "target", - "type": "string", - "description": "Backend runtime to wire (e.g., dev_layer', feed_server" - } - ], - "timeout": 120, - "requires_confirmation": true, - "health": "working", - "issues": [] - }, - { - "file": "backend/app/skills/builtin/skill_ucore_index.py", - "name": "uCore Index", - "skill_id": "ucore_index", - "category": "system", - "description": "", - "params": [], - "timeout": 30, - "requires_confirmation": false, - "health": "working", - "issues": [] - }, - { - "file": "backend/app/skills/builtin/skill_vault_discovery.py", - "name": "Vault Discovery", - "skill_id": "vault_discovery", - "category": "system", - "description": "", - "params": [], - "timeout": 120, - "requires_confirmation": false, - "health": "working", - "issues": [] - }, - { - "file": "backend/app/skills/builtin/tasker_sync.py", - "name": "Tasker Sync", - "skill_id": "tasker_sync", - "category": "workflow", - "description": "", - "params": [], - "timeout": 120, - "requires_confirmation": true, - "health": "working", - "issues": [] - }, - { - "file": "backend/app/skills/builtin/usx_standard.py", - "name": "USX Standard Builder v3", - "skill_id": "usx-standard", - "category": "developer", - "description": "Audit/repair CSS to USX variable-only standard; validate token system; scaffold surfaces compliantly", - "params": [ - { - "name": "action", - "type": "string", - "description": "Action: audit', repair', validate-tokens', audit-surface', scaffold-surface', report" + "commands": [ + "handle_chat", + "handle_cost_status", + "handle_get_agent", + "handle_get_workflow", + "handle_health", + "handle_list_agents", + "handle_list_proposals", + "handle_list_workflows", + "handle_llm_health", + "handle_proposal_status", + "handle_propose", + "handle_roundtable_status", + "handle_route_task", + "handle_run_workflow", + "handle_vote", + "main", + "make_node", + "node_fn" + ] + }, + "llm_router": { + "file": "backend/app/mcp/llm_router.py", + "endpoints": [ + "fallback", + "ollama", + "roundtable" + ], + "variables": { + "backends": "[]" }, - { - "name": "target", - "type": "string", - "description": "Optional: specific file, surface, or glob to target" - } - ], - "timeout": 120, - "requires_confirmation": true, - "health": "working", - "issues": [] - }, - { - "file": "backend/app/skills/builtin/vault_sync.py", - "name": "Vault Sync", - "skill_id": "vault_sync", - "category": "maintenance", - "description": "", - "params": [], - "timeout": 300, - "requires_confirmation": true, - "health": "working", - "issues": [] - }, - { - "file": "backend/app/skills/builtin/workflow_audit.py", - "name": "Workflow Audit", - "skill_id": "workflow_audit", - "category": "system", - "description": "", - "params": [], - "timeout": 60, - "requires_confirmation": false, - "health": "working", - "issues": [] - }, - { - "file": "backend/app/skills/builtin/workflow_guard.py", - "name": "Workflow Guard", - "skill_id": "workflow_guard", - "category": "system", - "description": "", - "params": [], - "timeout": 30, - "requires_confirmation": false, - "health": "working", - "issues": [] - }, - { - "file": "backend/app/skills/builtin/workflow_pause.py", - "name": "Workflow Pause", - "skill_id": "workflow_pause", - "category": "system", - "description": "", - "params": [], - "timeout": 30, - "requires_confirmation": true, - "health": "working", - "issues": [] - } - ], - "mcp_servers": [ - { - "name": "ucore-bridge", - "type": "stdio", - "command": "node", - "args": [ - "../uDev/mcp-bridge/build/index.js" - ], - "cwd": "${workspaceFolder}", - "env": { - "UCORE_URL": "http://localhost:8484" - }, - "disabled": false, - "source": ".vscode/mcp.json", - "health": "working", - "issues": [] - }, - { - "name": "hivemind", - "file": "backend/app/mcp/hivemind_server.py", - "source": "backend auto-discovery", - "command": "python3 -m app.mcp.hivemind_server", - "cwd": "/Users/fredbook/Code/uCore/backend/app", - "health": "working", - "issues": [] - } - ], - "runtimes": [ - { - "name": "dev_layer", - "file": "backend/app/services/dev_layer.py", - "endpoints": [], - "variables": { - "mode": "DevMode(raw.lower())" - }, - "commands": [ - "get_dev_layer", - "get_status", - "reset_dev_layer", - "toggle" - ], - "health": "working", - "issues": [] - }, - { - "name": "feed_server", - "file": "backend/app/mcp/feed/feed_server.py", - "endpoints": [], - "variables": {}, - "commands": [ - "close", - "ingest_activity", - "link_task_to_activity", - "query_feed", - "suggest_binders" - ], - "health": "working", - "issues": [] - }, - { - "name": "feed_consumer", - "file": "backend/app/services/feed_consumer.py", - "endpoints": [], - "variables": {}, - "commands": [ - "consume_activity" - ], - "health": "working", - "issues": [] - }, - { - "name": "hivemind_server", - "file": "backend/app/mcp/hivemind_server.py", - "endpoints": [], - "variables": { - "registry": "SpecializedAgentRegistry(agents_config)", - "consensus": "ConsensusEngine()", - "llm_router": "LLMRouter(llm_config)", - "app": "web.Application()" - }, - "commands": [ - "handle_chat", - "handle_cost_status", - "handle_get_agent", - "handle_get_workflow", - "handle_health", - "handle_list_agents", - "handle_list_proposals", - "handle_list_workflows", - "handle_llm_health", - "handle_proposal_status", - "handle_propose", - "handle_roundtable_status", - "handle_route_task", - "handle_run_workflow", - "handle_vote", - "main", - "make_node", - "node_fn" - ], - "health": "working", - "issues": [] - }, - { - "name": "llm_router", - "file": "backend/app/mcp/llm_router.py", - "endpoints": [ - "fallback", - "ollama", - "roundtable" - ], - "variables": { - "backends": "[]" - }, - "commands": [ - "chat_completion", - "close", - "health_check" - ], - "health": "working", - "issues": [] - }, - { - "name": "model_pricing", - "file": "backend/app/services/model_pricing.py", - "endpoints": [], - "variables": {}, - "commands": [ - "cheapest_model", - "cost_tiers", - "estimate_cost", - "summarize_tiers" - ], - "health": "working", - "issues": [] - }, - { - "name": "template_manager", - "file": "backend/app/services/template_manager.py", - "endpoints": [], - "variables": {}, - "commands": [ - "count_by_tier", - "create_template", - "delete_template", - "find_template", - "fork_template", - "from_dict", - "get_path", - "get_template_manager", - "has_cookiecutter", - "list_templates", - "reset_template_manager", - "scaffold_from_template", - "summary", - "to_dict" - ], - "health": "working", - "issues": [] - }, - { - "name": "tasker_ingest", - "file": "backend/app/mcp/tasker_ingest.py", - "endpoints": [ - "action", - "auto_commit", - "checklist", - "dry_run", - "lessons", - "outcome", - "session_id", - "summary", - "tasker_file", - "workspace" - ], - "variables": {}, - "commands": [ - "run" - ], - "health": "working", - "issues": [] - } - ], - "routes": [ - { - "method": "GET", - "path": "/api/agents", - "handler": "handle_list_agents", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/agents/spec/capability/{capability}", - "handler": "handle_agents_spec_capability", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/agents/spec/get/{agent_id}", - "handler": "handle_agents_spec_get", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/agents/spec/list", - "handler": "handle_agents_spec_list", - "health": "working", - "issues": [] - }, - { - "method": "POST", - "path": "/api/agents/spec/plan", - "handler": "handle_agents_spec_plan", - "health": "working", - "issues": [] - }, - { - "method": "POST", - "path": "/api/agents/spec/route", - "handler": "handle_agents_spec_route", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/agents/stats", - "handler": "handle_agents_stats", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/autonomy/state", - "handler": "handle_autonomy_state", - "health": "working", - "issues": [] - }, - { - "method": "POST", - "path": "/api/binder/add", - "handler": "handle_binder_add", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/binder/list", - "handler": "handle_binder_list", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/binder/search", - "handler": "handle_binder_search", - "health": "working", - "issues": [] - }, - { - "method": "POST", - "path": "/api/budget/reload", - "handler": "handle_budget_reload", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/budget/status", - "handler": "handle_budget_status", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/budget/usage", - "handler": "handle_budget_usage", - "health": "working", - "issues": [] - }, - { - "method": "POST", - "path": "/api/chat", - "handler": "handle_chat", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/chat/modes", - "handler": "handle_chat_modes", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/chat/prompts", - "handler": "handle_chat_prompts", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/config", - "handler": "handle_get_config", - "health": "working", - "issues": [] - }, - { - "method": "POST", - "path": "/api/cost/estimate", - "handler": "handle_cost_estimate", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/cost/models", - "handler": "handle_cost_models", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/cost/providers", - "handler": "handle_cost_providers", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/cost/stats", - "handler": "handle_cost_stats", - "health": "working", - "issues": [] - }, - { - "method": "POST", - "path": "/api/developer/chat", - "handler": "handle_developer_chat", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/developer/chat/stream", - "handler": "handle_developer_chat_stream", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/developer/repos", - "handler": "handle_list_repos", - "health": "working", - "issues": [] - }, - { - "method": "POST", - "path": "/api/developer/repos/{repo_name}/commit", - "handler": "handle_commit_repo_files", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/developer/repos/{repo_name}/diff", - "handler": "handle_get_repo_file_diff", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/developer/repos/{repo_name}/file-preview", - "handler": "handle_get_repo_file_preview", - "health": "working", - "issues": [] - }, - { - "method": "PUT", - "path": "/api/developer/repos/{repo_name}/file-preview", - "handler": "handle_update_repo_file", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/developer/repos/{repo_name}/files", - "handler": "handle_list_repo_files", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/developer/repos/{repo_name}/review", - "handler": "handle_list_repo_review", - "health": "working", - "issues": [] - }, - { - "method": "POST", - "path": "/api/developer/repos/{repo_name}/stage", - "handler": "handle_stage_repo_file", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/developer/repos/{repo_name}/status", - "handler": "handle_repo_status", - "health": "working", - "issues": [] - }, - { - "method": "POST", - "path": "/api/developer/repos/{repo_name}/unstage", - "handler": "handle_unstage_repo_file", - "health": "working", - "issues": [] - }, - { - "method": "POST", - "path": "/api/developer/start", - "handler": "handle_start_developer", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/developer/status", - "handler": "handle_developer_status", - "health": "working", - "issues": [] - }, - { - "method": "POST", - "path": "/api/developer/stop", - "handler": "handle_stop_developer", - "health": "working", - "issues": [] - }, - { - "method": "POST", - "path": "/api/developer/workspace", - "handler": "handle_workspace_switch", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/docker/ps", - "handler": "handle_docker_ps", - "health": "working", - "issues": [] - }, - { - "method": "POST", - "path": "/api/editor/save-to-binder", - "handler": "handle_save_to_binder", - "health": "working", - "issues": [] - }, - { - "method": "POST", - "path": "/api/editor/scrape-web", - "handler": "handle_scrape_web", - "health": "working", - "issues": [] - }, - { - "method": "POST", - "path": "/api/editor/summarize", - "handler": "handle_summarize", - "health": "working", - "issues": [] - }, - { - "method": "POST", - "path": "/api/exec", - "handler": "handle_exec", - "health": "working", - "issues": [] - }, - { - "method": "POST", - "path": "/api/extensions/announce", - "handler": "handle_extension_announce", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/extensions/status", - "handler": "handle_extensions_status", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/flow-router/analytics", - "handler": "handle_flow_router_analytics", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/flow-router/history", - "handler": "handle_flow_router_history", - "health": "working", - "issues": [] - }, - { - "method": "POST", - "path": "/api/flow-router/route", - "handler": "handle_flow_router_route", - "health": "working", - "issues": [] - }, - { - "method": "POST", - "path": "/api/gridsmith/grid/create", - "handler": "handle_gridsmith_grid_create", - "health": "working", - "issues": [] - }, - { - "method": "POST", - "path": "/api/gridsmith/location/latlon-to-ucode", - "handler": "handle_gridsmith_latlon_to_ucode", - "health": "working", - "issues": [] - }, - { - "method": "POST", - "path": "/api/gridsmith/location/ucode-to-latlon", - "handler": "handle_gridsmith_ucode_to_latlon", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/gridsmith/status", - "handler": "handle_gridsmith_status", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/gridsmith/tools", - "handler": "handle_gridsmith_tools", - "health": "working", - "issues": [] - }, - { - "method": "POST", - "path": "/api/gridsmith/world/import-basic", - "handler": "handle_gridsmith_import_basic", - "health": "working", - "issues": [] - }, - { - "method": "POST", - "path": "/api/mcp/call", - "handler": "handle_mcp_call", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/mcp/diagnostics", - "handler": "handle_mcp_diagnostics", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/mcp/tools", - "handler": "handle_mcp_discover", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/models", - "handler": "handle_models", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/ollama/models/available", - "handler": "handle_ollama_models_available", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/ollama/performance", - "handler": "handle_ollama_performance", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/ollama/status", - "handler": "handle_ollama_status", - "health": "working", - "issues": [] - }, - { - "method": "POST", - "path": "/api/render", - "handler": "handle_render", - "health": "working", - "issues": [] - }, - { - "method": "POST", - "path": "/api/render/event", - "handler": "handle_publish_event", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/render/stream", - "handler": "handle_stream", - "health": "working", - "issues": [] - }, - { - "method": "POST", - "path": "/api/research/enhance", - "handler": "handle_research_enhance", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/research/list", - "handler": "handle_research_list", - "health": "working", - "issues": [] - }, - { - "method": "POST", - "path": "/api/research/process", - "handler": "handle_research_process", - "health": "working", - "issues": [] - }, - { - "method": "POST", - "path": "/api/research/start", - "handler": "handle_research_start", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/research/status", - "handler": "handle_research_status", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/research/stream/{job_id}", - "handler": "handle_research_stream", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/research/vault-scan", - "handler": "handle_vault_scan", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/secrets", - "handler": "handle_list_secrets", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/secrets/audit", - "handler": "handle_secret_audit", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/secrets/env", - "handler": "handle_list_env_vars", - "health": "working", - "issues": [] - }, - { - "method": "POST", - "path": "/api/secrets/export-env", - "handler": "handle_export_to_env", - "health": "working", - "issues": [] - }, - { - "method": "POST", - "path": "/api/secrets/import-env", - "handler": "handle_import_from_env", - "health": "working", - "issues": [] - }, - { - "method": "POST", - "path": "/api/secrets/sync-github", - "handler": "handle_sync_github", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/secrets/{name}", - "handler": "handle_get_secret", - "health": "working", - "issues": [] - }, - { - "method": "POST", - "path": "/api/secrets/{name}", - "handler": "handle_set_secret", - "health": "working", - "issues": [] - }, - { - "method": "DELETE", - "path": "/api/secrets/{name}", - "handler": "handle_delete_secret", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/skills", - "handler": "handle_list_skills", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/skills/health", - "handler": "handle_health", - "health": "working", - "issues": [] - }, - { - "method": "POST", - "path": "/api/skills/run", - "handler": "handle_run_named_skill", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/skills/state", - "handler": "handle_skill_state", - "health": "working", - "issues": [] - }, - { - "method": "POST", - "path": "/api/skills/{skill_id}/run", - "handler": "handle_run_skill", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/skills/{skill_id}/source", - "handler": "handle_skill_source", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/system", - "handler": "system_info_handler", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/system/maintenance", - "handler": "maintenance_status_handler", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/system/workflow", - "handler": "workflow_status_handler", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/tools", - "handler": "handle_list_tools", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/tools/{tool_id}/status", - "handler": "handle_tool_status", - "health": "working", - "issues": [] - }, - { - "method": "POST", - "path": "/api/toon/clear", - "handler": "handle_toon_clear", - "health": "working", - "issues": [] - }, - { - "method": "POST", - "path": "/api/toon/encode", - "handler": "handle_toon_encode", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/toon/stats", - "handler": "handle_toon_stats", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/variables", - "handler": "handle_get_variables", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/variables/install", - "handler": "handle_get_install_variables", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/variables/user", - "handler": "handle_get_user_variables", - "health": "working", - "issues": [] - }, - { - "method": "PUT", - "path": "/api/variables/user", - "handler": "handle_update_user_variables", - "health": "working", - "issues": [] - }, - { - "method": "GET", - "path": "/api/workflow/tasks", - "handler": "handle_workflow_tasks", - "health": "working", - "issues": [] - } - ], - "paths": [ - { - "path": "config", - "type": "config", - "description": "Configuration directory", - "health": "working", - "issues": [] - }, - { - "path": "seeds", - "type": "config", - "description": "Configuration directory", - "health": "working", - "issues": [] - }, - { - "path": "docs", - "type": "config", - "description": "Configuration directory", - "health": "working", - "issues": [] - }, - { - "path": "backend/config", - "type": "config", - "description": "Configuration directory", - "health": "working", - "issues": [] - }, - { - "path": "frontend-vue/src/styles", - "type": "config", - "description": "Configuration directory", - "health": "working", - "issues": [] - }, - { - "path": "~/.config/hivemind/.env", - "type": "runtime", - "description": "User runtime path", - "health": "working", - "issues": [] - }, - { - "path": "~/.cline/mcp_settings.json", - "type": "runtime", - "description": "User runtime path", - "health": "working", - "issues": [] - }, - { - "path": "~/.continue/config.yaml", - "type": "runtime", - "description": "User runtime path", - "health": "working", - "issues": [] - }, - { - "path": "~/.local/share/udos/Vault/", - "type": "runtime", - "description": "User runtime path", - "health": "working", - "issues": [] - }, - { - "path": "~/.local/share/udos/programs/", - "type": "runtime", - "description": "User runtime path", - "health": "working", - "issues": [] - }, - { - "path": "~/.local/share/udos/snacks/", - "type": "runtime", - "description": "User runtime path", - "health": "working", - "issues": [] - }, - { - "path": "backend/app/skills/builtin/", - "type": "skills", - "description": "Builtin skills directory", - "health": "working", - "issues": [] - }, - { - "path": "backend/app/api/", - "type": "api", - "description": "REST API handlers", - "health": "working", - "issues": [] - }, - { - "path": "backend/app/mcp/", - "type": "mcp", - "description": "MCP servers directory", - "health": "working", - "issues": [] - }, - { - "path": "backend/app/services/", - "type": "services", - "description": "Backend service modules", - "health": "working", - "issues": [] - } - ], - "secrets": [ - { - "key": "OPENROUTER_API_KEY", - "scope": "environment", - "store": "~/.config/hivemind/.env", - "description": "Models: glm-5.1, claude-opus-4.7, deepseek-v4-flash, qwen3.6-27b", - "health": "working", - "issues": [] - }, - { - "key": "OLLAMA_BASE_URL", - "scope": "environment", - "store": "~/.config/hivemind/.env", - "description": "Model: qwen2.5-coder:3b (run: ollama pull qwen2.5-coder:3b)", - "health": "working", - "issues": [] - }, - { - "key": "ANTHROPIC_API_KEY", - "scope": "environment", - "store": "~/.config/hivemind/.env", - "description": "", - "health": "working", - "issues": [] - }, - { - "key": "OPENAI_API_KEY", - "scope": "environment", - "store": "~/.config/hivemind/.env", - "description": "", - "health": "working", - "issues": [] - } - ], - "variables": [ - { - "scope": "user", - "file": "~/.local/share/udos/Vault/variables/user.yaml", - "description": "User-level persistent variables", - "examples": [ - "theme", - "editor_font_size", - "last_world" - ], - "health": "working", - "issues": [] - }, - { - "scope": "global", - "file": "~/.local/share/udos/Vault/variables/global.yaml", - "description": "System-wide variables", - "examples": [ - "runtime_version", - "engine" - ], - "health": "working", - "issues": [] - }, - { - "scope": "snack", - "file": "snack manifest (per-container)", - "description": "Per-snack container state", - "examples": [ - "level", - "player_hp" - ], - "health": "working", - "issues": [] - }, - { - "scope": "system", - "file": "memory only (not persisted)", - "description": "Runtime-only state", - "examples": [ - "pid", - "uptime_seconds" - ], - "health": "working", - "issues": [] - }, - { - "scope": "user", - "source": "variables_api.py", - "description": "Exposed via /api/variables/user", - "examples": [], - "health": "working", - "issues": [] - }, - { - "scope": "install", - "source": "variables_api.py", - "description": "Exposed via /api/variables/install", - "examples": [], - "health": "working", - "issues": [] + "commands": [ + "chat_completion", + "close", + "health_check" + ] + }, + "model_pricing": { + "file": "backend/app/services/model_pricing.py", + "endpoints": [], + "variables": {}, + "commands": [ + "cheapest_model", + "cost_tiers", + "estimate_cost", + "summarize_tiers" + ] + }, + "template_manager": { + "file": "backend/app/services/template_manager.py", + "endpoints": [], + "variables": {}, + "commands": [ + "count_by_tier", + "create_template", + "delete_template", + "find_template", + "fork_template", + "from_dict", + "get_path", + "get_template_manager", + "has_cookiecutter", + "list_templates", + "reset_template_manager", + "scaffold_from_template", + "summary", + "to_dict" + ] + } } - ] - }, - "health": { - "total_items": 173, - "working": 173, - "untested": 0, - "broken": 0, - "orphaned": 0, - "health_pct": 100.0 + } }, - "recommendations": [] + "summary": { + "total_skills": 36, + "total_routes": 98, + "total_secrets": 0, + "total_variables": 6, + "total_mcp_servers": 2, + "total_paths": 15, + "total_runtimes": 7 + } } \ No newline at end of file diff --git a/seeds/surface-registry.json b/seeds/surface-registry.json index 364d531a..e1f6bc7b 100644 --- a/seeds/surface-registry.json +++ b/seeds/surface-registry.json @@ -50,8 +50,8 @@ "devTab": true, "devOnly": true, "description": "Developer hub — Control Panel unifies ecosystem status, plus drill-down panels for models, agents, repos, MCP, skills, workflows", - "runtimes": ["dev_layer", "tasker_ingest", "control_service"], - "panels": ["ControlPanel", "AgentsPanel", "ClineCliPanel", "FeedPanel", "KanbanPanel", "MCPServersPanel", "ModelsPanel", "ReposPanel", "ReviewPanel", "SettingsPanel", "SkillsPanel", "WorkflowsPanel", "RegistryPanel"] + "runtimes": ["dev_layer", "control_service"], + "panels": ["ControlPanel", "AgentsPanel", "FeedPanel", "KanbanPanel", "MCPServersPanel", "ModelsPanel", "ReposPanel", "ReviewPanel", "SettingsPanel", "SkillsPanel", "WorkflowsPanel", "RegistryPanel"] }, "workflow": { "component": "WorkflowSurface", @@ -101,7 +101,7 @@ "devTab": false, "devOnly": false, "description": "Documentation and help", - "runtimes": ["docs_roundup"], + "runtimes": ["documentation_api"], "panels": [] }, "teletext": { @@ -156,12 +156,6 @@ "protocols": ["MCP"], "endpoints": [] }, - "tasker_ingest": { - "service": "Tasker Ingest Bridge", - "file": "backend/app/mcp/tasker_ingest.py", - "protocols": ["MCP", "internal"], - "endpoints": [] - }, "control_service": { "service": "Control Panel Aggregation Service", "file": "backend/app/services/control_service.py", @@ -181,4 +175,4 @@ "endpoints": [] } } -} \ No newline at end of file +}