Choose daily graph encoding and hourly drill-down #68

Closed
opened 2026-09-11 04:08:38 +00:00 by xavierk · 2 comments
Owner

Parent map: Fenris TUI polish and hourly history

Question

Which graph encoding and interaction model best communicates hourly writes and a day's usage without misleading users?

HITL prototype, plus grilling + domain-modeling. Wait for the hourly-history contract. Use a cheap throwaway visual/interaction artifact for human reaction, not production implementation. Verify relevant framework capability against current docs; do not infer support from library names.

Approved direction: bytes written per hour default; amber graph in default theme; keyboard and clickable controls for range → day → hourly detail → back. Reads are optional, not promised.

Compare hourly bars with one candle per day derived from hourly values. Define exactly what open/high/low/close, body, wick, unit, daily total and partial day would mean; reject candles if they obscure usage or manufacture unavailable values. Decide whether reads earn a selector or remain out of scope. Resolve default horizon, available ranges, date/time-zone labels, selected-day details, navigation keys/focus/back behavior, empty/zero/gap rendering and minimum terminal behavior. No fabricated history; gaps remain distinguishable from zero.

Answer must link the prototype and record live human choice with rationale, legends, controls and example data states. This ticket cannot be resolved by the agent choosing on the human's behalf.

Parent map: [Fenris TUI polish and hourly history](https://git.bongbetic.com/xavierk/Fenris/issues/65) ## Question Which graph encoding and interaction model best communicates hourly writes and a day's usage without misleading users? HITL prototype, plus grilling + domain-modeling. Wait for the hourly-history contract. Use a cheap throwaway visual/interaction artifact for human reaction, not production implementation. Verify relevant framework capability against current docs; do not infer support from library names. Approved direction: bytes written per hour default; amber graph in default theme; keyboard and clickable controls for range → day → hourly detail → back. Reads are optional, not promised. Compare hourly bars with one candle per day derived from hourly values. Define exactly what open/high/low/close, body, wick, unit, daily total and partial day would mean; reject candles if they obscure usage or manufacture unavailable values. Decide whether reads earn a selector or remain out of scope. Resolve default horizon, available ranges, date/time-zone labels, selected-day details, navigation keys/focus/back behavior, empty/zero/gap rendering and minimum terminal behavior. No fabricated history; gaps remain distinguishable from zero. Answer must link the prototype and record live human choice with rationale, legends, controls and example data states. This ticket cannot be resolved by the agent choosing on the human's behalf.
xavierk added this to the Wayfinder: Fenris TUI polish and hourly history milestone 2026-09-11 04:08:38 +00:00
xavierk added the wayfinder:prototype label 2026-09-11 04:08:38 +00:00
xavierk added a new dependency 2026-09-11 04:08:51 +00:00
xavierk added a new dependency 2026-09-11 04:08:51 +00:00
xavierk added a new dependency 2026-09-11 04:08:51 +00:00
xavierk self-assigned this 2026-09-11 05:31:42 +00:00
Author
Owner

Prototype checkpoint — artifact ready for live reaction, not a resolution

Live human choice still required; this ticket cannot be resolved by the agent choosing on the human's behalf.

Asset: branch prototype/graph-encoding — run prototype/graph-encoding/run, or review screenshots/*.svg (80×24 and 140×40 per variant). Throwaway; not for main.

Three structurally different encodings, each rendering the same synthetic 14-day evidence with every contract state present (measured 0 B, gap ≠ zero, unallocated stacked separately, partial day "so far", pre-monitoring day, deliberate-disable annotation):

  • A Daily bars — one bar per local display day; Enter/click → 24 hourly bars; Esc/Backspace back.
  • B Daily candles — open = first evidenced hour, close = last, high/low = max/min hourly GB; body solid when close ≥ open, hollow otherwise, dashed when partial; ╎ column = no evidence.
  • C Rolling 72 h strip — continuous hourly columns with day separators; hour readout, no day level.

Interaction (all variants): ←/→ select, Enter drill, Esc/Backspace back, mouse click maps widget-local x to a column. Legend always on screen.

Framework facts verified against current docs + installed Textual 8.2.8: no BarChart widget exists (roadmap-only), Sparkline is non-interactive, textual-plotext is a separate uninstalled package. Bars/candles/strip therefore require a custom block-glyph renderable in the usage-history pane (as prototyped); clicks arrive as widget-local coordinates, keyboard via bindings+focus. No new dependency needed for variant A or B.

Open decisions going to live grilling: encoding choice (recommendation: reject candles — they bury the daily total, price-like OHLC implies dynamics usage data does not have, and gap/partial semantics distort bodies), reads selector scope, default horizon + offered ranges, label format, navigation keys/focus interaction with existing global bindings, minimum terminal size behavior.

## Prototype checkpoint — artifact ready for live reaction, not a resolution Live human choice still required; this ticket cannot be resolved by the agent choosing on the human's behalf. **Asset**: branch [prototype/graph-encoding](https://git.bongbetic.com/xavierk/Fenris/src/branch/prototype/graph-encoding) — run `prototype/graph-encoding/run`, or review `screenshots/*.svg` (80×24 and 140×40 per variant). Throwaway; not for main. Three structurally different encodings, each rendering the same synthetic 14-day evidence with every contract state present (measured 0 B, gap ≠ zero, unallocated stacked separately, partial day "so far", pre-monitoring day, deliberate-disable annotation): - **A Daily bars** — one bar per local display day; Enter/click → 24 hourly bars; Esc/Backspace back. - **B Daily candles** — open = first evidenced hour, close = last, high/low = max/min hourly GB; body solid when close ≥ open, hollow otherwise, dashed when partial; `╎` column = no evidence. - **C Rolling 72 h strip** — continuous hourly columns with day separators; hour readout, no day level. Interaction (all variants): ←/→ select, Enter drill, Esc/Backspace back, mouse click maps widget-local x to a column. Legend always on screen. **Framework facts verified against current docs + installed Textual 8.2.8**: no BarChart widget exists (roadmap-only), Sparkline is non-interactive, textual-plotext is a separate uninstalled package. Bars/candles/strip therefore require a custom block-glyph renderable in the usage-history pane (as prototyped); clicks arrive as widget-local coordinates, keyboard via bindings+focus. No new dependency needed for variant A or B. Open decisions going to live grilling: encoding choice (recommendation: reject candles — they bury the daily total, price-like OHLC implies dynamics usage data does not have, and gap/partial semantics distort bodies), reads selector scope, default horizon + offered ranges, label format, navigation keys/focus interaction with existing global bindings, minimum terminal size behavior.
Author
Owner

Resolution — daily bars with hourly drill-down; candles rejected

Live grilling this session: human accepted all recommendations Q1–Q7. Prototype asset: branch prototype/graph-encoding (run prototype/graph-encoding/run; SVG exports in screenshots/ at 80×24 and 140×40). Throwaway branch, not for main.

Encoding (Q1)

Adopted: one bar per local display day (bytes written), drill into 24 hourly bars. Range view → selected day → hourly detail → back, as approved at charting.

Candles rejected. Their tested semantics were: open = first evidenced hourly value of the day, close = last, high/low = max/min hourly GB, body solid when close ≥ open / hollow when close < open / dashed when partial, ╎ column = no evidence, unit GB per hour. Rejection rationale: bodies bury the daily total (users track the sum, not intraday shape), OHLC imports price-chart dynamics usage data does not have, and gap/partial evidence contorts bodies into misleading shapes. Daily total therefore remains the explicit bar+readout value, never manufactured from candle geometry.

Rolling-strip variant rejected as default: no day totals without adding the day level drill-down already provides.

Scope (Q2)

Reads stay out of scope: writes-only graph. Charting approved writes-per-hour; reads would add a data path and axis for a metric the endurance projection never consumes. Revisit only as a fresh effort after handoff.

Ranges (Q3)

Default horizon 14 days; selectable 7 / 14 / 28 / 90 days, mirroring the 7/28/90 scenario-range horizons so graph and projection share vocabulary. Ranges limit the viewport only — retained history browsing and older controller segments unchanged per the accepted hourly-history contract.

Labels (Q4)

Header line: usage history · Local · UTC±HH:MM · <tz name> — current system timezone, always visible. Day row under bars: day-of-month. Selected-day readout: Wed 09 Sep. Hourly detail: every third hour 00…21 plus midnight → 23:00 local. DST 23/25-hour days and repeated local hours follow the accepted local-display-day contract.

Controls (Q5)

Graph pane focusable: ←/→ select, Enter drill, Esc/Backspace back, mouse click selects (widget-local x → column), 1/2/3/4 switch the four ranges. No conflict with global bindings p/r/c/d/q, which stay unchanged. Footer shows graph keys while the graph is focused.

State rendering (Q6)

Always-visible legend, distinct glyphs: █ allocated, ▒ unallocated stacked on top (measured, attribution unknown — never spread into hours), ░ gap = no evidence (never zero), · measured 0 B, ┄ partial-day cap, ▼ selection marker. Selected-item readout states totals, evidenced hours, unallocated separately, coverage, and partial · N h elapsed · so far. Example data states exercised in the prototype: measured-zero day, gap day, unallocated day, heavy day, partial today, pre-monitoring day, deliberate-disable annotation.

Minimum terminal (Q7)

At 80×24 the range view fits fully (14 days × 4 columns) and drill-down fits (24 single-column hourly bars). Below 80×24 the graph region hides and shows a one-line textual history summary plus graph needs ≥80×24 — no error; the readout survives.

Framework obligation (verified, not assumed)

Installed Textual 8.2.8 has no BarChart widget (roadmap-only) and a non-interactive Sparkline; textual-plotext is not a dependency. Implementation therefore requires a custom block-glyph renderable in the usage-history pane with click mapped from widget-local coordinates. No new dependency.

Effect on the map

No new tickets graduate: remaining cross-cutting usability fog (combined degraded states, constrained layouts) stays fog until the layout/monitoring-signal prototypes surface concrete questions. Read-throughput selector is recorded out of scope on the map. Planning only — no production code changed.

## Resolution — daily bars with hourly drill-down; candles rejected Live grilling this session: human accepted all recommendations Q1–Q7. Prototype asset: branch [prototype/graph-encoding](https://git.bongbetic.com/xavierk/Fenris/src/branch/prototype/graph-encoding) (run `prototype/graph-encoding/run`; SVG exports in `screenshots/` at 80×24 and 140×40). Throwaway branch, not for main. ### Encoding (Q1) **Adopted: one bar per local display day (bytes written), drill into 24 hourly bars.** Range view → selected day → hourly detail → back, as approved at charting. **Candles rejected.** Their tested semantics were: open = first evidenced hourly value of the day, close = last, high/low = max/min hourly GB, body solid when close ≥ open / hollow when close < open / dashed when partial, `╎` column = no evidence, unit GB per hour. Rejection rationale: bodies bury the daily total (users track the sum, not intraday shape), OHLC imports price-chart dynamics usage data does not have, and gap/partial evidence contorts bodies into misleading shapes. Daily total therefore remains the explicit bar+readout value, never manufactured from candle geometry. Rolling-strip variant rejected as default: no day totals without adding the day level drill-down already provides. ### Scope (Q2) Reads stay out of scope: writes-only graph. Charting approved writes-per-hour; reads would add a data path and axis for a metric the endurance projection never consumes. Revisit only as a fresh effort after handoff. ### Ranges (Q3) Default horizon **14 days**; selectable **7 / 14 / 28 / 90 days**, mirroring the 7/28/90 scenario-range horizons so graph and projection share vocabulary. Ranges limit the viewport only — retained history browsing and older controller segments unchanged per the accepted hourly-history contract. ### Labels (Q4) Header line: `usage history · Local · UTC±HH:MM · <tz name>` — current system timezone, always visible. Day row under bars: day-of-month. Selected-day readout: `Wed 09 Sep`. Hourly detail: every third hour `00…21` plus `midnight → 23:00 local`. DST 23/25-hour days and repeated local hours follow the accepted local-display-day contract. ### Controls (Q5) Graph pane focusable: ←/→ select, Enter drill, Esc/Backspace back, mouse click selects (widget-local x → column), `1/2/3/4` switch the four ranges. No conflict with global bindings p/r/c/d/q, which stay unchanged. Footer shows graph keys while the graph is focused. ### State rendering (Q6) Always-visible legend, distinct glyphs: `█` allocated, `▒` unallocated stacked on top (measured, attribution unknown — never spread into hours), `░` gap = no evidence (never zero), `·` measured 0 B, `┄` partial-day cap, `▼` selection marker. Selected-item readout states totals, evidenced hours, unallocated separately, coverage, and `partial · N h elapsed · so far`. Example data states exercised in the prototype: measured-zero day, gap day, unallocated day, heavy day, partial today, pre-monitoring day, deliberate-disable annotation. ### Minimum terminal (Q7) At **80×24** the range view fits fully (14 days × 4 columns) and drill-down fits (24 single-column hourly bars). Below 80×24 the graph region hides and shows a one-line textual history summary plus `graph needs ≥80×24` — no error; the readout survives. ### Framework obligation (verified, not assumed) Installed Textual 8.2.8 has no BarChart widget (roadmap-only) and a non-interactive Sparkline; textual-plotext is not a dependency. Implementation therefore requires a custom block-glyph renderable in the usage-history pane with click mapped from widget-local coordinates. No new dependency. ### Effect on the map No new tickets graduate: remaining cross-cutting usability fog (combined degraded states, constrained layouts) stays fog until the layout/monitoring-signal prototypes surface concrete questions. Read-throughput selector is recorded out of scope on the map. Planning only — no production code changed.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Reference: xavierk/Fenris#68