2026-03-17 17:45:04 +00:00
// SPDX-License-Identifier: EUPL-1.2
package agentic
import (
"context"
"regexp"
"time"
2026-03-22 03:41:07 +00:00
core "dappco.re/go/core"
2026-03-17 17:45:04 +00:00
"github.com/modelcontextprotocol/go-sdk/mcp"
)
2026-03-30 22:54:19 +00:00
// input := agentic.ReviewQueueInput{Reviewer: "coderabbit", Limit: 4, DryRun: true}
2026-03-17 17:45:04 +00:00
type ReviewQueueInput struct {
2026-03-31 05:28:26 +00:00
Limit int ` json:"limit,omitempty" `
Reviewer string ` json:"reviewer,omitempty" `
DryRun bool ` json:"dry_run,omitempty" `
LocalOnly bool ` json:"local_only,omitempty" `
2026-03-17 17:45:04 +00:00
}
2026-03-30 22:54:19 +00:00
// out := agentic.ReviewQueueOutput{Success: true, Processed: []agentic.ReviewResult{{Repo: "go-io", Verdict: "clean"}}}
2026-03-17 17:45:04 +00:00
type ReviewQueueOutput struct {
Success bool ` json:"success" `
Processed [ ] ReviewResult ` json:"processed" `
Skipped [ ] string ` json:"skipped,omitempty" `
RateLimit * RateLimitInfo ` json:"rate_limit,omitempty" `
}
2026-03-30 22:54:19 +00:00
// result := agentic.ReviewResult{Repo: "go-io", Verdict: "findings", Findings: 3, Action: "fix_dispatched"}
2026-03-17 17:45:04 +00:00
type ReviewResult struct {
Repo string ` json:"repo" `
2026-03-31 05:28:26 +00:00
Verdict string ` json:"verdict" `
Findings int ` json:"findings" `
Action string ` json:"action" `
2026-03-17 17:45:04 +00:00
Detail string ` json:"detail,omitempty" `
}
2026-03-30 22:54:19 +00:00
// limit := agentic.RateLimitInfo{Limited: true, Message: "retry after 2026-03-22T06:00:00Z"}
2026-03-17 17:45:04 +00:00
type RateLimitInfo struct {
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
Limited bool ` json:"limited" `
RetryAt time . Time ` json:"retry_at,omitempty" `
Message string ` json:"message,omitempty" `
2026-03-17 17:45:04 +00:00
}
2026-03-30 15:33:01 +00:00
var retryAfterPattern = compileRetryAfterPattern ( )
2026-04-02 03:46:09 +00:00
const prManageScheduleInterval = 5 * time . Minute
2026-03-30 15:33:01 +00:00
func compileRetryAfterPattern ( ) * regexp . Regexp {
pattern , err := regexp . Compile ( ` (\d+)\s*minutes?\s*(?:and\s*)?(\d+)?\s*seconds? ` )
if err != nil {
return nil
}
return pattern
}
2026-03-17 17:45:04 +00:00
func ( s * PrepSubsystem ) registerReviewQueueTool ( server * mcp . Server ) {
mcp . AddTool ( server , & mcp . Tool {
Name : "agentic_review_queue" ,
2026-04-01 15:47:48 +00:00
Description : "Process the review queue. Supports coderabbit, codex, or both reviewers, auto-merges clean ones on GitHub, dispatches fix agents for findings, and respects rate limits." ,
2026-03-17 17:45:04 +00:00
} , s . reviewQueue )
}
2026-04-01 15:47:48 +00:00
// reviewers := reviewQueueReviewers("both")
func reviewQueueReviewers ( reviewer string ) [ ] string {
switch core . Lower ( core . Trim ( reviewer ) ) {
case "codex" :
return [ ] string { "codex" }
case "both" :
return [ ] string { "codex" , "coderabbit" }
default :
return [ ] string { "coderabbit" }
}
}
2026-04-01 18:04:39 +00:00
// result := c.Command("review-queue").Run(ctx, core.NewOptions(
2026-04-01 11:36:47 +00:00
//
// core.Option{Key: "limit", Value: 4},
//
// ))
2026-04-01 18:04:39 +00:00
func ( s * PrepSubsystem ) cmdReviewQueue ( options core . Options ) core . Result {
2026-04-01 11:36:47 +00:00
ctx := s . commandContext ( )
input := ReviewQueueInput {
Limit : optionIntValue ( options , "limit" ) ,
Reviewer : optionStringValue ( options , "reviewer" ) ,
DryRun : optionBoolValue ( options , "dry-run" ) ,
LocalOnly : optionBoolValue ( options , "local-only" ) ,
}
_ , output , err := s . reviewQueue ( ctx , nil , input )
if err != nil {
core . Print ( nil , "error: %v" , err )
return core . Result { Value : err , OK : false }
}
if output . RateLimit != nil && output . RateLimit . Message != "" {
core . Print ( nil , "rate limit: %s" , output . RateLimit . Message )
}
for _ , item := range output . Processed {
core . Print ( nil , "%s: %s (%s)" , item . Repo , item . Verdict , item . Action )
}
for _ , item := range output . Skipped {
core . Print ( nil , "skipped: %s" , item )
}
return core . Result { Value : output , OK : true }
}
2026-04-01 18:04:39 +00:00
// result := c.Command("pr-manage").Run(ctx, core.NewOptions(
//
// core.Option{Key: "limit", Value: 4},
//
// ))
func ( s * PrepSubsystem ) cmdPRManage ( options core . Options ) core . Result {
return s . cmdReviewQueue ( options )
}
2026-04-02 03:46:09 +00:00
// ctx, cancel := context.WithCancel(context.Background())
// go s.runPRManageLoop(ctx, 5*time.Minute)
func ( s * PrepSubsystem ) runPRManageLoop ( ctx context . Context , interval time . Duration ) {
if ctx == nil || interval <= 0 {
return
}
ticker := time . NewTicker ( interval )
defer ticker . Stop ( )
for {
select {
case <- ctx . Done ( ) :
return
case <- ticker . C :
if result := s . cmdPRManage ( core . NewOptions ( ) ) ; ! result . OK {
core . Warn ( "pr-manage scheduled run failed" , "error" , result . Value )
}
}
}
}
2026-03-17 17:45:04 +00:00
func ( s * PrepSubsystem ) reviewQueue ( ctx context . Context , _ * mcp . CallToolRequest , input ReviewQueueInput ) ( * mcp . CallToolResult , ReviewQueueOutput , error ) {
limit := input . Limit
if limit <= 0 {
limit = 4
}
2026-03-30 18:52:15 +00:00
basePath := s . codePath
if basePath == "" {
basePath = core . JoinPath ( HomeDir ( ) , "Code" )
}
basePath = core . JoinPath ( basePath , "core" )
2026-03-17 17:45:04 +00:00
candidates := s . findReviewCandidates ( basePath )
if len ( candidates ) == 0 {
return nil , ReviewQueueOutput {
Success : true ,
Processed : nil ,
} , nil
}
var processed [ ] ReviewResult
var skipped [ ] string
var rateInfo * RateLimitInfo
for _ , repo := range candidates {
2026-04-01 15:47:48 +00:00
repoDir := core . JoinPath ( basePath , repo )
for _ , reviewer := range reviewQueueReviewers ( input . Reviewer ) {
if len ( processed ) >= limit {
skipped = append ( skipped , core . Concat ( repo , " (limit reached)" ) )
break
}
2026-03-17 17:45:04 +00:00
2026-04-01 15:47:48 +00:00
if reviewer == "coderabbit" && rateInfo != nil && rateInfo . Limited && time . Now ( ) . Before ( rateInfo . RetryAt ) {
skipped = append ( skipped , core . Concat ( repo , " (rate limited)" ) )
continue
}
2026-03-17 17:45:04 +00:00
2026-04-01 15:47:48 +00:00
result := s . reviewRepo ( ctx , repoDir , repo , reviewer , input . DryRun , input . LocalOnly )
if result . Verdict == "rate_limited" {
if reviewer == "coderabbit" {
retryAfter := parseRetryAfter ( result . Detail )
rateInfo = & RateLimitInfo {
Limited : true ,
RetryAt : time . Now ( ) . Add ( retryAfter ) ,
Message : result . Detail ,
}
skipped = append ( skipped , core . Concat ( repo , " (rate limited: " , retryAfter . String ( ) , ")" ) )
}
continue
2026-03-17 17:45:04 +00:00
}
2026-04-01 15:47:48 +00:00
processed = append ( processed , result )
}
2026-03-17 17:45:04 +00:00
}
if rateInfo != nil {
s . saveRateLimitState ( rateInfo )
}
return nil , ReviewQueueOutput {
Success : true ,
Processed : processed ,
Skipped : skipped ,
RateLimit : rateInfo ,
} , nil
}
2026-03-30 22:54:19 +00:00
// repos := s.findReviewCandidates("/srv/Code/core")
2026-03-17 17:45:04 +00:00
func ( s * PrepSubsystem ) findReviewCandidates ( basePath string ) [ ] string {
feat(v0.8.0): full AX migration — ServiceRuntime, Actions, quality gates, transport
go-process:
- Register factory, Result lifecycle, 5 named Action handlers
- Start/Run/StartWithOptions/RunWithOptions all return core.Result
- core.ID() replaces fmt.Sprintf, core.As replaces errors.As
core/agent:
- PrepSubsystem + monitor.Subsystem + setup.Service embed ServiceRuntime[T]
- 22 named Actions + agent.completion Task pipeline in OnStartup
- ChannelNotifier removed — all IPC via c.ACTION(messages.X{})
- proc.go: all methods via s.Core().Process(), returns core.Result
- status.go: WriteAtomic + JSONMarshalString
- paths.go: Fs.NewUnrestricted() replaces unsafe.Pointer
- transport.go: ONE net/http file — HTTPGet/HTTPPost/HTTPDo/MCP transport
- All disallowed imports eliminated from source files (13 quality gates)
- String concat eliminated — core.Concat() throughout
- 1:1 _test.go + _example_test.go for every source file
- Reference docs synced from core/go v0.8.0
- RFC-025 updated with net/http, net/url, io/fs quality gates
- lib.go: io/fs eliminated via Data.ListNames, Array[T].Deduplicate
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 01:27:46 +00:00
paths := core . PathGlob ( core . JoinPath ( basePath , "*" ) )
2026-03-17 17:45:04 +00:00
var candidates [ ] string
feat(v0.8.0): full AX migration — ServiceRuntime, Actions, quality gates, transport
go-process:
- Register factory, Result lifecycle, 5 named Action handlers
- Start/Run/StartWithOptions/RunWithOptions all return core.Result
- core.ID() replaces fmt.Sprintf, core.As replaces errors.As
core/agent:
- PrepSubsystem + monitor.Subsystem + setup.Service embed ServiceRuntime[T]
- 22 named Actions + agent.completion Task pipeline in OnStartup
- ChannelNotifier removed — all IPC via c.ACTION(messages.X{})
- proc.go: all methods via s.Core().Process(), returns core.Result
- status.go: WriteAtomic + JSONMarshalString
- paths.go: Fs.NewUnrestricted() replaces unsafe.Pointer
- transport.go: ONE net/http file — HTTPGet/HTTPPost/HTTPDo/MCP transport
- All disallowed imports eliminated from source files (13 quality gates)
- String concat eliminated — core.Concat() throughout
- 1:1 _test.go + _example_test.go for every source file
- Reference docs synced from core/go v0.8.0
- RFC-025 updated with net/http, net/url, io/fs quality gates
- lib.go: io/fs eliminated via Data.ListNames, Array[T].Deduplicate
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 01:27:46 +00:00
for _ , p := range paths {
if ! fs . IsDir ( p ) {
2026-03-17 17:45:04 +00:00
continue
}
feat(v0.8.0): full AX migration — ServiceRuntime, Actions, quality gates, transport
go-process:
- Register factory, Result lifecycle, 5 named Action handlers
- Start/Run/StartWithOptions/RunWithOptions all return core.Result
- core.ID() replaces fmt.Sprintf, core.As replaces errors.As
core/agent:
- PrepSubsystem + monitor.Subsystem + setup.Service embed ServiceRuntime[T]
- 22 named Actions + agent.completion Task pipeline in OnStartup
- ChannelNotifier removed — all IPC via c.ACTION(messages.X{})
- proc.go: all methods via s.Core().Process(), returns core.Result
- status.go: WriteAtomic + JSONMarshalString
- paths.go: Fs.NewUnrestricted() replaces unsafe.Pointer
- transport.go: ONE net/http file — HTTPGet/HTTPPost/HTTPDo/MCP transport
- All disallowed imports eliminated from source files (13 quality gates)
- String concat eliminated — core.Concat() throughout
- 1:1 _test.go + _example_test.go for every source file
- Reference docs synced from core/go v0.8.0
- RFC-025 updated with net/http, net/url, io/fs quality gates
- lib.go: io/fs eliminated via Data.ListNames, Array[T].Deduplicate
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 01:27:46 +00:00
name := core . PathBase ( p )
if ! s . hasRemote ( p , "github" ) {
2026-03-17 17:45:04 +00:00
continue
}
feat(v0.8.0): full AX migration — ServiceRuntime, Actions, quality gates, transport
go-process:
- Register factory, Result lifecycle, 5 named Action handlers
- Start/Run/StartWithOptions/RunWithOptions all return core.Result
- core.ID() replaces fmt.Sprintf, core.As replaces errors.As
core/agent:
- PrepSubsystem + monitor.Subsystem + setup.Service embed ServiceRuntime[T]
- 22 named Actions + agent.completion Task pipeline in OnStartup
- ChannelNotifier removed — all IPC via c.ACTION(messages.X{})
- proc.go: all methods via s.Core().Process(), returns core.Result
- status.go: WriteAtomic + JSONMarshalString
- paths.go: Fs.NewUnrestricted() replaces unsafe.Pointer
- transport.go: ONE net/http file — HTTPGet/HTTPPost/HTTPDo/MCP transport
- All disallowed imports eliminated from source files (13 quality gates)
- String concat eliminated — core.Concat() throughout
- 1:1 _test.go + _example_test.go for every source file
- Reference docs synced from core/go v0.8.0
- RFC-025 updated with net/http, net/url, io/fs quality gates
- lib.go: io/fs eliminated via Data.ListNames, Array[T].Deduplicate
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 01:27:46 +00:00
ahead := s . commitsAhead ( p , "github/main" , "HEAD" )
2026-03-17 17:45:04 +00:00
if ahead > 0 {
feat(v0.8.0): full AX migration — ServiceRuntime, Actions, quality gates, transport
go-process:
- Register factory, Result lifecycle, 5 named Action handlers
- Start/Run/StartWithOptions/RunWithOptions all return core.Result
- core.ID() replaces fmt.Sprintf, core.As replaces errors.As
core/agent:
- PrepSubsystem + monitor.Subsystem + setup.Service embed ServiceRuntime[T]
- 22 named Actions + agent.completion Task pipeline in OnStartup
- ChannelNotifier removed — all IPC via c.ACTION(messages.X{})
- proc.go: all methods via s.Core().Process(), returns core.Result
- status.go: WriteAtomic + JSONMarshalString
- paths.go: Fs.NewUnrestricted() replaces unsafe.Pointer
- transport.go: ONE net/http file — HTTPGet/HTTPPost/HTTPDo/MCP transport
- All disallowed imports eliminated from source files (13 quality gates)
- String concat eliminated — core.Concat() throughout
- 1:1 _test.go + _example_test.go for every source file
- Reference docs synced from core/go v0.8.0
- RFC-025 updated with net/http, net/url, io/fs quality gates
- lib.go: io/fs eliminated via Data.ListNames, Array[T].Deduplicate
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 01:27:46 +00:00
candidates = append ( candidates , name )
2026-03-17 17:45:04 +00:00
}
}
return candidates
}
2026-03-30 22:54:19 +00:00
// result := s.reviewRepo(ctx, repoDir, "go-io", "coderabbit", false, false)
2026-03-21 16:05:59 +00:00
func ( s * PrepSubsystem ) reviewRepo ( ctx context . Context , repoDir , repo , reviewer string , dryRun , localOnly bool ) ReviewResult {
2026-03-17 17:45:04 +00:00
result := ReviewResult { Repo : repo }
2026-03-30 15:48:21 +00:00
process := s . Core ( ) . Process ( )
2026-03-17 17:45:04 +00:00
2026-04-01 15:47:48 +00:00
if reviewer != "codex" {
if rl := s . loadRateLimitState ( ) ; rl != nil && rl . Limited && time . Now ( ) . Before ( rl . RetryAt ) {
result . Verdict = "rate_limited"
result . Detail = core . Sprintf ( "retry after %s" , rl . RetryAt . Format ( time . RFC3339 ) )
return result
}
2026-03-17 17:45:04 +00:00
}
2026-03-21 16:05:59 +00:00
if reviewer == "" {
reviewer = "coderabbit"
}
2026-03-25 09:51:57 +00:00
command , args := s . buildReviewCommand ( repoDir , reviewer )
2026-03-30 15:48:21 +00:00
r := process . RunIn ( ctx , repoDir , command , args ... )
feat(v0.8.0): full AX migration — ServiceRuntime, Actions, quality gates, transport
go-process:
- Register factory, Result lifecycle, 5 named Action handlers
- Start/Run/StartWithOptions/RunWithOptions all return core.Result
- core.ID() replaces fmt.Sprintf, core.As replaces errors.As
core/agent:
- PrepSubsystem + monitor.Subsystem + setup.Service embed ServiceRuntime[T]
- 22 named Actions + agent.completion Task pipeline in OnStartup
- ChannelNotifier removed — all IPC via c.ACTION(messages.X{})
- proc.go: all methods via s.Core().Process(), returns core.Result
- status.go: WriteAtomic + JSONMarshalString
- paths.go: Fs.NewUnrestricted() replaces unsafe.Pointer
- transport.go: ONE net/http file — HTTPGet/HTTPPost/HTTPDo/MCP transport
- All disallowed imports eliminated from source files (13 quality gates)
- String concat eliminated — core.Concat() throughout
- 1:1 _test.go + _example_test.go for every source file
- Reference docs synced from core/go v0.8.0
- RFC-025 updated with net/http, net/url, io/fs quality gates
- lib.go: io/fs eliminated via Data.ListNames, Array[T].Deduplicate
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 01:27:46 +00:00
output , _ := r . Value . ( string )
2026-03-17 17:45:04 +00:00
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
if core . Contains ( output , "Rate limit exceeded" ) || core . Contains ( output , "rate limit" ) {
2026-03-17 17:45:04 +00:00
result . Verdict = "rate_limited"
result . Detail = output
return result
}
feat(v0.8.0): full AX migration — ServiceRuntime, Actions, quality gates, transport
go-process:
- Register factory, Result lifecycle, 5 named Action handlers
- Start/Run/StartWithOptions/RunWithOptions all return core.Result
- core.ID() replaces fmt.Sprintf, core.As replaces errors.As
core/agent:
- PrepSubsystem + monitor.Subsystem + setup.Service embed ServiceRuntime[T]
- 22 named Actions + agent.completion Task pipeline in OnStartup
- ChannelNotifier removed — all IPC via c.ACTION(messages.X{})
- proc.go: all methods via s.Core().Process(), returns core.Result
- status.go: WriteAtomic + JSONMarshalString
- paths.go: Fs.NewUnrestricted() replaces unsafe.Pointer
- transport.go: ONE net/http file — HTTPGet/HTTPPost/HTTPDo/MCP transport
- All disallowed imports eliminated from source files (13 quality gates)
- String concat eliminated — core.Concat() throughout
- 1:1 _test.go + _example_test.go for every source file
- Reference docs synced from core/go v0.8.0
- RFC-025 updated with net/http, net/url, io/fs quality gates
- lib.go: io/fs eliminated via Data.ListNames, Array[T].Deduplicate
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 01:27:46 +00:00
if ! r . OK && ! core . Contains ( output , "No findings" ) && ! core . Contains ( output , "no issues" ) {
2026-03-17 17:45:04 +00:00
result . Verdict = "error"
result . Detail = output
return result
}
s . storeReviewOutput ( repoDir , repo , reviewer , output )
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
if core . Contains ( output , "No findings" ) || core . Contains ( output , "no issues" ) || core . Contains ( output , "LGTM" ) {
2026-03-17 17:45:04 +00:00
result . Verdict = "clean"
result . Findings = 0
if dryRun {
result . Action = "skipped (dry run)"
return result
}
if localOnly {
result . Action = "clean (local only)"
return result
}
if err := s . pushAndMerge ( ctx , repoDir , repo ) ; err != nil {
feat(v0.8.0): full AX migration — ServiceRuntime, Actions, quality gates, transport
go-process:
- Register factory, Result lifecycle, 5 named Action handlers
- Start/Run/StartWithOptions/RunWithOptions all return core.Result
- core.ID() replaces fmt.Sprintf, core.As replaces errors.As
core/agent:
- PrepSubsystem + monitor.Subsystem + setup.Service embed ServiceRuntime[T]
- 22 named Actions + agent.completion Task pipeline in OnStartup
- ChannelNotifier removed — all IPC via c.ACTION(messages.X{})
- proc.go: all methods via s.Core().Process(), returns core.Result
- status.go: WriteAtomic + JSONMarshalString
- paths.go: Fs.NewUnrestricted() replaces unsafe.Pointer
- transport.go: ONE net/http file — HTTPGet/HTTPPost/HTTPDo/MCP transport
- All disallowed imports eliminated from source files (13 quality gates)
- String concat eliminated — core.Concat() throughout
- 1:1 _test.go + _example_test.go for every source file
- Reference docs synced from core/go v0.8.0
- RFC-025 updated with net/http, net/url, io/fs quality gates
- lib.go: io/fs eliminated via Data.ListNames, Array[T].Deduplicate
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 01:27:46 +00:00
result . Action = core . Concat ( "push failed: " , err . Error ( ) )
2026-03-17 17:45:04 +00:00
} else {
result . Action = "merged"
}
} else {
result . Verdict = "findings"
result . Findings = countFindings ( output )
result . Detail = truncate ( output , 500 )
if dryRun {
result . Action = "skipped (dry run)"
return result
}
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
findingsFile := core . JoinPath ( repoDir , ".core" , "coderabbit-findings.txt" )
2026-03-22 03:41:07 +00:00
fs . Write ( findingsFile , output )
2026-03-17 17:45:04 +00:00
2026-03-26 06:38:02 +00:00
task := core . Sprintf (
"Fix CodeRabbit findings. The review output is in .core/coderabbit-findings.txt. Read it, verify each finding against the code, fix what's valid. Run tests. Commit: fix(coderabbit): address review findings\n\nFindings summary (%d issues):\n%s" ,
2026-03-17 17:45:04 +00:00
result . Findings , truncate ( output , 1500 ) )
2026-03-21 17:25:23 +00:00
if err := s . dispatchFixFromQueue ( ctx , repo , task ) ; err != nil {
result . Action = "fix_dispatch_failed"
result . Detail = err . Error ( )
} else {
result . Action = "fix_dispatched"
}
2026-03-17 17:45:04 +00:00
}
return result
}
2026-03-30 22:54:19 +00:00
// _ = s.pushAndMerge(ctx, repoDir, "go-io")
2026-03-17 17:45:04 +00:00
func ( s * PrepSubsystem ) pushAndMerge ( ctx context . Context , repoDir , repo string ) error {
2026-03-30 15:48:21 +00:00
process := s . Core ( ) . Process ( )
if r := process . RunIn ( ctx , repoDir , "git" , "push" , "github" , "HEAD:refs/heads/dev" , "--force" ) ; ! r . OK {
2026-03-26 06:38:02 +00:00
return core . E ( "pushAndMerge" , core . Concat ( "push failed: " , r . Value . ( string ) ) , nil )
2026-03-17 17:45:04 +00:00
}
2026-03-30 15:48:21 +00:00
process . RunIn ( ctx , repoDir , "gh" , "pr" , "ready" , "--repo" , core . Concat ( GitHubOrg ( ) , "/" , repo ) )
2026-03-25 09:51:57 +00:00
2026-03-30 15:48:21 +00:00
if r := process . RunIn ( ctx , repoDir , "gh" , "pr" , "merge" , "--merge" , "--delete-branch" ) ; ! r . OK {
2026-03-26 06:38:02 +00:00
return core . E ( "pushAndMerge" , core . Concat ( "merge failed: " , r . Value . ( string ) ) , nil )
2026-03-17 17:45:04 +00:00
}
return nil
}
2026-03-30 22:54:19 +00:00
// _ = s.dispatchFixFromQueue(ctx, "go-io", task)
2026-03-21 17:25:23 +00:00
func ( s * PrepSubsystem ) dispatchFixFromQueue ( ctx context . Context , repo , task string ) error {
2026-03-17 17:45:04 +00:00
input := DispatchInput {
Repo : repo ,
Task : task ,
Agent : "claude:opus" ,
}
2026-03-21 17:25:23 +00:00
_ , out , err := s . dispatch ( ctx , nil , input )
if err != nil {
return err
}
if ! out . Success {
2026-03-26 06:38:02 +00:00
return core . E ( "dispatchFixFromQueue" , core . Concat ( "dispatch failed for " , repo ) , nil )
2026-03-21 17:25:23 +00:00
}
return nil
2026-03-17 17:45:04 +00:00
}
2026-03-30 22:54:19 +00:00
// findings := countFindings(output)
2026-03-17 17:45:04 +00:00
func countFindings ( output string ) int {
count := 0
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
for _ , line := range core . Split ( output , "\n" ) {
trimmed := core . Trim ( line )
if core . HasPrefix ( trimmed , "- " ) || core . HasPrefix ( trimmed , "* " ) ||
core . Contains ( trimmed , "Issue:" ) || core . Contains ( trimmed , "Finding:" ) ||
core . Contains ( trimmed , "⚠" ) || core . Contains ( trimmed , "❌" ) {
2026-03-17 17:45:04 +00:00
count ++
}
}
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
if count == 0 && ! core . Contains ( output , "No findings" ) {
2026-03-31 05:28:26 +00:00
count = 1
2026-03-17 17:45:04 +00:00
}
return count
}
2026-03-30 22:54:19 +00:00
// delay := parseRetryAfter("please try after 4 minutes and 56 seconds")
2026-03-17 17:45:04 +00:00
func parseRetryAfter ( message string ) time . Duration {
2026-03-30 15:33:01 +00:00
if retryAfterPattern == nil {
return 5 * time . Minute
}
matches := retryAfterPattern . FindStringSubmatch ( message )
2026-03-17 17:45:04 +00:00
if len ( matches ) >= 2 {
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
mins := parseInt ( matches [ 1 ] )
2026-03-17 17:45:04 +00:00
secs := 0
if len ( matches ) >= 3 && matches [ 2 ] != "" {
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
secs = parseInt ( matches [ 2 ] )
2026-03-17 17:45:04 +00:00
}
return time . Duration ( mins ) * time . Minute + time . Duration ( secs ) * time . Second
}
return 5 * time . Minute
}
2026-03-30 22:54:19 +00:00
// cmd, args := s.buildReviewCommand(repoDir, "coderabbit")
2026-03-25 09:51:57 +00:00
func ( s * PrepSubsystem ) buildReviewCommand ( repoDir , reviewer string ) ( string , [ ] string ) {
2026-03-17 17:45:04 +00:00
switch reviewer {
case "codex" :
2026-03-25 09:51:57 +00:00
return "codex" , [ ] string { "review" , "--base" , "github/main" }
2026-03-31 05:34:44 +00:00
default :
2026-03-25 09:51:57 +00:00
return "coderabbit" , [ ] string { "review" , "--plain" , "--base" , "github/main" , "--config" , "CLAUDE.md" , "--cwd" , repoDir }
2026-03-17 17:45:04 +00:00
}
}
2026-03-30 22:54:19 +00:00
// s.storeReviewOutput(repoDir, "go-io", "coderabbit", output)
2026-03-17 17:45:04 +00:00
func ( s * PrepSubsystem ) storeReviewOutput ( repoDir , repo , reviewer , output string ) {
2026-03-30 18:52:15 +00:00
dataDir := core . JoinPath ( HomeDir ( ) , ".core" , "training" , "reviews" )
2026-03-22 03:41:07 +00:00
fs . EnsureDir ( dataDir )
2026-03-17 17:45:04 +00:00
timestamp := time . Now ( ) . Format ( "2006-01-02T15-04-05" )
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
filename := core . Sprintf ( "%s_%s_%s.txt" , repo , reviewer , timestamp )
2026-03-17 17:45:04 +00:00
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
fs . Write ( core . JoinPath ( dataDir , filename ) , output )
2026-03-17 17:45:04 +00:00
entry := map [ string ] string {
"repo" : repo ,
"reviewer" : reviewer ,
"timestamp" : time . Now ( ) . Format ( time . RFC3339 ) ,
"output" : output ,
"verdict" : "clean" ,
}
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
if ! core . Contains ( output , "No findings" ) && ! core . Contains ( output , "no issues" ) {
2026-03-17 17:45:04 +00:00
entry [ "verdict" ] = "findings"
}
feat(v0.8.0): full AX migration — ServiceRuntime, Actions, quality gates, transport
go-process:
- Register factory, Result lifecycle, 5 named Action handlers
- Start/Run/StartWithOptions/RunWithOptions all return core.Result
- core.ID() replaces fmt.Sprintf, core.As replaces errors.As
core/agent:
- PrepSubsystem + monitor.Subsystem + setup.Service embed ServiceRuntime[T]
- 22 named Actions + agent.completion Task pipeline in OnStartup
- ChannelNotifier removed — all IPC via c.ACTION(messages.X{})
- proc.go: all methods via s.Core().Process(), returns core.Result
- status.go: WriteAtomic + JSONMarshalString
- paths.go: Fs.NewUnrestricted() replaces unsafe.Pointer
- transport.go: ONE net/http file — HTTPGet/HTTPPost/HTTPDo/MCP transport
- All disallowed imports eliminated from source files (13 quality gates)
- String concat eliminated — core.Concat() throughout
- 1:1 _test.go + _example_test.go for every source file
- Reference docs synced from core/go v0.8.0
- RFC-025 updated with net/http, net/url, io/fs quality gates
- lib.go: io/fs eliminated via Data.ListNames, Array[T].Deduplicate
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 01:27:46 +00:00
jsonLine := core . JSONMarshalString ( entry )
2026-03-17 17:45:04 +00:00
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
jsonlPath := core . JoinPath ( dataDir , "reviews.jsonl" )
refactor(agentic): route file I/O through core.Fs
Replace raw os.* file operations with Core Fs equivalents:
- os.Stat → fs.Exists/fs.IsFile/fs.IsDir (resume, pr, plan, mirror, prep)
- os.ReadDir → fs.List (queue, status, plan, mirror, review_queue)
- os.Remove → fs.Delete (dispatch)
- os.OpenFile(append) → fs.Append (events, review_queue)
- strings.Replace → core.Replace (scan)
Eliminates os import from resume.go, pr.go. Eliminates strings
import from scan.go. Trades os for io in events.go.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 09:08:45 +00:00
r := fs . Append ( jsonlPath )
if ! r . OK {
return
2026-03-17 19:27:44 +00:00
}
feat(v0.8.0): full AX migration — ServiceRuntime, Actions, quality gates, transport
go-process:
- Register factory, Result lifecycle, 5 named Action handlers
- Start/Run/StartWithOptions/RunWithOptions all return core.Result
- core.ID() replaces fmt.Sprintf, core.As replaces errors.As
core/agent:
- PrepSubsystem + monitor.Subsystem + setup.Service embed ServiceRuntime[T]
- 22 named Actions + agent.completion Task pipeline in OnStartup
- ChannelNotifier removed — all IPC via c.ACTION(messages.X{})
- proc.go: all methods via s.Core().Process(), returns core.Result
- status.go: WriteAtomic + JSONMarshalString
- paths.go: Fs.NewUnrestricted() replaces unsafe.Pointer
- transport.go: ONE net/http file — HTTPGet/HTTPPost/HTTPDo/MCP transport
- All disallowed imports eliminated from source files (13 quality gates)
- String concat eliminated — core.Concat() throughout
- 1:1 _test.go + _example_test.go for every source file
- Reference docs synced from core/go v0.8.0
- RFC-025 updated with net/http, net/url, io/fs quality gates
- lib.go: io/fs eliminated via Data.ListNames, Array[T].Deduplicate
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 01:27:46 +00:00
core . WriteAll ( r . Value , core . Concat ( jsonLine , "\n" ) )
2026-03-17 17:45:04 +00:00
}
2026-03-30 22:54:19 +00:00
// s.saveRateLimitState(&RateLimitInfo{Limited: true, RetryAt: time.Now().Add(30 * time.Minute)})
2026-03-17 17:45:04 +00:00
func ( s * PrepSubsystem ) saveRateLimitState ( info * RateLimitInfo ) {
2026-03-30 18:52:15 +00:00
path := core . JoinPath ( HomeDir ( ) , ".core" , "coderabbit-ratelimit.json" )
2026-03-30 18:59:41 +00:00
if r := fs . WriteAtomic ( path , core . JSONMarshalString ( info ) ) ; ! r . OK {
if err , ok := r . Value . ( error ) ; ok {
core . Warn ( "reviewQueue: failed to persist rate limit state" , "path" , path , "reason" , err )
return
}
core . Warn ( "reviewQueue: failed to persist rate limit state" , "path" , path )
}
2026-03-17 17:45:04 +00:00
}
2026-03-30 22:54:19 +00:00
// info := s.loadRateLimitState()
2026-03-17 17:45:04 +00:00
func ( s * PrepSubsystem ) loadRateLimitState ( ) * RateLimitInfo {
2026-03-30 18:52:15 +00:00
path := core . JoinPath ( HomeDir ( ) , ".core" , "coderabbit-ratelimit.json" )
2026-03-22 03:41:07 +00:00
r := fs . Read ( path )
if ! r . OK {
2026-03-17 17:45:04 +00:00
return nil
}
var info RateLimitInfo
feat(v0.8.0): full AX migration — ServiceRuntime, Actions, quality gates, transport
go-process:
- Register factory, Result lifecycle, 5 named Action handlers
- Start/Run/StartWithOptions/RunWithOptions all return core.Result
- core.ID() replaces fmt.Sprintf, core.As replaces errors.As
core/agent:
- PrepSubsystem + monitor.Subsystem + setup.Service embed ServiceRuntime[T]
- 22 named Actions + agent.completion Task pipeline in OnStartup
- ChannelNotifier removed — all IPC via c.ACTION(messages.X{})
- proc.go: all methods via s.Core().Process(), returns core.Result
- status.go: WriteAtomic + JSONMarshalString
- paths.go: Fs.NewUnrestricted() replaces unsafe.Pointer
- transport.go: ONE net/http file — HTTPGet/HTTPPost/HTTPDo/MCP transport
- All disallowed imports eliminated from source files (13 quality gates)
- String concat eliminated — core.Concat() throughout
- 1:1 _test.go + _example_test.go for every source file
- Reference docs synced from core/go v0.8.0
- RFC-025 updated with net/http, net/url, io/fs quality gates
- lib.go: io/fs eliminated via Data.ListNames, Array[T].Deduplicate
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 01:27:46 +00:00
if ur := core . JSONUnmarshalString ( r . Value . ( string ) , & info ) ; ! ur . OK {
2026-03-17 17:45:04 +00:00
return nil
}
return & info
}