* 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.
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
- Create an
.mdxfile 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... - Add the page to the
navigation.sidebararray indocs-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