Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
26 changes: 15 additions & 11 deletions DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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) |

Expand Down
12 changes: 7 additions & 5 deletions docs/api/state-dir.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).

Expand Down Expand Up @@ -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
Expand Down
53 changes: 32 additions & 21 deletions statusline.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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.

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand Down
27 changes: 17 additions & 10 deletions t/statusline.bats
Original file line number Diff line number Diff line change
Expand Up @@ -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" {
Expand Down Expand Up @@ -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"
}

Expand Down
Loading