Commit graph

281 commits

Author SHA1 Message Date
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
284fae66b0 Merge pull request 'feat: all commands in services — main.go 97 lines' (#19) from fix/pending-cleanups into dev 2026-03-25 00:06:59 +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
4b8628007d Merge pull request 'test: 282 new tests — agentic 44.1%, monitor 84.2%' (#18) from feat/core-di-migration into dev 2026-03-25 00:00:02 +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
36c29cd0af test: 66 _Good/_Bad/_Ugly tests for agentic pure logic functions
Coverage 9.5% → 14.1%. Tests for: agentCommand, containerCommand,
buildAutoPRBody, emitEvent, countFileRefs, modelVariant, baseAgent,
resolveWorkspace, findWorkspaceByPR, extractPRNumber.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-24 23:00:25 +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
42642d8702 Merge pull request 'feat: Core DI migration — service conclave + IPC pipeline' (#17) from feat/core-di-migration into dev 2026-03-24 22:11:05 +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
b6f73dbdd2 feat: core.New() returns *Core — no unwrapping needed. main.go clean.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-24 21:50:19 +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
ae04bfd389 fix: use c.Run() void — handles os.Exit internally. 114 lines.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-24 21:36:19 +00:00
Snider
a31cea36a6 feat: use c.Run() — main.go down to 120 lines
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-24 21:33:00 +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
ea433b84de feat: all services registered in core.New() — no manual wiring
- process: registered as WithService in core.New()
- MCP: registered as WithName("mcp") in core.New(), retrieves
  agentic/monitor/brain via ServiceFor during construction
- Commands use ServiceFor to access services — no captured vars
- initMCP closure eliminated
- No service creation after core.New() completes

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-24 20:51:51 +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
b5873a8f31 Merge pull request 'fix(monitor): agent.completed events + nested concurrency config' (#16) from feat/monitor-notifications-and-concurrency into dev
Reviewed-on: #16
2026-03-24 14:44:02 +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
d305663573 Merge branch 'main' into dev 2026-03-24 10:24:09 +00:00
Snider
dba621c108 feat(update): version injection via ldflags, park update command
- var version set by build ldflags (-X dappco.re/go/agent.version=X)
- Channel detection (stable/prerelease/dev) from version string
- Update command parked until version moves to module root
- Add Makefile with build/install/test targets + VERSION support

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