add skills

This commit is contained in:
YeonGyu-Kim
2026-03-14 12:45:53 +09:00
parent 0dcfcd372b
commit c7518eae2d
81 changed files with 19037 additions and 0 deletions
@@ -0,0 +1,15 @@
{
"run_id": "eval-1-with_skill",
"expectations": [
{"text": "Plan uses git worktree in a sibling directory", "passed": true, "evidence": "Uses ../omo-wt/feat-max-background-agents"},
{"text": "Branch is created from origin/dev", "passed": true, "evidence": "git checkout dev && git pull origin dev, then branch"},
{"text": "Plan specifies multiple atomic commits for multi-file changes", "passed": true, "evidence": "2 commits: schema+tests, then concurrency+manager"},
{"text": "Runs bun run typecheck, bun test, and bun run build before pushing", "passed": true, "evidence": "Explicit pre-push section with all 3 commands"},
{"text": "PR is created targeting dev branch", "passed": true, "evidence": "--base dev in gh pr create"},
{"text": "Verification loop includes all 3 gates: CI, review-work, and Cubic", "passed": true, "evidence": "Gate A (CI), Gate B (review-work 5 agents), Gate C (Cubic)"},
{"text": "Gates are checked in order: CI first, then review-work, then Cubic", "passed": true, "evidence": "Explicit ordering in verify loop pseudocode"},
{"text": "Cubic check uses gh api to check cubic-dev-ai[bot] reviews", "passed": true, "evidence": "Mentions cubic-dev-ai[bot] and 'No issues found' signal"},
{"text": "Plan includes worktree cleanup after merge", "passed": true, "evidence": "Phase 4: git worktree remove ../omo-wt/feat-max-background-agents"},
{"text": "Code changes reference actual files in the codebase", "passed": true, "evidence": "References src/config/schema/background-task.ts, src/features/background-agent/concurrency.ts, manager.ts"}
]
}
@@ -0,0 +1,454 @@
# Code Changes: `max_background_agents` Config Option
## 1. `src/config/schema/background-task.ts` — Add schema field
```typescript
import { z } from "zod"
export const BackgroundTaskConfigSchema = z.object({
defaultConcurrency: z.number().min(1).optional(),
providerConcurrency: z.record(z.string(), z.number().min(0)).optional(),
modelConcurrency: z.record(z.string(), z.number().min(0)).optional(),
maxDepth: z.number().int().min(1).optional(),
maxDescendants: z.number().int().min(1).optional(),
/** Maximum number of background agents that can run simultaneously across all models/providers (default: 5, minimum: 1) */
maxBackgroundAgents: z.number().int().min(1).optional(),
/** Stale timeout in milliseconds - interrupt tasks with no activity for this duration (default: 180000 = 3 minutes, minimum: 60000 = 1 minute) */
staleTimeoutMs: z.number().min(60000).optional(),
/** Timeout for tasks that never received any progress update, falling back to startedAt (default: 1800000 = 30 minutes, minimum: 60000 = 1 minute) */
messageStalenessTimeoutMs: z.number().min(60000).optional(),
syncPollTimeoutMs: z.number().min(60000).optional(),
})
export type BackgroundTaskConfig = z.infer<typeof BackgroundTaskConfigSchema>
```
**Rationale:** Follows exact same pattern as `maxDepth` and `maxDescendants``z.number().int().min(1).optional()`. The field is optional; runtime default of 5 is applied in `ConcurrencyManager`. No barrel export changes needed since `src/config/schema.ts` already does `export * from "./schema/background-task"` and the type is inferred.
---
## 2. `src/config/schema/background-task.test.ts` — Add validation tests
Append after the existing `syncPollTimeoutMs` describe block (before the closing `})`):
```typescript
describe("maxBackgroundAgents", () => {
describe("#given valid maxBackgroundAgents (10)", () => {
test("#when parsed #then returns correct value", () => {
const result = BackgroundTaskConfigSchema.parse({ maxBackgroundAgents: 10 })
expect(result.maxBackgroundAgents).toBe(10)
})
})
describe("#given maxBackgroundAgents of 1 (minimum)", () => {
test("#when parsed #then returns correct value", () => {
const result = BackgroundTaskConfigSchema.parse({ maxBackgroundAgents: 1 })
expect(result.maxBackgroundAgents).toBe(1)
})
})
describe("#given maxBackgroundAgents below minimum (0)", () => {
test("#when parsed #then throws ZodError", () => {
let thrownError: unknown
try {
BackgroundTaskConfigSchema.parse({ maxBackgroundAgents: 0 })
} catch (error) {
thrownError = error
}
expect(thrownError).toBeInstanceOf(ZodError)
})
})
describe("#given maxBackgroundAgents not provided", () => {
test("#when parsed #then field is undefined", () => {
const result = BackgroundTaskConfigSchema.parse({})
expect(result.maxBackgroundAgents).toBeUndefined()
})
})
describe('#given maxBackgroundAgents is non-integer (2.5)', () => {
test("#when parsed #then throws ZodError", () => {
let thrownError: unknown
try {
BackgroundTaskConfigSchema.parse({ maxBackgroundAgents: 2.5 })
} catch (error) {
thrownError = error
}
expect(thrownError).toBeInstanceOf(ZodError)
})
})
})
```
**Rationale:** Follows exact test pattern from `maxDepth`, `maxDescendants`, and `syncPollTimeoutMs` tests. Uses `#given`/`#when`/`#then` nested describe style. Tests valid, minimum boundary, below minimum, not provided, and non-integer cases.
---
## 3. `src/features/background-agent/concurrency.ts` — Add global agent limit
```typescript
import type { BackgroundTaskConfig } from "../../config/schema"
const DEFAULT_MAX_BACKGROUND_AGENTS = 5
/**
* Queue entry with settled-flag pattern to prevent double-resolution.
*
* The settled flag ensures that cancelWaiters() doesn't reject
* an entry that was already resolved by release().
*/
interface QueueEntry {
resolve: () => void
rawReject: (error: Error) => void
settled: boolean
}
export class ConcurrencyManager {
private config?: BackgroundTaskConfig
private counts: Map<string, number> = new Map()
private queues: Map<string, QueueEntry[]> = new Map()
private globalRunningCount = 0
constructor(config?: BackgroundTaskConfig) {
this.config = config
}
getMaxBackgroundAgents(): number {
return this.config?.maxBackgroundAgents ?? DEFAULT_MAX_BACKGROUND_AGENTS
}
getGlobalRunningCount(): number {
return this.globalRunningCount
}
canSpawnGlobally(): boolean {
return this.globalRunningCount < this.getMaxBackgroundAgents()
}
acquireGlobal(): void {
this.globalRunningCount++
}
releaseGlobal(): void {
if (this.globalRunningCount > 0) {
this.globalRunningCount--
}
}
getConcurrencyLimit(model: string): number {
// ... existing implementation unchanged ...
}
async acquire(model: string): Promise<void> {
// ... existing implementation unchanged ...
}
release(model: string): void {
// ... existing implementation unchanged ...
}
cancelWaiters(model: string): void {
// ... existing implementation unchanged ...
}
clear(): void {
for (const [model] of this.queues) {
this.cancelWaiters(model)
}
this.counts.clear()
this.queues.clear()
this.globalRunningCount = 0
}
getCount(model: string): number {
return this.counts.get(model) ?? 0
}
getQueueLength(model: string): number {
return this.queues.get(model)?.length ?? 0
}
}
```
**Key changes:**
- Add `DEFAULT_MAX_BACKGROUND_AGENTS = 5` constant
- Add `globalRunningCount` private field
- Add `getMaxBackgroundAgents()`, `getGlobalRunningCount()`, `canSpawnGlobally()`, `acquireGlobal()`, `releaseGlobal()` methods
- `clear()` resets `globalRunningCount` to 0
- All existing per-model methods remain unchanged
---
## 4. `src/features/background-agent/concurrency.test.ts` — Add global limit tests
Append new describe block:
```typescript
describe("ConcurrencyManager global background agent limit", () => {
test("should default max background agents to 5 when no config", () => {
// given
const manager = new ConcurrencyManager()
// when
const max = manager.getMaxBackgroundAgents()
// then
expect(max).toBe(5)
})
test("should use configured maxBackgroundAgents", () => {
// given
const config: BackgroundTaskConfig = { maxBackgroundAgents: 10 }
const manager = new ConcurrencyManager(config)
// when
const max = manager.getMaxBackgroundAgents()
// then
expect(max).toBe(10)
})
test("should allow spawning when under global limit", () => {
// given
const config: BackgroundTaskConfig = { maxBackgroundAgents: 2 }
const manager = new ConcurrencyManager(config)
// when
manager.acquireGlobal()
// then
expect(manager.canSpawnGlobally()).toBe(true)
expect(manager.getGlobalRunningCount()).toBe(1)
})
test("should block spawning when at global limit", () => {
// given
const config: BackgroundTaskConfig = { maxBackgroundAgents: 2 }
const manager = new ConcurrencyManager(config)
// when
manager.acquireGlobal()
manager.acquireGlobal()
// then
expect(manager.canSpawnGlobally()).toBe(false)
expect(manager.getGlobalRunningCount()).toBe(2)
})
test("should allow spawning again after release", () => {
// given
const config: BackgroundTaskConfig = { maxBackgroundAgents: 1 }
const manager = new ConcurrencyManager(config)
manager.acquireGlobal()
// when
manager.releaseGlobal()
// then
expect(manager.canSpawnGlobally()).toBe(true)
expect(manager.getGlobalRunningCount()).toBe(0)
})
test("should not go below zero on extra release", () => {
// given
const manager = new ConcurrencyManager()
// when
manager.releaseGlobal()
// then
expect(manager.getGlobalRunningCount()).toBe(0)
})
test("should reset global count on clear", () => {
// given
const config: BackgroundTaskConfig = { maxBackgroundAgents: 5 }
const manager = new ConcurrencyManager(config)
manager.acquireGlobal()
manager.acquireGlobal()
manager.acquireGlobal()
// when
manager.clear()
// then
expect(manager.getGlobalRunningCount()).toBe(0)
})
})
```
---
## 5. `src/features/background-agent/manager.ts` — Enforce global limit
### In `launch()` method — add check before task creation (after `reserveSubagentSpawn`):
```typescript
async launch(input: LaunchInput): Promise<BackgroundTask> {
// ... existing logging ...
if (!input.agent || input.agent.trim() === "") {
throw new Error("Agent parameter is required")
}
// Check global background agent limit before spawn guard
if (!this.concurrencyManager.canSpawnGlobally()) {
const max = this.concurrencyManager.getMaxBackgroundAgents()
const current = this.concurrencyManager.getGlobalRunningCount()
throw new Error(
`Background agent spawn blocked: ${current} agents running, max is ${max}. Wait for existing tasks to complete or increase background_task.maxBackgroundAgents.`
)
}
const spawnReservation = await this.reserveSubagentSpawn(input.parentSessionID)
try {
// ... existing code ...
// After task creation, before queueing:
this.concurrencyManager.acquireGlobal()
// ... rest of existing code ...
} catch (error) {
spawnReservation.rollback()
throw error
}
}
```
### In `trackTask()` method — add global check:
```typescript
async trackTask(input: { ... }): Promise<BackgroundTask> {
const existingTask = this.tasks.get(input.taskId)
if (existingTask) {
// ... existing re-registration logic unchanged ...
return existingTask
}
// Check global limit for new external tasks
if (!this.concurrencyManager.canSpawnGlobally()) {
const max = this.concurrencyManager.getMaxBackgroundAgents()
const current = this.concurrencyManager.getGlobalRunningCount()
throw new Error(
`Background agent spawn blocked: ${current} agents running, max is ${max}. Wait for existing tasks to complete or increase background_task.maxBackgroundAgents.`
)
}
// ... existing task creation ...
this.concurrencyManager.acquireGlobal()
// ... rest unchanged ...
}
```
### In `tryCompleteTask()` — release global slot:
```typescript
private async tryCompleteTask(task: BackgroundTask, source: string): Promise<boolean> {
if (task.status !== "running") {
// ... existing guard ...
return false
}
task.status = "completed"
task.completedAt = new Date()
// ... existing history record ...
removeTaskToastTracking(task.id)
// Release per-model concurrency
if (task.concurrencyKey) {
this.concurrencyManager.release(task.concurrencyKey)
task.concurrencyKey = undefined
}
// Release global slot
this.concurrencyManager.releaseGlobal()
// ... rest unchanged ...
}
```
### In `cancelTask()` — release global slot:
```typescript
async cancelTask(taskId: string, options?: { ... }): Promise<boolean> {
// ... existing code up to concurrency release ...
if (task.concurrencyKey) {
this.concurrencyManager.release(task.concurrencyKey)
task.concurrencyKey = undefined
}
// Release global slot (only for running tasks, pending never acquired)
if (task.status !== "pending") {
this.concurrencyManager.releaseGlobal()
}
// ... rest unchanged ...
}
```
### In `handleEvent()` session.error handler — release global slot:
```typescript
if (event.type === "session.error") {
// ... existing error handling ...
task.status = "error"
// ...
if (task.concurrencyKey) {
this.concurrencyManager.release(task.concurrencyKey)
task.concurrencyKey = undefined
}
// Release global slot
this.concurrencyManager.releaseGlobal()
// ... rest unchanged ...
}
```
### In prompt error handler inside `startTask()` — release global slot:
```typescript
promptWithModelSuggestionRetry(this.client, { ... }).catch((error) => {
// ... existing error handling ...
if (existingTask) {
existingTask.status = "interrupt"
// ...
if (existingTask.concurrencyKey) {
this.concurrencyManager.release(existingTask.concurrencyKey)
existingTask.concurrencyKey = undefined
}
// Release global slot
this.concurrencyManager.releaseGlobal()
// ... rest unchanged ...
}
})
```
---
## Summary of Changes
| File | Lines Added | Lines Modified |
|------|-------------|----------------|
| `src/config/schema/background-task.ts` | 2 | 0 |
| `src/config/schema/background-task.test.ts` | ~50 | 0 |
| `src/features/background-agent/concurrency.ts` | ~25 | 1 (`clear()`) |
| `src/features/background-agent/concurrency.test.ts` | ~70 | 0 |
| `src/features/background-agent/manager.ts` | ~20 | 0 |
Total: ~167 lines added, 1 line modified across 5 files.
@@ -0,0 +1,136 @@
# Execution Plan: `max_background_agents` Config Option
## Phase 0: Setup — Branch + Worktree
1. **Create branch** from `dev`:
```bash
git checkout dev && git pull origin dev
git checkout -b feat/max-background-agents
```
2. **Create worktree** in sibling directory:
```bash
mkdir -p ../omo-wt
git worktree add ../omo-wt/feat-max-background-agents feat/max-background-agents
```
3. **All subsequent work** happens in `../omo-wt/feat-max-background-agents/`, never in the main worktree.
---
## Phase 1: Implement — Atomic Commits
### Commit 1: Add `max_background_agents` to config schema
**Files changed:**
- `src/config/schema/background-task.ts` — Add `maxBackgroundAgents` field to `BackgroundTaskConfigSchema`
- `src/config/schema/background-task.test.ts` — Add validation tests for the new field
**What:**
- Add `maxBackgroundAgents: z.number().int().min(1).optional()` to `BackgroundTaskConfigSchema`
- Default value handled at runtime (5), not in schema (all schema fields are optional per convention)
- Add given/when/then tests: valid value, below minimum, not provided, non-number
### Commit 2: Enforce limit in BackgroundManager + ConcurrencyManager
**Files changed:**
- `src/features/background-agent/concurrency.ts` — Add global agent count tracking + `getGlobalRunningCount()` + `canSpawnGlobally()`
- `src/features/background-agent/concurrency.test.ts` — Tests for global limit enforcement
- `src/features/background-agent/manager.ts` — Check global limit before `launch()` and `trackTask()`
**What:**
- `ConcurrencyManager` already manages per-model concurrency. Add a separate global counter:
- `private globalRunningCount: number = 0`
- `private maxBackgroundAgents: number` (from config, default 5)
- `acquireGlobal()` / `releaseGlobal()` methods
- `getGlobalRunningCount()` for observability
- `BackgroundManager.launch()` checks `concurrencyManager.canSpawnGlobally()` before creating task
- `BackgroundManager.trackTask()` also checks global limit
- On task completion/cancellation/error, call `releaseGlobal()`
- Throw descriptive error when limit hit: `"Background agent spawn blocked: ${current} agents running, max is ${max}. Wait for existing tasks to complete or increase background_task.maxBackgroundAgents."`
### Local Validation
```bash
bun run typecheck
bun test src/config/schema/background-task.test.ts
bun test src/features/background-agent/concurrency.test.ts
bun run build
```
---
## Phase 2: PR Creation
1. **Push branch:**
```bash
git push -u origin feat/max-background-agents
```
2. **Create PR** targeting `dev`:
```bash
gh pr create \
--base dev \
--title "feat: add max_background_agents config to limit concurrent background agents" \
--body-file /tmp/pull-request-max-background-agents-$(date +%s).md
```
---
## Phase 3: Verify Loop
### Gate A: CI
- Wait for `ci.yml` workflow to complete
- Check: `gh pr checks <PR_NUMBER> --watch`
- If fails: read logs, fix, push, re-check
### Gate B: review-work (5 agents)
- Run `/review-work` skill which launches 5 parallel background sub-agents:
1. Oracle — goal/constraint verification
2. Oracle — code quality
3. Oracle — security
4. Hephaestus — hands-on QA execution
5. Hephaestus — context mining from GitHub/git
- All 5 must pass. If any fails, fix and re-push.
### Gate C: Cubic (cubic-dev-ai[bot])
- Wait for Cubic bot review on PR
- Must say "No issues found"
- If issues found: address feedback, push, re-check
### Loop
```
while (!allGatesPass) {
if (CI fails) → fix → push → continue
if (review-work fails) → fix → push → continue
if (Cubic has issues) → fix → push → continue
}
```
---
## Phase 4: Merge + Cleanup
1. **Squash merge:**
```bash
gh pr merge <PR_NUMBER> --squash --delete-branch
```
2. **Remove worktree:**
```bash
git worktree remove ../omo-wt/feat-max-background-agents
```
---
## File Impact Summary
| File | Change Type |
|------|-------------|
| `src/config/schema/background-task.ts` | Modified — add schema field |
| `src/config/schema/background-task.test.ts` | Modified — add validation tests |
| `src/features/background-agent/concurrency.ts` | Modified — add global limit tracking |
| `src/features/background-agent/concurrency.test.ts` | Modified — add global limit tests |
| `src/features/background-agent/manager.ts` | Modified — enforce global limit in launch/trackTask |
5 files changed across 2 atomic commits. No new files created (follows existing patterns).
@@ -0,0 +1,47 @@
# PR Description
**Title:** `feat: add max_background_agents config to limit concurrent background agents`
**Base:** `dev`
---
## Summary
- Add `maxBackgroundAgents` field to `BackgroundTaskConfigSchema` (default: 5, min: 1) to cap total simultaneous background agents across all models/providers
- Enforce the global limit in `BackgroundManager.launch()` and `trackTask()` with descriptive error messages when the limit is hit
- Release global slots on task completion, cancellation, error, and interrupt to prevent slot leaks
## Motivation
The existing concurrency system in `ConcurrencyManager` limits agents **per model/provider** (e.g., 5 concurrent `anthropic/claude-opus-4-6` tasks). However, there is no **global** cap across all models. A user running tasks across multiple providers could spawn an unbounded number of background agents, exhausting system resources.
`max_background_agents` provides a single knob to limit total concurrent background agents regardless of which model they use.
## Config Usage
```jsonc
// .opencode/oh-my-opencode.jsonc
{
"background_task": {
"maxBackgroundAgents": 10 // default: 5, min: 1
}
}
```
## Changes
| File | What |
|------|------|
| `src/config/schema/background-task.ts` | Add `maxBackgroundAgents` schema field |
| `src/config/schema/background-task.test.ts` | Validation tests (valid, boundary, invalid) |
| `src/features/background-agent/concurrency.ts` | Global counter + `canSpawnGlobally()` / `acquireGlobal()` / `releaseGlobal()` |
| `src/features/background-agent/concurrency.test.ts` | Global limit unit tests |
| `src/features/background-agent/manager.ts` | Enforce global limit in `launch()`, `trackTask()`; release in completion/cancel/error paths |
## Testing
- `bun test src/config/schema/background-task.test.ts` — schema validation
- `bun test src/features/background-agent/concurrency.test.ts` — global limit enforcement
- `bun run typecheck` — clean
- `bun run build` — clean
@@ -0,0 +1,163 @@
# Verification Strategy
## Pre-Push Local Validation
Before every push, run all three checks sequentially:
```bash
bun run typecheck && bun test && bun run build
```
Specific test files to watch:
```bash
bun test src/config/schema/background-task.test.ts
bun test src/features/background-agent/concurrency.test.ts
```
---
## Gate A: CI (`ci.yml`)
### What CI runs
1. **Tests (split):** mock-heavy tests run in isolation (separate `bun test` processes), rest in batch
2. **Typecheck:** `bun run typecheck` (tsc --noEmit)
3. **Build:** `bun run build` (ESM + declarations + schema)
4. **Schema auto-commit:** if generated schema changed, CI commits it
### How to monitor
```bash
gh pr checks <PR_NUMBER> --watch
```
### Common failure scenarios and fixes
| Failure | Likely Cause | Fix |
|---------|-------------|-----|
| Typecheck error | New field not matching existing type imports | Verify `BackgroundTaskConfig` type is auto-inferred from schema, no manual type updates needed |
| Test failure | Test assertion wrong or missing import | Fix test, re-push |
| Build failure | Import cycle or missing export | Check barrel exports in `src/config/schema.ts` (already re-exports via `export *`) |
| Schema auto-commit | Generated JSON schema changed | Pull the auto-commit, rebase if needed |
### Recovery
```bash
# Read CI logs
gh run view <RUN_ID> --log-failed
# Fix, commit, push
git add -A && git commit -m "fix: address CI failure" && git push
```
---
## Gate B: review-work (5 parallel agents)
### What it checks
Run `/review-work` which launches 5 background sub-agents:
| Agent | Role | What it checks for this PR |
|-------|------|---------------------------|
| Oracle (goal) | Goal/constraint verification | Does `maxBackgroundAgents` actually limit agents? Is default 5? Is min 1? |
| Oracle (quality) | Code quality | Follows existing patterns? No catch-all files? Under 200 LOC? given/when/then tests? |
| Oracle (security) | Security review | No injection vectors, no unsafe defaults, proper input validation via Zod |
| Hephaestus (QA) | Hands-on QA execution | Actually runs tests, checks typecheck, verifies build |
| Hephaestus (context) | Context mining | Checks git history, related issues, ensures no duplicate/conflicting PRs |
### Pass criteria
All 5 agents must pass. Any single failure blocks.
### Common failure scenarios and fixes
| Agent | Likely Issue | Fix |
|-------|-------------|-----|
| Oracle (goal) | Global limit not enforced in all exit paths (completion, cancel, error, interrupt) | Audit every status transition in `manager.ts` that should call `releaseGlobal()` |
| Oracle (quality) | Test style not matching given/when/then | Restructure tests with `#given`/`#when`/`#then` describe nesting |
| Oracle (quality) | File exceeds 200 LOC | `concurrency.ts` is 137 LOC + ~25 new = ~162 LOC, safe. `manager.ts` is already large but we're adding ~20 lines to existing methods, not creating new responsibility |
| Oracle (security) | Integer overflow or negative values | Zod `.int().min(1)` handles this at config parse time |
| Hephaestus (QA) | Test actually fails when run | Run tests locally first, fix before push |
### Recovery
```bash
# Review agent output
background_output(task_id="<review-work-task-id>")
# Fix identified issues
# ... edit files ...
git add -A && git commit -m "fix: address review-work feedback" && git push
```
---
## Gate C: Cubic (`cubic-dev-ai[bot]`)
### What it checks
Cubic is an automated code review bot that analyzes the PR diff. It must respond with "No issues found" for the gate to pass.
### Common failure scenarios and fixes
| Issue | Likely Cause | Fix |
|-------|-------------|-----|
| "Missing error handling" | `releaseGlobal()` not called in some error path | Add `releaseGlobal()` to the missed path |
| "Inconsistent naming" | Field name doesn't match convention | Use `maxBackgroundAgents` (camelCase in schema, `max_background_agents` in JSONC config) |
| "Missing documentation" | No JSDoc on new public methods | Add JSDoc comments to `canSpawnGlobally()`, `acquireGlobal()`, `releaseGlobal()`, `getMaxBackgroundAgents()` |
| "Test coverage gap" | Missing edge case test | Add the specific test case Cubic identifies |
### Recovery
```bash
# Read Cubic's review
gh api repos/code-yeongyu/oh-my-openagent/pulls/<PR_NUMBER>/reviews
# Address each comment
# ... edit files ...
git add -A && git commit -m "fix: address Cubic review feedback" && git push
```
---
## Verification Loop Pseudocode
```
iteration = 0
while true:
iteration++
log("Verification iteration ${iteration}")
# Gate A: CI (cheapest, check first)
push_and_wait_for_ci()
if ci_failed:
read_ci_logs()
fix_and_commit()
continue
# Gate B: review-work (5 agents, more expensive)
run_review_work()
if any_agent_failed:
read_agent_feedback()
fix_and_commit()
continue
# Gate C: Cubic (external bot, wait for it)
wait_for_cubic_review()
if cubic_has_issues:
read_cubic_comments()
fix_and_commit()
continue
# All gates passed
break
# Merge
gh pr merge <PR_NUMBER> --squash --delete-branch
```
No iteration cap. Loop continues until all three gates pass simultaneously in a single iteration.
---
## Risk Assessment
| Risk | Probability | Mitigation |
|------|------------|------------|
| Slot leak (global count never decremented) | Medium | Audit every exit path: `tryCompleteTask`, `cancelTask`, `handleEvent(session.error)`, `startTask` prompt error, `resume` prompt error |
| Race condition on global count | Low | `globalRunningCount` is synchronous (single-threaded JS), no async gap between check and increment in `launch()` |
| Breaking existing behavior | Low | Default is 5, same as existing per-model default. Users with <5 total agents see no change |
| `manager.ts` exceeding 200 LOC | Already exceeded | File is already ~1500 LOC (exempt due to being a core orchestration class with many methods). Our changes add ~20 lines to existing methods, not a new responsibility |
@@ -0,0 +1 @@
{"total_tokens": null, "duration_ms": 292000, "total_duration_seconds": 292}