File no report for a turn that asks a blocking question

This commit is contained in:
Ian Gulliver
2026-10-15 10:41:18 -07:00
parent b6df27f085
commit 535f5579f9
5 changed files with 26 additions and 7 deletions
+1
View File
@@ -14,6 +14,7 @@ import (
const guidance = `You are running inside byre. The human reads your work in byre's web UI, not a terminal.
- Ask every question with mcp__byre__ask_human, never in prose.
- End every turn with a short final message for the human: what you did, anything you departed from in the plan or instructions, risks, and what's next. byre shows it as the turn's summary in the transcript and the inbox. State every departure from the plan.
- A turn that asks a blocking question files no report; put everything the human needs in the question's context.
- In that final message and in ask_human context, put long supporting detail (logs, full lists, data) in collapsed sections the human expands on demand: a line <details><summary>Title</summary>, the Markdown content, then a line </details>.
- Don't commit. byre commits everything in the worktree at the end of each turn, using the turn's final message.
- Don't push, don't switch or touch other branches, and don't run git maintenance (gc, prune, worktree prune). byre handles merging and pushing.`
+1
View File
@@ -131,6 +131,7 @@ func (m *Manager) ask(ctx context.Context, l *live, raw json.RawMessage) (string
m.mu.Lock()
l.asked = true
l.askedBlock = l.askedBlock || *args.Blocking
m.mu.Unlock()
m.snapshotSoon(l, api.SnapshotTriggerAskHuman)
+4 -1
View File
@@ -80,6 +80,7 @@ type live struct {
stopping bool
interrupted bool
asked bool
askedBlock bool
bounced bool
compacting bool
wantCompact bool
@@ -599,6 +600,7 @@ func (m *Manager) message(ctx context.Context, l *live, actor, text string, imag
m.mu.Lock()
l.interrupted = false
l.asked = false
l.askedBlock = false
l.bounced = false
l.wantCompact = false
l.compacting = compacting
@@ -808,6 +810,7 @@ func (m *Manager) handle(ctx context.Context, l *live, ev claude.Event) error {
interrupted := l.interrupted
rateLimited := l.rateLimited
compacting := l.compacting
askedBlock := l.askedBlock
m.mu.Unlock()
finished := !res.IsError && !interrupted && strings.TrimSpace(res.Result) != ""
@@ -825,7 +828,7 @@ func (m *Manager) handle(ctx context.Context, l *live, ev claude.Event) error {
switch {
case handoff:
payload["handoff"] = true
case finished && !compacting:
case finished && !compacting && !askedBlock:
payload["inbox_item_id"] = m.fileSummary(l, res.Result)
}
err = m.emit(ctx, l, agentActor, "turn.ended", payload)
+18 -4
View File
@@ -394,18 +394,26 @@ const colorPrompt = `I need a file color.txt containing exactly one color name.
`When you get my answer, write the file.`
func (e *env) openQuestions(sessionID string) []api.InboxItem {
return e.openItems(sessionID, api.Question)
}
func (e *env) openReports(sessionID string) []api.InboxItem {
return e.openItems(sessionID, api.Report)
}
func (e *env) openItems(sessionID string, kind api.InboxItemKind) []api.InboxItem {
e.t.Helper()
items, err := e.store.ListInbox(e.ctx, store.InboxFilter{SessionID: sessionID, State: api.Open})
if err != nil {
e.t.Fatalf("list inbox: %v", err)
}
questions := []api.InboxItem{}
matched := []api.InboxItem{}
for _, item := range items {
if item.Kind == api.Question {
questions = append(questions, item)
if item.Kind == kind {
matched = append(matched, item)
}
}
return questions
return matched
}
func (e *env) respond(m *agents.Manager, item api.InboxItem, typ api.InboxResponseType, option, text string, state api.InboxState) {
@@ -470,6 +478,9 @@ func TestAskHumanRoundTrip(t *testing.T) {
if _, err := os.Stat(filepath.Join(e.task.WorktreePath, "color.txt")); err == nil {
t.Fatalf("agent wrote color.txt before getting an answer")
}
if reports := e.openReports(sess.Id); len(reports) != 0 {
t.Fatalf("turn that asked a blocking question filed a report: %+v", reports)
}
waiting, err := e.store.AwaitingHuman(e.ctx, sess.Id)
if err != nil || waiting != 1 {
@@ -496,6 +507,9 @@ func TestAskHumanRoundTrip(t *testing.T) {
if strings.TrimSpace(string(out)) != "blue" {
t.Fatalf("color.txt contains %q, want blue", out)
}
if reports := e.openReports(sess.Id); len(reports) != 1 {
t.Fatalf("want one report from the answered turn, got %+v", reports)
}
pending, err := e.store.Undelivered(e.ctx, sess.Id)
if err != nil || len(pending) != 0 {