Expose wire timestamps and sidebar folder groups (#1, #8, #3) #10

Merged
ethan merged 1 commit from feat/wire-timestamps-and-project-groups into main 2026-08-09 20:07:57 -05:00
Member

Closes #1, closes #8, closes #3.

All three issues guessed correctly that these fields were already on the wire and being dropped during payload flattening. Confirmed live against a real account on 2026-08-09.

#1 — modified_time (+ created_time)

The read side was the easy half. The half that isn't in the issue:

v2 stores whatever modifiedTime the client sends — it does not override it.

Every update method here is built by re-reading the existing raw payload and mutating it, so simply surfacing the field would have frozen modifiedTime at its original value on any item this server ever wrote. That's a watermark that exists and is permanently wrong, which is worse than one that's absent — the sync would silently stop seeing its own writes.

_drop_modified_time() now strips the field from every update payload so the server stamps the write.

Verified live:

write modified_time advanced?
title edit ✅
tag add (metadata only) ✅

That also answers the question the issue asked us to document: any write bumps it, not only content edits. Which means your own push bumps it too — the sync has to record what it wrote, or it will read its own writes back as user edits. That caveat is called out in TOOLS.md.

One consequence worth knowing before you build against it: a write response carries modified_time: null. The batch response returns only an etag, so the new instant isn't knowable without a re-read. Reads always have it.

#8 — completed_time

On tasks, null while open, so "not done" is never confused with "done at the epoch" as the acceptance list required.

#3 — list_project_groups()

Folder groups arrive on the same batch/check/0 full state as projects, under a top-level projectGroups key — no new endpoint needed. Deleted groups are filtered unless include_deleted=true.

The docs are explicit about the level confusion the issue flagged: a folder group holds projects; a column holds items inside one project.

Also included

Read-side Project.sort_order, since it fell out of the same model change. Worth flagging for #9: sidebar sort order is a positive integer where lower sorts higher (0, 393216, 655360, … in visible order), not the large negative the issue guessed. Recorded in DESIGN.md §11 so the #9 write side has the semantics already pinned down.

Testing

146 passed. Unit tests use a fake transport and structurally cannot catch the echo semantics, so the watermark contract also gets a live test (test_modified_time_bumps_on_every_write_live) that fails loudly if anyone reintroduces the echo. All 3 live tests pass against a real account.

Contracts documented in DESIGN.md §10 (timestamps + the echo rule) and §11 (groups + ordering).

🤖 Generated with Claude Code

https://claude.ai/code/session_012VU4wBhFAAosZLQcWqvHsf

Closes #1, closes #8, closes #3. All three issues guessed correctly that these fields were already on the wire and being dropped during payload flattening. Confirmed live against a real account on 2026-08-09. ## #1 — `modified_time` (+ `created_time`) The read side was the easy half. The half that isn't in the issue: **v2 stores whatever `modifiedTime` the client sends — it does not override it.** Every update method here is built by re-reading the existing raw payload and mutating it, so simply surfacing the field would have frozen `modifiedTime` at its original value on any item this server ever wrote. That's a watermark that exists and is *permanently wrong*, which is worse than one that's absent — the sync would silently stop seeing its own writes. `_drop_modified_time()` now strips the field from every update payload so the server stamps the write. Verified live: | write | `modified_time` advanced? | |---|---| | title edit | ✅ | | tag add (metadata only) | ✅ | That also answers the question the issue asked us to document: **any write bumps it**, not only content edits. Which means *your own push bumps it too* — the sync has to record what it wrote, or it will read its own writes back as user edits. That caveat is called out in `TOOLS.md`. One consequence worth knowing before you build against it: **a write response carries `modified_time: null`.** The batch response returns only an etag, so the new instant isn't knowable without a re-read. Reads always have it. ## #8 — `completed_time` On tasks, `null` while open, so "not done" is never confused with "done at the epoch" as the acceptance list required. ## #3 — `list_project_groups()` Folder groups arrive on the **same `batch/check/0` full state** as projects, under a top-level `projectGroups` key — no new endpoint needed. Deleted groups are filtered unless `include_deleted=true`. The docs are explicit about the level confusion the issue flagged: a folder group holds *projects*; a column holds items inside *one project*. ## Also included Read-side `Project.sort_order`, since it fell out of the same model change. Worth flagging for #9: sidebar sort order is a **positive** integer where lower sorts higher (`0, 393216, 655360, …` in visible order), **not** the large negative the issue guessed. Recorded in DESIGN.md §11 so the #9 write side has the semantics already pinned down. ## Testing `146 passed`. Unit tests use a fake transport and structurally cannot catch the echo semantics, so the watermark contract also gets a live test (`test_modified_time_bumps_on_every_write_live`) that fails loudly if anyone reintroduces the echo. All 3 live tests pass against a real account. Contracts documented in DESIGN.md §10 (timestamps + the echo rule) and §11 (groups + ordering). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_012VU4wBhFAAosZLQcWqvHsf
Closes #1, closes #8, closes #3.

All three were already on the wire and dropped during payload flattening —
confirmed live against a real account 2026-08-09.

#1 modified_time / created_time on tasks, notes and projects.

The read side was the easy half. The load-bearing half: v2 *stores whatever
modifiedTime the client sends* rather than overriding it. Every update method
here is built by re-reading the raw payload and mutating it, so shipping the
field without more would have frozen modifiedTime at its original value on any
item this server ever wrote — a watermark that exists and is permanently wrong,
which is worse than one that is absent.

_drop_modified_time() now strips the field from every update payload so the
server stamps the write. Verified live: a title edit and a metadata-only tag
add both advance it. That also answers the question the issue asked us to
document — ANY write bumps it, not only content edits.

Consequence, documented at every layer: a write response carries
modified_time = null, because the batch response returns only an etag and the
new instant is not knowable without a re-read.

#8 completed_time on tasks, None while open — so "not done" is never confused
with "done at the epoch".

#3 list_project_groups(). Folder groups arrive on the SAME batch/check/0 full
state as projects, under a top-level projectGroups key, so this needs no new
endpoint. Deleted groups filtered unless asked. Docs are explicit about the
level confusion the issue flagged: a folder group holds projects, a column
holds items inside one project.

Also read-side Project.sort_order. Sidebar sort order is a POSITIVE int where
lower sorts higher (verified against the real sidebar), not the large negative
the issue guessed — recorded in DESIGN.md §11 ahead of the #9 write side.

Unit tests use a fake transport and cannot catch the echo semantics, so the
watermark contract also gets a live test.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012VU4wBhFAAosZLQcWqvHsf
ethan merged commit e131b31c70 into main 2026-08-09 20:07:57 -05:00
Sign in to join this conversation.
No reviewers
No labels
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
halfst/ticktick-mcp!10
No description provided.