Skip to content

Build Candyland quest and campaign execution flows #97

Description

@benitogf

/forge plan contract — Candyland quest & campaign execution flows

This is the .plan build contract for /forge (slug: candyland-quest-campaign).
It is the build-phase source of truth: the tech-lead partitions it into fork-safe
tasks, coders build them concurrently, integration + /gh-self-review converge, and
it delivers one PR per impacted repo. Mirrors GitHub issue
benitogf/detritus#97.

This is a handoff backlog for moving Detritus/Candyland toward an intent-driven
development model. It is deliberately self-contained so a fresh agent can pick it up
without needing the conversation that produced it.

Intent

Evolve Candyland from one-shot multi-agent runs into an intent-driven execution
sidecar (Run → Quest → Campaign), with Detritus owning its lifecycle and serving as
the knowledge/command surface its agents compose. Candyland is the execution +
observability surface; Detritus owns doctrine, install/lifecycle, and thin commands.

Acceptance / completion gate

The build is complete when, per core/completion (inherited via roles/tech-lead
and core/build — not restated):

  • Every - [ ] acceptance item below that is in scope is satisfied and checked.
  • go test ./... is green in both repos; go vet and GOOS=windows go build ./...
    pass in candyland; the candyland stub-based e2e (scripts/e2e.mjs) and validators pass.
  • The Composition Constraint holds at runtime: a spawned agent can kb_get doctrine
    and open a PR against a non-public repo in a real stub-driven run (0.2a + 0.9).
  • A clean /gh-self-review over the full diff, no new deferral markers.
  • The two repo PRs are coordinated at merge per Delivery Shape (no window where
    /candyland spawns a deleted subcommand).

Out of scope & blockers (core/completion dispositions)

  • OUT OF SCOPE — do NOT build: Phase 4 centralized learning / the hill-climbing
    meta-loop. Phase 4 is only local-trace-shape readiness (stable IDs, parent-child
    links, exportable JSONL). Building a central store/analytics is a deferral, not
    in-scope work.
  • GATE — build order, not a separate PR: the Phase 3 campaign intent-review build is
    blocked until the core/intent-review doctrine doc exists (authoring that doc is an
    in-scope Phase 3 task — do it first). The 0.2 HTTP-comms work is gated on the
    CLI-version spike (in-scope, first candyland commit); if the CLI floor is unmet,
    comms-mcp stays a process and 0.2 is skipped without blocking the rest.
  • Separate doctrine change (own commit): /janitor plan-gating (Phase 2) alters
    in-session behavior; keep it an isolated, clearly-labelled commit in the detritus PR.

Fork-safe partitioning guidance (for the greenfield phases)

Phase 0/1 items are concrete (file:line). The greenfield phases are specified at goal
level; the tech-lead should partition them along these file-disjoint seams so
coders don't collide, and gate each with the named verification oracle (all stub-based,
no live model — no API budget):

  • Phase 2 (Quest): (a) Quest model + storage [run/types.go-analog, conductor];
    (b) quest REST endpoints (Phase 7) [httpapi]; (c) quest tick loop [conductor];
    (d) /quest command [detritus]; (e) quest dashboard view [src/ UI]. Seams a→b→c are
    ordered; d and e fork off once the API shape is fixed.
    Oracle: a scripted-stub tick test asserting discover→triage→run→review→PR
    transitions and stop/pause state, on the existing writeFakeClaude harness.
  • Phase 3 (Campaign): (a) core/intent-review doctrine doc [detritus, FIRST];
    (b) Campaign model + storage; (c) campaign REST endpoints; (d) supervisor/intent-lead
    flow + the two gates; (e) /campaign command; (f) campaign UI. b/c/e/f are
    file-disjoint; d depends on a+b.
    Oracle: a stub campaign asserting Intent-Brief gate, impl-plan gate, and the
    per-commitment intent-review verdict schema {satisfied,partial,missed} + cited
    evidence; assert missed blocks the repo PR and partial annotates only.
  • Phase 6 (UI hierarchy): UI-only (src/), disjoint from backend; partition by
    view (history/filters, run/task, quest, campaign pivots). Oracle: the existing UI
    validators + a render/smoke check per view.
  • Phase 7 (APIs): co-built with the Phase 2/3 models that back them; Oracle:
    per-endpoint handler tests.

Delivery Shape Of This Work

The entire issue ships as exactly one PR per repo — one in detritus, one in
candyland (the two are distinct repos with no shared history, so a PR cannot span
both). Phasing is by commits within each PR, not by multiple PRs over time. The
phase numbers and "Suggested First Implementation Order" below define commit
order
, not separate deliverables.

  • The two PRs are coordinated at merge: the candyland PR adds the new REST/HTTP
    surface and removes control-mcp; the detritus PR switches to driving candyland
    over that surface. Merge the consumer-ready side such that /candyland never has a
    window where it spawns a deleted subcommand (detritus adopting the existing REST +
    ensure-up can land first; candyland's deletion follows or lands together). This is a
    merge-ordering note, not a phasing-into-many-PRs requirement.
  • Within each PR, order commits by the phase sequence so the diff is reviewable
    commit-by-commit even though it lands as one PR.

Settled Decisions (so an autonomous run needs no clarification)

These resolve the load-bearing ambiguities a coder would otherwise invent. Each
supersedes any looser phrasing in its phase.

  • Intent-review method (Phase 3). The intent reviewer kb_gets a new doctrine
    doc (core/intent-review); it does not inline a rubric. Method: extract each
    commitment from the Intent Brief (one checkable assertion each); for each, emit a
    verdict in {satisfied, partial, missed} with cited evidence (diff/PR/test) —
    never a vibe check. Gate: a missed blocks that repo's PR; a partial annotates the
    PR and routes to the review router but does not block. Defer Phase 3 build until
    this doc exists.
  • Budget / concurrency (Autonomy section). Env-tunable, mirroring
    resilience.go:81-92: CANDYLAND_QUEST_TOKEN_CAP, CANDYLAND_CAMPAIGN_TOKEN_CAP,
    CANDYLAND_CONCURRENCY (default = the current parallel-coder cap). Cost estimate =
    agents × attemptTimeout × model-rate, computed inline; drop the cobus reference.
    Cap-hit disposition = degrade-to-serial, then deliver-partial — never pause
    (a pre-PR pause is a no-PR terminal state under /smith). Anchor token defaults on the
    existing candyland TokensBudget/per-token constants.
  • Autonomy level at launch. The launching /quest///campaign command passes an
    explicit level. Campaigns default to L2/L3 (end-to-end delivery is their
    contract); never L1 (report-only would strand a campaign with no PR and no in-loop
    opt-up moment). Quests default per their intent.
  • deliver field. Mirrors the per-repo PRs[] shape, not a scalar branch:<name>
    (which can't carry N per-repo branch names): deliver ∈ {pr, branch} + derived
    per-repo branch campaign/<campaignID>. Resume = fetch + checkout existing
    campaign/<id> if present, else create.
  • Phases slice. Exactly ["Build","Integrate","Review","PR"]. Land 0.7 + 0.8 in
    one change. Update the Go literal indices (executor_claude.go:292 =1,
    helpers.go:52 ==10; len-N math self-adjusts) and the duplicated UI
    array src/meta/run.js:7 (consumed by RunWorkspace/RunSwitcher/AuditPanel/Dashboard/
    Tasks) in lockstep; convert offsets to named constants.
  • 0.2 per-agent MCP identity. Use a per-agent URL /mcp/comms/<agentID> (survives
    header-stripping proxies; header is the weaker fallback). Assert a minimum claude
    CLI version
    at sidecar startup (the installer floats latest; type:http support is
    a CLI property pinned nowhere). Run the spike as the first commit; keep 0.2 separable
    so comms-mcp can remain a process if the CLI floor is unmet.
  • Windows-only items (0.4 exe-swap, 0.5 tree-kill; 0.3 home-dir partly). On this
    WSL/Linux host they are static-verified + cross-compiled only (GOOS=windows
    build + vet); runtime verification is deferred to a Windows host and the run must
    not claim they "work." Kill mechanism = a Windows Job Object
    (JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE), not "job-object OR taskkill /T". Exe-swap
    mirrors detritus's own self-update rename-aside (update.go:244-263).

Product Intent

Detritus is the knowledge and command surface. It owns doctrine,
install/update/lifecycle guidance, skills, and thin commands that route work.
Detritus skills should be composable by default and should avoid duplicate
workflow implementations. When two commands need the same behavior, define the
shared doctrine once and have commands compose or delegate to it.

Candyland is the execution and observability surface. It owns long-lived agent
processes, task graphs, run records, traces, dashboard views, and local records
that can be centralized later with low friction.

Do not add heavy observability to in-session commands like /forge, /smith, or
/vibe. Those are transitional/control surfaces. The durable loop runtime should
live in Candyland, where each agent has its own session and the records are
granular enough to improve the platform later.

Component Tree

The intended model is hierarchical. Each level owns data at its own abstraction
level and aggregates child records without flattening away the detail.

Detritus command / skill surface
  -> Candyland sidecar API
    -> Campaign
       -> Quest(s), when a workstream is persistent or exploratory
          -> Work item / finding
             -> Run
                -> Agent task
                   -> Agent events, tool calls, tests, review findings
       -> Direct run(s), when a campaign has finite scoped delivery work
    -> Quest
       -> Work item / finding
          -> Run
             -> Agent task
                -> Agent events, tool calls, tests, review findings
    -> Run
       -> Agent task
          -> Agent events, tool calls, tests, review findings

Definitions:

  • Run is the atomic Candyland execution unit. It is the current flow: a
    tech-lead/coder/reviewer agent graph takes a scoped instruction and delivers a
    PR or a precise failure.
  • Quest is an iterative loop container. It may be open-ended or finite. It can
    discover/triage findings, execute a provided task list, check compliance, audit
    a PR or repo against a document, or run any other repeated work pattern. A
    quest repeatedly launches runs, so one quest can produce many PRs.
  • Campaign is a program-level execution container. Its input can be a broad
    goal or a detailed plan; what makes it a campaign is that Candyland owns the
    end-to-end execution cycle, including brief/plan validation, decomposition,
    child quests/runs, review, and delivery.
  • Work item / finding is the bridge between discovery and execution: a
    quest or campaign decides it is worth doing, then launches a run to do it.
  • Agent task is the partitioned work inside a run.

Observability follows the same tree:

  • A run records detailed agent/task/tool/test/review traces.
  • A quest records its objective, findings, work-item decisions, child runs, PRs,
    blockers, and tick history.
  • A campaign records original intent, Intent Brief, domain scope, open
    questions/escalations, child quests/runs, review routing, PRs, and final
    intent-review outcome.
  • Future centralization can consume these records, but everyday users do not
    need to edit or understand the trace store.

Operating Principles To Facilitate

Candyland should facilitate intent-driven development without trying to encode
an organization's human process as product roles. The product model should stay
general and execution-focused:

  • Users may provide high-level intent, a partial brief, or a detailed plan.
  • The agent system turns intent into concrete briefs, work items, execution
    plans, and delivery artifacts.
  • Once a run, quest, or campaign has started, Candyland should not enter a
    user-feedback question loop. Agent roles must decide, answer, escalate within
    the agent hierarchy, and handle all handleable work to completion.
  • Handoffs are artifact-driven: original intent, issue links, PR links, and run
    traces are the map. Avoid manual explanation as the primary context carrier.
  • Final review checks the shipped result against original intent, not only
    against the generated task list.
  • Escalation is routine and early: unresolved definition questions and mid-build
    scope divergence should be routed promptly to the pertinent agent role instead
    of hidden until delivery.
  • Candyland can recommend review routing based on touched areas, but it should
    not model human domain experts as agents or require machine-readable org
    ownership rules.

Composition Constraint (compose doctrine, do not re-implement in Go)

The Product Intent says: define shared doctrine once and have commands compose or
delegate. Nearly everything quests/campaigns need already exists as Detritus
doctrine
. Quest/campaign agents must kb_get these docs at the relevant role;
Candyland Go owns only the genuinely new orchestration substrate (data models,
APIs, process supervision, trace store). If quest/campaign re-encode loop cadence,
fork-gating, the review rubric, or completion logic in Go, the system drifts and
the plan violates its own principle.

Concept Compose this doctrine — do not rebuild
Quest loop: cadence, cross-tick scratchpad state, skip-streak, mid-loop pivot, target-scoped non-overlap, /gh delivery core/loop + flows/build/janitor
"Parallelize only when PRs won't collide" core/todo-audit fork-gates (evidence window, disjoint files/modules, no cross-dep)
Real review loop (Phase 0.8) core/review-rigor + /gh-self-review (explicitly forbids the hand-rolled rubric Phase 0.8 warns against)
Handle-to-completion / bounded escalation / "only a hard blocker pauses" core/completion (done-definition, 3 dispositions, K=3 circuit breaker)
Campaign Intent Brief / brief-plan validation / intent lead core/planning (.plan/<slug>.md contract + readiness/consistency checks) + core/dream (executive intake, architect owns decisions, no user loop)
Run partition / build unit / delivery / per-run tech lead roles/tech-lead + core/build + core/coder

core/coordination is already dual-driver: "Realization B is built in the
candyland repo (its own PR); detritus ships the protocol + Realization A; candyland
consumes the same contract." Hold this line.

  • Before building Phases 2-3, encode this constraint into the quest/campaign
    role prompts: each role kb_gets its doctrine rather than carrying an
    inlined rubric.

HARD PREREQUISITE — this principle is unbuildable on the current substrate.
Today a spawned agent's --mcp-config carries only candyland-comms
(resilience.go:137-140); the detritus MCP is not wired in, so kb_get /
kb_search are unreachable and no agent can load doctrine. The composition model is
dead on day one until the substrate item below lands. It is a firm requirement
that every candyland-spawned agent can reach the detritus MCP and the same
authenticated gh used in the VSCode sessions. See 0.2a.

Delivery & PR Policy (who opens PRs)

PR delivery is owned by the top-level container, never by the run itself. A
run takes a delivery directive from its parent:

  • Standalone run → opens its own PR in its repo. (deliver: pr)
  • Standalone quest → may open many PRs over time — typically one per
    accepted work item / child run. The quest aggregates them.
  • Campaign → child quests and runs open NO PRs. Each child integrates its
    work into a per-repo campaign branch (deliver: branch, commit-only, no PR).
    The campaign opens one PR per repo involved, after final intent review.
    Campaigns may span multiple repos, but each repo gets a single campaign PR so
    agents and human reviewers track the program coherently.

This implies a differentiated campaign-child flow: a run under a campaign uses
the same partition/build/integrate/review machinery but its delivery step targets
the campaign's per-repo branch instead of gh pr create. Make deliver an
explicit field on the run spec, set by the parent container; the run flow branches
only at the final delivery step.

Delivery is the agents' responsibility, and handling blockers is part of it. A
blocker never abandons delivery: the agent hierarchy resolves it, takes one of the
core/completion dispositions, or escalates upward (quest lead → campaign tech lead
→ intent lead) until the work is delivered. Stopping the whole campaign because one
work item hit a blocker is not an option — only a genuinely unresolvable hard blocker
pauses, with a visible reason, and the per-repo campaign branch persists as durable
working state so the campaign resumes from where it stopped (never silently
abandoned). Because children never open PRs, there are also no half-open PRs to
reconcile mid-campaign — the per-repo PR opens once delivery is actually done.

  • Add deliver to the run spec: pr (default for standalone) or
    branch:<name> (commit-only, no PR) for campaign children.
  • Quest delivery: one PR per accepted work item (or safe group), aggregated.
  • Campaign delivery: per-repo campaign branch + one PR per repo at the gate.
  • Encode blocker handling as a delivery obligation: agents resolve/escalate
    within the hierarchy per core/completion; the campaign branch persists
    across a pause so a resumed campaign continues delivering.

Autonomy Levels & Budget Ceilings

Staged autonomy and explicit spend limits keep program-scale execution safe and
affordable (L1/L2/L3 rollout and per-loop budgets are standard loop-engineering
practice). A campaign can spawn intent-lead + research + mapper + campaign-TL + quest-leads +
run-TLs + builders + reviewers — each a claude process. Unbounded, this is a real
cost-explosion / HTTP 429 risk.

  • Autonomy level per quest/campaign: L1 report-only (discover + triage,
    no edits/PRs), L2 assisted (build but gate PR/delivery), L3 unattended
    (full loop to PR). Default conservative; the user opts up.
  • Budget ceiling: a token/spend cap per quest and per campaign. When the
    cap is hit, pause with a visible reason rather than thrash.
  • Pre-launch cost estimate: rough projected spend from scope + fleet size
    shown before an L3 launch (cobus loop-cost analogue).
  • Concurrency cap: bound simultaneous claude agents across a
    quest/campaign (rate-limit / cost). Tie into the parallelize-only-when-safe
    fork-gate from core/todo-audit.

Naming Decisions

  • Use quest for an iterative loop. A quest can be open-ended or finite and can
    produce many PRs over time.
  • Use campaign for a program-level development cycle. A campaign may start
    from a broad goal or a detailed plan; it drives validation, decomposition,
    implementation, review, and delivery.
  • Prefer single-word Detritus commands. Do not require users to compose commands
    like /candyland janitor or /plan a /janitor.
  • Candidate command surface:
    • /quest launches or manages a Candyland quest.
    • /campaign launches or manages a Candyland campaign.
    • /candyland remains the generic sidecar/control command while the new
      surfaces mature.

Current State

Detritus currently provides:

  • KB tools: kb_list, kb_get, kb_search, kb_sections.
  • Code context tools: code_map, code_outline, code_graph.
  • Learned-memory tools: skill_put, skill_search, skill_get.
  • Commands/docs for /plan, /vibe, /smith, /forge, /janitor, /gh,
    /code, /todo, testing, style, and maintainer workflows.
  • Candyland install/update/registration support.
  • The future role for Detritus is to ensure the Candyland sidecar is installed
    and running, then call Candyland-owned APIs as a client. Detritus should not
    host a separate REST API for quests, campaigns, or runs.

Candyland currently provides:

  • One-shot multi-agent build runs that produce a PR.
  • A conductor, ooo bus, task graph, per-agent stream output, run history, and
    dashboard.
  • A launch_run/run_status/stop_run control MCP in the current release.
  • Per-task and per-agent records that are already close to the trace data needed
    for future platform improvement, though currently local-only and run-scoped.

Main gap: Candyland has runs, but not first-class persistent quests or
program-level campaigns.

Phase 0 - Candyland Sidecar Hardening

Build these first (as the leading commits — see Delivery Shape). Most work is in
the Candyland repo; Detritus owns install/lifecycle/registration changes.

Target Process Model

When a Candyland run is active there should be exactly:

  • One Candyland app process hosting the ooo bus, UI, REST API, and HTTP MCP
    surfaces.
  • One claude process per active agent.

There should be no long-lived per-session control-mcp process and no per-agent
stdio comms-mcp process. git, gh, and version probes are transient and must
spawn windowless on Windows.

Settled decisions:

  • Delete control-mcp entirely. No per-session MCP process for the editor to
    sidecar trigger. The Detritus command/skill drives Candyland by ensuring the
    sidecar is up and calling Candyland REST.
  • Convert comms-mcp from a per-agent stdio process to an HTTP MCP endpoint on
    the Candyland app. The bridge is still needed because agents speak MCP tools,
    but it must not be a separate process.
  • Detritus owns Candyland lifecycle: install, update, register, start, and
    health-check.
  • Data lives at ~/.candyland/, never inside a project.
  • Installation/management must be pure Go in Detritus. No dangling shell
    scripts.
  • Candyland UI is observe-only, with a single developer lens. No run creation
    wizard and no mode split.
  • Candyland does no routing. Vibe/smith/quest/campaign routing happens before
    delegation; Mode is removed end-to-end.

0.1 Remove control-mcp; Detritus Drives Sidecar Lifecycle And Run Trigger

  • Delete Candyland control-mcp as a stdio subcommand.
  • Delete runControlMCP in Candyland main.go.
  • Delete the MCP tool layer in Candyland internal/control/control.go
    (RegisterTools, launch/status/stop MCP tools).
  • Remove the per-session MCP registration from Detritus setup:
    registerCandylandJSON, candyland.go, setup.go, and related tests.
  • Move resilient ensure-up into Detritus Go code: health-check Candyland,
    start the Detritus-managed binary if down, poll until ready, and fail with
    clear feedback if startup fails.
  • Keep Candyland REST run creation/begin as the way Detritus triggers
    Candyland. Detritus is only the client/launcher; Candyland owns the API.
  • Keep POST /api/runs plus /begin as the trigger path.
  • Update the Detritus /candyland skill/command so invocation is:
    ensure Candyland is up -> create and begin the run -> point the user at
    the dashboard.
  • Remove now-dead Candyland code:
    internal/control/spawn_unix.go, internal/control/spawn_windows.go,
    detachSysProc, and CANDYLAND_ADDR plumbing.
  • Cutover sequencing (atomic): deleting control-mcp removes
    launch_run/run_status/stop_run, which /candyland uses today. The
    Detritus REST client + removal from all host MCP configs + the setup_test.go
    update must land together — a partial update breaks /candyland.
  • Liveness probe: GET /api/system shells out 3 uncached subprocesses
    per call
    (claude/git/gh --version, system.godepVersion). Do not
    use it as the hot ensure-up health check; add a cheap dedicated liveness
    endpoint (or cache the version probes — see 0.5).

0.2 Convert comms-mcp To An HTTP MCP Endpoint On The App

  • PRECONDITION SPIKE (do before committing to 0.2): the transport is easy
    (go-sdk ships Streamable-HTTP; the ooo/mux server can host it). The hard part
    is per-agent identity — today it's a per-process env var
    (CANDYLAND_AGENT_ID, baked in at spawn, coordinator.go:76-86). A single
    shared HTTP endpoint can't tell agents apart, and --mcp-config headers are
    static per file. So you need a per-agent URL (e.g. /mcp/comms/<id>) or a
    per-agent config. Verify the pinned claude CLI actually supports a type:http
    mcp-config entry with per-agent identity before building — the Go SDK supporting
    it is necessary but not sufficient.
  • Convert comms-mcp into an HTTP MCP endpoint hosted by the Candyland app.
  • Host comms_send, comms_inbox, brief_get, graph_read, and
    graph_propose on the Candyland app, calling the in-process ooo bus
    directly where possible.
  • Give each agent MCP identity through URL/header rather than process env.
  • Use a per-agent URL such as /mcp/comms/<agentID> or a request header for
    identity.
  • Update agent MCP config generation to target the HTTP endpoint.
  • Change c.busMCPConfig / resilience.go so each agent's --mcp-config
    points at the HTTP endpoint instead of spawning candyland comms-mcp.
  • Delete the old comms-mcp stdio subcommand once unused.
  • Verify a real run with two agents has exactly two claude processes plus
    one app process, and no comms-mcp children.
  • Keep the test stub that fetches its brief over HTTP working.

0.2a Agent Capability Substrate (detritus MCP + gh for every spawned agent)

Firm requirement: every candyland-spawned agent (tech lead, coders, reviewers,
and the future quest/campaign roles) must have, at spawn:

  1. The detritus MCPkb_get / kb_search / code_* / skill_* reachable, so
    roles load doctrine instead of carrying inlined rubrics (the Composition
    Constraint). This is the substrate the whole quest/campaign model assumes.
  2. The authenticated gh used in the VSCode sessions — so any agent can read
    private repos and open PRs, end-to-end, without re-auth.
  • Wire the detritus MCP into the generated --mcp-config for every spawned
    agent, alongside candyland-comms. Do it the way that does not
    reintroduce a per-agent process (the thing 0.2 removes): prefer a long-lived
    detritus HTTP MCP
    the agents connect to, or have the candyland app proxy
    kb_*/code_*/skill_* over its own HTTP MCP — not a per-agent stdio detritus
    spawn. Reconcile with 0.2's transport decision so both MCPs ride the same model.
  • If a long-lived/proxied detritus MCP is not feasible for some tool, fall back to
    orchestrator-resolved doctrine injected into a new bus.Brief field — but the
    live MCP is the requirement; injection is the degraded path, not the goal.
  • gh auth propagation is owned by detritus at sidecar launch: when detritus
    ensures-up / starts the sidecar, pass through the gh credential context (env /
    GH_*/GITHUB_TOKEN, GH_CONFIG_DIR/~/.config/gh, HOME/USERPROFILE) so
    the sidecar and every agent it spawns inherit it. See 0.9 for the diagnosis.
  • Verify end-to-end: a spawned agent calls kb_get successfully AND opens a PR
    against a non-public repo, in one real run.

0.3 Fixed Home Data Directory

  • Store Candyland data under ~/.candyland/ by default.
  • Default dataPath to ~/.candyland/db using os.UserHomeDir.
  • Keep --dataPath override.
  • Create the data directory on startup.
  • Verify Windows resolves ~/.candyland/ under the user profile.
  • Add db/ to Detritus .gitignore.
  • Remove any stray db/ already created in the Detritus repo.
  • Add best-effort migration from legacy project-local ./db/data.
  • If legacy ./db/data exists in the launch cwd, move it to
    ~/.candyland/db on startup. If no DB exists or migration fails, log and
    continue with a fresh DB. Never fail startup/update over this.

0.4 Pure-Go Install And Sidecar Management In Detritus

  • Remove Candyland install scripts; Detritus Go owns install/update/start.
  • Move Candyland install/update/register into Detritus Go code: download the
    release binary, place it, and register it.
  • Delete Candyland install.sh and install.ps1.
  • Update any README/docs that reference Candyland install scripts.
  • Detritus manages Candyland start/stop/health as part of lifecycle
    ownership.
  • 🔴 Windows running-exe swap: Windows can't overwrite a running .exe
    (observed: candyland --update fails with "rename … Access is denied" while
    the sidecar runs). Detritus-owned update must stop the sidecar first, or use
    the rename-aside-and-swap pattern Detritus already uses for its own
    self-update.

0.5 Windows: No Flashing Console Windows On Any Spawn

  • Ensure all Windows process spawns are windowless.
  • Route internal/httpapi/system.go version probes
    (claude/git/gh --version, run on every /api/system poll) through
    the windowless process config.
  • Route internal/conductor/git.go git commands through the same helper.
  • Audit all other exec.Command sites.
  • Share one configureProc helper across packages.
  • Remember items 0.1 and 0.2 remove the largest window sources:
    per-session control-mcp and per-agent comms-mcp.
  • Broaden git/gh: internal/conductor/git.go spawns the actual git/gh
    commands (not just version probes) via plain exec.CommandContext — route
    these through the windowless helper too, not only the --version calls.
  • 🔴 Deep Windows process-tree kill: proc_windows.go kills only the direct
    child; Unix kills the whole process group (syscall.Kill(-pid),
    proc_unix.go). With long-lived quest/campaign agents + pause/stop/resume,
    stop/cancel will orphan claude's grandchildren on Windows. Needs a
    Windows job-object or taskkill /T. Gets worse at quest/campaign scale.

0.6 Remove "Launch From Your Editor" UI And Dead Backend Code

  • Remove UI run creation/planning wizard. Candyland observes and runs flows;
    Detritus/user sessions trigger them.
  • Remove src/components/PlanningFlow.js and related UI.
  • Remove backend endpoints/handlers that only served UI-initiated run
    creation or planning Q&A, including /api/runs/{id}/questions and wizard
    plumbing. Keep only the Detritus-triggered REST path.

0.7 Remove Developer/Non-Developer Split And Mode End-To-End

  • Remove developer/non-developer mode split and Mode fields end-to-end.
  • Remove UI mode split: src/mode.js, src/components/ModeToggle.js, and
    mode branches in App.js / panels.
  • Render everything through the developer lens.
  • Remove backend Mode: internal/run/types.go,
    internal/conductor/planner.go, conductor mode assignments, and
    Spec.Mode.
  • Remove the misleading "developer task" label seen when launching via
    /candyland.
  • Make the phase stepper match real behavior.
  • Remove the "Plan" step from Phases in internal/run/types.go, or rename
    it to "Partition" if the split is worth surfacing. Recommendation from the
    existing backlog: drop it.
  • Coordinate with review-loop work below. Settle final stepper as something
    like ["Build", "Integrate", "Review", "PR"].
  • Fix index math in internal/conductor/helpers.go and
    executor_claude.go; they currently index Phases by offset such as
    len(run.Phases) - 2.
  • Sweep dead mode constants, props, styles, labels, and tests.

0.8 Enforce Real Review Loop With Separate Review Agents

  • Add a real review phase after integration: spawn separate reviewer agents,
    fix findings, and re-review until clean or bounded failure.
  • Today review is mostly cosmetic: the flow is Plan -> Build -> Integrate ->
    PR, and Review in Phases is not backed by reviewer agents.
  • Add a genuine review phase after integration.
  • Spawn separate review agent(s) over the integrated branch.
  • Collect findings.
  • Loop fix -> re-review until review comes back empty, with bounded
    max-rounds mirroring maxReplans / maxAttempts.
  • Use Detritus review doctrine (review-rigor / /gh-self-review style)
    rather than an ad-hoc inline rubric.
  • Make the UI Review phase reflect real review activity.
  • Verify on a real run that a reviewer agent is spawned, findings drive
    fixes, and the run only opens the PR once review is clean.

0.9 Reuse Authenticated gh In Candyland Jobs

Firm requirement (see 0.2a): every Candyland-spawned agent uses the same
authenticated gh as the VSCode sessions and can open PRs in private repos.

  • Correct the diagnosis first. Agent (claude) spawns already inherit full
    os.Environ() (claudeEnv(), resilience.go:26-32,143), and the detached
    sidecar launch leaves cmd.Env nil (control.go:92-94) so it inherits the
    parent env too — detachSysProc only sets session/console flags and touches
    no env. The "detached path drops env/HOME" hypothesis is refuted by the
    code
    ; do not chase it.
  • Verify the real candidates instead: (a) the gh token's scope for the
    target private repo; (b) on Windows, whether USERPROFILE/HOME in the
    spawning context resolves to the profile gh auth login actually wrote to
    (credential-manager / GH_CONFIG_DIR resolution).
  • Propagate at the launch boundary (the new owner is detritus): once detritus
    owns ensure-up/start (0.1), it must pass the gh credential context through to
    the sidecar at launch (GH_*/GITHUB_TOKEN, GH_CONFIG_DIR/~/.config/gh,
    HOME/USERPROFILE); agents then inherit it via os.Environ(). This is the
    same propagation tracked in 0.2a — keep them consistent.
  • Verify end-to-end: a Candyland-spawned agent opens a PR against a non-public
    repo in one real run.

0.10 Deterministic Replay / Test Harness

  • There is no deterministic way to regression-test multi-agent orchestration
    without burning tokens. The HTTP brief-stub kept in 0.2 (a process that fetches
    its brief over HTTP and stands in for claude) is the seed of one — formalize
    it into a replay/regression harness so quest/campaign flows can be tested
    without real model calls (no API budget; see local-only constraint).

0.11 Update README To Match Reality

  • Rewrite Candyland README after these changes land.
  • Describe Candyland as an observe-only sidecar to Detritus.
  • Document that runs are driven from the user session/Detritus, not from
    in-UI planning.
  • Document one app process plus one claude process per active agent.
  • Document data at ~/.candyland/.
  • Document Detritus-managed install/update/lifecycle.
  • Document the real review loop.
  • Remove references to deleted pieces:
    control-mcp, comms-mcp as a process, install.sh, install.ps1,
    "Launch from your editor" UI, developer/non-developer modes, and the
    "Plan" phase.
  • Use present tense. Docs should describe the codebase as it is, not narrate
    the migration.
  • Check Detritus docs/README for stale Candyland mentions. Detritus README is
    generated, so update source docs and regenerate.

Phase 1 - Preserve And Normalize Run Traces

Candyland already records much of what is needed inside each task run. Do not
invent a separate trace platform yet. First, normalize the local record shape so
it can later be centralized with low friction. Centralized learning is not part
of the near-term implementation.

Trace Contract By Level

Trace enough at each level to support local observability now and centralization
later without backfilling missing relationships.

Run trace:

  • Stable IDs: runID, parent questID and/or campaignID when present.
  • Input: original scoped instruction, intent reference, folders/repos, branch,
    mode/type, created-by surface.
  • Agent graph: agent IDs, roles, worktrees, assigned task IDs, model/runtime,
    start/end timestamps, final state.
  • Task graph: task IDs, dependencies, owner agent, touched files, tests,
    verification result, review status.
  • Events: sequence number, timestamp, agentID, taskID when known, event type,
    tool/test/review metadata, redacted text/log payload.
  • Delivery: commits, PRs, issue links, review findings, retries, blockers,
    failure reason, cancellation reason.

Quest trace:

  • Stable IDs: questID, optional parent campaignID.
  • Objective: refined loop intent, target repos/folders, scope, safety boundary,
    verification command(s), stop/pause criteria.
  • Tick log: tick ID, start/end, discovery summary, triage decisions, active work
    items, launched runs, PRs, blockers, next action.
  • Findings/work items: source tick, evidence, classification, decision, child
    runID if accepted, final disposition.
  • Rollup: PRs opened, work items completed/skipped/blocked, current status,
    pause reason, last progress.

Campaign trace:

  • Stable IDs: campaignID.
  • Input: immutable original campaign input, requester/context, target repos or
    folders if known.
  • Intent Brief: restated goal, domain/surface scope, assumptions, open
    questions, dependencies, rough sizing, review routing suggestions.
  • Planning decisions: what became a quest, what became a direct run, what was
    split/deferred/blocked, and why.
  • Child links: questIDs, runIDs, PRs, issues, blockers.
  • Review: implementation-plan review result, final intent-review result, and
    campaign gate decisions.

Centralization constraint:

  • Store parent-child links and stable IDs from the start.

  • Keep traces local-first and exportable.

  • Redact or mark sensitive payloads before any future sync.

  • Do not build central storage, analytics, or platform-learning jobs yet.

  • Audit current run, agent, task, bus, and event schemas.

  • Define a stable local trace schema for each run:
    intent, prompt/brief, folders/repos, agents, task graph, messages, tool
    events, tests, review findings, retries, failures, PRs, timestamps, and
    rough token/cost fields when available.

  • Ensure every agent event is tied to runID, agentID, taskID where
    applicable, and a monotonic sequence.

  • Preserve original intent separately from generated plans/tasks so final
    review can compare output against intent, not only against task completion.

  • Store PR/issue links as first-class trace artifacts.

  • Make local trace export possible, even if centralization is deferred.

  • Do not build a broad user-facing analytics/editing surface yet. Platform
    improvement from trace data is a separate concern from everyday developer
    use.

Phase 2 - Quest: Candyland Iterative Loop

A quest is a Candyland-native iterative loop. It is the generalized sidecar
homologue to /janitor, but it is not constrained to maintenance. A quest can
be open-ended or finite, and it may produce several PRs over time.

Quest Roles

  • Quest lead: owns the loop objective, iteration strategy, triage decisions, and
    whether findings/tasks become work items. This is the technical overview role
    for the quest level.
  • Discovery/planning agent(s): inspect the target scope, provided task list,
    reference document, PR, or repo to identify the next work items.
  • Triage/review agent: checks whether a finding/task is safe, in-scope,
    actionable, and worth launching as a run.
  • Run tech lead: the existing per-run tech lead that partitions an accepted work
    item into agent tasks.
  • Builder/reviewer agents: the existing per-run implementation and review
    agents.

The quest lead decides whether to launch one run at a time or several parallel
runs. It should parallelize only when findings are independent enough that the
resulting PRs will not collide.

Quest Inputs

Examples:

  • Keep flaky tests under control.
  • Audit auth/session handling.
  • Continuously reduce dead code.
  • Improve test isolation in a repo.
  • Keep a migration moving during downtime.
  • Execute this task list, one PR per task or per safe group of tasks.
  • Check this PR against a policy/specification document.
  • Check this repo against a compliance or architecture document.
  • Repeatedly audit one topic until no safe/actionable work remains.

Quest Lifecycle

  • Add a first-class Quest model separate from Run.
  • A quest owns many findings, work items, runs, PRs, and trace records.
    It cascades into the existing run flow for each accepted work item/PR.
  • A quest begins with an intent-refinement phase equivalent to "plan a
    janitor" so the loop has a crisp objective, scope, iteration strategy,
    safety boundary, and verification command.
  • Formalize /janitor as plan-gated setup even for in-session use: it should
    refine loop intent before execution instead of jumping straight to generic
    scheduled audit. /janitor remains the in-session variant; /quest is
    Candyland-native.
    NOTE: /janitor is currently the deliberately not plan-gated sibling of
    /smith (it jumps to the audit tick). Plan-gating it changes in-session
    behavior and touches core/loop/janitor.md doctrine — track this as a
    separate doctrine change, not bundled with the Candyland substrate work.
  • Quest agents must compose doctrine (see Composition Constraint): the tick
    loop kb_gets core/loop + core/todo-audit (parallelize-only-when-safe) +
    core/completion; reviewers kb_get core/review-rigor. Do not re-encode in Go.
  • Honor the Delivery & PR Policy: a standalone quest aggregates many PRs
    (one per safe work item); a campaign-owned quest opens none (deliver: branch).
  • Apply Autonomy Levels & Budget Ceilings: quests carry an L1/L2/L3 level and
    a spend cap; pause with a visible reason at the cap.
  • Remove or de-emphasize detailed scheduling options for quest setup. The
    default goal is to minimize downtime between ticks and keep working until
    stopped, blocked, or out of safe findings.
  • Add quest tick behavior:
    discover or select the next work item, triage safe work, launch one or
    more build/check runs, review, open PRs or record compliance results,
    update quest state, immediately continue when safe.
  • Let the quest lead decide per tick whether accepted work items run
    sequentially or in parallel, based on file/repo overlap, risk, and review
    load.
  • Support multiple PRs per quest.
  • Add a stop/pause mechanism and visible reason for pause.
  • Add bounded retry/escalation behavior so a quest does not thrash on the
    same blocked item forever.
  • Add quest dashboard views: objective, current tick, findings, active runs,
    PRs, blockers, recent trace, and next action.

Detritus Surface

  • Add a single-word /quest command in Detritus.
  • /quest should read Detritus KB guidance, refine intent, ensure
    Candyland is running, then call Candyland-owned quest APIs.
  • Update /janitor docs to either delegate to /quest or clearly describe
    itself as the in-session/legacy variant.
  • Keep Candyland as the execution source of truth; Detritus should not
    duplicate quest state in scratchpads except as a launch handoff.

Phase 3 - Campaign: Intent-Driven Development Flow

A campaign is a program-level intent-to-delivery execution cycle. It can start
from a high-level goal, a partially formed brief, or a detailed implementation
plan. The distinguishing feature is not the input shape; it is that Candyland
owns the full execution cycle after launch: validation, decomposition,
implementation, review, and delivery.

Once launched, a campaign should not ask the user clarifying questions. If the
input is under-specified, contradictory, or becomes invalid mid-build, the
campaign routes that issue through its internal roles. Only a true hard blocker
pauses execution.

Campaign Roles

  • Intent lead: owns restating the intent, normalizing broad goals or detailed
    plans into the campaign record, collecting unresolved questions, and deciding
    when the Intent Brief is ready for the campaign gate.
  • Research agent(s): inspect code, existing PRs/issues, docs, and relevant
    external context where allowed.
  • Surface/domain mapper: marks backend, frontend, game logic, dashboard/UI,
    deployment, design, hardware, or other areas as needed/not needed with
    reasons. This is a product/technical mapping role, not a human ownership
    model.
  • Campaign tech lead: owns the technical overview above quests/runs. It decides
    what becomes a quest, what becomes a direct run, what can parallelize, and
    what must wait.
  • Quest lead(s): own persistent/exploratory workstreams spawned by the campaign.
  • Run tech lead(s): own the existing per-run partition/build/integrate/review
    flow.
  • Builder/reviewer agents: implement and review scoped run tasks.
  • Review router: suggests which human reviewers or review areas are needed based
    on touched surfaces. It does not model those humans as agents.
  • Intent reviewer: checks final output against original intent, not just the
    generated task list.

Campaign Communication Flow

Communication is hierarchical and agent-directed. Peer agents do not negotiate
scope directly, and no role asks the user for clarification during execution.

Campaign supervisor
  -> Intent lead
     -> Research agent(s)
     -> Surface/domain mapper
  -> Campaign tech lead
     -> Quest lead(s)
        -> Discovery agent(s)
        -> Triage/review agent
        -> Run tech lead(s)
           -> Builder/reviewer agents
     -> Direct run tech lead(s)
        -> Builder/reviewer agents
  -> Review router
  -> Intent reviewer

Rules:

  • The campaign supervisor owns the campaign state and is the only role that
    moves the campaign between stages.
  • The intent lead owns definition questions. It answers them from available
    context, assumptions, research, and role feedback; it does not ask the user.
  • The campaign tech lead owns technical decomposition, dependency ordering, and
    parallelization across child quests/runs.
  • Quest leads own persistent workstreams spawned by the campaign.
  • Run tech leads own per-run partition/build/integrate/review flow.
  • Builders and reviewers report blockers/findings upward to their run tech lead.
  • Quest-level issues route to the quest lead; campaign-level issues route to the
    campaign tech lead or intent lead depending on whether they are technical or
    definitional.
  • The review router suggests human review areas/reviewers for the resulting PRs
    but does not contact the user or model humans as agents.

Campaign Lifecycle

  • Add a first-class Campaign model above Quest and Run.
  • Store the original campaign input as immutable campaign input, whether it
    is a high-level goal, partial brief, or detailed plan.
  • Generate an Intent Brief with:
    restated goal, scope by domain, resolved/escalated questions, draft task
    list, dependencies, rough sizing, and review-routing suggestions.
  • Add a campaign gate after the Intent Brief: verify that the brief reflects
    the campaign input before implementation planning proceeds.
  • Add a campaign gate after implementation planning: verify that the
    proposed quests/runs would plausibly deliver the campaign input before
    execution begins.
  • Allow the campaign to split into multiple features/tasks when the intent
    is too broad.
  • Support early intent escalation when research or mapping agents cannot
    resolve what the campaign input means. This escalates to the intent lead
    or campaign supervisor, not to the user.
  • Support mid-build intent realignment when reality no longer fits the
    original box: redefine, split, or add resources inside the campaign flow.
  • Convert the approved/settled brief into child quests and/or direct
    Candyland runs. Persistent or exploratory streams become quests; finite
    scoped delivery items can become direct runs.
  • Let the campaign tech lead decide what can execute in parallel across
    child quests/runs, based on repo/file overlap, dependencies, risk, and
    review load.
  • Preserve artifact-driven handoff: original intent, brief, issues, PR links,
    and trace records.
  • Keep campaign delivery to one PR per repo involved. Campaigns may span
    multiple repos, but each repo should have a single campaign PR so agents
    and human reviewers can track the campaign coherently.
  • Suggest review routing for specialized or high-risk surfaces without
    modeling domain reviewers as agents.
  • Add final intent review before campaign completion.
  • Specify the intent-review METHOD (the campaign's novel core — currently just
    named).
    core/review-rigor covers code mechanics, not intent-satisfaction.
    Define a concrete, reproducible method for "did we build what was meant":
    e.g. the intent reviewer reads the immutable original input + Intent Brief, then
    checks each brief commitment against the shipped diff/PRs and produces a
    satisfied/partial/missed verdict per commitment with evidence — not a vibe check.
    Without a method this gate is not reproducible.
  • Apply Autonomy Levels & Budget Ceilings: a campaign carries a global spend
    cap across all child quests/runs and a pre-launch cost estimate; it pauses at the
    cap with a visible reason rather than spawning unboundedly.
  • Delivery follows the Delivery & PR Policy: child quests/runs commit to a
    per-repo campaign branch (no child PRs); the campaign opens one PR per repo at
    the gate after intent review. Agents handle blockers to completion (resolve or
    escalate within the hierarchy); a blocker does not abandon delivery, and the
    per-repo branch persists across a pause for resume.

Detritus Surface

  • Add a single-word /campaign command in Detritus.
  • /campaign should launch the Candyland campaign flow directly from a
    high-level instruction, partial brief, or detailed plan. It should ensure
    Candyland is running and call Candyland-owned campaign APIs. It should not
    require a prior /plan.
  • Keep /vibe distinct from /campaign. /vibe is an in-session flow
    that does not use the Candyland multi-agent loop. /campaign is a
    Candyland-run program-level execution flow. They may share doctrine, but
    one should not be described as a simple legacy/delegating form of the
    other.

Phase 4 - Centralization Readiness Only

Do not implement centralized learning data yet. This phase is only about making
local traces easy to centralize later with minimal migration friction.
Developers should not need to understand or edit platform traces to use
Detritus/Candyland.

  • Keep all run/quest/campaign records local-first.
  • Use stable IDs and parent-child links at every level.
  • Keep trace schemas versioned.
  • Keep export format explicit and boring, preferably JSON/JSONL.
  • Mark/redact sensitive payload fields before any future sync path exists.
  • Avoid building dashboards, analytics, or platform-improvement jobs that
    depend on a central store.
  • Keep learned-memory writes verified-gated if/when traces later feed
    improvement workflows.
  • Why this matters (the eventual purpose): the deferred meta-loop is
    langchain's "Loop 4 / hill climbing" — an analysis agent reads production
    traces, spots systematic failure patterns across runs, and proposes
    prompt/tool/rubric adjustments, making the inner loops better over time. We do
    NOT build it now (no API budget; local-only). But design the Phase 1 trace
    contract — stable IDs, parent-child links, per-finding verdicts — so this loop
    can later consume traces with zero backfill. That is the reason the trace shape
    is first-class now.

Phase 5 - Documentation And Command Cleanup

Do this after substrate changes land, not before.

  • Update Detritus docs/flows/build/candyland.md to describe the final
    lifecycle-owned sidecar.
  • Add docs/flows/build/quest.md.
  • Add docs/flows/build/campaign.md or docs/flows/plan/campaign.md;
    choose location based on whether the command is framed as build or intake.
  • Regenerate Detritus command shims and README:
    detritus --readme && detritus --plugin-commands.
  • Run go generate ./... after KB docs change.
  • Run go test ./... in Detritus.
  • Update Candyland README to describe the observe-only sidecar, quests,
    campaigns, local traces, review phase, and Detritus-managed lifecycle.

Phase 6 - Candyland UI Hierarchy

The UI should expose the hierarchy without hiding the atomic run/task history.
Users should be able to inspect work at the level they care about and pivot
between levels.

  • Keep one main history/work section with filters and pivoted views, rather
    than separate top-level history pages.
  • Provide view pivots for runs/tasks, quests, and campaigns inside that main
    section.
  • Keep an all-runs/all-tasks history view so users can see runs executed
    outside any quest or campaign.
  • Add filters for:
    no parent, campaign, quest, repo/folder, status, PR state, date, and text.
  • In run/task views, show parent campaign/quest links when present.
  • In quest views, show objective, current tick, findings, accepted work
    items, child runs, PRs, blockers, and next action.
  • In campaign views, show original intent, Intent Brief, gates, child
    quests/runs, PRs, blockers, review routing, and final intent review.
  • Support pivoting from any level to children and back to parent without
    losing filters.
  • Keep the UI observe/control focused: pause/stop/resume where supported,
    but no broad trace editing.

Phase 7 - Candyland Quest/Campaign APIs

Candyland owns the quest/campaign/run APIs. Detritus should only ensure the
sidecar is running and call these APIs as a client.

Quest API outline:

  • POST /api/quests creates a quest.
  • POST /api/quests/{id}/begin starts or continues quest execution.
  • GET /api/quests/{id} returns quest status and rollup state.
  • POST /api/quests/{id}/pause pauses future ticks without deleting state.
  • POST /api/quests/{id}/resume resumes a paused quest.
  • POST /api/quests/{id}/stop stops the quest and marks the reason.
  • GET /api/quests/{id}/runs lists child runs.
  • GET /api/quests/{id}/findings lists findings/work items.

Campaign API outline:

  • POST /api/campaigns creates a campaign.
  • POST /api/campaigns/{id}/begin starts or continues campaign execution.
  • GET /api/campaigns/{id} returns campaign status and rollup state.
  • POST /api/campaigns/{id}/pause pauses future execution without deleting
    state.
  • POST /api/campaigns/{id}/resume resumes a paused campaign.
  • POST /api/campaigns/{id}/stop stops the campaign and marks the reason.
  • GET /api/campaigns/{id}/quests lists child quests.
  • GET /api/campaigns/{id}/runs lists direct and quest-child runs.

Suggested First Implementation Order

  1. Finish Phase 0 hardening, especially HTTP comms MCP and real review phase.
  2. Normalize local run trace records without centralization.
  3. Add parent-child trace links and UI filters for run/task history.
  4. Add Quest data model and dashboard view.
  5. Add /quest Detritus command and Candyland quest launch endpoint.
  6. Implement quest tick loop with discovery -> triage -> run -> review -> PR.
  7. Add Campaign model and Intent Brief generation.
  8. Add campaign gates for Intent Brief and implementation-plan review.
  9. Add Candyland quest/campaign APIs.
  10. Add /campaign Detritus command that launches Candyland campaigns.
  11. Add review routing suggestions and final intent review.
  12. Keep centralization as future work; only preserve exportable local traces now.

?? Generated with Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    planeSynced with Plane

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions