Skip to content

feat(calendar): task CRUD + deterministic action-item extraction (ODIAA-2329) - #32

Merged
amirhmoradi merged 2 commits into
mainfrom
odiaa-2329-tasks-crud
Sep 28, 2026
Merged

amirhmoradi merged 2 commits into
mainfrom
odiaa-2329-tasks-crud

Conversation

@amirhmoradi

Copy link
Copy Markdown
Contributor

Summary

  • Adds VTODO task list/create/update via a trimmed, vendored calendar_tasks Experiment API (cal.manager/CalTodo), per the CEO's Option-A vendoring decision.
  • Surfaced consistently: extension routes (POST /tasks/list|create|update), CLI (tb tasks list|create|update), MCP tools (task_list, task_create, task_update).
  • Adds deterministic (no LLM) action-item extraction from an email body: POST /messages/:id/action-items, tb action-items <id>, email_action_items (MCP).
  • Every new write path is gated behind a default-false tasksWrite access switch, classified in extension/src/access-control.js and documented in docs/ACCESS-CONTROL.md/access.example.json.
  • Manifest version bumped 2.2.0 → 2.3.0; strict_max_version left at the required 155.* ceiling.

Live smoke test (required manual verification)

Ran against a real, isolated Thunderbird instance — separate profile, --no-remote, separate bridge port (7800/7801), signature checks disabled locally only for this test — with real cal.manager storage-calendar internals, not mocks:

=== create with due + priority ===
{"ok":true,"data":{"id":"4f4fb8fd-...","title":"Final: due+priority","due":"2026-10-05T02:00:00.000Z","priority":3,"completed":false,"status":"NEEDS-ACTION"}}

=== create, no due/priority given ===
{"ok":true,"data":{"title":"Final: no due","due":null,"priority":0,"completed":false,"status":"NEEDS-ACTION"}}

=== create all-day ===
{"ok":true,"data":{"title":"Final: all-day","due":"2026-11-01","allDay":true,"status":"NEEDS-ACTION"}}

=== update: mark completed only (title/priority/due must survive) ===
{"ok":true,"data":{"title":"Final: due+priority","due":"2026-10-05T02:00:00.000Z","priority":3,"completed":true,"completedDate":"2026-09-28T01:23:14.000Z","status":"COMPLETED"}}

=== update: mark pending again ===
{"ok":true,"data":{"title":"Final: due+priority","due":"2026-10-05T02:00:00.000Z","priority":3,"completed":false,"status":"NEEDS-ACTION"}}

=== update: change only due date (title/priority must survive) ===
{"ok":true,"data":{"title":"Final: due+priority","due":"2026-10-10T02:00:00.000Z","priority":3,"status":"NEEDS-ACTION"}}

=== update: change only title (due/priority must survive) ===
{"ok":true,"data":{"title":"Final: renamed","due":"2026-10-10T02:00:00.000Z","priority":3,"status":"NEEDS-ACTION"}}

This surfaced and fixed two real bugs the unit tests can't catch (they don't exercise real cal.manager objects):

  1. calIDateTime.getInTimezone(...).jsDate is unreliable (always undefined) on any date handed back by the storage calendar after an adoptItem/modifyItem round-trip — even for a correctly-set date. Switched to reading nativeTime (epoch microseconds) directly.
  2. The WebExtension schema layer normalizes unset optional CalendarTaskProperties fields to null, not undefined — so the partial-update logic's !== undefined checks were spuriously true for every field on every update() call, silently wiping title/priority and resetting due on updates that only meant to touch one field (e.g. --completed). Checks now use != null.

action-items wasn't exercised live (this bare smoke-test profile has no mail account/messages to extract from) — covered instead by the 29 dedicated unit tests plus the access-control suite.

Test plan

  • npm test (57/57), node --test test/access-control.test.mjs (80/80), node --test test/action-items.test.mjs (29/29) all pass
  • Live smoke test against real Thunderbird/cal.manager internals (isolated profile/port) — see above
  • Manifest version bumped; strict_max_version unchanged at 155.*
  • ATN manual review (required for Experiment APIs) — pending submission after merge

🤖 Generated with Claude Code

amirhmoradi and others added 2 commits September 28, 2026 01:25
…AA-2329)

Adds VTODO task list/create/update through a trimmed, vendored calendar_tasks
Experiment API (cal.manager/CalTodo), surfaced consistently across extension
routes, CLI (`tb tasks`), and MCP tools, plus a deterministic (no LLM)
action-item extractor for turning an email body into a checklist
(`tb action-items`). Every new write path is access-controlled behind a
default-false `tasksWrite` switch.

Live-smoke-tested against a real, isolated Thunderbird instance (separate
profile/port, --no-remote) per the required manual verification, which
surfaced and fixed two real bugs in the Experiment API glue that the unit
tests couldn't catch since they don't exercise real cal.manager objects:
- `calIDateTime.getInTimezone(...).jsDate` is unreliable (always undefined)
  on any date handed back by the storage calendar after an adoptItem/
  modifyItem round-trip; switched to reading `nativeTime` directly.
- The WebExtension schema layer normalizes optional `CalendarTaskProperties`
  fields the caller didn't set to `null`, not `undefined`, so the
  partial-update logic's `!== undefined` checks were spuriously true for
  every field on every update — wiping title/priority and resetting due
  dates on updates that only meant to change one field. Checks now use
  `!= null`.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Resolves conflicts from ODIAA-2328 (calendar events CRUD + clash
detection) landing on main after this branch's tasks CRUD +
action-item extraction work, across access control, background.js
route handlers, MCP tool catalog, tests, and docs. Tool count
reconciled to 31; all test suites (quick, extension, mcp, docs) pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@amirhmoradi
amirhmoradi merged commit a9f37b8 into main Sep 28, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant