Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,26 @@ Nothing yet.

---

## [0.8.1] - 2026-03-25

### 🐛 Bug Fixes
- **Git Integration**: Replaced broken branch-per-iteration strategy with correct commit-per-iteration design. One branch for the entire loop, one commit per successful iteration. Failed/discarded iterations fully reverted to the appropriate target commit.

### 🔄 Refactoring
- **Domain model**: Added `gitStartCommitSha` (loop), `gitCommitSha` and `preIterationCommitSha` (iteration). Old fields kept for migration safety.
- **Git utilities**: Added `getCurrentCommitSha()`, `commitAllChanges()`, `resetToCommit()` with SHA validation.

### 🗄️ Database
- **Migration 12**: Adds `git_start_commit_sha` to loops, `git_commit_sha` and `pre_iteration_commit_sha` to loop_iterations.

---

## [0.8.0] - 2026-03-25

### 🚀 Features
- **Loop Pause/Resume**: Pause active loops mid-iteration, resume from last checkpoint. Paused state persists across restart. MCP: `PauseLoop`, `ResumeLoop`. CLI: `beat loop pause`, `beat loop resume`
- **Scheduled Loops**: Compose loops with cron/one-time schedules. Each execution creates a new loop instance. MCP: `ScheduleLoop`. CLI: `beat schedule create --loop`
- **Git Integration**: Optional `--git-branch` for branch-per-iteration strategy. Diffs tracked between iterations. Automatic cleanup on completion
- **Git Integration**: Optional `--git-branch` for git-aware loop iteration tracking. Diffs tracked between iterations. (Corrected in v0.8.1)

### 🖥️ CLI Improvements
- `--direction minimize|maximize` → `--minimize`/`--maximize` boolean flags (mutual exclusion validated)
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,8 @@ Checkpoints are captured automatically on task completion/failure, preserving th

| Tool | Role | What It Does |
|------|------|-------------|
| **[Skim](https://github.com/dean0x/skim)** | Context Optimization | Compresses code, test output, build output, and git output for optimal LLM reasoning |
| **[DevFlow](https://github.com/dean0x/devflow)** | Quality Orchestration | 18 parallel reviewers, working memory, self-learning, production-grade lifecycle workflows |
| **[Skim](https://github.com/dean0x/skim)** | Context Optimization | Code-aware AST parsing across 12 languages, command rewriting, test/build/git output compression |
| **[DevFlow](https://github.com/dean0x/devflow)** | Quality Orchestration | 18 parallel reviewers, working memory, self-learning, composable plugin system |
| **Backbeat** | Agent Orchestration | Orchestration at scale. Karpathy optimization loops, multi-agent pipelines, DAG dependencies, autoscaling |

Backbeat scales execution. DevFlow enforces quality. Skim optimizes context. No other stack covers all three.
Expand Down
59 changes: 23 additions & 36 deletions docs/ROADMAP.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Backbeat Development Roadmap

## Current Status: v0.7.0 ✅
## Current Status: v0.8.0 ✅

**Status**: Released (2026-03-21)
**Status**: Released (2026-03-25)

Backbeat v0.7.0 adds task/pipeline loops — condition-driven iteration with retry and optimize strategies. See [FEATURES.md](./FEATURES.md) for complete list of current capabilities.
Backbeat v0.8.0 adds loop lifecycle control (pause/resume), scheduled loops, and git integration for loops. See [FEATURES.md](./FEATURES.md) for complete list of current capabilities.

---

Expand Down Expand Up @@ -110,43 +110,29 @@ Condition-driven iteration — repeat a task or pipeline until an exit condition

---

### v0.8.0 - Loop Enhancements
**Goal**: Loop lifecycle control, schedule composition, and git-aware iterations
**Priority**: High — completes the loop story started in v0.7.0
### v0.8.0 - Loop Enhancements ✅
**Status**: **RELEASED** (2026-03-25)

Loop lifecycle control (pause/resume), scheduled loops, and git integration for loops.

#### Features
- **Loop + Schedule Composition**: "Every night, loop until spec is done" — composable loops with cron/one-time schedules. A schedule trigger creates a loop instance per execution.
- **Loop Pause/Resume**: Pause an active loop mid-iteration and resume it later. Paused loops retain iteration state and checkpoint.
- **Git Integration for Loops**: Loop-aware git state management — branch per iteration, diff tracking between iterations, automatic branch cleanup on loop completion.
- **Loop + Schedule Composition**: Composable loops with cron/one-time schedules. Each trigger creates a new loop instance.
- **Loop Pause/Resume**: Pause active loops mid-iteration, resume from last checkpoint.
- **Git Integration**: Optional `--git-branch` for per-iteration git tracking with diff summaries.

#### CLI
```bash
# Schedule a loop
beat schedule create --cron "0 9 * * *" --loop \
--prompt "implement next item from spec.md" \
--exit-condition "./check-spec-complete.sh" \
--max-iterations 10

# Pause/resume
beat loop pause <loop-id>
beat loop resume <loop-id>

# Git integration
beat loop create "refactor auth module" \
--exit-condition "./tests-pass.sh" \
--git-branch-per-iteration \
--base-branch main
```
See [RELEASE_NOTES_v0.8.0.md](./releases/RELEASE_NOTES_v0.8.0.md) for full details.

#### MCP Tools
- `ScheduleLoop` — create a scheduled loop (cron/one-time trigger → loop)
- `PauseLoop` / `ResumeLoop` — lifecycle control
- `CreateLoop` updated — optional `gitConfig` parameter for branch-per-iteration
---

#### Builds On
- v0.4.0 scheduling (cron/one-time), checkpoints
- v0.6.0 scheduled pipelines pattern
- v0.7.0 task/pipeline loops
### v0.8.1 - Git Integration Fix
**Goal**: Fix git integration design — replace branch-per-iteration with commit-per-iteration
**Priority**: High — corrects v0.8.0 git behavior

#### Changes
- **Commit-per-iteration**: One branch for the entire loop, one commit per successful iteration
- **Revert on failure**: Failed/discarded iterations fully reverted to last good commit
- **Domain model**: `gitBaseBranch` → `gitStartCommitSha`, `gitBranch` on iteration → `gitCommitSha`
- **Database**: Migration 12 adds new columns; old columns kept (dead, harmless)

---

Expand Down Expand Up @@ -281,7 +267,8 @@ beat recipe create my-workflow # interactive recipe builder
| v0.5.0 | ✅ Released | Multi-Agent Support |
| v0.6.0 | ✅ Released | Architectural Simplification + Bug Fixes |
| v0.7.0 | ✅ Released | Task/Pipeline Loops |
| v0.8.0 | 📋 Planned | Loop Enhancements |
| v0.8.0 | ✅ Released | Loop Enhancements |
| v0.8.1 | 🔧 In Progress | Git Integration Fix |
| v0.9.0 | 📋 Planned | Agent Failover + Smart Routing |
| v0.10.0 | 📋 Planned | Workflow Recipes & Templates |
| v0.11.0 | 💭 Research | Monitoring + REST API + Dashboard |
Expand Down
6 changes: 3 additions & 3 deletions docs/releases/RELEASE_NOTES_v0.8.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ Compose loops with cron or one-time schedules:

### Git Integration (PR #115)

Optional branch-per-iteration strategy for loops:
Optional git-aware loop iteration tracking:

- **`--git-branch`**: Creates an isolated branch for each iteration
- **`--git-branch`**: Creates a branch for the loop and tracks changes per iteration
- **Diff Tracking**: Diffs automatically tracked between iterations
- **Automatic Cleanup**: Branches cleaned up on loop completion
- **Note**: v0.8.1 corrects the git integration design from branch-per-iteration to commit-per-iteration for correctness

---

Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "backbeat",
"version": "0.8.0",
"version": "0.8.1",
"main": "dist/index.js",
"bin": {
"beat": "./dist/cli.js"
Expand Down
3 changes: 3 additions & 0 deletions src/adapters/mcp-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2071,6 +2071,7 @@ export class MCPAdapter {
workingDirectory: loop.workingDirectory,
gitBranch: loop.gitBranch ?? null,
gitBaseBranch: loop.gitBaseBranch ?? null,
gitStartCommitSha: loop.gitStartCommitSha ?? null,
scheduleId: loop.scheduleId ?? null,
createdAt: new Date(loop.createdAt).toISOString(),
updatedAt: new Date(loop.updatedAt).toISOString(),
Expand All @@ -2096,6 +2097,8 @@ export class MCPAdapter {
exitCode: iter.exitCode ?? null,
errorMessage: iter.errorMessage ?? null,
gitBranch: iter.gitBranch ?? null,
gitCommitSha: iter.gitCommitSha ?? null,
preIterationCommitSha: iter.preIterationCommitSha ?? null,
gitDiffSummary: iter.gitDiffSummary ?? null,
startedAt: new Date(iter.startedAt).toISOString(),
completedAt: iter.completedAt ? new Date(iter.completedAt).toISOString() : null,
Expand Down
4 changes: 2 additions & 2 deletions src/cli/commands/loop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,7 @@ async function handleLoopStatus(loopArgs: string[]): Promise<void> {
lines.push(`Fresh Context: ${loop.freshContext}`);
lines.push(`Working Dir: ${loop.workingDirectory}`);
if (loop.gitBranch) lines.push(`Git Branch: ${loop.gitBranch}`);
if (loop.gitBaseBranch) lines.push(`Git Base: ${loop.gitBaseBranch}`);
if (loop.gitStartCommitSha) lines.push(`Git Start: ${loop.gitStartCommitSha.slice(0, 8)}`);
if (loop.scheduleId) lines.push(`Schedule: ${loop.scheduleId}`);
lines.push(`Created: ${new Date(loop.createdAt).toISOString()}`);
if (loop.completedAt) lines.push(`Completed: ${new Date(loop.completedAt).toISOString()}`);
Expand Down Expand Up @@ -415,7 +415,7 @@ async function handleLoopStatus(loopArgs: string[]): Promise<void> {
const score = iter.score !== undefined ? ` | score: ${iter.score}` : '';
const task = iter.taskId ? ` | task: ${iter.taskId}` : ' | task: cleaned up';
const error = iter.errorMessage ? ` | error: ${iter.errorMessage}` : '';
const git = iter.gitBranch ? ` | branch: ${iter.gitBranch}` : '';
const git = iter.gitCommitSha ? ` | commit: ${iter.gitCommitSha.slice(0, 8)}` : '';
process.stderr.write(
` #${iter.iterationNumber} ${ui.colorStatus(iter.status)}${score}${task}${error}${git}\n`,
);
Expand Down
8 changes: 6 additions & 2 deletions src/core/domain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,8 @@ export interface Loop {
readonly consecutiveFailures: number;
readonly status: LoopStatus;
readonly gitBranch?: string; // Branch name for loop iteration work (v0.8.0)
readonly gitBaseBranch?: string; // Base branch to diff against (v0.8.0)
readonly gitBaseBranch?: string; // Base branch to diff against (v0.8.0, dead after v0.8.1)
readonly gitStartCommitSha?: string; // Commit SHA at loop creation for revert target (v0.8.1)
readonly scheduleId?: ScheduleId; // Owning schedule if created via scheduled loop (v0.8.0)
readonly createdAt: number;
readonly updatedAt: number;
Expand All @@ -563,7 +564,9 @@ export interface LoopIteration {
readonly score?: number;
readonly exitCode?: number;
readonly errorMessage?: string;
readonly gitBranch?: string; // Branch used for this iteration (v0.8.0)
readonly gitBranch?: string; // Branch used for this iteration (v0.8.0, dead after v0.8.1)
readonly gitCommitSha?: string; // Commit SHA after iteration changes committed (v0.8.1)
readonly preIterationCommitSha?: string; // Commit SHA before iteration started (v0.8.1)
readonly gitDiffSummary?: string; // Git diff --stat summary of iteration changes (v0.8.0)
readonly startedAt: number;
readonly completedAt?: number;
Expand Down Expand Up @@ -622,6 +625,7 @@ export const createLoop = (request: LoopCreateRequest, workingDirectory: string,
status: LoopStatus.RUNNING,
gitBranch: request.gitBranch,
gitBaseBranch: undefined,
gitStartCommitSha: undefined,
scheduleId,
createdAt: now,
updatedAt: now,
Expand Down
15 changes: 15 additions & 0 deletions src/implementations/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,21 @@ export class Database implements TransactionRunner {
db.exec(`ALTER TABLE schedule_executions ADD COLUMN loop_id TEXT`);
},
},
{
version: 12,
description: 'Add commit-per-iteration git columns to loops and loop_iterations (v0.8.1)',
up: (db) => {
// New column on loops: captures HEAD SHA at loop creation
db.exec(`ALTER TABLE loops ADD COLUMN git_start_commit_sha TEXT`);

// New columns on loop_iterations: commit SHA after changes, pre-iteration snapshot
db.exec(`ALTER TABLE loop_iterations ADD COLUMN git_commit_sha TEXT`);
db.exec(`ALTER TABLE loop_iterations ADD COLUMN pre_iteration_commit_sha TEXT`);

// Old columns (git_base_branch on loops, git_branch on iterations) kept —
// SQLite cannot DROP COLUMN easily and dead columns are harmless
},
},
];
}

Expand Down
29 changes: 25 additions & 4 deletions src/implementations/loop-repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ const LoopRowSchema = z.object({
completed_at: z.number().nullable(),
git_branch: z.string().nullable(),
git_base_branch: z.string().nullable(),
git_start_commit_sha: z.string().nullable(),
schedule_id: z.string().nullable(),
});

Expand All @@ -69,6 +70,8 @@ const LoopIterationRowSchema = z.object({
started_at: z.number(),
completed_at: z.number().nullable(),
git_branch: z.string().nullable(),
git_commit_sha: z.string().nullable(),
pre_iteration_commit_sha: z.string().nullable(),
git_diff_summary: z.string().nullable(),
});

Expand Down Expand Up @@ -128,6 +131,7 @@ interface LoopRow {
readonly completed_at: number | null;
readonly git_branch: string | null;
readonly git_base_branch: string | null;
readonly git_start_commit_sha: string | null;
readonly schedule_id: string | null;
}

Expand All @@ -144,6 +148,8 @@ interface LoopIterationRow {
readonly started_at: number;
readonly completed_at: number | null;
readonly git_branch: string | null;
readonly git_commit_sha: string | null;
readonly pre_iteration_commit_sha: string | null;
readonly git_diff_summary: string | null;
}

Expand Down Expand Up @@ -177,14 +183,14 @@ export class SQLiteLoopRepository implements LoopRepository, SyncLoopOperations
max_consecutive_failures, cooldown_ms, fresh_context, status,
current_iteration, best_score, best_iteration_id, consecutive_failures,
created_at, updated_at, completed_at,
git_branch, git_base_branch, schedule_id
git_branch, git_base_branch, git_start_commit_sha, schedule_id
) VALUES (
@id, @strategy, @taskTemplate, @pipelineSteps, @exitCondition,
@evalDirection, @evalTimeout, @workingDirectory, @maxIterations,
@maxConsecutiveFailures, @cooldownMs, @freshContext, @status,
@currentIteration, @bestScore, @bestIterationId, @consecutiveFailures,
@createdAt, @updatedAt, @completedAt,
@gitBranch, @gitBaseBranch, @scheduleId
@gitBranch, @gitBaseBranch, @gitStartCommitSha, @scheduleId
)
`);

Expand All @@ -210,6 +216,7 @@ export class SQLiteLoopRepository implements LoopRepository, SyncLoopOperations
completed_at = @completedAt,
git_branch = @gitBranch,
git_base_branch = @gitBaseBranch,
git_start_commit_sha = @gitStartCommitSha,
schedule_id = @scheduleId
WHERE id = @id
`);
Expand Down Expand Up @@ -238,8 +245,8 @@ export class SQLiteLoopRepository implements LoopRepository, SyncLoopOperations
INSERT INTO loop_iterations (
loop_id, iteration_number, task_id, pipeline_task_ids,
status, score, exit_code, error_message, started_at, completed_at,
git_branch, git_diff_summary
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
git_branch, git_commit_sha, pre_iteration_commit_sha, git_diff_summary
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);

this.updateIterationStmt = this.db.prepare(`
Expand All @@ -250,6 +257,8 @@ export class SQLiteLoopRepository implements LoopRepository, SyncLoopOperations
error_message = @errorMessage,
completed_at = @completedAt,
git_branch = @gitBranch,
git_commit_sha = @gitCommitSha,
pre_iteration_commit_sha = @preIterationCommitSha,
git_diff_summary = @gitDiffSummary
WHERE id = @id
`);
Expand Down Expand Up @@ -390,6 +399,8 @@ export class SQLiteLoopRepository implements LoopRepository, SyncLoopOperations
iteration.startedAt,
iteration.completedAt ?? null,
iteration.gitBranch ?? null,
iteration.gitCommitSha ?? null,
iteration.preIterationCommitSha ?? null,
iteration.gitDiffSummary ?? null,
);
},
Expand Down Expand Up @@ -441,6 +452,8 @@ export class SQLiteLoopRepository implements LoopRepository, SyncLoopOperations
errorMessage: iteration.errorMessage ?? null,
completedAt: iteration.completedAt ?? null,
gitBranch: iteration.gitBranch ?? null,
gitCommitSha: iteration.gitCommitSha ?? null,
preIterationCommitSha: iteration.preIterationCommitSha ?? null,
gitDiffSummary: iteration.gitDiffSummary ?? null,
});
},
Expand Down Expand Up @@ -473,6 +486,8 @@ export class SQLiteLoopRepository implements LoopRepository, SyncLoopOperations
iteration.startedAt,
iteration.completedAt ?? null,
iteration.gitBranch ?? null,
iteration.gitCommitSha ?? null,
iteration.preIterationCommitSha ?? null,
iteration.gitDiffSummary ?? null,
);
}
Expand All @@ -492,6 +507,8 @@ export class SQLiteLoopRepository implements LoopRepository, SyncLoopOperations
errorMessage: iteration.errorMessage ?? null,
completedAt: iteration.completedAt ?? null,
gitBranch: iteration.gitBranch ?? null,
gitCommitSha: iteration.gitCommitSha ?? null,
preIterationCommitSha: iteration.preIterationCommitSha ?? null,
gitDiffSummary: iteration.gitDiffSummary ?? null,
});
}
Expand Down Expand Up @@ -529,6 +546,7 @@ export class SQLiteLoopRepository implements LoopRepository, SyncLoopOperations
completedAt: loop.completedAt ?? null,
gitBranch: loop.gitBranch ?? null,
gitBaseBranch: loop.gitBaseBranch ?? null,
gitStartCommitSha: loop.gitStartCommitSha ?? null,
scheduleId: loop.scheduleId ?? null,
};
}
Expand Down Expand Up @@ -581,6 +599,7 @@ export class SQLiteLoopRepository implements LoopRepository, SyncLoopOperations
consecutiveFailures: data.consecutive_failures,
gitBranch: data.git_branch ?? undefined,
gitBaseBranch: data.git_base_branch ?? undefined,
gitStartCommitSha: data.git_start_commit_sha ?? undefined,
scheduleId: data.schedule_id ? ScheduleId(data.schedule_id) : undefined,
createdAt: data.created_at,
updatedAt: data.updated_at,
Expand Down Expand Up @@ -619,6 +638,8 @@ export class SQLiteLoopRepository implements LoopRepository, SyncLoopOperations
exitCode: data.exit_code ?? undefined,
errorMessage: data.error_message ?? undefined,
gitBranch: data.git_branch ?? undefined,
gitCommitSha: data.git_commit_sha ?? undefined,
preIterationCommitSha: data.pre_iteration_commit_sha ?? undefined,
gitDiffSummary: data.git_diff_summary ?? undefined,
startedAt: data.started_at,
completedAt: data.completed_at ?? undefined,
Expand Down
Loading
Loading