A locked-decisions map — not a formal spec doc, no code written by this effort — covering the calendar-backend groundwork needed before implementation starts: the local SQLite cache/store (designed generically enough that RSS/Diun can plug in later, per docs/research/smart-tablet-screen-additions.md), the calendar sync approach for all three account types (Mail-in-a-Box/Nextcloud CalDAV, Microsoft Graph, Nextcloud Tasks/VTODO), and the Docker groundwork (FrankenPHP dev/prod image, docker-compose + override) everything will run behind. Done when nothing is left to decide before a Symfony project can be bootstrapped and the schema/sync/Docker decisions executed.
Notes
Domain: single-context repo, no CONTEXT.md/ADRs yet (greenfield) — /domain-modeling should create them lazily as terms/decisions get pinned down on tickets.
Prior research already done and settled (see Decisions so far): docs/research/smart-tablet-screen-stack.md, docs/research/smart-tablet-screen-config-secrets.md, docs/research/smart-tablet-screen-nextcloud-todos.md, docs/research/smart-tablet-screen-calendar-webhooks.md. docs/research/smart-tablet-screen-additions.md (Diun/RSS) is background only — informs the generic schema shape, not ticketed in this map.
Access layer decided: Doctrine DBAL (not full ORM) + hand-defined typed PHP entities/DTOs hydrated from DBAL rows — not raw arrays.
Docker decided: FrankenPHP-based image, docker-compose.yml + docker-compose.override.yml for local dev, just the Symfony container (no reverse proxy — stays external on the host), SQLite on a named volume at var/.
Symfony project bootstrap itself is deliberately NOT a decision ticket on this map — it's mechanical Task work once the decisions below are locked (see Not yet specified).
Grilling tickets on this map: always invoke /grilling and /domain-modeling.
Decisions so far
Calendar sync protocol per account type — CalDAV (RFC 4791) against Nextcloud's /remote.php/dav/calendars/<user>/ for Mail-in-a-Box accounts; Microsoft Graph API (OAuth2, tenant=common, offline_access) for Outlook/Exchange — CalDAV isn't offered for Outlook.com and EWS is being retired.
Multi-account config & secrets storage approach — Symfony Configuration tree (useAttributeAsKey() prototype, mirroring Doctrine's dbal.connections/Messenger's transports) for the account list structure in config/packages/smart_screen.yaml, with actual secret strings (app-passwords, client secret, refresh token) in Symfony's native secrets vault, referenced via %env(...)%.
Nextcloud Tasks/VTODO integration approach — same CalDAV protocol/client (sabre/dav + sabre/vobject) as events, filtered to VTODO via a calendar-query REPORT, same app-password auth — no separate integration needed.
Calendar/Graph webhook vs polling support — sync-token polling (WebDAV RFC 6578) for Nextcloud/CalDAV (Mail-in-a-Box's bundled Nextcloud is 3 major versions behind the Webhooks app's minimum); real Microsoft Graph webhook subscriptions (/subscriptions on the event resource, max ~7-day duration) with a scheduled renewal job, plus low-frequency polling fallback for resilience.
FrankenPHP Docker image setup for dev/prod — base image dunglas/frankenphp:1-php8.5 (Debian); FrankenPHP's own Symfony guide (the dunglas/symfony-docker reference: shared base stage → dev stage w/ Xdebug+watch → prod_builder/prod stages); worker mode left off in prod by default (needs an app audit for state leaks first); pdo_sqlite + sodium confirmed enabled out of the box. Compose: base docker-compose.yml (single service, prod-shaped) + docker-compose.override.yaml for dev, with a namedvar/ volume (not anonymous) so the SQLite cache persists across rebuilds.
Not yet specified
Symfony project bootstrap (composer create-project, FrankenPHP scaffold) — mechanical Task once the entity-schema and Docker tickets close; tickets when the decisions below are locked (this map or the next).
RSS feed and Diun webhook event storage — the cache schema is designed generically enough to fit them (per the additions.md research), but they get no tickets of their own on this map; a future map picks up their concrete integration once the calendar slice is built.
Exact SQLite migration/versioning workflow (Doctrine Migrations Bundle setup specifics) — follows once the entity schema ticket closes.
Out of scope
(none yet)
## Destination
A locked-decisions map — not a formal spec doc, no code written by this effort — covering the calendar-backend groundwork needed before implementation starts: the local SQLite cache/store (designed generically enough that RSS/Diun can plug in later, per `docs/research/smart-tablet-screen-additions.md`), the calendar sync approach for all three account types (Mail-in-a-Box/Nextcloud CalDAV, Microsoft Graph, Nextcloud Tasks/VTODO), and the Docker groundwork (FrankenPHP dev/prod image, docker-compose + override) everything will run behind. Done when nothing is left to decide before a Symfony project can be bootstrapped and the schema/sync/Docker decisions executed.
## Notes
- Domain: single-context repo, no `CONTEXT.md`/ADRs yet (greenfield) — `/domain-modeling` should create them lazily as terms/decisions get pinned down on tickets.
- Prior research already done and settled (see Decisions so far): `docs/research/smart-tablet-screen-stack.md`, `docs/research/smart-tablet-screen-config-secrets.md`, `docs/research/smart-tablet-screen-nextcloud-todos.md`, `docs/research/smart-tablet-screen-calendar-webhooks.md`. `docs/research/smart-tablet-screen-additions.md` (Diun/RSS) is background only — informs the generic schema shape, not ticketed in this map.
- Access layer decided: Doctrine DBAL (not full ORM) + hand-defined typed PHP entities/DTOs hydrated from DBAL rows — not raw arrays.
- Docker decided: FrankenPHP-based image, `docker-compose.yml` + `docker-compose.override.yml` for local dev, just the Symfony container (no reverse proxy — stays external on the host), SQLite on a named volume at `var/`.
- Symfony project bootstrap itself is deliberately NOT a decision ticket on this map — it's mechanical Task work once the decisions below are locked (see Not yet specified).
- Grilling tickets on this map: always invoke `/grilling` and `/domain-modeling`.
## Decisions so far
- [Calendar sync protocol per account type](https://git.arthurerlich.de/haylan/Smart-Tablet-Screen/src/branch/main/docs/research/smart-tablet-screen-stack.md) — CalDAV (RFC 4791) against Nextcloud's `/remote.php/dav/calendars/<user>/` for Mail-in-a-Box accounts; Microsoft Graph API (OAuth2, `tenant=common`, `offline_access`) for Outlook/Exchange — CalDAV isn't offered for Outlook.com and EWS is being retired.
- [Multi-account config & secrets storage approach](https://git.arthurerlich.de/haylan/Smart-Tablet-Screen/src/branch/main/docs/research/smart-tablet-screen-config-secrets.md) — Symfony `Configuration` tree (`useAttributeAsKey()` prototype, mirroring Doctrine's `dbal.connections`/Messenger's `transports`) for the account list structure in `config/packages/smart_screen.yaml`, with actual secret strings (app-passwords, client secret, refresh token) in Symfony's native secrets vault, referenced via `%env(...)%`.
- [Nextcloud Tasks/VTODO integration approach](https://git.arthurerlich.de/haylan/Smart-Tablet-Screen/src/branch/main/docs/research/smart-tablet-screen-nextcloud-todos.md) — same CalDAV protocol/client (`sabre/dav` + `sabre/vobject`) as events, filtered to `VTODO` via a `calendar-query` REPORT, same app-password auth — no separate integration needed.
- [Calendar/Graph webhook vs polling support](https://git.arthurerlich.de/haylan/Smart-Tablet-Screen/issues/2) — sync-token polling (WebDAV RFC 6578) for Nextcloud/CalDAV (Mail-in-a-Box's bundled Nextcloud is 3 major versions behind the Webhooks app's minimum); real Microsoft Graph webhook subscriptions (`/subscriptions` on the `event` resource, max ~7-day duration) with a scheduled renewal job, plus low-frequency polling fallback for resilience.
- [FrankenPHP Docker image setup for dev/prod](https://git.arthurerlich.de/haylan/Smart-Tablet-Screen/issues/3) — base image `dunglas/frankenphp:1-php8.5` (Debian); FrankenPHP's own Symfony guide (the `dunglas/symfony-docker` reference: shared base stage → `dev` stage w/ Xdebug+watch → `prod_builder`/`prod` stages); worker mode left off in prod by default (needs an app audit for state leaks first); `pdo_sqlite` + `sodium` confirmed enabled out of the box. Compose: base `docker-compose.yml` (single service, prod-shaped) + `docker-compose.override.yaml` for dev, with a **named** `var/` volume (not anonymous) so the SQLite cache persists across rebuilds.
## Not yet specified
- Symfony project bootstrap (`composer create-project`, FrankenPHP scaffold) — mechanical Task once the entity-schema and Docker tickets close; tickets when the decisions below are locked (this map or the next).
- RSS feed and Diun webhook event storage — the cache schema is designed generically enough to fit them (per the additions.md research), but they get no tickets of their own on this map; a future map picks up their concrete integration once the calendar slice is built.
- Exact SQLite migration/versioning workflow (Doctrine Migrations Bundle setup specifics) — follows once the entity schema ticket closes.
## Out of scope
(none yet)
Decision recorded: Calendar/Graph webhook vs polling support closed — sync-token polling (RFC 6578) for Nextcloud/CalDAV (Mail-in-a-Box's bundled Nextcloud is too old for the Webhooks app), real webhook subscriptions + scheduled renewal job for Microsoft Graph. See docs/research/smart-tablet-screen-calendar-webhooks.md. This unblocks #4 and #5.
Decision recorded: [Calendar/Graph webhook vs polling support](https://git.arthurerlich.de/haylan/Smart-Tablet-Screen/issues/2) closed — sync-token polling (RFC 6578) for Nextcloud/CalDAV (Mail-in-a-Box's bundled Nextcloud is too old for the Webhooks app), real webhook subscriptions + scheduled renewal job for Microsoft Graph. See docs/research/smart-tablet-screen-calendar-webhooks.md. This unblocks #4 and #5.
Decision recorded: FrankenPHP Docker image setup for dev/prod closed — base image dunglas/frankenphp:1-php8.5 (Debian), FrankenPHP's own Symfony guide (dunglas/symfony-docker reference: shared base stage -> dev stage w/ Xdebug+watch -> prod_builder/prod stages), worker mode left off in prod by default (needs an app audit first), pdo_sqlite + sodium confirmed enabled out of the box. Compose: base docker-compose.yml (single service, prod-shaped) + docker-compose.override.yaml for dev (bind-mount source, dev target, dev env), with a NAMED var/ volume (not anonymous) so the SQLite cache persists across rebuilds. See docs/research/smart-tablet-screen-frankenphp-docker.md. This unblocks #4 and #5 — both are now on the frontier.
Decision recorded: [FrankenPHP Docker image setup for dev/prod](https://git.arthurerlich.de/haylan/Smart-Tablet-Screen/issues/3) closed — base image dunglas/frankenphp:1-php8.5 (Debian), FrankenPHP's own Symfony guide (dunglas/symfony-docker reference: shared base stage -> dev stage w/ Xdebug+watch -> prod_builder/prod stages), worker mode left off in prod by default (needs an app audit first), pdo_sqlite + sodium confirmed enabled out of the box. Compose: base docker-compose.yml (single service, prod-shaped) + docker-compose.override.yaml for dev (bind-mount source, dev target, dev env), with a NAMED var/ volume (not anonymous) so the SQLite cache persists across rebuilds. See docs/research/smart-tablet-screen-frankenphp-docker.md. This unblocks #4 and #5 — both are now on the frontier.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Destination
A locked-decisions map — not a formal spec doc, no code written by this effort — covering the calendar-backend groundwork needed before implementation starts: the local SQLite cache/store (designed generically enough that RSS/Diun can plug in later, per
docs/research/smart-tablet-screen-additions.md), the calendar sync approach for all three account types (Mail-in-a-Box/Nextcloud CalDAV, Microsoft Graph, Nextcloud Tasks/VTODO), and the Docker groundwork (FrankenPHP dev/prod image, docker-compose + override) everything will run behind. Done when nothing is left to decide before a Symfony project can be bootstrapped and the schema/sync/Docker decisions executed.Notes
CONTEXT.md/ADRs yet (greenfield) —/domain-modelingshould create them lazily as terms/decisions get pinned down on tickets.docs/research/smart-tablet-screen-stack.md,docs/research/smart-tablet-screen-config-secrets.md,docs/research/smart-tablet-screen-nextcloud-todos.md,docs/research/smart-tablet-screen-calendar-webhooks.md.docs/research/smart-tablet-screen-additions.md(Diun/RSS) is background only — informs the generic schema shape, not ticketed in this map.docker-compose.yml+docker-compose.override.ymlfor local dev, just the Symfony container (no reverse proxy — stays external on the host), SQLite on a named volume atvar/./grillingand/domain-modeling.Decisions so far
/remote.php/dav/calendars/<user>/for Mail-in-a-Box accounts; Microsoft Graph API (OAuth2,tenant=common,offline_access) for Outlook/Exchange — CalDAV isn't offered for Outlook.com and EWS is being retired.Configurationtree (useAttributeAsKey()prototype, mirroring Doctrine'sdbal.connections/Messenger'stransports) for the account list structure inconfig/packages/smart_screen.yaml, with actual secret strings (app-passwords, client secret, refresh token) in Symfony's native secrets vault, referenced via%env(...)%.sabre/dav+sabre/vobject) as events, filtered toVTODOvia acalendar-queryREPORT, same app-password auth — no separate integration needed./subscriptionson theeventresource, max ~7-day duration) with a scheduled renewal job, plus low-frequency polling fallback for resilience.dunglas/frankenphp:1-php8.5(Debian); FrankenPHP's own Symfony guide (thedunglas/symfony-dockerreference: shared base stage →devstage w/ Xdebug+watch →prod_builder/prodstages); worker mode left off in prod by default (needs an app audit for state leaks first);pdo_sqlite+sodiumconfirmed enabled out of the box. Compose: basedocker-compose.yml(single service, prod-shaped) +docker-compose.override.yamlfor dev, with a namedvar/volume (not anonymous) so the SQLite cache persists across rebuilds.Not yet specified
composer create-project, FrankenPHP scaffold) — mechanical Task once the entity-schema and Docker tickets close; tickets when the decisions below are locked (this map or the next).Out of scope
(none yet)
Decision recorded: Calendar/Graph webhook vs polling support closed — sync-token polling (RFC 6578) for Nextcloud/CalDAV (Mail-in-a-Box's bundled Nextcloud is too old for the Webhooks app), real webhook subscriptions + scheduled renewal job for Microsoft Graph. See docs/research/smart-tablet-screen-calendar-webhooks.md. This unblocks #4 and #5.
Decision recorded: FrankenPHP Docker image setup for dev/prod closed — base image dunglas/frankenphp:1-php8.5 (Debian), FrankenPHP's own Symfony guide (dunglas/symfony-docker reference: shared base stage -> dev stage w/ Xdebug+watch -> prod_builder/prod stages), worker mode left off in prod by default (needs an app audit first), pdo_sqlite + sodium confirmed enabled out of the box. Compose: base docker-compose.yml (single service, prod-shaped) + docker-compose.override.yaml for dev (bind-mount source, dev target, dev env), with a NAMED var/ volume (not anonymous) so the SQLite cache persists across rebuilds. See docs/research/smart-tablet-screen-frankenphp-docker.md. This unblocks #4 and #5 — both are now on the frontier.