Skip to content

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 /tasks
  • GET /tasks/{task_id}
  • PATCH /tasks/{task_id}
  • POST /tasks/{task_id}/clone
  • POST /tasks/{task_id}/complete
  • POST /tasks/{task_id}/waiting
  • DELETE /tasks/{task_id}
  • POST /tasks/bulk/update
  • POST /tasks/bulk/complete
  • POST /tasks/bulk/waiting
  • POST /tasks/bulk/archive
  • GET /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, or include_pagination=true are 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, and review_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, and last_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.

flowchart TD A[User] --> B[Luna Interface] B --> C[Keeper kernel service] C --> D[Objectives / tasks / blockers] C --> E[Planning records and preflights] D --> F[Approval-gated task mutations] E --> G[Read-only recommendations] F --> H[Lich approval boundary] H --> I[Canonical Keeper HTTP mutation] I --> C C --> J[Structured output] J --> B