[Bug] Обновлять сохранённые payout calendars в hourly pass #53
Notifications
Due Date
No due date set.
Depends on
#55 [Bug] Публиковать новый полный forecast независимо от request window
WealthLedger/backend
Reference: WealthLedger/backend#53
Reference in New Issue
Block a user
Problem
events.refresh→readMany→requestMany(force=false) переиспользует stable dividends/bond-events. Bond key постоянный; dividend key обновляется при смене окна, но intraday corrections подавлены. Missing MOEX block трактуется как malformed вопреки spec; верная provider semantics неизвестна.
Expected Behavior
Scheduled pass получает изменения календаря, сохраняет previous complete calendar/forecast при failure и не force-читает unrelated metadata/quotes/history.
Evidence
Audit date:
2026-10-11. Reports: 06, 07, 08. Finding IDs: BE-003, BE-014, FLOW-006, RISK-004, TQ-006, TA-F03, TA-F14. Verified revision:0ad84ff4f0b4712abc7e69dc299856f14af6eadd; Confidence: High для reuse/conflict, Medium provider absence. Verification status: static; missing-block runtime-unverified.Affected code: upcoming-events.service.ts:182–239; catalog-enrichment.service.ts:113–125,184–194,679–692; moex.client.ts:119–198.
Scope
In scope
Calendar-specific existing force/freshness, shared UID coalescing и named missing/empty/malformed cases.
Out of scope
Whole-source force, удаление shared cache/FK, ослабление malformed/cursor guards, live financial calls.
Proposed Approach
Применить current force только календарям. Missing-block трактовку менять лишь по primary contract/обезличенному offline fixture; conservative failure сохраняется до решения.
Acceptance Criteria
Verification
Existing upcoming-events SQLite/real-parser cases и moex.client.spec.ts; actual scheduled source path вместо scheduler stub. Relevant backend gates на конечном diff.
Dependencies
backend #55: failed-existing-calendar publication/fallback proof до force. Native dependency установлена и прочитана обратно.
Traceability
Audit campaign: WealthLedger/main#23 . Audit findings: BE-003, BE-014, FLOW-006, RISK-004, TQ-006, TA-F03, TA-F14. Repository: WealthLedger/backend; T04.
Merged: PR #157 (squash, develop
aeb4647). CI (commits, security, verify) was green before merge.Decisions (owner)
MOEX_UNAVAILABLE); the previous complete calendar/forecast is kept. Confirmed empty block and 404 "not found" stay a successful "no data".moex.client.tsandmoex.client.spec.tsunchanged.Change
sourcePolicy:dividendsandbond-eventsget policycalendar(wasstable).CatalogEnrichmentService.readMany/requestManytake optionalrefreshBefore; a calendar is re-read only iffetchedAt < refreshBefore. Without it behaviour is unchanged (GET, stock/bond income, candles, discovery).UpcomingEventsService.refreshtakeswalkStartedAt;TbankHourlyScheduler.dispatchEventspasses the walk start (same as portfolio). A second account with the same UID sees the freshfetchedAtand skips the load.Evidence
test/upcoming-events.e2e-spec.ts(SQLite HTTP, realrefresh→readMany→requestManypath, scheduler not stubbed) failed on the old code; 30/30 pass after the fix. Cases: changed then cancelled payout in the same window bumpsdataRevisionand events; failed refresh keeps row,fetchedAt,lastSuccessfulAt, forecast and shows failure; two accounts with the same UID = one load per walk; GET and a plainrefresh()without a walk stamp make no calendar RPC and detail is not re-read; bond calendar with a constant key is re-read only on a walk pass.npm run verify: 42 suites / 310 tests, lint, typecheck, build — OK.npm run test:e2e(all SQLite suites): exit 0.npm run openapi:check: OK.Not verified / limits
API contract
No change.
AC 5 closed: spec aligned and merged — WealthLedger/specs#77 (develop
aa1a57a, Issue specs#76). Scenario "MOEX отвечает без запрошенного блока" now describes a source failure (missing/malformed block or cursor; previous complete calendar/forecast kept,metashows the failed attempt); new scenario for confirmed empty block / 404 = successful "no data". Spec now matchesMoexClientandmoex.client.spec.ts. All five AC are checked. Still not verified in runtime: real MOEX behaviour on a missing block (live calls forbidden).