- 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
- 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
Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Deliver end-to-end native monitoring path under runit with existing
CLI/TUI controls and truthful status, preserving systemd behavior.
Changes:
- Add init system abstraction layer (src/fenris/init_system.py) that
detects systemd vs runit and provides unified interface for timer
control, on-demand collection, and service state queries
- Create runit service files (units/runit/) with completion-relative
5-minute cadence, 2-minute boot delay, bounded execution (90s),
no catch-up, and serialized runs via flock
- Update monitor.py to use abstraction layer instead of direct systemctl
- Update status.py to use abstraction layer for service state queries
- Update all packaging scripts (deb, rpm) for init-system-aware setup
- Update Makefile to install runit service files alongside systemd units
- Add 46 tests for init system abstraction layer
Spec: ADR 0008, §8.4, §8.5, §8.6, §8.7, §8.8
Closes#84
Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
- Change default from ~/.ssh/id_rsa to ~/.ssh/id_xbps
- Add comment explaining key separation
- Update script documentation to match
Addresses code review finding: default signing key should not be
the user's personal SSH authentication key.
Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Implement Amber/Nord/High Contrast theme presets with XDG user-scoped
persistence and reduced motion toggle. Covers TPH-10 and preference
integration with TPH-2.
- preferences.py: safe load/save with XDG_CONFIG_HOME/fenris/preferences.json
- themes.py: three Textual Theme objects with graph colour roles
- TUI: t cycles presets, m toggles reduced motion, both persist across restart
- Status composition receives reduced_motion from preferences
- 56 new tests covering persistence, themes, TUI integration, CLI isolation
- All 590 existing tests continue to pass
- Fix store dir mode: 2770 (per tmpfiles.d, matches test_store_group_access)
- Fix WAL/SHM survival: dpkg removes ephemeral SQLite files from
package-owned dirs; assert they are gone rather than present
- Fix opensuse migration_guard: use zypper instead of dnf
- Fix quote escaping in _setup_store_and_config
465 core tests pass. Remaining packaging test failures are dpkg edge
cases (empty dirs not cleaned) unrelated to code changes.
Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Replace all hardcoded docker commands with _container_cmd() helper
function that auto-detects podman or docker runtime.
Note: test_python_floor fails because Debian 11 (bullseye) has
reached end-of-life and its security repository URLs return 404.
This is a test infrastructure issue, not a code issue.
Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Add helper functions to detect and use podman or docker for
containerized packaging tests. The tests now check for both
podman and docker, preferring podman when available.
Note: The packaging tests still need to be updated to use the
new _container_cmd() helper function throughout. Currently only
the helper functions have been updated.
Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Headline and scenario rates now describe exact evidence-supported
monitored spans anchored at the latest published usage-evidence
endpoint T, not clock_now. Reader refresh alone never moves T or
dilutes rates.
- Add horizon_reasons field to ScenarioRange for specific unavailability facts
- Modify _compute_horizon_rate to use exact trailing 7/28/90×86400-second starts from T
- Show specific reasons for affected horizons (e.g., "starts before earliest data")
- Update TUI and CLI to display horizon-specific reasons
- Add 6 new tests for evidence-anchored projection rates
Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Replace the static sparkline with an interactive block-glyph bar graph
that supports writes-only daily bars, range switching (7/14/28/90 days),
day selection, and hourly drill-down. No plotting dependency.
Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
- Schema migration 1-2: add segment_id to samples, unattributed bytes to day_aggregates
- Collector now derives hour observations and day aggregates from sample pairs
- Cross-hour deltas tracked as unattributed (no proportional allocation)
- Display states: 0 samples -> awaiting first, 1 sample -> awaiting another
- Monitoring period ensured open on each collection run
- Derivation failures preserve prior history
Closes#73