Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 129 additions & 0 deletions docs/changes/unreleased/923-turn-decides-promotion-inherits.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
---
kind: changed
title: The turn decides whether to become a task, and a task it starts inherits what it already read
pr: 923
surface: [chat, engine]
invalidates:
- "A REPLY WAS MOVED TO A TASK AT ROUND TEN. The checkpoint's first two marks
each woke a separate model to read the transcript, and a `split` from that
reader ENDED the turn and handed the work to a worker. Neither mark reads
anybody now and neither moves anything: they put one note into the running
turn at its next step — `[taking stock] 10 rounds so far, 9 files opened,
121 KB of results in front of you.` plus the three roads on — and the model that is holding
the context decides. `checkpointSplitNote`, the `split` decision and the
seam mark are gone; `checkpointNotes` is the count of rungs that tell, and
`checkpointMarks` is still where the last one stands."
- "THE CEILING WAS A COUNT OF ROUNDS. It is a runaway net, and `turnHasRunAway`
reads ONE fact: the turn's context has passed the room it can still work in
(`compactThresholdOf` of its own window, the build's own line for how far a
conversation may grow). The other roads to the same ceiling were already
written and are not repeated in it — the loop watch past its nudge ceiling
(`handOverLoopingTurn`), an unattended session's wall (`turnwall.go`, asked
ahead of this ladder) and a believed completion claim disproved by
`checkpointPrice` more rounds of real work (`checkpointMeter.askAgainAt`).
No new number anywhere. A turn that reads ten distinct files in forty-eight
seconds is told twice and moved never, which is the measured failure this
closes."
- "A TASK STARTED OUT OF A RUNNING TURN OPENED ON A BRIEF ABOUT THAT TURN.
It was handed a written brief plus a `CALLS THAT HAVE ALREADY RUN` list of
pointers at the caller's own results, and it re-read them: measured at 551k
input tokens, 24% cached, 393 seconds, no item ticked. A promoted worker is
now a read-only FORK of the caller's transcript — the caller's messages and
tool results verbatim, with the brief as its opening message — admitted
through the same door as the tool. The stub-pointer section is not written
for an inherited worker at all (`admissionContext` still serves every other
kind), and `quickFromDrawing`'s bare-spec path is gone."
- "`quick_task` had no way to be handed the conversation that started it. It
takes `inherit` (a boolean, off by default), described in the four words the
prefix budget can afford: `Opens on this conversation`. The refusal is a
result the model reads, it names the window it did not fit, and it says to
hand the work out with a brief instead. There is one constructor, not two —
`newQuickSpec` gained a field."
- "`quick_task`'s DESCRIPTION SAID THE ROAD LAW THAT THE PAGE ALSO SAYS. `If you
will read the result and carry on, it is quick. If it must be checked and
merged on its own, or survive the window closing, it is a task.` is
`beltfacts.go`'s `What must be checked and landed on its own, or must survive
you, is a task; what you will read and carry on with is quick`, word for word
in two places and paid for on every request of every turn. The description's
copy is deleted: the page renders wherever this verb does
(`Config.mayQuickTask` puts both verbs on a belt or neither), and what is left
on the tool is what only the tool says — the two roads in one sentence, the
step that is no hand-off at all, and the grain."
- "AND WHAT `inherit` DOES IS A VERB LAW, NOT A PAGE LAW. It rode
`beltfacts.go`'s handoff paragraph for a wave — one sentence naming the
field — and a page sentence is paid for by every request of every turn
whether or not the belt carries the tool it is about. It is registered at
`quick_task` now (`lawregistry_test.go`, class `verb`), which is the class
the diet's own taxonomy already had for a fact about one field of one tool."
- "THE `inherit` BOUND IS READ AGAINST THE WORKER'S WINDOW, NOT THE
CONVERSATION'S. The two are routinely different — a `task.model` pin or the
crew's worker seat hands tasks to a cheaper model than the person is talking
to — so the model is settled by the resolver BEFORE the spec is built, and
`newQuickSpec` takes it and writes it onto the node. The quick door used to
resolve the model after building the spec and assign it afterwards; that
order is reversed and the assignment happens once."
- "A NET THAT FIRES ONCE HAS TO SAY SO. The net measures a condition rather
than a boundary and a full context stays full, and three of
`handOverRunningTurn`'s endings LEAVE THE TURN RUNNING — no brief came back,
the conversation is holding work of its own, the reader says nothing is left.
Without a latch each of those was followed by a fresh mastermind call and an
unconditional cut of the next step, every round until the turn ended.
`checkpointMeter.netFiredAt` records the firing and `netRested` holds the net
off for `checkpointPrice` rounds of real work — one predicate for all four
endings, in the unit the meter already used for a disproved claim."
- "THE BRIEF ROAD SURVIVES ONLY WHERE INHERITANCE CANNOT GO — the rung that
fired BECAUSE the context is too big to carry. There the brief now carries
the compiled results themselves, under `WHAT THIS WORK ALREADY FOUND OUT`,
rather than pointers at calls the worker would have to run again."
- "`forkSeed` was deleted with #811's 1,368 lines when the fork left the belt.
It is back in `fork.go`, restored rather than rewritten, and it is what
`inherit` is built on: there is one read-only fork of a transcript in this
package and promotion uses it."
- "THE LINE A MARK USED TO WRITE IS NOT PRINTED ANY MORE. `this has parts ·
handing it to a task that can take them side by side` was the mark's own
notice; a mark writes nothing at all now, so the two pages quoting it as a
live line were describing an event that cannot happen. The two lines a move
can still write are the ceiling's `this is running long · moving it to a task
that is watched and can split` and the carry-on's `this is running long ·
carrying on here, in this folder, with everything already read` — the second
of which replaces `this has parts · a quick task is taking them here`,
because what the line has to say now is what the worker took with it."
- "The manual said an answer that runs long is read and moved at round ten,
that `markreader` is asked three times, and that the reading decides whether
the step stops. `chat/tasks.md` now has `An answer that runs long is told, and
decides for itself` and `When a reply is taken out of your hands`, plus a
section on keeping what was already read; `how-tasks-run.md` says which tasks
inherit a transcript and which get a brief; `models-and-cost.md` says
`markreader` is asked at most once during an answer, on a reply that can no
longer work where it is; and `screen.md`'s `taking stock` row says the same,
beside a new section separating that phase word from the `[taking stock]`
line the model is handed."
---

The decision was being made by the wrong party, on the wrong signal, and the move
threw away the thing that was worth keeping. A summary reader on a small account
cannot know whether a turn is nearly done; a count of rounds says nothing about
whether the work is going anywhere; and a worker that opens on a brief about your
reading starts by doing your reading again.

So the count buys a sentence instead of a decision. The model holding the context
is the only party that knows whether it is two files from an answer, and the note
gives it the three facts it cannot see about itself and the three roads on, with
the cost stated once where the choice is. Nothing about it goes into the prefix:
it arrives through the one ambient door a loop nudge already uses, at the next
step boundary, and it never starts a turn.

What is left of the old ceiling is a net rather than a schedule. It asks whether
this turn has run away — no room left to work in, a loop already nudged, a wall
already spent — and every bound is derived from something the build had already
measured, so there is no new number to tune and no case where a productive turn
meets one.

The prefix comes DOWN. Measured against `dev` at 84016ad09, on a machine with ripgrep:
the lean prefix goes 31,341 → 31,267 bytes and the fixed 39,073 → 39,000, against
budgets of 31,500 and 48,000. The `inherit` property costs 72 bytes and the road law
deleted from `quick_task`'s description gives back 145. On a machine WITHOUT ripgrep —
which is what CI is, and `grep`'s description is longer there — the same pair reads
31,448 → 31,375 and 39,180 → 39,107: the same 73 bytes back, in the environment the
gate measures. The note itself is in neither figure: it rides the event, at the
boundary where it matters, and costs a turn that never reaches a rung nothing at all.
2 changes: 1 addition & 1 deletion internal/manual/chat/adaptive-runs.md
Original file line number Diff line number Diff line change
Expand Up @@ -1130,7 +1130,7 @@ the run goes on to deliver, be reviewed, and land like any other — **with the
checklist it already read off your request**, so a job that fell back here is still held to
the behaviours you stated rather than only to how its answer reads.

## When the run says a brief could not be written — a plan already drawn is kept
## When the run says a brief could not be written — the run said the brief could not be written, what happened, a plan already drawn is kept

You only see that line when **no plan was drawn at all**. Planning is seven or eight model
calls, some of them one per node, and a fault in a late one is not a reason to throw away
Expand Down
21 changes: 12 additions & 9 deletions internal/manual/chat/how-tasks-run.md
Original file line number Diff line number Diff line change
Expand Up @@ -614,9 +614,12 @@ A task is the same agent you talk to, with the same tools, in a quieter place.

It inherits the conversation's provider client, context window, image support, roles
source, search provider and fetcher, **connected accounts**, image-generation model and
document engine. It inherits **not the transcript** — one assembled brief is its whole
world, and your own message is the first part of it (below, under *What the task actually
reads*). The
document engine. **The transcript it inherits only when it was promoted** — a quick task
started with `inherit`, or a reply the ceiling carried on — and that one opens holding
this conversation exactly as it stands, every result in it, reading none of it again
(*tasks*, under *Can it keep what it read*). Every other task inherits **not the
transcript**: one assembled brief is its whole world, and your own message is the first
part of it (below, under *What the task actually reads*). The
one thing it is given of what aforge remembers about you is the handful of lines its own
brief needs: the conversation asks the router once, against that brief, and puts the
answer at the top of the task's instructions (what-i-remember). The task itself never
Expand Down Expand Up @@ -1110,9 +1113,9 @@ request, the line shows its identity mark and name alone rather than an empty qu
If several tasks arrive before one answer, their lines are stacked in arrival order above
that answer.

**That reply is priced exactly like one you typed.** It climbs the same three points, has the
same ceiling, and is handed to a task the same way — see *An answer that runs long is read and
moved* in *Tasks*. It used to be exempt, and a measured run had one such reply grind for 46
**That reply is priced exactly like one you typed.** It climbs the same ladder, has the
same ceiling, and is handed to a task the same way — see *When a reply is taken out of your
hands* in *Tasks*. It used to be exempt, and a measured run had one such reply grind for 46
minutes with nobody watching and then leave the session idle for seven and a half hours.

## Which task is this answer about
Expand Down Expand Up @@ -1811,9 +1814,9 @@ working task from a hung one.
**asked to hand parts of it out**, and is waiting while a model on the thinking tier reads
the parts and decides whether, and how.

**A new task never waits on it.** When work is moved out of a reply that had parts in it
(`this has parts · handing it to a task that can take them side by side`), the parts
somebody drew are read **beside** the task's worker, which starts at once on the whole
**A new task never waits on it.** When a reply is moved onto a task because it could no
longer work where it was (`this is running long · moving it to a task that is watched and
can split`), the parts drawn on the way out are read **beside** the task's worker, which starts at once on the whole
brief — so a task that has just appeared shows its worker's own calls, not this word. When
that reading answers: parts are handed out as workers of their own, each from the task's
copy of the folder as it stood at that moment, and the task gathers their reports (the
Expand Down
21 changes: 12 additions & 9 deletions internal/manual/chat/models-and-cost.md
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ under the class answering it, saying which model comes out. As shipped:
| `planner` | mastermind | the plan that steers an adaptive run |
| `designer` | mastermind | writes and reviews a harness page |
| `routerconfirm` | mastermind | a second look before work starts itself |
| `markreader` | mastermind | what is left of a long answer, and whether it has parts |
| `markreader` | mastermind | what is left of an answer that is being taken out of your hands, drawn as parts |
| `handoff` | mastermind | the instruction a handed-over turn gives whoever finishes it |
| `division` | mastermind | the parts a worker hands its own work out in |

Expand All @@ -396,14 +396,17 @@ about to be spent; `division` reads a task's parts before any of them exists, an
every part ever takes runs on the brief it leaves behind.

`markreader` and `handoff` are the two calls a long answer makes (*Tasks*). `markreader` is
asked at most three times, and only on an answer that has already spent ten rounds of tool
calls, plus once at the end of any answer that touched a tool at all — it reads the account of
the work and says what is left of your question. **It runs beside the work rather than
stopping it**: the next step of the answer goes out immediately and the reading happens
alongside it, so a reading that says "carry on" — which is nearly all of them — costs you
nothing at all. A reading that says the work has independent parts in it stops the step where
it stands and hands the answer over. It used to be awaited, and a measured one held the work
for 8.1 seconds to decide nothing. `handoff` writes the instruction the task
asked **at most once during an answer** — only when that answer can no longer work where it
is: its context full, the loop watch already spent, or the wall run out — plus once at the end
of any answer that touched a tool at all. It reads the account of the work and says what is
left of your question. **It runs beside the work rather than stopping it**: the next step of
the answer goes out immediately and the reading happens alongside it. What it draws is the
list the work carries on with; the step it lands beside is stopped either way, because the
decision to stop was taken before it was asked. It used to be awaited, and a measured one held
the work for 8.1 seconds to decide nothing. **A long answer no longer buys one of these
every ten rounds**: the two earlier moments cost no call at all now — aforge tells the model
what its answer has run up and the model decides for itself (*Tasks*, under *An answer that
runs long is told*). `handoff` writes the instruction the task
opens on when an answer is handed over. Both sit on mastermind for the same measured reason:
a cheap model asked "is this finished" answered `(done)` about half-finished work 15 times out
of 18, and that is the one answer that quietly drops a handover you were owed. There is no
Expand Down
28 changes: 27 additions & 1 deletion internal/manual/chat/screen.md
Original file line number Diff line number Diff line change
Expand Up @@ -3316,12 +3316,38 @@ same clock:
| `running <tool>` | one call on the belt is executing |
| `checking` | a reader is deciding whether the answer finished the ask, or whether it should have been work. The two are asked **at the same time** rather than one after the other, so this stage lasts as long as the slower of them and not both |
| `checking whether this is safe to run` | the safety stand-in is being asked whether one command is plainly safe, before you are asked about it. This is **the one reading that really does come first** — it decides whether the command runs at all — and it answers in ten seconds or not at all |
| `taking stock` | a second model is being shown an account of the work so far and asked what is left of what you asked for. **The work does not stop for it**: the next step goes out straight away and the reading happens alongside it. If the reading says what is left has independent parts in it, the step is stopped where it stands and the answer is handed over |
| `taking stock` | a second model is being shown an account of the work so far and asked what is left of what you asked for. **The work does not stop for it**: the next step goes out straight away and the reading happens alongside it. It happens **once**, and only on a reply that can no longer work where it is — its context full, the loop watch already spent, the wall run out, or a reply that said it had finished and then worked on for another ten rounds. The step it lands beside is stopped whatever the reading says; what the reading supplies is the list the work carries on with (*Tasks*, under *When a reply is taken out of your hands*) |
| `tidying` | the conversation is being compacted |
| `briefing a worker` | your turn is being handed to a task, and the instruction it opens on is being written — fifteen to thirty seconds is normal (see *How tasks run*) |

Each of them is taken off the screen the moment the wait ends.

## Two things are called taking stock — the word on the status line and the `[taking stock]` line the model gets

**They are different moments, and only one of them asks a model anything.**

**`taking stock` in the phase words** is a wait, in the table above. A second model is being
shown an account of the work and asked to draw what is left of your question. It happens
**once in an answer**, at the moment that answer is being taken out of your hands — its
context full, the loop watch spent, the wall run out, or a claim that it had finished
disproved by ten more rounds of real work (*Tasks*, under *When a reply is taken out of your
hands*).

**`[taking stock]` at the head of a line is aforge talking to the model writing your
answer**, and nobody is asked anything at all. After ten finished rounds of tool calls, and
again after twenty, a note rides into the next request the answer was going to make anyway,
carrying what this answer has run up and the roads on:

```
[taking stock] 10 rounds so far, 9 files opened, 116 KB of results in front of you. Three roads on, and the choice is yours: answer now from what you have; carry the rest on in a room, with `quick_task` and `inherit` set, which opens on this transcript as you are holding it; or hand out the parts you have not read yet. A worker that does not inherit opens on a brief about your work instead of on your work, and reads it all again. Nothing has been decided for you and nothing has been moved.
```

The brackets are the harness's own voice — the family `[stuck]` and `[silent]` belong to
(*Keys*) — so the model can tell it from something you typed. **It is not drawn on your
screen**: no phase word, no line in your transcript, no task started, nothing moved. What
the answer does about it is the answer's own choice (*Tasks*, under *An answer that runs long
is told, and decides for itself*).

## What happens when I send a message — does anything run before the model is asked, what runs before my answer, and why the wait is only the model

**Nothing runs before the model is asked.** From the instant you press enter, the
Expand Down
Loading
Loading