M9 section S1, commit 3 — two more shared-toolkit pieces:
- common.iso(dt): the "x.isoformat() if x else None" idiom (repeated 20+ times
across every serializer) as one helper. Adopted in Note.serialize() and the
revision serializer; other serializers adopt it in their sections.
- colors.py: NOTE_COLORS (canonical, on the model) + a single normalize_color().
notes.py now imports the palette + normalizer from here and drops its local
copy. labels.py's identical LABEL_COLORS/_normalize_label_color fold into this
in the Organize section (S3); sync in S4.
normalize_color and NOTE_COLORS remain importable from thoughtsync.notes (used by
tests + sync), so nothing downstream breaks. common has no in-app imports, so the
model→common→colors chain has no cycle. Behavior-preserving.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FRgehjoz7Yv8LkUfADxACm
M9 hardening/DRY pass — section S1, commit 1 (the shared-toolkit foundation):
- Add src/thoughtsync/common.py with parse_dt() and coerce_bool(): one home for
the ISO-date and truthy-flag coercions that were duplicated across modules.
notes.py adopts them and deletes _parse_iso_dt, _iso_to_dt and _truthy
(rule 22 — old copies removed; callers, incl. tests, updated).
- Security: the session cookie is now marked Secure automatically on any request
that arrived over HTTPS (directly or via a proxy's X-Forwarded-Proto), via a
SecureCookieSessionInterface override. Hardens HTTPS deployments without
breaking plain-HTTP LAN installs — no config.
Behavior-preserving refactor + one security hardening. The backend serialization
layer, the json_error sweep, and the notes.py split follow as their own commits.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FRgehjoz7Yv8LkUfADxACm