diff --git a/pkg/agentic/actions.go b/pkg/agentic/actions.go index a12afbe..55f5a45 100644 --- a/pkg/agentic/actions.go +++ b/pkg/agentic/actions.go @@ -239,7 +239,7 @@ func (s *PrepSubsystem) handleAutoPR(ctx context.Context, options core.Options) Repo: workspaceStatus.Repo, Branch: workspaceStatus.Branch, PRURL: workspaceStatus.PRURL, - PRNum: extractPRNumber(workspaceStatus.PRURL), + PRNum: extractPullRequestNumber(workspaceStatus.PRURL), }) } } @@ -270,13 +270,13 @@ func (s *PrepSubsystem) handleVerify(ctx context.Context, options core.Options) s.Core().ACTION(messages.PRMerged{ Repo: workspaceStatus.Repo, PRURL: workspaceStatus.PRURL, - PRNum: extractPRNumber(workspaceStatus.PRURL), + PRNum: extractPullRequestNumber(workspaceStatus.PRURL), }) } else if workspaceStatus.Question != "" { s.Core().ACTION(messages.PRNeedsReview{ Repo: workspaceStatus.Repo, PRURL: workspaceStatus.PRURL, - PRNum: extractPRNumber(workspaceStatus.PRURL), + PRNum: extractPullRequestNumber(workspaceStatus.PRURL), Reason: workspaceStatus.Question, }) } diff --git a/pkg/agentic/auto_pr.go b/pkg/agentic/auto_pr.go index 48ba0ce..8d9519c 100644 --- a/pkg/agentic/auto_pr.go +++ b/pkg/agentic/auto_pr.go @@ -23,18 +23,18 @@ func (s *PrepSubsystem) autoCreatePR(workspaceDir string) { process := s.Core().Process() // PRs target dev — agents never merge directly to main - base := "dev" + defaultBranch := "dev" - processResult := process.RunIn(ctx, repoDir, "git", "log", "--oneline", core.Concat("origin/", base, "..HEAD")) + processResult := process.RunIn(ctx, repoDir, "git", "log", "--oneline", core.Concat("origin/", defaultBranch, "..HEAD")) if !processResult.OK { return } - out := core.Trim(processResult.Value.(string)) - if out == "" { + commitLogOutput := core.Trim(processResult.Value.(string)) + if commitLogOutput == "" { return } - commitCount := len(core.Split(out, "\n")) + commitCount := len(core.Split(commitLogOutput, "\n")) org := workspaceStatus.Org if org == "" { @@ -62,7 +62,7 @@ func (s *PrepSubsystem) autoCreatePR(workspaceDir string) { ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() - prURL, _, err := s.forgeCreatePR(ctx, org, workspaceStatus.Repo, workspaceStatus.Branch, base, title, body) + pullRequestURL, _, err := s.forgeCreatePR(ctx, org, workspaceStatus.Repo, workspaceStatus.Branch, defaultBranch, title, body) if err != nil { if result := ReadStatusResult(workspaceDir); result.OK { workspaceStatusUpdate, ok := workspaceStatusValue(result) @@ -81,7 +81,7 @@ func (s *PrepSubsystem) autoCreatePR(workspaceDir string) { if !ok { return } - workspaceStatusUpdate.PRURL = prURL + workspaceStatusUpdate.PRURL = pullRequestURL writeStatusResult(workspaceDir, workspaceStatusUpdate) } } diff --git a/pkg/agentic/commands.go b/pkg/agentic/commands.go index 83ba96e..e6f4da3 100644 --- a/pkg/agentic/commands.go +++ b/pkg/agentic/commands.go @@ -42,7 +42,7 @@ func (s *PrepSubsystem) runTask(ctx context.Context, options core.Options) core. repo := options.String("repo") agent := options.String("agent") task := options.String("task") - issueStr := options.String("issue") + issueValue := options.String("issue") org := options.String("org") if repo == "" || task == "" { @@ -56,7 +56,7 @@ func (s *PrepSubsystem) runTask(ctx context.Context, options core.Options) core. org = "core" } - issue := parseIntStr(issueStr) + issue := parseIntStr(issueValue) core.Print(nil, "core-agent run task") core.Print(nil, " repo: %s/%s", org, repo) @@ -131,30 +131,30 @@ func (s *PrepSubsystem) cmdPrep(options core.Options) core.Result { prepInput.Branch = "dev" } - _, out, err := s.TestPrepWorkspace(context.Background(), prepInput) + _, prepOutput, err := s.TestPrepWorkspace(context.Background(), prepInput) if err != nil { core.Print(nil, "error: %v", err) return core.Result{Value: err, OK: false} } - core.Print(nil, "workspace: %s", out.WorkspaceDir) - core.Print(nil, "repo: %s", out.RepoDir) - core.Print(nil, "branch: %s", out.Branch) - core.Print(nil, "resumed: %v", out.Resumed) - core.Print(nil, "memories: %d", out.Memories) - core.Print(nil, "consumers: %d", out.Consumers) - if out.Prompt != "" { + core.Print(nil, "workspace: %s", prepOutput.WorkspaceDir) + core.Print(nil, "repo: %s", prepOutput.RepoDir) + core.Print(nil, "branch: %s", prepOutput.Branch) + core.Print(nil, "resumed: %v", prepOutput.Resumed) + core.Print(nil, "memories: %d", prepOutput.Memories) + core.Print(nil, "consumers: %d", prepOutput.Consumers) + if prepOutput.Prompt != "" { core.Print(nil, "") - core.Print(nil, "--- prompt (%d chars) ---", len(out.Prompt)) - core.Print(nil, "%s", out.Prompt) + core.Print(nil, "--- prompt (%d chars) ---", len(prepOutput.Prompt)) + core.Print(nil, "%s", prepOutput.Prompt) } return core.Result{OK: true} } func (s *PrepSubsystem) cmdStatus(_ core.Options) core.Result { workspaceRoot := WorkspaceRoot() - fsys := s.Core().Fs() - listResult := fsys.List(workspaceRoot) + filesystem := s.Core().Fs() + listResult := filesystem.List(workspaceRoot) if !listResult.OK { core.Print(nil, "no workspaces found at %s", workspaceRoot) return core.Result{OK: true} @@ -231,12 +231,12 @@ func (s *PrepSubsystem) cmdExtract(options core.Options) core.Result { return core.Result{Value: core.E("agentic.cmdExtract", core.Concat("extract workspace template ", templateName), nil), OK: false} } - fsys := s.Core().Fs() + filesystem := s.Core().Fs() paths := core.PathGlob(core.JoinPath(target, "*")) for _, p := range paths { name := core.PathBase(p) marker := " " - if fsys.IsDir(p) { + if filesystem.IsDir(p) { marker = "/" } core.Print(nil, " %s%s", name, marker) diff --git a/pkg/agentic/commands_workspace.go b/pkg/agentic/commands_workspace.go index bed4977..b1f7ff3 100644 --- a/pkg/agentic/commands_workspace.go +++ b/pkg/agentic/commands_workspace.go @@ -40,7 +40,7 @@ func (s *PrepSubsystem) cmdWorkspaceList(_ core.Options) core.Result { func (s *PrepSubsystem) cmdWorkspaceClean(options core.Options) core.Result { workspaceRoot := WorkspaceRoot() - fsys := s.Core().Fs() + filesystem := s.Core().Fs() filter := options.String("_arg") if filter == "" { filter = "all" @@ -86,7 +86,7 @@ func (s *PrepSubsystem) cmdWorkspaceClean(options core.Options) core.Result { for _, name := range toRemove { path := core.JoinPath(workspaceRoot, name) - fsys.DeleteAll(path) + filesystem.DeleteAll(path) core.Print(nil, " removed %s", name) } core.Print(nil, "\n %d workspaces removed", len(toRemove)) diff --git a/pkg/agentic/deps.go b/pkg/agentic/deps.go index df31142..0ded1fc 100644 --- a/pkg/agentic/deps.go +++ b/pkg/agentic/deps.go @@ -114,8 +114,8 @@ func parseCoreDeps(gomod string) []coreDep { repo := suffix if core.HasPrefix(suffix, "core/") { // core/process → go-process - sub := core.TrimPrefix(suffix, "core/") - repo = core.Concat("go-", sub) + repoSuffix := core.TrimPrefix(suffix, "core/") + repo = core.Concat("go-", repoSuffix) } else if suffix == "core" { repo = "go" } diff --git a/pkg/agentic/dispatch.go b/pkg/agentic/dispatch.go index 2255668..c36920d 100644 --- a/pkg/agentic/dispatch.go +++ b/pkg/agentic/dispatch.go @@ -240,12 +240,12 @@ func agentOutputFile(workspaceDir, agent string) string { // detectFinalStatus reads workspace state after agent exit to determine outcome. // Returns (status, question) — "completed", "blocked", or "failed". -func detectFinalStatus(repoDir string, exitCode int, procStatus string) (string, string) { +func detectFinalStatus(repoDir string, exitCode int, processStatus string) (string, string) { blockedPath := core.JoinPath(repoDir, "BLOCKED.md") if blockedResult := fs.Read(blockedPath); blockedResult.OK && core.Trim(blockedResult.Value.(string)) != "" { return "blocked", core.Trim(blockedResult.Value.(string)) } - if exitCode != 0 || procStatus == "failed" || procStatus == "killed" { + if exitCode != 0 || processStatus == "failed" || processStatus == "killed" { question := "" if exitCode != 0 { question = core.Sprintf("Agent exited with code %d", exitCode) @@ -348,14 +348,14 @@ func (s *PrepSubsystem) broadcastComplete(agent, workspaceDir, finalStatus strin // onAgentComplete handles all post-completion logic for a spawned agent. // Called from the monitoring goroutine after the process exits. -func (s *PrepSubsystem) onAgentComplete(agent, workspaceDir, outputFile string, exitCode int, procStatus, output string) { +func (s *PrepSubsystem) onAgentComplete(agent, workspaceDir, outputFile string, exitCode int, processStatus, output string) { // Save output if output != "" { fs.Write(outputFile, output) } repoDir := WorkspaceRepoDir(workspaceDir) - finalStatus, question := detectFinalStatus(repoDir, exitCode, procStatus) + finalStatus, question := detectFinalStatus(repoDir, exitCode, processStatus) // Update workspace status (disk + registry) result := ReadStatusResult(workspaceDir) diff --git a/pkg/agentic/logic_test.go b/pkg/agentic/logic_test.go index d1c5036..f9a58b8 100644 --- a/pkg/agentic/logic_test.go +++ b/pkg/agentic/logic_test.go @@ -727,33 +727,33 @@ func TestHandlers_FindWorkspaceByPR_Ugly_CorruptStatusFile(t *testing.T) { assert.Equal(t, "", result) } -// --- extractPRNumber --- +// --- extractPullRequestNumber --- -func TestVerify_ExtractPRNumber_Good_FullURL(t *testing.T) { - assert.Equal(t, 42, extractPRNumber("https://forge.lthn.ai/core/agent/pulls/42")) - assert.Equal(t, 1, extractPRNumber("https://forge.lthn.ai/core/go-io/pulls/1")) - assert.Equal(t, 999, extractPRNumber("https://forge.lthn.ai/core/go-log/pulls/999")) +func TestVerify_ExtractPullRequestNumber_Good_FullURL(t *testing.T) { + assert.Equal(t, 42, extractPullRequestNumber("https://forge.lthn.ai/core/agent/pulls/42")) + assert.Equal(t, 1, extractPullRequestNumber("https://forge.lthn.ai/core/go-io/pulls/1")) + assert.Equal(t, 999, extractPullRequestNumber("https://forge.lthn.ai/core/go-log/pulls/999")) } -func TestVerify_ExtractPRNumber_Good_NumberOnly(t *testing.T) { +func TestVerify_ExtractPullRequestNumber_Good_NumberOnly(t *testing.T) { // If someone passes a bare number as a URL it should still work - assert.Equal(t, 7, extractPRNumber("7")) + assert.Equal(t, 7, extractPullRequestNumber("7")) } -func TestVerify_ExtractPRNumber_Bad_EmptyURL(t *testing.T) { - assert.Equal(t, 0, extractPRNumber("")) +func TestVerify_ExtractPullRequestNumber_Bad_EmptyURL(t *testing.T) { + assert.Equal(t, 0, extractPullRequestNumber("")) } -func TestVerify_ExtractPRNumber_Bad_TrailingSlash(t *testing.T) { +func TestVerify_ExtractPullRequestNumber_Bad_TrailingSlash(t *testing.T) { // URL ending with slash has empty last segment - assert.Equal(t, 0, extractPRNumber("https://forge.lthn.ai/core/go-io/pulls/")) + assert.Equal(t, 0, extractPullRequestNumber("https://forge.lthn.ai/core/go-io/pulls/")) } -func TestVerify_ExtractPRNumber_Bad_NonNumericEnd(t *testing.T) { - assert.Equal(t, 0, extractPRNumber("https://forge.lthn.ai/core/go-io/pulls/abc")) +func TestVerify_ExtractPullRequestNumber_Bad_NonNumericEnd(t *testing.T) { + assert.Equal(t, 0, extractPullRequestNumber("https://forge.lthn.ai/core/go-io/pulls/abc")) } -func TestVerify_ExtractPRNumber_Ugly_JustSlashes(t *testing.T) { +func TestVerify_ExtractPullRequestNumber_Ugly_JustSlashes(t *testing.T) { // All slashes — last segment is empty - assert.Equal(t, 0, extractPRNumber("///")) + assert.Equal(t, 0, extractPullRequestNumber("///")) } diff --git a/pkg/agentic/mirror.go b/pkg/agentic/mirror.go index 1a1673a..3c11f98 100644 --- a/pkg/agentic/mirror.go +++ b/pkg/agentic/mirror.go @@ -128,11 +128,11 @@ func (s *PrepSubsystem) mirror(ctx context.Context, _ *mcp.CallToolRequest, inpu sync.Pushed = true // Create PR: dev → main on GitHub - prURL, err := s.createGitHubPR(ctx, repoDir, repo, ahead, files) + pullRequestURL, err := s.createGitHubPR(ctx, repoDir, repo, ahead, files) if err != nil { sync.Skipped = core.Sprintf("PR creation failed: %v", err) } else { - sync.PRURL = prURL + sync.PRURL = pullRequestURL } synced = append(synced, sync) diff --git a/pkg/agentic/pr.go b/pkg/agentic/pr.go index 1a3d895..e8d6923 100644 --- a/pkg/agentic/pr.go +++ b/pkg/agentic/pr.go @@ -119,25 +119,25 @@ func (s *PrepSubsystem) createPR(ctx context.Context, _ *mcp.CallToolRequest, in } // Create PR via Forge API - prURL, prNum, err := s.forgeCreatePR(ctx, org, workspaceStatus.Repo, workspaceStatus.Branch, base, title, body) + pullRequestURL, pullRequestNumber, err := s.forgeCreatePR(ctx, org, workspaceStatus.Repo, workspaceStatus.Branch, base, title, body) if err != nil { return nil, CreatePROutput{}, core.E("createPR", "failed to create PR", err) } // Update status with PR URL - workspaceStatus.PRURL = prURL + workspaceStatus.PRURL = pullRequestURL writeStatusResult(workspaceDir, workspaceStatus) // Comment on issue if tracked if workspaceStatus.Issue > 0 { - comment := core.Sprintf("Pull request created: %s", prURL) + comment := core.Sprintf("Pull request created: %s", pullRequestURL) s.commentOnIssue(ctx, org, workspaceStatus.Repo, workspaceStatus.Issue, comment) } return nil, CreatePROutput{ Success: true, - PRURL: prURL, - PRNum: prNum, + PRURL: pullRequestURL, + PRNum: pullRequestNumber, Title: title, Branch: workspaceStatus.Branch, Repo: workspaceStatus.Repo, @@ -162,17 +162,17 @@ func (s *PrepSubsystem) buildPRBody(workspaceStatus *WorkspaceStatus) string { } func (s *PrepSubsystem) forgeCreatePR(ctx context.Context, org, repo, head, base, title, body string) (string, int, error) { - var pr pullRequestView + var pullRequest pullRequestView err := s.forge.Client().Post(ctx, core.Sprintf("/api/v1/repos/%s/%s/pulls", org, repo), &forge_types.CreatePullRequestOption{ Title: title, Body: body, Head: head, Base: base, - }, &pr) + }, &pullRequest) if err != nil { return "", 0, core.E("forgeCreatePR", "create PR failed", err) } - return pr.HTMLURL, int(pullRequestNumber(pr)), nil + return pullRequest.HTMLURL, int(pullRequestNumber(pullRequest)), nil } func (s *PrepSubsystem) commentOnIssue(ctx context.Context, org, repo string, issue int, comment string) { @@ -275,36 +275,36 @@ func (s *PrepSubsystem) listPRs(ctx context.Context, _ *mcp.CallToolRequest, inp } func (s *PrepSubsystem) listRepoPRs(ctx context.Context, org, repo, state string) ([]PRInfo, error) { - var prs []pullRequestView - err := s.forge.Client().Get(ctx, core.Sprintf("/api/v1/repos/%s/%s/pulls?limit=50&page=1", org, repo), &prs) + var pullRequests []pullRequestView + err := s.forge.Client().Get(ctx, core.Sprintf("/api/v1/repos/%s/%s/pulls?limit=50&page=1", org, repo), &pullRequests) if err != nil { return nil, core.E("listRepoPRs", core.Concat("failed to list PRs for ", repo), err) } var result []PRInfo - for _, pr := range prs { - prState := pr.State - if prState == "" { - prState = "open" + for _, pullRequest := range pullRequests { + pullRequestState := pullRequest.State + if pullRequestState == "" { + pullRequestState = "open" } - if state != "" && state != "all" && prState != state { + if state != "" && state != "all" && pullRequestState != state { continue } var labels []string - for _, l := range pr.Labels { - labels = append(labels, l.Name) + for _, label := range pullRequest.Labels { + labels = append(labels, label.Name) } result = append(result, PRInfo{ Repo: repo, - Number: int(pullRequestNumber(pr)), - Title: pr.Title, - State: prState, - Author: pullRequestAuthor(pr), - Branch: pr.Head.Ref, - Base: pr.Base.Ref, + Number: int(pullRequestNumber(pullRequest)), + Title: pullRequest.Title, + State: pullRequestState, + Author: pullRequestAuthor(pullRequest), + Branch: pullRequest.Head.Ref, + Base: pullRequest.Base.Ref, Labels: labels, - Mergeable: pr.Mergeable, - URL: pr.HTMLURL, + Mergeable: pullRequest.Mergeable, + URL: pullRequest.HTMLURL, }) } diff --git a/pkg/agentic/prep.go b/pkg/agentic/prep.go index 2471486..996607e 100644 --- a/pkg/agentic/prep.go +++ b/pkg/agentic/prep.go @@ -92,7 +92,7 @@ func (s *PrepSubsystem) SetCore(c *core.Core) { // OnStartup implements core.Startable — registers named Actions, starts the queue runner, // and registers CLI commands. The Action registry IS the capability map. // -// c.Action("agentic.dispatch").Run(ctx, opts) +// c.Action("agentic.dispatch").Run(ctx, options) // c.Actions() // ["agentic.dispatch", "agentic.prep", "agentic.status", ...] func (s *PrepSubsystem) OnStartup(ctx context.Context) core.Result { c := s.Core() @@ -194,7 +194,7 @@ func (s *PrepSubsystem) OnStartup(ctx context.Context) core.Result { }) // PerformAsync wrapper — runs the completion Task in background with progress tracking. - // c.PerformAsync("agentic.complete", opts) broadcasts ActionTaskStarted/Completed. + // c.PerformAsync("agentic.complete", options) broadcasts ActionTaskStarted/Completed. c.Action("agentic.complete", s.handleComplete).Description = "Run completion pipeline (QA → PR → Verify) in background" // Hydrate workspace registry from disk @@ -552,8 +552,8 @@ func (s *PrepSubsystem) prepWorkspace(ctx context.Context, _ *mcp.CallToolReques // Maps repo name to plans directory: go-io → core/go/io, agent → core/agent, core-bio → core/php/bio. // Preserves subdirectory structure so sub-package specs land in specs/{pkg}/RFC.md. // -// s.copyRepoSpecs("/tmp/ws", "go-io") // copies plans/core/go/io/**/RFC*.md → /tmp/ws/specs/ -// s.copyRepoSpecs("/tmp/ws", "core-bio") // copies plans/core/php/bio/**/RFC*.md → /tmp/ws/specs/ +// s.copyRepoSpecs("/tmp/workspace", "go-io") // copies plans/core/go/io/**/RFC*.md → /tmp/workspace/specs/ +// s.copyRepoSpecs("/tmp/workspace", "core-bio") // copies plans/core/php/bio/**/RFC*.md → /tmp/workspace/specs/ func (s *PrepSubsystem) copyRepoSpecs(workspaceDir, repo string) { fs := (&core.Fs{}).NewUnrestricted() @@ -586,7 +586,7 @@ func (s *PrepSubsystem) copyRepoSpecs(workspaceDir, repo string) { } // Glob RFC*.md at each depth level (root, 1 deep, 2 deep, 3 deep). - // Preserves subdirectory structure: specDir/pkg/sub/RFC.md → specs/pkg/sub/RFC.md + // Preserves subdirectory structure: specDir/pkg/nested/RFC.md → specs/pkg/nested/RFC.md specsDir := core.JoinPath(workspaceDir, "specs") fs.EnsureDir(specsDir) diff --git a/pkg/agentic/verify.go b/pkg/agentic/verify.go index 43d404e..dfe3bc5 100644 --- a/pkg/agentic/verify.go +++ b/pkg/agentic/verify.go @@ -29,8 +29,8 @@ func (s *PrepSubsystem) autoVerifyAndMerge(workspaceDir string) { org = "core" } - prNum := extractPRNumber(workspaceStatus.PRURL) - if prNum == 0 { + pullRequestNumber := extractPullRequestNumber(workspaceStatus.PRURL) + if pullRequestNumber == 0 { return } @@ -47,7 +47,7 @@ func (s *PrepSubsystem) autoVerifyAndMerge(workspaceDir string) { } // Attempt 1: run tests and try to merge - mergeOutcome := s.attemptVerifyAndMerge(repoDir, org, workspaceStatus.Repo, workspaceStatus.Branch, prNum) + mergeOutcome := s.attemptVerifyAndMerge(repoDir, org, workspaceStatus.Repo, workspaceStatus.Branch, pullRequestNumber) if mergeOutcome == mergeSuccess { markMerged() return @@ -56,7 +56,7 @@ func (s *PrepSubsystem) autoVerifyAndMerge(workspaceDir string) { // Attempt 2: rebase onto main and retry if mergeOutcome == mergeConflict || mergeOutcome == testFailed { if s.rebaseBranch(repoDir, workspaceStatus.Branch) { - if s.attemptVerifyAndMerge(repoDir, org, workspaceStatus.Repo, workspaceStatus.Branch, prNum) == mergeSuccess { + if s.attemptVerifyAndMerge(repoDir, org, workspaceStatus.Repo, workspaceStatus.Branch, pullRequestNumber) == mergeSuccess { markMerged() return } @@ -64,7 +64,7 @@ func (s *PrepSubsystem) autoVerifyAndMerge(workspaceDir string) { } // Both attempts failed — flag for human review - s.flagForReview(org, workspaceStatus.Repo, prNum, mergeOutcome) + s.flagForReview(org, workspaceStatus.Repo, pullRequestNumber, mergeOutcome) if result := ReadStatusResult(workspaceDir); result.OK { workspaceStatusUpdate, ok := workspaceStatusValue(result) @@ -85,13 +85,13 @@ const ( ) // attemptVerifyAndMerge runs tests and tries to merge. Returns the outcome. -func (s *PrepSubsystem) attemptVerifyAndMerge(repoDir, org, repo, branch string, prNum int) mergeResult { +func (s *PrepSubsystem) attemptVerifyAndMerge(repoDir, org, repo, branch string, pullRequestNumber int) mergeResult { testResult := s.runVerification(repoDir) if !testResult.passed { comment := core.Sprintf("## Verification Failed\n\n**Command:** `%s`\n\n```\n%s\n```\n\n**Exit code:** %d", testResult.testCmd, truncate(testResult.output, 2000), testResult.exitCode) - s.commentOnIssue(context.Background(), org, repo, prNum, comment) + s.commentOnIssue(context.Background(), org, repo, pullRequestNumber, comment) return testFailed } @@ -99,14 +99,14 @@ func (s *PrepSubsystem) attemptVerifyAndMerge(repoDir, org, repo, branch string, ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() - if mergeResult := s.forgeMergePR(ctx, org, repo, prNum); !mergeResult.OK { + if mergeAttempt := s.forgeMergePR(ctx, org, repo, pullRequestNumber); !mergeAttempt.OK { comment := core.Sprintf("## Tests Passed — Merge Failed\n\n`%s` passed but merge failed", testResult.testCmd) - s.commentOnIssue(context.Background(), org, repo, prNum, comment) + s.commentOnIssue(context.Background(), org, repo, pullRequestNumber, comment) return mergeConflict } comment := core.Sprintf("## Auto-Verified & Merged\n\n**Tests:** `%s` — PASS\n\nAuto-merged by core-agent dispatch system.", testResult.testCmd) - s.commentOnIssue(context.Background(), org, repo, prNum, comment) + s.commentOnIssue(context.Background(), org, repo, pullRequestNumber, comment) return mergeSuccess } @@ -140,7 +140,7 @@ func (s *PrepSubsystem) rebaseBranch(repoDir, branch string) bool { } // flagForReview adds the "needs-review" label to the PR via Forge API. -func (s *PrepSubsystem) flagForReview(org, repo string, prNum int, result mergeResult) { +func (s *PrepSubsystem) flagForReview(org, repo string, pullRequestNumber int, mergeOutcome mergeResult) { ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) defer cancel() @@ -151,16 +151,16 @@ func (s *PrepSubsystem) flagForReview(org, repo string, prNum int, result mergeR payload := core.JSONMarshalString(map[string]any{ "labels": []int{s.getLabelID(ctx, org, repo, "needs-review")}, }) - url := core.Sprintf("%s/api/v1/repos/%s/%s/issues/%d/labels", s.forgeURL, org, repo, prNum) + url := core.Sprintf("%s/api/v1/repos/%s/%s/issues/%d/labels", s.forgeURL, org, repo, pullRequestNumber) HTTPPost(ctx, url, payload, s.forgeToken, "token") // Comment explaining the situation reason := "Tests failed after rebase" - if result == mergeConflict { + if mergeOutcome == mergeConflict { reason = "Merge conflict persists after rebase" } comment := core.Sprintf("## Needs Review\n\n%s. Auto-merge gave up after retry.\n\nLabelled `needs-review` for human attention.", reason) - s.commentOnIssue(ctx, org, repo, prNum, comment) + s.commentOnIssue(ctx, org, repo, pullRequestNumber, comment) } // ensureLabel creates a label if it doesn't exist. @@ -278,20 +278,20 @@ func (s *PrepSubsystem) runNodeTests(repoDir string) verifyResult { } // forgeMergePR merges a PR via the Forge API. -func (s *PrepSubsystem) forgeMergePR(ctx context.Context, org, repo string, prNum int) core.Result { +func (s *PrepSubsystem) forgeMergePR(ctx context.Context, org, repo string, pullRequestNumber int) core.Result { payload := core.JSONMarshalString(map[string]any{ "Do": "merge", "merge_message_field": "Auto-merged by core-agent after verification\n\nCo-Authored-By: Virgil ", "delete_branch_after_merge": true, }) - url := core.Sprintf("%s/api/v1/repos/%s/%s/pulls/%d/merge", s.forgeURL, org, repo, prNum) + url := core.Sprintf("%s/api/v1/repos/%s/%s/pulls/%d/merge", s.forgeURL, org, repo, pullRequestNumber) return HTTPPost(ctx, url, payload, s.forgeToken, "token") } -// extractPRNumber gets the PR number from a Forge PR URL. -func extractPRNumber(prURL string) int { - parts := core.Split(prURL, "/") +// extractPullRequestNumber gets the PR number from a Forge PR URL. +func extractPullRequestNumber(pullRequestURL string) int { + parts := core.Split(pullRequestURL, "/") if len(parts) == 0 { return 0 } diff --git a/pkg/agentic/verify_test.go b/pkg/agentic/verify_test.go index 00d54a3..fbad4a2 100644 --- a/pkg/agentic/verify_test.go +++ b/pkg/agentic/verify_test.go @@ -122,23 +122,23 @@ func TestVerify_ForgeMergePR_Bad_NetworkError(t *testing.T) { assert.False(t, r.OK) } -// --- extractPRNumber (additional _Ugly cases) --- +// --- extractPullRequestNumber (additional _Ugly cases) --- -func TestVerify_ExtractPRNumber_Ugly_DoubleSlashEnd(t *testing.T) { - assert.Equal(t, 0, extractPRNumber("https://forge.lthn.ai/core/go-io/pulls/42/")) +func TestVerify_ExtractPullRequestNumber_Ugly_DoubleSlashEnd(t *testing.T) { + assert.Equal(t, 0, extractPullRequestNumber("https://forge.lthn.ai/core/go-io/pulls/42/")) } -func TestVerify_ExtractPRNumber_Ugly_VeryLargeNumber(t *testing.T) { - assert.Equal(t, 999999, extractPRNumber("https://forge.lthn.ai/core/go-io/pulls/999999")) +func TestVerify_ExtractPullRequestNumber_Ugly_VeryLargeNumber(t *testing.T) { + assert.Equal(t, 999999, extractPullRequestNumber("https://forge.lthn.ai/core/go-io/pulls/999999")) } -func TestVerify_ExtractPRNumber_Ugly_NegativeNumber(t *testing.T) { +func TestVerify_ExtractPullRequestNumber_Ugly_NegativeNumber(t *testing.T) { // atoi of "-5" is -5, parseInt wraps atoi - assert.Equal(t, -5, extractPRNumber("https://forge.lthn.ai/core/go-io/pulls/-5")) + assert.Equal(t, -5, extractPullRequestNumber("https://forge.lthn.ai/core/go-io/pulls/-5")) } -func TestVerify_ExtractPRNumber_Ugly_ZeroExplicit(t *testing.T) { - assert.Equal(t, 0, extractPRNumber("https://forge.lthn.ai/core/go-io/pulls/0")) +func TestVerify_ExtractPullRequestNumber_Ugly_ZeroExplicit(t *testing.T) { + assert.Equal(t, 0, extractPullRequestNumber("https://forge.lthn.ai/core/go-io/pulls/0")) } // --- ensureLabel --- @@ -410,7 +410,7 @@ func TestVerify_AutoVerifyAndMerge_Bad_InvalidPRURL(t *testing.T) { failCount: make(map[string]int), } - // extractPRNumber returns 0 for invalid URL, so autoVerifyAndMerge returns early + // extractPullRequestNumber returns 0 for invalid URL, so autoVerifyAndMerge returns early assert.NotPanics(t, func() { s.autoVerifyAndMerge(dir) }) @@ -519,7 +519,7 @@ func TestAutopr_Truncate_Ugly_EmptyString(t *testing.T) { func TestVerify_AutoVerifyAndMerge_Ugly(t *testing.T) { // Workspace with status=completed, repo=test, PRURL="not-a-url" - // extractPRNumber returns 0 for "not-a-url" → early return, no panic + // extractPullRequestNumber returns 0 for "not-a-url" → early return, no panic dir := t.TempDir() require.NoError(t, writeStatus(dir, &WorkspaceStatus{ Status: "completed", @@ -580,17 +580,17 @@ func TestVerify_AttemptVerifyAndMerge_Ugly(t *testing.T) { assert.True(t, commentCalled, "should have posted a comment about test failure") } -// --- extractPRNumber (extended Ugly) --- +// --- extractPullRequestNumber (extended Ugly) --- -func TestVerify_ExtractPRNumber_Ugly(t *testing.T) { +func TestVerify_ExtractPullRequestNumber_Ugly(t *testing.T) { // Just a bare number "5" → last segment is "5" → returns 5 - assert.Equal(t, 5, extractPRNumber("5")) + assert.Equal(t, 5, extractPullRequestNumber("5")) // Trailing slash → last segment is empty string → parseInt("") → 0 - assert.Equal(t, 0, extractPRNumber("https://forge.lthn.ai/core/go-io/pulls/42/")) + assert.Equal(t, 0, extractPullRequestNumber("https://forge.lthn.ai/core/go-io/pulls/42/")) // Non-numeric string → parseInt("abc") → 0 - assert.Equal(t, 0, extractPRNumber("abc")) + assert.Equal(t, 0, extractPullRequestNumber("abc")) } // --- EnsureLabel Ugly --- diff --git a/pkg/brain/brain.go b/pkg/brain/brain.go index d3c3e41..6319af0 100644 --- a/pkg/brain/brain.go +++ b/pkg/brain/brain.go @@ -2,8 +2,8 @@ // Package brain gives MCP and HTTP services the same OpenBrain capability map. // -// sub := brain.New(nil) -// core.Println(sub.Name()) +// subsystem := brain.New(nil) +// core.Println(subsystem.Name()) package brain import ( @@ -33,36 +33,36 @@ var errBridgeNotAvailable = core.E("brain", "bridge not available", nil) // Subsystem routes `brain_*` MCP tools through the shared IDE bridge. // -// sub := brain.New(nil) -// core.Println(sub.Name()) // "brain" +// subsystem := brain.New(nil) +// core.Println(subsystem.Name()) // "brain" type Subsystem struct { bridge *ide.Bridge } // New builds the bridge-backed OpenBrain subsystem used by MCP. // -// sub := brain.New(nil) -// core.Println(sub.Name()) +// subsystem := brain.New(nil) +// core.Println(subsystem.Name()) func New(bridge *ide.Bridge) *Subsystem { return &Subsystem{bridge: bridge} } // Name keeps the subsystem address stable for core.WithService and MCP. // -// name := sub.Name() // "brain" +// name := subsystem.Name() // "brain" func (s *Subsystem) Name() string { return "brain" } // RegisterTools publishes the bridge-backed brain tools on an MCP server. // -// sub := brain.New(nil) -// sub.RegisterTools(server) +// subsystem := brain.New(nil) +// subsystem.RegisterTools(server) func (s *Subsystem) RegisterTools(server *mcp.Server) { s.registerBrainTools(server) } // Shutdown satisfies the MCP subsystem lifecycle without extra cleanup. // -// _ = sub.Shutdown(context.Background()) +// _ = subsystem.Shutdown(context.Background()) func (s *Subsystem) Shutdown(_ context.Context) error { return nil } diff --git a/pkg/brain/direct.go b/pkg/brain/direct.go index adbbc64..73aac2c 100644 --- a/pkg/brain/direct.go +++ b/pkg/brain/direct.go @@ -14,8 +14,8 @@ import ( // DirectSubsystem talks to OpenBrain over HTTP without the IDE bridge. // -// sub := brain.NewDirect() -// core.Println(sub.Name()) // "brain" +// subsystem := brain.NewDirect() +// core.Println(subsystem.Name()) // "brain" type DirectSubsystem struct { apiURL string apiKey string @@ -25,8 +25,8 @@ var _ coremcp.Subsystem = (*DirectSubsystem)(nil) // NewDirect builds the HTTP-backed OpenBrain subsystem. // -// sub := brain.NewDirect() -// core.Println(sub.Name()) +// subsystem := brain.NewDirect() +// core.Println(subsystem.Name()) func NewDirect() *DirectSubsystem { apiURL := core.Env("CORE_BRAIN_URL") if apiURL == "" { @@ -58,13 +58,13 @@ func NewDirect() *DirectSubsystem { // Name keeps the direct subsystem address stable for core.WithService and MCP. // -// name := sub.Name() // "brain" +// name := subsystem.Name() // "brain" func (s *DirectSubsystem) Name() string { return "brain" } // RegisterTools publishes the direct `brain_*` and `agent_*` tools on an MCP server. // -// sub := brain.NewDirect() -// sub.RegisterTools(server) +// subsystem := brain.NewDirect() +// subsystem.RegisterTools(server) func (s *DirectSubsystem) RegisterTools(server *mcp.Server) { mcp.AddTool(server, &mcp.Tool{ Name: "brain_remember", @@ -87,7 +87,7 @@ func (s *DirectSubsystem) RegisterTools(server *mcp.Server) { // Shutdown satisfies the MCP subsystem lifecycle without extra cleanup. // -// _ = sub.Shutdown(context.Background()) +// _ = subsystem.Shutdown(context.Background()) func (s *DirectSubsystem) Shutdown(_ context.Context) error { return nil } func brainKeyPath(home string) string { diff --git a/pkg/brain/messaging.go b/pkg/brain/messaging.go index 4255ced..176c838 100644 --- a/pkg/brain/messaging.go +++ b/pkg/brain/messaging.go @@ -12,8 +12,8 @@ import ( // RegisterMessagingTools adds direct agent messaging tools to an MCP server. // -// sub := brain.NewDirect() -// sub.RegisterMessagingTools(server) +// subsystem := brain.NewDirect() +// subsystem.RegisterMessagingTools(server) func (s *DirectSubsystem) RegisterMessagingTools(server *mcp.Server) { mcp.AddTool(server, &mcp.Tool{ Name: "agent_send", diff --git a/pkg/lib/lib.go b/pkg/lib/lib.go index 6612d41..26cfd34 100644 --- a/pkg/lib/lib.go +++ b/pkg/lib/lib.go @@ -18,7 +18,7 @@ // r := lib.Task("code/review") // r.Value.(string) // r := lib.Persona("secops/dev") // r.Value.(string) // r := lib.Flow("go") // r.Value.(string) -// r := lib.ExtractWorkspace("default", "/tmp/ws", data) +// r := lib.ExtractWorkspace("default", "/tmp/workspace", data) // core.Println(r.OK) package lib @@ -80,18 +80,18 @@ func ensureMounted() core.Result { mountedData := &core.Data{Registry: core.NewRegistry[*core.Embed]()} for _, item := range []struct { - name string - fsys embed.FS - basedir string - assign func(*core.Embed) + name string + filesystem embed.FS + baseDir string + assign func(*core.Embed) }{ - {name: "prompt", fsys: promptFiles, basedir: "prompt", assign: func(emb *core.Embed) { promptFS = emb }}, - {name: "task", fsys: taskFiles, basedir: "task", assign: func(emb *core.Embed) { taskFS = emb }}, - {name: "flow", fsys: flowFiles, basedir: "flow", assign: func(emb *core.Embed) { flowFS = emb }}, - {name: "persona", fsys: personaFiles, basedir: "persona", assign: func(emb *core.Embed) { personaFS = emb }}, - {name: "workspace", fsys: workspaceFiles, basedir: "workspace", assign: func(emb *core.Embed) { workspaceFS = emb }}, + {name: "prompt", filesystem: promptFiles, baseDir: "prompt", assign: func(emb *core.Embed) { promptFS = emb }}, + {name: "task", filesystem: taskFiles, baseDir: "task", assign: func(emb *core.Embed) { taskFS = emb }}, + {name: "flow", filesystem: flowFiles, baseDir: "flow", assign: func(emb *core.Embed) { flowFS = emb }}, + {name: "persona", filesystem: personaFiles, baseDir: "persona", assign: func(emb *core.Embed) { personaFS = emb }}, + {name: "workspace", filesystem: workspaceFiles, baseDir: "workspace", assign: func(emb *core.Embed) { workspaceFS = emb }}, } { - mounted := mountEmbed(item.fsys, item.basedir) + mounted := mountEmbed(item.filesystem, item.baseDir) if !mounted.OK { mountResult = mounted return @@ -109,21 +109,21 @@ func ensureMounted() core.Result { return mountResult } -func mountEmbed(fsys embed.FS, basedir string) core.Result { - result := core.Mount(fsys, basedir) +func mountEmbed(filesystem embed.FS, baseDir string) core.Result { + result := core.Mount(filesystem, baseDir) if result.OK { return result } if err, ok := result.Value.(error); ok { return core.Result{ - Value: core.E("lib.mountEmbed", core.Concat("mount ", basedir), err), + Value: core.E("lib.mountEmbed", core.Concat("mount ", baseDir), err), OK: false, } } return core.Result{ - Value: core.E("lib.mountEmbed", core.Concat("mount ", basedir), nil), + Value: core.E("lib.mountEmbed", core.Concat("mount ", baseDir), nil), OK: false, } } @@ -355,9 +355,9 @@ func ListTasks() []string { } result := listNamesRecursive("task", ".") - a := core.NewArray(result...) - a.Deduplicate() - return a.AsSlice() + names := core.NewArray(result...) + names.Deduplicate() + return names.AsSlice() } // ListPersonas returns available persona paths, including nested directories. @@ -368,9 +368,9 @@ func ListPersonas() []string { return nil } - a := core.NewArray(listNamesRecursive("persona", ".")...) - a.Deduplicate() - return a.AsSlice() + names := core.NewArray(listNamesRecursive("persona", ".")...) + names.Deduplicate() + return names.AsSlice() } // listNamesRecursive walks an embed tree via Data.ListNames. @@ -397,7 +397,7 @@ func listNamesRecursive(mount, dir string) []string { subPath := core.JoinPath(mount, relPath) // Try as directory — recurse if it has contents - if sub := data.ListNames(subPath); sub.OK { + if childNames := data.ListNames(subPath); childNames.OK { slugs = append(slugs, listNamesRecursive(mount, relPath)...) } diff --git a/pkg/monitor/monitor.go b/pkg/monitor/monitor.go index 33f9b4a..12942a6 100644 --- a/pkg/monitor/monitor.go +++ b/pkg/monitor/monitor.go @@ -82,7 +82,7 @@ var _ coremcp.Subsystem = (*Subsystem)(nil) // Deprecated: prefer Register with core.WithService(monitor.Register). // -// mon.SetCore(c) +// monitorService.SetCore(c) func (m *Subsystem) SetCore(coreApp *core.Core) { m.ServiceRuntime = core.NewServiceRuntime(coreApp, Options{}) }