Skip to content

feat(schedule): sync occurrence overrides to the client - #273

Merged
Wintercom merged 5 commits into
1024XEngineer:mainfrom
LUPENGHAN:feature/schedule-occurrence-override-sync
Aug 18, 2026
Merged

feat(schedule): sync occurrence overrides to the client#273
Wintercom merged 5 commits into
1024XEngineer:mainfrom
LUPENGHAN:feature/schedule-occurrence-override-sync

Conversation

@LUPENGHAN

@LUPENGHAN LUPENGHAN commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

关联 Issue

Closes #272

改动

  • voice.command.result 消息加 occurrence_overrides 字段:CommandOutcome/CommandResult 把 ScheduleMutationResult.occurrence_overrides(scope=this_occurrence 时早就有数据,只是一直没传到线上)透传出去;schedule_tools.py 新增 _override_for_client,序列化方式对齐已有的 _snapshot_for_client
  • LocalScheduleWriter.applyCommandResult 把 schedule 和 occurrence_overrides 拆成两条独立处理路径(this_occurrence 删除只产生 override、不产生新 schedule 快照),写入直接复用已有且测试过的 ScheduleLocalRepository.upsertOccurrenceOverride

验证

  • 前端:npx tsc --noEmitnpx eslint . 全绿;npm run test:vitest 51/51、jest 299/299
  • 后端:uv run ruff check/ruff format --check/mypy 全绿;uv run pytest tests/intelligence/realtime tests/gateway:143/143
  • 新增了 localScheduleWriter.test.ts 四个用例:写 schedule;未 applied 时不写;只有 override 没有 schedule 时正确写入;两者都没有时不写

本轮不含(见 #272 Out of Scope)

  • 不接 SqliteScheduleSyncService 全量快照同步、不新建 sync/data 适配器——独立缺口,另开 issue

备注

不依赖提醒功能栈(#264-#271)——occurrence_overrides 表和写入方法本来就在 main 上,不是提醒功能引入的;LocalScheduleWriter.ts 里跟提醒引擎相关的 scheduleReader.refresh() 留在 #271 自己那边,不在这个 PR 里。两边都改了同一个文件的不同部分,等 #271 落地时 rebase 一下合并即可,是预期内的小合并,不是遗漏。

@vercel

vercel Bot commented Aug 17, 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 17, 2026 10:55am

Closes 1024XEngineer#272

voice.command.result gains an occurrence_overrides field alongside
schedule/schedules. Backend: CommandOutcome/CommandResult carry it
through from ScheduleMutationResult.occurrence_overrides (already
populated by _delete_recurring_range for scope=this_occurrence, but
previously dropped before reaching the wire); schedule_tools.py's
_mutation_result serializes each ScheduleOccurrenceOverrideSnapshot
the same way _snapshot_for_client already does for schedules.

Frontend: AppliedCommand carries the new field through from the WS
message; LocalScheduleWriter.applyCommandResult now handles schedule
and occurrence_overrides independently (a this_occurrence delete
produces only an override, no new schedule snapshot) and writes each
override via the already-existing, already-tested
ScheduleLocalRepository.upsertOccurrenceOverride.

Independent of the reminder-integration branch stack (1024XEngineer#264-1024XEngineer#271) --
the occurrence-override table and upsertOccurrenceOverride already
exist on main; this only needed LocalScheduleWriter's plain-main
shape, not the reminder stack's SqliteLocalScheduleReader wiring.
@LUPENGHAN
LUPENGHAN force-pushed the feature/schedule-occurrence-override-sync branch from 9b3e961 to 862a92b Compare August 17, 2026 10:32
@LUPENGHAN
LUPENGHAN marked this pull request as ready for review August 17, 2026 10:34
@codecov

codecov Bot commented Aug 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.88889% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...atures/assistant/data/local/LocalScheduleWriter.ts 80.00% 2 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@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.

已完整检查后端 ScheduleMutationResult 到 WebSocket payload 的透传、两种前端会话服务的消息映射,以及 LocalScheduleWriter 对 schedule/occurrence override 的独立落库路径。改动与 #272 的协议和本地持久化契约一致,未发现需要阻止合并的正确性问题。

静态校验 git diff --check 通过。当前工作区未安装 frontend node_modules,且没有 backend uv 可执行文件,因此未在本地复跑自动化测试。

View job run

Prettier fix for LocalScheduleWriter.ts (npm run check caught it).

Adds a realtime-toolbox test exercising a delete with scope=this_occurrence
that produces an occurrence_override -- the only path that actually calls
_override_for_client. Closes the 2-line patch-coverage gap Codecov flagged
on the previous commit.
Closes the 1-line patch-coverage gap Codecov flagged: requireString's
throw path had no test on this branch (the file existed on main with
no dedicated test at all before this PR added one).
@MeteorsLiu

Copy link
Copy Markdown
Collaborator

cc @Wintercom 涉及协议改动

@MeteorsLiu
MeteorsLiu requested a review from Wintercom August 17, 2026 11:10
Comment thread backend/src/timeflow/intelligence/realtime/schedule_tools.py
Comment thread frontend/src/features/assistant/data/local/LocalScheduleWriter.ts Outdated
@Wintercom

Copy link
Copy Markdown
Collaborator

【Milestone 过程管理 / 阻塞合并】关联 Issue #272 已归入 MS4,但 PR 自身没有 Milestone。git_rules.txt 第 5–9 行要求本轮所有 Issue/交付进入 Milestone,以便查看完成度并在结束时形成发布功能清单。请把 PR #273 同步归入 MS4,保持 Issue、PR 与迭代交付一致。

@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.

按照 git_rules.txt 的合并标准,本轮存在以下阻塞项:

  1. “仅删除本次”在目标 occurrence 已有 replace override 时仍不同步:后端只透传第一条 schedule,丢掉被软删除的 replacement schedules,且没有新 override。
  2. LocalScheduleWriter 忽略 repository 的 boolean 写入结果,失败时仍 resolve 并触发上层 ACK applied;schedule/overrides 也未在同一事务中原子应用。
  3. Issue #272 在 MS4,但 PR 未设置 Milestone,迭代追踪不一致。

请修复协议的完整最终快照、原子持久化和失败 ACK 语义,并补充对应端到端/回滚测试。具体内容已使用 gh 逐条评论。

…he first

_delete_recurring_range's this_occurrence path, when it hits an
existing replace override, soft-deletes the replacement schedule and
returns it alongside the untouched parent in
ScheduleMutationResult.schedules. _mutation_result only ever forwarded
schedules[0] to the client, so that soft-delete never reached
voice.command.result and the client kept showing the stale
replacement as active.

CommandOutcome already had a schedules (plural) field - only
list_schedules populated it. _mutation_result now fills it with every
schedule the mutation touched; schedule (singular) stays as
schedules[0] for backward compat with clients not yet updated.
applyCommandResult ignored the boolean applyCloudSchedule()/
upsertOccurrenceOverride() return, so a failed write (missing parent
schedule, account mismatch) still let the caller send
message.ack status=applied - the server believed the command was
persisted when it wasn't.

Now every write's return value is checked and a false throws, and all
writes for one command result run inside a single transaction via the
repository's new withTransaction(). A command can produce multiple
schedule and override writes that together represent one voice
command landing; without the transaction a partial failure would
leave state the server never actually had, and since the caller
already skips the ack on any throw, that state would never get
retried either.

Also switches to consuming the schedules (plural) field the backend
now populates for mutations, explicitly excluding list_schedules by
operation instead of relying on the coincidence that query results
never set schedule/occurrence_overrides.

@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 87b5338 into 1024XEngineer:main Aug 18, 2026
6 checks passed
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.

bug(schedule): 删除单次重复日程后,前端收不到 occurrence_override,日历/提醒不会同步

3 participants