Skip to content

Commit 7e577be

Browse files
committed
fix: make orchestrator bootstrap and heartbeat messages concise
Bootstrap no longer inlines HEARTBEAT.md and repo memory — the orchestrator reads those files itself. Heartbeat reduced to a single-line summary (active/stuck counts + decisions path). Context savings: 87-99% vs old format depending on task count.
1 parent b3c975b commit 7e577be

2 files changed

Lines changed: 97 additions & 192 deletions

File tree

internal/agent/orchestrator.go

Lines changed: 13 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -128,51 +128,19 @@ func (o *Orchestrator) composeBootstrapMessage() string {
128128
repoStatePath := memory.RepoStatePath(o.dataDir, o.org, o.repo)
129129
decisionsPath := memory.RepoDecisionsPath(o.dataDir, o.org, o.repo)
130130

131-
var sb strings.Builder
132-
133-
heartbeat, err := memory.ReadRepoHeartbeat(o.dataDir, o.org, o.repo)
134-
if err == nil && heartbeat != "" {
135-
// Replace template placeholders with real paths so the orchestrator
136-
// can act on the bootstrap message without waiting for a heartbeat.
137-
heartbeat = strings.ReplaceAll(heartbeat, "{decisionsPath}", decisionsPath)
138-
heartbeat = strings.ReplaceAll(heartbeat, "{repoStatePath}", repoStatePath)
139-
sb.WriteString("# Heartbeat Instructions\n\n")
140-
sb.WriteString(heartbeat)
141-
sb.WriteString("\n\n")
142-
}
143-
144-
repoMemory, err := memory.ReadRepoMemory(o.dataDir, o.org, o.repo)
145-
if err == nil && repoMemory != "" {
146-
sb.WriteString("# Repository Memory\n\n")
147-
sb.WriteString(repoMemory)
148-
sb.WriteString("\n\n")
149-
}
150-
151-
if sb.Len() == 0 {
152-
return ""
153-
}
154-
155131
instance, err := o.queries.GetInstance(context.Background(), o.instanceID)
156132
workingDir := ""
157133
if err == nil {
158134
workingDir = instance.WorkingDirectory
159135
}
160136

161-
return fmt.Sprintf("You are the orchestrator AI. Read and internalize these instructions. "+
162-
"You will receive periodic heartbeat messages. Your working directory is: %s\n\n"+
163-
"## Key Paths\n"+
164-
"- Repo state: `%s`\n"+
165-
"- Decisions: `%s`\n"+
166-
"- Memory: `%s/MEMORY.md`\n\n"+
167-
"## Available Agents\n"+
168-
"- `@flock-history-analyzer` — For historical analysis. Spawn with a query like "+
169-
"\"What happened with issue #42?\" or \"List PRs merged this week\". "+
170-
"Use this instead of browsing git history manually.\n"+
171-
"- `@flock-self-reflect` — Invoke for completed tasks to update memory.\n"+
172-
"- `@flock-implementation-agent` — Sub-agents handle issue resolution.\n\n"+
173-
"%s"+
174-
"Acknowledge that you understand your role.",
175-
workingDir, repoStatePath, decisionsPath, repoStatePath, sb.String())
137+
return fmt.Sprintf("You are the orchestrator for this repo. You will receive periodic heartbeat messages.\n\n"+
138+
"Paths: working_dir=`%s` state=`%s` decisions=`%s`\n\n"+
139+
"Read your instructions: `cat %s/HEARTBEAT.md`\n"+
140+
"Read repo memory: `cat %s/MEMORY.md`\n\n"+
141+
"Agents: `@flock-history-analyzer` (history queries), `@flock-self-reflect` (post-completion), `@flock-implementation-agent` (issue resolution).\n\n"+
142+
"Read HEARTBEAT.md now, then acknowledge.",
143+
workingDir, repoStatePath, decisionsPath, repoStatePath, repoStatePath)
176144
}
177145

178146
// SendHeartbeat sends a heartbeat message to the orchestrator session and
@@ -217,48 +185,26 @@ func (o *Orchestrator) SendHeartbeat(ctx context.Context) error {
217185
}
218186

219187
func (o *Orchestrator) composeHeartbeatMessage(ctx context.Context) string {
220-
instance, err := o.queries.GetInstance(ctx, o.instanceID)
221-
workingDir := ""
222-
if err == nil {
223-
workingDir = instance.WorkingDirectory
224-
}
225-
226-
repoStatePath := memory.RepoStatePath(o.dataDir, o.org, o.repo)
227188
decisionsPath := memory.RepoDecisionsPath(o.dataDir, o.org, o.repo)
228189

229-
var sb strings.Builder
230-
sb.WriteString(fmt.Sprintf("# Heartbeat\n\nWorking directory: `%s`\nRepo state: `%s`\nDecisions: `%s`\n\n", workingDir, repoStatePath, decisionsPath))
231-
232-
// Task count summaries instead of full listings
233190
activeCount, _ := o.queries.CountActiveTasksByInstance(ctx, o.instanceID)
234191
stuckTasks, _ := o.queries.ListStuckTasks(ctx, sqlc.ListStuckTasksParams{
235192
InstanceID: o.instanceID,
236193
Column2: fmt.Sprintf("%d", o.cfg.StuckThresholdSecs),
237194
})
238-
stuckCount := len(stuckTasks)
239-
240-
sb.WriteString(fmt.Sprintf("## Current State\n\nActive tasks: %d\nStuck tasks: %d\n", activeCount, stuckCount))
241195

242-
// Include last heartbeat timestamp
243-
lastHB, err := o.queries.GetLastHeartbeatByInstance(ctx, o.instanceID)
244-
if err == nil && lastHB != "" {
245-
sb.WriteString(fmt.Sprintf("Last heartbeat: %s\n", lastHB))
246-
}
247-
sb.WriteString("\n")
196+
var sb strings.Builder
197+
sb.WriteString(fmt.Sprintf("Heartbeat: active=%d stuck=%d decisions=`%s`\n", activeCount, len(stuckTasks), decisionsPath))
248198

249-
// Include stuck task IDs so orchestrator can act on them
250-
if stuckCount > 0 {
251-
sb.WriteString("### Stuck Tasks\n")
199+
if len(stuckTasks) > 0 {
200+
sb.WriteString("Stuck:")
252201
for _, t := range stuckTasks {
253-
sb.WriteString(fmt.Sprintf("- #%d %s (task_id: %s, last: %s)\n",
254-
t.IssueNumber, t.Title, t.ID, t.LastActivityAt))
202+
sb.WriteString(fmt.Sprintf(" #%d(%s,last:%s)", t.IssueNumber, t.ID, t.LastActivityAt))
255203
}
256204
sb.WriteString("\n")
257205
}
258206

259-
sb.WriteString("## Instructions\n")
260-
sb.WriteString("Follow the steps in your HEARTBEAT.md instructions.\n")
261-
sb.WriteString("For detailed task history, spawn `@flock-history-analyzer` with your query.\n")
207+
sb.WriteString("Run your HEARTBEAT.md steps now.")
262208

263209
return sb.String()
264210
}

0 commit comments

Comments
 (0)