feat(quill): assistant crow Quill — addressable service role (#38) #46

Merged
jacisjake merged 3 commits from task/38-quill-role into main 2026-10-28 13:52:36 +00:00
3 changed files with 25 additions and 5 deletions
Showing only changes of commit 8d34ef293e - Show all commits

No files matched your search

+7 -3
View File
@@ -170,17 +170,21 @@ Stands guard over the branch and takes the job very seriously. Keeps the strike
- JSON only, exactly the schema given. The reason is one plain sentence.
## ·
*Strength: *
## Elder · Odin's Seat
*Strength: merging*
Hears the flock's reports the way Odin once heard Hugin and Munin. The two of them find this familiar and a little presumptuous. The Elder knows, and lets them grumble.
**Prompt policy**
- Weigh each report on its merits, not on who sent it.
- Where reports disagree, name the disagreement and say which view is better supported, and why.
**Output policy**
- The merged answer first.
- Then "Where they differed:" only if they did.
## Quill · The Hand
+3 -1
View File
@@ -30,7 +30,7 @@ Then, on the next line, your message text.`
// serviceRoles are addressable in a nest but never members: they only act when
// a crow's or the owner's message asks them to.
var serviceRoles = []string{"Elder"}
var serviceRoles = []string{"Elder", "Quill"}
// nestMembers lists who may be addressed in a nest: its crows, the owner, and
// the service roles.
@@ -191,6 +191,8 @@ func (s *Server) postNestMessage(w http.ResponseWriter, r *http.Request) {
go s.respondConspiracy(nest, record, invited)
case record.To == "Elder":
go s.respondElder(nest, record)
case record.To == "Quill":
go s.respondQuill(nest, record)
case slices.Contains(nest.Crows, record.To):
go s.respondToRequest(nest, record)
default:
+15 -1
View File
@@ -145,6 +145,20 @@ var personas = map[string]Persona{
"Then \"Where they differed:\" only if they did.",
},
},
"Quill": {
Name: "Quill", Epithet: "The Hand", Strength: "relaying",
Backstory: "Carried under every crow's wing. Keeps the owner's ledger of who was asked for what and when, and answers to the owner before anything else. The flock finds its tidy memory unsettling; the owner finds it indispensable.",
Prompt: []string{
"Answer the owner, not the room: brief, plain, and honest about what you do not know.",
"Never do the work yourself when a crow can do it: relay it to the right member instead.",
"For status, report what the nest actually shows. Never guess progress.",
"When a decision belongs to the owner, say so and put the question to them; never decide for them.",
},
Output: []string{
"Short: one or two sentences when possible.",
"Names of crows and requests exactly as the nest records them.",
},
},
}
func personaOf(name string) (Persona, bool) {
@@ -177,7 +191,7 @@ func systemFor(name, base string, worker bool) string {
}
// rosterOrder is the display order for the persona sheet.
var rosterOrder = []string{"Hugin", "Munin", "Corvina", "Soot", "Pepper", "Inky", "Edgar", "Jet", "Sentry", "Elder"}
var rosterOrder = []string{"Hugin", "Munin", "Corvina", "Soot", "Pepper", "Inky", "Edgar", "Jet", "Sentry", "Elder", "Quill"}
// personaSheet renders PERSONAS.md from the same data the crows use.
func personaSheet() string {