Skip to content

Lift whole forest by absolute repo staleness#107

Merged
thalida merged 3 commits into
mainfrom
feat/tree-absolute-age
Jul 17, 2026
Merged

Lift whole forest by absolute repo staleness#107
thalida merged 3 commits into
mainfrom
feat/tree-absolute-age

Conversation

@thalida

@thalida thalida commented Jul 17, 2026

Copy link
Copy Markdown
Owner

What

Tree height is normalized to a repo's own commit span, so a repo untouched for years still shows "young" trees at its newest end. This adds a scan-time staleness lift: a stale repo's whole forest reads old, while keeping the intra-repo oldest→newest spread.

staleness = clamp(daysIdle / STALE_HORIZON_DAYS, 0, STALENESS_CAP)   // daysIdle = scanned_at − newest commit
maturity  = staleness + (1 − staleness) · relT                        // relT = 1 − ageT (oldest = 1, newest = 0)
height    = MIN_HEIGHT + maturity · (MAX_HEIGHT − MIN_HEIGHT)
  • asOf = manifest.scanned_at, not Date.now() — a live clock would drift tree height over time and break the deterministic goldens.
  • Fresh repo is a no-op: staleness ≈ 0maturity == relT → byte-identical to today's repo-relative sizing.
  • Stale repo: whole forest lifts toward MAX while preserving oldest→newest ordering. The oldest tree stays pinned at MAX (maturity 1); the newest floors at staleness.

Settings

The two tunables are Rebuild-routed TREES settings, in the Settings panel under Trees → Height by age:

Setting Default Range Meaning
Staleness horizon (days) 730 (~2y) 30–3650 Idle time (newest commit → scan) for the forest to reach its oldest look
Staleness cap 0.85 0–1 Ceiling on the lift; < 1 so a dead repo never fully flattens. 0 turns the lift off

Design notes

  • AgeRange carries the raw, settings-independent daysIdle; treeHeight derives the staleness lift from the current cfg horizon/cap. The tree renderer and the firefly orbit field both derive height from that one path (with the same TREES.value), so orbs never drift off their trees and either slider re-runs the rebuild with no extra wiring.
  • The horizon is floored at 1 day so a mis-set 0 can't divide by zero.
  • Threading: manifest.scanned_at → trees rebuildcreateTreeRenderer, and → fireflies rebuildplaceFireflies. Both forward it to computeAgeRange; omitting it keeps daysIdle = 0.
  • Golden bench: decorationGolden.bench.test.ts calls the renderer/placement without scanned_at, so daysIdle = 0 → digest byte-identical, no recapture needed. The bench now guards the fresh-repo no-op invariant.

Tests

TDD in treeEncoding.test.ts: daysIdle counting/clamp + ISO-datetime scanned_at; a fresh repo is unchanged; a stale repo lifts the whole forest while preserving ordering; the cap holds; the horizon/cap settings tune the lift (incl. cap 0 = off); a same-day (span 0) stale repo degrades sanely. Full suite green (2553), lint clean.

🤖 Generated with Claude Code

thalida and others added 3 commits July 16, 2026 23:42
Tree height was normalized to a repo's own commit span, so a repo
untouched for years still showed "young" trees at its newest end.
Add a scan-time staleness lift so a stale repo's whole forest reads
old while keeping its internal oldest->newest spread.

  staleness = clamp((scanned_at - newest commit) / STALE_HORIZON_DAYS,
                    0, STALENESS_CAP)
  maturity  = staleness + (1 - staleness) * relT   // relT = 1 - ageT
  height    = MIN_HEIGHT + maturity * (MAX_HEIGHT - MIN_HEIGHT)

asOf is manifest.scanned_at (NOT Date.now): a live clock would drift
height over time and break the deterministic goldens. staleness lives
on AgeRange so the tree renderer and firefly orbits derive identical
heights from one computation. Omitting scanned_at keeps staleness 0,
i.e. byte-identical repo-relative sizing (the pre-feature behavior),
so the decoration golden bench is unchanged and needs no recapture.

Constants (hardcoded first pass): STALE_HORIZON_DAYS=730 (~2y),
STALENESS_CAP=0.85 (a forest never fully flattens).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Promote the two hardcoded absolute-age constants to Rebuild-routed
TREES settings (Staleness horizon, Staleness cap) under Height by age.

AgeRange now carries the raw settings-independent `daysIdle` (scanned_at
− newest commit); treeHeight derives the staleness lift from the current
cfg horizon/cap, so both the tree renderer and firefly field apply the
identical, live-configured lift. A cap of 0 turns the lift off; the
horizon is floored at 1 day so a mis-set 0 can't divide by zero.

Golden decoration bench still passes no scanned_at → daysIdle 0 → digest
unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@thalida
thalida merged commit 658fc02 into main Jul 17, 2026
1 check passed
@thalida
thalida deleted the feat/tree-absolute-age branch July 17, 2026 04:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant