diff --git a/.beads/formulas/mol-deacon-patrol.formula.toml b/.beads/formulas/mol-deacon-patrol.formula.toml index 1c357490..7ec83e38 100644 --- a/.beads/formulas/mol-deacon-patrol.formula.toml +++ b/.beads/formulas/mol-deacon-patrol.formula.toml @@ -665,46 +665,71 @@ Skip dispatch - system is healthy. [[steps]] id = "costs-digest" -title = "Aggregate daily costs" +title = "Aggregate daily costs [DISABLED]" needs = ["session-gc"] description = """ -**DAILY DIGEST** - Aggregate yesterday's session cost wisps. +**⚠️ DISABLED** - Skip this step entirely. -Session costs are recorded as ephemeral wisps (not exported to JSONL) to avoid -log-in-database pollution. This step aggregates them into a permanent daily -"Cost Report YYYY-MM-DD" bead for audit purposes. +Cost tracking is temporarily disabled because Claude Code does not expose +session costs in a way that can be captured programmatically. + +**Why disabled:** +- The `gt costs` command uses tmux capture-pane to find costs +- Claude Code displays costs in the TUI status bar, not in scrollback +- All sessions show $0.00 because capture-pane can't see TUI chrome +- The infrastructure is sound but has no data source + +**What we need from Claude Code:** +- Stop hook env var (e.g., `$CLAUDE_SESSION_COST`) +- Or queryable file/API endpoint + +**Re-enable when:** Claude Code exposes cost data via API or environment. + +See: GH#24, gt-7awfj + +**Exit criteria:** Skip this step - proceed to next.""" + +[[steps]] +id = "patrol-digest" +title = "Aggregate daily patrol digests" +needs = ["costs-digest"] +description = """ +**DAILY DIGEST** - Aggregate yesterday's patrol cycle digests. + +Patrol cycles (Deacon, Witness, Refinery) create ephemeral per-cycle digests +to avoid JSONL pollution. This step aggregates them into a single permanent +"Patrol Report YYYY-MM-DD" bead for audit purposes. **Step 1: Check if digest is needed** ```bash -# Preview yesterday's costs (dry run) -gt costs digest --yesterday --dry-run +# Preview yesterday's patrol digests (dry run) +gt patrol digest --yesterday --dry-run ``` -If output shows "No session cost wisps found", skip to Step 3. +If output shows "No patrol digests found", skip to Step 3. **Step 2: Create the digest** ```bash -gt costs digest --yesterday +gt patrol digest --yesterday ``` This: -- Queries all session.ended wisps from yesterday -- Creates a single "Cost Report YYYY-MM-DD" bead with aggregated data -- Deletes the source wisps +- Queries all ephemeral patrol digests from yesterday +- Creates a single "Patrol Report YYYY-MM-DD" bead with aggregated data +- Deletes the source digests **Step 3: Verify** -The digest appears in `gt costs --week` queries. -Daily digests preserve audit trail without per-session pollution. +Daily patrol digests preserve audit trail without per-cycle pollution. **Timing**: Run once per morning patrol cycle. The --yesterday flag ensures we don't try to digest today's incomplete data. -**Exit criteria:** Yesterday's costs digested (or no wisps to digest).""" +**Exit criteria:** Yesterday's patrol digests aggregated (or none to aggregate).""" [[steps]] id = "log-maintenance" title = "Rotate logs and prune state" -needs = ["costs-digest"] +needs = ["patrol-digest"] description = """ Maintain daemon logs and state files. diff --git a/internal/beads/handoff.go b/internal/beads/handoff.go index 7ab4afc5..0a2e7ee9 100644 --- a/internal/beads/handoff.go +++ b/internal/beads/handoff.go @@ -158,8 +158,12 @@ func (b *Beads) AttachMolecule(pinnedBeadID, moleculeID string) (*Issue, error) return nil, fmt.Errorf("fetching pinned bead: %w", err) } + // Allow pinned beads OR open polecat agent beads (polecats have a lifecycle, not permanent) if issue.Status != StatusPinned { - return nil, fmt.Errorf("issue %s is not pinned (status: %s)", pinnedBeadID, issue.Status) + _, role, _, ok := ParseAgentBeadID(pinnedBeadID) + if !(issue.Status == "open" && ok && role == "polecat") { + return nil, fmt.Errorf("issue %s is not pinned or open polecat (status: %s)", pinnedBeadID, issue.Status) + } } // Build attachment fields with current timestamp diff --git a/internal/cmd/sling.go b/internal/cmd/sling.go index 3c32b43e..d2885b3b 100644 --- a/internal/cmd/sling.go +++ b/internal/cmd/sling.go @@ -508,8 +508,10 @@ func runSling(cmd *cobra.Command, args []string) error { updateAgentHookBead(targetAgent, beadID, hookWorkDir, townBeadsDir) // Auto-attach mol-polecat-work to polecat agent beads - // This ensures polecats have the standard work molecule attached for guidance - if strings.Contains(targetAgent, "/polecats/") { + // This ensures polecats have the standard work molecule attached for guidance. + // Only do this for bare beads (no --on formula), since formula-on-bead + // mode already attaches the formula as a molecule. + if formulaName == "" && strings.Contains(targetAgent, "/polecats/") { if err := attachPolecatWorkMolecule(targetAgent, hookWorkDir, townRoot); err != nil { // Warn but don't fail - polecat will still work without molecule fmt.Printf("%s Could not attach work molecule: %v\n", style.Dim.Render("Warning:"), err) diff --git a/internal/formula/formulas/mol-deacon-patrol.formula.toml b/internal/formula/formulas/mol-deacon-patrol.formula.toml index 1c357490..7ec83e38 100644 --- a/internal/formula/formulas/mol-deacon-patrol.formula.toml +++ b/internal/formula/formulas/mol-deacon-patrol.formula.toml @@ -665,46 +665,71 @@ Skip dispatch - system is healthy. [[steps]] id = "costs-digest" -title = "Aggregate daily costs" +title = "Aggregate daily costs [DISABLED]" needs = ["session-gc"] description = """ -**DAILY DIGEST** - Aggregate yesterday's session cost wisps. +**⚠️ DISABLED** - Skip this step entirely. -Session costs are recorded as ephemeral wisps (not exported to JSONL) to avoid -log-in-database pollution. This step aggregates them into a permanent daily -"Cost Report YYYY-MM-DD" bead for audit purposes. +Cost tracking is temporarily disabled because Claude Code does not expose +session costs in a way that can be captured programmatically. + +**Why disabled:** +- The `gt costs` command uses tmux capture-pane to find costs +- Claude Code displays costs in the TUI status bar, not in scrollback +- All sessions show $0.00 because capture-pane can't see TUI chrome +- The infrastructure is sound but has no data source + +**What we need from Claude Code:** +- Stop hook env var (e.g., `$CLAUDE_SESSION_COST`) +- Or queryable file/API endpoint + +**Re-enable when:** Claude Code exposes cost data via API or environment. + +See: GH#24, gt-7awfj + +**Exit criteria:** Skip this step - proceed to next.""" + +[[steps]] +id = "patrol-digest" +title = "Aggregate daily patrol digests" +needs = ["costs-digest"] +description = """ +**DAILY DIGEST** - Aggregate yesterday's patrol cycle digests. + +Patrol cycles (Deacon, Witness, Refinery) create ephemeral per-cycle digests +to avoid JSONL pollution. This step aggregates them into a single permanent +"Patrol Report YYYY-MM-DD" bead for audit purposes. **Step 1: Check if digest is needed** ```bash -# Preview yesterday's costs (dry run) -gt costs digest --yesterday --dry-run +# Preview yesterday's patrol digests (dry run) +gt patrol digest --yesterday --dry-run ``` -If output shows "No session cost wisps found", skip to Step 3. +If output shows "No patrol digests found", skip to Step 3. **Step 2: Create the digest** ```bash -gt costs digest --yesterday +gt patrol digest --yesterday ``` This: -- Queries all session.ended wisps from yesterday -- Creates a single "Cost Report YYYY-MM-DD" bead with aggregated data -- Deletes the source wisps +- Queries all ephemeral patrol digests from yesterday +- Creates a single "Patrol Report YYYY-MM-DD" bead with aggregated data +- Deletes the source digests **Step 3: Verify** -The digest appears in `gt costs --week` queries. -Daily digests preserve audit trail without per-session pollution. +Daily patrol digests preserve audit trail without per-cycle pollution. **Timing**: Run once per morning patrol cycle. The --yesterday flag ensures we don't try to digest today's incomplete data. -**Exit criteria:** Yesterday's costs digested (or no wisps to digest).""" +**Exit criteria:** Yesterday's patrol digests aggregated (or none to aggregate).""" [[steps]] id = "log-maintenance" title = "Rotate logs and prune state" -needs = ["costs-digest"] +needs = ["patrol-digest"] description = """ Maintain daemon logs and state files.