Add human escalation path with severity levels (gt-1z3z)
Implements structured escalation channel for Gas Town: - gt escalate command with CRITICAL/HIGH/MEDIUM severity levels - Mayor startup check for pending escalations - Escalation beads with tag for audit trail - Mail routing to overseer with priority mapping - Documentation in docs/escalation.md 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -135,6 +135,11 @@ func runPrime(cmd *cobra.Command, args []string) error {
|
||||
// Run gt mail check --inject to inject any pending mail
|
||||
runMailCheckInject(cwd)
|
||||
|
||||
// For Mayor, check for pending escalations
|
||||
if ctx.Role == RoleMayor {
|
||||
checkPendingEscalations(ctx)
|
||||
}
|
||||
|
||||
// Output startup directive for roles that should announce themselves
|
||||
// Skip if in autonomous mode (slung work provides its own directive)
|
||||
if !hasSlungWork {
|
||||
@@ -1298,3 +1303,90 @@ func ensureBeadsRedirect(ctx RoleContext) {
|
||||
// Note: We don't print a message here to avoid cluttering prime output
|
||||
// The redirect is silently restored
|
||||
}
|
||||
|
||||
// checkPendingEscalations queries for open escalation beads and displays them prominently.
|
||||
// This is called on Mayor startup to surface issues needing human attention.
|
||||
func checkPendingEscalations(ctx RoleContext) {
|
||||
// Query for open escalations using bd list with tag filter
|
||||
cmd := exec.Command("bd", "list", "--status=open", "--tag=escalation", "--json")
|
||||
cmd.Dir = ctx.WorkDir
|
||||
|
||||
var stdout, stderr bytes.Buffer
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = &stderr
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
// Silently skip - escalation check is best-effort
|
||||
return
|
||||
}
|
||||
|
||||
// Parse JSON output
|
||||
var escalations []struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Priority int `json:"priority"`
|
||||
Description string `json:"description"`
|
||||
Created string `json:"created"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(stdout.Bytes(), &escalations); err != nil || len(escalations) == 0 {
|
||||
// No escalations or parse error
|
||||
return
|
||||
}
|
||||
|
||||
// Count by severity
|
||||
critical := 0
|
||||
high := 0
|
||||
medium := 0
|
||||
for _, e := range escalations {
|
||||
switch e.Priority {
|
||||
case 0:
|
||||
critical++
|
||||
case 1:
|
||||
high++
|
||||
default:
|
||||
medium++
|
||||
}
|
||||
}
|
||||
|
||||
// Display prominently
|
||||
fmt.Println()
|
||||
fmt.Printf("%s\n\n", style.Bold.Render("## 🚨 PENDING ESCALATIONS"))
|
||||
fmt.Printf("There are %d escalation(s) awaiting human attention:\n\n", len(escalations))
|
||||
|
||||
if critical > 0 {
|
||||
fmt.Printf(" 🔴 CRITICAL: %d\n", critical)
|
||||
}
|
||||
if high > 0 {
|
||||
fmt.Printf(" 🟠 HIGH: %d\n", high)
|
||||
}
|
||||
if medium > 0 {
|
||||
fmt.Printf(" 🟡 MEDIUM: %d\n", medium)
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
// Show first few escalations
|
||||
maxShow := 5
|
||||
if len(escalations) < maxShow {
|
||||
maxShow = len(escalations)
|
||||
}
|
||||
for i := 0; i < maxShow; i++ {
|
||||
e := escalations[i]
|
||||
severity := "MEDIUM"
|
||||
switch e.Priority {
|
||||
case 0:
|
||||
severity = "CRITICAL"
|
||||
case 1:
|
||||
severity = "HIGH"
|
||||
}
|
||||
fmt.Printf(" • [%s] %s (%s)\n", severity, e.Title, e.ID)
|
||||
}
|
||||
if len(escalations) > maxShow {
|
||||
fmt.Printf(" ... and %d more\n", len(escalations)-maxShow)
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
fmt.Println("**Action required:** Review escalations with `bd list --tag=escalation`")
|
||||
fmt.Println("Close resolved ones with `bd close <id> --reason \"resolution\"`")
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user