docs: refresh stale completed ADRs
This commit is contained in:
parent
88a66eff27
commit
932a7409fc
8 changed files with 210 additions and 31 deletions
|
|
@ -1,35 +1,59 @@
|
||||||
# V1 ADR 002: SQLite Document Schema V1
|
<!-- SPDX-FileCopyrightText: 2026 FocusFlow contributors -->
|
||||||
|
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
|
||||||
|
|
||||||
|
# V1 ADR 002: SQLite Document Schema V2
|
||||||
|
|
||||||
|
Status: **Accepted; updated to match current app state**
|
||||||
|
|
||||||
Status: **Accepted**
|
|
||||||
Date: 2026-06-26
|
Date: 2026-06-26
|
||||||
|
|
||||||
|
Last reviewed: 2026-07-07
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
The project pivoted from MongoDB to **SQLite‑first** persistence.
|
|
||||||
We need a stable, versioned relational schema that maps cleanly to the domain
|
The project is SQLite-first. The local app persists scheduler state in a Drift
|
||||||
objects while staying behind the repository abstraction. Drift will manage
|
database and keeps storage details behind repository and application-unit-of-work
|
||||||
migrations.
|
boundaries.
|
||||||
|
|
||||||
|
The original V1 schema was implemented and then migrated to the current Drift
|
||||||
|
schema version 2 when application-layer records became durable state.
|
||||||
|
|
||||||
## Decision
|
## Decision
|
||||||
* **Database file**: `adhd_scheduler.sqlite` in the user data directory.
|
|
||||||
* **Schema version**: `1` (managed by Drift).
|
* **Database file**: `scheduler.sqlite` under `~/ADHD_Scheduler/` by default.
|
||||||
|
Runtime composition may override this with `SCHEDULER_SQLITE_PATH`, and dev
|
||||||
|
tooling may pass `--sqlite`.
|
||||||
|
* **Schema version**: `2`, managed by Drift. Version 1 files are supported as
|
||||||
|
migration inputs; downgrades are not supported.
|
||||||
* **Tables**
|
* **Tables**
|
||||||
|
|
||||||
| Table | Purpose | Key fields |
|
| Table | Purpose | Key fields |
|
||||||
|-------|---------|------------|
|
|-------|---------|------------|
|
||||||
| `tasks` | Authoritative task rows | `id TEXT PRIMARY KEY`, `owner_id TEXT`, `project_id`, `parent_id`, `type`, `status`, `priority`, `reward`, `difficulty`, `scheduled_start_utc`, `scheduled_end_utc`, `actual_start_utc`, `actual_end_utc`, `completed_at_utc`, `revision INT`, `created_at_utc`, `updated_at_utc`, `backlog_entered_at_utc`, `backlog_entered_provenance TEXT` |
|
| `tasks` | Authoritative task rows | `id TEXT PRIMARY KEY`, `owner_id`, `title`, `project_id`, `parent_id`, `type`, `status`, `priority`, `reward`, `difficulty`, `duration_minutes`, scheduled/actual/completed UTC timestamps, `backlog_tags_json`, `reminder_override`, `stats_json`, `backlog_entered_at_utc`, `backlog_entered_provenance`, `revision`, `created_at_utc`, `updated_at_utc` |
|
||||||
| `projects` | Project configuration | `id TEXT PRIMARY KEY`, `owner_id`, `name`, `color_key`, config defaults …, `archived_at_utc`, `revision` |
|
| `task_activities` | Append-only task activity facts | `id TEXT PRIMARY KEY`, `owner_id`, `task_id`, `project_id`, `operation_id`, `code`, `occurred_at_utc`, `metadata_json` |
|
||||||
| `locked_blocks` | Recurring / one‑off locked time | `id TEXT PRIMARY KEY`, `owner_id`, `name`, `date TEXT`, `start_time TEXT`, `end_time TEXT`, `recurrence_json`, `hidden_by_default INT`, `archived_at_utc`, `revision` |
|
| `projects` | Project configuration | `id TEXT PRIMARY KEY`, `owner_id`, `name`, `color_key`, configured defaults, `archived_at_utc`, `revision`, `created_at_utc`, `updated_at_utc` |
|
||||||
| `locked_overrides` | Date‑scoped overrides | `id TEXT PRIMARY KEY`, `owner_id`, `locked_block_id`, `date TEXT`, `type`, JSON fields |
|
| `project_statistics` | Project-level aggregate statistics | `project_id PRIMARY KEY`, `owner_id`, completion and suggestion-count fields, `revision`, `created_at_utc`, `updated_at_utc` |
|
||||||
| `settings` | One row per owner | `owner_id TEXT PRIMARY KEY`, `timezone_id`, `day_start_minutes`, `day_end_minutes`, `compact_mode INT`, `revision` |
|
| `locked_blocks` | Recurring / one-off locked time | `id TEXT PRIMARY KEY`, `owner_id`, `name`, `date`, `start_time`, `end_time`, `recurrence_json`, `hidden_by_default`, `project_id`, `archived_at_utc`, `revision`, timestamps |
|
||||||
| `activities` | Append‑only internal events | `id TEXT PRIMARY KEY`, `owner_id`, `task_id`, `code`, `occurred_at_utc`, `metadata_json` |
|
| `locked_overrides` | Date-scoped locked-block overrides | `id TEXT PRIMARY KEY`, `owner_id`, `locked_block_id`, `date`, override type and payload fields |
|
||||||
| `snapshots` | Bounded diagnostic schedule snapshots | `id TEXT PRIMARY KEY`, `owner_id`, `operation_name`, `source_date TEXT`, `window_json`, `notice_json`, `changes_json`, `retention_expires_utc` |
|
| `settings` | One row per owner | `owner_id TEXT PRIMARY KEY`, `timezone_id`, `day_start_minutes`, `day_end_minutes`, `compact_mode`, `backlog_staleness_json`, `revision`, timestamps |
|
||||||
|
| `snapshots` | Bounded diagnostic schedule snapshots | `id TEXT PRIMARY KEY`, `owner_id`, `captured_at_utc`, `operation_name`, `source_date`, `target_date`, JSON payload columns, `retention_expires_utc`, `truncated`, `revision`, timestamps |
|
||||||
|
| `application_operations` | Committed operation records for idempotency | `operation_id TEXT PRIMARY KEY`, `owner_id`, `operation_name`, `committed_at_utc` |
|
||||||
|
| `notice_acknowledgements` | Owner-scoped acknowledged notice records | composite key `owner_id, notice_id`, `acknowledged_at_utc`, `revision`, timestamps |
|
||||||
|
|
||||||
* All timestamps are stored as **UTC**.
|
* UTC timestamps are stored in Drift `DateTime` columns. Civil dates and wall
|
||||||
* Optimistic concurrency: every mutable row carries `revision`. Updates must
|
times remain text fields where the domain model requires calendar-local values.
|
||||||
include `WHERE revision = :expected` and increment on success.
|
* Mutable domain/application records carry optimistic `revision` values. Updates
|
||||||
* Drift migrations are generated and tested; downgrades are not supported.
|
must compare the expected revision and increment on success. Append-only
|
||||||
|
activity and operation records are immutable facts and do not use mutable-save
|
||||||
|
revision checks.
|
||||||
|
* Schema version 2 migrations create the application-layer tables and indexes
|
||||||
|
needed for task activity, project statistics, idempotent operations, and notice
|
||||||
|
acknowledgements.
|
||||||
|
|
||||||
## Consequences
|
## Consequences
|
||||||
* Domain objects remain unchanged; adapters translate.
|
|
||||||
* Backup library (Block 24) can copy and encrypt this single file.
|
* Domain objects remain unchanged; adapters translate between domain objects and
|
||||||
* Repository contract tests assert schema integrity via Drift introspection.
|
SQLite rows.
|
||||||
|
* The backup library copies and encrypts this single SQLite file.
|
||||||
|
* Repository conformance tests cover adapter behavior, while Drift schema tests
|
||||||
|
verify version 2 tables and version 1 upgrade behavior.
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,33 @@
|
||||||
|
<!-- SPDX-FileCopyrightText: 2026 FocusFlow contributors -->
|
||||||
|
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
|
||||||
|
|
||||||
# V1 ADR 005: Export & Backup Boundary
|
# V1 ADR 005: Export & Backup Boundary
|
||||||
|
|
||||||
Status: **Accepted**
|
Status: **Accepted; updated to match current app state**
|
||||||
|
|
||||||
Date: 2026-06-26
|
Date: 2026-06-26
|
||||||
|
|
||||||
* **Backup** – passphrase‑encrypted SQLite copy (`.sqlite.aes` via AES‑256‑GCM, PBKDF2 200 k rounds).
|
Last reviewed: 2026-07-07
|
||||||
* **Readable export** – JSON and CSV via `ExportController`.
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
* **Backup**: passphrase-encrypted copy of the local SQLite database bytes.
|
||||||
|
Defaults:
|
||||||
|
* source DB: `~/ADHD_Scheduler/scheduler.sqlite`
|
||||||
|
* backup directory: `~/ADHD_Scheduler/backups`
|
||||||
|
* backup filename: `yyyymmdd_hhmm.db.enc`
|
||||||
|
* **Backup cryptography**: AES-256-GCM with PBKDF2-HMAC-SHA256 key derivation,
|
||||||
|
200,000 iterations, random salt, and random nonce. The encoded file is a JSON
|
||||||
|
envelope containing format metadata, salt, nonce, ciphertext, and MAC.
|
||||||
|
* **Restore**: decrypts a `.db.enc` backup and atomically replaces the target
|
||||||
|
SQLite file.
|
||||||
|
* **Readable export**: JSON and CSV are produced through `ExportController` and
|
||||||
|
export writer implementations.
|
||||||
|
|
||||||
|
## Boundary
|
||||||
|
|
||||||
|
Encrypted backup is the restorable binary preservation path for the SQLite
|
||||||
|
runtime database. JSON and CSV exports are readable user/data-portability outputs
|
||||||
|
and are not the canonical restore format.
|
||||||
|
|
||||||
No open questions remain.
|
No open questions remain.
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,32 @@
|
||||||
|
<!-- SPDX-FileCopyrightText: 2026 FocusFlow contributors -->
|
||||||
|
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
|
||||||
|
|
||||||
# V1 ADR 006: Schedule Snapshot Policy
|
# V1 ADR 006: Schedule Snapshot Policy
|
||||||
|
|
||||||
Status: **Accepted**
|
Status: **Accepted; updated to match current app state**
|
||||||
|
|
||||||
Date: 2026-06-26
|
Date: 2026-06-26
|
||||||
|
|
||||||
Persist committed diagnostic snapshots only; bounded retention 30 days or until notices acknowledged.
|
Last reviewed: 2026-07-07
|
||||||
|
|
||||||
No open questions remain.
|
## Decision
|
||||||
|
|
||||||
|
Persist committed diagnostic schedule snapshots only. Snapshots are diagnostic
|
||||||
|
records, not authoritative task state.
|
||||||
|
|
||||||
|
Snapshot retention is bounded by a `retention_expires_utc` timestamp derived from
|
||||||
|
the snapshot capture time plus 30 days. Repository cleanup deletes snapshots whose
|
||||||
|
retention expiry is before the cleanup instant.
|
||||||
|
|
||||||
|
Notice acknowledgement is stored separately in `notice_acknowledgements`. The
|
||||||
|
Today read model suppresses acknowledged notices when presenting pending notice
|
||||||
|
state, but acknowledgement does not delete the source snapshot early.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
* Snapshot rows can remain available for diagnostics until their retention
|
||||||
|
expiry even after all displayed notices have been acknowledged.
|
||||||
|
* Notice acknowledgement is an application-layer read-model concern, not a
|
||||||
|
snapshot lifecycle rule.
|
||||||
|
* Cleanup jobs and tests should target `retention_expires_utc`, not notice
|
||||||
|
acknowledgement status.
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,43 @@
|
||||||
|
<!-- SPDX-FileCopyrightText: 2026 FocusFlow contributors -->
|
||||||
|
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
|
||||||
|
|
||||||
# V1 ADR 008: Notification Abstraction
|
# V1 ADR 008: Notification Abstraction
|
||||||
|
|
||||||
Status: **Accepted**
|
Status: **Accepted; updated to match current app state**
|
||||||
|
|
||||||
Date: 2026-06-26
|
Date: 2026-06-26
|
||||||
|
|
||||||
`NotificationAdapter` interface abstracts scheduling/cancellation and feedback stream. Desktop implementation in `scheduler_notifications_desktop`; Fake adapter for tests.
|
Last reviewed: 2026-07-07
|
||||||
|
|
||||||
No open questions remain.
|
## Decision
|
||||||
|
|
||||||
|
`NotificationAdapter` in `scheduler_notifications` is the platform-neutral
|
||||||
|
boundary for reminder delivery. It exposes:
|
||||||
|
|
||||||
|
* `schedule(NotificationRequest request)`
|
||||||
|
* `cancel(String id)`
|
||||||
|
* `Stream<NotificationFeedback> feedback`
|
||||||
|
|
||||||
|
The pure scheduler core computes reminder policy and directive values only. It
|
||||||
|
does not import desktop, OS, process, or notification APIs.
|
||||||
|
|
||||||
|
The test fake, `FakeNotificationAdapter`, stores scheduled requests,
|
||||||
|
normalizes/captures cancellations, and can emit feedback events for workflow
|
||||||
|
tests.
|
||||||
|
|
||||||
|
The desktop implementation lives in `scheduler_notifications_desktop`.
|
||||||
|
It delegates to platform backends:
|
||||||
|
|
||||||
|
* Linux: `notify-send`
|
||||||
|
* macOS: `osascript`
|
||||||
|
* Windows and unsupported platforms: stdout fallback
|
||||||
|
|
||||||
|
The current desktop adapter satisfies the feedback-stream contract with an empty
|
||||||
|
stream. OS-level click/action feedback is not implemented in V1.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
* Reminder delivery remains replaceable at the composition boundary.
|
||||||
|
* Core tests can use the fake adapter without depending on platform APIs.
|
||||||
|
* Future OS feedback support must extend the desktop package without changing
|
||||||
|
the scheduler core boundary.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
<!-- SPDX-FileCopyrightText: 2026 FocusFlow contributors -->
|
||||||
|
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
|
||||||
|
|
||||||
|
# V1 ADR 002: SQLite Document Schema V1
|
||||||
|
|
||||||
|
Status: **Accepted**
|
||||||
|
Date: 2026-06-26
|
||||||
|
|
||||||
|
## Context
|
||||||
|
The project pivoted from MongoDB to **SQLite‑first** persistence.
|
||||||
|
We need a stable, versioned relational schema that maps cleanly to the domain
|
||||||
|
objects while staying behind the repository abstraction. Drift will manage
|
||||||
|
migrations.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
* **Database file**: `adhd_scheduler.sqlite` in the user data directory.
|
||||||
|
* **Schema version**: `1` (managed by Drift).
|
||||||
|
* **Tables**
|
||||||
|
|
||||||
|
| Table | Purpose | Key fields |
|
||||||
|
|-------|---------|------------|
|
||||||
|
| `tasks` | Authoritative task rows | `id TEXT PRIMARY KEY`, `owner_id TEXT`, `project_id`, `parent_id`, `type`, `status`, `priority`, `reward`, `difficulty`, `scheduled_start_utc`, `scheduled_end_utc`, `actual_start_utc`, `actual_end_utc`, `completed_at_utc`, `revision INT`, `created_at_utc`, `updated_at_utc`, `backlog_entered_at_utc`, `backlog_entered_provenance TEXT` |
|
||||||
|
| `projects` | Project configuration | `id TEXT PRIMARY KEY`, `owner_id`, `name`, `color_key`, config defaults …, `archived_at_utc`, `revision` |
|
||||||
|
| `locked_blocks` | Recurring / one‑off locked time | `id TEXT PRIMARY KEY`, `owner_id`, `name`, `date TEXT`, `start_time TEXT`, `end_time TEXT`, `recurrence_json`, `hidden_by_default INT`, `archived_at_utc`, `revision` |
|
||||||
|
| `locked_overrides` | Date‑scoped overrides | `id TEXT PRIMARY KEY`, `owner_id`, `locked_block_id`, `date TEXT`, `type`, JSON fields |
|
||||||
|
| `settings` | One row per owner | `owner_id TEXT PRIMARY KEY`, `timezone_id`, `day_start_minutes`, `day_end_minutes`, `compact_mode INT`, `revision` |
|
||||||
|
| `activities` | Append‑only internal events | `id TEXT PRIMARY KEY`, `owner_id`, `task_id`, `code`, `occurred_at_utc`, `metadata_json` |
|
||||||
|
| `snapshots` | Bounded diagnostic schedule snapshots | `id TEXT PRIMARY KEY`, `owner_id`, `operation_name`, `source_date TEXT`, `window_json`, `notice_json`, `changes_json`, `retention_expires_utc` |
|
||||||
|
|
||||||
|
* All timestamps are stored as **UTC**.
|
||||||
|
* Optimistic concurrency: every mutable row carries `revision`. Updates must
|
||||||
|
include `WHERE revision = :expected` and increment on success.
|
||||||
|
* Drift migrations are generated and tested; downgrades are not supported.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
* Domain objects remain unchanged; adapters translate.
|
||||||
|
* Backup library (Block 24) can copy and encrypt this single file.
|
||||||
|
* Repository contract tests assert schema integrity via Drift introspection.
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
<!-- SPDX-FileCopyrightText: 2026 FocusFlow contributors -->
|
||||||
|
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
|
||||||
|
|
||||||
|
# V1 ADR 005: Export & Backup Boundary
|
||||||
|
|
||||||
|
Status: **Accepted**
|
||||||
|
Date: 2026-06-26
|
||||||
|
|
||||||
|
* **Backup** – passphrase‑encrypted SQLite copy (`.sqlite.aes` via AES‑256‑GCM, PBKDF2 200 k rounds).
|
||||||
|
* **Readable export** – JSON and CSV via `ExportController`.
|
||||||
|
|
||||||
|
No open questions remain.
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<!-- SPDX-FileCopyrightText: 2026 FocusFlow contributors -->
|
||||||
|
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
|
||||||
|
|
||||||
|
# V1 ADR 006: Schedule Snapshot Policy
|
||||||
|
|
||||||
|
Status: **Accepted**
|
||||||
|
Date: 2026-06-26
|
||||||
|
|
||||||
|
Persist committed diagnostic snapshots only; bounded retention 30 days or until notices acknowledged.
|
||||||
|
|
||||||
|
No open questions remain.
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<!-- SPDX-FileCopyrightText: 2026 FocusFlow contributors -->
|
||||||
|
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
|
||||||
|
|
||||||
|
# V1 ADR 008: Notification Abstraction
|
||||||
|
|
||||||
|
Status: **Accepted**
|
||||||
|
Date: 2026-06-26
|
||||||
|
|
||||||
|
`NotificationAdapter` interface abstracts scheduling/cancellation and feedback stream. Desktop implementation in `scheduler_notifications_desktop`; Fake adapter for tests.
|
||||||
|
|
||||||
|
No open questions remain.
|
||||||
Loading…
Reference in a new issue