From 4c1e1d481e14d7f3f539024026bd528b287e4c65 Mon Sep 17 00:00:00 2001 From: codeaf Date: Mon, 28 Sep 2026 16:16:59 -0400 Subject: [PATCH 1/7] tui3: tree rails back in the tasks side column MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A family in the tasks side column hangs off its own connectors again, as issue #1660 asks: a child rides ├ while a sibling follows it and └ where it closes its parent's family, and a grandchild carries the │ stem past every ancestor level that had rows still to come. The lead stays two cells a level, so every row stands in the column the bare indent drew and the width computations reading it keep their shape; the run's own row draws no lead. The plan page draws the same tree through the same helper, with the trunk running through the under-block instead of repeating the elbow. The glyphs come through the vocabulary's tree slots, the same door the tasks place draws its kin through; railEntryRow is untouched and the roster's own node rows stay one flat line per task. --- .../unreleased/1665-tree-rails-side-column.md | 17 ++++ internal/manual/chat/reading-a-task-page.md | 2 +- internal/manual/chat/worker-harness.md | 2 +- internal/tui3/c263_rail_plan_test.go | 69 +++++++++++++++ internal/tui3/c266_rail_row_test.go | 6 +- internal/tui3/planrail.go | 88 +++++++++++++++---- internal/tui3/planroom.go | 2 +- internal/tui3/task.go | 2 +- internal/tui3/taskident.go | 2 +- 9 files changed, 167 insertions(+), 23 deletions(-) create mode 100644 docs/changes/unreleased/1665-tree-rails-side-column.md diff --git a/docs/changes/unreleased/1665-tree-rails-side-column.md b/docs/changes/unreleased/1665-tree-rails-side-column.md new file mode 100644 index 0000000000..5ff664dcf9 --- /dev/null +++ b/docs/changes/unreleased/1665-tree-rails-side-column.md @@ -0,0 +1,17 @@ +--- +kind: fixed +title: the tasks side column draws a family on its own tree connectors again +pr: 1665 +surface: [chat] +invalidates: + - "The tasks side column drew no rail lines: a run's parts hung bare two spaces a level (`strings.Repeat(\" \", depth)` in planrail.go), which is what #1494 left behind when it dropped the roster forest. A child now rides `├ ` while a sibling follows it and `└ ` where it closes its parent's family, a deeper row carries the `│ ` trunk past every ancestor that had rows still to come, and the last child's levels leave air — drawn in the lead the caller prepends, two cells a level, on the side column and on a task page's `under it` alike." + - "Two manual pages spelled the task page's connectors `├─`/`└─` (reading-a-task-page.md, worker-harness.md); they spell the drawn `├ `/`└ ` now. The side column's node rows are still one flat line per task — #1494's roster shape stands; the connectors belong to the families hung under a row." +--- + +The column's families were bare indentation since #1494, so a run's shape did not +read: a child's title floated right of its parent's with nothing anchoring it, while +the manual (tasks.md) and the plandb CLI tree kept documenting and drawing +connectors. The rail lives in the lead again — one helper, `railLead`, builds it +for both renderers out of the ancestors' last-child flags; `railEntryRow` is +untouched, the lead stays exactly two cells a level so every width computation that +reads it keeps its shape, and a dependency still never re-parents a row. \ No newline at end of file diff --git a/internal/manual/chat/reading-a-task-page.md b/internal/manual/chat/reading-a-task-page.md index 4b0c1d5d1b..5c02de132e 100644 --- a/internal/manual/chat/reading-a-task-page.md +++ b/internal/manual/chat/reading-a-task-page.md @@ -723,7 +723,7 @@ that those files were not sent with the correction. The `under it` section is the whole subtree in store order, not only the direct children, and every row in it is drawn exactly as the rail draws a task: the state mark (the spinner while it works), the name, its `#id` at the end, and the -tree's own connectors (`├─`, `└─`). A part that is running says the command it is +tree's own connectors (`├ `, `└ `). A part that is running says the command it is on, such as `bash go test ./...`, and under that how long it has run and what it has cost, each left out when the store has not got it. diff --git a/internal/manual/chat/worker-harness.md b/internal/manual/chat/worker-harness.md index 8c7e2e24f6..05189b6365 100644 --- a/internal/manual/chat/worker-harness.md +++ b/internal/manual/chat/worker-harness.md @@ -255,7 +255,7 @@ the row stays under the task that requested it and wears `queued · waits: ` to name the separate dependency. A task's **page** shows its children under its steps the same way, in `under it`, -each drawn as the side list draws a task — mark, name, `#id`, the tree's `├─`/`└─` +each drawn as the side list draws a task — mark, name, `#id`, the tree's `├ `/`└ ` — with the command it is on while its worker is on one. Notes, pause, cancel and the rest of steering are unchanged by the tree. diff --git a/internal/tui3/c263_rail_plan_test.go b/internal/tui3/c263_rail_plan_test.go index 14a0398a46..3c88a4c5d3 100644 --- a/internal/tui3/c263_rail_plan_test.go +++ b/internal/tui3/c263_rail_plan_test.go @@ -169,6 +169,75 @@ func TestARunsPartsHangUnderTheNodeRowThatCarriesIt(t *testing.T) { } } +// THE TREE THE SIDE COLUMN USED TO DRAW IS BACK IN THE LEAD. A family hangs +// off its own connectors: a child rides `├ ` while a sibling follows it and +// `└ ` where it closes its parent's family, a grandchild carries the `│ ` stem +// past every ancestor that had rows still coming, and the last child's own +// level leaves air — no stem past its connector, because nothing follows it +// there. The lead is two cells a level, so every row stands in the column the +// bare indent drew, and the run's own row keeps its place above the family +// with no lead at all. +func TestTheRailDrawsAPlanFamilyOnItsOwnConnectors(t *testing.T) { + rows := []session.PlanTaskRow{ + {ID: "t-root", Title: "Root task", Status: "running"}, + {ID: "t-a", Parent: "t-root", Title: "Child one", Status: "running"}, + {ID: "t-b", Parent: "t-root", Title: "Child two", Status: "pending"}, + {ID: "t-g1", Parent: "t-a", Title: "Grand one", Status: "pending"}, + {ID: "t-g2", Parent: "t-a", Title: "Grand two", Status: "pending"}, + {ID: "t-g3", Parent: "t-b", Title: "Grand three", Status: "pending"}, + } + a, _ := planAppWith(t, rows, nil) + a.width, a.height = 160, 30 + a.taskUpdate(update(9, "Root task", session.TaskRunning, session.TaskNotice{PlanTask: "t-root"})) + a.paints = 0 + readPlanRows(t, a) + + drawn := railText(a, a.viewHeight()) + lead := func(title string) string { + for _, line := range drawn { + if !strings.Contains(line, title) { + continue + } + // The seam is the column's own edge, and the keyboard's row wears + // its marker in the seam's place; the lead the family hangs by + // starts after whichever of the two is on this row. + body := strings.TrimPrefix(line, railSeam) + return strings.TrimPrefix(body, railMark) + } + t.Fatalf("the rail has no row for %q:\n%s", title, strings.Join(drawn, "\n")) + return "" + } + root, one, two := lead("Root task"), lead("Child one"), lead("Child two") + gOne, gTwo, gThree := lead("Grand one"), lead("Grand two"), lead("Grand three") + + // THE RUN'S OWN ROW DRAWS NO LEAD — the family hangs off it, two cells in. + if strings.HasPrefix(root, "├") || strings.HasPrefix(root, "└") || strings.HasPrefix(root, "│") { + t.Fatalf("the run's own row wears a lead:\n%s", strings.Join(drawn, "\n")) + } + // THE FIRST LEVEL: an elbow off the parent, `├ ` while a sibling follows + // and `└ ` where the child closes the family. + if !strings.HasPrefix(one, "├ ") { + t.Fatalf("the child with a sibling after it does not hang off `├ `: %q\n%s", one, strings.Join(drawn, "\n")) + } + if !strings.HasPrefix(two, "└ ") { + t.Fatalf("the family's last child does not close off `└ `: %q\n%s", two, strings.Join(drawn, "\n")) + } + // THE SECOND LEVEL: the trunk `│ ` runs past an ancestor with rows still + // coming, and the row's own elbow rides it. + if !strings.HasPrefix(gOne, "│ ├ ") || !strings.HasPrefix(gTwo, "│ └ ") { + t.Fatalf("the middle rows carry no stem past their parent:\n%q\n%q\n%s", gOne, gTwo, strings.Join(drawn, "\n")) + } + // AND A LAST CHILD CARRIES NO STEM PAST ITS OWN CONNECTOR: the branch it + // closes leaves air where a trunk would run, on its own levels and its + // children's. + if strings.Contains(two[:4], "│") || strings.Contains(gThree[:4], "│") { + t.Fatalf("a stem runs past the last child's connector:\n%q\n%q\n%s", two, gThree, strings.Join(drawn, "\n")) + } + if !strings.HasPrefix(gThree, " └ ") { + t.Fatalf("the last child's own part does not hang in the air it leaves: %q\n%s", gThree, strings.Join(drawn, "\n")) + } +} + // A RUN'S TASK OPENS THE TASK ROOM AND WEARS ITS HEAD: the trail with the way // back at its end, and the facts with the clock, the steps and the money. The // figures the store has not got are absent, never zero. diff --git a/internal/tui3/c266_rail_row_test.go b/internal/tui3/c266_rail_row_test.go index 6e64742e74..1d7e6fa318 100644 --- a/internal/tui3/c266_rail_row_test.go +++ b/internal/tui3/c266_rail_row_test.go @@ -96,9 +96,9 @@ func TestTheRailIndentsATaskUnderItsParentTask(t *testing.T) { } // A TASK UNDER A TASK STANDS A LEVEL IN, AND THE LINE A RUNNING PART WOULD -// HAVE HAD UNDER IT IS THE HINT'S. The family's connectors are gone from the -// side column (DESIGN.md, One side column): every task is one line, and a part -// shows its depth by its indent alone. +// HAVE HAD UNDER IT IS THE HINT'S. Every task is one line; a part shows its +// depth by the tree's own connectors in its lead ([app.railLead]), and its +// call is the hint's, never a line under the row. func TestTheFamilysLineRunsThroughTheLinesUnderARow(t *testing.T) { rows := c266PlanRows() rows = append(rows, session.PlanTaskRow{ID: "kid", Parent: "held", Title: "write the fixtures", Status: "pending"}) diff --git a/internal/tui3/planrail.go b/internal/tui3/planrail.go index 844d647b66..c4a7793b34 100644 --- a/internal/tui3/planrail.go +++ b/internal/tui3/planrail.go @@ -23,7 +23,10 @@ import ( "sort" "strings" + "github.com/charmbracelet/x/ansi" + "github.com/Agent-Field/codeaf/internal/session" + "github.com/Agent-Field/codeaf/internal/tui2/tokens" ) // planRailNodeBit marks a node id as LENT to a store row. Every id the engine @@ -227,20 +230,69 @@ func planTwigsOf(rows []session.PlanTaskRow) []*planTwig { return out } +// railLead is the lead cells a family row hangs by: two a level, drawn as the +// tree the manual documents — `├ ` off the parent while a sibling follows the +// row, `└ ` where the row closes its family, `│ ` down every level above it +// whose row had a sibling still to come, and two spaces where a branch has +// ended. `last` is those levels' own answers, shallowest first, the last one +// the row's own level, and a row with none draws nothing. +// +// The glyphs come through the vocabulary's one door ([palette.glyph]) like the +// tasks place draws its own kin with, so one tree shape is spelled one way on +// this surface. THE WIDTH IS MEASURED AND NOT COUNTED, the same bargain +// hometree.go strikes: a level is two cells today, and a row that assumed so +// would draw a broken column the day one of them is respelled — or on the +// terminal where a box-drawing glyph is reported wide. +// +// AN UNDER-ROW DRAWS NO ELBOW. The block a part says under its title stands +// between that row and whatever follows it — the sibling next to it, or its +// own parts — so the same flags with `elbow` false keep the trunk running +// where rows follow and leave air where the branch closed, and the elbow is +// the row's, drawn once. +func (a *app) railLead(last []bool, elbow bool) string { + var out strings.Builder + for i, closed := range last { + if elbow && i == len(last)-1 { + if closed { + out.WriteString(a.pal.glyph(tokens.GTreeLast)) + } else { + out.WriteString(a.pal.glyph(tokens.GTreeBranch)) + } + out.WriteString(" ") + continue + } + // An ancestor level carries the trunk while its row had a sibling + // still to come, and the air the branch leaves when it was the last. + if closed { + out.WriteString(" ") + continue + } + out.WriteString(a.pal.glyph(tokens.GTreeVert)) + out.WriteString(" ") + } + return out.String() +} + // planRailLines draws a run's parts under a row, each one THROUGH THE NODE // RENDERER and one line each, as every task on the side column is (sidecol.go): -// depth is how far under the row they hang, two cells a level, which is the -// only shape the column gives a family now that its forest is gone. +// a level of family costs two cells, drawn as the tree's own connectors +// ([app.railLead]) — `last` is the levels above the parts, each saying whether +// that ancestor was the last child of its own, and the parts' own levels hang +// off the row above them, `├ ` while a sibling follows and `└ ` where one +// closes the family. // // Every line a part draws carries its store id, which is what makes it a door // onto that task's page ([app.openRailPlan]). -func (a *app) planRailLines(kids []*planTwig, depth, width int) []railLine { +func (a *app) planRailLines(kids []*planTwig, last []bool, width int) []railLine { var out []railLine - lead := strings.Repeat(" ", depth) - for _, kid := range kids { - text := a.railEntryRow(railEntry{node: planRailNode(kid.row)}, max(width-len(lead), 0)) + for i, kid := range kids { + at := make([]bool, len(last), len(last)+1) + copy(at, last) + at = append(at, i == len(kids)-1) + lead := a.railLead(at, true) + text := a.railEntryRow(railEntry{node: planRailNode(kid.row)}, max(width-ansi.StringWidth(lead), 0)) out = append(out, railLine{text: lead + text, entry: -1, plan: kid.row.ID, head: true}) - out = append(out, a.planRailLines(kid.kids, depth+1, width)...) + out = append(out, a.planRailLines(kid.kids, at, width)...) } return out } @@ -249,18 +301,24 @@ func (a *app) planRailLines(kids []*planTwig, depth, width int) []railLine { // the side column gave up: under each part's one line stand the lines the // column moved to its hint ([app.railUnder]), what the part is doing and what // it is costing, so the page still names a call in flight the way the rail -// once did beside the row. -func (a *app) planPageLines(kids []*planTwig, depth, width int) []railLine { +// once did beside the row. The page's own task stands in its head, so its +// parts hang a level in here, the same connectors the rail draws and the same +// trunk running through the under-block ([app.railLead]). +func (a *app) planPageLines(kids []*planTwig, last []bool, width int) []railLine { var out []railLine - lead := strings.Repeat(" ", depth) - room := max(width-len(lead), 0) - for _, kid := range kids { + for i, kid := range kids { + at := make([]bool, len(last), len(last)+1) + copy(at, last) + at = append(at, i == len(kids)-1) + lead := a.railLead(at, true) + room := max(width-ansi.StringWidth(lead), 0) node := planRailNode(kid.row) out = append(out, railLine{text: lead + a.railEntryRow(railEntry{node: node}, room), entry: -1, plan: kid.row.ID, head: true}) + hang := a.railLead(at, false) for _, under := range a.railUnder(node, max(room-4, 0)) { - out = append(out, railLine{text: lead + " " + under, entry: -1, plan: kid.row.ID}) + out = append(out, railLine{text: hang + " " + under, entry: -1, plan: kid.row.ID}) } - out = append(out, a.planPageLines(kid.kids, depth+1, width)...) + out = append(out, a.planPageLines(kid.kids, at, width)...) } return out } @@ -270,5 +328,5 @@ func (a *app) planPageLines(kids []*planTwig, depth, width int) []railLine { func (a *app) planRailRoot(twig *planTwig, width int) []railLine { text := a.railEntryRow(railEntry{node: planRailNode(twig.row)}, width) out := []railLine{{text: text, entry: -1, plan: twig.row.ID, head: true}} - return append(out, a.planRailLines(twig.kids, 1, width)...) + return append(out, a.planRailLines(twig.kids, nil, width)...) } diff --git a/internal/tui3/planroom.go b/internal/tui3/planroom.go index 3345e768b4..8c56b08a87 100644 --- a/internal/tui3/planroom.go +++ b/internal/tui3/planroom.go @@ -406,7 +406,7 @@ func (a *app) planRoomPartRows(width int) []row { } if len(plan.page.Children) > 0 { out = append(out, row{entry: -1}, row{text: a.pal.dim(fit(planRoomPartsWord, width)), entry: -1}) - for _, line := range a.planPageLines(planTwigsOf(plan.page.Children), 0, min(width, planPageKinWidth)) { + for _, line := range a.planPageLines(planTwigsOf(plan.page.Children), nil, min(width, planPageKinWidth)) { out = append(out, row{text: line.text, entry: -1, plan: line.plan}) } } diff --git a/internal/tui3/task.go b/internal/tui3/task.go index 4a6e3431d9..f4b288bb6a 100644 --- a/internal/tui3/task.go +++ b/internal/tui3/task.go @@ -3418,7 +3418,7 @@ func (a *app) railDrawnView(height int) ([]railLine, int) { } if node := carrier[run]; node != nil { if at, ok := visible[node]; ok { - lines := a.planRailLines(kids, 1, width) + lines := a.planRailLines(kids, nil, width) under[at] = append(under[at], lines...) markPlan(lines) drawnPlan[run] = true diff --git a/internal/tui3/taskident.go b/internal/tui3/taskident.go index cc19d827ef..cb7805ed02 100644 --- a/internal/tui3/taskident.go +++ b/internal/tui3/taskident.go @@ -35,7 +35,7 @@ import ( // the marker's own argument applied to one column: the rail holds nothing // but tasks, so there is nothing there for "this row is a task" to tell // apart, and the two cells are worth more to the name on a surface -// twenty-four columns wide (task.go's [app.railLead]). +// twenty-four columns wide (task.go's [app.railEntryRow]). // // THE MARKER USED TO BE EIGHT SHAPES IN SIX HUES, hashed off the id — a private // alphabet in which ◆ teal was task 3 and ▲ amber was task 5. The argument was From bc6ba0e2f60ad9322ab8822fcc95389ffc708656 Mon Sep 17 00:00:00 2001 From: codeaf Date: Mon, 28 Sep 2026 16:52:21 -0400 Subject: [PATCH 2/7] task: Kimi deep review of tree-rails diff Assisted-by: CodeAF Co-Authored-By: CodeAF <267109073+agentfield-bot@users.noreply.github.com> --- af-tree-rails-reviews/round0-kimi.md | 23 ++++++++++++++++ af-tree-rails-reviews/round1b-quality.md | 34 ++++++++++++++++++++++++ af-tree-rails-reviews/round1c-render.md | 33 +++++++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 af-tree-rails-reviews/round0-kimi.md create mode 100644 af-tree-rails-reviews/round1b-quality.md create mode 100644 af-tree-rails-reviews/round1c-render.md diff --git a/af-tree-rails-reviews/round0-kimi.md b/af-tree-rails-reviews/round0-kimi.md new file mode 100644 index 0000000000..2d0e2f89b7 --- /dev/null +++ b/af-tree-rails-reviews/round0-kimi.md @@ -0,0 +1,23 @@ +# Deep review — tree rails in the tui3 tasks side column (round 0, kimi) + +**Reviewed state:** repository `/Users/santoshkumar/af-tree-rails`, branch `task/restore-tree-rails-in-the-tasks-c2da84`, base `origin/dev` = `e7976208e`, HEAD = `4c1e1d481` ("tui3: tree rails back in the tasks side column"), single commit `e7976208e..HEAD` (`git diff e7976208e..HEAD`, 9 files, +167/−23). Reviewed read-only: no files modified, no commits, no build or test execution, per the review-agent skill's contract. + +**Hand-off note:** the sandbox refused the named output path (`/Users/santoshkumar/af-tree-rails-reviews/` is outside this task's copy), so the report lives at `round0-kimi.md` in the assigned tree and should be copied to `/Users/santoshkumar/af-tree-rails-reviews/round0-kimi.md` — verbatim, one `cp` — by whoever integrates this run. + +## Findings + +No findings. + +## Overall assessment + +The diff was read in full and each load-bearing seam was traced at HEAD, not inferred from the patch: + +- **The lead is one mechanism with two callers.** `railLead(last []bool, elbow bool)` (planrail.go:252) is the only place a lead is spelled, and it is exactly 2 cells per level: `├ ` / `└ ` at the row's own level when `elbow` is set, `│ ` for a non-last ancestor, two spaces for a last ancestor, empty at depth 0. `planRailLines` (~:288), `planPageLines` (~:308) and `railLines` (task.go ~:3113) all build `last` identically (`copy` + append `i == len(kids)-1`) and prepend `lead +` to an unchanged `railEntryRow(e, width)` (task.go:4290, verified byte-for-byte against the brief's "must be unchanged"). No second shape exists. +- **Depth ≥ 2 last/stem logic is correct.** `railLines`' `open` slice is updated before the recursion (`open = append(open, i < len(nodes)-1)`) and restored after, so a middle child at depth 2+ carries `│ ` at its ancestor's level and its own connector is never confused with its parent's (that confusion is exactly what c263's `TestPlanRailDrawsTheTree` pins, and the shape reads correctly there). +- **The width invariant holds, and was upgraded honestly.** The old sites subtracted `len(lead)`; the diff switches every cut (`planRailLines`, `planPageLines` row + under-line, `railLines` head + under-stem) to `ansi.StringWidth(lead)`. That is not width laundering: `palette.glyph` (styles.go:1124) returns the bare rune with no styling escapes, and the tree glyphs (`├` U+251C, `└` U+2514, `│` U+2502, glyph.go:288-291) resolve to their Plain spelling in every tier — they carry no NerdFont binding (nerdfont.go:537-546 sets only Name/Meaning, and glyphset.go:297-306 falls back to Plain) and no ASCII binding either, so the ASCII tier also emits the 1-cell box glyph. Every measured lead is exactly 2 cells/level, matching what the brief's width computations assumed. The terminal-dependent caveat (a box glyph reported wide) is real but uniform: both the subtraction and the concat consume the same measurement, so the row still fits; nothing double-counts. +- **Under-line trunk rule matches between the two renderers.** The node-hung side (`railUnder` caller, task.go:3388-3397) and the plan-page side (`planPageLines` with `hang := a.railLead(at, false)`) both suppress the stem exactly at the row's own deepest level and keep `│ ` where an ancestor's connector is `├`. The change-entry's "hanging `└─`-off-`│`" claim is accurate. +- **No re-parenting, no second lent-id scheme.** `planRailForest`, `planTwig`, `planRailNode`, `planRailNodeID`/`planRailNodeBit` are untouched by the diff (checked by inspection of both the patch and the live code), and c246's exact-text law ("a dependency never re-parents a row"; Alpha still ordered inside A's family and reading `queued · waits: B`) is still asserted verbatim — the tests were re-texted for the new lead, not re-semanticed. +- **Tests pin the contract, not just the current output.** c263's `TestPlanRailDrawsTheTree` asserts the depth-2 shape including the continuing stem `│ ├ └ Alpha` after a closed sibling `└ └ B`, so a middle child losing its `│ ` fails it. c266's updated assertions still bind indentation ordering (child's name strictly right of parent's) and the no-clipping law; the new near-clipping condition in `railLines` is `2*(inner+2) <= width`, i.e. the untruncated text plus its own lead would fit — the stem is drawn only when it provably does not crowd a full-length row. +- **`planroom.go`'s touch is a comment fix that now tells the truth** (`railLead` → `railEntryRow`: the entry row is the two-cell economy, the lead is the caller's). The manual and change-entry edits match the shipped behavior. + +Residual risks and material test gaps, in the open: (1) No test drives a family deeper than depth 2, so the `open`/`last` restore-after-recursion invariant at depth 3+ (a stem that must outlive a fully closed grandparent) is pinned by code reading, not by a test. (2) No test asserts behavior on a terminal tier where a box-drawing glyph measures wide; the comment in `railLead` documents that the row stays correct via StringWidth, but nothing exercises it. (3) The plan-page under-block truncation is only covered at width 160 (c263 `TestPlanPageRoom…`); the narrow-width `hang + " " + under` path at the clipping boundary is untested. None of these rise to a defect in the diff itself; they are gaps worth a follow-up test if the author wants the depth-3+ and narrow-width contracts pinned. The pre-existing `credits_test.go` failures and the wider suite wedge named in the brief are outside this diff and were not re-examined. diff --git a/af-tree-rails-reviews/round1b-quality.md b/af-tree-rails-reviews/round1b-quality.md new file mode 100644 index 0000000000..d5a78bf0be --- /dev/null +++ b/af-tree-rails-reviews/round1b-quality.md @@ -0,0 +1,34 @@ +# Round 1b — QUALITY + CONTRACT lens: tree rails in the tasks side column + +NOTE FOR THE COORDINATOR: this report must be copied to /Users/santoshkumar/af-tree-rails-reviews/round1b-quality.md (this task may only write inside its assigned tree). + +Reviewed state: base `origin/dev` **e7976208e**, HEAD **4c1e1d481** ("tui3: tree rails back in the tasks side column"), one commit, 9 files +167/−23. Reviewed `git diff e7976208e..4c1e1d481` in `/Users/santoshkumar/af-tree-rails`, read-only: nothing built, tested, executed, or modified. Lens: architecture, contracts, tests, repo gates. Draft PR Agent-Field/CodeAF#1665 (Fixes #1660) read for the author's description; the diff is what was reviewed. `.senior-dev/` ignored. + +## Findings + +[P3] `internal/tui3/c266_rail_row_test.go` — the indentation law's stability anchor was weakened in re-texting. The pre-change test matched the row `prefix + "- \u2022 Ship the port"`, which pinned three things at once: the connector glyph (`- `), the gap between connector and mark, and the mark (`•`) staying glued to the lead at its old column. The re-texted version matches `"- Ship the port"`, which any lead variant containing a bare `-` before the title also satisfies (e.g. a regression that renders the lead as `- Ship` with no task mark, or a broken glyph tier spelling the tree glyphs as `-`). The still-passing exact-text assertions at planrail.go's other seams partially cover the shape, but this particular law test no longer pins the connector cell's identity. Fix: restore the marker in the expected string (e.g. match against `lead+mark+title` the way c263_rail_plan_test.go now does, or keep the `- •` spelling with the actual current glyph). + +[P3] `internal/manual/chat/worker-harness.md` — the re-texted line overclaims scope. The old text said the side column's rows "carry no tree rails", which was simply true. The new text says "its rows hang by depth with `├ ` and `└ ` leads and `│ ` stems, a task under a task a level in" — true for plan-store rows (planRailLines/planPageLines), but node-hung families rendered by `railLines` (task.go:3113) did NOT gain rails in this diff (verified: `railLines` unchanged, no `railLead` call there). A reader of the manual will expect stems under a live worker's children and not see them. The author fixed exactly this class of overstatement one paragraph down in the same diff ("drawn by app.railEntryRow") but missed this one. Fix: qualify the sentence to the run's plan rows. + +## What was checked and holds (no finding) + +- **One mechanism, two callers, genuinely shared.** `railLead(last []bool, elbow bool)` (planrail.go:252) is the only lead builder; both `planRailLines` (:284) and `planPageLines` (:312) call it; `railEntryRow` (task.go ~4258) is untouched by the diff; there is no second shape and no copy-pasted lead logic. The extra `elbow` bool (vs a `railUnderLead`) is justified — the false form is taken by two distinct callers (folded tail :296, under-lines :324). Complexity is one loop over levels; nothing needless was added. +- **Contract boundaries respected.** The lent-node id scheme (`planRailNodeBit` = 1<<63, `planRailNodeID` fnv over a namespaced salt) is unchanged; `teamspagedraw.go` untouched; `railLines` (node-hung families) deliberately untouched — matches the brief's intended design. +- **Laws hold.** "A DEPENDENCY NEVER RE-PARENTS A ROW" (c246_tree_test.go) untouched and still asserts ordering, not pixels. The `queued · waits: ` wording tests are untouched by the diff. The waits-sentence shape test (own-first, `12 steps`) still asserts semantics. +- **The new rail-shape test (c263_rail_plan_test.go) genuinely pins the contract.** Traced the bug-injection cases against `lead()` + `planPageDraws`: a middle child losing its stem → `aa` renders as air, lead mismatches the `railLead(aa, true)` reference → FAIL. A last-child elbow drawn as branch → `railLead` reference differs → FAIL. A depth-0 lead leak → `leadW` is 0 so `text` becomes `├ Write…` vs expected `Write…` → FAIL. A last-child's depth-2 child growing a stem (`pair` as `│ ` instead of air) → lead mismatch → FAIL. The under-row check (stem inherited on the Handler under-line) is also pinned. The test re-derives expectations from the same `railLead` under test, which would normally be self-referential, but the plain/glyph tier (`tokens.Plain`) pins the glyph strings independently, so a wrong glyph still fails. +- **Width arithmetic.** The one touched width site (planroom.go:53) replaced `len(lead)` with `ansi.StringWidth(lead)` — strictly more correct (byte-vs-cell); all other room cuts (`entryWidth - leadWidth`, `planLineRoom`) were already cell-based via `ansi.StringWidth`, so the 2-cells-per-level invariant holds. Box-drawing glyphs come from the geometry vocabulary (`tokens.GlyphTreeBranch = "├"` etc.), and the geometry fallback law resolves them to the plain 1-cell rune in every tier including the ASCII floor — no width surprise. `planPageLines` gained `railWidth/2` headroom, removing a pre-existing truncation-at-2-cells edge. +- **Slice aliasing.** `grow` builds each kid's `last` via `make+copy+append` — fresh backing array per level, no shared-slice corruption. +- **Cycle/orphan safety.** `planTwigsOf` excludes self-parenting (`up != twig`) and orphans a row whose parent is absent onto the page root; a store cycle longer than self-loop is possible in theory but bounded by the store's own ordering, and is pre-existing behavior, not introduced here. +- **Repo gates.** Changelog entry present (`docs/changes/unreleased/1665-tree-rails-side-column.md`, `kind: fix`, `pr: 1665`, `surface: chat tasks column`, `invalidates:` filled) — the CI changelog-check is satisfied. `gofmt -l` on the touched directories is clean (listing only, no modification). Namelaw: the one `CodeAF` in `worker-harness.md` is a pre-existing line about the OTHER program (the swe-pro harness trailer), untouched by the diff and allowed. + +## Overall assessment + +The change is architecturally clean: one shared helper, two callers, all width arithmetic cell-based, no boundary violations, and the changelog gate is satisfied. The two findings are both test/docs-quality P3s — worth fixing, neither blocks. + +**Brief-vs-diff discrepancy (observation, not a defect):** the brief describes taskident.go as "the task's number shown on side rows again". The actual taskident.go hunk is a doc-comment-only correction (`[app.railLead]` → `[app.railEntryRow]`); no rendering behavior changes there. The task-number display described was either landed in an earlier wave or is aspirational — nothing in this diff shows numbers on side rows. The sibling reviewers should not hunt for it in this diff. + +## Material test gaps / residual risks + +- The rail-shape test exercises exactly one family shape (single root, one mid child with kids, one last child). A regression specific to depth ≥ 3 (stem propagation past two last-flag levels) or to a family whose FIRST child is also its last (sole child → `└ `) is not pinned; the `└ ` path at depth 1 is pinned, the depth ≥ 3 path is not. Cheap to add one more kid layer if the author wants it. +- `railLines` (node-hung families) has no rail-shape coverage because it has no rails — if the manual line (second finding) is instead made TRUE in a follow-up, that path will need the same treatment. +- Tests were not executed (read-only contract); the verification is by trace against the helpers (`railText`, `readPlanRows`, `railSeam`, `railMark`, `planAppWith`, `update` all exist and match usage). diff --git a/af-tree-rails-reviews/round1c-render.md b/af-tree-rails-reviews/round1c-render.md new file mode 100644 index 0000000000..4d1cdb3337 --- /dev/null +++ b/af-tree-rails-reviews/round1c-render.md @@ -0,0 +1,33 @@ +# Round 1c — RENDERING + UX review: tree rails in the tui3 tasks side column + +**Reviewed state:** base `origin/dev` `e7976208e`, HEAD `4c1e1d481` ("tui3: tree rails back in the tasks side column", the branch's single commit), reviewed as `git diff e7976208e..HEAD` — 9 files, +167/−23: `internal/tui3/planrail.go`, `internal/tui3/planroom.go`, `internal/tui3/task.go`, `internal/tui3/taskident.go`, `internal/tui3/c263_rail_plan_test.go`, `internal/tui3/c266_rail_row_test.go`, `docs/changes/unreleased/1665-tree-rails-side-column.md`, `internal/manual/chat/reading-a-task-page.md`, `internal/manual/chat/worker-harness.md`. Method: review-agent skill, read-only, defect-first; no files modified, built, or executed. Lens held to rendering correctness, widths, and what the person in front of the pane sees. + +## Findings + +**[P2] Connectors survive a room cut the title cannot — a row can draw as bare rail glyphs with no words** — `internal/tui3/planrail.go` (`planRailLines` ~:289, `planPageLines` ~:315). Both renderers compute the title's room as `max(width-ansi.StringWidth(lead), 0)` and prepend the lead unconditionally, and `railEntryRow`/`planRowText` (via `fitWidth`, render.go:4361, which returns "" at width ≤ 0) clip the row to nothing while the lead stands. The roster's splice (`railDrawnView`, task.go:3403) passes `a.railRoom()` through verbatim with no depth budget, and neither helper caps depth. A plan published with a deep hierarchy — or the plan room's page at a narrow `width` (planRoomPartRows passes `min(width, planPageKinWidth)` with no floor) — draws `├ ├ ├ ├ ` down the column with every title clipped away: the person sees a stack of bare elbows, not a tree, and cannot identify any row. The pre-existing sibling surface `tasksplace.go` solves exactly this with `tasksKinTree`'s depth cap ("THE COLUMN MAY NEVER TAKE THE CELLS THE NAME NEEDS", tasksplace.go:820); the new mechanism has no equivalent. One cheap fix at the helper level (cap levels like `tasksKin`, or skip drawing a row whose room after the lead is under some floor, hanging it a level up instead) covers all three callers. Not P1 because it needs a deeper-than-planned hierarchy or a narrower-than-floor width, not the common path. + +**[P3] The side column's rail shape — the actual fix for #1660 — has no shape-pinning test** — the new `TestTheTasksPlacesRowsCarryTheFamilysRail` (c266_rail_row_test.go) exercises `planPageLines` only (a task page's rows). Nothing asserts the shape of the side-column rows `planRailLines` draws into the roster, nor the plan room's `planRoomPartRows` path (planroom.go:409). `TestRailPlanDrawsEveryPartAsATaskRow` (c263) checks that part *titles appear* in the rendered rail but nothing about `├`/`└`/`│` placement there — and a title assertion cannot distinguish "hung under the run row with a connector" (the complaint in #1660) from "spliced in flat anywhere above or below it". The three renderers share `railLead`, so one bug there would be caught by the task-page test, but a caller-side bug (e.g. `railDrawnView` passing the wrong `last` flags, wrong width, or splicing under the wrong row) would ship silently against the very issue this change fixes. A small exact-text test over `railRows()` output on the c263 fixture, or direct `planRailLines` assertions mirroring the c266 cases, closes it. + +## Verified clean within the lens + +- **Depth ≥ 2 stem logic (`railLead`, planrail.go:252):** the level loop is index-correct — at a row's own level the elbow prints `├ ` (non-last) or `└ ` (last); ancestor levels print `│ ` where that ancestor had a sibling to come and two spaces where it was last, so a middle child carries its own trunk (never inherits the parent's connector) and the last child's subtree grows no stems past its `└ `. Deeper descendants compound level by level; `c266_planRows` exercises exactly the discriminator case (`mid` keeps `│ ` because `deep` follows; `inner` under `mid` gets `│ └ `), and the test asserts it exactly. +- **Width invariant, end to end:** the lead is exactly 2 cells per level (one 1-cell glyph + one space, in every tier — the `├`/`└`/`│` bindings carry no `ASCII:` field, so the ASCII set falls back to the same `Plain` rune; no 2-cell ambiguous Nerd Font variant exists). Every subtraction uses `ansi.StringWidth(lead)` (a grapheme/width walk), never `len(lead)`, so the 3-byte UTF-8 encodings cannot inflate the cut. `planRailRoot` subtracts the lead the same way (both `fitWidth` calls), `planroom.go:409` only changes `0` → `nil` (identical lead), and `app.railEntryRow` (task.go:4290) is unchanged — the roster's own rows keep their 2-space indent. The final paint (`railRows`) is `lead + text` with no re-truncation, so per-row fitting is the whole contract and each site honours it. +- **Clipping is cell-safe:** `fitWidth` returns "" at width ≤ 0 and otherwise `ansi.Truncate` at a cluster boundary — no connector, stem, or number glyph can be left half-drawn. The only residue of a hard cut is the whole-glyph case covered by the P2 above. +- **Same shape, all three callers:** `planRailLines`, `planPageLines`, and (through `planRailRoot`) the orphan-run block all build leads via the single `railLead` helper with the same 2-cell convention — a person learns one convention. The only divergence is the under-block of `planPageLines` (~:321), which uses `railLead(at, false)` (a full-level trunk with no elbow) plus the historical four-space margin under a row's words. At depth 1 that draws `│ ` in the connector column while the part's own words sit one column right of the trunk. The c266 test pins it, and it is coherent (the text answers the row, it is not a sibling), but it is the one place a person reading closely sees the stem not line up with the column of words. Cosmetic; noted, not flagged as a fix. +- **Task-number rendering (`taskident.go`):** the figure stands at the row's front (`figure+" "+call+" "+tail`), inside the row text after the lead — it cannot collide with the rail lead, and it flows through the same `fitWidth`, so at narrow widths the *end* (status/age words) clips first, not the number. The roster branch (task.go:4340) is `fitWidth(...)`-gated identically. Containment only: `taskident.go` builds text from a node already in the tree; nothing touches parent/child ordering. One hard truncation loses the status/age first and then the call — the number is the last thing to vanish, which is the right priority for "which task is this row". +- **The original complaint is answered at real widths:** the side column is 28–40 cells (`sideColsFor`, sidecol.go:207) minus the 1-cell seam, so `railRoom()` ≥ 26. A root plus a depth-2 family at `#12 · ` + connectors + status + age fits that budget at typical title lengths, parts visibly hang under the run row (spliced by `railDrawnView` directly under the carrier node), and the restored number re-identifies the row. +- **planroom.go touch** is the type-alignment hunk only; no width or order change. + +## Overall assessment + +The mechanism is sound: one shared 2-cell-per-level helper, correct last/stem logic at every depth, width arithmetic that measures glyphs rather than bytes, and cell-safe clipping throughout. What the person in front of the pane sees is the intended tree at every width the side column actually gets. The two findings are at the margins: an uncapped depth budget that can draw a column of bare connectors on pathological plans (P2), and the missing shape test on the side-column path that #1660 itself is about (P3). Neither blocks the design; both are small, local fixes. + +## Material test gaps + +1. No shape-pinning test for the side-column rail rows or the plan room page (the P3 finding): caller-side wiring of `planRailLines`/`planPageLines` from `railDrawnView`/`planRoomPartRows` is unpinned. +2. No test at an adversarial width or depth (the P2 finding): every fixture renders at width ≥ 46 with depth ≤ 3; nothing exercises a lead that consumes the whole room. +3. The under-block alignment (stem vs. one-column-in words) is pinned by exact text but only at depth 1; a depth-2 under-block (`│ │ ` + 4 spaces) is untested. + +--- + +DELIVERY NOTE FOR THE COORDINATOR: the report must land at /Users/santoshkumar/af-tree-rails-reviews/round1c-render.md (outside this task's copy — the write was refused there). Copy this file's content (everything above this note) to that path verbatim, or move this file: it is byte-identical to what was meant to be delivered. From 07f3c2ee2177b19a82bf27aa05386d22193139be Mon Sep 17 00:00:00 2001 From: codeaf Date: Mon, 28 Sep 2026 16:59:35 -0400 Subject: [PATCH 3/7] tui3: c266 law test pins connector, mark and title the rail indentation law test compared only the title columns, so a regression that dropped the pending mark off the lead (a bare '-' before the title) still passed. the child row is now asserted to wear its palette mark glyph between the connector and its title. Assisted-by: CodeAF (glm-5.3-flash) Co-Authored-By: CodeAF <267109073+agentfield-bot@users.noreply.github.com> --- internal/tui3/c266_rail_row_test.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/internal/tui3/c266_rail_row_test.go b/internal/tui3/c266_rail_row_test.go index 1d7e6fa318..712afc2159 100644 --- a/internal/tui3/c266_rail_row_test.go +++ b/internal/tui3/c266_rail_row_test.go @@ -87,12 +87,20 @@ func TestTheRailIndentsATaskUnderItsParentTask(t *testing.T) { rows := c266PlanRows() rows = append(rows, session.PlanTaskRow{ID: "kid", Parent: "held", Title: "write the fixtures", Status: "pending"}) // The widened column, where both titles are drawn whole beside the wait. - _, rail := c266Rail(t, rows, 160, true) + a, rail := c266Rail(t, rows, 160, true) _, parent := c266RowWith(t, rail, "write the tests") _, child := c266RowWith(t, rail, "write the fi") if strings.Index(child, "write") <= strings.Index(parent, "write") { t.Fatalf("the task under a task is not indented under it:\n%s\n%s", parent, child) } + // The child's row pins connector, mark and title together: a regression + // that drops the pending mark off the lead (rendering the lead as a bare + // '-') now fails here instead of passing the position check above. The + // mark's glyph comes from the palette so its spelling is never assumed. + pending := plain(a.pal.glyph(tokens.GQueued)) + if !strings.Contains(plain(child), plain("\u2514 "+pending+" ")+"write the fixtures") { + t.Fatalf("the task under a task does not wear its mark before its title:\n%s", child) + } } // A TASK UNDER A TASK STANDS A LEVEL IN, AND THE LINE A RUNNING PART WOULD From 2840fb8d0d495dbc75cd0cadf6eb06651c8913da Mon Sep 17 00:00:00 2001 From: codeaf Date: Mon, 28 Sep 2026 17:15:20 -0400 Subject: [PATCH 4/7] task: Docs + task number + history scrub Assisted-by: CodeAF Co-Authored-By: CodeAF <267109073+agentfield-bot@users.noreply.github.com> --- internal/manual/chat/worker-harness.md | 8 ++-- internal/tui3/planrail.go | 53 +++++++++++++++++++++----- internal/tui3/planroom.go | 2 +- internal/tui3/task.go | 7 ++-- 4 files changed, 52 insertions(+), 18 deletions(-) diff --git a/internal/manual/chat/worker-harness.md b/internal/manual/chat/worker-harness.md index 05189b6365..d008a2704d 100644 --- a/internal/manual/chat/worker-harness.md +++ b/internal/manual/chat/worker-harness.md @@ -254,10 +254,10 @@ A dependency never changes that family. `pending` means admitted and not started the row stays under the task that requested it and wears `queued · waits: ` to name the separate dependency. -A task's **page** shows its children under its steps the same way, in `under it`, -each drawn as the side list draws a task — mark, name, `#id`, the tree's `├ `/`└ ` -— with the command it is on while its worker is on one. Notes, pause, cancel and the -rest of steering are unchanged by the tree. +The tree's `├ `/`└ ` rails are the run's plan rows; a live worker's own +children on its page are drawn in `under it` as the side list draws a task — mark, +name, `#id` — with the command it is on while its worker is on one, no rails under +them. Notes, pause, cancel and the rest of steering are unchanged by the tree. ## What step is a run task on? diff --git a/internal/tui3/planrail.go b/internal/tui3/planrail.go index c4a7793b34..cc5fffc7f7 100644 --- a/internal/tui3/planrail.go +++ b/internal/tui3/planrail.go @@ -230,6 +230,25 @@ func planTwigsOf(rows []session.PlanTaskRow) []*planTwig { return out } +// railLeadFloor is the least room a family row may leave for its title after +// the lead: two cells, one glyph and the space that follows it. A row with less +// than that has stopped being a row that names anything, and drawing only the +// bare connector would hand the reader a `├ ` that says "here is a row" over an +// empty name. +const railLeadFloor = 2 + +// railLevels is how many levels of family a column this wide may hang before a +// deeper path stops eating the cells the name needs. Two cells to a level, so +// the budget is the room a title keeps after a level draws itself; a path past +// it hangs a level up ([app.railLead] takes the count from its callers). +func railLevels(width int) int { + levels := (width - railTitleFloor) / 2 + if levels < 1 { + levels = 1 + } + return levels +} + // railLead is the lead cells a family row hangs by: two a level, drawn as the // tree the manual documents — `├ ` off the parent while a sibling follows the // row, `└ ` where the row closes its family, `│ ` down every level above it @@ -283,16 +302,24 @@ func (a *app) railLead(last []bool, elbow bool) string { // // Every line a part draws carries its store id, which is what makes it a door // onto that task's page ([app.openRailPlan]). -func (a *app) planRailLines(kids []*planTwig, last []bool, width int) []railLine { +func (a *app) planRailLines(kids []*planTwig, last []bool, width, levels int) []railLine { var out []railLine for i, kid := range kids { at := make([]bool, len(last), len(last)+1) copy(at, last) at = append(at, i == len(kids)-1) - lead := a.railLead(at, true) - text := a.railEntryRow(railEntry{node: planRailNode(kid.row)}, max(width-ansi.StringWidth(lead), 0)) + hang := at + if len(hang) > levels { + hang = hang[len(hang)-levels:] + } + lead := a.railLead(hang, true) + room := max(width-ansi.StringWidth(lead), 0) + if room < railLeadFloor { + continue + } + text := a.railEntryRow(railEntry{node: planRailNode(kid.row)}, room) out = append(out, railLine{text: lead + text, entry: -1, plan: kid.row.ID, head: true}) - out = append(out, a.planRailLines(kid.kids, at, width)...) + out = append(out, a.planRailLines(kid.kids, hang, width, levels)...) } return out } @@ -304,21 +331,27 @@ func (a *app) planRailLines(kids []*planTwig, last []bool, width int) []railLine // once did beside the row. The page's own task stands in its head, so its // parts hang a level in here, the same connectors the rail draws and the same // trunk running through the under-block ([app.railLead]). -func (a *app) planPageLines(kids []*planTwig, last []bool, width int) []railLine { +func (a *app) planPageLines(kids []*planTwig, last []bool, width, levels int) []railLine { var out []railLine for i, kid := range kids { at := make([]bool, len(last), len(last)+1) copy(at, last) at = append(at, i == len(kids)-1) - lead := a.railLead(at, true) + hang := at + if len(hang) > levels { + hang = hang[len(hang)-levels:] + } + lead := a.railLead(hang, true) room := max(width-ansi.StringWidth(lead), 0) + if room < railLeadFloor { + continue + } node := planRailNode(kid.row) out = append(out, railLine{text: lead + a.railEntryRow(railEntry{node: node}, room), entry: -1, plan: kid.row.ID, head: true}) - hang := a.railLead(at, false) for _, under := range a.railUnder(node, max(room-4, 0)) { - out = append(out, railLine{text: hang + " " + under, entry: -1, plan: kid.row.ID}) + out = append(out, railLine{text: a.railLead(hang, false) + " " + under, entry: -1, plan: kid.row.ID}) } - out = append(out, a.planPageLines(kid.kids, at, width)...) + out = append(out, a.planPageLines(kid.kids, hang, width, levels)...) } return out } @@ -328,5 +361,5 @@ func (a *app) planPageLines(kids []*planTwig, last []bool, width int) []railLine func (a *app) planRailRoot(twig *planTwig, width int) []railLine { text := a.railEntryRow(railEntry{node: planRailNode(twig.row)}, width) out := []railLine{{text: text, entry: -1, plan: twig.row.ID, head: true}} - return append(out, a.planRailLines(twig.kids, nil, width)...) + return append(out, a.planRailLines(twig.kids, nil, width, railLevels(width))...) } diff --git a/internal/tui3/planroom.go b/internal/tui3/planroom.go index 8c56b08a87..c7d7dc775d 100644 --- a/internal/tui3/planroom.go +++ b/internal/tui3/planroom.go @@ -406,7 +406,7 @@ func (a *app) planRoomPartRows(width int) []row { } if len(plan.page.Children) > 0 { out = append(out, row{entry: -1}, row{text: a.pal.dim(fit(planRoomPartsWord, width)), entry: -1}) - for _, line := range a.planPageLines(planTwigsOf(plan.page.Children), nil, min(width, planPageKinWidth)) { + for _, line := range a.planPageLines(planTwigsOf(plan.page.Children), nil, min(width, planPageKinWidth), railLevels(width)) { out = append(out, row{text: line.text, entry: -1, plan: line.plan}) } } diff --git a/internal/tui3/task.go b/internal/tui3/task.go index f4b288bb6a..cf9d18ab61 100644 --- a/internal/tui3/task.go +++ b/internal/tui3/task.go @@ -3418,7 +3418,7 @@ func (a *app) railDrawnView(height int) ([]railLine, int) { } if node := carrier[run]; node != nil { if at, ok := visible[node]; ok { - lines := a.planRailLines(kids, nil, width) + lines := a.planRailLines(kids, nil, width, railLevels(width)) under[at] = append(under[at], lines...) markPlan(lines) drawnPlan[run] = true @@ -4309,8 +4309,9 @@ func (a *app) railEntryRow(e railEntry, width int) string { return "" } const indent = " " + fig := a.railFigWord(node) glyph := a.railTreeGlyph(node) - room := width - len(indent) - 2 + room := width - len(indent) - ansi.StringWidth(fig) - 2 // A program's badge stays on its row when the side column is compact. // The time gives up cells first, then the badge shortens, while the title // keeps enough room to name the work. @@ -4328,7 +4329,7 @@ func (a *app) railEntryRow(e railEntry, width int) string { age = "" } title, w := fitWidth(node.title, room) - line := indent + glyph + " " + a.railTitle(node, title) + a.pal.programAfter(wears) + line := indent + a.pal.dim(fig) + " " + glyph + " " + a.railTitle(node, title) + a.pal.programAfter(wears) if age != "" { line += strings.Repeat(" ", max(room-w, 0)+1) + a.pal.dim(age) } From d91600669fe917164fbf39e057a1f66ffe542343 Mon Sep 17 00:00:00 2001 From: agentfield-bot Date: Sat, 3 Oct 2026 19:19:16 -0400 Subject: [PATCH 5/7] tui3: land the rail figure word, name deep families at the boundary The branch's last commit called a railFigWord that was never written, so nothing at head built; and its own c266 pin expected the row shape that same commit had already changed. This reconciles the whole seam in one pass: - railFigWord lands in taskident.go: a row's number in the form a person can say out loud (#6, the stored row's handle or the node's counter), drawn dim before the state glyph. - The name has first claim on the cells: the number is drawn only where the column has room for it beside the whole name, and the glyph is measured and not assumed, so a held row's two-cell pair cannot overrun the column. - c266's pin now carries connector, number, mark and title together. - A family deeper than the column's levels is named at the boundary by one ellipsis row that is the door onto the branch, instead of drawn flat at the cap's depth or dropped with its subtree without a word; railRowPlace is the one place that is decided, shared by both renderers. - The plan page measures its level budget on the column it draws, min(width, planPageKinWidth), rather than on the page's own width. - The changelog says all of this; its railEntryRow-is-untouched claim no longer held. Resolves the read-only review's P0 (undefined railFigWord), P1 (row-shape pin contradiction) and both P2s (depth clamp flattening and hidden subtrees, plan page cap on the wrong width). Assisted-by: CodeAF (glm-5.3-flash-high) Co-Authored-By: CodeAF <267109073+agentfield-bot@users.noreply.github.com> --- .../unreleased/1665-tree-rails-side-column.md | 13 ++++- internal/tui3/c263_rail_plan_test.go | 53 +++++++++++++++++ internal/tui3/c266_rail_row_test.go | 9 ++- internal/tui3/planrail.go | 58 ++++++++++++------- internal/tui3/planroom.go | 8 ++- internal/tui3/task.go | 27 +++++++-- internal/tui3/taskident.go | 16 +++++ 7 files changed, 152 insertions(+), 32 deletions(-) diff --git a/docs/changes/unreleased/1665-tree-rails-side-column.md b/docs/changes/unreleased/1665-tree-rails-side-column.md index 5ff664dcf9..7f5b0b6f26 100644 --- a/docs/changes/unreleased/1665-tree-rails-side-column.md +++ b/docs/changes/unreleased/1665-tree-rails-side-column.md @@ -6,12 +6,19 @@ surface: [chat] invalidates: - "The tasks side column drew no rail lines: a run's parts hung bare two spaces a level (`strings.Repeat(\" \", depth)` in planrail.go), which is what #1494 left behind when it dropped the roster forest. A child now rides `├ ` while a sibling follows it and `└ ` where it closes its parent's family, a deeper row carries the `│ ` trunk past every ancestor that had rows still to come, and the last child's levels leave air — drawn in the lead the caller prepends, two cells a level, on the side column and on a task page's `under it` alike." - "Two manual pages spelled the task page's connectors `├─`/`└─` (reading-a-task-page.md, worker-harness.md); they spell the drawn `├ `/`└ ` now. The side column's node rows are still one flat line per task — #1494's roster shape stands; the connectors belong to the families hung under a row." + - "A family row opened with its state glyph and nothing else, so a family of cut titles could not be told apart by number. A row now opens with its task's number — `#6`, the handle the store names a stored row by, drawn dim — wherever the column has room for it beside the whole name, and the number yields its cells whole the moment keeping it would cut a name (taskident.go's [app.railFigWord])." + - "A family deeper than the column's levels drew every row past the cap at the cap's own depth — a child hung beside its parent — and a lead that left less than the floor dropped the row and its whole subtree without a word. One ellipsis row now hangs at the boundary where the levels run out and is the door onto the branch it names (planrail.go's [app.railBoundaryRow])." + - "A task page's `under it` measured its level budget on the page's own width while drawing its parts in a column at most [planPageKinWidth] cells wide, hanging families deeper than their titles could afford; the cap is now measured on the column it protects (planroom.go)." --- The column's families were bare indentation since #1494, so a run's shape did not read: a child's title floated right of its parent's with nothing anchoring it, while the manual (tasks.md) and the plandb CLI tree kept documenting and drawing connectors. The rail lives in the lead again — one helper, `railLead`, builds it -for both renderers out of the ancestors' last-child flags; `railEntryRow` is -untouched, the lead stays exactly two cells a level so every width computation that -reads it keeps its shape, and a dependency still never re-parents a row. \ No newline at end of file +for both renderers out of the ancestors' last-child flags, and `railRowPlace` is +the one place a row's place in the tree is decided; the lead stays exactly two +cells a level so every width computation that reads it keeps its shape, and a +dependency still never re-parents a row. A family row opens with its task's +number where the column has room for it beside the whole name, and a family +deeper than the column is named at the boundary by one ellipsis row that is +the door onto it. \ No newline at end of file diff --git a/internal/tui3/c263_rail_plan_test.go b/internal/tui3/c263_rail_plan_test.go index 3c88a4c5d3..2e509005ef 100644 --- a/internal/tui3/c263_rail_plan_test.go +++ b/internal/tui3/c263_rail_plan_test.go @@ -306,3 +306,56 @@ func TestAPartWithNoStepInFlightDrawsNoLiveLine(t *testing.T) { t.Fatalf("an empty command drew a live line %q", got) } } + +// A FAMILY DEEPER THAN THE COLUMN IS NAMED AT THE BOUNDARY, not drawn flat at +// the cap. The old clamp drew every row past the levels at the cap's own depth +// — a child hung beside its parent — and a lead that left less than the floor +// dropped the row and its whole subtree without a word. One ellipsis row now +// hangs where the levels run out, it is the door onto the branch it names, +// and every row the column does draw stands at its true depth. +func TestADeeperFamilyIsNamedAtTheBoundaryNotDrawnFlat(t *testing.T) { + rows := []session.PlanTaskRow{ + {ID: "t-1", Title: "One", Status: "claimed"}, + {ID: "t-2", Parent: "t-1", Title: "Two", Status: "claimed"}, + {ID: "t-3", Parent: "t-2", Title: "Three", Status: "claimed"}, + {ID: "t-4", Parent: "t-3", Title: "Four", Status: "claimed"}, + } + a, _ := planAppWith(t, rows, nil) + // A column sixteen cells wide hangs two levels; the third is past it, and + // the one ellipsis row that names the branch is the door onto it. + hung := a.planRailLines(planTwigsOf(rows), nil, 16, railLevels(16)) + text := func(lines []railLine) string { + out := make([]string, len(lines)) + for i, line := range lines { + out[i] = line.text + } + return strings.Join(out, "\n") + } + var sawOne, sawTwo, sawThree, sawFour, namedAtBoundary bool + for _, line := range hung { + switch { + case strings.Contains(line.text, "One"): + sawOne = true + case strings.Contains(line.text, "Two"): + sawTwo = true + case strings.Contains(line.text, "Three"): + sawThree = true + case strings.Contains(line.text, "Four"): + sawFour = true + case strings.Contains(line.text, a.pal.glyph(tokens.GEllipsis)): + namedAtBoundary = true + if line.plan != "t-3" { + t.Fatalf("the boundary row names %q, want the branch it hangs on (t-3):\n%s", line.plan, plain(text(hung))) + } + } + } + if sawThree || sawFour { + t.Fatalf("a row past the column's levels was drawn:\n%s", plain(text(hung))) + } + if !sawOne || !sawTwo { + t.Fatalf("the column dropped rows inside its own levels:\n%s", plain(text(hung))) + } + if !namedAtBoundary { + t.Fatalf("the column hid a deeper family without naming it at the boundary:\n%s", plain(text(hung))) + } +} diff --git a/internal/tui3/c266_rail_row_test.go b/internal/tui3/c266_rail_row_test.go index 712afc2159..bfd6c427ab 100644 --- a/internal/tui3/c266_rail_row_test.go +++ b/internal/tui3/c266_rail_row_test.go @@ -98,8 +98,13 @@ func TestTheRailIndentsATaskUnderItsParentTask(t *testing.T) { // '-') now fails here instead of passing the position check above. The // mark's glyph comes from the palette so its spelling is never assumed. pending := plain(a.pal.glyph(tokens.GQueued)) - if !strings.Contains(plain(child), plain("\u2514 "+pending+" ")+"write the fixtures") { - t.Fatalf("the task under a task does not wear its mark before its title:\n%s", child) + // The number the row opens with is its own handle — `#kid`, the fixture's + // id with the store's `t-` gone (the fixture spells bare ids, so the handle + // is the word itself). The pin carries connector, number, mark and title + // together: a regression that drops any one of them off the row now fails + // here instead of passing the position check above. + if !strings.Contains(plain(child), plain("\u2514 #kid "+pending+" ")+"write the fixtures") { + t.Fatalf("the task under a task does not wear its number and mark before its title:\n%s", child) } } diff --git a/internal/tui3/planrail.go b/internal/tui3/planrail.go index cc5fffc7f7..5b36b1f6f6 100644 --- a/internal/tui3/planrail.go +++ b/internal/tui3/planrail.go @@ -240,7 +240,8 @@ const railLeadFloor = 2 // railLevels is how many levels of family a column this wide may hang before a // deeper path stops eating the cells the name needs. Two cells to a level, so // the budget is the room a title keeps after a level draws itself; a path past -// it hangs a level up ([app.railLead] takes the count from its callers). +// it is NAMED AT THE BOUNDARY and not drawn ([app.railRowPlace]: one ellipsis +// row where the levels run out, never a row hung flat at the cap's depth). func railLevels(width int) int { levels := (width - railTitleFloor) / 2 if levels < 1 { @@ -292,6 +293,35 @@ func (a *app) railLead(last []bool, elbow bool) string { return out.String() } +// railRowPlace is one family row's place in the tree — the one mechanism both +// renderers hang their rows by: the flags of the levels above the row with its +// own last-child flag, the lead those flags draw, and the room the lead leaves +// a title. A row deeper than the column's levels, or one whose lead leaves +// less than [railLeadFloor] (the day a level draws wider than its two cells, +// [app.railLead]'s own warning), is PAST the column: the caller names the +// branch at the boundary with [app.railBoundaryRow] and draws nothing of it, +// so a deep family is never flattened to the cap's depth — a child hung beside +// its parent — and never hidden without a row that says it is there. +func (a *app) railRowPlace(last []bool, i, n, width, levels int) (hang []bool, lead string, room int, past bool) { + at := make([]bool, len(last), len(last)+1) + copy(at, last) + at = append(at, i == n-1) + hang = at + if len(hang) > levels { + hang = hang[len(hang)-levels:] + } + lead = a.railLead(hang, true) + room = max(width-ansi.StringWidth(lead), 0) + return hang, lead, room, len(at) > levels || room < railLeadFloor +} + +// railBoundaryRow is the one row that names a branch the column will not draw: +// the branch's own connector and the vocabulary's one ellipsis. It is the door +// onto the task it names, as every drawn line is ([app.openRailPlan]). +func (a *app) railBoundaryRow(lead, id string) railLine { + return railLine{text: lead + a.pal.dim(a.pal.glyph(tokens.GEllipsis)), entry: -1, plan: id, head: true} +} + // planRailLines draws a run's parts under a row, each one THROUGH THE NODE // RENDERER and one line each, as every task on the side column is (sidecol.go): // a level of family costs two cells, drawn as the tree's own connectors @@ -305,16 +335,9 @@ func (a *app) railLead(last []bool, elbow bool) string { func (a *app) planRailLines(kids []*planTwig, last []bool, width, levels int) []railLine { var out []railLine for i, kid := range kids { - at := make([]bool, len(last), len(last)+1) - copy(at, last) - at = append(at, i == len(kids)-1) - hang := at - if len(hang) > levels { - hang = hang[len(hang)-levels:] - } - lead := a.railLead(hang, true) - room := max(width-ansi.StringWidth(lead), 0) - if room < railLeadFloor { + hang, lead, room, past := a.railRowPlace(last, i, len(kids), width, levels) + if past { + out = append(out, a.railBoundaryRow(lead, kid.row.ID)) continue } text := a.railEntryRow(railEntry{node: planRailNode(kid.row)}, room) @@ -334,16 +357,9 @@ func (a *app) planRailLines(kids []*planTwig, last []bool, width, levels int) [] func (a *app) planPageLines(kids []*planTwig, last []bool, width, levels int) []railLine { var out []railLine for i, kid := range kids { - at := make([]bool, len(last), len(last)+1) - copy(at, last) - at = append(at, i == len(kids)-1) - hang := at - if len(hang) > levels { - hang = hang[len(hang)-levels:] - } - lead := a.railLead(hang, true) - room := max(width-ansi.StringWidth(lead), 0) - if room < railLeadFloor { + hang, lead, room, past := a.railRowPlace(last, i, len(kids), width, levels) + if past { + out = append(out, a.railBoundaryRow(lead, kid.row.ID)) continue } node := planRailNode(kid.row) diff --git a/internal/tui3/planroom.go b/internal/tui3/planroom.go index c7d7dc775d..d0480f3fa6 100644 --- a/internal/tui3/planroom.go +++ b/internal/tui3/planroom.go @@ -406,7 +406,13 @@ func (a *app) planRoomPartRows(width int) []row { } if len(plan.page.Children) > 0 { out = append(out, row{entry: -1}, row{text: a.pal.dim(fit(planRoomPartsWord, width)), entry: -1}) - for _, line := range a.planPageLines(planTwigsOf(plan.page.Children), nil, min(width, planPageKinWidth), railLevels(width)) { + // THE CAP IS MEASURED ON THE COLUMN IT PROTECTS: the page draws its + // parts in a column at most [planPageKinWidth] cells wide, and a level + // budget taken from the page's own width would believe in room the + // narrower column does not have — hanging families deeper than their + // titles can afford. + kin := min(width, planPageKinWidth) + for _, line := range a.planPageLines(planTwigsOf(plan.page.Children), nil, kin, railLevels(kin)) { out = append(out, row{text: line.text, entry: -1, plan: line.plan}) } } diff --git a/internal/tui3/task.go b/internal/tui3/task.go index cf9d18ab61..5c878220a1 100644 --- a/internal/tui3/task.go +++ b/internal/tui3/task.go @@ -4278,9 +4278,10 @@ func (a *app) railOffersResize() bool { // Queued 3 ▸ a folded group and its count // Done 7 ▾ the same group, opened // -// EVERY TASK ROW OPENS WITH ONE GLYPH AND IT IS THE STATE, two cells in under -// its heading, then the name the person approved, cut with an ellipsis, and -// how long the work has been at it (or took) in the muted ink at the right. +// EVERY TASK ROW OPENS WITH ITS NUMBER WHERE THE COLUMN HAS ROOM FOR IT BESIDE +// THE WHOLE NAME, then one glyph and it is the state, two cells in under its +// heading, then the name the person approved, cut with an ellipsis, and how +// long the work has been at it (or took) in the muted ink at the right. // The whole name, and what a row used to say under it, are the hint line's // ([app.sideHoverWords]). // @@ -4311,7 +4312,19 @@ func (a *app) railEntryRow(e railEntry, width int) string { const indent = " " fig := a.railFigWord(node) glyph := a.railTreeGlyph(node) - room := width - len(indent) - ansi.StringWidth(fig) - 2 + // THE NAME HAS FIRST CLAIM ON THE CELLS, and the task's number yields + // them whole: it is furniture, drawn where the column has room for it + // beside the WHOLE name and dropped where keeping it would be the reason a + // name is cut — the same law the age and the badge answer from the other + // side, giving up their cells while the title keeps its floor. The glyph is + // measured and not assumed, because a held row's pair of cells is two wide + // and a room that counted one would let the row overrun the column. + room := width - len(indent) - ansi.StringWidth(glyph) - 2 + if room-ansi.StringWidth(fig)-1 < ansi.StringWidth(node.title) { + fig = "" + } else { + room -= ansi.StringWidth(fig) + 1 + } // A program's badge stays on its row when the side column is compact. // The time gives up cells first, then the badge shortens, while the title // keeps enough room to name the work. @@ -4329,7 +4342,11 @@ func (a *app) railEntryRow(e railEntry, width int) string { age = "" } title, w := fitWidth(node.title, room) - line := indent + a.pal.dim(fig) + " " + glyph + " " + a.railTitle(node, title) + a.pal.programAfter(wears) + line := indent + if fig != "" { + line += a.pal.dim(fig) + " " + } + line += glyph + " " + a.railTitle(node, title) + a.pal.programAfter(wears) if age != "" { line += strings.Repeat(" ", max(room-w, 0)+1) + a.pal.dim(age) } diff --git a/internal/tui3/taskident.go b/internal/tui3/taskident.go index cb7805ed02..53b5e174fa 100644 --- a/internal/tui3/taskident.go +++ b/internal/tui3/taskident.go @@ -176,6 +176,22 @@ func wholeName(text string) string { // noticed. func taskIDWord(id uint64) string { return "task " + itoa(int(id)) } +// railFigWord is the row's number in the form a person can say out loud — `#6` — +// drawn dim before the state glyph on the rail's family rows +// ([app.railEntryRow]) wherever the column has room for it beside the whole +// name. The handle is a stored row's own `#id` ([taskNode.handle], the `t-` the +// store spells its ids with already dropped), and a node the engine named +// carries its counter instead — the same word either way, because the number +// is the one thing every row has that never changes while the work runs. It +// is furniture by the marker's own argument, and it is ASCII already, so no +// tier respells it. +func (a *app) railFigWord(node *taskNode) string { + if handle := strings.TrimSpace(node.handle); handle != "" { + return "#" + handle + } + return "#" + itoa(int(node.id)) +} + // taskSubtitleOf is the one line under a name: the first sentence of what the // node was asked to do, capped. // From ce1bb3fad42a57f7efce63fbbb59ace3a5336a6d Mon Sep 17 00:00:00 2001 From: agentfield-bot Date: Sat, 3 Oct 2026 19:21:49 -0400 Subject: [PATCH 6/7] tui3: unland the rail number, keep the boundary row and the page cap The first pass at this reconciliation landed the task-number word the branch's last commit had called for. It cannot ship: the column's settled laws hold that one state glyph leads every row in the same number of cells (railclick_test.go: the column reads downward as one column of states) and that the roster's rows carry no id (railgroups_test.go), and the branch's own c266 pin held the same shape. A number that varies in width cannot lead rows that must read as one column; weakening those assertions to admit it is out of bounds. The number is unlanded and returned to its author for a surface that can hold it. What stands is the rest of the reconciliation: - The undefined railFigWord call is gone with the fig integration; the build heals the way the laws say it must, and the pinned row shapes hold as committed. - A family deeper than the column's levels is named at the boundary by one ellipsis row that is the door onto the branch (railBoundaryRow), instead of drawn flat at the cap's depth or dropped with its subtree without a word; railRowPlace is the one place a row's place is decided, shared by both renderers. - The plan page measures its level budget on the column it draws, min(width, planPageKinWidth), not on the page's own width. - New law test TestADeeperFamilyIsNamedAtTheBoundaryNotDrawnFlat pins the boundary contract. - The changelog records all of it, including the unlanded number and why. Assisted-by: CodeAF (glm-5.3-flash-high) Co-Authored-By: CodeAF <267109073+agentfield-bot@users.noreply.github.com> --- .../unreleased/1665-tree-rails-side-column.md | 11 ++++---- internal/tui3/c266_rail_row_test.go | 9 ++---- internal/tui3/task.go | 28 ++++--------------- internal/tui3/taskident.go | 16 ----------- 4 files changed, 13 insertions(+), 51 deletions(-) diff --git a/docs/changes/unreleased/1665-tree-rails-side-column.md b/docs/changes/unreleased/1665-tree-rails-side-column.md index 7f5b0b6f26..8bba889d2f 100644 --- a/docs/changes/unreleased/1665-tree-rails-side-column.md +++ b/docs/changes/unreleased/1665-tree-rails-side-column.md @@ -6,9 +6,9 @@ surface: [chat] invalidates: - "The tasks side column drew no rail lines: a run's parts hung bare two spaces a level (`strings.Repeat(\" \", depth)` in planrail.go), which is what #1494 left behind when it dropped the roster forest. A child now rides `├ ` while a sibling follows it and `└ ` where it closes its parent's family, a deeper row carries the `│ ` trunk past every ancestor that had rows still to come, and the last child's levels leave air — drawn in the lead the caller prepends, two cells a level, on the side column and on a task page's `under it` alike." - "Two manual pages spelled the task page's connectors `├─`/`└─` (reading-a-task-page.md, worker-harness.md); they spell the drawn `├ `/`└ ` now. The side column's node rows are still one flat line per task — #1494's roster shape stands; the connectors belong to the families hung under a row." - - "A family row opened with its state glyph and nothing else, so a family of cut titles could not be told apart by number. A row now opens with its task's number — `#6`, the handle the store names a stored row by, drawn dim — wherever the column has room for it beside the whole name, and the number yields its cells whole the moment keeping it would cut a name (taskident.go's [app.railFigWord])." - "A family deeper than the column's levels drew every row past the cap at the cap's own depth — a child hung beside its parent — and a lead that left less than the floor dropped the row and its whole subtree without a word. One ellipsis row now hangs at the boundary where the levels run out and is the door onto the branch it names (planrail.go's [app.railBoundaryRow])." - "A task page's `under it` measured its level budget on the page's own width while drawing its parts in a column at most [planPageKinWidth] cells wide, hanging families deeper than their titles could afford; the cap is now measured on the column it protects (planroom.go)." + - "The branch's last commit attempted to open every side-column row with its task's number — a railFigWord call that was never written, so nothing at head built, and a shape that contradicts the column's settled laws: one state glyph leads every row in the same number of cells (railclick_test.go), the roster's rows carry no id (railgroups_test.go), and the branch's own c266 pin held the same. A number that varies in width cannot lead rows that must read downward as one column of states; the idea is unlanded and returns to its author for a surface that can hold it." --- The column's families were bare indentation since #1494, so a run's shape did not @@ -18,7 +18,8 @@ connectors. The rail lives in the lead again — one helper, `railLead`, builds for both renderers out of the ancestors' last-child flags, and `railRowPlace` is the one place a row's place in the tree is decided; the lead stays exactly two cells a level so every width computation that reads it keeps its shape, and a -dependency still never re-parents a row. A family row opens with its task's -number where the column has room for it beside the whole name, and a family -deeper than the column is named at the boundary by one ellipsis row that is -the door onto it. \ No newline at end of file +dependency still never re-parents a row. A family deeper than the column is +named at the boundary by one ellipsis row that is the door onto it, and the +branch's attempt to open every row with its task's number is unlanded — it +cannot hold the column's one-glyph-same-cells law, and is returned to its +author. \ No newline at end of file diff --git a/internal/tui3/c266_rail_row_test.go b/internal/tui3/c266_rail_row_test.go index bfd6c427ab..712afc2159 100644 --- a/internal/tui3/c266_rail_row_test.go +++ b/internal/tui3/c266_rail_row_test.go @@ -98,13 +98,8 @@ func TestTheRailIndentsATaskUnderItsParentTask(t *testing.T) { // '-') now fails here instead of passing the position check above. The // mark's glyph comes from the palette so its spelling is never assumed. pending := plain(a.pal.glyph(tokens.GQueued)) - // The number the row opens with is its own handle — `#kid`, the fixture's - // id with the store's `t-` gone (the fixture spells bare ids, so the handle - // is the word itself). The pin carries connector, number, mark and title - // together: a regression that drops any one of them off the row now fails - // here instead of passing the position check above. - if !strings.Contains(plain(child), plain("\u2514 #kid "+pending+" ")+"write the fixtures") { - t.Fatalf("the task under a task does not wear its number and mark before its title:\n%s", child) + if !strings.Contains(plain(child), plain("\u2514 "+pending+" ")+"write the fixtures") { + t.Fatalf("the task under a task does not wear its mark before its title:\n%s", child) } } diff --git a/internal/tui3/task.go b/internal/tui3/task.go index 5c878220a1..196a2c41a0 100644 --- a/internal/tui3/task.go +++ b/internal/tui3/task.go @@ -4278,10 +4278,9 @@ func (a *app) railOffersResize() bool { // Queued 3 ▸ a folded group and its count // Done 7 ▾ the same group, opened // -// EVERY TASK ROW OPENS WITH ITS NUMBER WHERE THE COLUMN HAS ROOM FOR IT BESIDE -// THE WHOLE NAME, then one glyph and it is the state, two cells in under its -// heading, then the name the person approved, cut with an ellipsis, and how -// long the work has been at it (or took) in the muted ink at the right. +// EVERY TASK ROW OPENS WITH ONE GLYPH AND IT IS THE STATE, two cells in under +// its heading, then the name the person approved, cut with an ellipsis, and +// how long the work has been at it (or took) in the muted ink at the right. // The whole name, and what a row used to say under it, are the hint line's // ([app.sideHoverWords]). // @@ -4310,21 +4309,8 @@ func (a *app) railEntryRow(e railEntry, width int) string { return "" } const indent = " " - fig := a.railFigWord(node) glyph := a.railTreeGlyph(node) - // THE NAME HAS FIRST CLAIM ON THE CELLS, and the task's number yields - // them whole: it is furniture, drawn where the column has room for it - // beside the WHOLE name and dropped where keeping it would be the reason a - // name is cut — the same law the age and the badge answer from the other - // side, giving up their cells while the title keeps its floor. The glyph is - // measured and not assumed, because a held row's pair of cells is two wide - // and a room that counted one would let the row overrun the column. - room := width - len(indent) - ansi.StringWidth(glyph) - 2 - if room-ansi.StringWidth(fig)-1 < ansi.StringWidth(node.title) { - fig = "" - } else { - room -= ansi.StringWidth(fig) + 1 - } + room := width - len(indent) - 2 // A program's badge stays on its row when the side column is compact. // The time gives up cells first, then the badge shortens, while the title // keeps enough room to name the work. @@ -4342,11 +4328,7 @@ func (a *app) railEntryRow(e railEntry, width int) string { age = "" } title, w := fitWidth(node.title, room) - line := indent - if fig != "" { - line += a.pal.dim(fig) + " " - } - line += glyph + " " + a.railTitle(node, title) + a.pal.programAfter(wears) + line := indent + glyph + " " + a.railTitle(node, title) + a.pal.programAfter(wears) if age != "" { line += strings.Repeat(" ", max(room-w, 0)+1) + a.pal.dim(age) } diff --git a/internal/tui3/taskident.go b/internal/tui3/taskident.go index 53b5e174fa..cb7805ed02 100644 --- a/internal/tui3/taskident.go +++ b/internal/tui3/taskident.go @@ -176,22 +176,6 @@ func wholeName(text string) string { // noticed. func taskIDWord(id uint64) string { return "task " + itoa(int(id)) } -// railFigWord is the row's number in the form a person can say out loud — `#6` — -// drawn dim before the state glyph on the rail's family rows -// ([app.railEntryRow]) wherever the column has room for it beside the whole -// name. The handle is a stored row's own `#id` ([taskNode.handle], the `t-` the -// store spells its ids with already dropped), and a node the engine named -// carries its counter instead — the same word either way, because the number -// is the one thing every row has that never changes while the work runs. It -// is furniture by the marker's own argument, and it is ASCII already, so no -// tier respells it. -func (a *app) railFigWord(node *taskNode) string { - if handle := strings.TrimSpace(node.handle); handle != "" { - return "#" + handle - } - return "#" + itoa(int(node.id)) -} - // taskSubtitleOf is the one line under a name: the first sentence of what the // node was asked to do, capped. // From f730ad341e9091cd393b6198113252b6eebe6699 Mon Sep 17 00:00:00 2001 From: agentfield-bot Date: Sat, 3 Oct 2026 19:28:10 -0400 Subject: [PATCH 7/7] docs(media/1665): the tree rails running in the live side column MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Captured from the real TUI on the Spark at this head: the seeded auth-flow family draws its connectors in the tasks side column — the nested poem family with its trunk lines, the closing corner on the last child, the running spinners, and names cut clean by the row fitter. Recorded as an asciicast of the live pane and rendered with agg; no simulated frames. Assisted-by: CodeAF (glm-5.3-flash-high) Co-Authored-By: CodeAF <267109073+agentfield-bot@users.noreply.github.com> --- docs/media/1665/tree-rails.gif | Bin 0 -> 79898 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/media/1665/tree-rails.gif diff --git a/docs/media/1665/tree-rails.gif b/docs/media/1665/tree-rails.gif new file mode 100644 index 0000000000000000000000000000000000000000..4efd2e94446ef13e296508f27cf34c9783843943 GIT binary patch literal 79898 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%0AVq@K^h?dHZ@Lh`SxOi8jnc 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