- Add background queue runner (runner.go) — 30s tick + poke on completion
- drainQueue now loops to fill all slots per tick
- Add run orchestrator command — standalone queue runner without MCP
- Slim agentic_status — stats only, blocked workspaces listed
- Docker containerised dispatch — all agents run in core-dev container
- Forge stopwatch start/stop on issue when agent starts/completes
- issue create supports --milestone, --assignee, --ref
- Auto-PR targets dev branch (not main)
- PR body includes Closes #N for issue-linked work
- CLI usage strings use spaces not slashes
- Review agent uses exec with sandbox bypass (not codex review subcommand)
- Local model support via codex --oss with socat Ollama proxy
Co-Authored-By: Virgil <virgil@lethean.io>
CompletionNotifier interface now has AgentStarted() and
AgentCompleted() instead of Poke(). Dispatch pushes notifications
directly to monitor with agent/repo/status data. Monitor pushes
MCP channel events immediately — no scanning, no dedup maps,
no filesystem polling latency.
Events.jsonl kept as audit log only, not notification mechanism.
Timer-based scan kept for startup seeding and stale detection.
Co-Authored-By: Virgil <virgil@lethean.io>
filepath.Base gave just "main" — every branch-based workspace
collided in seenCompleted map. Now uses relative path like
"core/go/main" for unique deduplication.
Co-Authored-By: Virgil <virgil@lethean.io>
Status MCP tool only scanned one level deep (*/status.json).
New workspaces at */*/*/status.json were invisible. Now scans
both layouts. Also removed unused os/filepath imports.
Co-Authored-By: Virgil <virgil@lethean.io>
monitorAPIURL, monitorBrainKey, monitorHomeDir, monitorPath were
added by Codex on remote dev but lost when we force-pushed after
rebase conflict. Restored with simplified Core.Env() usage.
Co-Authored-By: Virgil <virgil@lethean.io>
countRunningByAgent only scanned one level deep (*/status.json)
but new workspaces are at */*/*/status.json. Concurrency limits
weren't enforced — all agents dispatched as running instead of
queuing. Now scans both old and new layouts.
Co-Authored-By: Virgil <virgil@lethean.io>
QA gate failed because cloned repos weren't in a Go workspace.
Extract default template (including go.work with use ./repo)
into workspace dir before cloning. Fixes go test isolation.
Co-Authored-By: Virgil <virgil@lethean.io>
- emitStartEvent fires when agent spawns (dispatch.go)
- Monitor detects new "running" workspaces and pushes agent.started
channel notification with repo and agent info
- agent.complete already included blocked/failed status — no change
- Both old and new workspace layouts supported
Co-Authored-By: Virgil <virgil@lethean.io>
workspaceStatusPaths scans both old (*/status.json) and new
(*/*/*/status.json) workspace layouts. Fixes agent.complete
channel notifications not firing for org/repo/identifier paths.
Replaces filepath.Glob with core.PathGlob in harvest.go.
Co-Authored-By: Virgil <virgil@lethean.io>
When model variant was specified, the splice inserted --model
between -o and its value, making Codex see -o with no file path.
Fixed by appending --model and prompt sequentially instead of
splicing into the middle of the args slice.
Co-Authored-By: Virgil <virgil@lethean.io>
Agent name like "codex:gpt-5.3-codex-spark" contains a colon which
breaks file paths. Use base name (before ":") for the log file.
Co-Authored-By: Virgil <virgil@lethean.io>
After agent completes, run build + vet + test before creating PR.
If QA fails, mark workspace as failed with "QA check failed" —
bad code never gets PR'd.
Supports Go (build/vet/test), PHP (composer install/test),
and Node (npm install/test). Unknown languages pass through.
Co-Authored-By: Virgil <virgil@lethean.io>
Replace raw http.Client calls with go-forge typed API:
- prep.go: getIssueBody via forge.Issues.Get, pullWikiContent
via forge.Wiki.ListPages/GetPage
- pr.go: forgeCreatePR via forge.Pulls.Create, commentOnIssue
via forge.Issues.CreateComment, listRepoPRs via forge.Pulls.ListAll
- scan.go: listOrgRepos via forge.Repos.ListOrgRepos
Eliminates manual JSON marshalling, auth headers, pagination loops,
and anonymous struct declarations. One Forge client, one auth,
type-safe responses.
Co-Authored-By: Virgil <virgil@lethean.io>
Major simplification of the dispatch model:
- Workspace dir: .core/workspace/{org}/{repo}/{pr|task|branch|tag}/
- Clone into repo/ (not src/), metadata in .meta/
- One of issue, pr, branch, or tag required for dispatch
- All context (brain, consumers, git log, wiki, plan) assembled
into prompt string — no TODO.md, PROMPT.md, CONTEXT.md files
- Resume detection: skip clone if repo/.git exists
- Default agent changed to codex
- spawnAgent drops srcDir param, runs from repo/
- No --skip-git-repo-check (repo/ IS a git repo)
- All downstream files: srcDir → repoDir
Track PRs, not workspace iterations.
Co-Authored-By: Virgil <virgil@lethean.io>
Move all tests from tests/ to package root for proper coverage.
Fix Fs zero-value: path() and validatePath() default empty root
to "/" so &Fs{} works without New().
New tests: PathGlob, PathIsAbs, CleanPath, Cli.SetOutput,
ServiceShutdown, Core.Context, Fs zero-value, Fs protected
delete, Command lifecycle with implementation, error formatting
branches, PerformAsync completion/no-handler/after-shutdown,
Extract with templates, Embed path traversal.
Coverage: 76.9% → 82.3% (23 test files, 82 new test cases).
Co-Authored-By: Virgil <virgil@lethean.io>
Codex agents are sandboxed to src/ and don't get ~/Code/go.work.
This template creates a go.work with `use .` so the Go toolchain
works in workspace mode inside the sandbox.
Co-Authored-By: Virgil <virgil@lethean.io>
Replace all os.UserHomeDir/os.Getenv/os.Hostname with core.Env().
Replace all filepath.Base/Dir/Glob/IsAbs with core.PathBase/PathDir/
PathGlob/PathIsAbs.
10 files migrated: paths, prep, review_queue, remote, dispatch,
ingest, mirror, plan, verify, watch.
Imports eliminated: 5x os, 7x filepath. All file I/O and path
construction now routes through Core primitives.
Bumps dappco.re/go/core to v0.6.0.
Co-Authored-By: Virgil <virgil@lethean.io>
Was skipping directories entirely (`if entry.IsDir() { continue }`),
so .core/reference/ and its contents were never extracted.
Replaced fs.ReadDir loop with fs.WalkDir to handle nested dirs.
Added tests: CreatesFiles, CreatesSubdirectories, TemplateSubstitution.
Co-Authored-By: Virgil <virgil@lethean.io>
Inline instructions for Codex agents — no spec references,
just the actual rules with code examples. Extracted into every
workspace automatically.
Co-Authored-By: Virgil <virgil@lethean.io>
Reference files (.core/reference/) are now part of the embedded
workspace template. ExtractWorkspace extracts them automatically —
no hardcoded filesystem paths, ships with the binary.
Co-Authored-By: Virgil <virgil@lethean.io>
Replace separate go-io (coreio) and go-log (coreerr) packages with
Core's built-in Fs and error/logging functions. This is the reference
implementation for how all Core ecosystem packages should migrate.
Changes:
- coreio.Local.Read/Write/EnsureDir/Delete/IsFile → core.Fs methods
- coreerr.E() → core.E(), coreerr.Info/Warn/Error → core.Info/Warn/Error
- (value, error) return pattern → core.Result pattern (r.OK, r.Value)
- go-io and go-log moved from direct to indirect deps in go.mod
- Added AX usage-example comments on key public types
- Added newFs("/") helper for unrestricted filesystem access
Co-Authored-By: Virgil <virgil@lethean.io>
inbox.message events now include full message objects (id, from,
subject, content) so recipients can read messages inline without
calling agent_inbox. Charon's messages arrive directly in session.
Co-Authored-By: Virgil <virgil@lethean.io>
Both checkCompletions and checkInbox now seed on first run —
existing workspaces and messages are recorded without firing
channel events. Only genuinely new events trigger notifications.
Tests updated to pre-seed flags and filter debug events.
Co-Authored-By: Virgil <virgil@lethean.io>
Track inbox by highest message ID instead of unread count. Fixes:
- API pagination limit (max 20) no longer causes missed notifications
- Restart no longer floods with all existing unread messages (seeded)
- Each new message fires exactly once regardless of read state
Added MONITOR_INTERVAL env override and debugChannel helper for
faster iteration during channel development.
All three channel types confirmed working:
- agent.complete: workspace status changes
- inbox.message: new messages by ID tracking
- monitor.debug: real-time debug trace
Co-Authored-By: Virgil <virgil@lethean.io>
The MCP agent_inbox tool wraps the response as {messages:[...]},
but the raw API returns {data:[...]}. The monitor calls the raw
API directly, so it needs to parse {data:[...]}.
Verified with curl against live API. Removed debug channel events.
Co-Authored-By: Virgil <virgil@lethean.io>
High: verify rebase push now targets Forge remote, not local origin
High/Security: planPath sanitises ID via filepath.Base to prevent
path traversal in plan read/update/delete
Medium: mirror compares and pushes local default branch, not HEAD
Findings 3-6 verified as false positives/known issues (bridge async
by design, API returns top-level memories not {data:...}, inbox uses
{messages:...} confirmed against live API).
Co-Authored-By: Virgil <virgil@lethean.io>
Medium: remote dispatch now propagates inner dispatchOut.Success
instead of hardcoding true.
Low: updateStatus clears stale question field when new state
has no question, preventing leftover rejection messages.
Co-Authored-By: Virgil <virgil@lethean.io>
High/Security: sanitise input.Repo via filepath.Base to prevent
path traversal in workspace prep (../escape from CODE_PATH).
High/Security: sanitise repo.Repo from API response in syncRepos
to prevent path traversal via crafted checkin responses.
Medium: dispatchFixFromQueue now returns error, review_queue checks
success before recording fix_dispatched.
Known issues updated with async bridge provider findings.
Co-Authored-By: Virgil <virgil@lethean.io>
High: workspace names use UnixNano to prevent same-second collisions
High: sync only pulls the branch the server reported (was pulling current)
Medium: drainQueue serialised via mutex to prevent concurrent over-dispatch
Medium: remote_status checks JSON-RPC error field before reporting success
Medium: dead agent PIDs without output log marked failed, not completed
Low: detectLanguage uses ordered slice instead of map for deterministic results
Also: URL-encoded agent names in messaging, monitor inbox, and sync endpoints.
Co-Authored-By: Virgil <virgil@lethean.io>