Nests: team rooms with core crows and borrowing
- Server: nests API, persistence, core-first picking, borrowed flag (built by the backend agent against EVENTS.md; tests pass with -race) - UI: nests in the sidebar, one feed per nest, header shows the core team, New nest dialog, borrowed crows labeled, per-nest waiting count - start.command: one-click local server in controlled mode (mock crows, localhost only, state in .crows/) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UQnSw4RRWp8uqpj3nsnnXF
This commit is contained in:
14 files changed
+782
-62
No files matched your search
+13
-4
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
@@ -30,6 +31,7 @@ type Approver interface {
|
||||
type List struct {
|
||||
ID, Requester, Source string
|
||||
Items []string
|
||||
Nest Nest // zero value means the todo nest
|
||||
Parallel bool // fly items at once (web); the command line goes one by one
|
||||
}
|
||||
|
||||
@@ -51,13 +53,16 @@ func itemID(listID string, i int) string { return fmt.Sprintf("%s-%d", listID, i
|
||||
|
||||
// Run flies a list and returns per-item results and the list budget.
|
||||
func (p *Pipeline) Run(ctx context.Context, l List, em Emitter) ([]itemResult, *Budget) {
|
||||
if l.Nest.ID == "" {
|
||||
l.Nest = defaultNest
|
||||
}
|
||||
listBudget := &Budget{name: "list", limit: p.G.TokenBudgetPerList}
|
||||
items := make([]map[string]any, len(l.Items))
|
||||
for i, text := range l.Items {
|
||||
items[i] = map[string]any{"item_id": itemID(l.ID, i), "text": text}
|
||||
}
|
||||
p.Log.Write(Event{Kind: "list", Requester: l.Requester, Note: fmt.Sprintf("%s (%d items)", l.Source, len(l.Items))})
|
||||
em.Emit("list.started", map[string]any{"items": items, "requester": l.Requester})
|
||||
em.Emit("list.started", map[string]any{"items": items, "requester": l.Requester, "nest_id": l.Nest.ID})
|
||||
|
||||
results := make([]itemResult, len(l.Items))
|
||||
if l.Parallel {
|
||||
@@ -89,7 +94,7 @@ func (p *Pipeline) item(ctx context.Context, l List, i int, listBudget *Budget,
|
||||
em.Emit("item.skipped", map[string]any{"item_id": id, "reason": reason})
|
||||
return r
|
||||
}
|
||||
w := &itemWatcher{em: em, itemID: id}
|
||||
w := &itemWatcher{em: em, itemID: id, core: l.Nest.Crows}
|
||||
itemBudget := &Budget{name: "item", limit: p.G.TokenBudgetPerItem}
|
||||
|
||||
t, err := p.Flock.Triage(ctx, w, item, itemBudget, listBudget)
|
||||
@@ -115,7 +120,7 @@ func (p *Pipeline) item(ctx context.Context, l List, i int, listBudget *Budget,
|
||||
em.Emit("verdict", map[string]any{"item_id": id, "target": "triage", "verdict": "auto"})
|
||||
}
|
||||
|
||||
out, err := p.Flock.Execute(ctx, w, item, t, itemBudget, listBudget)
|
||||
out, err := p.Flock.Execute(ctx, w, item, t, l.Nest, itemBudget, listBudget)
|
||||
if err != nil {
|
||||
return skip("flight failed: " + err.Error())
|
||||
}
|
||||
@@ -173,10 +178,14 @@ func (p *Pipeline) save() {
|
||||
type itemWatcher struct {
|
||||
em Emitter
|
||||
itemID string
|
||||
core []string // the nest's core crows; empty for todo
|
||||
}
|
||||
|
||||
// Flying marks a crow borrowed when the nest has a core and the crow is a
|
||||
// roster crow outside it. The Sentry and Elder are never borrowed.
|
||||
func (w *itemWatcher) Flying(crow string) {
|
||||
w.em.Emit("crow.flying", map[string]any{"item_id": w.itemID, "crow": crow})
|
||||
borrowed := len(w.core) > 0 && slices.Contains(roster, crow) && !slices.Contains(w.core, crow)
|
||||
w.em.Emit("crow.flying", map[string]any{"item_id": w.itemID, "crow": crow, "borrowed": borrowed})
|
||||
}
|
||||
|
||||
func (w *itemWatcher) Landed(crow string, tokens int, err error) {
|
||||
|
||||
Reference in new issue
Block a user