Commit Graph

2 Commits

Author SHA1 Message Date
Levi Neuwirth cf4ac1c5ef
docs(tree): revision 2 — five review corrections, all verified in source
REFRESH WAS UNSCOPED FOR THE ANCHOR CONSUMER, and this is the one that
would have wasted implementation time. Two acceptance criteria rested on
`g` refresh preserving collapse and selection. The outline's header
offers "RET visit  n/p move  q quit" — NO `g` — it supplies no
`on_refresh`, and `listview.refresh` opens `if not (p and p.on_refresh)
then return end`. The criteria were unreachable for the only consumer
that exists.

Refresh is now out of scope, with the question it actually raises stated
rather than hidden: an outline refresh means re-requesting
textDocument/documentSymbol, which is an async round-trip with its own
await, failure and staleness handling, and it raises who owns the result
when it arrives against a buffer the user may have edited or left. That
is LSP request-lifecycle work; bundling it here would make the tree lane
responsible for it. Acceptance is re-scoped to what the primitive
controls — collapse and selection surviving a RE-RENDER — and the
refresh follow-on is parked with its precondition.

ACCEPTANCE 1 DECIDED Q#TR4 WHILE CALLING IT OPEN. "No `string.rep`
indentation in lsp.lua" commits to primitive-owned indentation, which is
exactly the question Q#TR4 leaves unresolved. The criterion is
representation-neutral now: the outline renders its hierarchy THROUGH
the primitive rather than by pre-formatting it, and whether the
primitive emits the indentation or the consumer still supplies a string
alongside structural depth stays open.

Q#TR1 MISREAD §14, and the correction changes the tradeoff rather than
softening it. Revision 1 said a separate treeview would be "exactly the
second primitive §14 warns about". §14 EXPLICITLY LISTS A TREE in the
reusable set it wants, alongside virtual list. What it warns against is
bespoke per-consumer plumbing — each subsystem inventing its own UI
vocabulary. A treeview sharing the existing buffer/panel disciplines is
not that; a tree hand-rolled inside lsp.lua would be. The real tradeoff
is narrower and is recorded without a leaning, because the scout still
found nothing that decides it.

THE REGRESSION CRITERION NAMED THE WRONG CONSUMERS, and worse, named the
exact ones §14 exists to correct. `*buffer-list*` and project search do
NOT use listview; §14 measured three call sites, all in lsp.lua —
`*references*`, `*outline*`, `*lsp-help*` — and calls the older claim a
documentation error. Repeating it would have re-introduced a mistake
that document was written to fix. The criterion now protects the actual
siblings, and says what to do if the broader surfaces are ever in scope.

CONSUMER ACCOUNTING TIGHTENED. Five named future consumers, not six. And
the split that matters: ONE existing anchor consumer (the outline) plus
ONE future constraint source (dired's deferred `i`) — dired constrains
the design but cannot validate it, because nothing has been built
against it. Calling them "two consumers that exist" overstated the
evidence by exactly one. "Every input a tree needs" is qualified to
"every input a tree needs to RENDER", since stable node identity is
precisely what no existing field supplies.

Framing only, still unapproved.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 13:22:24 +02:00
Levi Neuwirth 61b1062c5f
docs: frame the tree primitive — anchored on two real consumers
COHERENCE.md §14 grades Tree as the last missing workbench primitive,
and §20 Priority 5 names it as what remains after the bottom panel. Its
argument is to build it once "before dired's directory view and the
workers tree harden their own conventions".

THIS FRAMING NARROWS THAT ARGUMENT DELIBERATELY. §14 lists six future
consumers, and designing a shared primitive against six hypothetical
ones is how you get a model that fits none. The scout found a better
basis: one consumer already ships a tree and fakes it, and a second is
already scoped and deliberately deferred.

THE HIERARCHY ALREADY EXISTS AND IS ALREADY DISCARDED. `Symbol::
push_hier` walks a genuine LSP DocumentSymbol tree — it recurses on
`children` — and flattens it, preserving `depth`, a `::`-joined parent
chain, and document order with parents before children. `lsp.lua` then
re-renders that depth as LEADING SPACES INSIDE THE ROW TEXT, under its
own comment "FLAT with a `depth` field --- indent, don't recurse". So
the outline has no collapse, no expand, no parent/child navigation, and
every input a tree needs is already computed. It is the anchor consumer
because it needs no new plumbing and its limitation is observable today
rather than hypothetical.

Dired is the second: it landed a flat listing for Emacs parity and
deferred `i` insert-subdirectory in its own §13 — the restraint §14
credits, and what keeps the door open.

The workers view is NOT a consumer yet: it is a Rust-generated text
buffer raw-switched into the active window, with no rows.

REFRESH RESTORES A LINE, NOT A NODE, and that is the crux rather than a
detail. `listview.refresh` saves `cursor_line()`, rebuilds rows wholesale
from a freshly produced array, and re-seats by walking `move_down`.
Today that is a mild wrong-restore. Collapse breaks it outright, because
expanding a node inserts rows ABOVE the cursor — and collapse state
itself must survive refresh, which requires recognising "the same node"
across two independently produced arrays. Neither `line_to_item` nor an
opaque `item` can do that. This is why the stable-identity question
decides whether selection and expansion survive a model update at all.

Four questions are left genuinely open: extend listview versus a
separate treeview (no leaning recorded — the scout found nothing that
decides it); who owns collapse state; what a stable node identity is;
and whether the row still carries pre-rendered text. On identity the
scout did establish constraints: listview cannot derive one because
`item` is opaque; the outline's parent chain plus name is nearly
sufficient but collides on overloads; dired's path would be genuinely
stable. So identity is almost certainly consumer-supplied, which makes
it part of the public contract rather than an internal detail.

NO INTERACTION ISLAND unless evidence forces one. Expand/collapse are
buffer-local bindings on a generated buffer, exactly as RET/n/p/g/q
already are. §6 grades islands "weak, and growing"; this must not add to
that count, and if some behaviour cannot be expressed that way it is a
finding to report rather than a licence.

§1.6 states plainly what this document is: unlike the last two lanes
there is no fallout to census and no baseline to diff, so it ARGUES a
model rather than measuring one — the shape that has historically needed
the most review rounds here.

Framing only. Acceptance is explicitly not final pending the open
questions.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 13:13:34 +02:00