fix(ax): preserve transport causes and remove MustCompile
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
7672e0922f
commit
a757ca81e3
6 changed files with 50 additions and 17 deletions
|
|
@ -435,6 +435,7 @@ func (s *PrepSubsystem) gitCmd(ctx context.Context, dir string, args ...string)
|
|||
## Changelog
|
||||
|
||||
- 2026-03-29: cmd/core-agent no longer rewrites `os.Args` before startup. The binary-owned commands now use named handlers, keeping the entrypoint on Core CLI primitives instead of repo-local argument mutation.
|
||||
- 2026-03-26: WIP — net/http consolidated to transport.go (ONE file). net/url + io/fs eliminated. RFC-025 updated with 3 new quality gates (net/http, net/url, io/fs). 1:1 test + example test coverage. Array[T].Deduplicate replaces custom helpers. Remaining: remove dead `client` field from test literals, brain/provider.go Gin handler.
|
||||
- 2026-03-30: transport helpers preserve request and read causes, brain direct API calls surface upstream bodies, and review queue retry parsing no longer uses `MustCompile`.
|
||||
- 2026-03-26: net/http consolidated to transport.go (ONE file). net/url + io/fs eliminated. RFC-025 updated with 3 new quality gates (net/http, net/url, io/fs). 1:1 test + example test coverage. Array[T].Deduplicate replaces custom helpers.
|
||||
- 2026-03-25: Quality gates pass. Zero disallowed imports (all 10). encoding/json→Core JSON. path/filepath→Core Path. os→Core Env/Fs. io→Core ReadAll/WriteAll. go-process fully Result-native. ServiceRuntime on all subsystems. 22 named Actions + Task pipeline. ChannelNotifier→IPC. Reference docs synced.
|
||||
- 2026-03-25: Initial spec — written with full core/go v0.8.0 domain context.
|
||||
|
|
|
|||
|
|
@ -434,6 +434,7 @@ func (s *PrepSubsystem) gitCmd(ctx context.Context, dir string, args ...string)
|
|||
|
||||
## Changelog
|
||||
|
||||
- 2026-03-30: transport helpers preserve request and read causes, brain direct API calls surface upstream bodies, and review queue retry parsing no longer uses `MustCompile`.
|
||||
- 2026-03-30: main now logs startup failures with structured context, and the workspace contract reference restored usage-example comments for the Action lifecycle messages.
|
||||
- 2026-03-30: plan IDs now come from core.ID(), workspace prep validates org/repo names with core.ValidateName, and plan paths use core.SanitisePath.
|
||||
- 2026-03-29: cmd/core-agent no longer rewrites `os.Args` before startup. The binary-owned commands now use named handlers, keeping the entrypoint on Core CLI primitives instead of repo-local argument mutation.
|
||||
|
|
|
|||
|
|
@ -53,6 +53,16 @@ type RateLimitInfo struct {
|
|||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
var retryAfterPattern = compileRetryAfterPattern()
|
||||
|
||||
func compileRetryAfterPattern() *regexp.Regexp {
|
||||
pattern, err := regexp.Compile(`(\d+)\s*minutes?\s*(?:and\s*)?(\d+)?\s*seconds?`)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return pattern
|
||||
}
|
||||
|
||||
func (s *PrepSubsystem) registerReviewQueueTool(server *mcp.Server) {
|
||||
mcp.AddTool(server, &mcp.Tool{
|
||||
Name: "agentic_review_queue",
|
||||
|
|
@ -293,8 +303,10 @@ func countFindings(output string) int {
|
|||
// parseRetryAfter extracts the retry duration from a rate limit message.
|
||||
// Example: "please try after 4 minutes and 56 seconds"
|
||||
func parseRetryAfter(message string) time.Duration {
|
||||
re := regexp.MustCompile(`(\d+)\s*minutes?\s*(?:and\s*)?(\d+)?\s*seconds?`)
|
||||
matches := re.FindStringSubmatch(message)
|
||||
if retryAfterPattern == nil {
|
||||
return 5 * time.Minute
|
||||
}
|
||||
matches := retryAfterPattern.FindStringSubmatch(message)
|
||||
if len(matches) >= 2 {
|
||||
mins := parseInt(matches[1])
|
||||
secs := 0
|
||||
|
|
|
|||
|
|
@ -48,7 +48,8 @@ func (s *httpStream) Send(data []byte) error {
|
|||
|
||||
r := core.ReadAll(resp.Body)
|
||||
if !r.OK {
|
||||
return core.E("httpStream.Send", "failed to read response", nil)
|
||||
err, _ := r.Value.(error)
|
||||
return core.E("httpStream.Send", "failed to read response", err)
|
||||
}
|
||||
s.response = []byte(r.Value.(string))
|
||||
return nil
|
||||
|
|
@ -183,7 +184,7 @@ func httpDo(ctx context.Context, method, url, body, token, authScheme string) co
|
|||
req, err = http.NewRequestWithContext(ctx, method, url, nil)
|
||||
}
|
||||
if err != nil {
|
||||
return core.Result{OK: false}
|
||||
return core.Result{Value: core.E("httpDo", "create request", err), OK: false}
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
|
@ -197,12 +198,13 @@ func httpDo(ctx context.Context, method, url, body, token, authScheme string) co
|
|||
|
||||
resp, err := defaultClient.Do(req)
|
||||
if err != nil {
|
||||
return core.Result{OK: false}
|
||||
return core.Result{Value: core.E("httpDo", "request failed", err), OK: false}
|
||||
}
|
||||
|
||||
r := core.ReadAll(resp.Body)
|
||||
if !r.OK {
|
||||
return core.Result{OK: false}
|
||||
err, _ := r.Value.(error)
|
||||
return core.Result{Value: core.E("httpDo", "failed to read response", err), OK: false}
|
||||
}
|
||||
|
||||
return core.Result{Value: r.Value.(string), OK: resp.StatusCode < 400}
|
||||
|
|
@ -246,13 +248,13 @@ func mcpInitializeResult(ctx context.Context, url, token string) core.Result {
|
|||
body := core.JSONMarshalString(initReq)
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", url, core.NewReader(body))
|
||||
if err != nil {
|
||||
return core.Result{Value: core.E("mcpInitialize", "create request", nil), OK: false}
|
||||
return core.Result{Value: core.E("mcpInitialize", "create request", err), OK: false}
|
||||
}
|
||||
mcpHeaders(req, token, "")
|
||||
|
||||
resp, err := defaultClient.Do(req)
|
||||
if err != nil {
|
||||
return core.Result{Value: core.E("mcpInitialize", "request failed", nil), OK: false}
|
||||
return core.Result{Value: core.E("mcpInitialize", "request failed", err), OK: false}
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
|
|
@ -270,7 +272,10 @@ func mcpInitializeResult(ctx context.Context, url, token string) core.Result {
|
|||
"jsonrpc": "2.0",
|
||||
"method": "notifications/initialized",
|
||||
})
|
||||
notifReq, _ := http.NewRequestWithContext(ctx, "POST", url, core.NewReader(notif))
|
||||
notifReq, err := http.NewRequestWithContext(ctx, "POST", url, core.NewReader(notif))
|
||||
if err != nil {
|
||||
return core.Result{Value: core.E("mcpInitialize", "create notification request", err), OK: false}
|
||||
}
|
||||
mcpHeaders(notifReq, token, sessionID)
|
||||
notifResp, err := defaultClient.Do(notifReq)
|
||||
if err == nil {
|
||||
|
|
@ -300,13 +305,13 @@ func mcpCall(ctx context.Context, url, token, sessionID string, body []byte) ([]
|
|||
func mcpCallResult(ctx context.Context, url, token, sessionID string, body []byte) core.Result {
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", url, core.NewReader(string(body)))
|
||||
if err != nil {
|
||||
return core.Result{Value: core.E("mcpCall", "create request", nil), OK: false}
|
||||
return core.Result{Value: core.E("mcpCall", "create request", err), OK: false}
|
||||
}
|
||||
mcpHeaders(req, token, sessionID)
|
||||
|
||||
resp, err := defaultClient.Do(req)
|
||||
if err != nil {
|
||||
return core.Result{Value: core.E("mcpCall", "request failed", nil), OK: false}
|
||||
return core.Result{Value: core.E("mcpCall", "request failed", err), OK: false}
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
|
|
@ -339,9 +344,6 @@ func readSSEDataResult(resp *http.Response) core.Result {
|
|||
r := core.ReadAll(resp.Body)
|
||||
if !r.OK {
|
||||
err, _ := r.Value.(error)
|
||||
if err == nil {
|
||||
return core.Result{Value: core.E("readSSEData", "failed to read response", nil), OK: false}
|
||||
}
|
||||
return core.Result{Value: core.E("readSSEData", "failed to read response", err), OK: false}
|
||||
}
|
||||
for _, line := range core.Split(r.Value.(string), "\n") {
|
||||
|
|
|
|||
|
|
@ -36,6 +36,15 @@ func TestTransport_HTTPGet_Good(t *testing.T) {
|
|||
assert.Equal(t, `{"status":"ok"}`, result.Value.(string))
|
||||
}
|
||||
|
||||
func TestTransport_HTTPGet_Bad_InvalidURL(t *testing.T) {
|
||||
result := HTTPGet(context.Background(), "://bad", "", "")
|
||||
|
||||
assert.False(t, result.OK)
|
||||
err, ok := result.Value.(error)
|
||||
require.True(t, ok)
|
||||
assert.Contains(t, err.Error(), "create request")
|
||||
}
|
||||
|
||||
func TestTransport_DriveGet_Good(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, "/repos/core/go-io", r.URL.Path)
|
||||
|
|
|
|||
|
|
@ -108,6 +108,7 @@ func (s *DirectSubsystem) apiCall(ctx context.Context, method, path string, body
|
|||
if s.apiKey == "" {
|
||||
return core.Result{
|
||||
Value: core.E("brain.apiCall", "no API key (set CORE_BRAIN_KEY or create ~/.claude/brain.key)", nil),
|
||||
OK: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -119,13 +120,20 @@ func (s *DirectSubsystem) apiCall(ctx context.Context, method, path string, body
|
|||
r := agentic.HTTPDo(ctx, method, requestURL, bodyStr, s.apiKey, "Bearer")
|
||||
if !r.OK {
|
||||
core.Error("brain API call failed", "method", method, "path", path)
|
||||
return core.Result{Value: core.E("brain.apiCall", "API call failed", nil)}
|
||||
if err, ok := r.Value.(error); ok {
|
||||
return core.Result{Value: core.E("brain.apiCall", "API call failed", err), OK: false}
|
||||
}
|
||||
if responseBody, ok := r.Value.(string); ok && responseBody != "" {
|
||||
return core.Result{Value: core.E("brain.apiCall", core.Concat("API call failed: ", core.Trim(responseBody)), nil), OK: false}
|
||||
}
|
||||
return core.Result{Value: core.E("brain.apiCall", "API call failed", nil), OK: false}
|
||||
}
|
||||
|
||||
var result map[string]any
|
||||
if ur := core.JSONUnmarshalString(r.Value.(string), &result); !ur.OK {
|
||||
core.Error("brain API response parse failed", "method", method, "path", path)
|
||||
return core.Result{Value: core.E("brain.apiCall", "parse response", nil)}
|
||||
err, _ := ur.Value.(error)
|
||||
return core.Result{Value: core.E("brain.apiCall", "parse response", err), OK: false}
|
||||
}
|
||||
|
||||
return core.Result{Value: result, OK: true}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue