Skip to content

The explorer is told who actually reads its report - #183

Merged
tobert merged 4 commits into
mainfrom
explore-names-its-reader
Sep 10, 2026
Merged

The explorer is told who actually reads its report#183
tobert merged 4 commits into
mainfrom
explore-names-its-reader

Conversation

@tobert

@tobert tobert commented Sep 8, 2026

Copy link
Copy Markdown
Owner

All three explorer-phase tools shared one system preamble that stated, six times, that a synthesis agent would write the final answer from the report. That is true for consult's explore′ sweep and for deliberate's dossier pass. It is false for standalone explore, whose report goes straight back to the calling agent — src/server/mod.rs even carried a comment saying so, right beside the call that sent the wrong framing.

This is the first of the prompt changes coming out of the survey in ~/exomemory/briefs/agent-prompt-survey/, and the one item there that needed no research to justify: a shipped tool was telling a model something untrue about its own situation.

The decision: name the reader, don't drop the framing

Who reads the report is what makes it a hand-off rather than an answer. An explorer that believes a rewrite is coming can reasonably leave a thread for the rewriter to pull — that is the correct behavior on a sweep. On standalone explore nobody pulls it, so the preamble now says the report is the finished deliverable.

ReportReader::{SynthesisAgent, CallingAgent} rides inside Phase::Explorer(ReportReader) rather than beside it. There is no way to name the explorer phase without deciding who reads it, so the reader cannot be forgotten at a call site — the compiler asked all four production sites, and each answers in a comment.

Only the opening and three phrases vary. ReaderWords declares them in one place and both_explorer_readers_share_one_body normalizes through that exact list, so a rewrite of the reading guidance cannot land on one tool and miss the other, and a fourth varying phrase someone forgets to declare shows up as a body mismatch rather than slipping past.

kaibo://prompts now lists the explorer phase twice, because it renders two different texts and a listing that showed one would misreport the other tool — the same class of bug this PR fixes.

Two bugs found by reading the output rather than the diff

Rendering both preambles and reading them whole caught what the diff hid.

A lowercase sentence. The substituted noun phrase opens a sentence at one of the four sites, producing "Keep the report focused and evidence-first. the synthesis agent trusts your citations". Fixed with a capitalized form, and no_sentence_in_an_explorer_preamble_opens_lowercase holds it for both readers.

A contradiction one sentence deep. "anything you leave out is missing from its answer" still implied the calling agent composes an answer from the report, immediately after the opening said the report is the answer. The consequence is now reader-aware: the caller's version reads "is missing from what it can act on".

Tests

Four, and both of the new text guards were run against the pre-fix behavior and fail there:

test the_calling_agent_is_never_promised_a_synthesis_agent ... FAILED
test no_sentence_in_an_explorer_preamble_opens_lowercase ... FAILED
  SynthesisAgent: a sentence opens on lowercase "the" here: ...trust the result.

the_reader_reaches_the_preamble_rig_forwards is the wiring pin: it runs explore_with with each reader against a scripted client and asserts on the preamble rig actually forwarded, so a hardcoded phase inside the function would fail rather than compile.

Review

kaibo reviewed itself — cast crusoe, GLM-5.2 synth over a Deepseek-V4-Flash explorer, no diff supplied. It confirmed the wiring complete across all five sites (it found the consult sweep's hardcoded SynthesisAgent, which the question hadn't named), confirmed no bypass path builds an explorer preamble outside Phase::Explorer(reader), and found the "its answer" seam above plus three stale docs describing [prompts].explorer as covering only the nested sweep. That staleness predates this branch — the key has always driven all three explorer tools — but the reader split makes it actively misleading, so all three are corrected, and docs/config.md now states what an override costs: it is a full replace, so it erases the reader distinction and must read correctly for both.

One residual, accepted

No test pins which reader each of the four production call sites passes. The type forces a choice and the comments document it, but that wiring is guarded by review. Closing it needs a seam that can observe a live call's preamble — a bigger change than this fix, and a natural thing to fold into the composition seam if we build one.

Next in the stack: consult and deliberate, once this shape is settled.

🤖 Generated with Claude Code

tobert and others added 3 commits September 10, 2026 10:29
All three explorer tools shared one preamble that stated, six times, that a
synthesis agent would write the final answer from the report. That is true for
`consult`'s `explore′` sweep and for `deliberate`'s dossier pass. It is false
for standalone `explore`, whose report goes straight back to the calling agent
— `server/mod.rs` even carried a comment saying so.

The decision: name the reader rather than drop the framing. Who reads the report
is what makes it a hand-off instead of an answer, and an explorer that believes
a rewrite is coming can reasonably leave a thread for the rewriter to pull. On
`explore` nobody pulls it, so the preamble now says the report is the finished
deliverable.

`ReportReader` rides inside `Phase::Explorer` rather than beside it, so the
reader cannot be forgotten: there is no way to name the explorer phase without
deciding who reads it, and the compiler asked all four production call sites.
Only the opening and one noun phrase vary — a test pins the rest of the body
byte-identical across readers, so a rewrite of the reading guidance cannot land
on one tool and miss the other.

`kaibo://prompts` now lists the explorer phase twice, because it renders two
different texts and a listing that showed one would misreport the other tool.

Reading the rendered output caught a second bug the diff hid: the substituted
noun phrase opens a sentence in one of the four sites, so it needed a
capitalized form. `no_sentence_in_an_explorer_preamble_opens_lowercase` holds
that now. Both new tests were run against the pre-fix text and fail there.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
kaibo's own review (cast `crusoe`, GLM-5.2 synth) read the two rendered
preambles and found the seam the first pass left: "anything you leave out is
missing from its answer" still implied the calling agent composes an answer
from the report, one sentence after the opening said the report IS the answer.
Mild, but it reintroduces exactly the framing this branch removes, so the
consequence is now reader-aware too — the caller's version reads "is missing
from what it can act on".

That made a third phrase vary, which the shared-body test would have policed by
hand. `ReaderWords` now declares every reader-varying phrase in one place, and
the test normalizes through that list instead of through literals: a fourth
varying phrase someone forgets to declare shows up as a body mismatch rather
than slipping past.

The same review found three docs describing `[prompts].explorer` as covering
only "the nested `explore′` sweep inside `consult`". That was already wrong
before this branch — the key has always driven all three explorer tools — and
the reader split makes the omission actively misleading, so all three are
corrected. `docs/config.md` also now says what an override costs: it is a full
replace, so it erases the reader distinction and has to read correctly for a
report handed to a synth and for one that goes straight back to the caller.

One residual the review named and we are accepting: no test pins which reader
each of the four production call sites passes. The type forces a choice and the
comments document it, but the wiring is guarded by review. Closing it needs a
seam that can observe a live call's preamble, which is a bigger change than
this fix.

Reviewed-by: kaibo cast `crusoe` (zai/GLM-5.2 synth, deepseek-ai/Deepseek-V4-Flash explorer)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Amy, on this PR's config prose: "a bit difficult to read, comes through very
Claude-compact", and asked for an STE-like pass. The block had been a single
paragraph of stacked clauses — four parentheticals, ALL-CAPS emphasis mid-sentence,
and the phase list crammed onto the end of a run-on. `docs/config.example.toml` is
embedded in the binary and read start to finish by whoever is configuring kaibo, so
that density is charged to every reader we have.

Rewritten to the rules now written down in AGENTS.md: one idea per sentence, no
em-dash clause chains, the phase list as a list. It is longer in lines and smaller
in distinct words, which is the trade the guide licenses — a familiar word may need
a longer sentence.

`sweep` is now `survey` in this text, Amy's call on the vocabulary split: `survey`
is the published name for one explorer investigation pass, and it is already what
`explore`'s tool description and CLI help say, so it is the one with standing.
`sweep` stays the internal name in code. The rest of the published occurrences move
in the style-guide branch, where the Terms table that licenses the split lives.

The config.md paragraph this PR adds got the same pass, and now links to the guide
instead of re-explaining the two-readers rule in the template.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@tobert
tobert merged commit 98188b6 into main Sep 10, 2026
1 check passed
tobert added a commit that referenced this pull request Sep 10, 2026
Every built-in preamble in `src/consult/prompts.rs` now reads in one
plain register: identity in one sentence, one concern per paragraph,
plain imperative sentences, no capitalized section labels, and the
deliverable obligation closing each prompt. The explorer used to assume
source code (imports, impls, call sites, symbols). It now reads a
project tree and the files a question touches, so a documentation-only
repository is a normal target, and the consult driver, deliberation
prompt, house-rules splice, and history framing use the same word for
the same thing.

This is a restyle, not a rewrite. Every phrase the tests pin survives
except two that moved with their sentences, and the whole-file rule
keeps all four of its registers, because the test that holds them
records the measurement behind the repetition.

One instruction is new in every prompt that produces a deliverable:

```text
Separate what you read from what you infer and from what remains unknown.
```

The house-rules splice now says each section is headed by the path of
the file it came from; the headers were already there. The consult
driver's context paragraph now precedes the answer paragraph, so the
prompt ends on the obligation to write the answer, where a small model
attends most. For the calling agent, the gap sentence is split so "it"
sits next to its antecedent:

```text
Your report is the only view of this project the agent that asked receives.
Anything you leave out is missing from what that agent can act on.
```

Left for a later pass: the explorer's reading guidance still repeats the
kaish sandbox addendum.

Stacks on #183, which must merge first.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
tobert added a commit that referenced this pull request Sep 10, 2026
Two things, and the first explains the second.

## The style pointer has been dead since 2026-08-18

AGENTS.md said: *"kaibo adopts kaish's `docs/style.md` — that guide
names kaibo as an adopter… Read it before you write help text or
operator docs."*

Both halves are false. kaish deleted that file in `e22b0be3` ("rearrange
files and remove docs/style.md") and folded its content into its own
AGENTS.md § "Writing style", which no longer mentions kaibo. So for
three weeks every reader told to go read it — a fresh session, a
subagent with no kaish checkout, a person — has found nothing. Amy asked
whether we'd ever absorbed it, after noticing #183's config prose read
"very Claude-compact". This is very plausibly why.

Absorbed into AGENTS.md directly rather than a new `docs/style.md`
(Amy's call): one file, always loaded, nothing to fetch. It costs
resident context and the trade is deliberate.

**Only what kaibo didn't already have.** "Fast and informative failures"
is covered by our error-strings paragraph, "Write for model context" by
the three-audiences block, and kaish's "Published builtin text" is
clap/ParamSchema-specific — we have our own published/internal rule.
New:

- **Vocabulary choices** — small vocabulary, plain words over figures of
speech, American spelling. Framed on the reason that actually binds
here: our synths are mostly not English-first.
- **One term, one meaning** — including the rule for a term that differs
between published and internal text.
- **Provide specific values** — the exit code, the size, the default,
the condition.
- **The example is the rule** — show the correct example first; make it
carry the rule alone.
- **A Terms table**, seeded with twelve kaibo terms that carry a
guarantee: `backend`, `cast`, `slot`, `arm`, `lane`, `phase`, `survey`,
`sweep`, `dossier`, `family`, `allowed set`, `published`. It grows when
a collision appears in real prose, not in advance — shaped to receive
later analysis rather than to be complete now.

Two deduplications, because one term, one meaning applies to the guide
itself: the `family` ruling restated what the table now defines, and the
three-audiences block argued the non-English-first case in full — it now
points at Vocabulary choices, where that argument lives once.

## `sweep` and `survey` were two words for one thing

`explore`'s tool description and CLI help say **survey**. The operator
docs said **sweep**, 26 times, never defined. A calling agent reads the
first and an operator reads the second, so the split was invisible from
either seat.

`survey` wins in published text because it is already resident — it is
in the front-door `explore` description every calling agent pays for
each session. `sweep` stays the internal name in code (~450 uses, its
own module). Converted 26 sites across `README.md`,
`config.example.toml`, `config.md`, `casts.md`, `rate-limits.md`,
`sandbox-probes.md`.

**The verb sense is deliberately untouched**, which is why this was done
by hand rather than with `sed`. "Omit `backend` to sweep every
configured backend" is ordinary English for iterating over all of
something, not kaibo's noun — three sites keep it.

**Not converted:** ~25 occurrences in tool descriptions and clap doc
comments in `server.rs`/`cli.rs`. They sit under the 2048-character
resident budget and are the text the coming prompt work will most likely
rewrite, so renaming them now would collide for no benefit. The Terms
table states that the retirement is in progress rather than implying
it's finished.

Suite: **1341 passed, 0 failed**.

Overlaps #183 in `docs/config.example.toml` and `docs/config.md` — both
sides now say `survey`, so the conflict is trivial whichever merges
first.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
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.

1 participant