Commit graph

157 commits

Author SHA1 Message Date
Snider
3522a3bea5 test: RegisterTools GBU + buildPrompt git log — 840 tests, 79.9% coverage
- RegisterTools: exercises all 12 register*Tool functions via mcp.NewServer (+1.4pp)
- buildPrompt: test with real git repo for RECENT CHANGES path
- AX-7: 92% categories filled

0.1pp from 80%. Remaining gap is process-dependent functions
awaiting go-process v0.7.0 update.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-25 10:28:30 +00:00
Snider
d67336c761 test: batch 5 — proc.go GBU + getGitLog + runGoTests + prepWorkspace — 836 tests
New: proc_test.go with 28 tests for all proc.go helpers (runCmd, gitCmd,
gitOutput, processIsRunning, processKill, ensureProcess).

Plus: getGitLog GBU, runGoTests GBU, prepWorkspace Good,
listLocalRepos Ugly, loadRateLimitState Bad, runLoop skips.

AX-7: 501/543 filled (92%), 167/181 functions complete
Coverage: 78.5%, 836 tests

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-25 10:20:50 +00:00
Snider
4eb1111faa refactor: clean up proc.go — ensureProcess bridge, processIsRunning/processKill helpers
- proc.go: ensureProcess() as temporary bridge until go-process gets v0.7.0 update
- processIsRunning/processKill: use go-process ProcessID when available, fall back to PID
- WorkspaceStatus: add ProcessID field for go-process managed lookup
- dispatch.go: simplified spawnAgent goroutine — uses proc.Done() instead of syscall poll
- Removed syscall import from dispatch.go

Next: update go-process to v0.7.0 Core contract, then replace
syscall.Kill calls in queue.go, shutdown.go, status.go, dispatch_sync.go
with processIsRunning/processKill.

Coverage: 78.1%, 802 tests

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-25 10:10:17 +00:00
Snider
8521a55907 refactor: eliminate os/exec from all source files → go-process
Replace ALL exec.Command calls with proc.go helpers backed by go-process:
- runCmd/runCmdEnv/runCmdOK — general command execution
- gitCmd/gitCmdOK/gitOutput — git-specific helpers
- ensureProcess() — lazy default service init

Refactored files (0 source files import os/exec now):
- verify.go: runGoTests, runPHPTests, runNodeTests, rebaseBranch
- dispatch.go: runQA (3 exec.Command chains → runCmdOK)
- prep.go: getGitLog, prepWorkspace clone/branch
- pr.go: createPR branch detection + push
- auto_pr.go: commit counting + push
- mirror.go: all git ops + gh CLI calls
- review_queue.go: pushAndMerge, buildReviewCommand (returns string+args now)
- paths.go: DefaultBranch

Coverage: 77.2% → 78.4% (+1.2pp from testable process paths)
802 tests

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-25 09:51:57 +00:00
Snider
c0bc7675a1 test: batch 4 — fill 36 testable gaps, 802 tests, AX-7 89%
- commands.go: factory wrapper Good/Bad/Ugly
- dispatch.go: containerCommand Bad
- queue.go: UnmarshalYAML/loadAgentsConfig Good/Bad/Ugly
- remote.go: resolveHost/remoteToken Bad/Ugly
- remote_client.go: setHeaders Bad
- prep.go: TestPrepWorkspace/TestBuildPrompt public API GBU
- prep.go: sanitise Good tests (collapseRepeatedRune, sanitisePlanSlug, trimRuneEdges)
- ingest.go: ingestFindings/createIssueViaAPI Ugly
- scan.go: scan Good
- runner.go: Poke Ugly, StartRunner Bad/Ugly
- process_register.go: ProcessRegister Good/Bad/Ugly

AX-7: 462/516 filled (89%), 152/172 functions complete
Coverage: 77.2%, 802 tests

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-25 09:31:38 +00:00
Snider
eeaed52256 test: batch 3 — add 73 Good/Bad/Ugly tests across 9 files
Fill missing categories for:
- prep.go: 25 lifecycle/detect/env tests
- prep_extra.go: pullWikiContent/renderPlan/brainRecall/findConsumers Ugly
- pr.go: buildPRBody/commentOnIssue/createPR/listPRs/listRepoPRs GBU
- epic.go: createEpic/createIssue/resolveLabelIDs/createLabel Ugly
- scan.go: scan/listOrgRepos/listRepoIssues GBU
- events (logic_test.go): emitStartEvent/emitCompletionEvent GBU
- review_queue_extra.go: buildReviewCommand/countFindings/parseRetryAfter/store/save/load
- watch.go: findActiveWorkspaces/resolveWorkspaceDir Bad/Ugly
- paths.go: newFs/parseInt Good
- plan_crud.go: generatePlanID/planList/writePlan Bad/Ugly

AX-7 scorecard: 425/516 categories filled (82%)
Gap: 166 → 91 missing categories
Tests: 690 → 765 (+75)
Coverage: 76.0% → 76.8%

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-25 09:19:05 +00:00
Snider
9002b7ca8a test: batch 2 — add 44 Bad/Ugly tests for commands, verify, queue, remote_client
Fill missing categories for:
- commands.go: CmdExtract/Orchestrator/Prep/Prompt/RunTask/Status Bad/Ugly
- commands_workspace.go: List/Clean/Dispatch/ExtractField Bad/Ugly
- verify.go: EnsureLabel/GetLabelID/ForgeMergePR/FileExists/FlagForReview/RunVerification Ugly
- queue.go: CanDispatchAgent/CountRunning/DelayForAgent/DrainOne/DrainQueue Bad/Ugly
- remote_client.go: McpInitialize/McpCall/ReadSSEData/SetHeaders/DrainSSE Ugly

Gap: 208 → 166 missing categories (-42)
Tests: 646 → 690 (+44)
Coverage: 74.4% → 76.0%

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-25 08:53:28 +00:00
Snider
a5afad870c test: batch 1 — add 80 Bad/Ugly tests for paths, plan, status, shutdown, forge cmds
Fill missing Good/Bad/Ugly categories for:
- paths.go: LocalFs, WorkspaceRoot, CoreRoot, PlansRoot, AgentName, GitHubOrg, parseInt, DefaultBranch
- plan.go: planCreate/Read/Update/Delete/List Ugly, planPath Ugly, validPlanStatus Ugly
- status.go: writeStatus Bad, status Good/Bad
- shutdown.go: dispatchStart/shutdownGraceful Bad/Ugly, shutdownNow Ugly
- commands_forge.go: all 9 cmd* functions Ugly (with httptest mocks)
- sanitise.go: Bad/Ugly for all 5 functions
- prep.go: various lifecycle Bad/Ugly

Gap: 260 → 208 missing categories
Tests: 566 → 646 (+80)
Coverage: 74.4%

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-25 08:43:35 +00:00
Snider
97d06c1e90 refactor(test): bulk rename 478 tests to TestFile_Function_{Good,Bad,Ugly}
Mechanical rename of all test functions to follow the convention:
  TestFilename_FunctionName_{Good,Bad,Ugly}

Examples:
  TestForgeMergePR_Good_Success → TestVerify_ForgeMergePR_Good_Success
  TestAgentCommand_Good_Gemini → TestDispatch_AgentCommand_Good_Gemini
  TestReadStatus_Bad_NoFile → TestStatus_ReadStatus_Bad_NoFile

Gap analysis now works: 137 functions still need 260 missing categories.
566 tests, agentic 74.3% — naming is now the tooling.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-25 08:32:08 +00:00
Snider
8b46e15d24 test: add Ugly tests for queue, shutdown, verify, status, review_queue
- TestQueue_CanDispatchAgent_Ugly — Core.Config concurrency path
- TestQueue_DrainQueue_Ugly — Core lock path
- TestShutdown_ShutdownNow_Ugly — deep layout
- TestVerify_AutoVerifyAndMerge_Ugly — invalid PR URL
- TestVerify_AttemptVerifyAndMerge_Ugly — build failure
- TestVerify_ExtractPRNumber_Ugly — edge cases
- TestStatus_WriteStatus_Ugly — full roundtrip
- TestReviewQueue_LoadRateLimitState_Ugly — corrupt JSON

agentic 74.3%, 566 tests

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-25 08:26:15 +00:00
Snider
acae0d804f test: add Good/Bad/Ugly for status, paths, auto_pr, prep — agentic 74.0%
New properly named tests:
- TestStatus_Status_Ugly — dead PID detection (blocked/completed/failed)
- TestPaths_DefaultBranch_{Good,Bad,Ugly} — main/master/non-git
- TestAutoPR_AutoCreatePR_{Good,Bad,Ugly} — early returns + no commits
- TestPrep_BuildPrompt_{Good,Bad,Ugly} — basic/empty/persona+issue

558 agentic tests, 74.0% coverage

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-25 08:16:53 +00:00
Snider
3c894e8101 refactor(test): proper naming — resume, remote, remote_status
Rewrite tests with TestFile_Function_{Good,Bad,Ugly} convention.
Split remote_dispatch_test.go → remote.go tests + remote_status_test.go.
Resume tests consolidated with all 3 test categories.

agentic 73.2% (recovering after catch-all deletion)

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-25 08:11:39 +00:00
Snider
52f6031822 refactor(test): delete catch-all test files, rewrite dispatch_test.go
Delete edge_case_test.go, coverage_push_test.go, dispatch_extra_test.go.
Rewrite dispatch_test.go with proper naming: TestDispatch_Function_{Good,Bad,Ugly}.

Every function in dispatch.go now has Good/Bad/Ugly test groups.
Tests for non-dispatch functions will be restored to their correct files.

agentic 72.6% (temporary regression — tests being redistributed)

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-25 08:08:36 +00:00
Snider
18eac65b70 test: coverage push — status dead PID, PR branches, DefaultBranch — agentic 75.5%
822 total tests across all packages. Exercises:
- statusRemote error/call-fail paths
- loadRateLimitState corrupt JSON
- shutdownNow deep layout
- findReviewCandidates no github remote
- prepWorkspace path traversal
- dispatch dry run
- DefaultBranch master fallback
- attemptVerifyAndMerge test failure
- resume completed workspace
- buildPRBody, runQA node, extractPRNumber edges

Coverage: agentic 74.8% → 75.5% (+0.7pp)

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-25 01:24:08 +00:00
Snider
ccf8e3bc7d test: edge cases for status, createPR, autoCreatePR, buildPrompt — agentic 74.8%
- status: dead PID detection (blocked/completed/failed paths)
- createPR: no status file, branch detection from git, default title
- autoCreatePR: no status, empty branch/repo, no commits
- DefaultBranch: git repo + non-git fallback
- cmdPrep: issue/pr/branch/tag parsing paths
- cmdRunTask: defaults + issue parsing
- canDispatchAgent: Core config path
- buildPrompt: persona + plan template branches
- writeStatus: timestamp + field preservation

Coverage: agentic 72.3% → 74.8% (+2.5pp)

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-25 01:20:08 +00:00
Snider
e0a6b33729 test: issue tracking + broadcast with Forge mocks — agentic 72.3%
Add httptest mocks for startIssueTracking/stopIssueTracking with Forge,
broadcastStart/broadcastComplete with Core IPC.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-25 01:15:19 +00:00
Snider
d30b07e34f test: renderPlan + dispatchRemote + statusRemote — agentic 71.5%, 559 tests
- renderPlan: test with real embedded templates (bug-fix, new-feature)
- dispatchRemote: full MCP roundtrip with httptest mock
- statusRemote: validation + unreachable + full roundtrip

Coverage: agentic 67.8% → 71.5% (+3.7pp)

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-25 01:13:35 +00:00
Snider
1e12b062dd refactor: decompose spawnAgent monolith — agentic 67.8%, 546 tests
Extract 7 functions from 150-line spawnAgent goroutine:
- detectFinalStatus: BLOCKED.md + exit code → status/question
- trackFailureRate: fast-failure detection + backoff
- startIssueTracking/stopIssueTracking: Forge stopwatch
- broadcastStart/broadcastComplete: IPC + audit events
- onAgentComplete: orchestrates all post-completion steps
- agentOutputFile: log path helper

spawnAgent is now: build command → start process → broadcast → monitor.
All extracted functions are independently testable.

Coverage: agentic 66.4% → 67.8% (+1.4pp)

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-25 01:11:04 +00:00
Snider
cf68371421 test: command method edge cases — agentic 66.4%, 524 tests
Add error path tests for all forge commands (API errors, missing args),
PR with body, issue create with labels/milestones, workspace clean filters.

Coverage: agentic 65.1% → 66.4% (+1.3pp)

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-25 00:56:19 +00:00
Snider
3d7adb91eb refactor: extract commands.go closures into methods — agentic 65.1%
Extract run/task, orchestrator, prep, status, prompt, extract closures
into standalone methods on PrepSubsystem. Extract shared parseIntStr helper.

Coverage: agentic 61.4% → 65.1% (+3.7pp)
Total: 512 agentic tests

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-25 00:54:07 +00:00
Snider
b5cb294b25 refactor: extract command closures into named methods — agentic 61.4%
Move all command closure bodies from registerForgeCommands,
registerWorkspaceCommands into standalone methods (cmd*) on PrepSubsystem.
This makes them directly testable without CLI integration.

New: 9 forge command methods (cmdIssueGet, cmdIssueList, cmdIssueComment,
cmdIssueCreate, cmdPRGet, cmdPRList, cmdPRMerge, cmdRepoGet, cmdRepoList)
+ 3 workspace methods (cmdWorkspaceList, cmdWorkspaceClean, cmdWorkspaceDispatch)

Coverage: agentic 54.3% → 61.4% (+7.1pp)
Total: 501 agentic tests, 727 across all packages

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-25 00:52:00 +00:00
Snider
277510ee16 test: 413 new tests — agentic 54.3%, setup 75.8%, all packages passing
Coverage: agentic 40.1% → 54.3%, setup 71.5% → 75.8%
Total: 695 passing tests across all packages (was ~357)

New test files (15):
- commands_forge_test.go — parseForgeArgs, fmtIndex
- commands_workspace_test.go — extractField (9 cases)
- commands_test.go — command registration + Core integration
- handlers_test.go — RegisterHandlers, IPC pipeline, lifecycle
- plan_crud_test.go — full CRUD via MCP handlers (23 tests)
- prep_extra_test.go — buildPrompt, findConsumersList, pullWikiContent, getIssueBody
- queue_extra_test.go — ConcurrencyLimit YAML, delayForAgent, drainOne
- remote_client_test.go — mcpInitialize, mcpCall, readSSEData, setHeaders
- remote_test.go — resolveHost, remoteToken
- resume_test.go — resume dry run, agent override, validation
- review_queue_test.go — countFindings, parseRetryAfter, buildAutoPRBody
- review_queue_extra_test.go — buildReviewCommand, rateLimitState, reviewQueue
- verify_extra_test.go — attemptVerifyAndMerge, autoVerifyAndMerge pipeline
- watch_test.go — findActiveWorkspaces, resolveWorkspaceDir
- setup/setup_extra_test.go — defaultBuildCommand, defaultTestCommand all branches

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-25 00:44:17 +00:00
Snider
04f91ed6a0 feat: all commands in services — cmd/ has only main.go (97 lines) + update.go (24 lines)
- forge.go moved to pkg/agentic/commands_forge.go
- Uses s.forge directly (no newForgeClient())
- registerForgeCommands called in OnStartup
- main.go: 97 lines, zero command registration
- cmd/ total: 121 lines (was 650+)

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-25 00:06:46 +00:00
Snider
72387dde91 feat: move workspace + process commands into services — main.go 98 lines
- ProcessRegister: proper factory in pkg/agentic
- Workspace commands (list/clean/dispatch): moved to agentic.registerWorkspaceCommands
- workspace.go deleted from cmd/
- main.go: 98 lines, just core.New + app commands + c.Run()

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-25 00:03:22 +00:00
Snider
4359b3d8d4 test(agentic): add status_extra_test.go — status, shutdown, brain, and lifecycle tests
Tests status tool (empty/mixed/deep/corrupt workspaces), shutdown tools
(start/graceful/now with queued cleanup), brainRecall (success/empty/error),
prepWorkspace validation, listPRs, Poke, OnShutdown, drainQueue.
23 tests pushing coverage from 39.4% to 44.1%.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-24 23:31:28 +00:00
Snider
27032d980f test(agentic): add pr_test.go — PR creation and listing tests
Tests forgeCreatePR, createPR (validation, dry-run, custom title),
listPRs validation, commentOnIssue via mock Forge API.
9 tests covering the PR creation pipeline.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-24 23:31:21 +00:00
Snider
64f3f3b060 test(agentic): add dispatch_test.go — dispatch validation, runQA, workspace tests
Tests dispatch input validation, DryRun flow with real git clone,
runQA with valid/broken Go projects, workspaceDir path resolution,
buildPRBody formatting, and canDispatchAgent concurrency checks.
17 tests covering the dispatch pipeline without Docker.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-24 23:31:14 +00:00
Snider
507bf55eb5 test(agentic): add scan_test.go — Forge issue scanning with mock API
Tests scan tool with mockScanServer (org repos, issue listing, dedup),
listRepoIssues (assignee extraction, URL rewriting, error handling).
11 tests covering filtering, limits, labels, and deduplication.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-24 23:31:07 +00:00
Snider
0008e269e4 test(agentic): add ingest_test.go — findings ingestion and issue creation tests
Tests ingestFindings pipeline (completed/not-completed/no-log/quota-exhausted),
createIssueViaAPI with mock Brain API, and security-specific countFileRefs cases.
13 tests covering the full ingest flow and edge cases.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-24 23:30:58 +00:00
Snider
ce682e42fe test(agentic): add verify_test.go — PR merge, labels, and verification tests
Tests forgeMergePR, ensureLabel, getLabelID, runVerification, flagForReview,
autoVerifyAndMerge, fileExists, truncate via mock Forge API.
33 tests covering merge success/conflict/error, label CRUD, and project detection.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-24 23:30:50 +00:00
Snider
805be3bf32 test(agentic): add epic_test.go — Forge API integration tests with httptest
Tests createIssue, resolveLabelIDs, createLabel, createEpic via mock Forge.
Shared mockForgeServer and newTestSubsystem helpers for reuse.
19 tests covering success, validation, and error paths.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-24 23:30:44 +00:00
Snider
f2b4eeb0fa test(agentic): add mirror_test.go — git helper integration tests
Tests hasRemote, commitsAhead, filesChanged with real temp git repos.
Tests extractJSONField, DefaultBranch, listLocalRepos, GitHubOrg.
35 tests using _Good/_Bad/_Ugly naming convention.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-24 23:30:38 +00:00
Snider
bd2eadc271 test(monitor): add logic_test.go — 26 tests for uncovered functions
Covers SetCore, handleAgentStarted, handleAgentCompleted, checkIdleAfterDelay,
countLiveWorkspaces, pidAlive, OnStartup, OnShutdown, and Register using
_Good/_Bad/_Ugly naming convention. Coverage: 76.1% → 84.2%.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-24 23:12:32 +00:00
Snider
42e558ed38 test(agentic): add queue/status/plan/register test files
Adds four new test files covering previously untested functions:
- queue_logic_test.go: countRunningByModel, drainQueue, Poke, StartRunner, DefaultBranch, LocalFs
- status_logic_test.go: ReadStatus/writeStatus field coverage + WorkspaceStatus JSON round-trip
- plan_logic_test.go: planPath sanitisation + readPlan/writePlan round-trips with phases
- register_test.go: Register (service discovery, core wiring, config loading), OnStartup, OnShutdown

All 56 new tests follow _Good/_Bad/_Ugly convention and pass with go test ./pkg/agentic/...

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-24 23:07:02 +00:00
Snider
80b827b7c8 test(agentic): add logic_test.go — 66 tests for 10 pure functions
Covers agentCommand, containerCommand, buildAutoPRBody, emitEvent,
countFileRefs, modelVariant, baseAgent, resolveWorkspace,
findWorkspaceByPR, and extractPRNumber with _Good/_Bad/_Ugly cases.
All 66 pass. Uses t.TempDir() + t.Setenv("CORE_WORKSPACE") for
filesystem-dependent tests.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-24 22:59:42 +00:00
Snider
60b0b0b63b fix: test uses core.New() directly — no .Value unwrap
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-24 21:53:45 +00:00
Snider
67658ec90c fix: imports forge.lthn.ai/core/mcp → dappco.re/go/mcp
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-24 21:40:04 +00:00
Snider
909bb3bb6c feat: services own their commands — main.go is 132 lines
Commands moved to their owning services:
- agentic: run/task, run/orchestrator, prep, status, prompt, extract
- mcp: mcp, serve (in core/mcp OnStartup)
- main.go: version, check, env (app-level only)

ServiceStartup before Cli().Run() — services register commands in OnStartup.
ServiceShutdown on exit.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-24 21:24:29 +00:00
Snider
4803f396d3 feat: use mcp.Register — MCP is a Core service
- core.New() includes mcp.Register — auto-discovers subsystems
- mcp/serve commands use c.Service("mcp") for typed retrieval
- ServiceStartup called once before Cli().Run()
- run/task and run/orchestrator registered by agentic.OnStartup
- Removed ServiceFor generics — c.Service() returns instances directly

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-24 21:16:20 +00:00
Snider
919dcf963f feat: factories return instances — WithService handles registration
Register factories no longer call c.RegisterService() explicitly.
WithService auto-discovers name from package path and registers.
Eliminates double-registration error.

Uses WithOption("name", "core-agent") for Options struct.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-24 20:36:17 +00:00
Snider
adafee0f18 feat: use RegisterService + ServiceFor — proper instance lifecycle
- agentic.PrepSubsystem implements Startable/Stoppable
- monitor.Subsystem implements Startable/Stoppable (OnStartup/OnShutdown)
- Register factories use c.RegisterService() — auto-discovers interfaces
- Register factories return instances via Result.Value
- main.go uses ServiceFor[T]() instead of ConfigGet — typed retrieval
- No more c.Config().Set("x.instance") workaround

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-24 17:42:16 +00:00
Snider
d9e7fa092b feat: complete DI migration — IPC pipeline + Config + Locks
Phase 4 complete:
- Auto-PR handler emits PRCreated message
- Verify handler listens for PRCreated, emits PRMerged/PRNeedsReview
- findWorkspaceByPR() for workspace lookup from PR events
- Remove legacy inline fallback from dispatch goroutine

Phase 5 complete:
- agents.yaml loaded once at startup into c.Config()
- canDispatchAgent reads from c.Config() (no re-parsing)
- drainQueue uses c.Lock("drain") when Core available

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-24 16:44:19 +00:00
Snider
4e69daf2da feat: replace initServices() with core.New() service conclave
Services are now registered during Core construction:
  core.New(
      core.WithService(agentic.Register),
      core.WithService(monitor.Register),
      core.WithService(brain.Register),
  )

- Remove initServices() closure — services created once in conclave
- Commands use c.ServiceStartup()/c.ServiceShutdown() for lifecycle
- Service instances retrieved via c.Config() for MCP tool registration
- run/orchestrator reduced to ServiceStartup + block + ServiceShutdown
- run/task uses conclave's agentic instance

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-24 16:33:04 +00:00
Snider
9f8a63ae21 feat: Register() factories + consume new core.New() API
Each package exposes Register(c *Core) Result for core.WithService():
- agentic.Register: creates PrepSubsystem, wires IPC handlers, lifecycle
- monitor.Register: creates Subsystem, wires IPC handler, lifecycle
- brain.Register: creates Direct, registers service

main.go updated for core.New() returning Result.
Ready for core.New(WithService(agentic.Register)) pattern.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-24 16:23:44 +00:00
Snider
cdb29a2f75 feat(ipc): replace CompletionNotifier callbacks with Core IPC messages
Phase 3 of Core DI migration:
- Remove CompletionNotifier interface from pkg/agentic
- dispatch.go emits messages.AgentStarted/AgentCompleted via c.ACTION()
- monitor registers IPC handlers in SetCore() — handleAgentStarted/handleAgentCompleted
- Remove circular callback wiring (SetCompletionNotifier) from main.go
- Export ReadStatus for cross-package use
- Update run/orchestrator to use SetCore() instead of SetCompletionNotifier()

Services now communicate through typed messages, not direct references.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-24 14:46:59 +00:00
Snider
bb88604045 feat(core): wire Core framework into agentic + monitor subsystems
Phase 2 of Core DI migration:
- Add *core.Core field + SetCore() to PrepSubsystem and monitor.Subsystem
- Register agentic/monitor/brain as Core services with lifecycle hooks
- Mark SetCompletionNotifier and SetNotifier as deprecated (removed in Phase 3)
- Fix monitor test to match actual event names
- initServices() now wires Core refs before legacy callbacks

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-24 14:44:53 +00:00
Snider
781a5b414e feat(messages): define IPC message types for inter-service communication
12 message types covering: agent lifecycle (Started/Completed),
QA+PR pipeline (QAResult/PRCreated/PRMerged/PRNeedsReview),
queue orchestration (QueueDrained/PokeQueue/RateLimitDetected),
monitor events (HarvestComplete/HarvestRejected/InboxMessage).

These replace the CompletionNotifier and ChannelNotifier callback
interfaces with typed broadcast messages via c.ACTION().

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-24 14:44:23 +00:00
Snider
53acf4000d feat(concurrency): nested per-model limits under agent pools
Concurrency config now supports both flat and nested formats:

  claude: 1                    # flat — 1 total
  codex:                       # nested — 2 total, per-model caps
    total: 2
    gpt-5.4: 1
    gpt-5.3-codex-spark: 1

canDispatchAgent checks pool total first, then per-model limit.
countRunningByModel added for exact agent string matching.
ConcurrencyLimit custom YAML unmarshaler handles both int and map.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-24 13:05:41 +00:00
Snider
04e3d492e9 fix(monitor): emit agent.completed per task, verify PIDs for queue.drained
- Export ReadStatus (was readStatus) for cross-package use
- AgentCompleted now emits agent.completed with repo/agent/workspace/status
  for every finished task, not just failures
- queue.drained only fires when genuinely empty — verified by checking
  PIDs are alive via kill(0), not just trusting stale status files
- Fix Docker mount paths: /root/ → /home/dev/ for non-root container
- Update all callers and tests

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-24 13:02:41 +00:00
Snider
9bdd47d9d5 feat(agent): v0.3.0 — dispatch control, run task CLI, quiet notifications, spark pool
- Add agentic_dispatch_start / shutdown / shutdown_now MCP tools
- Queue frozen by default, CORE_AGENT_DISPATCH=1 to auto-start
- Add run task CLI command — single task e2e (prep → spawn → wait)
- Add DispatchSync for blocking dispatch without MCP
- Quiet notifications — only agent.failed and queue.drained events
- Remove duplicate notification paths (direct callback + polling loop)
- codex-spark gets separate concurrency pool (baseAgent routing)
- Rate-limit backoff detection (3 fast failures → 30min pause)
- Review agent uses exec with sandbox bypass (not codex review)
- Bump: core-agent 0.3.0, core plugin 0.15.0, devops plugin 0.2.0

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-23 16:08:08 +00:00