orch-code 0.1.1

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.
Files changed (116) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/LICENSE +21 -0
  3. package/README.md +624 -0
  4. package/cmd/apply.go +111 -0
  5. package/cmd/auth.go +393 -0
  6. package/cmd/auth_test.go +100 -0
  7. package/cmd/diff.go +57 -0
  8. package/cmd/doctor.go +149 -0
  9. package/cmd/explain.go +192 -0
  10. package/cmd/explain_test.go +62 -0
  11. package/cmd/init.go +100 -0
  12. package/cmd/interactive.go +1372 -0
  13. package/cmd/interactive_input.go +45 -0
  14. package/cmd/interactive_input_test.go +55 -0
  15. package/cmd/logs.go +72 -0
  16. package/cmd/model.go +84 -0
  17. package/cmd/plan.go +149 -0
  18. package/cmd/provider.go +189 -0
  19. package/cmd/provider_model_doctor_test.go +91 -0
  20. package/cmd/root.go +67 -0
  21. package/cmd/run.go +123 -0
  22. package/cmd/run_engine.go +208 -0
  23. package/cmd/run_engine_test.go +30 -0
  24. package/cmd/session.go +589 -0
  25. package/cmd/session_helpers.go +54 -0
  26. package/cmd/session_integration_test.go +30 -0
  27. package/cmd/session_list_current_test.go +87 -0
  28. package/cmd/session_messages_test.go +163 -0
  29. package/cmd/session_runs_test.go +68 -0
  30. package/cmd/sprint1_integration_test.go +119 -0
  31. package/cmd/stats.go +173 -0
  32. package/cmd/stats_test.go +71 -0
  33. package/cmd/version.go +4 -0
  34. package/go.mod +45 -0
  35. package/go.sum +108 -0
  36. package/internal/agents/agent.go +31 -0
  37. package/internal/agents/coder.go +167 -0
  38. package/internal/agents/planner.go +155 -0
  39. package/internal/agents/reviewer.go +118 -0
  40. package/internal/agents/runtime.go +25 -0
  41. package/internal/agents/runtime_test.go +77 -0
  42. package/internal/auth/account.go +78 -0
  43. package/internal/auth/oauth.go +523 -0
  44. package/internal/auth/store.go +287 -0
  45. package/internal/confidence/policy.go +174 -0
  46. package/internal/confidence/policy_test.go +71 -0
  47. package/internal/confidence/scorer.go +253 -0
  48. package/internal/confidence/scorer_test.go +83 -0
  49. package/internal/config/config.go +331 -0
  50. package/internal/config/config_defaults_test.go +138 -0
  51. package/internal/execution/contract_builder.go +160 -0
  52. package/internal/execution/contract_builder_test.go +68 -0
  53. package/internal/execution/plan_compliance.go +161 -0
  54. package/internal/execution/plan_compliance_test.go +71 -0
  55. package/internal/execution/retry_directive.go +132 -0
  56. package/internal/execution/scope_guard.go +69 -0
  57. package/internal/logger/logger.go +120 -0
  58. package/internal/models/contracts_test.go +100 -0
  59. package/internal/models/models.go +269 -0
  60. package/internal/orchestrator/orchestrator.go +701 -0
  61. package/internal/orchestrator/orchestrator_retry_test.go +135 -0
  62. package/internal/orchestrator/review_engine_test.go +50 -0
  63. package/internal/orchestrator/state.go +42 -0
  64. package/internal/orchestrator/test_classifier_test.go +68 -0
  65. package/internal/patch/applier.go +131 -0
  66. package/internal/patch/applier_test.go +25 -0
  67. package/internal/patch/parser.go +89 -0
  68. package/internal/patch/patch.go +60 -0
  69. package/internal/patch/summary.go +30 -0
  70. package/internal/patch/validator.go +104 -0
  71. package/internal/planning/normalizer.go +416 -0
  72. package/internal/planning/normalizer_test.go +64 -0
  73. package/internal/providers/errors.go +35 -0
  74. package/internal/providers/openai/client.go +498 -0
  75. package/internal/providers/openai/client_test.go +187 -0
  76. package/internal/providers/provider.go +47 -0
  77. package/internal/providers/registry.go +32 -0
  78. package/internal/providers/registry_test.go +57 -0
  79. package/internal/providers/router.go +52 -0
  80. package/internal/providers/state.go +114 -0
  81. package/internal/providers/state_test.go +64 -0
  82. package/internal/repo/analyzer.go +188 -0
  83. package/internal/repo/context.go +83 -0
  84. package/internal/review/engine.go +267 -0
  85. package/internal/review/engine_test.go +103 -0
  86. package/internal/runstore/store.go +137 -0
  87. package/internal/runstore/store_test.go +59 -0
  88. package/internal/runtime/lock.go +150 -0
  89. package/internal/runtime/lock_test.go +57 -0
  90. package/internal/session/compaction.go +260 -0
  91. package/internal/session/compaction_test.go +36 -0
  92. package/internal/session/service.go +117 -0
  93. package/internal/session/service_test.go +113 -0
  94. package/internal/storage/storage.go +1498 -0
  95. package/internal/storage/storage_test.go +413 -0
  96. package/internal/testing/classifier.go +80 -0
  97. package/internal/testing/classifier_test.go +36 -0
  98. package/internal/tools/command.go +160 -0
  99. package/internal/tools/command_test.go +56 -0
  100. package/internal/tools/file.go +111 -0
  101. package/internal/tools/git.go +77 -0
  102. package/internal/tools/invalid_params_test.go +36 -0
  103. package/internal/tools/policy.go +98 -0
  104. package/internal/tools/policy_test.go +36 -0
  105. package/internal/tools/registry_test.go +52 -0
  106. package/internal/tools/result.go +30 -0
  107. package/internal/tools/search.go +86 -0
  108. package/internal/tools/tool.go +94 -0
  109. package/main.go +9 -0
  110. package/npm/orch.js +25 -0
  111. package/package.json +41 -0
  112. package/scripts/changelog.js +20 -0
  113. package/scripts/check-release-version.js +21 -0
  114. package/scripts/lib/release-utils.js +223 -0
  115. package/scripts/postinstall.js +157 -0
  116. package/scripts/release.js +52 -0
@@ -0,0 +1,132 @@
1
+ package execution
2
+
3
+ import (
4
+ "strings"
5
+
6
+ "github.com/furkanbeydemir/orch/internal/models"
7
+ )
8
+
9
+ type RetryDirectiveBuilder struct{}
10
+
11
+ func NewRetryDirectiveBuilder() *RetryDirectiveBuilder {
12
+ return &RetryDirectiveBuilder{}
13
+ }
14
+
15
+ func (b *RetryDirectiveBuilder) FromValidation(state *models.RunState, attempt int) *models.RetryDirective {
16
+ if state == nil {
17
+ return nil
18
+ }
19
+ directive := &models.RetryDirective{
20
+ Stage: "validation",
21
+ Attempt: attempt,
22
+ Reasons: []string{},
23
+ FailedGates: []string{},
24
+ Instructions: []string{},
25
+ Avoid: []string{
26
+ "Do not retry with the same scope violation or forbidden change.",
27
+ "Do not expand scope unless the change is explicitly justified.",
28
+ },
29
+ }
30
+ for _, result := range state.ValidationResults {
31
+ if result.Status != models.ValidationFail {
32
+ continue
33
+ }
34
+ directive.FailedGates = append(directive.FailedGates, result.Name)
35
+ directive.Reasons = append(directive.Reasons, result.Summary)
36
+ directive.Instructions = append(directive.Instructions, result.ActionableItems...)
37
+ }
38
+ if len(directive.Instructions) == 0 {
39
+ directive.Instructions = append(directive.Instructions, "Regenerate the patch to satisfy all failed validation gates.")
40
+ }
41
+ return normalizeDirective(directive)
42
+ }
43
+
44
+ func (b *RetryDirectiveBuilder) FromTest(state *models.RunState, attempt int) *models.RetryDirective {
45
+ if state == nil {
46
+ return nil
47
+ }
48
+ reasons := []string{"The previous patch failed test execution."}
49
+ failedTests := []string{}
50
+ instructions := []string{"Fix the failing test behavior while keeping the patch inside the approved scope."}
51
+ for _, failure := range state.TestFailures {
52
+ failedTests = append(failedTests, failure.Code)
53
+ reasons = append(reasons, failure.Summary)
54
+ switch failure.Code {
55
+ case "test_timeout":
56
+ instructions = append(instructions, "Reduce the cause of timeout or make the code path deterministic enough to complete within the test budget.")
57
+ case "missing_required_tests":
58
+ instructions = append(instructions, "Add or restore the required tests instead of bypassing verification.")
59
+ case "test_assertion_failure":
60
+ instructions = append(instructions, "Fix the functional behavior that caused the assertion failure.")
61
+ case "flaky_test_suspected":
62
+ instructions = append(instructions, "Stabilize the code path instead of weakening the test.")
63
+ default:
64
+ instructions = append(instructions, "Resolve the setup or runtime issue causing the test command failure.")
65
+ }
66
+ }
67
+ if len(failedTests) == 0 {
68
+ failedTests = splitNonEmptyLines(state.TestResults)
69
+ }
70
+ directive := &models.RetryDirective{
71
+ Stage: "test",
72
+ Attempt: attempt,
73
+ Reasons: reasons,
74
+ FailedTests: failedTests,
75
+ Instructions: instructions,
76
+ Avoid: []string{
77
+ "Do not remove or weaken tests to make them pass.",
78
+ "Do not introduce unrelated changes while fixing test failures.",
79
+ },
80
+ }
81
+ for _, result := range state.ValidationResults {
82
+ if result.Status == models.ValidationFail {
83
+ directive.FailedGates = append(directive.FailedGates, result.Name)
84
+ }
85
+ }
86
+ return normalizeDirective(directive)
87
+ }
88
+
89
+ func (b *RetryDirectiveBuilder) FromReview(state *models.RunState, attempt int) *models.RetryDirective {
90
+ if state == nil || state.Review == nil {
91
+ return nil
92
+ }
93
+ directive := &models.RetryDirective{
94
+ Stage: "review",
95
+ Attempt: attempt,
96
+ Reasons: append([]string{}, state.Review.Comments...),
97
+ Instructions: append([]string{}, state.Review.Suggestions...),
98
+ Avoid: []string{
99
+ "Do not ignore reviewer findings.",
100
+ "Do not broaden the patch beyond what is needed to resolve review findings.",
101
+ },
102
+ }
103
+ if len(directive.Instructions) == 0 {
104
+ directive.Instructions = append(directive.Instructions, "Address the review findings and preserve all prior passing validation and test conditions.")
105
+ }
106
+ return normalizeDirective(directive)
107
+ }
108
+
109
+ func normalizeDirective(d *models.RetryDirective) *models.RetryDirective {
110
+ if d == nil {
111
+ return nil
112
+ }
113
+ d.Reasons = uniqueNonEmpty(d.Reasons)
114
+ d.FailedGates = uniqueNonEmpty(d.FailedGates)
115
+ d.FailedTests = uniqueNonEmpty(d.FailedTests)
116
+ d.Instructions = uniqueNonEmpty(d.Instructions)
117
+ d.Avoid = uniqueNonEmpty(d.Avoid)
118
+ return d
119
+ }
120
+
121
+ func splitNonEmptyLines(text string) []string {
122
+ parts := strings.Split(strings.TrimSpace(text), "\n")
123
+ result := make([]string, 0, len(parts))
124
+ for _, part := range parts {
125
+ trimmed := strings.TrimSpace(part)
126
+ if trimmed == "" {
127
+ continue
128
+ }
129
+ result = append(result, trimmed)
130
+ }
131
+ return result
132
+ }
@@ -0,0 +1,69 @@
1
+ package execution
2
+
3
+ import (
4
+ "fmt"
5
+ "strings"
6
+
7
+ "github.com/furkanbeydemir/orch/internal/models"
8
+ )
9
+
10
+ type ScopeGuard struct{}
11
+
12
+ func NewScopeGuard() *ScopeGuard {
13
+ return &ScopeGuard{}
14
+ }
15
+
16
+ func (g *ScopeGuard) Validate(contract *models.ExecutionContract, patch *models.Patch) models.ValidationResult {
17
+ result := models.ValidationResult{
18
+ Name: "scope_compliance",
19
+ Stage: "validation",
20
+ Status: models.ValidationPass,
21
+ Severity: models.SeverityLow,
22
+ Summary: "patch stayed inside allowed scope",
23
+ Details: []string{},
24
+ }
25
+
26
+ if contract == nil {
27
+ result.Status = models.ValidationWarn
28
+ result.Severity = models.SeverityMedium
29
+ result.Summary = "execution contract missing; scope compliance could not be fully validated"
30
+ return result
31
+ }
32
+ if patch == nil {
33
+ result.Status = models.ValidationFail
34
+ result.Severity = models.SeverityHigh
35
+ result.Summary = "patch missing for scope validation"
36
+ return result
37
+ }
38
+ if len(contract.AllowedFiles) == 0 || len(patch.Files) == 0 {
39
+ return result
40
+ }
41
+
42
+ allowed := map[string]struct{}{}
43
+ for _, file := range contract.AllowedFiles {
44
+ allowed[strings.TrimSpace(file)] = struct{}{}
45
+ }
46
+
47
+ violations := make([]string, 0)
48
+ for _, file := range patch.Files {
49
+ path := strings.TrimSpace(file.Path)
50
+ if path == "" {
51
+ continue
52
+ }
53
+ if _, ok := allowed[path]; ok {
54
+ continue
55
+ }
56
+ violations = append(violations, path)
57
+ }
58
+
59
+ if len(violations) == 0 {
60
+ return result
61
+ }
62
+
63
+ result.Status = models.ValidationFail
64
+ result.Severity = models.SeverityHigh
65
+ result.Summary = fmt.Sprintf("patch changed files outside allowed scope: %s", strings.Join(violations, ", "))
66
+ result.Details = violations
67
+ result.ActionableItems = []string{"Regenerate the patch using only allowed files or expand scope explicitly with justification."}
68
+ return result
69
+ }
@@ -0,0 +1,120 @@
1
+ package logger
2
+
3
+ import (
4
+ "encoding/json"
5
+ "fmt"
6
+ "os"
7
+ "path/filepath"
8
+ "sync"
9
+ "time"
10
+
11
+ "github.com/furkanbeydemir/orch/internal/models"
12
+ )
13
+
14
+ type Logger struct {
15
+ runID string
16
+ repoRoot string
17
+ entries []models.LogEntry
18
+ mu sync.Mutex
19
+ verbose bool
20
+ }
21
+
22
+ func New(runID, repoRoot string, verbose bool) *Logger {
23
+ return &Logger{
24
+ runID: runID,
25
+ repoRoot: repoRoot,
26
+ entries: make([]models.LogEntry, 0),
27
+ verbose: verbose,
28
+ }
29
+ }
30
+
31
+ func (l *Logger) Log(actor, step, message string) {
32
+ entry := models.LogEntry{
33
+ Timestamp: time.Now(),
34
+ Actor: actor,
35
+ Step: step,
36
+ Message: message,
37
+ }
38
+
39
+ l.mu.Lock()
40
+ l.entries = append(l.entries, entry)
41
+ l.mu.Unlock()
42
+
43
+ if l.verbose {
44
+ fmt.Printf("[%s] %s\n", actor, message)
45
+ }
46
+ }
47
+
48
+ func (l *Logger) Verbose(actor, step, message string) {
49
+ if l.verbose {
50
+ l.Log(actor, step, message)
51
+ }
52
+ }
53
+
54
+ func (l *Logger) Entries() []models.LogEntry {
55
+ l.mu.Lock()
56
+ defer l.mu.Unlock()
57
+
58
+ result := make([]models.LogEntry, len(l.entries))
59
+ copy(result, l.entries)
60
+ return result
61
+ }
62
+
63
+ func (l *Logger) Save() error {
64
+ l.mu.Lock()
65
+ entries := make([]models.LogEntry, len(l.entries))
66
+ copy(entries, l.entries)
67
+ l.mu.Unlock()
68
+
69
+ runsDir := filepath.Join(l.repoRoot, ".orch", "runs")
70
+ if err := os.MkdirAll(runsDir, 0o755); err != nil {
71
+ return fmt.Errorf("failed to create runs directory: %w", err)
72
+ }
73
+
74
+ data, err := json.MarshalIndent(entries, "", " ")
75
+ if err != nil {
76
+ return fmt.Errorf("failed to serialize logs: %w", err)
77
+ }
78
+
79
+ logPath := filepath.Join(runsDir, l.runID+".json")
80
+ if err := os.WriteFile(logPath, data, 0o644); err != nil {
81
+ return fmt.Errorf("failed to write log file: %w", err)
82
+ }
83
+
84
+ return nil
85
+ }
86
+
87
+ func LoadRunLog(repoRoot, runID string) ([]models.LogEntry, error) {
88
+ logPath := filepath.Join(repoRoot, ".orch", "runs", runID+".json")
89
+
90
+ data, err := os.ReadFile(logPath)
91
+ if err != nil {
92
+ return nil, fmt.Errorf("failed to read log file: %w", err)
93
+ }
94
+
95
+ var entries []models.LogEntry
96
+ if err := json.Unmarshal(data, &entries); err != nil {
97
+ return nil, fmt.Errorf("failed to parse log file: %w", err)
98
+ }
99
+
100
+ return entries, nil
101
+ }
102
+
103
+ func ListRuns(repoRoot string) ([]string, error) {
104
+ runsDir := filepath.Join(repoRoot, ".orch", "runs")
105
+
106
+ entries, err := os.ReadDir(runsDir)
107
+ if err != nil {
108
+ return nil, fmt.Errorf("failed to read runs directory: %w", err)
109
+ }
110
+
111
+ var runs []string
112
+ for _, entry := range entries {
113
+ if !entry.IsDir() && filepath.Ext(entry.Name()) == ".json" {
114
+ name := entry.Name()
115
+ runs = append(runs, name[:len(name)-len(".json")])
116
+ }
117
+ }
118
+
119
+ return runs, nil
120
+ }
@@ -0,0 +1,100 @@
1
+ package models
2
+
3
+ import (
4
+ "encoding/json"
5
+ "testing"
6
+ "time"
7
+ )
8
+
9
+ func TestRunStateStructuredArtifactsJSONRoundtrip(t *testing.T) {
10
+ now := time.Now().UTC()
11
+ state := RunState{
12
+ ID: "run-1",
13
+ Task: Task{ID: "task-1", Description: "fix auth bug", CreatedAt: now},
14
+ TaskBrief: &TaskBrief{
15
+ TaskID: "task-1",
16
+ UserRequest: "fix auth bug",
17
+ NormalizedGoal: "Fix auth bug while preserving existing behavior.",
18
+ TaskType: TaskTypeBugfix,
19
+ RiskLevel: RiskHigh,
20
+ },
21
+ Plan: &Plan{
22
+ TaskID: "task-1",
23
+ Summary: "Fix auth bug while preserving existing behavior.",
24
+ TaskType: TaskTypeBugfix,
25
+ RiskLevel: RiskHigh,
26
+ AcceptanceCriteria: []AcceptanceCriterion{{ID: "ac-1", Description: "Bug path no longer fails."}},
27
+ },
28
+ ExecutionContract: &ExecutionContract{
29
+ TaskID: "task-1",
30
+ AllowedFiles: []string{"internal/auth/service.go"},
31
+ PatchBudget: PatchBudget{MaxFiles: 2, MaxChangedLines: 80},
32
+ },
33
+ ValidationResults: []ValidationResult{{
34
+ Name: "scope_compliance",
35
+ Stage: "validation",
36
+ Status: ValidationPass,
37
+ Severity: SeverityLow,
38
+ Summary: "patch stayed inside allowed scope",
39
+ }},
40
+ RetryDirective: &RetryDirective{
41
+ Stage: "validation",
42
+ Attempt: 1,
43
+ FailedGates: []string{"scope_compliance"},
44
+ Instructions: []string{"Keep the patch inside allowed scope."},
45
+ },
46
+ ReviewScorecard: &ReviewScorecard{
47
+ RequirementCoverage: 9,
48
+ ScopeControl: 10,
49
+ RegressionRisk: 8,
50
+ Readability: 8,
51
+ Maintainability: 8,
52
+ TestAdequacy: 8,
53
+ Decision: ReviewAccept,
54
+ },
55
+ Confidence: &ConfidenceReport{
56
+ Score: 0.88,
57
+ Band: "high",
58
+ Reasons: []string{"all mandatory signals aligned"},
59
+ },
60
+ TestFailures: []TestFailure{{
61
+ Code: "test_assertion_failure",
62
+ Summary: "expected 200 got 500",
63
+ Details: []string{"expected 200 got 500"},
64
+ }},
65
+ Status: StatusPlanning,
66
+ StartedAt: now,
67
+ }
68
+
69
+ encoded, err := json.Marshal(state)
70
+ if err != nil {
71
+ t.Fatalf("marshal: %v", err)
72
+ }
73
+
74
+ var decoded RunState
75
+ if err := json.Unmarshal(encoded, &decoded); err != nil {
76
+ t.Fatalf("unmarshal: %v", err)
77
+ }
78
+
79
+ if decoded.TaskBrief == nil || decoded.TaskBrief.TaskType != TaskTypeBugfix {
80
+ t.Fatalf("task brief did not roundtrip")
81
+ }
82
+ if decoded.ExecutionContract == nil || len(decoded.ExecutionContract.AllowedFiles) != 1 {
83
+ t.Fatalf("execution contract did not roundtrip")
84
+ }
85
+ if len(decoded.ValidationResults) != 1 || decoded.ValidationResults[0].Name != "scope_compliance" {
86
+ t.Fatalf("validation results did not roundtrip")
87
+ }
88
+ if decoded.RetryDirective == nil || decoded.RetryDirective.Stage != "validation" {
89
+ t.Fatalf("retry directive did not roundtrip")
90
+ }
91
+ if decoded.ReviewScorecard == nil || decoded.ReviewScorecard.Decision != ReviewAccept {
92
+ t.Fatalf("review scorecard did not roundtrip")
93
+ }
94
+ if decoded.Confidence == nil || decoded.Confidence.Band != "high" {
95
+ t.Fatalf("confidence report did not roundtrip")
96
+ }
97
+ if len(decoded.TestFailures) != 1 || decoded.TestFailures[0].Code != "test_assertion_failure" {
98
+ t.Fatalf("test failures did not roundtrip")
99
+ }
100
+ }
@@ -0,0 +1,269 @@
1
+ package models
2
+
3
+ import "time"
4
+
5
+ // State machine: Created → Analyzing → Planning → Coding → Validating → Testing → Reviewing → Completed/Failed
6
+ type RunStatus string
7
+
8
+ const (
9
+ StatusCreated RunStatus = "created"
10
+ StatusAnalyzing RunStatus = "analyzing"
11
+ StatusPlanning RunStatus = "planning"
12
+ StatusCoding RunStatus = "coding"
13
+ StatusValidating RunStatus = "validating"
14
+ StatusTesting RunStatus = "testing"
15
+ StatusReviewing RunStatus = "reviewing"
16
+ StatusCompleted RunStatus = "completed"
17
+ StatusFailed RunStatus = "failed"
18
+ )
19
+
20
+ type ReviewDecision string
21
+
22
+ const (
23
+ ReviewAccept ReviewDecision = "accept"
24
+ ReviewRevise ReviewDecision = "revise"
25
+ )
26
+
27
+ type TaskType string
28
+
29
+ const (
30
+ TaskTypeUnknown TaskType = "unknown"
31
+ TaskTypeFeature TaskType = "feature"
32
+ TaskTypeBugfix TaskType = "bugfix"
33
+ TaskTypeTest TaskType = "test"
34
+ TaskTypeRefactor TaskType = "refactor"
35
+ TaskTypeDocs TaskType = "docs"
36
+ TaskTypeChore TaskType = "chore"
37
+ )
38
+
39
+ type RiskLevel string
40
+
41
+ const (
42
+ RiskLow RiskLevel = "low"
43
+ RiskMedium RiskLevel = "medium"
44
+ RiskHigh RiskLevel = "high"
45
+ )
46
+
47
+ type ValidationStatus string
48
+
49
+ const (
50
+ ValidationPass ValidationStatus = "pass"
51
+ ValidationWarn ValidationStatus = "warn"
52
+ ValidationFail ValidationStatus = "fail"
53
+ )
54
+
55
+ type ValidationSeverity string
56
+
57
+ const (
58
+ SeverityLow ValidationSeverity = "low"
59
+ SeverityMedium ValidationSeverity = "medium"
60
+ SeverityHigh ValidationSeverity = "high"
61
+ SeverityCritical ValidationSeverity = "critical"
62
+ )
63
+
64
+ type Task struct {
65
+ ID string `json:"id"`
66
+ Description string `json:"description"`
67
+ CreatedAt time.Time `json:"created_at"`
68
+ }
69
+
70
+ type TaskBrief struct {
71
+ TaskID string `json:"task_id"`
72
+ UserRequest string `json:"user_request"`
73
+ NormalizedGoal string `json:"normalized_goal"`
74
+ TaskType TaskType `json:"task_type"`
75
+ RiskLevel RiskLevel `json:"risk_level"`
76
+ Constraints []string `json:"constraints,omitempty"`
77
+ Assumptions []string `json:"assumptions,omitempty"`
78
+ SuccessDefinition []string `json:"success_definition,omitempty"`
79
+ }
80
+
81
+ type AcceptanceCriterion struct {
82
+ ID string `json:"id"`
83
+ Description string `json:"description"`
84
+ }
85
+
86
+ type Plan struct {
87
+ TaskID string `json:"task_id"`
88
+ Summary string `json:"summary,omitempty"`
89
+ TaskType TaskType `json:"task_type,omitempty"`
90
+ RiskLevel RiskLevel `json:"risk_level,omitempty"`
91
+ Steps []PlanStep `json:"steps"`
92
+ FilesToModify []string `json:"files_to_modify"`
93
+ FilesToInspect []string `json:"files_to_inspect"`
94
+ Risks []string `json:"risks"`
95
+ TestStrategy string `json:"test_strategy"`
96
+ TestRequirements []string `json:"test_requirements,omitempty"`
97
+ AcceptanceCriteria []AcceptanceCriterion `json:"acceptance_criteria,omitempty"`
98
+ Invariants []string `json:"invariants,omitempty"`
99
+ ForbiddenChanges []string `json:"forbidden_changes,omitempty"`
100
+ }
101
+
102
+ type PlanStep struct {
103
+ Order int `json:"order"`
104
+ Description string `json:"description"`
105
+ TargetFile string `json:"target_file,omitempty"`
106
+ }
107
+
108
+ type Patch struct {
109
+ TaskID string `json:"task_id"`
110
+ Files []PatchFile `json:"files"`
111
+ // RawDiff contains raw unified diff text.
112
+ RawDiff string `json:"raw_diff"`
113
+ }
114
+
115
+ type PatchFile struct {
116
+ Path string `json:"path"`
117
+ Status string `json:"status"`
118
+ Diff string `json:"diff"`
119
+ }
120
+
121
+ type RepoMap struct {
122
+ RootPath string `json:"root_path"`
123
+ // Language is the detected primary language.
124
+ Language string `json:"language"`
125
+ PackageManager string `json:"package_manager"`
126
+ TestFramework string `json:"test_framework"`
127
+ // Files is the repository file inventory.
128
+ Files []FileInfo `json:"files"`
129
+ }
130
+
131
+ type FileInfo struct {
132
+ Path string `json:"path"`
133
+ Language string `json:"language"`
134
+ Size int64 `json:"size"`
135
+ Imports []string `json:"imports,omitempty"`
136
+ }
137
+
138
+ type PatchBudget struct {
139
+ MaxFiles int `json:"max_files"`
140
+ MaxChangedLines int `json:"max_changed_lines"`
141
+ }
142
+
143
+ type ScopeExpansionPolicy struct {
144
+ Allowed bool `json:"allowed"`
145
+ RequiresReason bool `json:"requires_reason"`
146
+ MaxExtraFiles int `json:"max_extra_files"`
147
+ }
148
+
149
+ type ExecutionContract struct {
150
+ TaskID string `json:"task_id"`
151
+ PlanID string `json:"plan_id,omitempty"`
152
+ AllowedFiles []string `json:"allowed_files,omitempty"`
153
+ InspectFiles []string `json:"inspect_files,omitempty"`
154
+ RequiredEdits []string `json:"required_edits,omitempty"`
155
+ ProhibitedActions []string `json:"prohibited_actions,omitempty"`
156
+ AcceptanceCriteria []string `json:"acceptance_criteria,omitempty"`
157
+ Invariants []string `json:"invariants,omitempty"`
158
+ PatchBudget PatchBudget `json:"patch_budget"`
159
+ ScopeExpansionPolicy ScopeExpansionPolicy `json:"scope_expansion_policy"`
160
+ }
161
+
162
+ type ValidationResult struct {
163
+ Name string `json:"name"`
164
+ Stage string `json:"stage"`
165
+ Status ValidationStatus `json:"status"`
166
+ Severity ValidationSeverity `json:"severity"`
167
+ Summary string `json:"summary"`
168
+ Details []string `json:"details,omitempty"`
169
+ ActionableItems []string `json:"actionable_items,omitempty"`
170
+ Metadata map[string]string `json:"metadata,omitempty"`
171
+ }
172
+
173
+ type RetryDirective struct {
174
+ Stage string `json:"stage"`
175
+ Attempt int `json:"attempt"`
176
+ Reasons []string `json:"reasons,omitempty"`
177
+ FailedGates []string `json:"failed_gates,omitempty"`
178
+ FailedTests []string `json:"failed_tests,omitempty"`
179
+ Instructions []string `json:"instructions,omitempty"`
180
+ Avoid []string `json:"avoid,omitempty"`
181
+ }
182
+
183
+ type ReviewScorecard struct {
184
+ RequirementCoverage int `json:"requirement_coverage"`
185
+ ScopeControl int `json:"scope_control"`
186
+ RegressionRisk int `json:"regression_risk"`
187
+ Readability int `json:"readability"`
188
+ Maintainability int `json:"maintainability"`
189
+ TestAdequacy int `json:"test_adequacy"`
190
+ Decision ReviewDecision `json:"decision"`
191
+ Findings []string `json:"findings,omitempty"`
192
+ }
193
+
194
+ type ConfidenceReport struct {
195
+ Score float64 `json:"score"`
196
+ Band string `json:"band"`
197
+ Reasons []string `json:"reasons,omitempty"`
198
+ Warnings []string `json:"warnings,omitempty"`
199
+ }
200
+
201
+ type TestFailure struct {
202
+ Code string `json:"code"`
203
+ Summary string `json:"summary"`
204
+ Details []string `json:"details,omitempty"`
205
+ Flaky bool `json:"flaky,omitempty"`
206
+ }
207
+
208
+ type RunState struct {
209
+ ID string `json:"id"`
210
+ ProjectID string `json:"project_id,omitempty"`
211
+ SessionID string `json:"session_id,omitempty"`
212
+ Task Task `json:"task"`
213
+ TaskBrief *TaskBrief `json:"task_brief,omitempty"`
214
+ Status RunStatus `json:"status"`
215
+ Plan *Plan `json:"plan,omitempty"`
216
+ ExecutionContract *ExecutionContract `json:"execution_contract,omitempty"`
217
+ Patch *Patch `json:"patch,omitempty"`
218
+ Context *ContextResult `json:"context,omitempty"`
219
+ ValidationResults []ValidationResult `json:"validation_results,omitempty"`
220
+ RetryDirective *RetryDirective `json:"retry_directive,omitempty"`
221
+ ReviewScorecard *ReviewScorecard `json:"review_scorecard,omitempty"`
222
+ Confidence *ConfidenceReport `json:"confidence,omitempty"`
223
+ TestFailures []TestFailure `json:"test_failures,omitempty"`
224
+ // Review contains review output when available.
225
+ Review *ReviewResult `json:"review,omitempty"`
226
+ // TestResults stores summarized test execution output.
227
+ TestResults string `json:"test_results,omitempty"`
228
+ Retries RetryState `json:"retries"`
229
+ UnresolvedFailures []string `json:"unresolved_failures,omitempty"`
230
+ BestPatchSummary string `json:"best_patch_summary,omitempty"`
231
+ Logs []LogEntry `json:"logs"`
232
+ StartedAt time.Time `json:"started_at"`
233
+ CompletedAt *time.Time `json:"completed_at,omitempty"`
234
+ Error string `json:"error,omitempty"`
235
+ }
236
+
237
+ type RetryState struct {
238
+ Validation int `json:"validation"`
239
+ Testing int `json:"testing"`
240
+ Review int `json:"review"`
241
+ }
242
+
243
+ type ReviewResult struct {
244
+ Decision ReviewDecision `json:"decision"`
245
+ Comments []string `json:"comments"`
246
+ Suggestions []string `json:"suggestions,omitempty"`
247
+ }
248
+
249
+ type LogEntry struct {
250
+ Timestamp time.Time `json:"timestamp"`
251
+ Actor string `json:"actor"`
252
+ Step string `json:"step"`
253
+ Message string `json:"message"`
254
+ }
255
+
256
+ type ContextResult struct {
257
+ SelectedFiles []string `json:"selected_files"`
258
+ RelatedTests []string `json:"related_tests"`
259
+ RelevantConfigs []string `json:"relevant_configs"`
260
+ }
261
+
262
+ type ToolResult struct {
263
+ ToolName string `json:"tool_name"`
264
+ Success bool `json:"success"`
265
+ Output string `json:"output"`
266
+ Error string `json:"error,omitempty"`
267
+ ErrorCode string `json:"error_code,omitempty"`
268
+ Metadata map[string]string `json:"metadata,omitempty"`
269
+ }