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
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed

- `assets/tui/` is now vendored from its canonical public upstream,
[pane](https://github.com/TGPSKI/pane), and picks up the 2026-08-04
`bar_chart` upgrades (aggregation binning, outlier clipping, top-row
label collision handling) plus the new `interact.py` module
(`prompt_search`, `filter_rows`, `hbar`, `cycle`). Byte-identity is now
checkable with `make vendor-check` in a pane checkout; provenance
updated in `assets/tui/README.md`, `LINEAGE.md`,
`references/phase-03-browser.md`, and `PATTERN.md`.

## [0.1.0] - 2026-08-04

### Added
Expand Down
33 changes: 22 additions & 11 deletions LINEAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Everything below is checkable. Dates come from `git log`.
| 2026-07-30 | **2** — the re-application | `15-trust-repair/eval/watch-matrix.sh`, 128 lines | Proof the pattern ports as rules, not code |
| 2026-08-03 | — | the `no-out` reattribution | L17 — the screen produces suspicion, not evidence |
| 2026-08-03/04 | **3** — the live view | `matrix_tui.py` (1,869) + `live.py` (629) | Work in flight; probed liveness; process control |
| 2026-08-04 | **0b′** — the framework goes public | [pane](https://github.com/TGPSKI/pane) | Generation 0b promoted to its own repository: the canonical upstream every copy now re-vendors from, with the byte-identity check as tooling (`make vendor-check`) instead of an instruction |

Nothing here took a quarter to mature, and the pattern did not begin in an
eval. It began in operations.
Expand All @@ -31,16 +32,20 @@ over live server data: `sh-web-analytics` (nginx access logs across several
domains) and `sh-github-analytics` (repository traffic). Both predate the eval
suite by more than two weeks.

Both currently live in a private operations repository. `sh-github-analytics`
is slated for public release as **`catnip`**; this document will carry the link
once it lands rather than pointing at a URL that does not resolve yet.
`sh-web-analytics` reads production access logs and is expected to stay
private.

So for now, unlike every other reference in this document, generation 0 cannot
be followed. The claims made about it below are deliberately the ones that
survive that: line counts, dates, a docstring, and a byte-for-byte diff against
code you *can* read in [leather](https://github.com/TGPSKI/leather) and
Both apps currently live in a private operations repository.
`sh-github-analytics` is slated for public release as **`catnip`**; this
document will carry the link once it lands rather than pointing at a URL that
does not resolve yet. `sh-web-analytics` reads production access logs and is
expected to stay private.

The *framework layer* they share, however, is public as of 2026-08-04:
[**pane**](https://github.com/TGPSKI/pane) is the canonical upstream of
`shared/tui/`, carrying the provenance hashes of the extraction in its own
LINEAGE.md. The apps — the readers, views, and callouts — are what remain
private; the claims made about them below are deliberately the ones that
survive that: line counts, dates, a docstring, and a byte-for-byte diff
against code you *can* read in [pane](https://github.com/TGPSKI/pane),
[leather](https://github.com/TGPSKI/leather) and
[adherence-suite](https://github.com/TGPSKI/adherence-suite).

They matter for three reasons.
Expand All @@ -52,7 +57,7 @@ those two apps on 2026-07-13 — its own docstring says so:
> bounds-checked put, base color pairs, run loop with min-size guard, footer
> renderer, scroll indicator, CSV loading, and the curses.wrapper bootstrap.

All four files are **byte-identical** to the `tui/` package vendored into
All four files were **byte-identical** to the `tui/` package vendored into
leather's eval scripts and again into adherence-suite:

```
Expand All @@ -62,6 +67,12 @@ framework.py 108 lines IDENTICAL
windows.py 58 lines IDENTICAL
```

(Since 2026-08-04 the upstream is [pane](https://github.com/TGPSKI/pane),
`charts.py` has grown aggregation binning and outlier clipping, a fifth
module `interact.py` joined at its third duplication, and the identity
claim is a command — `make vendor-check` in pane — rather than a table
that can go stale like this one did.)

So **L8's `N–M of T` indicator is not an eval idea.** It is
`TuiApp.scroll_indicator`, written for a log dashboard sixteen days earlier,
and every later watcher inherited it without rewriting a line.
Expand Down
8 changes: 4 additions & 4 deletions PATTERN.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,10 +224,10 @@ What transfers: rank by the question, name your callouts after what they
already ruined, distinct glyphs for distinct absences, probe liveness, one data
path.

**The exception is the drawing layer.** `shared/tui/` — bounds-checked put, a
min-size guard, a footer renderer, a scroll indicator — has been vendored
byte-identically into three codebases across twenty-five days, because it
encodes nothing about any particular job. That is the test for what belongs in
**The exception is the drawing layer.** [pane](https://github.com/TGPSKI/pane)
— bounds-checked put, a min-size guard, a footer renderer, a scroll indicator
— has been vendored byte-identically into four codebases across twenty-five
days, because it encodes nothing about any particular job. That is the test for what belongs in
a framework: if it knows what the numbers *mean*, it is not framework, and
copying it is how a watcher inherits the wrong question.

Expand Down
55 changes: 37 additions & 18 deletions assets/tui/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# `assets/tui/` — the drawing layer, to be copied verbatim
# `assets/tui/` — the drawing layer, vendored from pane

Three stdlib-only modules, 326 lines. **Copy them into the target repository in
Phase 3. Do not rewrite them, and do not improve them.**
Five stdlib-only modules, ~570 lines, vendored byte-identically from
[**pane**](https://github.com/TGPSKI/pane) — the canonical upstream of this
package. **Copy them into the target repository in Phase 3. Do not rewrite
them, and do not improve them.**

This is the one part of a run watcher that ports as *code* rather than as
rules, and the reason is the test worth remembering:
Expand All @@ -10,33 +12,45 @@ rules, and the reason is the test worth remembering:

Nothing in here knows what a trial, an arm, a request or a repository is. It
can put a string inside the terminal bounds, refuse to draw in a window too
small to be honest, render a bar, and say `1-22/73`. That is all — which is
exactly why it has survived three codebases unchanged while every layer above
it was rewritten from scratch each time.
small to be honest, render a bar, prompt for a search, and say `1-22/73`.
That is all — which is exactly why it has survived four codebases unchanged
while every layer above it was rewritten from scratch each time.

## Provenance

Extracted on 2026-07-13 from two live server-analytics dashboards, and vendored
byte-identically since. The files here match, to the byte, the copies in:
Extracted on 2026-07-13 from two live server-analytics dashboards, vendored
byte-identically since, and promoted to its own repository —
[pane](https://github.com/TGPSKI/pane) — on 2026-08-04. The files here match,
to the byte, `src/pane/` upstream and the copies in:

- [leather](https://github.com/TGPSKI/leather) — `examples/14-sig-triage/eval/scripts/tui/`
- [adherence-suite](https://github.com/TGPSKI/adherence-suite) — `src/adherence/tui/`

Verify it yourself rather than believing it:
Verify it yourself rather than believing it — pane ships the checker:

```bash
diff assets/tui/framework.py /path/to/leather/examples/14-sig-triage/eval/scripts/tui/framework.py
cd pane && make vendor-check # diffs every portfolio copy against src/pane
```

See [LINEAGE.md](../../LINEAGE.md) for why that matters.
To refresh this copy after an upstream change:

```bash
cd pane && tools/vendor.sh ../run-watcher/assets/tui
```

See [LINEAGE.md](../../LINEAGE.md) here and
[pane's LINEAGE.md](https://github.com/TGPSKI/pane/blob/main/LINEAGE.md) for
why the byte-identity claim matters.

## What each module gives you

| File | Provides | Laws it carries |
|---|---|---|
| `framework.py` | `TuiApp` (bounds-checked `_put`, colour pairs, run loop, footer, **`scroll_indicator`**), `curses_main` | **L8** — the `N–M of T` indicator is `scroll_indicator`, not something you write per project |
| `charts.py` | `bar_chart` and friends | — |
| `charts.py` | `bar_chart`: single or stacked series, peak markers, half-blocks, aggregation binning (long series bin rather than fall off the right edge), outlier clipping (`clip_ratio` caps the y-axis at a robust bound and labels what ran past it) | **L5**'s spirit at the axis: the y-max must describe a bar the user can actually see |
| `fmt.py` | `compact_num`, `human_bytes`, `duration`, `pct`, `sparkline`, safe `to_int`/`to_float` | **L5** — the coercers return a default rather than raising on a torn value |
| `interact.py` | `prompt_search` (bottom-row `/`), `filter_rows`, `hbar`, `cycle` | Extracted at the third duplication; takes the app as an argument so `framework.py` stays byte-identical |
| `windows.py` | `trailing_hours`, `trailing_days`, `trend`, `stack_cells` | see below |

Two things in `framework.py` are load-bearing and easy to delete by accident:

Expand All @@ -48,7 +62,7 @@ Two things in `framework.py` are load-bearing and easy to delete by accident:

## `windows.py`, and why it ships anyway

All four modules are here, including `windows.py`, which looks at first like
All five modules are here, including `windows.py`, which looks at first like
pure analytics vestige — `trailing_hours`, `trailing_days` and `trend` are
time-series helpers whose docstring names the "hourly CSV wire format", and no
watcher in three generations has called any of them.
Expand All @@ -64,10 +78,8 @@ if segments:
time-series module. Dropping `windows.py` leaves `bar_chart` working for every
call the existing watchers make, and raising `ImportError` the first time
someone passes stacked segments — a latent failure planted in the one component
that is supposed to be the safe part.

Keeping the package whole also keeps it byte-identical to the public copies,
which is what makes the `diff` above worth running.
that is supposed to be the safe part. This is why pane's `tools/vendor.sh`
copies the whole package or nothing.

**Genuinely unused, and safe to ignore:** `read_csv` in `framework.py`, and the
three time-series functions above. They survive for byte-identity, not because
Expand All @@ -77,6 +89,13 @@ The lesson generalizes past this file: **"unused" and "unreachable" are not the
same claim.** A lazy import inside a branch is invisible to every check that
reads the top of a file.

## Testing what you build on it

pane also ships `pty_smoke.py` (not vendored here — it's test infra, and
POSIX-only): drive any TUI in a real pseudo-terminal, feed it keys, assert a
clean exit with no traceback. Point it at your watcher from your own test
suite rather than writing a second harness.

## What you still have to write

Everything that knows anything:
Expand All @@ -87,5 +106,5 @@ Everything that knows anything:
- every law from the [design laws](../../references/design-laws.md) except L8's
indicator, which you get for free

The framework is roughly 300 lines. A useful watcher is 300–2,500 more. Copying
The framework is roughly 570 lines. A useful watcher is 300–2,500 more. Copying
this is the last shortcut you get.
9 changes: 9 additions & 0 deletions assets/tui/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"""Vendored curses-TUI primitives (stdlib only).

Source: https://github.com/TGPSKI/pane @ a51e682
(src/pane: framework.py, charts.py, fmt.py, windows.py, interact.py),
copied verbatim so this repository runs from a fresh clone with no
external path dependency. Upstream owns the API; keep edits there and
re-vendor with pane's tools/vendor.sh rather than diverging here.
Verify with pane's tools/check-vendor.sh.
"""
Loading