Completes step 7, and with it the dev agent's escalation story.
ask_expert (A14), deliberately small
One stateless call for a question needing no repo access. A14 warns against letting this grow into a second escalation subsystem, so it has no diff application, no session state, and no tools.
Structured output is contracted at the prompt level, since vendors differ on JSON enforcement — so the reply is parsed and validated against a local schema with one retry. It tolerates a fenced code block, because models wrap JSON in one habitually, but not missing or malformed fields.
The budget is counted from the canonical log, not from memory. It therefore survives a restart, and a crash cannot hand a circling executor a fresh allowance. Exhaustion raises rather than quietly answering: a caller that has spent its consults should get on with the work or escalate, which is the behaviour A14 is protecting.
Consults are recorded as delegation_started/delegation_completed with role consultant. A16 asks for a "consult/delegation eval set", so reusing the composite events gets consult evaluation for free instead of inventing a parallel record.
Eval capture (A16)
evals.py pairs starts with completions into records, exports JSON lines, and summarises by model. Verdicts live outside the canonical log: the log records what happened, a verdict is a later judgement about it, and judgements get revised — so a good can become a bad without rewriting history. Schema v3 adds the outcomes table, which means a v1 store now traverses two migrations; the existing migration test was updated to assert that, making it a better exercise of the hook than it was.
An unpaired start (process died mid-delegation) is skipped rather than reported half-formed — a partial record would pollute the very set A16 exists to make trustworthy.
Executor integration
ask_expert is offered as a tool only when a desk is configured, in addition to the observable escalation triggers and never instead of them (A12). Two details worth noting: consulting is not mutating, so a completion claim backed only by consults is still challenged by the check added in 7b; and an exhausted budget comes back as a failed tool result rather than an exception, so the model learns it must stop asking rather than crashing the milestone.
Found live
Passing an empty message list and putting the question in the system prompt fails outright — the Messages API requires at least one non-system message, so the "stateless" consult was unsendable. The question now travels as a synthesised user message that is never appended to the log, which keeps the consult stateless while being a valid request. The unit test got better as a result: it now asserts the caller's repo context is absent and the question is present, rather than that nothing was sent at all.
23 new unit tests, 2 live. 245 unit + 17 integration green locally.
Completes step 7, and with it the dev agent's escalation story.
### `ask_expert` (A14), deliberately small
One stateless call for a question needing no repo access. A14 warns against letting this grow into a second escalation subsystem, so it has no diff application, no session state, and no tools.
- **Structured output is contracted at the prompt level**, since vendors differ on JSON enforcement — so the reply is parsed and validated against a local schema with one retry. It tolerates a fenced code block, because models wrap JSON in one habitually, but not missing or malformed fields.
- **The budget is counted from the canonical log, not from memory.** It therefore survives a restart, and a crash cannot hand a circling executor a fresh allowance. Exhaustion *raises* rather than quietly answering: a caller that has spent its consults should get on with the work or escalate, which is the behaviour A14 is protecting.
- **Consults are recorded as `delegation_started`/`delegation_completed` with role `consultant`.** A16 asks for a "consult/delegation eval set", so reusing the composite events gets consult evaluation for free instead of inventing a parallel record.
### Eval capture (A16)
`evals.py` pairs starts with completions into records, exports JSON lines, and summarises by model. **Verdicts live outside the canonical log**: the log records what happened, a verdict is a later judgement about it, and judgements get revised — so a `good` can become a `bad` without rewriting history. Schema v3 adds the outcomes table, which means a v1 store now traverses *two* migrations; the existing migration test was updated to assert that, making it a better exercise of the hook than it was.
An unpaired start (process died mid-delegation) is skipped rather than reported half-formed — a partial record would pollute the very set A16 exists to make trustworthy.
### Executor integration
`ask_expert` is offered as a tool only when a desk is configured, **in addition to** the observable escalation triggers and never instead of them (A12). Two details worth noting: consulting is not mutating, so a completion claim backed only by consults is still challenged by the check added in 7b; and an exhausted budget comes back as a failed tool result rather than an exception, so the model learns it must stop asking rather than crashing the milestone.
### Found live
Passing an empty message list and putting the question in the system prompt fails outright — the Messages API requires at least one non-system message, so the "stateless" consult was unsendable. The question now travels as a synthesised user message that is never appended to the log, which keeps the consult stateless while being a valid request. The unit test got better as a result: it now asserts the caller's repo context is absent *and* the question is present, rather than that nothing was sent at all.
23 new unit tests, 2 live. 245 unit + 17 integration green locally.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Completes step 7.
- expert.py: one stateless call for a question needing no repo access.
Structured output is contracted at the prompt level, since vendors
differ on JSON enforcement, so the reply is parsed and validated
locally with one retry — tolerating a fenced code block, because
models wrap JSON habitually, but not missing fields
- The per-task budget is counted from the canonical log rather than
memory, so it survives a restart and cannot hand a circling executor
a fresh allowance. Exhaustion raises rather than answering: a caller
out of consults should get on with the work or escalate
- Consults are recorded as delegation_started/completed with role
consultant. A16 asks for a "consult/delegation eval set", and
reusing the composite events gets consult evaluation for free rather
than inventing a parallel record
- evals.py pairs starts with completions into eval records, with
verdicts kept outside the canonical log: the log records what
happened, a verdict is a later judgement about it, and judgements
get revised. Schema v3 adds the outcomes table, so a v1 store now
traverses two migrations — a better exercise of the hook than one
- ask_expert is offered to the executor as a tool only when a desk is
configured, in addition to the observable escalation triggers and
never instead of them (A12). Consulting is not mutating, so a
completion claim backed only by consults is still challenged
Found live: passing an empty message list and putting the question in
the system prompt fails outright — the Messages API requires at least
one non-system message. The question now travels as a synthesised user
message that is never appended to the log, which keeps the consult
stateless while remaining a valid request.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Completes step 7, and with it the dev agent's escalation story.
ask_expert(A14), deliberately smallOne stateless call for a question needing no repo access. A14 warns against letting this grow into a second escalation subsystem, so it has no diff application, no session state, and no tools.
delegation_started/delegation_completedwith roleconsultant. A16 asks for a "consult/delegation eval set", so reusing the composite events gets consult evaluation for free instead of inventing a parallel record.Eval capture (A16)
evals.pypairs starts with completions into records, exports JSON lines, and summarises by model. Verdicts live outside the canonical log: the log records what happened, a verdict is a later judgement about it, and judgements get revised — so agoodcan become abadwithout rewriting history. Schema v3 adds the outcomes table, which means a v1 store now traverses two migrations; the existing migration test was updated to assert that, making it a better exercise of the hook than it was.An unpaired start (process died mid-delegation) is skipped rather than reported half-formed — a partial record would pollute the very set A16 exists to make trustworthy.
Executor integration
ask_expertis offered as a tool only when a desk is configured, in addition to the observable escalation triggers and never instead of them (A12). Two details worth noting: consulting is not mutating, so a completion claim backed only by consults is still challenged by the check added in 7b; and an exhausted budget comes back as a failed tool result rather than an exception, so the model learns it must stop asking rather than crashing the milestone.Found live
Passing an empty message list and putting the question in the system prompt fails outright — the Messages API requires at least one non-system message, so the "stateless" consult was unsendable. The question now travels as a synthesised user message that is never appended to the log, which keeps the consult stateless while being a valid request. The unit test got better as a result: it now asserts the caller's repo context is absent and the question is present, rather than that nothing was sent at all.
23 new unit tests, 2 live. 245 unit + 17 integration green locally.
🤖 Generated with Claude Code