Files
Prakash Dalai 01fe314afa feat(cloud): Projects entity + snapshot scheduler for Mission Control (#1114)
* feat(cloud): add Projects entity, scheduler wiring, and project_id refs

Adds the Projects entity (workspace > project > pocket/task/cycle) as a
Linear-style scoping primitive, threads optional project_id through the
existing Pocket / Task / Cycle entities, and wires an opt-in in-process
daily-snapshot scheduler for the burnup chart.

Project entity:
- 4-file shape under ee/cloud/projects/ matching pockets canonical.
- Beanie ProjectDocument indexed on (workspace, status).
- ProjectCreated / ProjectUpdated / ProjectArchived / ProjectDeleted
  realtime events.
- Soft-archive (idempotent) + hard-delete with cascade soft-unassign on
  Pockets, Tasks, and Cycles in the same workspace. Children keep their
  data; only the project_id reference clears.
- import-linter contract entry forbids non-service.py imports of the
  project Beanie doc.

project_id wired into siblings:
- Pockets, Tasks, Cycles all carry an optional project_id (default None
  preserves existing rows).
- Each entity validates a supplied project_id against the current
  workspace before write.
- list endpoints accept ?project_id=<id> (empty string filters for the
  Mission Control "Unassigned" bucket).
- Mission Control facade threads project_id through the visible-pocket
  set so Nudges inherit their parent pocket's project assignment.

Scheduler:
- ee.cloud.cycles.scheduler runs an asyncio loop that sleeps until the
  next UTC midnight then calls snapshot_all_active() for every workspace
  with at least one active cycle.
- Gated on POCKETPAW_CLOUD_SCHEDULER_ENABLED=true so test runs and dev
  shells don't spawn a background task. Production hosts that prefer
  external cron / Kubernetes CronJob / Celery beat keep the flag unset
  and dispatch the same callable from their platform scheduler.
- POST /cycles/{id}/snapshot manually triggers today's snapshot for
  testing and onboarding. Idempotent within a UTC day.
- list_active_workspace_ids helper exposed on cycles.service so the loop
  doesn't need direct Beanie access.

Tests (78 new + adjacent passing):
- test_projects_service.py: CRUD, tenant isolation, archive idempotence,
  cascade unassign on delete.
- test_projects_router.py: HTTP smoke + tenancy.
- test_cycles_snapshot_scheduler.py: manual trigger + idempotence,
  workspace discovery, scheduler start/stop wiring.
- test_mission_control_project_filter.py: project_id narrows the
  visible-pocket set on the items feed.

import-linter: 13 contracts kept (Projects added, all others unchanged).

* docs(advanced): add Mission Control (Cloud) operator console page

The existing /advanced/mission-control page describes the local
multi-agent orchestration framework (file-based JSON storage, single
process). This new page covers the cloud SaaS surface: workspace-scoped
REST API + MongoDB-backed entities served by ee/cloud/.

The page opens with a callout flagging the distinction so readers landing
from search don't conflate the two. It then walks through the
vocabulary (Tray, Pawprints, Snags, Projects, Cycles), the
Workspace > Project > Pocket > Cycle/Task hierarchy, the WorkItem shape,
the REST endpoint inventory across mission_control / tasks / cycles /
projects, the SSE event surface, and the scheduler wiring options
(in-process opt-in vs external cron).

Sidebar entry added to docs-config.json under Advanced, just below the
existing Mission Control entry, with a cloud-themed lucide:cloud icon.

* fix(projects): abort delete if cascade-unassign fails

The previous _unassign_project swallowed every exception per child and
let agent_delete proceed to drop the project row. If the pockets, tasks,
or cycles bulk-update failed (transient mongo error, version mismatch),
the project was gone while its children kept dangling project_id values
that resolved to nothing — only fixable by hand in mongo.

Narrow the except to ImportError (the lazy-import degrade for forks
that ship without a child entity) and let everything else propagate. A
failed cascade now aborts the delete with the children still attached,
so the caller can retry safely.

New test test_delete_aborts_if_cascade_unassign_fails monkeypatches the
tasks unassign helper to raise, asserts agent_delete raises, and
verifies the project row survives.

Addresses pocketpaw#1114 review.

* fix(mission-control): façade now composes Tasks alongside Nudges

The Mission Control items endpoint only queried Instinct (Nudges).
Any Task created via POST /api/v1/tasks landed in Mongo but never
surfaced in GET /mission-control/items. Operators creating work via
the new modal saw their task disappear from the feed on every refresh
even though the backend returned a valid Task id with status
"in_progress".

Smoke-test trace that surfaced it:
  [NewWorkItemModal] created OK { id: 6a08…, status: in_progress }
  [MissionControl] onCreated → refreshing feed
  [WorkFeed] listWorkItems → 0 items {}

agent_list_work_items now:

- Pulls Tasks via tasks_service.agent_list_tasks (lazy import keeps
  the façade installable on forks without the Tasks entity, matching
  the projects/_unassign_project pattern).
- Drops the early `if not visible: return []` — that gated the whole
  feed on pocket visibility, which is correct for Instinct Nudges
  (pocket-scoped) but wrong for Tasks (workspace-scoped, may have
  null/empty pocket_id).
- Projects each Task into a WorkItem via the new _task_to_work_item
  helper. Status mapping: proposed → IN_PROGRESS, in_progress →
  IN_PROGRESS, awaiting_approval → AWAITING_APPROVAL, done → DONE,
  reverted → REJECTED, failed → FAILED, blocked → BLOCKED. Section
  routing: agent in-flight → AGENTS, terminal → PAWPRINTS/SNAGS,
  everything else → TRAY.
- ID prefix matches the convention the bulk endpoints already
  expect: `task:<id>` for Tasks, `nudge:<id>` for Actions.

Test changes:

- New regression test_includes_tasks_alongside_nudges proves a Task
  surfaces in the items list AND keeps surfacing when the workspace
  has no visible pockets (the empty-string pocket case from the
  captain's smoke test).
- Three existing autouse fixtures stub agent_list_tasks to [] so
  Instinct-only test files don't need a Beanie test DB. Tests that
  exercise the Tasks branch override the stub.

All 57 MC + projects + cycles tests pass; ruff clean.
2026-05-16 22:08:12 +05:30
..
2026-03-10 18:16:41 +05:30

PocketPaw Documentation

The official documentation for PocketPaw — a self-hosted AI agent with a native desktop app, controlled via Telegram, Discord, Slack, WhatsApp, Signal, Matrix, Teams, Google Chat, or a web dashboard.

Live site: pocketpaw.xyz

Stack

Built with Lito Docs — an open-source tool that converts Markdown/MDX into beautiful, searchable documentation sites with zero configuration. Lito provides the Astro-based SSG, Pagefind search, 20+ MDX components, and light/dark theming out of the box.

Structure

docs/
├── docs-config.json          # Site config (nav, branding, SEO, search)
├── _landing/                  # Custom HTML/CSS landing page
├── public/                    # Static assets (logos, OG images)
├── introduction/              # Welcome & overview
├── getting-started/           # Install, quick-start, config, project structure
├── desktop-client/            # Desktop app overview, installation, development, API server
├── concepts/                  # Architecture, message bus, agent loop, memory, tools, security
├── channels/                  # 9+ channel guides (Telegram, Discord, Slack, WhatsApp, etc.)
├── backends/                  # Claude Agent SDK, OpenAI Agents, Google ADK, Codex CLI, OpenCode, Copilot SDK
├── tools/                     # 50+ built-in tools
├── integrations/              # OAuth, Gmail, Calendar, Drive, Docs, Spotify, Reddit, MCP
├── security/                  # Guardian AI, injection scanner, audit log/CLI/daemon
├── memory/                    # File store, Mem0, sessions, context building, isolation
├── advanced/                  # Model router, plan mode, scheduler, skills, Deep Work, Mission Control
├── deployment/                # Self-hosting, Docker, systemd
└── api/                       # 39 REST endpoint docs + WebSocket protocol

client/                        # Desktop app source (Tauri 2.0 + SvelteKit)
├── CLAUDE.md                  # Client architecture and dev conventions
├── src/                       # SvelteKit frontend (Svelte 5 + Tailwind CSS 4 + shadcn-svelte)
└── src-tauri/                 # Rust backend (system tray, shortcuts, multi-window)

Local Development

No package.json or Astro config needed — Lito handles everything.

Preview

npx --yes @litodocs/cli dev -i .

Build

npx --yes @litodocs/cli build -i .

Output goes to ./dist/.

Deployment

The site auto-deploys to GitHub Pages on push to main via the workflow in .github/workflows/deploy-docs.yml.

To deploy manually, build and upload the dist/ folder to any static hosting provider (Vercel, Netlify, Cloudflare Pages, etc.).

Adding Pages

  1. Create an .mdx file in the appropriate directory with YAML frontmatter:
    ---
    title: Page Title
    description: "A 150-160 character description with front-loaded keywords."
    section: Section Name
    ogType: article
    keywords: ["keyword1", "keyword2", "keyword3"]
    tags: ["tag1", "tag2"]
    ---
    
    Content here...
    
  2. Add the page to the navigation.sidebar array in docs-config.json.

MDX Components

Provided by Lito — no local definitions needed:

Component Usage
<Card>, <CardGroup> Feature cards with icons and links
<Steps>, <Step> Numbered step sequences
<Tabs>, <Tab> Tabbed content blocks
<Callout> Info/warning/tip callouts
<ResponseField> API field documentation
<RequestExample>, <ResponseExample> API example blocks

License

MIT