Keeper Architecture
Keeper is the AncientOS kernel service for objectives, tasks, planning records, blockers, preflight context, and next-review needs. It is not a transport and not an application UI. Luna, Discord, terminal TUI, Keeper Console, and future interfaces consume Keeper through governed runtime or canonical Keeper service boundaries.
Keeper coordinates work after the relevant Lich gate allows objective/task coordination. Read-only views and planning summaries are safe by default. Mutation remains governed: task completion, waiting, archive, focus, active, someday, duplicate-marker, and similar state changes require the existing approval path before conversational runtime applies them.
Current Operations
Keeper is surfaced conversationally in #kotl through the Luna runtime, but
the canonical task backend remains the Keeper HTTP service. Luna may call
/nl/handle for capture and read/query requests, and it may call the existing
canonical task mutation endpoints only after Lich approval:
- List open, waiting, blocked, owner-scoped, and next-task views.
- Ask "what should I do next?" and render the next Keeper task.
- Capture legitimate tasks such as "remind me to call Alex tomorrow."
- Show cached visible metadata/details for recently rendered tasks.
- Mark resolved visible tasks complete after Lich approval.
- Mark resolved visible tasks waiting after Lich approval.
- Review captured inbox tasks, show review-next, duplicate, low-confidence, and likely-garbage signals, and request triage mutations after Lich approval.
- Preserve Keeper bundle proposals and confirmation before bundle commit.
- Preserve drafting assistance for next-task follow-up messages.
Keeper Console Application
Keeper Console is a private, local-only web UI for manual task management. It is
implemented as services/keeper-console and is intended to run on the host loopback
interface only, currently http://127.0.0.1:8022 when started from the root Docker
Compose file. It is an application/interface over Keeper, not the Keeper kernel
service itself. It is not a public product surface and has no independent task
storage.
The console serves static browser assets and exposes a narrow proxy under
/api/keeper/*. That proxy is allowlisted to canonical Keeper task endpoints only:
GET /tasksGET /tasks/{task_id}PATCH /tasks/{task_id}POST /tasks/{task_id}/clonePOST /tasks/{task_id}/completePOST /tasks/{task_id}/waitingDELETE /tasks/{task_id}POST /tasks/bulk/updatePOST /tasks/bulk/completePOST /tasks/bulk/waitingPOST /tasks/bulk/archiveGET /tasks/review/insights
The console must not import database modules, connect to Postgres, use
app/task_store.py, call /nl/handle for mutations, or create an alternate Keeper
backend. Its only mutation path is through the canonical Keeper HTTP service.
Supported manual operations:
- View and refresh task lists.
- Filter tasks by status, priority, context, owner label, archive inclusion, and text search where the Keeper API supports it.
- Use operational planning views for Today, Upcoming, Waiting/blocked, Priority, Project grouping, Focus, Inbox, Fast Triage, Bulk Cleanup, and Review.
- Inspect task details and raw task JSON.
- Edit supported task fields through
PATCH /tasks/{task_id}, including due dates, estimates, priority scores, project labels, tags, dependency IDs, scheduling state, scheduled dates, review timestamps, and today focus. - Reorder the priority queue by dragging tasks in Priority view; this writes priority scores through canonical task PATCH calls.
- Generate proposal-only schedule artifacts from visible tasks. The console does not write calendar events or alter task schedules from those artifacts.
- Clone, complete, mark waiting, and soft archive individual tasks.
- Activate, focus today, move to someday, mark duplicate, and set inbox/review metadata from the task detail panel or review-next triage strip.
- Bulk complete, bulk waiting, bulk activate, bulk someday, and bulk soft archive selected tasks after browser confirmation.
- Show read-only duplicate, likely-garbage, and low-confidence indicators from Keeper heuristics. The console never auto-deletes or auto-merges.
Soft archive semantics remain conservative. Console archive buttons are labelled
as soft archive actions, and the proxy calls DELETE /tasks/{task_id} or
POST /tasks/bulk/archive; Keeper updates archive/status fields and does not
physically delete task rows.
Known limitations:
- Authentication is intentionally minimal because the service is local-only. Before any non-local exposure, the boundary should move behind explicit auth, CSRF protection, audit attribution, and a tighter operator access policy.
- Project, opportunity, contact, and calendar management are read-only or omitted unless represented on task rows by the canonical task API.
- Unsupported task fields are rendered from raw JSON for inspection rather than inventing storage in the UI.
- The console is a manual operations surface only; it does not run scheduler logic and does not authorize Luna or Lich to execute mutations.
Approval Flow
Conversational mutations first resolve against visible Keeper task context. Complete, waiting, archive, focus, active, someday, and duplicate-marker requests create Lich approval surfaces with target IDs and human-readable summaries. Lich records approval or rejection only; it does not execute Keeper mutations. The runtime post-approval execution layer consumes approved Keeper decisions and calls canonical Keeper complete, waiting, archive, or PATCH HTTP endpoints.
Discord views render approval controls only. They do not contain task mutation
logic, and Keeper core modules under app/keeper/ stay transport-neutral.
Delete Limitation And Archive
Conversational task deletion in Luna still fails closed. Keeper exposes
canonical soft archive endpoints for console and approval-gated conversational
use; those endpoints do not physically delete task rows. They set
status='archived', inbox_state='archived', archived_at, and optional
archive metadata.
Conversational delete requests are intercepted and return:
I can’t delete tasks yet because Keeper has no canonical delete endpoint wired.
Luna must not pass delete commands such as "delete these" or "batch delete
these tasks" to /nl/handle, and it must not capture those commands as new
tasks.
Canonical Task API
The Keeper service is the canonical task API. Current canonical task endpoints:
GET /tasks: backward-compatible plain list by default; filtered and paginated envelope when filters,limit,offset, orinclude_pagination=trueare supplied.POST /tasks: create a task.GET /tasks/{task_id}: get one task with detail metadata.PATCH /tasks/{task_id}: patch allowed task fields.GET /tasks/review/insights: return read-only duplicate, likely-garbage, and low-confidence review signals.POST /scheduling/proposals: generate a schedule proposal artifact from canonical task metadata, availability windows, and busy windows. This endpoint is read/proposal only and does not mutate tasks or calendars.POST /tasks/{task_id}/complete: mark one task done, preserving the existing response shape used by Luna.POST /tasks/{task_id}/waiting: mark one task waiting, preserving the existing response shape used by Luna.POST /tasks/{task_id}/open: reopen one task.POST /tasks/{task_id}/clone: clone one task into a new open task.DELETE /tasks/{task_id}: soft archive one task; no physical delete.POST /tasks/bulk/update: patch multiple tasks.POST /tasks/bulk/complete: complete multiple tasks.POST /tasks/bulk/waiting: mark multiple tasks waiting.POST /tasks/bulk/archive: soft archive multiple tasks.
Canonical inbox and triage fields live on task rows:
inbox_state:inbox,active,focus,waiting,someday,archived.review_state:unreviewed,reviewing,reviewed,needs_clarification,duplicate,garbage.triage_score,capture_source,capture_confidence,reviewed_at,focus_rank,active_rank,someday,duplicate_of,archived_reason,user_notes, andreview_batch_id.
Existing tasks migrate with active-compatible defaults. Conversational captures
enter inbox_state='inbox' and review_state='unreviewed'.
Archive/delete semantics are intentionally conservative: archive is reversible through future patch/open flows, physical deletion is not exposed, and UI or Discord destructive flows should remain approval-gated before calling archive endpoints.
Keeper Scheduler Roadmap
Keeper scheduling is proposal-first and approval-gated:
- Task planning metadata lives on canonical Keeper tasks:
due_date,estimate_minutes/estimated_minutes,priority_score,energy_level,project,context,tags,recurrence_rule, dependency relationships, blocker relationships,today_focus,scheduled_date,scheduling_state, andlast_reviewed_at. - Calendar availability is read-only input. Busy windows are evidence, not an instruction to write events.
- Scheduler output is a structured artifact with time-block suggestions, conflicts, workload math, blocked-task evidence, reshuffle suggestions, and focus-session recommendations.
- No hidden scheduler loop exists. Proposals are generated only from explicit user/API requests.
- The scheduler never mutates tasks and never mutates calendars. Future writes require explicit human approval and a separate approval-gated execution path.
- Lich may later present approval surfaces, but Lich remains decision and approval infrastructure only; it does not execute Keeper or calendar writes.
Backend Rule
Luna must not invent a parallel Keeper task backend. app/task_store.py is not
a Keeper mutation path. Canonical task state and mutations live behind the
Keeper service API.