Sync reminders, and each application's submission and final response, with a CalDAV task list (VTODO) #12

Open
opened 2026-09-30 20:30:03 +00:00 by tiagoagueda · 3 comments
Owner

Over CalDAV, postulo-dav syncs interviews only: its parser drops VTODO (src/postulo_dav/text.py), and the calendar it makes takes VEVENT alone (src/postulo_dav/dav.py), for good (RFC 4791 §5.2.3). The ask: "The TODO component (tasks) is what best resembles a application submission, an update, a final response, a reminder. Once again, 'paired' to Postulo element based on a URI."

Only a reminder is an editable record of its own; a submission (a status change stamping applied_at), a final response (an ending) and an update (an append-only timeline entry) are not, so those mappings are readings. "Based on a URI" is read as #7 settles it everywhere: list plus UID as the key, the task's address (which a move changes) a cache.

What the plugin should do

A task list of its own, since that calendar cannot hold tasks: chosen by address in #13, with its own switch, or made by the first sync (never by Test or in read-only). A server without tasks gets a note, not a failed run. By their documentation (untried here), Nextcloud, Radicale, Fastmail and DAVx5's task apps carry tasks; Google CalDAV and upgraded iCloud Reminders do not, as the README should say.

Reminders, both ways: SUMMARY, DUE, STATUS, COMPLETED in UTC. From the phone, an edit changes the reminder, a moved DUE postpones it, a tick completes it at its moment, an untick reopens it, CANCELLED completes it. A task made in the list becomes a reminder, about an application if made under its task (RELATED-TO); one without DUE (optional in RFC 5545) stays unadopted, with a note. Done ones leave after 90 days (HISTORY), as interviews do. An interview's reminder, already its event's VALARM, gets no task while events are on.

Submission: a draft application has an Apply to <role> at <company> task, DUE its deadline, COMPLETED at applied_at, CANCELLED if withdrawn unsent. A tick files suggest(..., suggested_status="applied") with the tick's moment as occurred_at, which accepting stamps into applied_at; declined, the next run unticks it. As suggest never files one external_id twice, even once declined (src/postulo/applications/suggestions.py in the core), the id is the UID plus COMPLETED.

Final response: a sent, open application has a Hear back from <company> about <role> task, COMPLETED when accepted or rejected, CANCELLED when withdrawn or ghosted. An offer, not an ending, leaves it open.

Writing tasks: only these properties are written; what else the phone set (DESCRIPTION, CATEGORIES, its own alarms) is kept. PRIORITY goes on milestone tasks from the application's, inverted (RFC 5545 §3.8.1.9: 1 highest, 9 lowest): high 1, normal 5, low 9. Nesting is tracked: a VALARM's DESCRIPTION never lands on its task (the fault #11 fixes). A task deleted on the phone flags its link; the reminder stays. Deleting here removes only tasks Postulo made, and cancels the person's own.

Decisions

  1. VTODO-only list or mixed calendar. Recommend VTODO-only, since clients tell a task list by its component set (DAVx5 does). Nextcloud accepting one from a client is unverified; its Calendar app cannot make one (nextcloud/server#41014).
  2. Ticks on milestone tasks. Recommend a suggestion for Apply, Hear-back pushed one way (a tick cannot say which ending; the next push undoes it). Alternatives: change_status on the surface, which postulo/postulo#331 advises against; a status-less suggestion.
  3. "An update". Recommend reading it as something still to do, such as chasing: already a reminder. Alternatives: every timeline entry, status changes included, as a done subtask (a flooded list); any company answer setting Hear-back IN-PROCESS.
  4. Grouping. Recommend REFID (RFC 9253) on an application's tasks, and its reminders under its open milestone task by RELATED-TO (RFC 5545 §3.8.4.5), which Nextcloud Tasks shows as subtasks (others unverified). Alternatives: a parent task per application; none.
  5. Adopting new tasks. Recommend only in Postulo's own list, pairing by hand elsewhere, so errands never become reminders (postulo/postulo#16).

Needs from the core

  • postulo/postulo#331: Reminder and its services, for the reminder half. The milestone tasks need only Application, suggest and SyncLink, all on the surface, so can come first.
  • postulo/postulo#326: synclink_one_per_record allows one twin per record per connection; an application may have two tasks and a deadline event (#11). Until then, one milestone task per application, moving to Hear-back once sent.
  • postulo/postulo#327: pairing a task with an existing reminder or application; until then, only tasks Postulo made or adopted.

New strings go through uv run postulo-messages extract, fr-fr and pt-pt filled on the commit (the test gates them), the 23 EU catalogues before a tag (uv run pytest -m release). New surface names mean repinning the core (the pyproject.toml dev group pins 1d6cdd7e0) and extending tests/test_surface.py.

Done when

In tests/test_dav.py:

  • a reminder round-trips as a task (edit, tick at its moment, untick, moved DUE); a task made under an application's task becomes its reminder;
  • an Apply tick files one suggestion; accepting stamps applied_at with the tick's moment, declining unticks it;
  • Hear-back closes as the application ends; a tick on the phone does not stick;
  • a VALARM never overwrites a task's DESCRIPTION; unwritten properties survive a push; Test and read-only make no list; a server without tasks leaves the rest syncing.

From the list of DAV plugin improvements of 2026-09-30.

Over CalDAV, postulo-dav syncs interviews only: its parser drops `VTODO` (`src/postulo_dav/text.py`), and the calendar it makes takes `VEVENT` alone (`src/postulo_dav/dav.py`), for good (RFC 4791 §5.2.3). The ask: "The TODO component (tasks) is what best resembles a application submission, an update, a final response, a reminder. Once again, 'paired' to Postulo element based on a URI." Only a reminder is an editable record of its own; a submission (a status change stamping `applied_at`), a final response (an ending) and an update (an append-only timeline entry) are not, so those mappings are readings. "Based on a URI" is read as #7 settles it everywhere: list plus UID as the key, the task's address (which a move changes) a cache. ## What the plugin should do **A task list of its own**, since that calendar cannot hold tasks: chosen by address in #13, with its own switch, or made by the first sync (never by *Test* or in read-only). A server without tasks gets a note, not a failed run. By their documentation (untried here), Nextcloud, Radicale, Fastmail and DAVx5's task apps carry tasks; Google CalDAV and upgraded iCloud Reminders do not, as the README should say. **Reminders, both ways**: `SUMMARY`, `DUE`, `STATUS`, `COMPLETED` in UTC. From the phone, an edit changes the reminder, a moved `DUE` postpones it, a tick completes it at its moment, an untick reopens it, `CANCELLED` completes it. A task made in the list becomes a reminder, about an application if made under its task (`RELATED-TO`); one without `DUE` (optional in RFC 5545) stays unadopted, with a note. Done ones leave after 90 days (`HISTORY`), as interviews do. An interview's reminder, already its event's `VALARM`, gets no task while events are on. **Submission**: a draft application has an `Apply to <role> at <company>` task, `DUE` its deadline, `COMPLETED` at `applied_at`, `CANCELLED` if withdrawn unsent. A tick files `suggest(..., suggested_status="applied")` with the tick's moment as `occurred_at`, which accepting stamps into `applied_at`; declined, the next run unticks it. As `suggest` never files one `external_id` twice, even once declined (`src/postulo/applications/suggestions.py` in the core), the id is the UID plus `COMPLETED`. **Final response**: a sent, open application has a `Hear back from <company> about <role>` task, `COMPLETED` when accepted or rejected, `CANCELLED` when withdrawn or ghosted. An offer, not an ending, leaves it open. **Writing tasks**: only these properties are written; what else the phone set (`DESCRIPTION`, `CATEGORIES`, its own alarms) is kept. `PRIORITY` goes on milestone tasks from the application's, inverted (RFC 5545 §3.8.1.9: 1 highest, 9 lowest): high 1, normal 5, low 9. Nesting is tracked: a `VALARM`'s `DESCRIPTION` never lands on its task (the fault #11 fixes). A task deleted on the phone flags its link; the reminder stays. Deleting here removes only tasks Postulo made, and cancels the person's own. ## Decisions 1. **VTODO-only list or mixed calendar.** Recommend VTODO-only, since clients tell a task list by its component set (DAVx5 does). Nextcloud accepting one from a client is unverified; its Calendar app cannot make one (nextcloud/server#41014). 2. **Ticks on milestone tasks.** Recommend a suggestion for Apply, Hear-back pushed one way (a tick cannot say which ending; the next push undoes it). Alternatives: `change_status` on the surface, which postulo/postulo#331 advises against; a status-less suggestion. 3. **"An update".** Recommend reading it as something still to do, such as chasing: already a reminder. Alternatives: every timeline entry, status changes included, as a done subtask (a flooded list); any company answer setting Hear-back `IN-PROCESS`. 4. **Grouping.** Recommend `REFID` (RFC 9253) on an application's tasks, and its reminders under its open milestone task by `RELATED-TO` (RFC 5545 §3.8.4.5), which Nextcloud Tasks shows as subtasks (others unverified). Alternatives: a parent task per application; none. 5. **Adopting new tasks.** Recommend only in Postulo's own list, pairing by hand elsewhere, so errands never become reminders (postulo/postulo#16). ## Needs from the core - postulo/postulo#331: `Reminder` and its services, for the reminder half. The milestone tasks need only `Application`, `suggest` and `SyncLink`, all on the surface, so can come first. - postulo/postulo#326: `synclink_one_per_record` allows one twin per record per connection; an application may have two tasks and a deadline event (#11). Until then, one milestone task per application, moving to Hear-back once sent. - postulo/postulo#327: pairing a task with an existing reminder or application; until then, only tasks Postulo made or adopted. New strings go through `uv run postulo-messages extract`, fr-fr and pt-pt filled on the commit (the test gates them), the 23 EU catalogues before a tag (`uv run pytest -m release`). New surface names mean repinning the core (the `pyproject.toml` dev group pins `1d6cdd7e0`) and extending `tests/test_surface.py`. ## Done when In `tests/test_dav.py`: - a reminder round-trips as a task (edit, tick at its moment, untick, moved `DUE`); a task made under an application's task becomes its reminder; - an Apply tick files one suggestion; accepting stamps `applied_at` with the tick's moment, declining unticks it; - Hear-back closes as the application ends; a tick on the phone does not stick; - a `VALARM` never overwrites a task's `DESCRIPTION`; unwritten properties survive a push; *Test* and read-only make no list; a server without tasks leaves the rest syncing. From the list of DAV plugin improvements of 2026-09-30.
Author
Owner

From postulo/postulo-dav#14, which closed with 32cca53: one thing it asked for can only be done here.

A LANGUAGE parameter found on a task made elsewhere survives a push. The parameter is a BCP 47 language tag (RFC 5545 §3.2.10), and this issue is where a task is patched instead of rebuilt, so it is where one that is already there must be kept as it was written. If Postulo ever writes one of its own, it writes the tag in its canonical form, as the core does since postulo/postulo#337 (pt-BR, not pt-br).

Worth a test of its own here: a task carrying LANGUAGE=pt-BR on a property comes back from a push with it.

From postulo/postulo-dav#14, which closed with `32cca53`: one thing it asked for can only be done here. **A `LANGUAGE` parameter found on a task made elsewhere survives a push.** The parameter is a BCP 47 language tag (RFC 5545 §3.2.10), and this issue is where a task is patched instead of rebuilt, so it is where one that is already there must be kept as it was written. If Postulo ever writes one of its own, it writes the tag in its canonical form, as the core does since postulo/postulo#337 (`pt-BR`, not `pt-br`). Worth a test of its own here: a task carrying `LANGUAGE=pt-BR` on a property comes back from a push with it.
Author
Owner

The maintainer's list of 2026-10-02 asks for iCalendar as the format for events and dated things. A core issue now covers part of this one: postulo/postulo#661.

It exports a reminder as a VTODO (SUMMARY, DUE, STATUS, COMPLETED, RELATED-TO the application), a mapping it offers on the plugin surface. It also proposes a stored uid on Reminder, which has none today (src/postulo/applications/models.py:600 in the core), because pairing by list and UID needs one. Both fit the reminders half of this issue.

This issue keeps its own scope: the task list, the “Apply to” and “Hear back” tasks, suggest on a tick, grouping, and writing only the properties Postulo owns.

From the list of improvements of 2026-10-02.

The maintainer's list of 2026-10-02 asks for iCalendar as the format for events and dated things. A core issue now covers part of this one: postulo/postulo#661. It exports a reminder as a `VTODO` (`SUMMARY`, `DUE`, `STATUS`, `COMPLETED`, `RELATED-TO` the application), a mapping it offers on the plugin surface. It also proposes a stored `uid` on `Reminder`, which has none today (`src/postulo/applications/models.py:600` in the core), because pairing by list and UID needs one. Both fit the reminders half of this issue. This issue keeps its own scope: the task list, the “Apply to” and “Hear back” tasks, `suggest` on a tick, grouping, and writing only the properties Postulo owns. From the list of improvements of 2026-10-02.
Author
Owner

From the status review of 2026-10-30: suggested order for this repository is this issue first (reminders and deadlines as tasks are what a phone shows every day), then #5 and #6 (WebDAV store and backup destination), which together make the plugin the Nextcloud integration most self-hosters look for.

From the status review of 2026-10-30: suggested order for this repository is this issue first (reminders and deadlines as tasks are what a phone shows every day), then #5 and #6 (WebDAV store and backup destination), which together make the plugin the Nextcloud integration most self-hosters look for.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Postulo/postulo-dav#12
No description provided.