Skip to content

feat(schedule): add PostgreSQL schedule repository - #192

Merged
Wintercom merged 4 commits into
1024XEngineer:mainfrom
znnnnnnn-wil:feat/schedule-postgres-storage
Aug 11, 2026
Merged

Wintercom merged 4 commits into
1024XEngineer:mainfrom
znnnnnnn-wil:feat/schedule-postgres-storage

Conversation

@znnnnnnn-wil

@znnnnnnn-wil znnnnnnn-wil commented Aug 11, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Verification

  • ruff check .
  • ruff format --check .
  • mypy
  • local quality gate: 110 passed, 22 PostgreSQL tests skipped without a disposable database, 93% coverage
  • CI PostgreSQL schema and repository integration suite: 22 passed against PostgreSQL 16
  • Alembic online upgrade/downgrade through head 20260810_0005

Scope

This PR contains only the PostgreSQL repository layer and its integration-test wiring on top of the schema merged in #186. ScheduleApplicationService, RRULE expansion, snapshot orchestration, WebSocket synchronization, and Agent-facing business APIs remain out of scope.

Replaces the PostgreSQL repository portion of #191.

@vercel

vercel Bot commented Aug 11, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
timeflow Ready Ready Preview Aug 11, 2026 8:03am

@fennoai fennoai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found three high-confidence correctness and data-integrity issues; details are inline. git diff --check passes. The focused tests were not runnable in this environment because uv and the project dependencies are unavailable.

Comment thread backend/alembic/versions/20260810_0003_schedule_storage_v3.py Outdated
Comment thread backend/alembic/versions/20260810_0003_schedule_storage_v3.py Outdated
Comment thread backend/src/timeflow/data/repositories/schedule.py Outdated
…res-storage

# Conflicts:
#	backend/alembic/env.py
#	backend/src/timeflow/data/__init__.py
#	backend/src/timeflow/data/models.py
#	backend/tests/test_schedule_schema.py
@znnnnnnn-wil znnnnnnn-wil changed the title feat(schedule): add PostgreSQL schedule storage feat(schedule): add PostgreSQL schedule repository Aug 11, 2026

@Wintercom Wintercom left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

@Wintercom
Wintercom merged commit f310c81 into 1024XEngineer:main Aug 11, 2026
5 checks passed
Wintercom pushed a commit that referenced this pull request Aug 12, 2026
… fix (#210)

* feat(realtime): implement schedule tools with account isolation and mapping layer

- Add account_id to StreamInfo/StreamIdentity/StreamContext protocols
- Change RealtimeAgent to use tools_factory for per-account tool binding
- Extract tool mapping functions from conversation layer to tool_mapping.py
- Implement ToolBox with five schedule tools and exception translation
- Add SeededScheduleService as temporary data source
- Validate candidates field for ambiguous_target questions

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* feat(realtime): enable schedule mutations with account isolation

- schedule_create/update/delete join the existing query tool, with
  instructions covering when to ask before touching a schedule versus
  acting on it.
- account_id now flows from the WS transport through to tool binding,
  so a session's tools are scoped to its authenticated account.
- A session stays open past a turn that asked a question, so a
  follow-up answer reaches a model that still remembers asking it.
- Hardens the Qwen-Audio transport pump: a binary frame is skipped
  rather than treated as fatal, and non-JSON text fails the turn with
  a reported reason instead of raising.

* feat(realtime): wire schedule tools to the real database

Switch main.py off the local duplicate data layer (DatabaseScheduleService
plus its own repository and transaction handling) onto the already-merged
ScheduleApplicationService + SqlAlchemyScheduleUnitOfWork (#192, #200),
and update the two database-backed end-to-end tests to match. Drops
~900 lines of implementation and tests that duplicated what was already
upstream.

* fix(realtime): stop double-wrapping the command.result payload

RealtimeAgent.tool_requested passed the whole ToolBox outcome dict
(itself {operation, status, schedule}) into CommandResult.schedule, so
the client received payload.schedule.schedule.title instead of
payload.schedule.title -- every mutation reached the client with no
usable schedule data. list_schedules had the same problem in reverse:
its matches never had anywhere correct to go at all.

CommandResult and the wire payload now carry separate optional
schedule/schedules fields, matching protocol §5.5/§5.6, and only the
one that applies is sent (schedules is exclude_none'd off the wire
for a mutation, and vice versa for a query).

Verified against a real voice round trip over the actual database, not
just the added regression tests.

* feat(realtime): add TIMEFLOW_VOICE_AGENT_MODE to select the agent backend

Settings gains voice_agent_mode ("1" realtime model, "2" LLM+ASR+TTS
pipeline), validated against those two values. main.py's _build_agent
already dispatches on it (committed in 6b88acf); mode 2 fails closed
with a clear message since the conversation pipeline does not
implement the Agent port yet.

* style(realtime): reformat test_realtime_sessions.py

ruff format wanted a call collapsed onto one line; missed running the
formatter locally before the earlier commit.

* fix(realtime): preserve negative UTC offsets in datetime normalization

normalize_datetime_args decided whether a datetime string already carried
a timezone by checking for a literal "+" or "Z", which misses negative
offsets like "-05:00". fromisoformat parses that as already aware, but
the code then force-replaced its tzinfo with LOCAL anyway -- silently
shifting the instant by the difference between the two zones (e.g. a
schedule meant for 07:00-05:00 gets stored as 07:00+08:00, 13 hours off).

Parse first and only attach LOCAL when the result is genuinely naive,
matching how _optional_datetime already decides the same thing.

Found by fennoai's review on PR #210.

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
Preview — 448cfc8c Deployed Aug 11, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants