The _add_unattributed_bytes function was adding the same cross-hour
delta to both days when the interval spanned midnight, violating the
spec requirement to preserve measured volume once as shared boundary
evidence.
Store the unattributed bytes only on the day where the interval starts
(the earlier day). This ensures the total across both days equals the
actual delta without duplication.
Closes#88 (cross-day delta duplication portion)
Add query_local_day_history() with evidence-limit metadata to local_day.py,
including LocalDayHistoryEntry dataclass that annotates each summary with
detail_available and derived_from_surviving flags. Document the 14-day
detail vs indefinite-summary retention policy in both local_day.py and
pruning.py.
Fix query_local_day_summary() to convert SQLite integer booleans to Python
bool, and add deduplication guard to the midnight-spanning hour logic in
derive_local_day_summary().
Add 17 lifecycle tests covering all acceptance criteria:
- AC1: Summaries survive sample pruning, evidence limits visible
- AC2: Boundary anchors preserved before pruning
- AC3: Timezone survives system-timezone change
- AC4: Legacy UTC summaries labelled incomplete
- AC5: Idempotent pruning and repair
- AC6: Volume conservation (no double counting)
- AC7: Real temporary stores
- AC8: User-visible transition from recent to aged
- AC9: Retention policy documented
All 775 tests pass.
Add keyboard-driven date navigation to the TUI:
- [ and ] keys select previous/next day, adjusting the visible range
- g opens a date entry modal for direct date navigation
- t returns to today's live view from any historical browsing
- Background refresh preserves the browsed selection
- Local-day widget shows data for the selected date
- Updated help screen and action legend with new bindings
- DatePickerScreen modal with input validation
- 24 headless tests covering navigation, date entry, browse
stability, local-day evidence, and constrained widths
Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
- Change default collection cadence from 5 minutes to 3 minutes
(CADENCE_DEFAULT_S=180, systemd OnUnitInactiveSec=3min,
runit CADENCE=180)
- Update freshness threshold to 450s (2×180 + AccuracySec + 60)
- Add _query_live_graph_data(): queries raw samples from the last
3 hours and computes interval byte deltas with actual timestamps
- Add LiveActivityGraph widget: vertical bar chart of interval
volumes with read/write toggle (w key), arrow key inspection,
and click support
- Wire live graph into TUI layout (full-width row between daily
graph and drive health), refresh cycle, and CSS grid
- Replace t theme binding with t today/live binding; theme
selection via preferences file
- Add w binding for read/write toggle on live graph
- Update action legend, help screen, and grid layout for new
live-activity row
- Add 20 tests covering cadence constants, live query, widget
rendering, toggle, and TUI integration
- Update all cadence documentation (README, ADR 0003, acceptance
criteria LC-2, fenris-redesign constants table, CHANGELOG)
Add local-day activity summaries derived from UTC hour observations using
the system timezone, with durable storage in a new local_days table.
The collector derives local-day read/write totals after UTC aggregation;
the TUI displays them with timezone, completeness state, and coverage.
Schema: bump SCHEMA_VERSION to 3, add local_days table (migration 2→3
is pure addition, idempotent, preserves newer-schema refusal).
Repair the collection-to-display path so each successful acquisition
publishes correct read/write deltas through the observation store and
visible dashboard.
Fixes:
- derive.py: accumulate bytes_read_delta on same-hour hour_observation
merge (was silently dropped)
- collector.py: rebuild day_aggregates from hour observations after
each collection run (previously only populated for cross-hour intervals)
- day_aggregate.py: add persist_day_aggregate upsert helper
- tui.py: query and display both read and write deltas in daily and
hourly readouts, constrained summaries, and graph data queries
Tests:
- Add 14 integration tests (test_measured_activity.py) exercising the
full collector→store→reader→display path with real fixtures and
injected time
- Update constrained-layout assertion to match new W/R format
Closes#89
Add XBPS build and sign steps to CI workflow
Add XBPS publication as independent gate (requires manual trigger)
Track format availability in release notes
Update release-footer.md with XBPS install instructions
Add --available/--withheld arguments to extract_changelog.py
Attach XBPS artifacts to Gitea release
Clean up XBPS signing key material after use