One task page for every task: a run's task opens the real task room - #1485
Draft
santoshkumarradha wants to merge 881 commits into
Draft
santoshkumarradha wants to merge 881 commits into
santoshkumarradha wants to merge 881 commits into
Conversation
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ker seat) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ker seat) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…urns late (#1210) The root's worker marks its task done in the store before it returns. When the supervisor's pass saw the store done in that gap, Start ended the run with the right outcome and an empty result. Start now reads the stored root result when the worker's return is missing. A new test holds the return back past one pass, so the gap is every run of the test.
…y, and the landing's deadline fits a real call Deciding whether to refresh ran after every message and opened the plan store twice each time; it now reads the rows the task sheet already carries and the store read moves into the command. The landing waited half a second for a call that takes two to four, so the refresh could only ever succeed in a test; it waits six. The rail no longer draws the whole plan when its reading holds no rows (a fallback added for a fixture), and the landing fixture sets the belt itself instead of inheriting it from the shell that ran it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…he loop and at the landing Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…nced answer The page the model read was six lines; it now says who is asking, when to open a task, what an answer is, and what to do when the record does not hold it or the person is steering. The rows, notes and task bodies are bounded, the page is said once, and the answer is found by its braces. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The owner, 2026-09-18: a prompt is written so it is true in any setup. Two example lines spelled a Go file and one a Go glob; they name a path and a name pattern instead. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…one next thing (#1209) The answer section of the chat prompt is reworded, close to byte neutral, so a reply leads with the action, uses the fewest numbered steps for a sequence, says where a multi-step job stands, gives cost in minutes or hours, states an error as cause then fix, and ends on the one thing that is the person's to do next. Shaped after the i-have-adhd skill by Ayoub Ghriss (MIT); no text copied. Fixed prefix 55,253 of 55,280; lean prefix 47,042 of 47,055.
…tore from a frame Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
## Summary - #1418 stopped refusing a deliverable path by walking up until it found a directory, declining only the filesystem root. On macOS `/home` is a symlink to `/System/Volumes/Data/home`, which is a directory, so the path that prompted the change — `/home/santosh/src/af-dev2-probe/bin/codeaf` on another host — still resolved as a place on this machine and was still refused. GLM 5.3 caught this in review of #1418. - `placeOnThisMachine` now asks about the directory the path names and nothing above it. A real folder outside the ground is still refused by name. A path whose directory is not on this machine, including one that merely begins with a directory that is, is left alone. - The test now uses that `/home/...` path rather than a made-up prefix, so the macOS symlink case is what it pins. ## Test plan - [x] `go test ./internal/session/ -count=1 -timeout 180s -run 'TestAPathOnAnotherMachineDoesNotRefuseTheTask|TestATaskNamingAFolderItDoesNotStandInIsRefused|TestATaskNamingACommandInItsOwnDirectoryIsAdmitted|TestATaskWhoseAcceptanceCarriesAProseSlashIsAdmitted|TestNoRoadInTheTaskEngineHasMoreEndingsThanItsLedgerRow|TestTheRatchet'` - [ ] `go test ./internal/session/ -count=1 -timeout 12m` (the package takes about six minutes; the targeted run above is what changed) — Drafted with [CodeAF](https://agentfield.ai/github/codeaf?utm_source=github&utm_medium=pull_request&utm_campaign=drafted_with) · reviewed and owned by the author
… and work/ cleanup - Fix false-positive scope matching in contract.go and skillcatalog.go where path components like 'users' or 'home' matched every workspace. - Sanitize process environment in runSkillCheck to prevent credential leakage to candidate check.sh scripts. - Add deferred panic recovery in Supervisor.launch and BashWorker.Run to prevent process crashes and inFlight deadlocks. - Bound SKILL.md file reads to 64KB in skills.go. - Acquire write transaction via beginWrite() for telemetry update in SkillFactAccessors to prevent SQLITE_BUSY under immediate locking. - Untrack committed scratch triage files under work/ and add work/ to .gitignore. Assisted-by: CodeAF (local/gemini-3.8-flash-high) Co-Authored-By: CodeAF <267109073+agentfield-bot@users.noreply.github.com>
…s at a price (#1416) * do, run, session: tests for the do door's contract, lockfiles, the check seat and the fallback receipt These are written against the unfixed tree first: codeaf do committing the person's own work on the run engine, running unbounded without --yes-spend, dropping --db and --keep without a word; a belt landing leaving project lockfiles behind; CODEAF_CHECK_MODEL never reaching a chat's run; and an approved hand-off falling back to the older engine with an identical receipt. * do, run, session: codeaf do edits in place, commits nothing, and stops at a price The run engine is the road every codeaf do takes, and it swept the directory's whole status into a commit on the checked-out branch, the person's own uncommitted edits and untracked files included. It now keeps the contract --dir states and the older road keeps: edited in place, nothing committed. The folder is read before the run and after, and the files the run names are the ones it changed (session.RunTreeSnapshot). Without --yes-spend a run is bounded again: at the plan-price figure and at what is left of today's limit, whichever is nearer, ending with exit 3. --db is refused in words on this road, and --keep says where the run's store is. A belt landing no longer drops paths by a name project files share: the .lock suffix and the bench-results folder are gone, and what the harness writes is answered in one place (harnessWrote). CODEAF_CHECK_MODEL now seats a chat run's checks. An approved hand-off the run engine could not start says so on its receipt. The wave 6 change entry is rewritten to say what is true. * manual, do: the do pages are reachable for the questions they already answered, and the status check reads git's own columns * session: an in-place run's snapshot asks where the repository is through the one asker * changes: the do door's entry carries its pull request's number
* tui3: a run's rows and its page look like the old task rows A run's parts are drawn through the node renderer (railEntryRows) by lending each store row a node: the working spinner, the #id, the clock and price line, and every part its own row in the old tree, never a done fold. A node row that carries a run is drawn as the head of its family with the parts under it. A run's page wears the task room's head: the trail with esc back, and the facts rule led by the state's spinner. Steps are the room's shell rows, parts are the rail's rows, and an empty live command draws no line (the stray ◑ $). * changes: a run's rows and its page look like every other task (#1420) * changes: name the ground-lint entry for its PR (1421)
…road says so (#1417) * Tests first: one run per batch, no silent fallback, an honest orphan row The tests, and only the seams they need (a slow or failing copy cut, a waiting hook, the run's root handed to a belt worker, the name of the run binding). No behaviour changes in this commit; every new test is expected to fail on it. * One run per batch: approved hand-offs join one run, never race to one store Starting a run is now one critical section, from the look for a live run until the run is registered, and every other hand-off waits at its door and then joins the run as a child. A batch approved at one moment is one run with every hand-off in it. A run road that fails says "task N did not start: <reason>" on both the proposal door and the typed /task, reads as a failure, and starts nothing on the older engine. Only a missing run engine or plan place still takes the older road. A worker is bound to its run's root as well as its store path (PLANDB_RUN), and the plandb CLI refuses a store at that path whose root is another run's. A run row's room takes a message as a note on its task's page. A run row nothing drives answers that it can be stopped instead of "no task N in this session", and a stop settles it. * The failed-road test reads the success receipt's own words, not the word started * Change entry for one run per batch * The stop law names the start door by its new name; the row-message heading stops competing with how to stop a task * The stop of a row nothing drives is said where stopping a run is * The #1418 change entry carries its pull request, so the entries are well formed again * The #1418 entry's pr line, and the remote-path test names no real person's folder The pr line was left unstaged by the rename. The test spelled a real checkout under /home, which exists on the machine it was measured on, so it failed there.
… loop (#1427) * session: a read sweep the chat will not hand off is handed off by the loop Three prompt formulations taught the hand-off judgement and two model families recited every one without acting on it: bounded lookups stayed inline, and an explicit parallel instruction came back as sequential calls. The loop is the one place the reading's cost is a fact rather than an instruction, so the loop spends the hand-off itself. A turn that reaches its third distinct read-only target — read, grep, find, ls, the earlyTools set — is enumerating a set, and the remainder goes to one quick task whose distilled answer comes back as the batch's tool result. One file stays a step; two files stay a step; a mixed batch, a bash call, or a write breaks the run. Every failure — refusal, a held node, a waited-out budget, an empty answer — runs the batch inline exactly as before, and the sweep is disabled for the rest of the turn so a failure is paid once. The hook fires only where a hand-off can run: conversations, with an engine linked. Assisted-by: CodeAF (moonshotai/kimi-k3) Co-Authored-By: CodeAF <267109073+agentfield-bot@users.noreply.github.com> * fix(session): cancel abandoned quick sweep, preserve rest in mixed batch fallback, and compact keys - Fix double-execution of non-reader tools (e.g. bash, edits) during fallback when a quick task fails or times out. - Fix event stream ordering: do not emit EventToolFinished or appendTook before confirming the hand-off succeeded. - Fix start timer wait: arm startTimer in select so governor-held nodes time out after 30s instead of blocking for 4m. - Clean up abandoned quick tasks with cancelTask so orphaned tasks do not consume resources. - Compact JSON arguments in sweepKey so whitespace variations do not register as distinct targets. - Use briefAskLimit for user question clipping in sweepBrief instead of arbitrary 400-char truncation. - Update phase clock with tellPhase during handoff and record noteCallOutcomes. Co-Authored-By: CodeAF <267109073+agentfield-bot@users.noreply.github.com> --------- Co-authored-by: agentfield-bot <agentfield-bot@users.noreply.github.com> Co-authored-by: CodeAF <267109073+agentfield-bot@users.noreply.github.com>
# Conflicts: # scripts/one-suite_test.sh
…nversation always moves, --max-hours ends the window (#1430) * engine, move, max-hours: tests first — an older engine is replaced, status names it, a held journal moves, the cap ends the window Red on the base: an older engine holding work is deferred to, --status does not exist, the engine's held-journal refusal reaches home as a bare string so move-it-here never asks, nothing names or stops a window that will not let go, an engine never honours a move request, and --max-hours leaves the window open. * engine --status, a named --stop, the older engine gives up the slot, a held conversation always moves, --max-hours ends the window Engine. `codeaf engine --status` / `--status-all` ask the socket which engine holds a workspace (pid, binary, build, start, windows) and stop nothing; `--stop` / `--stop-all` name the process they stopped. An engine from an OLDER build (by build moment, any file, or too old to answer) is replaced by `codeaf engine --daemon` and by every window's dial, busy or not, with one line naming it; a newer engine is joined and a tie never replaces, so two builds converge. Stop never signals its own process. Move. The engine's held-journal refusal reached home as a bare string, so enter on a conversation an in-process or older window held printed the instruction just followed and asked nothing; it now arrives as ErrSessionLocked and home asks the holder. An engine closes a conversation a window asked for (it had no surface to hear the request). After fifteen seconds unanswered the card names the holder (pid, tty, build, from its presence record) and enter offers to stop it with SIGTERM, a second time exiting at once. The request/presence files are frozen as a protocol between builds (session/holder.go) and pinned by byte tests. --max-hours. The wall stopped the work and left the window waiting for a person; it now leaves two minutes after the wall and insists thirty seconds later. * changes: the entry for #1430 * manual: the new sections do not pull a task question off its page, and three probes reach them The first words of the stop section ("a window stuck") and the time-limit section ("a test rig", "wait") moved 'is my task stuck while it waits for its test suite' off how-tasks-run. Reworded; probes added for engine --status, the window that will not let go and --max-hours closing the window. * changes: the #1430 title fits on one line
… what they are for (#1396) * skills: read Claude Code plugins, Codex system skills and linked folders; skills with memory off; the whole shelf in the catalog; /skill over the session host * skills: gofmt * skills: fixture names, the attachment door leaves the ledger, e2e helper name * skills: the plugin exclusion test checks its control first * skills: a plugin reads the skills its manifest or marketplace entry names * changes: the skills-from-other-tools entry * e2e, manual: the carried-skills line is 'skills ·' and folds into the work chip * e2e: the relevance eval counts a skill the run opened, one folder per request * tui3: the carried-skills line stays under the message, above the work chip * tui3: the no-shelf picker test reads the reason the clipped row keeps * e2e: the tide and ledger skills carry their answer, so the model does not go searching * session: the catalog asks for the skill before any other tool; the eval floor sits below the spread * skills: the attachment rides the facts photograph, and the picker asks its doors off the update loop * tui3: the chip press test runs the clearing door it is handed * tui3: the shelf read keeps its place in the door line
Brings #1415, #1424, #1425, #1334, #1422, #1388, #1389 and #1426 onto the integration branch (dev at debffab). Conflicted files and how each was settled: - internal/tui3/homeslash.go: the opens-a-conversation fate keeps dev2's /skill and dev's /manual and /folder (the union). dev's /project pin and the removal of /image and /ask stand. The fate-table and homeTrayCommand comments no longer name /image or the old /folder pin. - internal/manual/chat/home.md: dev's three rows for /attach, the commands that open a conversation and the ones that answer on Home, with dev2's /skill /skills added to the opens-a-conversation row so the table matches homeFate. - internal/tui3/notice_test.go: both sides appended tests; all are kept (dev2's unread and retired profile-key tests, dev's ranking and rotation tests). newNoticeBoard's third argument is "hints enabled", so the hints-off test still passes false. Not a textual conflict, but broken by the merge: - internal/manual/chat/compacting-over-and-over.md: the probe "what happened to the earlier messages" reached another page once dev2's pages and dev's respelled heading met. The heading carries the phrase again. Semantic collisions checked with no change needed: Escape and double-space against dev2's task page, rail, pickers and skill chips; one /skill row and alias; the tip table (22 rows, no priority column, the unread-keys line is not a tip and shows with hints disabled); ui.hints stays a read key beside dev2's task.parallel and retired-key rows; Home's working mark and the filtered Sessions list against dev2's stopped and interrupted runs; the attach tray carries files and skill chips side by side; the e2e word table; system.md; icons and banned words; change entries. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Found while merging dev into santos/dev2; every line was already stale on dev. - attaching-files.md still said a bare /attach on Home answers "type the path after /attach" and that /folder is Home's browser. A bare /attach opens the browser for the next conversation's folder; /project pins the folder; /folder opens a conversation first. - places.md said Home has no alt+<letter> keys. Home's foot draws alt+p, alt+e, alt+a and alt+k. The page also carried the "Where is the model filter in Settings" section twice, word for word; one copy is gone. - commands.go, hometip.go and notice.go comments still placed /attach relative to /image, drew /ask as Home's example tip, and called /ask a command "on its way out". Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
/skill is on Home's opens-a-conversation road, and the new conversation ran it through a dispatcher that rewrote the box to a bare "/skill ", so "/skills reviewer" typed on Home opened the picker with the query gone. The dispatcher now writes the command and its words back into the box, so the picker opens filtered on them, on Home and in a conversation alike. commands.md's table had no /skill row; it now names /skill, its /skills alias and what enter does on the shelf. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A task on the run engine and a task on the older engine must open the same task room with the same two tabs, and a line typed into a run task's room must reach its store as a note. Both fail on santos/dev2, where a run's task opens a separate store-backed page with no tabs.
A run's task opened a page of its own that looked like the task room and was not one. It now opens the real room, read through the run's store: - transcript tab: the brief, the trajectory's steps as the room's own shell calls, the notes, and the result - work tab: the run's working copy difference (session PlanTaskWork) - the box leaves a note on the store task (PlanNote), and x stops it through the plan's door after the stop card - the head reads the store's status, clock and spend, plus the model and tokens off the spend ledger, each left out when the ledger has none - the waits and the parts are drawn under the transcript in the rail's row shape, and a press on one opens that task's room Every room now carries the transcript and work tabs; an older-engine task's work tab lists the files it changed. The tasks place, the side list and the run's tab all open the room. The separate plan page and its keys are gone.
The live check showed an ended task's box saying 'this task has finished, say it to main' while enter sent a note the store then refused. The room now refuses before sending, with the words it already shows, and keeps the text in the box.
dev moved by one commit (f101ad2, #1440) while the first merge of the day was being pushed; this brings it in. Conflicted files and how each was settled: - cmd/codeaf/chatv3_process.go: v3Process keeps dev2's catalog ownership (catalogs, closed, lifetime, ownCatalog, keepCatalog, takeCatalogs) and gains #1440's creditWatcher inside the struct. The process builds the skill shelf, then installs the credit watcher; neither needs the other. closeAll cancels processCtx and then joins the watcher. - internal/manual/chat_test.go: dev2's six skills probes and #1440's five credits probes, side by side. Broken by the merge without a textual conflict: - internal/manual/chat/models-and-cost.md said a task on the shipped crew always runs on the paid worker model. While a known OpenRouter balance is low, an unwritten worker row now uses the free worker model; a row somebody chose keeps its model. Checked with no change: the free crew covers all five tiers, and every dev2 role (check, repair, belt worker, chat manager) reads one of them; a 402 is kept out of dev2's router retry, and the affordable-cap resend happens once; one low-credit warning path per frame beside dev2's chips, tip and keys rows; both launch roads wire ReadCredits once. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…and waited for #1440's balance reads at launch, on a key change and on a switch to a paid model run as Bubble Tea commands, and Bubble Tea does not wait for a command it started. On santos/dev2 the process's closeAll cancels and joins everything it owns (#1259, #1274), so a read could still be writing credits.json after the process that owns the profile had closed. tui3.Run now wraps ReadCredits in an owner that cancels every read in flight and waits for it before Run returns, and refuses a read that starts after close. Both local launch roads go through it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
An ended task's box names another door, so the scenario asserts the room's tabs and only logs the note box.
Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
One task page for every task
In a chat, a task on the run engine opened a page of its own. #1420 restyled it to look like the task room, but it was still a second page: no transcript or work tab, no steer box, no step rows, and no tokens or model. Now every task opens the real task room, on either engine. There is one page type.
What a run's task shows in the room
The room reads the run's task through the session layer:
refusedline. A call that did not run for any other reason has no row, but it still counts in the steps. Notes you left are drawn where corrections are drawn. Notes from a worker or the run name no author. The result comes last once the task has ended.session.PlanTaskWork, built onrunCopyOfand the run's notice copy.PlanNote). Once the store has it, the room saysthe worker reads a note at its next step. A refused note shows the store's own sentence on the line you typed. On a task that has ended, the room refuses the note before sending it, in the same words its box and foot already show (this task has finished), and keeps the text in the box.x: raises theStop this task?card and then stops the task through the plan's door. For the run's root, that is the run's stop.PlanTaskRowgainsModelandTokens, and the model shown is the one the task spent the most through.The tasks place, the side list and the run's tab all open this room. The separate plan page is removed, along with its
pkey and its direct part cancel.Tabs on every room
No room had tabs before, on either engine, so this PR adds them to every task room (not to the adaptive run's graph page):
transcriptandworkare named on the trail row beside the way back.tabover an empty box switches between them, and a click on a name does the same.Apart from the tabs, older-engine tasks are unchanged.
Kept from #1420
◑ $rows.Live.Step > 0and a command.Tests
internal/tui3/planroom_test.go:PlanTaskPage;readPlanWorkagainst a real git copy;esc.Tagged e2e
TaskOnTheRunEngineThe room assertions pass. The scenario still fails on this branch, but every remaining failure fails the same way on clean santos/dev2:
→ what ran under itnever appears;donebeforerunningcan be read;maininstead of the run's branch.On clean santos/dev2 the scenario also fails at the old page's own
steps · $wait.Live check (tmux, isolated home, telemetry off, deepseek-v4-flash)
A
/taskon the run engine, opened from the tasks place:transcript · worktabs.done 9s · 3 stepswithdeepseek/deepseek-v4-pro · 22.2k tok · $0.02.tabshows the three new files as a diff.On clean santos/dev2 the same press opens the old page, which has no tabs, no model and no tokens.
Manual
Updated
reading-a-task-page.md(new section on the two tabs),worker-harness.mdandtask-controls.md.