Skip to content

The Padi readout opens onto the watcher's ring - #421

Merged
srid merged 13 commits into
masterfrom
kolu-events-feed
Aug 29, 2026
Merged

The Padi readout opens onto the watcher's ring#421
srid merged 13 commits into
masterfrom
kolu-events-feed

Conversation

@srid

@srid srid commented Aug 28, 2026

Copy link
Copy Markdown
Member

The evidence

The doorbell, just the arrival events phase: olai computes the attention events over the mirror it already subscribes to, and shows them — the press on the kolu pill opens a drawer of the machine's recent "wanted attention", each event wearing a frozen StatePip of the exact moment it fired. Nothing is delivered to conversations; the orchestrator-side Monitor keeps soaking on the CLI daemon in parallel (phase B).

Photographed by saatchi against the REAL fleet of this machine (the serve adapter rides in .saatchi/, #419, so saatchi's own playwright driver draws the tab):

feed-opens

Forty-odd seconds later — the two terminals olai's own lanes sit beside (kolu-alert-49 author (pi), master's pinned terminal) crossed the ten-second window, their nags tick, the boot pulse is row one:

after-the-window

The section (throwaway — never committed; .saatchi/data held a two-node vault pointing at the live terminals and a _olai/Kolu.olai with held-for 10s) :

.saatchi/evidence.ts
import type { Saatchi } from "saatchi"

export default async ({ page, shot }: Saatchi) => {
  await page.waitForSelector('[data-testid="padi"][data-padi="connected"]', { timeout: 40_000 })
  // THE PRESS — the whole door, one click.
  await page.getByTestId("padi").click()
  await page.getByTestId("events-feed").waitFor()
  await shot("feed-opens")
  // Settlement window: `held-for` is ten seconds — one shade past it and the
  // held terminals read the transition, later the first nag.
  await new Promise((r) => setTimeout(r, 45_000))
  await shot("after-the-window")
}

The render round, at the head

The render is live in code now: the pulse cell eats the bar's register, the ring is attention-only, and the evidence's own run ate the one course-correction the earlier folds owed (59d87389 — a knob-edit saying its own cadence at once, so the stamp's memory never lies forward). The photographs witnessing both faces: #421 (comment)

The wiring (three folds, each carrying its own claim)

  1. The server computes. packages/kolu-client gains watch.ts — attention agentState → {awaiting, waiting}, a terminal-side hold window, nag pacing, a boot-armed heartbeat, a WATCH_RING = 200 ring. All derived from the wider feed the server already subscribes to and nothing else: exactly the thing the Padi readout needs to stay trustworthy, and the lens phase B's delivery will reuse. The knobs come from the vault: _olai/Kolu.olaiwatch: { held-for, nag, heartbeat } + a mutes group — read by packages/server/src/koluConfig.ts; the vault owner's file doesn't ship, the READ does. Malformed knobs land in the surface's own says. The heartbeat is not a row in here — it was one, the first night; the watch's heart folds onto the pulse wire cell now, carrying its own cadence alongside the stamp, and the drawer folds the ring as attention-only.
  2. The wire names it. wire/kolu.ts gains the KoluEvent schema (a fat value: row = the nostalgic hit — a terminal's frozen pip/bucket/since at fire, no live arithmetic) AND the WatchPulse stamp — at + the beat's own cadence, one per beat, on a new pulse CELL that the fold eats equally off (padiSaid's beatOf); a browser reads, never sets, both.
  3. The press reads. kolu-ui folds the wire to EventLines in padi/events.ts (the copulas from kolu's own says.ts: has been/still waiting for input, has been busy, finished — the machine's words, not English recomposed). props/EventsFeed.tsx composes EventRows off Recency (the waiting-pane eye, recency ticks drawn where the drawer sits), but heartbeats never reach it — the drawer eats attention rows alone. The pill folds the beat instead: padiSaid's beatOf folds the stamp into one of three registers — none (no stamp yet), fresh (watcher pulse 2m ago in the hover's words only), and quiet (older than twice the stamp's own cadence) — and past that threshold the chip coats the AMBER register the olai pill vocabulary never had: hollow dot, warm ring, the words kolu · watcher quiet 47m on it. Deliberately not violet — violet is the register an AGENT's ask eats (bg-alarm's own); the heart is the machine's own coil, and that's the second register the door's house needed.

The semantic wedge the doorbell already settled rides inside the watcher: the hold window sees a row only after the agent-state fold agreed the row IS in a held bucket — byte-fresh waiting trousers the hold just as the padi readout's eye does — and phase B inherits it in the same place.

Verified: bun run typecheck, just test (new suites: 14 watcher behaviors, 10 config-map behaviors, 5 event-fold behaviors), just e2e (984 scenarios, 8558 steps), sh scripts/check-kolu-deps.sh, just fmt-check, just bun-nix-fresh.

Out of scope by design, per the brief — not deferred here, already filed as its own lane (orch-p2b-wake on the board): delivery into conversations — the reader for notification taps, "owner is here" propagation. This ring and its absorber seam are the transport surface that lane leans on.

Deferrals

  • The events walker has no writers — the drawer is a read, not a registration. A "clear the ring" affordance doesn't exist.
  • Heartbeat/nag timers pace by wall clock on the server — a paused machine fires them late, not early (decided at the soak's edge case just test is slow: serial raco test, real-time waits, install prereq #4; matching padi's same semantics).

srid added 5 commits August 28, 2026 16:39
The doorbell takes a step at living in olai: the server derives the
transition/nag/heartbeat events of the attention fleet from the mirror it
already subscribes to — one watcher next to the mirror, its knobs the vault
owner's _olai/Kolu.olai; the browser shows the ring behind one press on
the kolu pill in the status cluster, each event in the machine's own
words (kolu's copulas), each with a frozen StatePip of the exact moment
the row flagged.
…ome, one mute resolution per observation

- HELD_BUCKETS was a second spelling of the set the folds' home already
  pins for every face — its own doc's argument, turned back on myself.
- heldBucketOf now answers holdability in one fold; observe's gate used
  to ask an 'other' bucket AND a Set membership in one breath.
- foldMutes runs ONCE per observation/reconfigure and both its readers
  (the silence gate, the ambiguity says) read the same answer — a value
  can never be silenced-to and said about in the same breath.
- The heldFor 0 ternary did a thing setTimeout already means; the
  re-pacing pass is one loop instead of two phases that were one fact.
…wire guarantee makes unreachable

koluConfig's PROPS array existed at runtime for a typeof on the next
line — a string union is the honest spelling. The events fold's
`since === null` arm folded a case the KoluEvent schema cannot carry;
the wire guarantees the observation clock, so the fold stops humoring
an invisible fourth shape. The test suite never named either, and now
has no reason to.
…MEs read straight

"The four members" had to become five when events joined, and the
kolu-client row of architecture had dropped the attach stream in the
renumber; kolu-ui README held a clause that argued against an
alternative nobody had reached for. All three rungs walk again.
@srid

srid commented Aug 28, 2026

Copy link
Copy Markdown
Member Author

Review — Claude Code (reader #1)

Verdict: request changes. The fold is real and the shape is right — one watcher over the mirror, one ring, one frozen fat value, no client-side arithmetic (I checked: EventsFeed.tsx reads nothing off the live fleet, narrowRowVocab runs over the frozen bag, active/bytesLive are stamped false server-side at watch.ts emitHold). Two findings land on the two claims the brief centres — "malformed values reported not swallowed" and the pacing — and both are wrong as wired.


MUST

1. packages/server/src/runtime.ts:732 — every malformed line is emitted at debug, and the default level is info. A vault typo is silent.

say: (line) => say(Effect.logDebug(line)) is the only sink for koluHalf's revision malformed lines and for watch.ts's ambiguous-mute sentence. packages/log/src/level.ts documents the default as info ("Quiet stays the default"). So the line never reaches a default server run.

That contradicts four places that promise otherwise:

  • koluConfig.ts:64 — "so a vault typo is a sentence on the server's console exactly once per new shape, and not a silent default"
  • koluConfig.ts:26 — "the line it earns is returned, for the caller to SAY … which is where the 'log line' the brief promises lives"
  • docs/kolu.md — "a malformed one defaults and is said on the server's console, so a typing mistake is never silent"
  • the PR body — "Malformed knobs land in the surface's own says." They land in a debug log, not in any surface member.

watch.ts's ambiguous-mute comment is the sharper version: "the only door the owner has to know is this line — the CLI's refusal, made a sentence." A door behind OLAI_LOG_LEVEL=debug is not a door. Either route these two to logWarn/logInfo (they are per-shape, not per-revision — the saidMalformed and saidAmbiguous latches already make them rare), or give them the errors cell the PR body implies. Do not leave the prose claiming a console line that isn't there.

2. packages/server/src/koluConfig.ts:560 is a valid duration, and nag: 0s turns the ring into a broadcast loop.

/^(\d+)(s|m|h)$/ matches 0s. Nothing downstream clamps: watch.ts:319 fireNag re-arms setTimeout(fireNag, config.nagMs) from inside itself, and watch.ts:286 rearmHeartbeat does setInterval(pulse, config.heartbeatMs). Probed against the real modules — watchConfigIn on a held-for: 0s / nag: 0s / heartbeat: 0s node, then one awaiting_user observation:

config: {"heldForMs":0,"nagMs":0,"heartbeatMs":0,"muted":[]}   ← malformed: []
in 200ms: emits=372 evicts=172 ringSize=200

~1,900 events/sec, the whole 200-deep ring churned every ~110 ms, every one of them a collection delta fanned out to every open tab — from one character in a file the docs invite the owner to hand-edit, with malformed: [] and therefore (see finding 1) not a word anywhere. nag: 0s is also the spelling a person reaches for to mean off. Reject non-positive durations as malformed, or floor them; 0 is the one value in the grammar that has no sane reading.


SHOULD

3. packages/kolu-client/src/mirror.ts:382 (cleared) → watch.remove — a link flap re-fires every standing hold, and re-dates its since.

link.ts:364 and link.ts:391 call sink.cleared() on every way a dial ends, including the retry loop against a padi that is merely restarting. cleared emits one remove per row; koluHalf's remove runs watch.remove(id), which releaseHolds. On reconnect each row is a fresh observe → a new Hold with since = now.

So a padi restart while three terminals sit awaiting: held-for later, three duplicate transitions, each reading "has been awaiting input for 60s" about a terminal that has been blocked for three hours, and each restarting the nag cadence from zero. A flapping socket is a feed of nothing but duplicate transitions, evicting the real ones out of a 200-deep ring.

watch.ts's header documents this for a server restart ("a server restart re-sees the fleet cold … re-dates every standing hold") and KoluEvent.row.since's doc calls it "an ordinary restart, not a lie." A link flap is neither ordinary-restart-rare nor visible to the reader, and the doc doesn't cover it. At minimum say so; better, keep holds across a cleared that is followed by a reconnect with the same id (the record is gone, but the hold's clock is the watcher's own).

4. packages/kolu-client/src/watch.ts:376-395reconfigure re-dates every fired hold's nag whenever either interval moves, and the comment claims it doesn't.

"The nag re-seeds — the interval is measured from the last emission, so a re-arm IS that" (watch.ts:391) is false: the re-arm is setTimeout(fireNag, config.nagMs) measured from now, and nothing records the last emission. And intervalsMoved (:376) conflates heldForMs with nagMs, so editing held-for — a knob with nothing to do with the nag — pushes every outstanding nag out by a full nagMs.

This is reachable in the ordinary way, and the config layer makes it worse: an intermediate malformed value while typing falls back to DEFAULT_WATCH, not to the previous value, so typing 90s over 60s walks the knob 60000 → (malformed→60000) → 9000 → 90000 — several genuine moves, each re-dating every nag. Under a 10-minute nag, a minute of editing the file is a nag that doesn't fire. Stamp lastEmittedAt on the Hold and re-arm from lastEmittedAt + nagMs, and split the two guards.

5. packages/server/src/koluConfig.ts:117-118watch and mutes can come from two different files.

The two regulars.find(...) calls are independent, so a shallow _olai/Kolu.olai holding only watch and a deeper sub/kolu.olai holding only mutes yields a config assembled from both. The header two paragraphs up says the opposite: "one file by convention … one file is one decision." Pick the deciding file once (the shallowest with either node) and read both from it, or amend the header.


NIT

6. The ring's cap has no test. WATCH_RING, push's eviction loop and sink.evict are the one member the brief names by number and the only place evict fires — watch.test.ts's collected() wires an evict that no case exercises. The eviction order and what a subscriber sees across the 200th event are untested in both directions.

7. packages/kolu-client/src/watch.test.ts:278 — the config-echo assertion is vacuous. expect(afterEcho).toBeGreaterThanOrEqual(first) is monotonically true over an append-only array; it passes whether or not the echo reset the cadence. The guard at watch.ts heartbeatMoved is correct — the test just doesn't say so. Assert the count is what the unchanged cadence predicts.

8. packages/web/src/client/padi/Padi.tsx silently drops aria-live="polite". The pill was the only live region announcing a kolu link change; as a <button> with aria-expanded it now announces only on focus. Nothing in the diff or the header notes the trade. If it's deliberate (a live region on an interactive control is genuinely odd), say so where the header explains the press.

9. packages/web/src/client/padi/Feed.tsx:32w-80 is dead, and overflow-x-hidden is missing. styleOf(at) writes an inline width (384px), which beats the class; commit/Panel.tsx:118 and settings/Panel.tsx:176 — the two panels this file says it wears the same contract as — carry no w-* and both carry overflow-x-hidden.

10. packages/surface/src/index.ts:1510 — the outSince export is unrelated to this PR. packages/web/src/client/chat/elapsed.ts:70 imports it and master doesn't export it, so this line is a fix for a pre-existing break riding in unannounced. Worth a line in the body — the PR's "Verified: bun run typecheck" is partly true because of it.

11. packages/kolu-client/src/watch.ts:355row.agentState ?? bucket is unreachable. heldBucketOf returns non-null only when narrowAgentState produced a literal, which requires a non-empty agentState. Same class as the arm 7254b6a removed one commit ago.

12. packages/kolu-client/src/index.ts:332 — the linkless watcher has no reachable stop. linklessHandlers gets connect: () => Effect.never, so the Effect.ensuring(..., watch.stop()) seam never runs on that path and the heartbeat setInterval lives forever. Harmless for a server, but Watch.stop exists and one of its two callers can't reach it.


What holds up

  • The wire is honestly frozen: pip.active/bytesLive stamped false at emit, agentState provably stable within a bucket (agentBucket is bijective on awaiting_user/waiting), no downstream recompute. faces.ts gives the browser "resource" only, and there are no writers.
  • Ring order survives the wire: nothing in @kolu/surface's client or server sorts collection keys, so the ev-<seq> keys' non-lexicographic order is harmless and EventsFeed's .reverse() over insertion order is the fire order. (It is nonetheless one sort away from breaking — a zero-padded seq would cost nothing.)
  • heldBucketOf is genuinely the one fold; releaseHold/liveHold close the nag-after-release race properly.
  • The age clamp at padi/events.ts (Math.max(now, atMs)) is the right seam for it, and it has the test it deserves.
  • Deferral honesty: phase-B-as-a-filed-lane and the wall-clock pacing note both read straight against the code.

@srid

srid commented Aug 28, 2026

Copy link
Copy Markdown
Member Author

Grok

Read the five commits at dda5c9e1 against the PR body, watch.ts (holds, nags, heartbeat, ring, mutes), koluConfig.ts, the KoluEvent wire, the events fold, EventsFeed/eventLine, the pill door, and padi's own watchStates knobs that this will replace. No suites run.

Verdict: OBJECT. Phase A is the right shape — server computes, wire is a frozen fat value, the browser is a log reader, _olai/Kolu.olai is live, phase B is honestly out of scope. The hold window, same-bucket refresh, mute-on-edit, interval-echo (vault keystrokes do not re-pace), boot pulse, age-clamp, and Dock vocabulary all hold under reading. What does not is the claim that a row leaving the fleet is "a terminal that left." A padi flap produces a transition that should not fire, and that is the event phase B will wake on.

MUST

  • packages/kolu-client/src/watch.ts:117remove equates "the terminal shut" with "the link dropped and every row went." The second is a false transition, not a close. link.ts calls sink.cleared() on socket close and on dial failure; mirror.ts:382 then sink.removes every id; remove releaseHolds; the reconnect observes the same ids still awaiting/waiting and starts a new hold at since = now (watch.ts:351). After held-for, each one emits kind: "transition" as if it had just entered the bucket.

    Real fleet sequence: a terminal has been awaiting_user past the window (transition, maybe a nag, already on the ring). Olai's padi socket drops — kolu restart, a mirror fault, the spaced redial. The watcher itself is still alive: heartbeat keeps pulsing, the ring still holds the old rows. Sixty seconds after the socket returns, the same terminal is said again as a fresh "has been awaiting input for 60s." Padi's daemon-side since does not reset on a client reconnect (PadiStateEventSchema: snapshot of currently-matching, not a new debounce). The soak's kolu watch will stay quiet. Olai will not.

    A closed terminal is not waiting for anything — that half of the comment is right. A fleet emptied because this process lost padi is still waiting; the watcher never stopped. cleared() must not be N close events for hold purposes. Keep the holds across a flap (pause the timers while there is no fleet, resume without re-arming since if the id returns in the same bucket) or emit a snapshot-kind on rejoin, matching padi's late-joiner. As written, phase B inherits a doorbell that rings every time the link blinks.

SHOULD

  • packages/kolu-client/src/watch.ts:365mute re-derivation is not live on remove. foldMutes runs on observe and reconfigure only. Prefix mute t against t1+t2 is ambiguous (silences nobody, said once) — tested. Then t2 closes: seen.delete("t2"), t2's hold dies, t1's hold is not re-asked. The mute now uniquely names t1. Nags keep firing until t1's next upsert. A quiet awaiting row can sit without an upsert for the whole nag interval; the timer does not observe. The comment at watch.ts:329 says the prefix table moves when the fleet moves. remove is a fleet move. Re-fold after the delete, the way rejoin already re-resolves claims when the id set shrinks (mirror.ts:369).

  • packages/server/src/koluConfig.ts:56nag: "0s" and heartbeat: "0s" are well-formed and spin. Padi refuses nagMs == 0 ("a nag every 0 ms is a spin") and caps all three knobs at MAX_TIMER_MS so setTimeout cannot overflow into fire-immediately-forever. DURATION accepts (\d+)(s|m|h), including zero; fireNag re-arms setTimeout(fn, 0) and rearmHeartbeat does setInterval(pulse, 0). held-for: 0s is the legal instant-report padi allows. The other two are not. Refuse 0 for nag/heartbeat (malformed line, default stands) and reject values past the timer ceiling.

NIT

  • packages/kolu-client/src/watch.ts:394 — the re-pace comment has raised and lowered backwards. Code: remaining = heldForMs - (now - since); a lowered window that has already elapsed fires at once (Math.max(0, remaining)); a raised one sits out more time. The test at watch.test.ts:288 is the lowered case and is right. The sentence is the opposite of the arithmetic.

  • packages/kolu-ui/src/props/KoluUi.tsx:68 — the arithmetic commit named five members in the READMEs; this socket still says "THE FOUR MEMBERS" while KoluClient now has events beside fleet. Same miss in packages/kolu-ui/README.md:5.

What holds, so it is not re-asked:

  • Hold window / flap-inside-window silent / waiting→awaiting as a new hold: matches padi's per-state since.
  • Same-bucket observe refreshes the row and leaves timers alone, so activity pulses and reclaim-on-keystroke do not starve nags.
  • reconfigure compares the three numbers before re-pacing; a vault revision that did not move a knob is a no-op on timers. Mute-on-edit kills the hold immediately.
  • Ring is insertion-order Map, cap 200, emit then evict; a reader may see 201 for one turn of push, then the remove. Fine.
  • KoluEvent is frozen; eventLine does not look up the live fleet; live flags stamped false at fire; age clamp Math.max(now, atMs) is the saatchi dash, tested.
  • Feed speaks Dock (StatePip, RowLabel, recencyText, narrowRowVocab). Pill is the existing popover toggle, desktop-only as the other chrome pills.
  • Mutes: prefix resolution, ambiguous silences nobody and says so, none is fail-open (padi's --ignore). File basename case-folded, first watch node, malformed durations said not swallowed. Defaults on absent.
  • Deferrals are honest: no conversation delivery (orch-p2b-wake), no "clear the ring," late-not-early on a paused machine.

The MUST is the only thing that should stop a merge. The rest can fold or file.

@srid
srid force-pushed the kolu-events-feed branch from afdd832 to 52b6023 Compare August 28, 2026 22:39
srid added 2 commits August 28, 2026 18:40
…rammar's floor

The release review round's semantic field, collated:

* THE FLAP EATS NOTHING. `mirror.ts`'s `cleared` no longer spells N
  terminal-closes: rows fall on a new `clearedRow`, and the watcher hangs
  its holds suspended, timerless. On the row's return in the same bucket
  the hold re-arms off its own clock (`since`, the new `lastEmittedAt`)
  — the arithmetic the daemon's own `since` runs against a reconnect —
  so a flapping socket cannot re-fire the standing holds or re-date the
  ring the way the previous one did. The blind debt is folded in the
  resume: a hold that crossed its window while the fleet was out fires
  on the return, once, and the nag thereafter stays on cadence.
* THE PACE REMEMBERS. `armNag` anchors at the last emission, never the
  edit; `reconfigure` splits `heldForMoved` and `nagMoved` into the two
  guards they always were, so one knob never touches the other's holds
  — the drain under a live vault that opus clocked at a minute of typing.
* THE GRAMMAR HAS BOTH BOUNDS. `nag` and `heartbeat` refuse `0` the way
  padi's own schema refusal says, and every duration reads against the
  ~24.8-day setTimeout ceiling above it — malformed gets the default,
  plus a line. `held-for: 0s` is padi's legal instantaneous report and
  the file promises it so.
* ONE FILE DECIDES. A `watch` node in one layer and a `mutes` in another
  no longer compose half a config: the first file by convention that
  carries EITHER node yields BOTH halves.
* THE SENTENCES REACH THE CONSOLE. `KoluDeps` gains a `warn`, wired at
  `logWarning`: malformed `_olai/Kolu.olai` knobs and the ambiguous-mute
  sentence land on the DEFAULT server console — the door the four places
  of prose ("said on the server's console") always claimed to keep.

The ledger's quieter entries fold in with it: `row.agentState ?? bucket`
was the fold the gate is a proof about, and goes; the heartbeat-echo test
now counts the cadence it asserted; the ring's eviction test finally runs
the cap in both directions.
…nel wears its paragons' coat

Grok's ledger-closing sweep: KoluUi's socket comment and kolu-ui's README
still read "the four members" while the composition has listed `events`
beside `fleet` since the first draft — the count is up and both sentences
are folded to it, with the member the room forgot named. Padi's header
answers opus's aria-live question in writing (a button with `aria-expanded`
is the wrong place for a live region, and the header plain says why).
The Feed panel wears its paragons' coat: the `w-80` that the inline width
was already overriding goes, and `overflow-x-hidden` joins — the shape
`commit/Panel.tsx` and `settings/Panel.tsx` already wear.
@srid
srid force-pushed the kolu-events-feed branch from 52b6023 to 914dfc3 Compare August 28, 2026 22:40
@srid

srid commented Aug 28, 2026

Copy link
Copy Markdown
Member Author

The two ledgers read as one and landed folded:

  • The flap — the two converged MUST — owns the fold at 7c476229: the mirror's sink separates remove (a row left; hold dies) from clearedRow (the link emptied the fleet; hold suspends timerless), and the watcher's idle indoors rides since and the new lastEmittedAt: a flap never re-dates, the nag owed over the blind span fires on the resume — late, never early, the daemon's own re-report semantics.
  • The sentences reach the default console: KoluDeps.warn bound at logWarning — the door whose four prose claims opus named.
  • Grammar's floor and ceiling: 0 refused for nag/heartbeat (padi's own refusal language); every duration bounded by the ~24.8-day timer ceiling. held-for: 0s remains padi's lawful instantaneous report.
  • One deciding file for watch AND mutes — the fold's home's own argument.
  • The interval guards split, and the nag re-arms from the last emission.
  • The NIT rung is in: echo-test counts its cadence, ring tests run the cap in both directions, ?? bucket gone, linkless stop reachable via ensuring, aria-live's chapter written where the door lives, Feed's classes paragon-shaped, the five-member lists made arithmetically so.

Local gates: bun run typecheck 18 targets, just test 87/87, just e2e 984 scenarios / 8558 steps, fmt-check clean. Head: 914dfc31.

@srid

srid commented Aug 28, 2026

Copy link
Copy Markdown
Member Author

The pulse on the pill — candidates, photographed by saatchi from the live tree

The candidate set folds the result on one axis: QUIET on the pill when the pulse still lands; violently LOUD when it went quiet. The ring's own row for the heartbeat folds OUT of the feed — all candidates C keep a thing-to-chew for A-ab eyelines. The rows here are the REAL fleet's; only the mock-adornments are painted in place (DOM surgery by .saatchi/evidence.ts, throwaway).

A — fresh pulse, the pill says nothing (except when inspected)

A1 — the pill, as the pill stands. Nothing beside it. The whole of the signal lives underneath.

a1-pill-quiet

… and on inspection the answer exists as one line beside the bar's cadre (title / hover-tip wrap):

a1-inspect-tip

A2 — a soft word rides the pill. The quietest public-version of A1: · the watcher beats 3s ago in label-dim beside the pill, no second shape.

a2-the-quiet-word

A3 — a low beat breathes through the dot. Each beat-window the pip-ring paints once around the DOT so fast-forward is effortless.

a3-dot-breath

B — pulse overdue: a pill that should not sit there quiet

B1 — the smear warms. The pill's background climbs above the cluster's paint, label untouched.

b1-pill-warms

B2 — the pill folds the word. The label edits + the strong paint on one smear: kolu · watch dead. Foremost "shove it somewhere" says enough.

b2-pill-names-it

B3 — a welded flag, the pill unmoved. An attention-flag welded to its edge.

b3-the-flag

C — the drawer with heartbeats folded out

C1 — attention rows, and only attention rows. held-for/nag/transitions of the fleet, the boot-pulse row itself folded out of EventsFeed's fold.

c1-fold-eats-the-beat

C2 — the pulse left a slip at the foot. The drawer's foot admits the last beat's age: quiet, under the list, right-anchored in the ink pond (· the pulse is 3s).

c2-foot-is-the-pulse

Drafting notes the shot-set discloses: B2's label fold pulled the pill's DOT along, so its green link mark is honest but cursedly vivid vis-à-vis the dead-pulse word — a survivor fold would have the DOT eat the alert ink in B's accent too. A3 needs a solid animation-on-beat CSS (the shot's stillness shows the outline at one arc; the fold is one frame per beat-window).

srid added 5 commits August 28, 2026 20:00
The ring holds ATTENTION events only from now on, so a quiet feed and
a dead watcher can no longer read alike: the heartbeat stops being a
row. Its answer moves to a new wire member — the `pulse` cell — carrying
two facts at every beat: WHEN it last stamped, and the CADENCE it was
stamped under (`everyMs`, the `heartbeat:` knob's own number, eaten
beside the stamp so the door's age arithmetic never has to know the
vault's names).

  * `wire/kolu.ts`: the `WatchPulse` shape + the `KOLU_UNPULSED` seed;
    the ring's doc retells what the ring eats now.
  * `wire/index.ts`: the `pulse` member: one stamp per beat, no settable
    verb in the browser's answer — the beat is a fact the wire says.
  * `watch.ts`: the heartbeat's fold folds `at` + `everyMs` onto the
    sink's new `beat` mouth, and stops pushing ring rows; the holder's
    header now tells the new fold.
  * `kolu-client/src/index.ts`: the deps' `pulse` setter hook, the
    kolu-members' `pulse` store (`get`-only), and both faces (linked
    and linkless alike: a linkless face STILL stamps, which is the
    fresh-install preview its header argues for).
  * `runtime.ts`: `pulse: () => published?.cells.pulse` — the same door
    the events collection already walks.
  * `faces.ts`: `pulse: "resource"` — a browser eats the read, never
    the handle.
  * `watch.test.ts`: the pulse-counting assertions MOVE to the `beat`
    sink, the `heartbeat`-in-ring assertions die with the build, and
    the ring-cap test's fill-account is attention rows alone.
`padiSaid` gains a second axis: the pulse's fold (`beatOf`) folds the
cell's stamp into one of three register words — `none` (no stamp ever
read), `fresh` (the beat is within two cadences), `quiet` (past two
cadences, the loud one). This is THE REGISTER the prototype named:
something of the machine's OWN is broken, not a human is owed one —
amber, as a third family beside the link's green/point at you, one
nobody else on the bar is allowed to borrow.

The fold's threshold is one line: `age > everyMs * 2`. The stamp
carries its own cadence, so the pill need never guess the knobs: it
runs its arithmetic off the pulse's own back. A beat within its own
window reads as the hover's answer — `watcher pulse 2m ago` — and past
the threshold the same sentence folds to `watcher quiet 47m` (the
drawer's loud edit, with the label's coat and the hollow dot eat the
same vernacular).

The pill's press is the resources' own: the events drawer eats
heartbeats out (skipping `heartbeat` rows before the rows have their
day), so the ring's eat is attention rows alone and the dead-feed /
quiet-feed distinction is made BY THE PILL rather than read off an
empty drawer.
The pill's coat and the hollow dot arrive, and they are the ONLY
member of the family: the violet (`--color-alarm` and siblings, the
vocabulary an AGENT's ask wears: blocked, skewed, waiting-for-a-human)
and the amber (`PILL_WARN_COAT`, `DOT_HOLLOW_WARN`, `TEXT_WARN`, the
prototype's own inks) answer two questions that are never one —
"do you owe a human something?" and "is your watcher listening?" — and
the reader's answer to a bar under pressure should not mix them.

The fold's coat choice is likewise one edit's worth: when the beat's
register goes `quiet` the ring on the pill, the hollow dot, and the
warning sentence beside the dot all arrive in the same breath.
`data-padibeat` names the REGISTER explicitly, so any reader counting
on the link's own axis (the chip's green door) is undisturbed.
`docs/kolu.md`'s feed section said the heartbeat was the surface — a
row's "the watcher is alive" proving the silence differently. It now
folds the door the round asked for: the ring eats attention, the
pill owns the beat, and the 2×-cadence threshold is the pill's own
arithmetic, not a knob somebody has to know. The pill's registers get
the desk the paint calls: `fresh` is the quiet coat, `quiet` is the
loud amber fold, and "heartbeat has no row" becomes a note (the mutes'
grammar rather than the drawer's fold). The kolu-ui README's row-blurb
gets the register's name as well.
The evidence's own run found this one: an operator lengthening the
knob — `10s` → `120s` mid-watch — leaves the pulse's last stamp carrying
yesterday's `everyMs`, which is the cadence the door's margin folds off.
The pill then says `watcher quiet 40s` at t+20s while its new answer
should be "this beat will land in two minutes"; the fold's arithmetic
is honest and the information it answered with was still old.

The eat: `rearmHeartbeat` fires its own pulse when called — which, by
`reconfigure`'s echo guard, is a MOVED knob and never a keystroke —
so the stamp's `everyMs` folds the new cadence onto the ring's own
margin the moment the file folds it. The boot is unchanged in shape
(one pulse at arms), and the two-cadence margin's arithmetic is
unchanged — only the stamp's own memory stops lying forward.
@srid

srid commented Aug 29, 2026

Copy link
Copy Markdown
Member Author

The pulse's register, photographed honestly — the fold is live on the PR's head.

Three shots, all REAL state; no coats painted by hand, no DOM folds this time — and C happened the eating the code rounds ate: a living grok · waiting lane answered with one transition and NO the watcher is alive rows (watcher is alive doesn't exist — said fold's drawer-eating is heartbeat-sans since ef80abcc).

A — the fresh face: the pill eats nothing; the recency sits in the inspection's door, verbatim:

the-pill-stays-quiet

B — the quiet face, arrived at honestly: the vault's heartbeat was REWRITTEN mid-run from 10s to 120s — the beat stopped landing at their door, the last stamp's own margin (2×10s) ran out, and the register folded on itself. The pill wears the register — hollow dot, amber ring and words, watcher quiet 40s — and the drawer behind it says on top what it eats: the config file's own knob-chips show the fold's honest caveat in real time.

the-pill-warns-quiet

C — the drawer, attention only: ONE row, no watcher-words on it:

the-drawer-keeps-attention-only

The fold's course-correction the evidence caught is 59d87389: heartbeat's knob-edit 120s left the stamp remembering 10s's margin, so rearmHeartbeat says the eat at once, and the stamp's everyMs never lies forward.

Live feedback straight off the first morning of the drawer in use: three
rows, all reading "master" — the label tells the intent-branch but not
WHOSE repo it belongs to. Kolu's own Dock spelling is `repo·branch`, so
that's the spelling the row's head now eats.

Wiring, minimal as the frozen design allows:

  * The `KoluEvent.row` wire bundle widens by ONE field — `repo: string |
    null` — frozen at fire time alongside everything else; the watch's
    freeze copies `hold.row.repo` (the `FleetTerminal` the wire already
    carries), never a query of the live fleet.
  * The fold `padi/events.ts` grows `who` via a separately named
    `repoPrefix`: `repo·label` where a repo is named (kolu's own
    spelling, no spaces: `nixos-config·master`), the label alone where
    none is — the Dock's group shows exactly that in the no-repo corner.
  * The drawer draws `who` where it read the raw `label`; chips and gate
    tests change none.
  * `padi/events.test.ts` folds the who-arm's three shapes: a repo, a
    repo named after the drawer, none.
@srid

srid commented Aug 29, 2026

Copy link
Copy Markdown
Member Author

The drawer disambiguates its rows — live feedback, folded.

The shot the human sent: three rows all reading master. The fold's answer, as the Dock's own spelling (the who panel — nixos-config·master, followed below by the row's words as always):

the-drawer-answers-who

Both rows are the same lane's own: the has been waiting (transition) and the still waiting (first nag, 40s on) — nixos-config's own grok · waiting shell, still its whole drawer. The who-line: repo·branch, the spelling the dock's grouping uses — for the intent-named or repo-less shell the fold falls back to plainly its own label, which is what the Dock's group answers in that corner. Plasticity there was a real corner's shape: the wire bundle widens by ONE field — repo: string | null — frozen at fire time, never the drawer's query.

@srid
srid merged commit 2ecc76d into master Aug 29, 2026
25 checks passed
@srid
srid deleted the kolu-events-feed branch August 29, 2026 12:30
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