Skip to content

chat: the /model menu's current model wears no band and the picker owns the wheel - #1693

Closed
ZeroPoint95 wants to merge 56 commits into
devfrom
task/fix-model-current-row-styling-an-60beed
Closed

ZeroPoint95 wants to merge 56 commits into
devfrom
task/fix-model-current-row-styling-an-60beed

Conversation

@ZeroPoint95

@ZeroPoint95 ZeroPoint95 commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

What

Two fixes to the /model menu, from the owner's Misc TODO:

The current model's row wears no band. The chosen row of the list drew
through the shared overlay renderer with the ladder's selected ground — a
full-width highlighted row wherever the cursor happened to be. The band on an
overlay is the cursor's language, so the menu's front mark now keeps its accent
and its weight and takes no ground: the cursor alone indicates the
highlighted row. The grammar is the picker's own (palette.frontUnlifted, set
by picker.rowsOwned and nowhere else); every other list that draws through the
shared renderer keeps the selected step for its front mark, and under the
cursor the chosen row still lifts like any other.

The picker owns the wheel while it is up. Every other overlay claimed the
wheel in tea.MouseWheelMsg's chain; the model picker had no branch, so a notch
fell all the way through to a.scroll and moved the transcript behind the
modal. It is claimed now, like every modal above it, walking the list's cursor —
which picker.move clamps at both ends, so neither the wheel nor the keys can
run past the last row or before the first.

Tests

New internal/tui3/modelmenu_test.go:

  • TestTheCurrentModelKeepsBoldAccentButNoGround — the marked row draws bold
    accent with no background run; the cursor's row carries the ground; the
    cursor on the marked row still lifts it.
  • TestTheModelPickerOwnsTheWheelAndClampsAtBothEnds — twenty notches down and
    up against a scrollable transcript: the cursor clamps at the ends, the list
    window stays inside the list, and a.offset never moves while the picker is
    open.
  • TestThePickerCursorStopsAtTheEndsOfTheList — thirty downs and ups plus page
    keys stop at the ends, and the walk still moves from the top.

TestTheWiderTiersAreByteIdenticalToTheOneLineLaw now paints its expectation
with the picker's front-mark grammar — the law it holds (rows draw through the
shared row function, byte for byte) is unchanged.
TestHoverReachesTheChoicesAndThePickerRows exempts the keyboard cursor's own
row from its "hovered too" check: since the mark takes no ground, the cursor's
row wears the cursor step by design.

Manual: the Scrolling section and the model picker's key sheet in keys.md
name the wheel, and models-and-cost.md states the mark's look.
docs/changes/unreleased/1693-model-menu-front-mark-and-wheel.md carries the
invalidations.

…ns the wheel

The chosen row of the /model list used to wear the ladder's selected
ground — a full-width band that read as a highlighted row wherever the
cursor happened to be. The band on an overlay is the cursor's language,
so the menu's front mark now keeps its accent and its weight and takes
no ground: the cursor alone lifts a row ([palette.frontUnlifted], the
picker's own grammar, said on its draw and nowhere else).

And a wheel notch with the list up used to fall all the way through to
the transcript and scroll words nobody can see move. The picker claims
the wheel like every modal above it, walking its cursor — which clamps
at both ends, so neither the wheel nor the keys run past the list.
@ZeroPoint95
ZeroPoint95 marked this pull request as draft September 30, 2026 18:07
@ZeroPoint95
ZeroPoint95 force-pushed the task/fix-model-current-row-styling-an-60beed branch from 9953867 to 269e2e5 Compare September 30, 2026 18:07
senior-dev and others added 21 commits September 30, 2026 14:52
…w is always drawn

The picker's scroll window was counted in list rows while the drawing spends
extra lines on a service's heading, the machines' heading and a why line, so
the cursor could sit inside the logical window and still fall past the rendered
bottom edge: the highlight vanished once the list bottomed out, and the menu
opened with no highlight anywhere because cursorToCurrent put it on the model
in use at the bottom of just such a window.

follow now counts screen lines (picker.rowLines, the same costs picker.height
reserves the frame with), and the menu's own door opens with the cursor visibly
on the list's first selectable row. The doors that open to confirm — a settings
slot, a role, a task's model word, home's draft, alt+o — keep opening ON the
row they hold, and ctrl+u still returns the emptied box to the model in use.
The rendered-ground assertion must use a color palette even when the runner has no terminal profile.

Assisted-by: CodeAF (gpt-6-sol)

Co-Authored-By: CodeAF <267109073+agentfield-bot@users.noreply.github.com>
@ZeroPoint95
ZeroPoint95 marked this pull request as ready for review September 30, 2026 19:39
@ZeroPoint95
ZeroPoint95 marked this pull request as draft September 30, 2026 19:55
ZeroPoint95 added a commit that referenced this pull request Sep 30, 2026


The two change entries were written for #1693, which this pull request
replaces with the same change on a clean history, so they are renamed to
the new number and their pr field says it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ZeroPoint95

Copy link
Copy Markdown
Contributor Author

Superseded by #1696 — same change, rebuilt on a clean branch without senior-dev's per-write wip commits.

AbirAbbas added a commit that referenced this pull request Oct 1, 2026
…ns the wheel (#1696)

* chat: the /model menu's current model wears no band and the picker owns the wheel

The chosen row of the /model list used to wear the ladder's selected
ground, a full-width band that read as a highlighted row wherever the
cursor happened to be. The band on an overlay is the cursor's language,
so the menu's front mark now keeps its accent and its weight and takes
no ground: the cursor alone lifts a row (palette.frontUnlifted, the
picker's own grammar, said on its draw and nowhere else). Every other
list that draws through the shared overlay renderer keeps the ladder's
selected step for its front mark.

A wheel notch with the list up used to fall all the way through to the
transcript and scroll words nobody can see move. The picker now claims
the wheel like every modal above it, walking its cursor, which clamps at
both ends so neither the wheel nor the keys run past the list.

The picker's scroll window was counted in list rows while the drawing
spends extra lines on a service's heading, the machines' heading and a
why line, so the cursor could sit inside the logical window and still
fall past the rendered bottom edge: the highlight vanished once the list
bottomed out, and the menu opened with no highlight anywhere because
cursorToCurrent put it on the model in use at the bottom of just such a
window. picker.follow now counts screen lines (picker.rowLines, the same
costs picker.height reserves the frame with), and the menu's own door
opens with the cursor visibly on the list's first selectable row. The
doors that open to confirm (a settings slot, a role, a task's model
word, home's draft, alt+o) keep opening ON the row they hold, and ctrl+u
still returns the emptied box to the model in use.

The fold tests that mean the model in use now walk to it explicitly, and
the rendered-ground assertion pins a color palette so it holds on a
headless runner with no terminal profile. The manual's key sheet,
Scrolling section and models page name the wheel, the first-row cursor
and the mark's look.

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(changes): the /model menu's entries carry their pull request, #1696

The two change entries were written for #1693, which this pull request
replaces with the same change on a clean history, so they are renamed to
the new number and their pr field says it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(chat): the manual says once where the /model cursor opens

models-and-cost.md still said the list opens on the model in use, in the
picker paragraph and in the sort-key section, while commands.md said /model
opens on the first row. Both now say what the code does: /model opens on the
first row, and the doors that open to confirm open on the model they hold.
Two comments that cited the old rule are corrected, and picker.follow says
why the keystroke-time callers may pass a row count as their line budget.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* tui3: /model opens on the held model; every model list owns the wheel

Opening /model on the first row made Enter switch models without a choice,
and a refreshed task list could replace the task's confirmation model.
Every model door now opens and refreshes on the model it holds, visibly on
screen. A missing held model falls back to the first selectable row.

The window still counts screen lines, including service and machine
headings and reason lines. Opening a fold keeps preceding context when
it fits and scrolls by the least otherwise, with the cursor kept visible.

Settings slot and role lists, home's draft list and the task composer's
list now own wheel gestures while open and leave the page beneath still.
The manual and change entries describe confirmation, refresh, wheel
ownership and the current model's bold accent without a band of its own.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: codeaf <agentfield-bot@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Abir Abbas <abirabbas1998@gmail.com>
AbirAbbas added a commit that referenced this pull request Oct 1, 2026
…er self-fixes (#1716)

* senior-dev: a run makes no wip commits; its work is codeaf's one commit

Every file senior-dev's model wrote or edited was committed on the run's
branch as it happened, `wip(write): <path>` / `wip(edit): <path>` under the
identity `senior-dev <senior-dev@localhost>`. It was a checkpoint from the
benchmark harness senior-dev was built in, and under codeaf it bought
nothing: the run works in a private worktree copy on a branch of its own,
its recorder snapshots through a temporary index and keeps its candidates
as commit-tree objects outside the branch, and codeaf commits whatever the
run leaves as one commit when it ends, crash or not. What it cost was
#1693 — fifty wip commits with senior-dev as an author, every one of which
the repository's squash merge would have written into dev's message.

The checkpoint is deleted rather than switched off: EagerCommit and its
opt-out SENIOR_DEV_EAGER_COMMIT, the recorders' CommitsOnWrite, and on
codeaf's side SENIOR_DEV_EXPECTED_BRANCH and DelegateSetup.Branch, which
existed only to fence those commits to the run's branch. The start-time
ignore and input lists stay, because the recorders read them.

A new test drives write and edit in a repository shaped the way codeaf
hands one over, task/ branch and old variable included, and fails on dev
with `senior-dev wip(write): added.txt`. The senior-dev and delegates
manual pages said the wip commits stay and nothing squashes them; they now
say there are none, and a probe asks why a pull request has them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* senior-dev: asked to leave its work uncommitted, and to write the message for codeaf's commit

A senior-dev run whose brief asked it to commit and push did exactly that:
its model ran `git -c user.name=codeaf … commit` and `git push` from its own
shell, and codeaf's finishing commit found nothing left to take. The owner
wants codeaf, not the program, to own the commit, and chose steering over a
shell that refuses git's writing verbs — parsing every road to git is a
guard that guesses.

So the line codeaf puts at the head of the brief now tells the program to
leave its work uncommitted and not to push, switch branches or rewrite
history, even where the brief below asks it to, and — when the program has
a notes folder of its own, as senior-dev does — to write the commit's
message to `.senior-dev/commit-message`: a subject of at most 72 characters
in the repository's own style, a blank line, and a body saying what changed
and why. codeaf reads that file before the notes leave the copy and makes
it the finishing commit's message, signed once (signOnce, so a line the
program already wrote is not repeated). With no message, or a blank one,
the commit is what it was: the task's title over the run's ending.

Nothing is blocked: a commit the program makes anyway stays on its branch.
senior-dev's own code is untouched. The chat's guide for senior-dev was
not lengthened, because the prompt-prefix budget refuses the bytes and the
brief's head line already overrides a brief that asks for commits.

The senior-dev page gains its own section on whether senior-dev commits or
pushes and who writes the message; delegates.md says the same in brief.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* senior-dev: on the turn its run ends, handing it back counts as fixing it yourself

The page the chat reads when a program's run ends told it to finish "a
small, clear gap" itself on the program's branch, in its own worktree,
and to fix or hand back a failure it saw in its own checks. A model reading
that did the follow-up work itself, in a worktree, where the program it had
just used was built for it — and that turn is the one moment a hand-off
carries on from the program's own branch, since the person's next message
starts any later run from their checkout again.

The page now says that wherever it tells the chat to fix or finish
something itself, handing the work back to the same program counts as doing
it, and is the road for anything beyond a line or two; the chat edits the
branch itself only for a trivial gap, or once the program can be sent back
no more. The failed and unverified bullets and the failed outcome's next
line say the same. The page is read on the wake turn only, so the fixed
prompt's byte budget is untouched. The two-hand-back cap is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(changes): senior-dev's clean commits and hand-backs, #1716

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(changes): the #1716 entry is valid YAML and its title fits one line

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* review: the design note dates the end of wip commits; PathIgnoredAtStart has its own test

From the review of #1716. The italic note under "Landing a tree delegate"
still said senior-dev's wip(edit) commits stay on its branch while the
decisions table above it said they ended on 2026-09-30; it now says so too.
And deleting the eager-commit tests took PathIgnoredAtStart's only direct
test with them; a table test makes its rule explicit again — the file
itself or anything under it as a folder, never a sibling sharing its prefix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* senior-dev: keep nudges uncommitted and measure new files

With eager commits gone a run's work is always uncommitted, so the
unsubmitted-work nudge and landing turn told every run's model to commit
before verifying, the opposite of its brief, and the patch summary diffed
the base against the working tree, which leaves out untracked files: a run
that wrote three files was shown as "measured its change 1 file · +28 -0",
and one that only added files as "its change is empty". The git-status
finding is gone (the candidate is frozen from the working tree through a
temporary index), and the summary measures the same snapshot tree the run
would hand in, keeping the working-tree diff only when that capture fails.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* session: a program's finishing commit is safe to read and truthful

The message a program writes for codeaf's finishing commit was read with a
plain open, so a named pipe there hung the finish with the copy held, a
symlink was followed, and a NUL byte made git refuse the commit; a message
of only credit lines or an unchanged copy the repository already tracked
was taken as this run's. It is now read only as a regular file, without
following links or blocking, and those cases fall back to the title and
ending.

A message written early also replaced the run's ending, so a stopped,
failed, limited or broken run's commit read as finished work. The message
is used alone only when the run passed; otherwise the ending follows it
before the credits, and a run codeaf found gone says so.

The brief now also asks the program not to stash, reset, clean, or check
out or restore files over its work, which no longer sits in HEAD. Once a
program can be sent back no more, the wake page agrees with its note: tell
the person what still does not work. Manual and change entry follow.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* session: a stale commit message is judged from where this run began

The check that a tracked commit message is not this run's own kept a
starting commit of its own (MessageBase), while #1705's ownBase already
answers where this run began, carried on or not. It reads ownBase, and the
extra persisted field is gone.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* manual: split the senior-dev and task-landing sections the merge overfilled

With #1705's text beside this branch's, the senior-dev sections on what
codeaf does when a run ends and where its work goes outgrew a retrievable
section, and the "codeaf committed to dev" probe stopped reaching its page.
The hand-back rule, the checks-passed-but-not-submitted ending and the
finishing commit's credit get headings of their own, taking the work home
is split from why a branch was kept, and an automatic hand-off is the rest
of this work, as the wake page says, not a next piece.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Abir Abbas <abirabbas1998@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants