ocak 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +27 -0
- data/lib/ocak/agent_generator.rb +2 -1
- data/lib/ocak/batch_processing.rb +102 -0
- data/lib/ocak/claude_runner.rb +12 -8
- data/lib/ocak/cli.rb +13 -0
- data/lib/ocak/command_runner.rb +39 -0
- data/lib/ocak/commands/hiz.rb +8 -7
- data/lib/ocak/commands/init.rb +5 -0
- data/lib/ocak/commands/issue/close.rb +37 -0
- data/lib/ocak/commands/issue/create.rb +59 -0
- data/lib/ocak/commands/issue/edit.rb +31 -0
- data/lib/ocak/commands/issue/list.rb +43 -0
- data/lib/ocak/commands/issue/view.rb +58 -0
- data/lib/ocak/commands/resume.rb +4 -3
- data/lib/ocak/commands/status.rb +20 -0
- data/lib/ocak/config.rb +4 -0
- data/lib/ocak/failure_reporting.rb +3 -2
- data/lib/ocak/git_utils.rb +18 -11
- data/lib/ocak/instance_builders.rb +50 -0
- data/lib/ocak/issue_backend.rb +31 -0
- data/lib/ocak/local_issue_fetcher.rb +165 -0
- data/lib/ocak/local_merge_manager.rb +104 -0
- data/lib/ocak/merge_manager.rb +31 -32
- data/lib/ocak/merge_orchestration.rb +8 -2
- data/lib/ocak/parallel_execution.rb +36 -0
- data/lib/ocak/pipeline_executor.rb +9 -183
- data/lib/ocak/pipeline_runner.rb +15 -180
- data/lib/ocak/planner.rb +1 -1
- data/lib/ocak/project_key.rb +38 -0
- data/lib/ocak/reready_processor.rb +11 -11
- data/lib/ocak/run_report.rb +5 -2
- data/lib/ocak/shutdown_handling.rb +67 -0
- data/lib/ocak/state_management.rb +104 -0
- data/lib/ocak/step_execution.rb +66 -0
- data/lib/ocak/templates/agents/auditor.md.erb +38 -9
- data/lib/ocak/templates/agents/implementer.md.erb +32 -8
- data/lib/ocak/templates/agents/merger.md.erb +12 -5
- data/lib/ocak/templates/agents/pipeline.md.erb +4 -0
- data/lib/ocak/templates/agents/reviewer.md.erb +2 -2
- data/lib/ocak/templates/agents/security_reviewer.md.erb +11 -0
- data/lib/ocak/templates/ocak.yml.erb +15 -0
- data/lib/ocak/verification.rb +6 -1
- data/lib/ocak/worktree_manager.rb +2 -0
- data/lib/ocak.rb +1 -1
- metadata +17 -1
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
|
|
5
|
+
module Ocak
|
|
6
|
+
# State accumulation and reporting logic extracted from PipelineExecutor.
|
|
7
|
+
# Includers must provide @config, @logger instance variables and pipeline_state, current_branch methods.
|
|
8
|
+
module StateManagement
|
|
9
|
+
StepContext = Struct.new(:issue_number, :idx, :role, :result, :state, :logger, :chdir)
|
|
10
|
+
|
|
11
|
+
def record_step_result(ctx, mutex: nil)
|
|
12
|
+
sync(mutex) { accumulate_state(ctx) }
|
|
13
|
+
save_step_progress(ctx)
|
|
14
|
+
write_step_output(ctx.issue_number, ctx.idx, ctx.role, ctx.result.output)
|
|
15
|
+
post_step_completion_comment(ctx.issue_number, ctx.role, ctx.result)
|
|
16
|
+
|
|
17
|
+
check_step_failure(ctx) || check_cost_budget(ctx.state, ctx.logger)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def accumulate_state(ctx)
|
|
21
|
+
update_pipeline_state(ctx.role, ctx.result, ctx.state)
|
|
22
|
+
ctx.state[:completed_steps] << ctx.idx
|
|
23
|
+
ctx.state[:steps_run] += 1
|
|
24
|
+
ctx.state[:total_cost] += ctx.result.cost_usd.to_f
|
|
25
|
+
ctx.state[:step_results][ctx.role] = ctx.result
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def sync(mutex, &)
|
|
29
|
+
if mutex
|
|
30
|
+
mutex.synchronize(&)
|
|
31
|
+
else
|
|
32
|
+
yield
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def save_step_progress(ctx)
|
|
37
|
+
pipeline_state.save(ctx.issue_number,
|
|
38
|
+
completed_steps: ctx.state[:completed_steps],
|
|
39
|
+
worktree_path: ctx.chdir,
|
|
40
|
+
branch: current_branch(ctx.chdir, logger: ctx.logger))
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def write_step_output(issue_number, idx, agent, output)
|
|
44
|
+
return if output.to_s.empty?
|
|
45
|
+
return unless issue_number.to_s.match?(/\A\d+\z/)
|
|
46
|
+
|
|
47
|
+
safe_agent = agent.to_s.gsub(/[^a-zA-Z0-9_-]/, '')
|
|
48
|
+
dir = File.join(@config.project_dir, '.ocak', 'logs', "issue-#{issue_number}")
|
|
49
|
+
FileUtils.mkdir_p(dir)
|
|
50
|
+
File.write(File.join(dir, "step-#{idx}-#{safe_agent}.md"), output)
|
|
51
|
+
rescue StandardError => e
|
|
52
|
+
@logger&.debug("Step output write failed: #{e.message}")
|
|
53
|
+
nil
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def check_step_failure(ctx)
|
|
57
|
+
return nil if ctx.result.success? || !%w[implement merge].include?(ctx.role)
|
|
58
|
+
|
|
59
|
+
ctx.logger.error("#{ctx.role} failed")
|
|
60
|
+
{ success: false, phase: ctx.role, output: ctx.result.output }
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def check_cost_budget(state, logger)
|
|
64
|
+
return nil unless @config.cost_budget && state[:total_cost] > @config.cost_budget
|
|
65
|
+
|
|
66
|
+
cost = format('%.2f', state[:total_cost])
|
|
67
|
+
budget = format('%.2f', @config.cost_budget)
|
|
68
|
+
logger.error("Cost budget exceeded ($#{cost}/$#{budget})")
|
|
69
|
+
{ success: false, phase: 'budget', output: "Cost budget exceeded: $#{cost}" }
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def update_pipeline_state(role, result, state)
|
|
73
|
+
case role
|
|
74
|
+
when 'review', 'verify', 'security', 'audit'
|
|
75
|
+
state[:last_review_output] = result.output
|
|
76
|
+
if role == 'audit'
|
|
77
|
+
state[:audit_output] = result.output
|
|
78
|
+
state[:audit_blocked] = !result.success? || result.output.to_s.match?(/BLOCK|🔴/)
|
|
79
|
+
end
|
|
80
|
+
when 'fix'
|
|
81
|
+
state[:had_fixes] = true
|
|
82
|
+
state[:last_review_output] = nil
|
|
83
|
+
when 'implement'
|
|
84
|
+
state[:last_review_output] = nil
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def log_cost_summary(total_cost, logger)
|
|
89
|
+
return if total_cost.zero?
|
|
90
|
+
|
|
91
|
+
budget = @config.cost_budget
|
|
92
|
+
budget_str = budget ? " / $#{format('%.2f', budget)} budget" : ''
|
|
93
|
+
logger.info("Pipeline cost: $#{format('%.4f', total_cost)}#{budget_str}")
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def save_report(report, issue_number, success:, failed_phase: nil)
|
|
97
|
+
report.finish(success: success, failed_phase: failed_phase)
|
|
98
|
+
report.save(issue_number, project_dir: @config.project_dir)
|
|
99
|
+
rescue StandardError => e
|
|
100
|
+
@logger&.debug("Report save failed: #{e.message}")
|
|
101
|
+
nil
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ocak
|
|
4
|
+
# Individual step execution logic extracted from PipelineExecutor.
|
|
5
|
+
# Includers must provide @config, @skip_steps, post_step_comment, build_step_prompt methods.
|
|
6
|
+
module StepExecution
|
|
7
|
+
def run_single_step(step, idx, issue_number, state, logger:, claude:, chdir:, mutex: nil) # rubocop:disable Metrics/ParameterLists
|
|
8
|
+
role = step[:role].to_s
|
|
9
|
+
agent = step[:agent].to_s
|
|
10
|
+
|
|
11
|
+
return nil if handle_already_completed(idx, role, @skip_steps, logger)
|
|
12
|
+
|
|
13
|
+
reason = skip_reason(step, state)
|
|
14
|
+
if reason
|
|
15
|
+
logger.info("Skipping #{role} — #{reason}")
|
|
16
|
+
record_skipped_step(issue_number, state, idx, agent, role, reason)
|
|
17
|
+
return nil
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
result = execute_step(step, issue_number, state[:last_review_output], logger: logger, claude: claude,
|
|
21
|
+
chdir: chdir)
|
|
22
|
+
state[:report].record_step(index: idx, agent: agent, role: role, status: 'completed', result: result)
|
|
23
|
+
ctx = StateManagement::StepContext.new(issue_number, idx, role, result, state, logger, chdir)
|
|
24
|
+
record_step_result(ctx, mutex: mutex)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def handle_already_completed(idx, role, skip_steps, logger)
|
|
28
|
+
return false unless skip_steps.include?(idx)
|
|
29
|
+
|
|
30
|
+
logger.info("Skipping #{role} (already completed)")
|
|
31
|
+
true
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def record_skipped_step(issue_number, state, idx, agent, role, reason)
|
|
35
|
+
post_step_comment(issue_number, "⏭️ **Skipping #{role}** — #{reason}")
|
|
36
|
+
state[:report].record_step(index: idx, agent: agent, role: role, status: 'skipped', skip_reason: reason)
|
|
37
|
+
state[:steps_skipped] += 1
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def execute_step(step, issue_number, review_output, logger:, claude:, chdir:)
|
|
41
|
+
agent = step[:agent].to_s
|
|
42
|
+
role = step[:role].to_s
|
|
43
|
+
logger.info("--- Phase: #{role} (#{agent}) ---")
|
|
44
|
+
post_step_comment(issue_number, "🔄 **Phase: #{role}** (#{agent})")
|
|
45
|
+
prompt = build_step_prompt(role, issue_number, review_output)
|
|
46
|
+
opts = { chdir: chdir }
|
|
47
|
+
opts[:model] = step[:model].to_s if step[:model]
|
|
48
|
+
claude.run_agent(agent.tr('_', '-'), prompt, **opts)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def skip_reason(step, state)
|
|
52
|
+
condition = step[:condition]
|
|
53
|
+
|
|
54
|
+
return 'merge handled by MergeManager' if step[:role].to_s == 'merge' && @skip_merge
|
|
55
|
+
return 'audit found blocking issues' if step[:role].to_s == 'merge' && @config.audit_mode && state[:audit_blocked]
|
|
56
|
+
return 'manual review mode' if step[:role].to_s == 'merge' && @config.manual_review
|
|
57
|
+
return 'fast-track issue (simple complexity)' if step[:complexity] == 'full' && state[:complexity] == 'simple'
|
|
58
|
+
if condition == 'has_findings' && !state[:last_review_output]&.include?('🔴')
|
|
59
|
+
return 'no blocking findings from review'
|
|
60
|
+
end
|
|
61
|
+
return 'no fixes were made' if condition == 'had_fixes' && !state[:had_fixes]
|
|
62
|
+
|
|
63
|
+
nil
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -18,6 +18,7 @@ You audit changed files as a pre-merge gate. You are read-only. Focus ONLY on fi
|
|
|
18
18
|
git diff main --name-only
|
|
19
19
|
```
|
|
20
20
|
3. Read every changed file in full
|
|
21
|
+
4. Read the corresponding test files for each changed file
|
|
21
22
|
|
|
22
23
|
## Audit Checklist
|
|
23
24
|
|
|
@@ -55,6 +56,7 @@ For each changed file, check:
|
|
|
55
56
|
- Are there tests for the changed code?
|
|
56
57
|
- Do tests cover error paths?
|
|
57
58
|
- Do tests cover edge cases?
|
|
59
|
+
- Do tests assert specific outcomes, not just "doesn't crash"?
|
|
58
60
|
|
|
59
61
|
### Data
|
|
60
62
|
- Potential N+1 queries or unbounded queries
|
|
@@ -66,22 +68,49 @@ For each changed file, check:
|
|
|
66
68
|
```
|
|
67
69
|
## Audit Report
|
|
68
70
|
|
|
69
|
-
|
|
70
|
-
[List any critical security or correctness issues. Use the word BLOCK for critical items.]
|
|
71
|
+
**Files audited**: N
|
|
71
72
|
|
|
72
|
-
###
|
|
73
|
-
[Pattern violations, missing tests, minor issues]
|
|
73
|
+
### Findings
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
#### 🔴 Critical — [Title]
|
|
76
|
+
**File**: `path/to/file:42`
|
|
77
|
+
**Category**: [Security | Pattern | Error Handling | Test Coverage | Data]
|
|
78
|
+
**Issue**: [What's wrong]
|
|
79
|
+
**Impact**: [What could go wrong]
|
|
80
|
+
**Recommendation**: [Exact fix]
|
|
77
81
|
|
|
78
|
-
|
|
79
|
-
|
|
82
|
+
#### 🟡 Warning — [Title]
|
|
83
|
+
**File**: `path/to/file:78`
|
|
84
|
+
**Category**: [category]
|
|
85
|
+
**Issue**: [What's wrong]
|
|
86
|
+
**Recommendation**: [How to fix]
|
|
87
|
+
|
|
88
|
+
#### 🟢 Good — [Title]
|
|
89
|
+
[What was done well]
|
|
90
|
+
|
|
91
|
+
### Test Coverage Summary
|
|
92
|
+
|
|
93
|
+
| Changed File | Test File | Methods Tested | Missing Coverage |
|
|
94
|
+
|-------------|-----------|---------------|-----------------|
|
|
95
|
+
| `path/to/file` | `path/to/file_test` | 4/5 | `edge_case_method` |
|
|
96
|
+
| `path/to/other` | ❌ None | 0/3 | All methods |
|
|
97
|
+
|
|
98
|
+
### Verdict
|
|
99
|
+
|
|
100
|
+
**PASS** — No critical findings. Safe to merge.
|
|
101
|
+
or
|
|
102
|
+
**PASS WITH WARNINGS** — N warnings found. Review recommended but not blocking.
|
|
103
|
+
or
|
|
104
|
+
**BLOCK** — N critical findings. Must fix before merge. [List the critical items]
|
|
80
105
|
```
|
|
81
106
|
|
|
82
|
-
|
|
107
|
+
## Blocking Rules
|
|
108
|
+
|
|
109
|
+
The auditor blocks merge (outputs "BLOCK" verdict) ONLY for:
|
|
83
110
|
- Authentication/authorization bypass
|
|
84
111
|
- Injection vulnerabilities
|
|
85
112
|
- Secrets exposure
|
|
86
113
|
- Data corruption risks
|
|
87
114
|
- Critical missing tests for dangerous operations
|
|
115
|
+
|
|
116
|
+
All other findings are warnings — reported but not blocking.
|
|
@@ -11,10 +11,11 @@ You implement GitHub issues. The issue is your single source of truth — everyt
|
|
|
11
11
|
|
|
12
12
|
## Before You Start
|
|
13
13
|
|
|
14
|
-
1.
|
|
15
|
-
2. Read `
|
|
16
|
-
3. Read
|
|
17
|
-
4.
|
|
14
|
+
1. Check branch state: `git log main..HEAD --oneline` and `git diff main --stat` — work may already be partially done
|
|
15
|
+
2. Read the issue via `gh issue view <number> --json title,body,labels`
|
|
16
|
+
3. Read `CLAUDE.md` for project conventions
|
|
17
|
+
4. Read every file listed in the issue's "Relevant files" and "Patterns to Follow" sections
|
|
18
|
+
5. When writing new code, find the closest existing example first (neighboring file in the same directory/namespace) and mirror its structure exactly
|
|
18
19
|
|
|
19
20
|
## Implementation Rules
|
|
20
21
|
|
|
@@ -103,6 +104,20 @@ You implement GitHub issues. The issue is your single source of truth — everyt
|
|
|
103
104
|
- Do NOT add comments, docstrings, or type annotations to code you didn't write
|
|
104
105
|
- Match the existing code style exactly — look at neighboring files
|
|
105
106
|
|
|
107
|
+
### Scope Discipline (CRITICAL)
|
|
108
|
+
|
|
109
|
+
You MUST NOT modify code outside the issue's scope. This is the #1 source of pipeline regressions. Specifically:
|
|
110
|
+
|
|
111
|
+
- **Do NOT change existing method signatures** unless the issue requires it
|
|
112
|
+
- **Do NOT change error messages or string literals** unless the issue requires it
|
|
113
|
+
- **Do NOT change query logic** (e.g., operators, sort order) unless the issue requires it
|
|
114
|
+
- **Do NOT change data types** unless the issue requires it
|
|
115
|
+
- **Do NOT delete existing tests** — only add new ones or modify tests for code you changed
|
|
116
|
+
- **Do NOT remove security protections** (rate limiting, input validation, access checks)
|
|
117
|
+
- **Do NOT change configuration files** unless the issue requires it
|
|
118
|
+
|
|
119
|
+
Before each commit, run `git diff main --stat` and verify EVERY changed file is justified by the issue's acceptance criteria. If a file appears that shouldn't be there, revert it with `git checkout main -- <file>`.
|
|
120
|
+
|
|
106
121
|
## Testing Requirements
|
|
107
122
|
|
|
108
123
|
Write tests for every acceptance criterion in the issue:
|
|
@@ -153,21 +168,30 @@ Combine related changes: a class and its tests go in one commit, not two.
|
|
|
153
168
|
- If a lint fix is needed for already-committed code, make a separate `style` commit
|
|
154
169
|
- Keep commits small and reviewable — if a commit touches 10+ files across unrelated areas, split it
|
|
155
170
|
|
|
171
|
+
## When Stuck
|
|
172
|
+
|
|
173
|
+
- **Test won't pass after 2 attempts**: Re-read the full source file and the full test file. The answer is usually in existing code you haven't read yet.
|
|
174
|
+
- **Can't find the right pattern**: Look at the nearest neighboring file in the same directory — don't invent a new approach.
|
|
175
|
+
- **Unsure what a method does**: Read the method's test file, not just its source.
|
|
176
|
+
- **Don't iterate on a broken approach**: If your approach fails twice, switch to a different strategy.
|
|
177
|
+
|
|
156
178
|
## Verification Checklist
|
|
157
179
|
|
|
158
180
|
After implementation, run these commands and fix ALL failures before finishing:
|
|
159
181
|
|
|
182
|
+
1. **Scope check**: `git diff main --stat` — verify every file is justified by the issue. Revert any unrelated changes.
|
|
183
|
+
2. **Deleted test check**: `git diff main` — verify you haven't deleted any existing test methods. If you have, restore them.
|
|
160
184
|
<%- if test_command -%>
|
|
161
|
-
|
|
185
|
+
3. **Tests**: `<%= test_command %>`
|
|
162
186
|
<%- end -%>
|
|
163
187
|
<%- if lint_command -%>
|
|
164
|
-
|
|
188
|
+
4. **Lint**: `<%= lint_command %>`
|
|
165
189
|
<%- end -%>
|
|
166
190
|
<%- if format_command -%>
|
|
167
|
-
|
|
191
|
+
5. **Format**: `<%= format_command %>`
|
|
168
192
|
<%- end -%>
|
|
169
193
|
<%- security_commands.each_with_index do |cmd, i| -%>
|
|
170
|
-
<%= i +
|
|
194
|
+
<%= i + 6 %>. **Security**: `<%= cmd %>`
|
|
171
195
|
<%- end -%>
|
|
172
196
|
|
|
173
197
|
Do NOT stop until all commands pass. If a test fails, read the error, fix the code, and re-run. Maximum 5 fix attempts per command — if still failing after 5 attempts, report what's failing and why.
|
|
@@ -13,21 +13,20 @@ You handle the git workflow for completed issues.
|
|
|
13
13
|
|
|
14
14
|
### 1. Verify Readiness
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
Tests and linters were already verified by the implementer. Do NOT re-run the full test suite — it wastes time and money.
|
|
17
17
|
|
|
18
|
+
Only re-run tests if you had to resolve merge conflicts or edit files:
|
|
18
19
|
```bash
|
|
19
20
|
<%- if test_command -%>
|
|
20
|
-
#
|
|
21
|
+
# ONLY after resolving merge conflicts:
|
|
21
22
|
<%= test_command %>
|
|
22
23
|
<%- end -%>
|
|
23
|
-
|
|
24
24
|
<%- if lint_command -%>
|
|
25
|
-
# Linter passes
|
|
26
25
|
<%= lint_command %>
|
|
27
26
|
<%- end -%>
|
|
28
27
|
```
|
|
29
28
|
|
|
30
|
-
If anything fails, STOP and report the failures. Do not create a PR with failing checks.
|
|
29
|
+
If anything fails after conflict resolution, STOP and report the failures. Do not create a PR with failing checks.
|
|
31
30
|
|
|
32
31
|
### 2. Create Pull Request
|
|
33
32
|
|
|
@@ -87,6 +86,13 @@ gh pr merge --merge --delete-branch
|
|
|
87
86
|
gh issue close <number> --comment "Implemented in PR #<pr-number>"
|
|
88
87
|
```
|
|
89
88
|
|
|
89
|
+
### 5. Clean Up
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
git checkout main
|
|
93
|
+
git pull origin main
|
|
94
|
+
```
|
|
95
|
+
|
|
90
96
|
## Output
|
|
91
97
|
|
|
92
98
|
```
|
|
@@ -94,4 +100,5 @@ gh issue close <number> --comment "Implemented in PR #<pr-number>"
|
|
|
94
100
|
- PR: #<number> (<url>)
|
|
95
101
|
- Issue: #<number> closed
|
|
96
102
|
- Branch: <name> (deleted)
|
|
103
|
+
- Merge commit: <sha>
|
|
97
104
|
```
|
|
@@ -43,6 +43,10 @@ git diff main --stat
|
|
|
43
43
|
git diff main
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
+
**Scope check**: Verify EVERY changed file is justified by the issue's acceptance criteria. If a file appears that shouldn't be there, revert it with `git checkout main -- <file>`.
|
|
47
|
+
|
|
48
|
+
**Deleted test check**: Verify you haven't deleted any existing test methods. If you have, restore them.
|
|
49
|
+
|
|
46
50
|
For each acceptance criterion, verify:
|
|
47
51
|
- Is it implemented correctly?
|
|
48
52
|
- Is it tested?
|
|
@@ -85,8 +85,8 @@ Rate each finding:
|
|
|
85
85
|
[What was done well]
|
|
86
86
|
|
|
87
87
|
### Acceptance Criteria Check
|
|
88
|
-
- [ ] Criterion 1:
|
|
89
|
-
- [ ] Criterion 2:
|
|
88
|
+
- [ ] Criterion 1: ✅ Implemented and tested / ❌ Missing [details]
|
|
89
|
+
- [ ] Criterion 2: ✅ / ❌
|
|
90
90
|
|
|
91
91
|
### Test Coverage
|
|
92
92
|
- [List any untested paths or missing edge cases]
|
|
@@ -10,6 +10,10 @@ model: sonnet
|
|
|
10
10
|
|
|
11
11
|
You perform security-focused code review. You are read-only.
|
|
12
12
|
|
|
13
|
+
## Focus
|
|
14
|
+
|
|
15
|
+
You run AFTER the code reviewer. Focus on security-specific concerns that a general code reviewer would miss. Do NOT repeat general code quality findings (missing tests, naming, patterns). Your value is catching auth bypasses, injection vectors, data exposure, and business logic integrity issues that require security expertise.
|
|
16
|
+
|
|
13
17
|
## Setup
|
|
14
18
|
|
|
15
19
|
1. Read CLAUDE.md for auth architecture and conventions
|
|
@@ -64,6 +68,13 @@ You perform security-focused code review. You are read-only.
|
|
|
64
68
|
- [ ] Logs don't contain PII or secrets
|
|
65
69
|
- [ ] No secrets in code (API keys, passwords, tokens)
|
|
66
70
|
|
|
71
|
+
### Business Logic Integrity
|
|
72
|
+
|
|
73
|
+
- [ ] Domain invariants maintained (e.g., balances sum correctly, state transitions are valid)
|
|
74
|
+
- [ ] Calculations use appropriate precision (no floating-point for monetary/exact values)
|
|
75
|
+
- [ ] Race conditions handled where concurrent access is possible (database locks, transactions)
|
|
76
|
+
- [ ] Multi-tenant data properly scoped — no cross-tenant access paths
|
|
77
|
+
|
|
67
78
|
### Dependencies
|
|
68
79
|
|
|
69
80
|
Run and report results of:
|
|
@@ -27,6 +27,12 @@ stack:
|
|
|
27
27
|
<%- end -%>
|
|
28
28
|
<%- end -%>
|
|
29
29
|
|
|
30
|
+
# Issue backend — 'github' (default) or 'local'
|
|
31
|
+
# Local mode stores issues as files in .ocak/issues/ (no gh CLI needed for issues).
|
|
32
|
+
# Auto-detected: if .ocak/issues/ contains .md files, local mode is used automatically.
|
|
33
|
+
# issues:
|
|
34
|
+
# backend: local
|
|
35
|
+
|
|
30
36
|
# Pipeline settings
|
|
31
37
|
pipeline:
|
|
32
38
|
max_parallel: 5
|
|
@@ -57,29 +63,38 @@ labels:
|
|
|
57
63
|
steps:
|
|
58
64
|
- agent: implementer
|
|
59
65
|
role: implement
|
|
66
|
+
model: sonnet
|
|
60
67
|
- agent: reviewer
|
|
61
68
|
role: review
|
|
69
|
+
model: opus
|
|
62
70
|
- agent: implementer
|
|
63
71
|
role: fix
|
|
64
72
|
condition: has_findings
|
|
73
|
+
model: sonnet
|
|
65
74
|
- agent: reviewer
|
|
66
75
|
role: verify
|
|
67
76
|
condition: had_fixes
|
|
77
|
+
model: opus
|
|
68
78
|
- agent: security_reviewer
|
|
69
79
|
role: security
|
|
80
|
+
model: sonnet
|
|
70
81
|
complexity: full # skipped for simple issues and --fast mode
|
|
71
82
|
- agent: implementer
|
|
72
83
|
role: fix
|
|
73
84
|
condition: has_findings
|
|
74
85
|
complexity: full # skipped for simple issues
|
|
86
|
+
model: sonnet
|
|
75
87
|
- agent: documenter
|
|
76
88
|
role: document
|
|
77
89
|
complexity: full # skipped for simple issues
|
|
90
|
+
model: sonnet
|
|
78
91
|
- agent: auditor
|
|
79
92
|
role: audit
|
|
80
93
|
complexity: full # skipped for simple issues
|
|
94
|
+
model: sonnet
|
|
81
95
|
- agent: merger
|
|
82
96
|
role: merge
|
|
97
|
+
model: sonnet
|
|
83
98
|
|
|
84
99
|
# Agent file paths — override to use custom agents
|
|
85
100
|
agents:
|
data/lib/ocak/verification.rb
CHANGED
|
@@ -52,7 +52,12 @@ module Ocak
|
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
def run_scoped_lint(logger, chdir:)
|
|
55
|
-
changed_stdout, = Open3.capture3('git', 'diff', '--name-only', 'main', chdir: chdir)
|
|
55
|
+
changed_stdout, stderr, status = Open3.capture3('git', 'diff', '--name-only', 'main', chdir: chdir)
|
|
56
|
+
unless status.success?
|
|
57
|
+
logger&.warn("Scoped lint skipped — git diff failed: #{stderr[0..200]}")
|
|
58
|
+
return nil
|
|
59
|
+
end
|
|
60
|
+
|
|
56
61
|
changed_files = changed_stdout.lines.map(&:strip).reject(&:empty?)
|
|
57
62
|
|
|
58
63
|
extensions = lint_extensions_for(@config.language)
|
|
@@ -16,6 +16,8 @@ module Ocak
|
|
|
16
16
|
|
|
17
17
|
def create(issue_number, setup_command: nil)
|
|
18
18
|
@mutex.synchronize do
|
|
19
|
+
raise ArgumentError, "Invalid issue number: #{issue_number}" unless issue_number.to_s.match?(/\A\d+\z/)
|
|
20
|
+
|
|
19
21
|
FileUtils.mkdir_p(@worktree_base)
|
|
20
22
|
|
|
21
23
|
branch = "auto/issue-#{issue_number}-#{SecureRandom.hex(4)}"
|
data/lib/ocak.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ocak
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Clay Harmon
|
|
@@ -36,35 +36,51 @@ files:
|
|
|
36
36
|
- bin/ocak
|
|
37
37
|
- lib/ocak.rb
|
|
38
38
|
- lib/ocak/agent_generator.rb
|
|
39
|
+
- lib/ocak/batch_processing.rb
|
|
39
40
|
- lib/ocak/claude_runner.rb
|
|
40
41
|
- lib/ocak/cli.rb
|
|
42
|
+
- lib/ocak/command_runner.rb
|
|
41
43
|
- lib/ocak/commands/audit.rb
|
|
42
44
|
- lib/ocak/commands/clean.rb
|
|
43
45
|
- lib/ocak/commands/debt.rb
|
|
44
46
|
- lib/ocak/commands/design.rb
|
|
45
47
|
- lib/ocak/commands/hiz.rb
|
|
46
48
|
- lib/ocak/commands/init.rb
|
|
49
|
+
- lib/ocak/commands/issue/close.rb
|
|
50
|
+
- lib/ocak/commands/issue/create.rb
|
|
51
|
+
- lib/ocak/commands/issue/edit.rb
|
|
52
|
+
- lib/ocak/commands/issue/list.rb
|
|
53
|
+
- lib/ocak/commands/issue/view.rb
|
|
47
54
|
- lib/ocak/commands/resume.rb
|
|
48
55
|
- lib/ocak/commands/run.rb
|
|
49
56
|
- lib/ocak/commands/status.rb
|
|
50
57
|
- lib/ocak/config.rb
|
|
51
58
|
- lib/ocak/failure_reporting.rb
|
|
52
59
|
- lib/ocak/git_utils.rb
|
|
60
|
+
- lib/ocak/instance_builders.rb
|
|
61
|
+
- lib/ocak/issue_backend.rb
|
|
53
62
|
- lib/ocak/issue_fetcher.rb
|
|
63
|
+
- lib/ocak/local_issue_fetcher.rb
|
|
64
|
+
- lib/ocak/local_merge_manager.rb
|
|
54
65
|
- lib/ocak/logger.rb
|
|
55
66
|
- lib/ocak/merge_manager.rb
|
|
56
67
|
- lib/ocak/merge_orchestration.rb
|
|
57
68
|
- lib/ocak/monorepo_detector.rb
|
|
69
|
+
- lib/ocak/parallel_execution.rb
|
|
58
70
|
- lib/ocak/pipeline_executor.rb
|
|
59
71
|
- lib/ocak/pipeline_runner.rb
|
|
60
72
|
- lib/ocak/pipeline_state.rb
|
|
61
73
|
- lib/ocak/planner.rb
|
|
62
74
|
- lib/ocak/process_registry.rb
|
|
63
75
|
- lib/ocak/process_runner.rb
|
|
76
|
+
- lib/ocak/project_key.rb
|
|
64
77
|
- lib/ocak/reready_processor.rb
|
|
65
78
|
- lib/ocak/run_report.rb
|
|
79
|
+
- lib/ocak/shutdown_handling.rb
|
|
66
80
|
- lib/ocak/stack_detector.rb
|
|
81
|
+
- lib/ocak/state_management.rb
|
|
67
82
|
- lib/ocak/step_comments.rb
|
|
83
|
+
- lib/ocak/step_execution.rb
|
|
68
84
|
- lib/ocak/stream_parser.rb
|
|
69
85
|
- lib/ocak/templates/agents/auditor.md.erb
|
|
70
86
|
- lib/ocak/templates/agents/documenter.md.erb
|