Skip to content
Merged
50 changes: 50 additions & 0 deletions cmd/aforge-demo-home/seed_work.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,56 @@ var demoTasks = []demoTask{
DurationMS: 7 * 60 * 1000,
},
},
// AND A GROUP OF LANDINGS NOBODY HAS CHECKED, ACROSS THREE PROJECTS. Home's
// `needs you` panel holds them as its `to check` group (homepanel_needs.go),
// and a fixture with ONE of them draws a group of one — which is the shape a
// group line, a count, a clause and a fold all fail to be tested by. These
// are deliberately unlike each other: one wrote seventeen files, one wrote
// none, one has a name long enough to be cut, and one is old enough to age
// onto the fold rather than onto a row.
{
project: firstProjectName, talk: "Reading the Ledger", ago: 26 * time.Hour,
entry: session.TaskIndexEntry{
ID: "1", Title: "Take the fortnight's spend apart by model", Status: string(session.TaskUnverified),
Outcome: "Opus is four fifths of the fortnight and nearly all of it is one conversation; the split is in the page.",
Files: []string{"docs/design/spend/fortnight.md"},
FilesChanged: 1, Cost: 0.19, Model: "anthropic/claude-sonnet-4", Tokens: 24_400,
DurationMS: 5 * 60 * 1000,
},
},
{
project: firstProjectName, talk: "The Manual's Missing Page", ago: 30 * time.Hour,
entry: session.TaskIndexEntry{
ID: "1", Title: "Write the page about reading a task, and every question it should answer",
Status: string(session.TaskUnverified),
Outcome: "Seventeen sections, each under two thousand characters, and every probe in the table reaches one of them.",
Files: []string{
"internal/manual/chat/reading-a-task-page.md", "internal/manual/chat/tasks.md",
"internal/manual/chat/home.md", "internal/manual/chat/keys.md",
},
FilesChanged: 17, Cost: 0.61, Model: "anthropic/claude-opus-4.1", Tokens: 88_200,
DurationMS: 19 * 60 * 1000,
},
},
{
project: "pricing-site", talk: "Pricing Research", ago: 7 * time.Hour,
entry: session.TaskIndexEntry{
ID: "2", Title: "Read what the four nearest competitors charge", Status: string(session.TaskUnverified),
Outcome: "Three of the four price per seat and one per workspace; nobody could check the figures against a source.",
Cost: 0.08, Model: "anthropic/claude-sonnet-4", Tokens: 11_300,
DurationMS: 3 * 60 * 1000,
},
},
{
project: "infra", talk: "Rotating the Deploy Key", ago: 4 * 24 * time.Hour,
entry: session.TaskIndexEntry{
ID: "1", Title: "Rotate the deploy key and re-fasten the runners", Status: string(session.TaskUnverified),
Outcome: "The key is rotated and both runners took it; the third host was not reachable to prove.",
Files: []string{"terraform/runners.tf", "runbooks/deploy-key.md"},
FilesChanged: 2, Cost: 0.24, Model: "anthropic/claude-sonnet-4", Tokens: 31_100,
DurationMS: 8 * 60 * 1000,
},
},
}

// writeTaskIndex appends every row to its own project's index and answers how
Expand Down
27 changes: 27 additions & 0 deletions docs/changes/unreleased/900-home-needs-groups.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
kind: changed
title: home's `needs you` is two groups — the questions that stopped something, then what to check
pr: 900
surface: [chat]
invalidates:
- "`needs you` drew a conversation's question, a watch and a landed task as ONE flat list ordered oldest-first. It is TWO GROUPS on one panel: the live questions first (a conversation stopped on a question or a consent card, a watch that needs somebody), longest wait first, and under them a dim group line `to check · N` with the clause `finished, nobody has checked it` at its right, holding every task whose call is the person's, NEWEST first. A live question is always above a landing, however old the landing is."
- "Every landing on home carried the sub-line `landed unchecked · enter to look` (`needsUncheckedWord` in `internal/tui3/placeprose.go`). That constant is DELETED and no row says it. A landing is ONE line — its name and `N files · age` — and only the row under the cursor grows a second line, which is the report's first sentence with the task's own two answers at its right."
- "A landing on `needs you` wore the amber `?` mark. It wears NONE. The mark means a thing has stopped and will not move until somebody answers it, and a landing has already finished; law 8's `two marks` is unchanged and the `?` is now only on a stopped conversation or watch."
- "The heading `needs you · N` counted every row including the landings. It counts the QUESTIONS only; the landings are counted on the group's own line. A frame holding only landings draws `needs you` with no number at all, which is the emptiness law applied to a heading."
- "A digit on home answered THE TOP question of `needs you`, always. It answers THE ONE ROW OF THE FRAME THAT IS DRAWING ITS ANSWERS: the row under the cursor when that row can take one, and the top answerable row otherwise. Drawing and routing read the same function (`app.homeAnswerAt`), so the chip on the screen and the key a person presses can never be two different rows."
- "A landed task could not be answered from home at all — `homeRowAnswers` gated the chips on a live session's presence question, which a `to check` row by construction does not have. A landing on `to check` now draws its own two answers in the TASK'S OWN WORDS (`session.TaskAsk.Yes`/`.No`) and takes them. THE KEYS ON HOME ARE `1` AND `2`, not the `[a]`/`[n]` the landing card, the task's room and its record take: a bare letter on home always types (the box promises `type to search or start something new` and the promise has no asterisk), and the drawn digit is the one printable exception that screen makes. The digit is mapped back to `session.LandingYesKey`/`LandingNoKey` before the answer leaves the surface, so the engine sees the same answer it has always seen."
- "A conversation whose presence says `waiting on you · your call on <title>` with no question object was drawn as a question on `needs you`, above the `to check` row for the same piece of work — one landing on two rows of one panel. `internal/session`'s `Agent.waitingOnPerson` reads a pending decision last and has no question object to write for it, so this is the ordinary shape of any live conversation sitting on a landed task. The conversation's row is dropped and the landing's row stands: it has the work's own name, its files and its answers."
- "`since you left` listed every landing since the look stamp, including the ones `needs you` was showing as your call. It DROPS a landing that the `to check` group is drawing, and shows it again as an ordinary line once that landing has been answered or has aged out of the group."
- "The pulse's `N want you` counted conversations, watches and errands and never a landed task, so it disagreed with `needs you · N` under it. It is the SUM OF BOTH GROUPS now, counted through the panel's own reading of a landing (`needsCallOf`, `needsAged`) rather than through a second test. The tab bar's `tasks` number is unrelated and unchanged — `placeCounts.ChangedIn` is `how many things moved since you last looked at this place`, and that seam is still nil."
- "A panel squeezed by taking rows off the bottom until it reached its floor, and there was no such thing as a group inside a panel. `homePanelRows.group` is a run of rows at the FOOT of a panel's list drawn under a dim line of its own; because they are last, the ordinary bottom-up cut spends them first, and when none is left the group's line goes and the panel's own fold names the group — `4 to check · 1 older · tasks`. `homeGridPanel.shrink` is now a plain walk down from the natural height rather than the old `least - 2` arithmetic."
- "The demo home (`cmd/aforge-demo-home`) had exactly ONE `unverified` row, which draws a `to check` group of one. It has five, across three projects, deliberately unlike each other: seventeen files, no files at all, a name long enough to be cut, and one old enough to age onto the fold instead of onto a row."
---

The note on the panel was that the difference between `needs you` and `to check` is not
obvious, and the fix is one dim clause on the group's line rather than a label on every row.
Seven words, once, in the place a heading's clause already lives — and the cursor row's
`1 accept 2 not right` says what checking one actually means.

Two things the tmux drive found that unit tests had not: the same landing drawn on two rows
of one panel whenever its conversation is live, and that `a`/`n` cannot be answer keys on a
screen whose foot promises that typing starts a conversation.
22 changes: 19 additions & 3 deletions docs/design/home-mission-control/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,29 @@ own keys, each a door to the place that owns it. The chat list is one panel.
5. **A short terminal squeezes in priority order.** `next up` and `spend` give
way first, then `since you left`, then `running`; `needs you` and `where you
were` shrink last. A squeezed panel keeps its heading and `N more · <place>`.
A GROUP INSIDE A PANEL folds before the panel gives up a row above it: the
rows of `needs you`'s `to check` group sit at the foot of the panel's list, so
the ordinary bottom-up cut spends them first, and when none is left the
group's line goes and the fold names it — `8 to check · tasks` (#884).
6. **Preselect the previous thing.** On double-space the cursor is on the chat
you were in before this one (this window's own stack, `chattabs.go`
`tabList`). Enter is a switch in two keys; esc goes back.
7. **Digits answer the top question from anywhere on home.** No cursor move.
The answers are drawn on the row so the key is never a guess.
7. **One row of the frame draws its answers, and the key answers it from
anywhere on home.** No cursor move. That row is the one under the cursor when
it can take an answer and the top answerable row otherwise (#884), so a digit
works on a frame nobody has walked and a landing somebody HAS walked onto is
the row the key means. The answers are drawn on the row so the key is never a
guess. THE WORDS ARE THE QUESTION'S OWN EVERYWHERE; the KEYS are too wherever
the question's own keys are digits, which is every card a conversation stops
on. The one exception is a landing in `to check`, whose keys are `[a]`/`[n]`
on its card, in its room and on its record and are `1`/`2` here — because a
bare letter on home types (home.go: the foot promises "type to search or start
something new" and the promise has no asterisk), and the drawn digit is the
one printable exception this screen makes. The digit is mapped back to
`session.LandingYesKey`/`LandingNoKey` before the answer leaves the surface.
8. **Two marks, one accent, one hint line.** `GlyphNeedsHuman` in `hueWarn` for
a person, the single spinner cell for work. Nothing else wears a glyph or a
a person — a conversation or a watch that has STOPPED, never a landing on
`to check`, which has already finished — the single spinner cell for work. Nothing else wears a glyph or a
colour. Money is a number. Key hints live on the foot and the alt map, never
inside a panel.
9. **Nothing grows past its budget.** Every panel folds inside itself with
Expand Down
Loading
Loading