feat(schedule): sync occurrence overrides to the client - #273
Merged
Wintercom merged 5 commits intoAug 18, 2026
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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
force-pushed
the
feature/schedule-occurrence-override-sync
branch
from
August 17, 2026 10:32
9b3e961 to
862a92b
Compare
LUPENGHAN
marked this pull request as ready for review
August 17, 2026 10:34
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Contributor
There was a problem hiding this comment.
已完整检查后端 ScheduleMutationResult 到 WebSocket payload 的透传、两种前端会话服务的消息映射,以及 LocalScheduleWriter 对 schedule/occurrence override 的独立落库路径。改动与 #272 的协议和本地持久化契约一致,未发现需要阻止合并的正确性问题。
静态校验 git diff --check 通过。当前工作区未安装 frontend node_modules,且没有 backend uv 可执行文件,因此未在本地复跑自动化测试。
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).
Collaborator
|
cc @Wintercom 涉及协议改动 |
Wintercom
reviewed
Aug 18, 2026
Wintercom
reviewed
Aug 18, 2026
Collaborator
Wintercom
requested changes
Aug 18, 2026
Wintercom
left a comment
Collaborator
There was a problem hiding this comment.
按照 git_rules.txt 的合并标准,本轮存在以下阻塞项:
- “仅删除本次”在目标 occurrence 已有 replace override 时仍不同步:后端只透传第一条 schedule,丢掉被软删除的 replacement schedules,且没有新 override。
LocalScheduleWriter忽略 repository 的 boolean 写入结果,失败时仍 resolve 并触发上层 ACK applied;schedule/overrides 也未在同一事务中原子应用。- 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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
关联 Issue
Closes #272
改动
验证
npx tsc --noEmit、npx eslint .全绿;npm run test:vitest 51/51、jest 299/299uv run ruff check/ruff format --check/mypy全绿;uv run pytest tests/intelligence/realtime tests/gateway:143/143本轮不含(见 #272 Out of Scope)
备注
不依赖提醒功能栈(#264-#271)——occurrence_overrides 表和写入方法本来就在 main 上,不是提醒功能引入的;LocalScheduleWriter.ts 里跟提醒引擎相关的 scheduleReader.refresh() 留在 #271 自己那边,不在这个 PR 里。两边都改了同一个文件的不同部分,等 #271 落地时 rebase 一下合并即可,是预期内的小合并,不是遗漏。