Agenda & Tasks List CLI/API with Completeness Metrics #9
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Implement the user-facing read routes
GET /v1/agenda,GET /v1/tasks, andGET /v1/items/{id}alongside their CLI counterpartslife agenda,life tasks list, andlife item get, delivering live schedule views, task inventories, individual item inspection with live absence verification from durable history, cursor-based pagination, capacity occupancy calculations (transparent vs opaque events), and explicit per-collection coverage metrics with honest degradation.Background & Technical Rationale
Radicale as Sole Authority for Live State:
In accordance with the core architecture,
life-servicenever falls back to memory, recalled context, or stale projection tables when serving live schedule and task inventory requests. Every query executes live CalDAV queries against the configured upstream Radicale collections. If a backend read fails, the service discloses partial coverage or unavailability—it never claims an empty calendar.Temporal & Capacity Semantics for Agenda Items:
DUEproperty represents a deadline, andDTSTARTrepresents earliest planned work. Neither reserves busy time. The agenda view must display deadlines distinctly from scheduled calendar events. Undated tasks must not be converted into fabricated midnight appointments.TRANSP:TRANSPARENTappear on the agenda (e.g. reminders, informational markers) but do not consume capacity.STATUS:CANCELLED) consume zero capacity.TRANSP:OPAQUE,STATUS:TENTATIVE) conservatively consume busy capacity unless explicit policy overrides this.Task Inventory & Undated Task Handling (Scenario W04):
life tasks list(andGET /v1/tasks) provides the authoritative backlog of actionable obligations.STATUS:NEEDS-ACTION,STATUS:IN-PROCESS, or unspecified external status. Completed tasks (STATUS:COMPLETED) and cancelled tasks (STATUS:CANCELLED) are excluded by default unless explicitly requested via--status allor--status completed.DTSTARTandDUEare valid tasks and must be included in the list.Multi-Collection Coverage & Non-Atomic CalDAV Reads:
Because CalDAV lacks multi-collection atomic snapshots:
complete: boolflag and acoveragearray detailing per-collection observations:{"collection_id": "personal", "state": "complete", "as_of": "2030-04-08T00:00:00Z"}.partialwith warningincomplete_calendar, and that collection's state is reported asunavailable.Cursor-Based Pagination Contract:
next_cursor(bound to normalized filter parameters and expiry time).cursor_expired(HTTP 422, exit code 2), requiring the client to restart.Exact Technical References & Specifications
Normative Quotations from Technical Architecture
From
docs/technical/cli-api.md(Command-to-route table, lines 27–30):From
docs/technical/cli-api.md(Common data types and pagination, lines 71–74):From
docs/technical/cli-api.md(Command payloads and results, lines 78–81):From
docs/technical/calendar.md(Query and time semantics, lines 24–28):From
docs/technical/verification.md(Workflow & Calendar scenarios, lines 17–18, 45):Response JSON Envelopes
1.
GET /v1/agendaSuccessful Response (200 OK)2.
GET /v1/tasksResponse with Undated Tasks (200 OK)3. Incomplete Coverage Warning Response (
200 OK,status: partial)What to Build (Step-by-Step)
1. Unified Domain Model (
src/domain/item.rs)Itemstruct mapping all standard fields fromdocs/technical/cli-api.md:id: Opaque string identifier (itm_<base32>).kind:ItemKind::TaskorItemKind::Event.collection_id: Logical collection ID.uid: Authoritative CalDAV UID string.recurrence_id: NullableTaggedTime.etag: Quoted entity tag ("\"...\"").title: Summary string.status: String representation (NEEDS-ACTION,IN-PROCESS,COMPLETED,CANCELLED,CONFIRMED,TENTATIVE).categories: Array of strings (containing strength likelife-activeand areas likearea-career).strength: NullableCommitmentStrength(life-active,life-queued,life-someday).deadline,planned_start,start,end: NullableTaggedTime.estimated_minutes: Optional integer (fromX-LIFE-ESTIMATED-MINUTES).energy: Optional enum (low,medium,highfromX-LIFE-ENERGY).project_id: Optional string (fromX-LIFE-PROJECT-ID).consumes_capacity: Boolean computed based on transparency and status.2. Coverage & Aggregation Engine (
src/calendar/aggregation.rs)REPORT calendar-queryacross all configured logical collections in parallel usingfutures::future::join_all.CollectionCoverage { collection_id, state: CoverageState::Complete, as_of: Utc::now() }.CollectionCoverage { collection_id, state: CoverageState::Unsupported, as_of: Utc::now() }.CollectionCoverage { collection_id, state: CoverageState::ConcurrentChange, as_of: Utc::now() }.CollectionCoverage { collection_id, state: CoverageState::Unavailable, as_of: Utc::now() }.complete = coverage.iter().all(|c| c.state == CoverageState::Complete).dependency_unavailable. If some succeed and some fail, returnstatus: partialwith warningincomplete_calendarorconcurrent_change.3. Cursor Pagination Engine (
src/api/pagination.rs)(query_hash, offset, expiry_timestamp).cursor_expired(HTTP 422 / exit code 2).4. Axum Handlers (
src/api/agenda.rs,src/api/tasks.rs,src/api/items.rs)GET /v1/agenda:period(today,tomorrow,week) ORfromandtoISO strings. Optionalcollection_ids.VEVENToccurrences and activeVTODOitems falling in the time window.start, tasks bydeadlineurgency.GET /v1/tasks:status(open|completed|cancelled|all),strength(life-active|life-queued|life-someday),limit,cursor.statusisopen, matchNEEDS-ACTION,IN-PROCESS, or unknown non-completed statuses.deadlineandplanned_startare null).GET /v1/items/{id}:itm_...) using local projection/mapping to find(collection_id, uid, recurrence_id).description(which is returned only on item get).life_events/operations) to determine if the item was previously deleted.not_found(HTTP 404, CLI exit 1) with historical context.not_found(HTTP 404, CLI exit 1).{ item: Item, as_of: DateTime<Utc>, complete: bool, coverage: Vec<CollectionCoverage> }.5. CLI Rendering (
src/cli/agenda.rs,src/cli/tasks.rs,src/cli/items.rs)life agenda,life tasks list, andlife item get <ID>:--json: Output strict JSON envelope matching specification.TIME,KIND,TITLE,COLLECTION,CAPACITY(indicating whether the item blocks capacity).ID,STATUS,STRENGTH,DEADLINE,TITLE,ESTIMATE.complete == false, print a noticeable warning banner indicating incomplete calendar coverage.Acceptance Criteria
life agendaandlife tasks listperform live HTTP CalDAV queries against Radicale; they never serve cached text from memory or SQLite without a live check.today,tomorrow, andweekreturns items partitioned by local midnight in the configured IANA timezone, displaying deadlines distinctly from calendar events.TRANSP:TRANSPARENThasconsumes_capacity: falseand is indicated as non-blocking.TRANSP:OPAQUE,STATUS:TENTATIVE) hasconsumes_capacity: true.consumes_capacity: false.life tasks listincludes undated tasks by default; they are never converted to fake midnight appointments or dropped from results.COMPLETEDandCANCELLEDtasks, while includingNEEDS-ACTION,IN-PROCESS, and external tasks with unknown non-standard status strings (retaining the raw status label).DTSTARTorDUEremains visible in task listings and candidate sets; a date-only deadline (VALUE=DATE) is displayed as that local date and not converted to a UTC midnight appointment.coverage: [{state: "unsupported"}]andcomplete: false, never silently dropped while claiming complete coverage.warnings: ["concurrent_change"]withcomplete: false, rather than claiming a globally atomic snapshot.life item get): Rereads the item live from Radicale to verify current ETag, returning fulldescription; resolving an item deleted from Radicale checks durable history to confirm live absence rather than returning stale cached projection.partial, overallcompleteis false, and coverage details which collection failed; free time is explicitly not declared.next_cursor, and returncursor_expired(exit code 2) when an expired cursor is passed.--jsonmatches the root envelope (schema_version: 1,request_id,status,data,warnings,error).Notes
docs/technical/cli-api.mdis deliberately deferred from this slice (documented deviation in PR #44).Blocked by
Issue #009: Agenda & Tasks List CLI/API with Completeness Metricsto Agenda & Tasks List CLI/API with Completeness Metricsnot_foundCLI exit code to 2 across commands #49