Adds `recoverStateOrphans` per RFC §15.5 — startup scans `.core/state/`
for leftover QA workspace buffers from dispatches that crashed before
commit, and discards them so partial cycles do not poison the diff
history described in RFC §7.
- `statestore.go` — new `recoverStateOrphans` wrapper around go-store's
`RecoverOrphans("")` so the agent inherits the store's configured
state directory
- `prep.go` — wires the recovery into OnStartup immediately after
`hydrateWorkspaces` so the registry, queue, and buffers all come back
into a consistent state on restart
- `statestore_test.go` — Good/Bad/Ugly coverage, includes the cwd
redirect guard so the go-store default relative path cannot leak test
artefacts into the package working tree
Co-Authored-By: Virgil <virgil@lethean.io>
Adds `.core/workspace/db.duckdb` — the permanent record of dispatch
cycles described in RFC §15.5. Stats rows persist BEFORE workspace
directories are deleted so "what happened in the last 50 dispatches"
queries survive cleanup and sync drain.
- `workspace_stats.go` — lazy go-store handle for the parent stats DB,
build/record/filter/list helpers, report payload projection
- `commit.go` — writes a stats row as part of the completion pipeline so
every committed dispatch carries forward into the permanent record
- `commands_workspace.go` — `workspace/clean` captures stats before
deleting, new `workspace/stats` command + `agentic.workspace.stats`
action answer the spec's "query on the parent" use case
Co-Authored-By: Virgil <virgil@lethean.io>
Extends DispatchReport with the three RFC §7 diff lists (New, Resolved,
Persistent) and a Clusters list that groups findings by tool/severity/
category/rule_id. runQAWithReport now queries the SQLite journal for up
to persistentThreshold previous cycles of the same workspace, computes
the diff against the current cycle, and populates .meta/report.json
before ws.Commit(). The full findings payload is also pushed to the
journal via CommitToJournal so later cycles have findings-level data
to compare against (workspace.Commit only stores aggregated counts).
Matches RFC §7 Post-Run Analysis without pulling in Poindexter as a
direct dependency — uses straightforward deterministic clustering so
agent stays inside the core/go-* dependency tier.
Co-Authored-By: Virgil <virgil@lethean.io>
The runQA handler now captures every lint finding, tool run, build, vet
and test result into a go-store workspace buffer and commits the cycle
to the journal. Intelligence survives in the report and the journal per
RFC §7 Completion Pipeline.
- qa.go: QAFinding / QAToolRun / QASummary / QAReport DTOs mirroring
lint.Report shape; DispatchReport struct written to .meta/report.json;
runQAWithReport opens NewWorkspace("qa-<workspace>"), invokes
core-lint run --output json via c.Process().RunIn(), records every
finding + tool + stage result, then commits
- runQALegacy preserved for graceful degradation when go-store is
unavailable (RFC §15.6)
- dispatch.go: runQA now delegates to runQAWithReport, bool contract
unchanged for existing call sites
- qa_test.go: Good/Bad/Ugly triads per repo convention
Poindexter clustering from RFC §7 Post-Run Analysis remains open —
needs its own RFC pass for the package boundary.
Co-Authored-By: Virgil <virgil@lethean.io>
Implements `core login CODE` — exchanges a 6-digit pairing code generated
at app.lthn.ai/device for an AgentApiKey, persisted to ~/.claude/brain.key.
Pairing code is the proof, so the POST is unauthenticated.
- auth.go: AuthLoginInput/Output DTOs + handleAuthLogin handler
- commands_platform.go: login / auth/login / agentic:login CLI commands
with cmdAuthLogin persisting the returned key
- prep.go: registered agentic.auth.login / agent.auth.login actions
- auth_test.go / commands_platform_test.go / prep_test.go: Good/Bad/Ugly
triads per repo convention, including key persistence verification
Co-Authored-By: Virgil <virgil@lethean.io>
- prep.go TrackWorkspace mirrors into queue + concurrency store groups
(previously only registry); hydrateWorkspaces reaps filesystem ghosts
(dead PID → failed, persisted back to status.json) so cmdStatus and
out-of-process consumers see coherent state (RFC §15.3)
- sync.go queue read/write goes through go-store first per RFC §16.5
("Queue persists across restarts in db.duckdb"), file remains fallback
for graceful degradation
- statestore.go stateStoreGet helper for go-store-first reads
- tests/cli/restart — new CLI test for RFC §15.7 "dispatch → kill →
restart → no ghost agents" dead-PID reap flow
- 4 new statestore tests: queue group mirror, concurrency refresh,
sync queue persistence, fs ghost reap with disk write-back
Co-Authored-By: Virgil <virgil@lethean.io>
Introduce an optional go-store persistence layer for the three state
groups described in RFC §15.3 — queue, concurrency, registry — plus
runtime_state and dispatch_history used by the sync pipeline.
- statestore.go lazily opens `.core/db.duckdb` via go-store when
available; nil-safe helpers return cleanly so in-memory/file-based
fallbacks survive when the store cannot open (graceful degradation,
RFC §15.6)
- prep.go tracks the store reference on the subsystem and closes it on
shutdown; hydrateWorkspaces now consults the registry group before
the filesystem scan so ghost agents are marked failed across
restarts, and TrackWorkspace mirrors updates back into the cache
- runtime_state.go persists backoff + fail-count snapshots into the
go-store runtime group so dispatch backoff survives restarts even
when the JSON file rotates
- commit.go writes the completed dispatch record into dispatch_history
for RFC §16.3 sync push to drain without rescanning workspaces
- statestore_test.go covers lazy-once init, restore/delete round trip,
ghost-agent failure marking, and runtime-state replay across
subsystem instances
Co-Authored-By: Virgil <virgil@lethean.io>
- sync.go: syncBackoffSchedule (1s/5s/15s/60s/5min) with per-push Attempts
and NextAttempt honoured on retry (RFC §16.5)
- runSyncFlushLoop: ticks every minute from OnStartup when API key present,
drains the queue without re-scanning workspaces
- SyncPushInput.QueueOnly: lets flush loop drain without triggering a full
workspace scan (prevents duplicate pushes)
- Sync ledger at .core/sync/ledger.json: fingerprints keyed by workspace
name + (updated_at, runs); skips already-synced workspaces until fresh
activity
- docs/RFC-AGENT.md: synced from plans/code/core/agent/RFC.md with latest
AgentPlan status enum, complete capability, pr.close/branch.delete,
indexed_at/org brain fields
Co-Authored-By: Virgil <virgil@lethean.io>
- Add isLEMProfile(): codex:lemmy/lemer/lemma/lemrd use --profile not --model
- Add isNativeAgent(): Claude agents run natively (not in Docker)
- Update localAgentCommandScript for LEM profile support
- 12 new tests (Good/Bad/Ugly for profiles, native agent, codex variants)
Co-Authored-By: Virgil <virgil@lethean.io>
- Replace broken registerMCPService with mcp.Register (fixes nil ServiceRuntime panic)
- Remove dead mcp_service.go, update tests to use mcp.Register directly
- Add setTestWorkspace() helper to clear workspaceRootOverride between tests
- Fix 40+ test files with workspace state poisoning from loadAgentConfig
- Fix forge.lthn.ai → dappco.re in findConsumersList test
- Fix BranchWorkspaceCount test to use isolated temp dir
- Add CLI test standard: 32 tests across 19 subsystems (tests/cli/)
- All 9 packages pass, 0 failures
Co-Authored-By: Virgil <virgil@lethean.io>
All RegisterTools and internal register*Tool methods updated from
*mcp.Server to *coremcp.Service. Tool registration calls updated to
use svc.Server() for SDK AddTool calls. Monitor subsystem updated
to store *coremcp.Service and access Server() for Sessions/ResourceUpdated.
Tests updated to create coremcp.Service via New() instead of raw SDK server.
Co-Authored-By: Virgil <virgil@lethean.io>
Replace all forge.lthn.ai import paths in Go source files with dappco.re
equivalents. Update go.mod deps to dappco.re versions as specified.
Co-Authored-By: Virgil <virgil@lethean.io>