Back to Dyad

Explore Chat History

plans/explore_chat_history.md

1.9.029.3 KB
Original Source

Explore Chat History

Generated by swarm planning session on 2026-07-21

Summary

Add a Pro-only explore_chat_history tool that delegates broad historical recall to a bounded read-only sub-agent. The sub-agent iteratively uses the existing same-app search_chats and read_chat primitives, then returns a compact synthesis with host-validated evidence targets, conflicts, confidence, and coverage gaps.

The primary agent will see explore_chat_history and the deterministic read_chat drill-down tool, but not search_chats. All three capabilities are unavailable to non-Pro users with no fallback. explore_chat_history defaults to always consent while remaining user-overridable through the existing tool-permission setting.

Problem Statement

The existing search_chatsread_chat flow can retrieve history, but it makes the primary model invent keyword queries, interpret noisy excerpts, issue repeated reads, and carry raw archival text in its context. This is weakest for vague recall requests such as “What did we decide about authentication?” where the user expects the agent to investigate prior conversations rather than ask them to remember exact terms.

A dedicated sub-agent can reformulate searches, inspect only the most relevant bounded context, identify conflicting or superseded decisions, and compress the evidence before returning it to the primary agent. This improves recall while keeping the primary context smaller and the retrieval process inspectable.

Product Principles

  • Backend-flexible: use the existing Dyad Engine model-client abstraction; do not introduce provider-specific APIs into the tool contract.
  • Productionizable: preserve stable same-app authorization, bounded resource use, cancellation, classified failures, and deterministic source attribution.
  • Intuitive but power-user friendly: broad recall uses one high-level tool; targeted verification remains possible through read_chat.
  • Transparent over magical: show visible progress and consulted evidence because the explorer runs without a prompt-time consent dialog by default.
  • Bridge, don't replace: retain the underlying deterministic local index and retrieval primitives rather than replacing them with opaque model-only memory.
  • Delightful: use a polished, compact history-exploration card with meaningful phases and readable evidence states.

Goals

  • Answer vague questions about prior decisions, requirements, failures, and work for the current app.
  • Keep raw historical text out of the primary model context except when the primary agent deliberately drills down with read_chat.
  • Return source-attributed evidence that cannot cite unobserved or cross-app messages.
  • Expose progress, partial coverage, conflicts, and uncertainty clearly.
  • Bound Engine cost, latency, tool calls, and historical-text disclosure.
  • Support Agent, Ask, and Plan modes for eligible Pro users because the tool is read-only.
  • Preserve recall of compacted-away messages from the current chat through explicit bounded reads.

Non-goals

  • Any non-Pro access or degraded fallback for explore_chat_history, search_chats, or read_chat.
  • Semantic/vector search or embeddings.
  • Cross-app history, including through @app: references.
  • Full transcript export, persistent synthesized memory, or automatic decision rewriting.
  • Replacing the existing user-facing chat-search dialog.
  • Searching aiMessagesJson, model thinking, raw tool-call transcripts, or bulky generated payloads.
  • Proving that every synthesis sentence is logically entailed by its evidence; the host validates provenance, not semantics.
  • Clickable chat/message deep links in MVP.
  • Gating the background FTS indexer lifecycle by entitlement in MVP.

User Stories

  • As a Pro user, I want the agent to investigate earlier conversations so I do not need to remember exact keywords or repeat prior decisions.
  • As a Pro user, I want to see which chats and messages support a recalled conclusion so I can judge its reliability.
  • As a Pro user, I want conflicting or superseded decisions surfaced rather than silently collapsed into one answer.
  • As a Pro power user, I want to inspect a cited discussion directly without paying for another broad exploration.
  • As a privacy-conscious Pro user, I want to override the explorer's default permission to ask or never.
  • As a non-Pro user, I should not receive any of the three history tools or a lower-quality fallback with different privacy semantics.

Scope

In Scope (MVP)

  • Pro-only high-level explore_chat_history wrapper and Engine sub-agent.
  • Internal-only search_chats capability and Pro-only main-facing read_chat.
  • Iterative keyword reformulation, bounded search, bounded contextual reads, and early stopping.
  • Host-maintained observation/evidence registry and host-rendered evidence fields.
  • Structured report with synthesis, findings, conflicts, gaps, confidence, index/truncation status, and next action.
  • Visible pending, success, empty, partial/indexing, conflict, cancelled, and error states.
  • Deterministic evidence-only fallback when valid observations exist but synthesis fails.
  • Entitlement, privacy, authorization, provenance, prompt-injection, performance, and UI tests.

Out of Scope (Follow-up)

  • Clickable evidence targets that open the source chat at a message.
  • Semantic retrieval or hybrid FTS/vector ranking.
  • Cross-app exploration.
  • Configurable sub-agent model or user-supplied sub-agent provider.
  • Cached synthesized reports.
  • Entitlement-gated index draining/backfill; revisit only if measured CPU or storage costs justify the lifecycle complexity.

Tool Exposure and Entitlement

Primary-agent matrix

Contextexplore_chat_historyread_chatsearch_chats
Eligible Pro Agent/Ask/PlanExposedExposedHidden
Non-Pro/basic agentHiddenHiddenHidden
Free-model mode where Engine sub-tools are unavailableHiddenHiddenHidden
Internal explorer sub-agentWrapper unavailableAvailable internallyAvailable internally

Implementation requirements:

  • Add isEnabled: (ctx) => ctx.isDyadPro to all three ToolDefinitions.
  • Add all three names to PRO_AGENT_ONLY_TOOLS so basic-agent filtering is explicit and testable.
  • Mark explore_chat_history with usesEngineEndpoint: true; free-model filtering must remove the whole history surface, including direct read_chat, per the product requirement.
  • Remove searchChatsTool from the primary TOOL_DEFINITIONS array, but retain its implementation and historical <dyad-search-chats> renderer support.
  • Add exploreChatHistoryTool to TOOL_DEFINITIONS; retain readChatTool there.
  • Re-check ctx.isDyadPro inside the explorer runner and internal adapters. Toolset exclusion is not an execution-time security boundary.
  • Fail with a classified DyadErrorKind.Precondition when Pro or Engine/model prerequisites are unavailable. Never fall back to the parent provider or expose the hidden low-level search tool.
  • explore_chat_history.defaultConsent = "always".
  • read_chat.defaultConsent remains "ask"; a power user may change it to always.
  • Internal sub-agent search/read calls are covered by the explorer invocation and must never trigger nested consent prompts.
  • Create an independent consent key for explore_chat_history; do not inherit or migrate stored search_chats/read_chat choices.
  • Preserve legacy search/read consent values in settings for reversibility, but do not render an unusable search_chats permission row when it is internal-only.
  • Permission copy must state that bounded same-app historical chat text may be sent to the Dyad Engine for synthesis.
  • Direct read_chat copy must separately state that selected historical text is sent to the active model.
  • Telemetry may contain duration, step/tool counts, observation bytes, index status, confidence, truncation, and outcome. It must never contain query text, chat titles/IDs, message IDs, excerpts, quotes, or transcript content.

This consent asymmetry is deliberate: the explorer is a visible, bounded product workflow that defaults to frictionless recall; direct read_chat can retrieve arbitrary pages outside an explorer run and retains a granular privacy prompt.

User Experience

Primary Flow

  1. The primary agent recognizes a broad or uncertain history question and calls explore_chat_history with a concise research question.
  2. A <dyad-explore-chat-history> card appears before retrieval and expands while work is in progress.
  3. The card streams action-level phases without exposing chain-of-thought or private excerpts:
    • “Searching prior chats…”
    • “Reading 2 relevant discussions…”
    • “Checking conflicting decisions…”
    • “Synthesizing evidence…”
  4. The sub-agent stops when evidence is sufficient or a hard budget is reached.
  5. A high-confidence complete result collapses to a header such as “3 chats · 6 evidence targets.”
  6. Partial, indexing, conflicting, low-confidence, cancelled, and failed results remain expanded with a concise warning.
  7. If exact context is still needed, the primary agent calls Pro-only read_chat with a cited chat/message target. It must not restart broad discovery.

Key States

  • Pending: expanded, visible immediately, phase text plus searched/read counts; no raw historical text in transient progress.
  • Success: concise synthesis with expandable findings and evidence targets; may collapse automatically when confidence is high and coverage is complete.
  • Empty: “No relevant prior discussion found”; recommend asking the user rather than treating absence as proof.
  • Indexing/partial: show that recall may be incomplete and preserve index/truncation warnings.
  • Conflict: keep expanded and show competing statements with dates/evidence; recency informs but does not automatically override.
  • Cancelled: stop progress immediately and never replace the card later with a stale completion.
  • Failure: show a classified, actionable failure; do not silently switch to direct search.
  • Evidence-only fallback: clearly label that synthesis failed and show only deterministically observed evidence.

Accessibility

  • Make the card keyboard-expandable with a clear accessible name and focus state.
  • Communicate status through text and icons, not color alone.
  • Announce phase changes politely; never stream token-by-token updates to a live region.
  • Keep evidence text selectable and copyable.
  • Give truncated queries/titles complete accessible names.
  • Render localized human-readable dates in the UI while preserving ISO timestamps in the model result.

Technical Design

Wrapper Tool

Add src/pro/main/ipc/handlers/local_agent/tools/explore_chat_history.ts, modeled on explore_code.ts.

Initial input schema:

ts
const exploreChatHistorySchema = z.object({
  query: z.string().trim().min(1).max(500),
});

Do not add an app_id or app_name; exploration is locked to ctx.appId. Avoid an intent enum until evaluation demonstrates that it changes useful behavior.

Tool properties:

ts
{
  name: "explore_chat_history",
  defaultConsent: "always",
  usesEngineEndpoint: true,
  isEnabled: (ctx) => ctx.isDyadPro,
  // No modifiesState flag: this is read-only.
}

The wrapper streams escaped progress XML, calls runExploreChatHistorySubagent, emits the final escaped card XML, and returns the compact report to the primary agent.

Sub-agent Modules

Follow the established explore_code separation:

  • explore_chat_history_subagent.ts — model loop, budgets, retries, abort handling, child ToolSet.
  • explore_chat_history_subagent_prompts.ts — archival/untrusted-data system and task prompts.
  • explore_chat_history_subagent_evidence.ts — observation registry, dedupe, byte accounting, candidate IDs.
  • explore_chat_history_subagent_report.ts — submit schema, host validation, action/confidence derivation, deterministic fallback.
  • explore_chat_history_subagent_progress.ts — action-level progress formatting without raw text.

Use the existing Engine client/model path from explore_code for MVP. Isolate model selection in one constant so it can change independently later.

Child ToolSet

The internal ToolSet contains exactly:

  • a locked-context search_chats adapter,
  • a locked-context read_chat adapter,
  • submit_report.

It must not inherit the primary toolset or gain file, network, MCP, SQL, mutation, or arbitrary app-selection capabilities.

Internal adapters should reuse typed low-level functions/results rather than parsing the wrapper's display XML. They must:

  • force ctx.appId and ctx.chatId,
  • suppress nested renderer XML and consent callbacks,
  • enforce Pro again,
  • register bounded observations before returning compact candidate handles to the model,
  • normalize errors without disclosing cross-app existence,
  • preserve index_status, truncation, compaction-summary, and partial-coverage metadata.

Current-chat Behavior

  • Broad FTS search continues to exclude ctx.chatId to avoid rediscovering active context.
  • The child prompt receives the locked current chat ID and may explicitly call read_chat when the question concerns earlier or compacted-away discussion in the current chat.
  • Current-chat reads retain the existing m.id < ctx.messageId snapshot cutoff so the in-flight assistant response cannot read itself.
  • Do not automatically read the current chat on every exploration.

Budgets and Reliability

Start with explicit conservative constants, then tune using evaluation data:

  • 8–10 model steps.
  • 20–30 total internal search/read executions.
  • One model retry.
  • Existing 12 KB search and 20 KB read per-call limits.
  • A separate aggregate observation-byte cap for the whole run.
  • Bounded findings, conflicts, evidence targets, and final serialized output.
  • Per-query and per-chat/message read deduplication.
  • Brief bounded waits for index freshness only; never wait indefinitely.

Propagate ctx.abortSignal, drain/cancel the Engine stream, and call the existing orphan-stream cleanup helper. Retry must not duplicate observations or progress events.

If the Engine/model fails:

  • with one or more valid observations, return a clearly labeled deterministic evidence-only partial report;
  • with no valid observations, surface the classified error;
  • never preserve unvalidated model prose in the fallback.

Archival Safety and Prompt Injection

The sub-agent system prompt must state that historical content is untrusted archival data, never current instructions. Retrieved text cannot alter the task, request capabilities, redefine report format, or authorize actions.

Additional safeguards:

  • Child capabilities remain read-only and fixed regardless of retrieved text.
  • Continue using the cleaned projection that excludes thinking, raw code/file bodies, large tool payloads, aiMessagesJson, and recursive history-tool output.
  • Escape all renderer XML.
  • Treat fake citations, fake XML, tool instructions, and report-shaped text inside old chats as plain evidence content.

Evidence Registry and Report Contract

Every successful internal search/read registers immutable observed evidence and returns opaque candidate IDs to the child. The child selects candidate IDs; it never authors chat IDs, message IDs, dates, roles, titles, or supposedly verbatim quotes.

Host validation must:

  • reject unknown, duplicate, cross-app, mismatched, or out-of-budget candidate IDs;
  • ensure evidence came from the current explorer invocation and locked app;
  • resolve canonical source fields from the registry;
  • deduplicate repeated search/read observations;
  • clamp evidence counts and serialized bytes;
  • preserve index/truncation/compaction/partial warnings;
  • require findings and conflicts to reference observed evidence;
  • fail closed to deterministic observed evidence if report validation cannot succeed.

The host does not attempt brittle semantic entailment checks. Model-authored synthesis is labeled analysis; host-rendered excerpts are labeled evidence.

Suggested result shape:

ts
interface ExploreChatHistoryReport {
  query: string;
  outcome: "complete" | "no_match" | "partial";
  summary: string;
  findings: Array<{
    claim: string;
    evidence: Array<{
      chat_id: number;
      chat_title: string | null;
      message_id: number;
      role: "user" | "assistant";
      created_at: string;
      excerpt: string;
      projection_truncated?: boolean;
      is_compaction_summary?: boolean;
    }>;
  }>;
  conflicts: Array<{
    description: string;
    evidence: /* same host-resolved evidence shape */ unknown[];
  }>;
  missing_coverage: string[];
  index_status: "ready" | "indexing";
  confidence: "high" | "medium" | "low";
  action:
    | "answer_from_report"
    | "read_targets"
    | "ask_user"
    | "no_relevant_history";
  archival_content: true;
  partial_reason?: string;
}

Use “evidence targets” or “source-attributed evidence” in MVP copy, not “links,” until navigation exists.

Indexer Lifecycle

Do not change the existing FTS schema, migrations, triggers, dirty queues, projection, or indexer lifecycle for MVP.

The derived on-device index may continue updating for non-Pro users even though no non-Pro tool can access it. This is a deliberate v1 trade-off that avoids entitlement-transition races and makes upgrades immediately useful; it is not a fallback tool surface.

Add privacy-safe measurements for drain duration, dirty-queue size, and index size. Define thresholds during evaluation that trigger a follow-up project to gate backfill/draining by entitlement. Deletion and projection-version repair must remain correct under any future gating design.

Components Affected

  • src/pro/main/ipc/handlers/local_agent/tools/explore_chat_history.ts — new primary wrapper and XML lifecycle.
  • src/pro/main/ipc/handlers/local_agent/tools/explore_chat_history_subagent*.ts — model loop, prompts, evidence, report, and progress.
  • src/pro/main/ipc/handlers/local_agent/tools/search_chats.ts — Pro gate and typed internal reuse seam; remove main-oriented wording where appropriate.
  • src/pro/main/ipc/handlers/local_agent/tools/read_chat.ts — Pro gate and description updated for explorer evidence drill-down.
  • src/pro/main/ipc/handlers/local_agent/tool_definitions.ts — entitlement/exposure matrix and new tool registration.
  • src/prompts/local_agent_prompt.ts — route broad historical recall to explorer and targeted evidence inspection to read_chat.
  • src/components/chat/DyadExploreChatHistory.tsx — new progress/report card.
  • src/components/chat/DyadMarkdownParser.tsx — parse <dyad-explore-chat-history> while retaining legacy search/read tags.
  • src/i18n/locales/*/chat.json — card labels, states, warnings, and accessibility text for every locale.
  • Local-agent integration tests and E2E request snapshots — exact eligible/ineligible tool surfaces and updated descriptions.
  • Fake Engine/LLM fixtures — deterministic multi-search/read sub-agent scenarios.

No database migration is expected.

Implementation Plan

Phase 1: Entitlement and Internal Retrieval Seams

  • Add execution-time and isEnabled(ctx.isDyadPro) gates to search/read.
  • Add all history tools to explicit Pro-only filtering.
  • Refactor search/read only enough to expose typed, bounded internal operations without renderer/consent side effects.
  • Remove search_chats from the primary toolset while preserving its implementation, tests, stored transcript rendering, and legacy consent value.
  • Update exact toolset tests for Pro, non-Pro/basic, free-model, Ask, and Plan modes.

Phase 2: Evidence and Report Foundation

  • Define observation registry, opaque candidate IDs, aggregate byte accounting, and dedupe rules.
  • Define submit_report schema and host-derived outcome/confidence/action rules.
  • Implement strict provenance validation and deterministic evidence-only fallback.
  • Add adversarial tests for invented IDs/quotes, cross-app/mismatched IDs, oversized reports, duplicates, stale/deleted sources, and report-shaped archival text.

Phase 3: Sub-agent Runner

  • Implement the locked read-only child ToolSet.
  • Add archival-data prompt framing and current-chat compacted-history instructions.
  • Add step, call, byte, retry, and dedupe budgets.
  • Add Engine/model preconditions, abort propagation, stream cleanup, partial failure, and retry handling.
  • Add progress events that contain phases/counts but no historical text.

Phase 4: Primary Tool and Prompt Guidance

  • Implement exploreChatHistoryTool with defaultConsent: "always" and usesEngineEndpoint: true.
  • Register the explorer and retain only Pro-gated read_chat as the main low-level drill-down.
  • Update system-prompt guidance: broad/unknown recall → explorer; known evidence target → read; no direct broad search.
  • Update consent/settings copy and ensure internal search has no orphaned visible permission row.

Phase 5: Renderer and Accessibility

  • Add the new history explorer card using existing Dyad card primitives and explore-code lifecycle conventions.
  • Implement pending, complete, empty, indexing/partial, conflict, cancelled, failed, and evidence-only states.
  • Keep partial/conflict/low-confidence states expanded; collapse only complete high-confidence results.
  • Render host-provided title/date/role/excerpt/IDs as selectable evidence targets.
  • Add keyboard, accessible-name, polite-live-region, truncation, and non-color status coverage.
  • Preserve rendering of historical dyad-search-chats and dyad-read-chat messages.

Phase 6: Evaluation, Snapshots, and Rollout

  • Build a curated recall benchmark for vague decisions, exact decisions, conflicts, no match, partial indexing, prompt injection, compacted current-chat history, and cross-app isolation.
  • Establish p50/p95 latency, time to first visible progress, maximum Engine input, primary-context savings, and cost baselines.
  • Add privacy-safe index CPU/queue/size measurements and follow-up thresholds.
  • Update prompt snapshots, exact tool-array integration tests, and every affected E2E request baseline, including extensionless snapshots.
  • Run focused unit/integration suites, formatting, linting, and type checks.
  • Run npm run build before targeted Playwright tests and update snapshots through Playwright rather than hand-editing them.

Testing Strategy

Unit

  • defaultConsent === "always", usesEngineEndpoint === true, and wrapper/runner Pro preconditions.
  • Exact child allowlist: search, read, submit only.
  • Steps, tool calls, aggregate bytes, retries, duplicate queries/reads, and final output bounds.
  • Abort and orphan-stream cleanup; no late completion after cancellation.
  • Provider failure before observations versus deterministic fallback after observations.
  • Observation registry rejects invented, duplicate, mismatched, cross-app, and out-of-budget candidates.
  • Host constructs every evidence field; fallback contains no unvalidated model prose.
  • Prompt-injection fixtures containing tool instructions, fake XML/citations, and fake reports.
  • Current-chat read cutoff before ctx.messageId and no automatic current-chat search.
  • Indexing, truncation, compaction-summary, conflict, and partial flags survive report formatting.

Integration

  • Eligible Pro requests expose explorer + read, never search.
  • Non-Pro/basic/free-model requests expose none of the three in Agent, Ask, and Plan modes.
  • Internal invocation still fails closed for non-Pro even if called directly.
  • The explorer performs multiple searches/reads and returns a source-attributed synthesis.
  • Cross-app and nonexistent targets remain indistinguishable and leak no partial evidence.
  • Consent settings show only main-facing tools and use independent defaults.

Renderer and E2E

  • Parser/card pending, success, empty, partial/indexing, conflict, cancelled, error, and evidence-only states.
  • Partial/conflict/low-confidence cards remain expanded.
  • Accessible keyboard interaction, status names, live-region behavior, and selectable evidence.
  • Fake Engine scenario demonstrates query reformulation, bounded reads, synthesis, and main-agent continuation.
  • Built E2E verifies Pro/non-Pro tool surfaces and visible progress before retrieval completes.
  • Persisted legacy search/read cards still render after main search exposure is removed.

Launch Criteria

  • Zero fabricated citations in the curated benchmark; every rendered target resolves to observed same-app evidence.
  • Cross-app IDs, mismatched message IDs, and in-flight current-chat content disclose nothing through success, error, or fallback paths.
  • Archived prompt injection cannot change the child task, add capabilities, spoof evidence, or expose excluded payloads.
  • All entitlement-matrix tests and request snapshots pass.
  • Complete, no-match, partial, indexing, conflict, cancelled, and failure outcomes are visually and programmatically distinguishable.
  • Cancellation leaves no orphan Engine stream; retry does not duplicate evidence or progress.
  • Index waits and all model/tool/byte budgets are hard-bounded.
  • A seeded large-history evaluation establishes acceptable p95 latency and Engine input/cost before release.
  • Telemetry contains no query, title, ID, excerpt, quote, or transcript content.
  • Permission and pending-card copy plainly disclose that bounded same-app history is sent to the Dyad Engine.

Risks and Mitigations

RiskLikelihoodImpactMitigation
Archived prompt injection changes behaviorHighHighFixed read-only child ToolSet, archival framing, host evidence registry, adversarial tests
Fabricated or misattributed evidenceMediumHighOpaque candidate IDs; host-resolved canonical fields; fail-closed fallback
Cross-app disclosureLowCriticalLocked ctx.appId, SQL scoping, execution-time Pro checks, indistinguishable NotFound behavior
Privacy surprise from always-default consentMediumHighVisible pre-retrieval card, explicit Engine disclosure, ask/never override, content-free telemetry
High latency or Engine costMediumMediumEarly stopping, tight steps/calls/bytes, dedupe, benchmark launch gate
Incomplete/stale index coverageMediumMediumBounded wait, visible index status/gaps, never infer absence as fact
Non-Pro indexing consumes resourcesMediumLow–MediumMeasure CPU/queue/index size; define threshold for entitlement-gating follow-up
Tool-surface regression from hidden searchMediumMediumExact tool-array tests, prompt/request snapshots, preserve legacy renderer
Cancellation leaves orphan workLowHighAbort propagation, stream draining/cleanup, retry idempotence tests

Open Questions

No blocking product questions remain for MVP. During implementation/evaluation, record the chosen numeric limits and launch thresholds for:

  • sub-agent steps and total internal calls within the proposed ranges;
  • aggregate observation and final-output byte budgets;
  • acceptable p95 latency and Engine input/cost;
  • recall-quality target on the curated benchmark;
  • CPU, queue-size, and index-size thresholds that would trigger Pro-gated index lifecycle work.

Decision Log

  • Pro-only, no fallback: all three history capabilities are absent outside eligible Pro contexts.
  • One broad surface: explore_chat_history replaces main-agent search_chats; search_chats remains an internal primitive.
  • Keep deterministic drill-down: Pro-only read_chat stays main-facing for known evidence targets and compacted current-chat context.
  • Intentional consent asymmetry: explorer defaults to always; direct read stays ask; both remain user-overridable.
  • Strict provenance, not semantic proof: the host controls citations and bounds while the model authors labeled analysis.
  • Current-chat handling: broad search excludes the active chat; explicit bounded current-chat reads may recover compacted-away context and stop before the in-flight response.
  • No migration: reuse the existing FTS5 index, projection, and dirty-queue lifecycle.
  • Indexer remains lifecycle-neutral in v1: measure non-Pro resource cost before adding entitlement transitions.
  • Evidence targets, not links: title/date/excerpt/IDs ship in MVP; clickable navigation is follow-up.
  • Transparency substitutes for prompt-time friction: the card appears before retrieval and exposes phases, sources, partial coverage, and failures.

Generated by dyad:swarm-to-plan