Commit Graph
54 Commits
Author SHA1 Message Date
xavierk e22b99442e fix: validate complete-day monitoring coverage 2026-09-29 03:34:17 +05:30
xavierk d045e88043 Clarify incomplete history readouts (#103) 2026-09-28 17:54:56 +05:30
xavierk c4524cf654 Preserve historical activity selection (#103) 2026-09-28 17:46:18 +05:30
xavierk f780212a02 fix: follow live activity until inspection (#102) 2026-09-28 16:32:13 +05:30
xavierk 3e1ecc90e4 refactor: consolidate pending recovery checks (#101) 2026-09-28 15:05:47 +05:30
xavierk 59d2dd2634 fix: clarify pending admission failure outcome (#101) 2026-09-28 15:05:02 +05:30
xavierk fd4db45ae1 fix: bound pending publication admission (#101) 2026-09-28 15:02:58 +05:30
xavierk 5aeee7b6f1 fix: guard local-day pruning across monitoring gaps (#100) 2026-09-28 14:37:22 +05:30
xavierk d69753690a fix: prune old detail after publication (#100) 2026-09-28 14:03:37 +05:30
xavierk 017a562566 fix: rebuild trustworthy local-day evidence (#99) 2026-09-28 13:09:00 +05:30
xavierk 7e392c4ea2 fix: conserve local-day activity evidence 2026-09-28 10:21:55 +05:30
xavierk 8a5ef05188 refactor: clarify publication recovery count 2026-09-28 09:06:09 +05:30
xavierk 847bde1be0 test: account for pending publication table 2026-09-28 03:23:46 +05:30
xavierk 0d45f263d2 feat: retain pending publications (#97) 2026-09-28 03:08:00 +05:30
xavierk 437ea6be73 fix: publish collection history atomically 2026-09-28 01:56:32 +05:30
xavierk e49aac8569 Release Fenris 0.5.0 with Chalktone dashboard and dotted activity plots 2026-09-19 11:13:37 +05:30
xavierk 8bac26836a Release Fenris 0.4.0
Release / release (push) Successful in 1m16s
2026-09-18 23:12:14 +05:30
xavierk cfd71a053b fix: store cross-midnight unattributed bytes once, not twice
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)
2026-09-18 16:54:17 +05:30
xavierk b76067104b feat(projection): add complete observation day gate (issue #94) 2026-09-18 15:23:59 +05:30
xavierk 95cca2e115 Keep local-day history trustworthy after detail expires (#93)
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.
2026-09-18 14:27:44 +05:30
xavierkandCommandCodeBot 5df8a12339 Browse activity dates from the keyboard (#92)
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>
2026-09-18 14:17:02 +05:30
xavierk e12f4a574c Plot live drive activity every three minutes (#91)
- 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)
2026-09-18 13:17:18 +05:30
xavierk 4f884b4b73 Show trustworthy local-day activity totals (#90)
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).
2026-09-18 12:28:23 +05:30
xavierk 366c2f55b7 Publish consistent measured drive activity (#89)
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
2026-09-17 17:43:31 +05:30
xavierk 69e08d9d3a Release Fenris 0.3.7
Release / release (push) Successful in 1m6s
2026-09-16 08:53:38 +05:30
xavierk 44c57b70dd Release Fenris 0.3.6
Release / release (push) Failing after 6s
2026-09-16 08:05:28 +05:30
xavierk 95c75badd5 Route TUI controls through polkit 2026-09-15 15:44:10 +05:30
xavierk 70dbae65fb Clarify TUI monitoring activation 2026-09-15 15:40:29 +05:30
xavierk d119a09b1f Fix Void package lifecycle on host 2026-09-15 15:30:51 +05:30
xavierkandCommandCodeBot e27052d09a Implement runit support for Fenris monitoring (issue #84)
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>
2026-09-15 00:15:24 +05:30
xavierk 197e8ed02d Make the combined dashboard usable at constrained sizes (issue #81)
- Add terminal size detection with _MIN_WIDTH (80) and _MIN_HEIGHT (24) thresholds
- Add constrained CSS layout: single-column grid, hide graph, show text summary
- Add #constrained-summary widget with textual history summary
- Add on_resize handler and _refresh()-based constrained state detection
- Preserve selected-day context across resize transitions
- Ensure all regions (headline, health, service, quit) remain usable when constrained
- Update DailyBarGraph._is_constrained to accept terminal_width parameter
- Add comprehensive tests for constrained layout behavior

Covers TPH-9 and cross-cutting TPH regression proof.
Closes #81
2026-09-14 17:05:44 +05:30
xavierk 79478653fa Persist accessible colour and motion preferences (issue #80)
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
2026-09-14 16:31:36 +05:30
xavierk 30122c5e6d feat: Apply Fenris identity and Drive health grouping (issue #79)\n\n- Add wolf glyph identity (Fenris by Bongbetic) with fallback for unsupported terminals\n- Remove duplicate maker credit from service strip (single placement in titlebox)\n- Move vendor wear under Drive health with full context\n- Preserve all existing behavior: continuity, pause block, quit rail, auth banner, controls\n\nCloses #79 2026-09-14 16:10:20 +05:30
xavierk 01240ec8f0 feat: Add shared status composition for truthful monitoring states (issue #78) 2026-09-14 15:42:31 +05:30
xavierk 7e270150bc feat: Show honest qualifying-day progress and confidence (issue #77) 2026-09-14 15:16:36 +05:30
xavierkandCommandCodeBot 4f2f30abac feat(#76): anchor scenario windows at evidence endpoint T
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>
2026-09-14 11:11:50 +05:30
xavierkandCommandCodeBot 5d916ee97f feat: add interactive daily writes bar graph with hourly drill-down (issue #75)
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>
2026-09-14 10:47:37 +05:30
xavierk 6917a658cb feat: implement repair and retention for observation history (issue #74) 2026-09-14 03:54:46 +05:30
xavierk d790ff84c5 feat(#73): publish trustworthy first usage history
- 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
2026-09-14 03:19:08 +05:30
xavierk d01df6468f feat(tui): clarify monitoring continuity and quitting 2026-09-10 19:43:32 +05:30
xavierk 7bbe5cede7 feat(tui): identify Fenris and explain polkit authentication 2026-09-10 13:28:05 +05:30
xavierk fb683f52ba fix(store): degrade on store permission errors, keep store group-readable (issue #54)
Release / release (push) Successful in 53s
- open_store_readonly(): stat() PermissionError (non-group user on the
  2750 store dir) now maps to StoreFault so status/TUI degrade instead
  of crashing with a traceback.
- init_store(): chmod db + -wal/-shm group rw after WAL setup — SQLite
  WAL readers need write access to sidecars even for mode=ro opens.
- Store dir 2750 → 2770 (tmpfiles + make install) and UMask=002 on the
  collect unit so root-created files stay group-accessible.
- rpm %post upgrade path re-runs systemd-tmpfiles --create to correct
  placement modes on existing machines.
Bump to 0.3.3.
2026-09-10 09:58:24 +05:30
xavierk 512df2ae83 fix(store): default store_path when config omits it (issue #53)
Release / release (push) Successful in 59s
Fresh installs shipped a config template with no store_path key while
collector.py demanded one via get_store_path() — every first collect
crashed with KeyError 'store_path'. Resolve to the packaged default
(/var/lib/fenris/observations.db) when absent, document the key in the
template, and cover the fresh-install path with regression tests.
Bump to 0.3.2.
2026-09-10 09:45:02 +05:30
xavierk b593a2742e fix: make RPM runtime portable on Tumbleweed 2026-09-04 11:46:58 +05:30
xavierk 2b05267690 feat: Fenris persistent TUI monitoring redesign
Implement the complete redesign per fenris-redesign spec:

- Observation store: SQLite WAL mode, six entities, schema versioning
- Collector: smartctl acquisition, sysfs identity, normalization
- Projection: sustained regime rate, habit change, confidence states
- Panes TUI: Textual keyboard-first layout with four normative regions
- Status CLI: read-only composition with four service facts
- Monitor helper: polkit-guarded toggle, collect, baseline ops
- Legacy migration: idempotent single-transaction import
- Hour classification, day aggregates, monitoring periods
- Pruning, segmentation, drive health facts

Cross-cutting acceptance sweep (CI-1 through CI-4):
- 59 tests covering state matrix, TUI/CLI parity, prohibition set,
  required wording and six disclosures
- Full suite: 289 tests, all green

Issues #20, #32 closed.
2026-09-02 11:48:08 +05:30
xavierk bc9b2f8940 feat: ship fenris-monitor helper, polkit policy, and systemd units (#29) 2026-09-02 10:27:56 +05:30
xavierk a2f7b6232b feat(tui): Panes TUI on Textual (issue #28)
Implements keyboard-first Panes TUI per spec section 7:

- One dense screen: headline band, usage-history, drive-health, service strip

- Bindings p/r/c/d/q with pause-asks/resume-doesnt asymmetry

- Privileged actions via terminal-attached fenris-monitor subprocess

- Disclosures view, empty-store greeting, first-run opt-in

- 35 headless tests: CI-1 state matrix, TUI-1/TUI-4 layout, CI-4, IN-3

Blocker #27 resolved. Closes #28
2026-09-01 23:54:50 +05:30
xavierk 7f006c7df7 feat(status): read-only CLI status command (issue #27)
Implement fenris status as the read-only CLI twin of the TUI,
composing from the observation store and allow-listed systemctl
properties per spec section 8.8.

New module src/fenris/status.py:
- Freshness grading with shared constants (section 8.9, LC-10)
- Configuration error from direct config reads (section 8.3, LC-4)
- Store fault / newer-schema exact phrases (section 9.4-9.5, FL-4/FL-5)
- Drive anomalies as ordinary facts (section 9.7, FL-7)
- Four separate service facts (section 7.3, LC-9, CI-2)
- Projection recomputed on read, never stored (section 6.10)
- Retired command rejection with migration pointers (section 8.8)
- Six disclosures via --disclosures flag (section 6.11, CI-4)

Updated fenris.py:
- Replaced old cmd_status with new status module integration
- Added retired command handlers (start/stop/run)
- Added global --device flag rejection

Tests: 43 new, 182 total passing, zero regressions.
Closes #27.
2026-09-01 23:49:23 +05:30
xavierk b99ebfe9dd fix(projection): regime dynamics, warming gate, habit change detection, horizon coverage
Fixes #26.

Changes:
- Fix warming gate: check total_days < 14 OR days_below_coverage > 2
- Rewrite _detect_habit_change: correct consecutive-day scanning
- Fix _compute_horizon_rate: require history spans full horizon

Tests: 29 new tests covering PR-2,3,6,7,9,15,16. 139 total passing.
2026-09-01 23:32:00 +05:30
xavierk 7802a72606 feat: implement projection core pure function (closes #25) 2026-09-01 23:16:33 +05:30