Calendar Events v0 Spec
Simple Summary
Calendar Events v0 is the Knowtation-side contract for synced external calendar events, timeline queries, multi-calendar toggles, and tiered agent calendar context.
Knowtation Hub already has a note calendar (month grid by note date). This spec adds a
separate event store and CalendarConnector layer. Scooling surfaces the unified timeline
through CalendarAdapter; agents read calendar context only through scoped retrieval at an
explicit tier — never from provider APIs directly.
Scooling plan: scooling/docs/CALENDAR-AND-SCHEDULE-PLAN.md (separate repo).
Status: Phase 1A–1B+ landed on feat/calendar-events-v0 (self-hosted Hub). Phase 1C+ not authorized until next gate.
Technical Summary
- Canonical owner: Knowtation (events, connector tokens, sync cursors, agent tier policy).
- Scooling: display filters, setup wizard UX,
CalendarAdapterdelegation only. - Default: read-only external sync; Tier 0 agent access for new calendars until user opts in.
- Separate toggles per calendar:
enabled_for_displayvsenabled_for_agents(independent).
Relationship To Existing Work
Hub note calendar (unchanged)
The Hub Calendar browse mode (HUB-API.md §4) remains notes by date. Calendar Events v0
does not replace it. Timeline API merges note-date buckets + event records when requested.
Import connectors
Calendar sync extends the Phase 3A connector pattern (ONBOARDING-WIZARD / import consent). New
source_type values require updates to lib/import-source-types.mjs when ICS file import ships.
Agent retrieval
Calendar fields are untrusted prompt content (same as note bodies). Agent tier caps are enforced
in Knowtation retrieval policy + Scooling WorkspaceScopeAdapter.
Memory consolidation daemon
The consolidation daemon (DAEMON-CONSOLIDATION-SPEC.md) does not ingest calendar titles into
memory. User-approved notes linked to events may enter the vault via review tray only.
Core Types (v0 draft)
CalendarConnector
| Field | Type | Description |
|---|---|---|
connector_id |
string | Stable id |
vault_id |
string | Scoped vault |
provider |
enum | ics_file, ics_url, google, microsoft (latter two later) |
display_name |
string | User label |
oauth_ref |
string? | Encrypted token handle — never returned to clients |
sync_cursor |
string? | Provider sync token |
last_sync_at |
ISO8601? | |
last_sync_error |
enum? | Bounded error code only |
revoked_at |
ISO8601? | Revoke deletes synced events per policy |
SourceCalendar
One row per sub-calendar within a connector (e.g. Google “Work”, “Personal”).
| Field | Type | Description |
|---|---|---|
source_calendar_id |
string | Provider calendar id |
connector_id |
string | Parent |
display_name |
string | |
color |
string? | Hex or token |
user_group |
enum? | personal, work, school, other — user-assigned |
enabled_for_sync |
boolean | Fetch events |
enabled_for_display |
boolean | Show on Scooling/Knowtation timeline |
enabled_for_agents |
boolean | Allow agent retrieval |
agent_context_tier_max |
0–4 | Cap for this calendar (default 0) |
CalendarEvent
| Field | Type | Description |
|---|---|---|
event_id |
string | Stable Knowtation id |
source_calendar_id |
string | |
external_uid |
string | Provider uid for dedup |
start |
ISO8601 | UTC instant |
end |
ISO8601 | UTC instant |
timezone |
string | IANA |
summary |
string? | Redacted in tier 0–1 agent responses |
busy |
boolean | Free/busy for tier 1 |
status |
enum | confirmed, cancelled, tentative |
recurrence_rule |
string? | Deferred expansion policy — see open decisions |
linked_note_paths |
string[]? | Tier 3 user links |
deleted_at |
ISO8601? | Tombstone on provider delete or revoke |
Agent Context Tiers
| Tier | Agent-visible fields |
|---|---|
| 0 | None from this calendar |
| 1 | start, end, busy — no title |
| 2 | Tier 1 + summary + user_group / calendar label |
| 3 | Tier 2 + linked_note_paths |
| 4 | Tier 3 + location/description (attendees redacted) — separate consent gate |
Retrieval API must accept agent_context_tier and source_calendar_ids[] and enforce caps server-side.
API Surfaces (v0 — self-hosted Hub)
GET /api/v1/calendar/timeline (shipped — self-hosted)
Query: from, to, vault_id, layers[], source_calendar_ids[], workspace_id?
Layers: notes, events, tasks (Phase 2G), agent_runs (Scooling metadata passthrough optional).
Response: merged, sorted slices with kind discriminator per item. No OAuth tokens.
Calendar API (self-hosted Hub)
Shipped (1B):
GET /api/v1/calendar/timeline— merged note-date + event slicesPOST /api/v1/calendar/events/import— one-time ICS text import (read-only)GET /api/v1/calendar/source-calendars— list toggles (no OAuth secrets)PATCH /api/v1/calendar/source-calendars/:id— update display/agent toggles, tier cap, user_group
Deferred (1C+):
POST /api/v1/calendar/connectors— begin connect (OAuth redirect or ICS URL)GET /api/v1/calendar/connectors— status, sub-calendars, togglesPOST /api/v1/calendar/connectors/:id/sync— manual refresh (rate-limited)DELETE /api/v1/calendar/connectors/:id— revoke + delete copied events
MCP/CLI parity deferred to separate phase after Hub REST is proven.
UI Filter Presets (Scooling — consumer)
Scooling maps presets to layers + source_calendar_ids:
| Preset | layers | notes |
|---|---|---|
| Notes only | notes |
Hub parity |
| Schedule | events, tasks, agent_runs |
notes optional |
| Everything | all enabled | |
| Learning focus | notes, tasks, workspace |
external events off |
Security Gates (must pass before implementation)
- Read-only OAuth scopes first.
- Revoke → delete events within documented SLA.
- Student personal calendars: not visible to teacher agents or roster.
- Org/minor policy may cap
agent_context_tier_maxat 0 or 1. - No calendar content in billing, public social, or unscoped MCP resources.
- Seven-tier tests: scope, revoke, timezone boundaries, tier enforcement, injection fixtures.
Phase 0 security checklist (signed off 2026-06-18)
| # | Gate | Status | Notes |
|---|---|---|---|
| 1 | Read-only OAuth scopes first (Google/Microsoft deferred to 1D) | Accepted | ICS file/URL are read-only by definition |
| 2 | Revoke → delete synced events within documented SLA | Accepted | Target: 24h; enforced in connector lifecycle (1B+) |
| 3 | Student personal calendars never visible to teacher/class agents | Accepted | Workspace scope + enabled_for_agents=false default |
| 4 | Org/minor policy may cap agent_context_tier_max at 0 or 1 |
Accepted | Hub policy layer in 1E |
| 5 | No calendar content in billing, public social, or unscoped MCP | Accepted | Tokens never in API responses; tier redaction server-side |
| 6 | Calendar text is untrusted prompt content (injection threat model) | Accepted | Same handling as note bodies; tier 0–1 redacts summary |
| 7 | enabled_for_display and enabled_for_agents are independent |
Accepted | Defaults: display on, agents off, tier 0 |
| 8 | Seven-tier test matrix before connector/network code | Accepted | Phase 1A: unit tests only; tiers 2–7 at 1B–1E |
Default for new SourceCalendar (agreed — not open):
| Field | Default |
|---|---|
enabled_for_sync |
true |
enabled_for_display |
true |
enabled_for_agents |
false |
agent_context_tier_max |
0 |
Canonical implementation: lib/calendar/source-calendar-defaults.mjs.
Implementation Phases
| Phase | Deliverable |
|---|---|
| 0 | This spec + security checklist (done) |
| 1A | Pure event normalizer + ICS parse (no network) (done) |
| 1B | Local event store + timeline merge with note dates (self-hosted Hub) (done) |
| 1B+ | PATCH /api/v1/calendar/source-calendars/:id — display/agent toggles (done) |
| 1C | ICS URL subscription sync (read-only) |
| 1D | Google read OAuth connector |
| 1E | Agent tier enforcement in retrieval API |
| 1F | Scooling CalendarAdapter live read |
Hosted canister parity follows self-hosted proof — not blocked on MuseHub staging for local dev.
Open Decisions
- Recurring events: expand at sync vs query time.
- Sync horizon: recommend 90 days past / 365 days future.
- Unified timeline endpoint vs client merge (recommend server merge).
Default for new— Resolved:SourceCalendarenabled_for_display=true,enabled_for_agents=false,agent_context_tier_max=0(see security checklist).
Non-Goals (v0)
- Two-way write to Google/Outlook (invites, study blocks) — separate gate.
- EventKit device sync — Apple client track.
- Auto-link every meeting to learning notes.
- Agents with full calendar access by default.