You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 0d8537e
Browse filesBrowse the repository at this point in the historyBrowse files
fix(cron): observation time = oldest source that actually contributed a stored field (codex P2)
- getLatestOpen tracks per-field provenance: fromInverter if any realtime field (gen/use/grid/batt/soc/genTotal) was overridden, fromStation if any is still station-sourced; observedAtOf() = min over contributing sources, null if a contributor has no timestamp; a device snapshot that overrides nothing no longer vouches for station values
- 5 tests (110 total)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
## P2: Do not replace the bundle timestamp when station fields still contribute
4
+
5
+
Location: `src/worker/deye.ts:410-418` (especially line 412).
6
+
7
+
`getInverterFlow()` returns a truthy object for ANY nonempty `dataList`, even when every mapped power/SOC field is undefined. `out.observedAt = inv.observedAt` is unconditional, while the value overrides are conditional. Therefore `observedAt` does not yet describe the values actually stored when measure points are absent/partial/unmapped.
8
+
9
+
Actual `getLatest` -> `pollAndStore` -> SQLite reproduction at 2026-09-22 17:20 BKK, station reading four days old and `generationPower=123`:
10
+
11
+
- Device timestamp fresh, `dataList=[{key:'SOC',value:75}]`: `observedAt=now`, `genPower=123` from the stale station, current sample written, `frozen_since` cleared.
12
+
- Device timestamp fresh, only `BatteryRatedCapacity=400`: same stale 123 W written as current despite **no value override at all**.
13
+
- Conversely fresh station, only `BatteryRatedCapacity=400` and absent device timestamp: `observedAt=null`, drops valid station sample although the device contributes none of its values.
14
+
15
+
Retain station provenance when no mapped field contributes. With partial overrides, freshness must cover every source still contributing persisted realtime fields (e.g. source set / conservative oldest usable timestamp, unknown if a contributing source lacks time), or avoid mixing stale fallback fields. Merely checking that *one* mapped inverter field is present is insufficient; the SOC-only case above still fails.
16
+
17
+
Please add source-composition regression cases for no mapped override and partial override in both age directions, alongside the complete override tests.
18
+
19
+
## Verified fixes and controls
20
+
21
+
- All original full-override reproductions now pass: fresh/missing station + stale inverter rejected; stale station + fresh complete inverter accepted; first-deploy old observation and advancing-but-old replay rejected; millisecond normalization and missing timestamp rejected correctly.
22
+
- Normal freeze/recovery and actual Hono purge authorization, dry-run, inclusive range, cache invalidation and daily preservation still pass.
- No app source/production changes. Harness extended in `.summary/review-088cb16.cjs`; it runs the actual current main source with fake upstream and in-memory SQLite, no external network.
0 commit comments