Skip to content

Commit 30bd14a

Browse files
committed
test message
1 parent f07afcd commit 30bd14a

9 files changed

Lines changed: 69 additions & 15 deletions

File tree

internal/agent/pool.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,10 @@ func (p *Pool) Close() {
177177
p.rateLimiter.Stop()
178178
p.mu.Lock()
179179
for _, agent := range p.agents {
180-
_ = agent.Close() // best-effort cleanup
180+
func() {
181+
defer func() { recover() }() // prevent one agent panic from skipping cleanup of others
182+
_ = agent.Close()
183+
}()
181184
}
182185
p.agents = nil
183186
p.mu.Unlock()

internal/evolution/engine.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,8 @@ func (e *Engine) clearSessionPlan() {
180180
path := filepath.Join(e.repoPath, "SESSION_PLAN.md")
181181
if err := os.Remove(path); err == nil {
182182
e.logger.Info("cleared SESSION_PLAN.md after successful merge")
183+
} else if !os.IsNotExist(err) {
184+
e.logger.Warn("failed to clear SESSION_PLAN.md", "err", err)
183185
}
184186
}
185187

@@ -240,7 +242,10 @@ func (e *Engine) Run(ctx context.Context, p iteragent.Provider, issues string) (
240242
return result, runErr
241243
}
242244

243-
hasChanges, _ := e.hasChanges(ctx)
245+
hasChanges, changesErr := e.hasChanges(ctx)
246+
if changesErr != nil {
247+
e.logger.Warn("could not determine if changes exist, assuming no changes", "err", changesErr)
248+
}
244249
if !hasChanges {
245250
e.logger.Info("no changes detected, skipping PR flow")
246251
result.Status = "no_changes"
@@ -409,7 +414,10 @@ func (e *Engine) auditLog(eventType, tool, detail string) {
409414
entry["detail"] = detail
410415
}
411416

412-
data, _ := json.Marshal(entry)
417+
data, err := json.Marshal(entry)
418+
if err != nil {
419+
return
420+
}
413421

414422
e.auditMu.Lock()
415423
defer e.auditMu.Unlock()

internal/evolution/memory.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@ func (e *Engine) appendLearningJSONL(title, source, context, takeaway string) er
2424
}
2525

2626
dayBytes, _ := os.ReadFile(filepath.Join(e.repoPath, "DAY_COUNT"))
27-
day, _ := strconv.Atoi(strings.TrimSpace(string(dayBytes)))
27+
dayStr := strings.TrimSpace(string(dayBytes))
28+
day, err := strconv.Atoi(dayStr)
29+
if err != nil && dayStr != "" {
30+
e.logger.Warn("DAY_COUNT is not a valid integer, defaulting to 0", "value", dayStr)
31+
}
2832

2933
entry := map[string]interface{}{
3034
"type": "lesson",
@@ -66,7 +70,11 @@ func (e *Engine) appendFailureJSONL(taskTitle, reason string) error {
6670
}
6771

6872
dayBytes, _ := os.ReadFile(filepath.Join(e.repoPath, "DAY_COUNT"))
69-
day, _ := strconv.Atoi(strings.TrimSpace(string(dayBytes)))
73+
dayStr := strings.TrimSpace(string(dayBytes))
74+
day, err := strconv.Atoi(dayStr)
75+
if err != nil && dayStr != "" {
76+
e.logger.Warn("DAY_COUNT is not a valid integer, defaulting to 0", "value", dayStr)
77+
}
7078

7179
entry := map[string]interface{}{
7280
"type": "failure",
@@ -126,7 +134,12 @@ func trimFailuresJSONL(path string, maxAge time.Duration) {
126134
if len(kept) == 0 {
127135
return
128136
}
129-
_ = os.WriteFile(path, []byte(strings.Join(kept, "\n")+"\n"), 0o644)
137+
// Write atomically: temp file then rename, so a crash mid-write can't corrupt the file.
138+
tmp := path + ".tmp"
139+
if err := os.WriteFile(tmp, []byte(strings.Join(kept, "\n")+"\n"), 0o644); err != nil {
140+
return
141+
}
142+
_ = os.Rename(tmp, path)
130143
}
131144

132145
// recentFailures reads memory/failures.jsonl and returns the last N entries

internal/evolution/parsing.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,14 @@ func parseSessionPlanTasks(plan string) []planTask {
3232
var num int
3333
var title string
3434
if idx := strings.IndexByte(rest, ':'); idx >= 0 {
35-
fmt.Sscanf(rest[:idx], "%d", &num)
35+
if n, _ := fmt.Sscanf(rest[:idx], "%d", &num); n == 0 {
36+
num = len(tasks) + 1
37+
}
3638
title = strings.TrimSpace(rest[idx+1:])
3739
} else {
38-
fmt.Sscanf(rest, "%d", &num)
40+
if n, _ := fmt.Sscanf(rest, "%d", &num); n == 0 {
41+
num = len(tasks) + 1
42+
}
3943
title = rest
4044
}
4145
current = &planTask{Number: num, Title: title}
@@ -150,7 +154,7 @@ func parseIssueResponses(plan string) []issueResponse {
150154
rest := strings.TrimPrefix(line, "- #")
151155
var num int
152156
fmt.Sscanf(rest, "%d", &num)
153-
if num == 0 {
157+
if num <= 0 || num > 999999 {
154158
continue
155159
}
156160
status := "comment"

internal/evolution/phases.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,9 @@ func (e *Engine) executeTask(ctx context.Context, p iteragent.Provider, task pla
337337
e.logger.Info("task succeeded on retry", "number", task.Number)
338338
} else {
339339
e.logger.Warn("task failed after retry, skipping", "number", task.Number)
340-
_ = e.appendFailureJSONL(task.Title, firstLine(failReason))
340+
if err := e.appendFailureJSONL(task.Title, firstLine(failReason)); err != nil {
341+
e.logger.Warn("failed to record task failure", "task", task.Title, "err", err)
342+
}
341343
}
342344
}
343345

@@ -386,7 +388,9 @@ func (e *Engine) runTaskAttempt(ctx context.Context, p iteragent.Provider, task
386388
return false, errCtx
387389
}
388390

389-
_ = e.appendLearningJSONL(firstLine(extractCommitMessage(taskOutput)), "evolution", task.Description, "")
391+
if err := e.appendLearningJSONL(firstLine(extractCommitMessage(taskOutput)), "evolution", task.Description, ""); err != nil {
392+
e.logger.Warn("failed to record task learning", "task", task.Title, "err", err)
393+
}
390394
return true, ""
391395
}
392396

internal/evolution/phases_communicate.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,15 +128,20 @@ func (e *Engine) persistJournalEntry(journalEntry string, day string) {
128128

129129
journalPath := filepath.Join(e.repoPath, "docs/JOURNAL.md")
130130
_ = os.MkdirAll(filepath.Dir(journalPath), 0o755)
131-
journal, _ := os.ReadFile(journalPath)
131+
journal, err := os.ReadFile(journalPath)
132+
if err != nil && !os.IsNotExist(err) {
133+
e.logger.Warn("failed to read JOURNAL.md, will overwrite", "err", err)
134+
}
132135

133136
header := "# iterate Evolution Journal\n"
134137
if !strings.HasPrefix(string(journal), header) {
135138
journal = []byte(header)
136139
}
137140
rest := strings.TrimPrefix(strings.TrimPrefix(string(journal), header), "\n")
138141
newContent := header + "\n" + extracted + "\n\n" + rest
139-
_ = os.WriteFile(journalPath, []byte(newContent), 0o644)
142+
if err := os.WriteFile(journalPath, []byte(newContent), 0o644); err != nil {
143+
e.logger.Warn("failed to write JOURNAL.md", "err", err)
144+
}
140145
}
141146

142147
// issueAlreadyCommented checks if the bot already commented on an issue today.
@@ -154,17 +159,24 @@ func (e *Engine) issueAlreadyCommented(ctx context.Context, issueNum int, day st
154159

155160
// postIssueComments posts GitHub comments for addressed issues.
156161
func (e *Engine) postIssueComments(ctx context.Context, p iteragent.Provider, tools []iteragent.Tool, systemPrompt string, skills *iteragent.SkillSet, plan string) {
162+
day := e.readDayCount()
157163
responses := parseIssueResponses(plan)
158164
for _, resp := range responses {
165+
if e.issueAlreadyCommented(ctx, resp.IssueNum, day) {
166+
e.logger.Info("skipping already-commented issue", "issue", resp.IssueNum, "day", day)
167+
continue
168+
}
159169
body := fmt.Sprintf("Status: %s\nReason: %s", resp.Status, resp.Reason)
160170
userMsg := fmt.Sprintf(`Post a GitHub issue comment on issue #%d. Be brief. Sign off with your day count.
161171
162172
Body: %s
163173
164174
Use: gh issue comment %d --repo %s --body "..."`, resp.IssueNum, body, resp.IssueNum, e.repo)
175+
issueCtx, issueCancel := context.WithTimeout(ctx, 90*time.Second)
165176
a := e.newAgent(p, tools, systemPrompt, skills)
166-
e.forwardEvents(a.Prompt(ctx, userMsg))
177+
e.forwardEvents(a.Prompt(issueCtx, userMsg))
167178
a.Finish()
179+
issueCancel()
168180
}
169181
}
170182

internal/social/engine.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,12 @@ func trimSocialJSONL(path string) {
401401
if len(kept) == 0 {
402402
return
403403
}
404-
_ = os.WriteFile(path, []byte(strings.Join(kept, "\n")+"\n"), 0o644)
404+
// Write atomically: temp file then rename to avoid partial writes on crash.
405+
tmp := path + ".tmp"
406+
if err := os.WriteFile(tmp, []byte(strings.Join(kept, "\n")+"\n"), 0o644); err != nil {
407+
return
408+
}
409+
_ = os.Rename(tmp, path)
405410
}
406411

407412
// --- GitHub REST API calls ---

scripts/build/generate_stats.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"""
88

99
import json
10+
import os
1011
import subprocess
1112
import sys
1213
from datetime import datetime, timedelta, timezone
@@ -135,6 +136,7 @@ def main():
135136
# Generate stats
136137
stats = generate_stats(repo_path)
137138
stats_file = f"{repo_path}/docs/stats.json"
139+
os.makedirs(os.path.dirname(stats_file), exist_ok=True)
138140
with open(stats_file, "w") as f:
139141
json.dump(stats, f, indent=2)
140142
print(f"Stats written to {stats_file}")
@@ -143,6 +145,7 @@ def main():
143145
# Generate weekly summary
144146
summary = generate_weekly_summary(repo_path)
145147
summary_file = f"{repo_path}/memory/weekly_summary.md"
148+
os.makedirs(os.path.dirname(summary_file), exist_ok=True)
146149
with open(summary_file, "w") as f:
147150
f.write(summary)
148151
print(f"\nSummary written to {summary_file}")

scripts/build/track_coverage.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,9 @@ def main():
9999
print(json.dumps(entry))
100100

101101
# Append to history
102+
import os
102103
history_file = f"{repo_path}/memory/coverage_history.jsonl"
104+
os.makedirs(os.path.dirname(history_file), exist_ok=True)
103105
with open(history_file, "a") as f:
104106
f.write(json.dumps(entry) + "\n")
105107

0 commit comments

Comments
 (0)