Repository navigation
Tasks: a task tree is bounded in depth, count and spend, and a worker does its own task - #2615
Merged
Merged
Conversation
…h, count and spend Carries #2446 (a4b3079 on workstreams/main) onto dev. The one conflict, in beltRunNotice, keeps dev's noticeResultWords and adds the per-task limit words to its report. Co-Authored-By: Claude Mythos 5.2 <[email protected]>
…er their own task, run subtasks keep their parent Carries #2452 (331433e on workstreams/main) onto dev. It applies cleanly on top of the carried #2446. Co-Authored-By: Claude Mythos 5.2 <[email protected]>
Carries #2461 (c5c8ea1 on workstreams/main) onto dev. It applies cleanly on top of the carried #2446 and #2452. Co-Authored-By: Claude Mythos 5.2 <[email protected]>
…cap question reaches tasks The carried text made the composer-cap section 3,300 characters and the question "how much money can a task spend before it stops and asks me" stopped reaching the tasks page. The tree limit now has its own headed section. Co-Authored-By: Claude Mythos 5.2 <[email protected]>
Co-Authored-By: Claude Mythos 5.2 <[email protected]>
AbirAbbas
marked this pull request as ready for review
November 12, 2026 19:38
A part a worker proposes runs on that worker and folds its bill into it when it lands, so the parent's spend already holds the part while the part's own row keeps its bill too. The tree total summed every node's spend, counting a child twice and a grandchild three times, and trees on CODEAF_TASK_BELT=node stopped at a fraction of their limit. The fold now marks the dollars it moved into the ledger worker, the mark travels with the worker's money into its node, and the tree total takes those folded dollars off each node's bill. The checkpoint keeps the folded part so a restored tree counts the same way. Co-Authored-By: Claude Opus 5.5 <[email protected]>
The whole-tree dollar ceiling on the run road read the profile's per-task cap alone. A raise the person gave one task (a proposal's or a rerun's raise_cap_usd, routed into the crew's guard, or a running task's live change) reached only the guard, so the engine still stopped the run at the old figure and its ending told the person to raise a limit they had just raised. The tree ceiling now starts from the higher of the profile's cap, the hand-off's raise and the crew guard's cap. The run spec carries a live ceiling the engine reads at every spend reading, and a live raise lifts it along with the guard. The guard's refusal sentence names the raised figure, and its per-task limit is read under its lock now that a raise can move it while calls are made. Co-Authored-By: Claude Opus 5.5 <[email protected]>
The per-task limit sentence was appended to whatever the run's ending already said. When the engine's tree ceiling cut a run before any seat's call was refused (the hand test: /crew cap task 0.002, then a /task), the note led with "ran and did not finish" and kept the sentence on a second line, and nothing recorded the crew's stop, so the row had no line under it and the Tasks column did not list the task. When the crew's guard refused the root's own call, the sentence was the worker's failure and was appended again, so the report said it twice. The ending now puts the sentence where a worker's failure stands, once, in the report and the outcome note, and a routed crew records it as its stop when nothing else did, which is what draws it under the row and lists the task as needing the person, as before the tree ceiling. Co-Authored-By: Claude Opus 5.5 <[email protected]>
The tasks page's "What a task is" said every task's brief carries the person's message word for word and a journal pointer, with a run-road proposal only putting its own work first. A proposed run's brief is its title, work, deliverable and done-condition and nothing of the conversation (proposalBrief), as how-tasks-run already says. The paragraph now tells the two apart, and a manual test holds it. Co-Authored-By: Claude Opus 5.5 <[email protected]>
`plandb task pivot` adds its replacement children before it cancels the subtree they replace, because cancelling a parent's last open child completes the parent and the add would then be refused. The new tree and worker gates read the doomed children as still open, so a worker that had added three parts and replaced them with three others was refused at its five, and a replacement keeping a replaced part's title was refused as a duplicate. The bounded add now takes the open tasks the pivot is about to cancel and leaves them out of the counts and the taken titles. Finished work the pivot keeps still counts. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The engine ends a run done with Limit cost when its final receipt crosses the tree's ceiling. The limit helpers fired on Limit cost alone, so such a finished run recorded the per-task limit as its crew's stop: the task sheet drew a limit warning, "run again with a stronger crew" was hidden and the routine completion was dropped. taskLimitWords now answers nothing for a run the engine ended done, as dev never stopped a crew for one. Co-Authored-By: Claude Opus 5.5 <[email protected]>
AbirAbbas
added a commit
that referenced
this pull request
Nov 13, 2026
Brings in #2613, #2612, #2614, #2618 and #2615. #2618 changes the visibility check this branch's job-events cursor reads: a spent GitHub rate limit returns GitHub's retry time instead of a cached denial, and is remembered per token until then. The two meet in repoAllowed, which this branch made answer (allow, known). A remembered limit answers false, false: a limit says nothing about the repository, so the job-events page ends before it as before any undecided one, as a fresh limit already does (askRepo's cache=false). Co-Authored-By: Claude Opus 5.5 <[email protected]>
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.
Summary
Four Tasks fixes merged only into
workstreams/main(#2446, #2452, #2461, #2464). Its integration pull request, #2306, was closed unmerged afterapp/v3was squashed ontodev(#2561), so none of them reacheddev. This carries them, one commit each, in their original order.What changes for a person:
plandb add,split,task insertandtask pivotrefuse a title the run already has, a task more than two levels below the run's root, and a run past 20 counted tasks. Every ordinary run has a dollar ceiling for its whole tree: the lower of the per-task limit (crew.task_cap, $5 by default) and what the conversation's limit leaves. Reaching it ends the run on its cost limit and names how to raise it.tasks.listkeeps a run subtask's parent. Stopping a run leaves nothing reading running.CODEAF_TASK_BELT=node, duplicate work is refused tree-wide and the tree has a spend total.The original pull requests hold the full validation contracts.
devstill has each faultBefore each commit, I ran that fix's own tests against the tree without its code change. Where a test needed a symbol the fix adds, I ran the tests that compile without it.
TestRunWorkerRefusesDuplicateTitles,TestRunWorkerTreeBoundsRefuseWholeBatches(ondev80b5861, with only the two limit constants supplied)duplicate was acceptedin all six cases;bound was not explained: exit 0for count, batch and depthTestStopRootCancelsOpenTasksEvenWhenRootAlreadyEnded,TestStoppedRunRejectsALateRootWorkersReview,TestDrainedWorkerReleasesItsClaimWithoutDiscardingRetainedWork,TestRetainedReadyWorkReadsQueuedOnlyAfterItsRunLeavesstop left open work,late return reopened or rewrote stopped workTestCrewFactoryGivesEachCheckItsOwnCeiling,TestCrewFactorySeparatesCheckEstimatesWithASharedDay,TestScopedCheckSpendEndsTheWholeRunAtTheTaskCapcheck-b call 0: the check stopped at its spend ceiling of $0.12TestNodeTree*(without the one test that reads the new checkpoint field)duplicate refusal missing "no: this run already has a task named",queued node bought a requestNothing was dropped: all four are carried whole.
What differs from the originals
beltRunNotice(internal/session/task_run_belt.go).devhad moved the result words intonoticeResultWords; that helper is kept and the per-task limit words are added to its report. Tasks: a proposed run reads only its assignment, workers add only under their own task, run subtasks keep their parent #2452, Tasks: each check in a run gets its own checker ceiling #2461 and Tasks: the older task road refuses duplicate work and holds a tree spend total #2464 applied cleanly on top.TestTheChatManualAnswersTheQuestionsPeopleAskfailed:"how much money can a task spend before it stops and asks me" should reach tasks; it reached [models-and-cost ...]. The tree-limit text now has its own section, "The per-task limit covers the whole task tree", and the composer section points to it. No test was changed.Tests
CI on this head (c7721f5) is green:
light gate,touched codeaf,touched session,touched tui3,touched rest,windows native testsand the requiredcheck.Locally on macOS,
make pr-readydid not pass as one command. The PR is marked ready on the strength of CI and the comparison withdevbelow:make test-quickmake test-packed-manual manual-gates./internal/plandb./internal/runTestDelegateForwarderReachesNote201fails; the runner reportsalready failing on the base./internal/session(8 shards)already failing on the base:TestACommandLookFailsOnAFailingPipeStage,TestWatchTickFailsOnAFailingPipeStage,TestBashBeltTruncationCapFollowsTheWindow,TestBashBeltTruncationWritesHeadTailAndPath,TestTheFixedPrefixStaysUnderItsBudget./internal/tui3(8 shards)Every selected test passed on its first run../cmd/codeaf(8 shards)The
cmd/codeafreds are 21 distinct tests across the two runs, in Factory, memory pause, hosted take-back, doctor and shared tools; none is a task test. Run by name:dev(ffeeb78) on this Mac:TestAppFactoryPlanFilesAnswer,TestDoReturnsAfterCancelledAccountingAndItsWriterStop,TestFactoryCommandStepRetryAfterOutOfAttempts,TestFactoryMeasuredOnlyPublication,TestFactoryPersonStopInFlightWakeAndPersonChat,TestFactoryRetryActionsDoNotReportStopped,TestFactoryStepRetryWaitsForPreviousLeaseCleanup,TestFactoryWriteUsesJobBaseAcrossAttempts,TestMemoryPauseResumeAfterTrailingProgress,TestMemoryPauseRetainsPendingForResume,TestMemoryPausedJobLifecycle,TestProcessCloseJoinsTheUsageWriter.ok ... 48.649s). They failed only inside the full runs, which shared the machine with other test suites (load average 26 to 39 during the first run).Each fix's own tests pass on this branch, along with the neighbouring spend-guard, crew, division and quick-task tests.
Changes after review
A pre-merge review and a hand test found defects that the carried commits brought along. They are fixed in six new commits (2ffcc3e to c245451, head now c245451), and two independent verifiers checked the fixes. Each test below failed on the previous head (c7721f5) and passes now.
crew.task_cap. Raising one task's limit through a proposal, a reroute or a live raise in/crewchanged nothing, so a task raised to $8 still stopped at $5. On a live raise, the guard's refusal also still named the old figure.Limits.CostCeiling) and the ceiling only ever rises.SpendGuard.raiseTaskCapupdates the figure and the refusal words together. A live raise also lifts the running tree.TestARaisedProposalRunsUnderItsRaisedTreeCeilingandTestALiveRaiseLiftsTheRunningTreeCeiling(internal/session);TestARaisedCostCeilingIsReadWhileTheRunGoes(internal/run).CODEAF_TASK_BELT=node), the tree total counted a finished child's spend twice.foldedUsd, so a restored tree also counts each dollar once.TestNodeTreeSpendCountsAPartFoldedIntoItsOwnerOnce(real nest harness, real folds at two levels).TestAPerTaskLimitEndingSaysItsSentenceOnceWhereThePersonLooks(internal/session, throughStartTaskto settle);TestAPerTaskLimitEndingIsSaidOnceUnderItsRowAndListed(internal/tui3).internal/manual/chat/tasks.mdsaid every brief carries the person's message and a journal pointer. A task proposed on the run road actually reads only its own assignment.TestTasksManualSaysAProposedRunReadsOnlyItsAssignment(internal/manual);make test-packed-manual manual-gatespasses.plandb task pivotcounted the tasks it was replacing. This finding was marked "evaluate" in review. It was real and the fix was small, so it is fixed here.TestWorkerPivotCountsOnlyTheWorkThatSurvivesIt(internal/plandb). A six-part pivot is still refused whole.Docs moved with the behaviour:
internal/manual/chat/tasks.md,how-tasks-run.mdandmodels-and-cost.md, plus a new manual test.2615-tasks-fixes-carried.md, which now lists these fixes.The wire protocol and the system prompt did not change. The new
RunSpec.CostCeilingandLimits.CostCeilingare internal Go fields.Earlier sections that are now out of date:
noticeResultWords' report. They lead it as the run's failure text.Left as is:
TestAClosedNodesDollarsAreStampedOnTheConversationfails on this Mac the same way on the new head, on c7721f5 and on unmodifieddev(ffeeb78):meta.json says the conversation spent 0 once its node closed, want the node's $0.75. This PR does not cause it, so it is not changed here.taskLimitWordsnow answers nothing for a run the engine ended done, as dev never stopped a crew for one. Proving test:TestAFinishedRunAtItsCeilingIsNoPerTaskLimitStop(failed with the crew stopped on "this task reached its $5 limit · raise it in /crew" before the change, passes after). Commit bd15299.🤖 Generated with Claude Code