diff --git a/CHANGELOG.md b/CHANGELOG.md index e7596cd..0b2bc8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,31 @@ # Changelog +## v0.39.0 — 2026-09-01 — a guess may not delete a window + +**v0.38.0 unfolded the future and the dry wall walked out with it.** The +first live frame after install: six windows to the reset, drawn as +`▮▯▯▯×××` — and read, by the person the row exists for, as *three slots +where six should be*. The `×` cells were the dry projection doing what it +had always done; folding had just kept the run compressed into one token. +Exposed cell by cell, the guess was overwriting windows — while the exact +same fact sat one row up as `! 7d dry ~Tue 22:00`, with better gates and +an exact time. + +**v0.33 already made this argument for the 5h strip** ("the strip is a +record, not a forecast": an empty cell is a fact, a `×` is a guess, and +the guess is owned by the badge and the notice). The 7d strip only kept +its `×` cells because the fold hid them. Same answer now on both windows: +**a future cell is a slot, never a verdict.** Every cell ahead draws +hollow — dim where the learned rhythm says you sleep — up to the grid's +edge. The one place the dry mark still draws is the folded token +(`...×(✕23)`): an early-week tail has no per-cell shape to say it, and +the count beside the mark is the point there. + +Nothing else moves: `week_dry_slot` still runs (it decides the token's +glyph), the notices still warn, the `week` report draws every slot. Ships +with [ccpace](https://github.com/thevibeworks/ccpace) v0.7.0, which +retires the `×` cell from its ledger the same way. 470 bats. + ## v0.38.0 — 2026-09-01 — the future has a shape **The fold's own argument expired.** "History is information; the future diff --git a/DESIGN.md b/DESIGN.md index 35298f2..3596c3d 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -93,14 +93,18 @@ grows at you: it holds its width for the life of the window and `▮` walks it. On the 5h strip that makes the hollow run the answer to *how long have I got* — `▃▄▮▯▯` is two whole hours after this one. -What neither strip draws on 5h is a *forecast*. An empty cell is a fact; -a `×` is a guess, and on a 5h window the guess is owned twice already — -the badge states the end (`5h[38%@23:00]`) and the notice names the wall -(`5h caps ~14:20`). Cells carry the shape, badges carry state, notices do -the warning. History draws in full; the live row folds the 7d future -after two kept cells into `...▯(x14)` — the 5h windows *after* the one -you are in, `×` red when the tail projects dry (the `week` -report still draws every slot). The count is the budget line's own, +What neither strip draws *in a cell* is a *forecast*. An empty cell is a +fact; a `×` is a guess, and the guess is owned already — the badge states +the end, the notice names the wall with an exact time (`5h caps ~14:20`, +`7d dry ~Tue 22:00`). The 5h strip learned this first; the 7d strip kept +its `×` cells only while the fold hid them, and unfolding called the +question — a run of `×××` was read live as *deleted windows*. A future +cell is a slot, never a verdict. Cells carry the shape, badges carry +state, notices do the warning. History draws in full; the live row folds +the 7d future after two kept cells into `...▯(x14)` — the 5h windows +*after* the one you are in, `×` in place of `▯` when the tail projects +dry, the one place the dry mark still draws (no per-cell shape exists in +a folded tail to say it). The `week` report still draws every slot. The count is the budget line's own, priced from the same instant. The fold pays only while the tail is wider than its own token: ten cells or fewer draw in full, because the future stopped being one fact the day the cells learned your rhythm — a hollow @@ -119,10 +123,10 @@ badges. ▮ now ▯ ahead — the hollow of ▮; on 5h, the hours left in the window; dim when your learned hours say you sleep through most of it - (× outranks it: unreachable beats asleep) ...▯(x14) the folded future: 14 more 5h windows after this one (live 7d - row, only while the tail is wider than this token) -× pace won't cover it (7d only: learned forecast, linear when cold) + row, only while the tail is wider than this token); × in place + of ▯ when the tail projects dry — the dry mark's only cell, + since the wall's own notice states the exact time ``` Burn cells take their badge's pressure color; nothing else in the row is diff --git a/README.md b/README.md index 3e5c846..c95ff85 100644 --- a/README.md +++ b/README.md @@ -335,8 +335,8 @@ fire. | `▁` | the baseline: ran and cost under a point, or ran idle inside the log's coverage. The shortest bar of the same Block Elements run as `▂▃▄`, so the zero line shares their font, width and height — it used to be `ˍ` (U+02CD), a modifier *letter*, which terminals resolve through the text face and which broke the strip's metrics mid-row. Override with `LEDGER_BASE_GLYPH` | | `░` | unknown: the log has no sample for that cell (never drawn as idle — a gap in the record is not a quiet session) | | `▮` | the cell you are in now | -| `▯` | a cell still ahead of you — the hollow of `▮`, an empty slot waiting. On the 5h strip the run of them is the hours left in the window. **Dim** when your learned hours say you sleep through most of it (under half the cell awake): capacity on the grid that is not really available. `×` outranks it — unreachable beats asleep — and a reader who cannot see the tint loses nothing actionable: a dim `▯` is still a window ahead | -| `×` | a cell the pool will not cover at the current pace — **7d only**: the learned forecast's dry point, linear when untrained. The 5h window's wall belongs to the `5h caps ~14:20` notice, which states the time instead of shading cells | +| `▯` | a cell still ahead of you — the hollow of `▮`, an empty slot waiting. On the 5h strip the run of them is the hours left in the window. **Dim** when your learned hours say you sleep through most of it (under half the cell awake): capacity on the grid that is not really available. A reader who cannot see the tint loses nothing actionable: a dim `▯` is still a window ahead | +| `×` | the dry mark — **in the folded token only** (`...×(✕28)`: the tail projects dry). A future cell is a slot, never a verdict: the wall belongs to the notices, which state the exact time (`7d dry ~Tue 22:00`, `5h caps ~14:20`) instead of overwriting windows on the grid | | `...▯(✕28)` | the folded 7d future: 28 more 5h windows after the one you are in, one token instead of a run of hollow cells (`×` red when the tail ends dry). The count is what the budget line prices — not the number of cells the fold happened to hide | | `✕` | not a cell — the multiplication sign, the row's one operator (`...▯(✕28)`, `0.7✕`, `19✕5h left`). Deliberately not `×` (U+00D7), which is already a reading: cells are the ink, the operator is punctuation, and `...×(×28)` has to say both at once. One terminal column and no emoji fallback, so the row still meets line 1's edge; override with `MULT_GLYPH` (`╳` and `✖` look stronger but are ambiguous-width and emoji-presentation respectively) | diff --git a/docs/api/state-dir.md b/docs/api/state-dir.md index 1065318..b49e692 100644 --- a/docs/api/state-dir.md +++ b/docs/api/state-dir.md @@ -7,7 +7,7 @@ read concurrently. - Contract version: **2** (bump on any breaking layout/field change; this file is the changelog) -- Synced with: statusline.sh v0.38.0 +- Synced with: statusline.sh v0.39.0 - Permissions: the script runs under `umask 077` — files are owner-only. Caches hold account PII (email, uuid, org names). @@ -256,10 +256,12 @@ A REST SLOT is a ledger cell you sleep through: fewer than half the cell, the break-even a reader can hold in their head. Both ledgers (this statusline's week strip, ccpace's block) draw such a future cell as a dim `▯`; the general rule is half the cell, which is how the 5h -strip applies it to 1h cells (1800). `×` outranks the tint — a cell the -pool cannot cover is unusable for a stronger reason — and the dim count -may sit one off the sentence's `~N awake`: cells are grid slots, the -sentence is clocks, the same one-cell tolerance the fold count carries. +strip applies it to 1h cells (1800). A future cell is a slot, never a +verdict: a dry projection does not overwrite cells (the wall belongs to +the notices, which state the exact time; only statusline's folded token +still carries the `×` mark). The dim count may sit one off the +sentence's `~N awake`: cells are grid slots, the sentence is clocks, the +same one-cell tolerance the fold count carries. `scoped_*` mirror the all-model fields for the `weekly_scoped` limit — the per-model weekly cap, `Fable` at time of writing. `scoped_name` is diff --git a/statusline.sh b/statusline.sh index 7b13514..52b75da 100755 --- a/statusline.sh +++ b/statusline.sh @@ -2987,7 +2987,6 @@ run_session_summary() { # ░ unknown: no samples on record for that window # ▮ the window you are in now # ▯ a window still ahead of you (dim: one you likely sleep through) -# × a window the pool will not cover at the current pace # The report draws every slot, so what follows ▮ IS the budget line's # "~N✕5h left" laid out cell by cell — ▮ itself is where you are, not a # window you have left. The live row folds that same tail and prints the @@ -3014,15 +3013,15 @@ run_session_summary() { # capacity on the grid that is not really available. The one # refinement this row carries by tint alone, and the one place # that is honest: a dim ▯ is still a window ahead, so a reader -# who cannot see the tint loses nothing actionable. × outranks -# it — a cell the pool cannot cover is unusable for a stronger -# reason — and the dim count may sit one off the budget's -# "~N awake": these are grid slots, the sentence is clocks, the -# same one-cell tolerance the fold count documents below. -# × a cell the pool will not cover at the current pace — 7d only -# ...▯(✕11) the folded future: 11 more 5h windows AFTER the one you are in, -# all alike (× red when the tail projects dry) — live 7d strip -# only. The count is windows ahead, the same number the budget +# who cannot see the tint loses nothing actionable. The dim +# count may sit one off the budget's "~N awake": these are grid +# slots, the sentence is clocks, the same one-cell tolerance +# the fold count documents below. +# ...▯(✕11) the folded future: 11 more 5h windows AFTER the one you are in +# (× in place of ▯ when the tail projects dry — the one place +# the dry mark still draws) — live 7d strip only, and only +# while the tail is wider than this token. The count is +# windows ahead, the same number the budget # line prices as "~11✕5h left" — one line, one arithmetic. (It is # NOT the hidden-cell count: the 34-cell grid spans 170h against a # 168h period, and a row that says 10 beside a budget that says 11 @@ -3035,11 +3034,14 @@ run_session_summary() { # strip that makes the hollow run the answer to "how long have I got" — five # slots, one per hour, `▃▃▮▯▯` is two whole hours left after this one, no # arithmetic and no second glance at the clock. -# What neither strip draws on 5h is a FORECAST. An empty cell is a fact (that -# hour has not happened); a × is a guess, and on a 5h window the guess is -# already owned twice — the badge states the end (`5h[38%@23:00]`) and a -# notice names the wall with its own gates and an exact time (`5h caps -# ~14:20`). Cells carry the shape, badges carry state, notices do the warning. +# What NEITHER strip draws in a cell is a FORECAST. An empty cell is a fact +# (that span has not happened); a × is a guess, and the guess is already +# owned — the badge states the end, a notice names the wall with its own +# gates and an exact time (`5h caps ~14:20`, `7d dry ~Tue 22:00`). v0.33 +# established this for the 5h strip; the 7d strip kept its × cells only +# because the fold hid them, and the unfold called the question: a run of +# ××× was read live as deleted windows. Cells carry the shape, badges carry +# state, notices do the warning; the folded token alone keeps the dry mark. # Shared by the live `--week` row and the `week` subcommand, so the two # surfaces cannot disagree. @@ -3189,9 +3191,12 @@ five_history_cells() { week_scan "$1" "$2" | sed -n 2p; } # The slot where burn exhausts the pool before reset (-1 = none). The learned # walk speaks first (gap in HOURS before reset); with no trained forecast fall # back to the linear projection claude.py's cap_eta uses, so a wall visible on -# one surface is visible on the other. Both wait out SEVEN_DAY_YOUNG_SECS: a -# row of × drawn across a window that opened an hour ago is drawn from last -# week's burn, and this row does not draw what it cannot see. +# one surface is visible on the other. Both wait out SEVEN_DAY_YOUNG_SECS — +# a guess drawn across a window that opened an hour ago comes from last +# week's burn, and this surface does not draw what it cannot see. Since the +# future cells stopped taking the × overwrite, this slot only decides the +# folded token's glyph (`...×` vs `...▯`) — kept, because the early-week +# fold is exactly where no per-cell shape exists to say it. week_dry_slot() { local seven_int="$1" seven_secs="$2" now="$3" period_start="$4" local dry_epoch="" walk_gap walk_end elapsed7 @@ -3296,12 +3301,18 @@ build_ledger_strip() { else if (lo >= 0 && lo <= start + cs && start <= hi) { g = BASE; c = C_DIM } else { g = "░"; c = C_DIM } } else if (i == nowslot) { g = "▮"; c = C_NOW } - else if (dry >= 0 && i >= dry) { g = "×"; c = C_DRY } else { # a window still ahead — dim when the learned rhythm says # the reader sleeps through most of it (under half the - # cell awake). × above outranks this: unreachable beats - # asleep. Unlearned, every ▯ stays plain. + # cell awake). Unlearned, every ▯ stays plain. A dry + # projection used to overwrite these as red × — v0.33 + # made the 5h strip a record, not a forecast, and the + # unfold put the same question to this one: a run of × + # was read as three DELETED windows (measured, live, + # 2026-09-01), while the guess it drew was already owned + # by the pinned notice with an exact time. Same answer + # here now: a future cell is a slot, never a verdict; + # only the fold token below still carries the dry mark. g = "▯"; c = "" if (have_rest && 2 * cell_awake(start) < cs) c = C_DIM } diff --git a/t/statusline.bats b/t/statusline.bats index 2181eeb..947d0e6 100644 --- a/t/statusline.bats +++ b/t/statusline.bats @@ -1854,17 +1854,22 @@ _mark_dim() { [[ "${out#*▮}" == "{R}▯{R}{D}▯▯{R}▯▯▯{R}" ]] } -@test "build_week_strip: × outranks rest — unreachable beats asleep" { +@test "build_week_strip: a dry guess may not delete a window" { + # The dry projection used to overwrite future cells as red ×, and an + # unfolded ▮▯▯▯××× was read as three DELETED windows (measured live). + # The wall already has an owner — the pinned notice, with an exact + # time — so the drawn cells are identical with the projection and + # without it; only the fold token still carries the dry mark. local mults="0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 1.54 1.54 1.54 1.54 1.54 1.54 1.54 1.54 1.54 1.54 1.54 1.54 1.54 1.54 1.54" - local ps now raw + local ps now with without ps=$(( ($(date +%s) / 86400 - 6) * 86400 )) now=$((ps + 27 * 18000 + 100)) - raw=$(TZ=UTC build_week_strip 50 "$now" "$ps" 31 "" 2 6 "$mults") - # the dry wall keeps its glyph and its red: no dim × anywhere - [[ "$(strip_ansi "$raw")" == *"×××"* ]] - [[ "$raw" != *$'\033[2m×'* ]] - # and the rest cells before it still dim - [[ "$(_mark_dim "$raw")" == *"▯{R}{D}▯▯{R}×××"* ]] + with=$(TZ=UTC build_week_strip 50 "$now" "$ps" 31 "" 2 6 "$mults") + without=$(TZ=UTC build_week_strip 50 "$now" "$ps" -1 "" 2 6 "$mults") + [ "$with" = "$without" ] + [[ "$(strip_ansi "$with")" != *×* ]] + # and the rest cells still dim + [[ "$(_mark_dim "$with")" == *"{D}▯▯{R}"* ]] } @test "build_week_strip: unlearned, every future cell stays plain" { @@ -2105,8 +2110,10 @@ _mark_dim() { [ "$status" -eq 0 ] plain=$(strip_ansi "$output") bar=$(echo "$plain" | head -1 | sed 's/^7d 80% //; s/ .*$//' | tr -d ' ') - # affordable windows first, then the wall — never × before ▯ - [[ "$bar" =~ ▮▯*×+$ ]] + # every future cell is a slot; the wall this frame projects is the + # advisor sentence's to state, not the grid's to draw + [[ "$bar" =~ ▮▯+$ ]] + [[ "$bar" != *×* ]] rm -rf "$tmpdir" }