Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions loopx/capabilities/catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from .value_connectors.catalog_entry import VALUE_CONNECTORS_CATALOG_ENTRY
from .explore.catalog_entry import EXPLORE_CATALOG_ENTRY
from .auto_research.catalog_entry import AUTO_RESEARCH_CATALOG_ENTRY
from .deep_research.catalog_entry import DEEP_RESEARCH_CATALOG_ENTRY
from .public_safe_outbound.catalog_entry import PUBLIC_SAFE_OUTBOUND_CATALOG_ENTRY
from .registry import CapabilityRegistry

Expand All @@ -49,6 +50,7 @@
VALUE_CONNECTORS_CATALOG_ENTRY,
EXPLORE_CATALOG_ENTRY,
AUTO_RESEARCH_CATALOG_ENTRY,
DEEP_RESEARCH_CATALOG_ENTRY,
PUBLIC_SAFE_OUTBOUND_CATALOG_ENTRY,
)
# Preserve the original import surface while routing all reads through the registry.
Expand Down
72 changes: 72 additions & 0 deletions loopx/capabilities/deep_research/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Deep research (evidence-ledger research loop)

`/loopx-deepresearch` turns one user question into a bounded, auditable research
session: question, source, claim, and contradiction ledgers live in
`.loopx/deepresearch/research.json`, the packet (`loopx deepresearch status`)
owns what to research next and when to stop, and the final report keeps every
citation resolvable to a recorded source.

## Boundary with auto-research

The built-in `auto-research` capability and this capability both do "bounded
research" but own different truths and must not be merged casually:

| | auto-research | deep-research (this) |
| --- | --- | --- |
| Unit of work | a LoopX **goal** with role-scoped workers | one **session ledger** in a project |
| State authority | goal todos, hypotheses, rollout events | `.loopx/deepresearch/` ledger |
| Progression | worker contract + terminal decision/review | packet expeditions + stop conditions |
| Output | promoted/retired hypotheses in canonical evidence | citation-auditable markdown report |
| Use when | open exploration inside the LoopX control plane | a single user question needing an auditable, source-cited answer |

Choose one per question; they do not share state and neither can close the
other's work.

## Boundary with explore

The `explore` capability owns the goal-scoped, public-safe, cross-session
knowledge topology (questions, findings, evidence relations). deep-research owns
a private, session-scoped transactional ledger:

- deep-research keeps raw source locators (URLs or local paths that may not be
safe to publish), claim lineage, precise contradiction pairs with sides-with
adjudication, stop/close decisions, and the citation report. It works
standalone — with no LoopX goal and no explore opt-in.
- explore keeps the canonical, reusable projection. Any future bridge is a
one-way, idempotent, public-safe derivation: explore receives sanitized,
opaque evidence refs only; it never reads the raw deep-research ledger or
report, never dual-writes research state, and a graph node resolving never
closes (or reopens) a research run.

In short: deep-research is the only writer of `.loopx/deepresearch/`, and
public surfaces only ever see derived, reconstructable projections.

One ledger invariant spans every transition that adjudicates a contradiction:
no resolution — inside `resolve-question` or standalone
`resolve-contradiction` — may overrule a claim that an already-answered
question cites as evidence. The ledger never silently invalidates a recorded
answer; late-arriving counterevidence that wins forces an explicit decision
(side with the cited claim, or close the run and revisit the question in a new
run) instead of leaving the report internally inconsistent.

## Lifecycle

`start` opens a run; `close` is the explicit terminal transition; the next
`start` archives the closed run (state + report) under
`.loopx/deepresearch/archive/<closed-at>/` and begins fresh. `start --new-run`
auto-closes only a run whose stop conditions already fired; an active run
always requires an explicit `close` first. State files are only ever rotated by
these typed transitions — never edited by hand.

## Layout

- Domain state machine and report (capability owner):
`loopx/capabilities/deep_research/runtime.py`
- CLI adapter: `loopx/cli_commands/deepresearch.py` (`loopx deepresearch …`)
- Host entry: the `/loopx-deepresearch` skill facade installed by
`loopx slash-commands --install`

Recorded claims carry caller-declared provenance: `--tool` records the tool the
caller says produced the evidence. The CLI records that provenance; it does not
attest that the tool actually ran — execution receipts belong to a future
host/harness bridge, not this ledger.
5 changes: 5 additions & 0 deletions loopx/capabilities/deep_research/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from __future__ import annotations

from .catalog_entry import DEEP_RESEARCH_CATALOG_ENTRY

__all__ = ["DEEP_RESEARCH_CATALOG_ENTRY"]
125 changes: 125 additions & 0 deletions loopx/capabilities/deep_research/catalog_entry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
from __future__ import annotations

from typing import Any


DEEP_RESEARCH_CATALOG_ENTRY: dict[str, Any] = {
"id": "deep-research",
"origin": "builtin",
"visibility": "public",
"provider_id": "loopx-core",
"documentation": {
"source_root": "loopx/capabilities/deep_research",
"site_root": "capabilities/deep-research",
"canonical": "README.md",
},
"title": "Evidence-ledger deep research loop",
"status": "experimental",
"real_world_anchor": (
"one user question driven by packet-cut expeditions over question, source, "
"claim, and contradiction ledgers, ending in a citation-auditable report"
),
"user_value": (
"Run a bounded, auditable research session per project: claims must come from "
"recorded sources, answers must cite recorded claims, and closeout is blocked "
"while any contradiction stays unresolved."
),
"entry_command": "loopx deepresearch start --question <text>",
"commands": [
{
"command": "loopx deepresearch start --question <text> [--max-sources N] [--new-run]",
"purpose": "Open a research run; archives the previous closed/stopped run instead of editing state files by hand.",
"write_boundary": ".loopx/deepresearch/ state and report files only",
},
{
"command": "loopx deepresearch status",
"purpose": "Emit the expedition packet: contract, ledgers, next expedition, stop conditions, evidence commands.",
"write_boundary": "read-only projection over the research ledger",
},
{
"command": "loopx deepresearch add-source --url-or-path <ref> --tool <tool> --claims-json <json>",
"purpose": "Record one consulted source and the claims extracted from it.",
"write_boundary": "one ledger transaction under the project research lock",

Check failure on line 42 in loopx/capabilities/deep_research/catalog_entry.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "one ledger transaction under the project research lock" 4 times.

See more on https://sonarcloud.io/project/issues?id=huangruiteng_loopx&issues=AaAqvgKGObA3UIzKZdlP&open=AaAqvgKGObA3UIzKZdlP&pullRequest=3453
},
{
"command": "loopx deepresearch add-subquestion --text <text> --from-claim <claim-id>",
"purpose": "Open a subquestion that derives from a recorded claim.",
"write_boundary": "one ledger transaction under the project research lock",
},
{
"command": "loopx deepresearch resolve-question --question-id <id> --answer <text> --evidence-claims <ids>",
"purpose": "Answer a recorded question citing recorded evidence claims; contradictions touching the evidence must be resolved in the same transition.",
"write_boundary": "one ledger transaction under the project research lock",
},
{
"command": "loopx deepresearch resolve-contradiction --contradiction-id <id> --sides-with <claim-id> --rationale <text>",
"purpose": "Close one open contradiction standalone with an explicit sides-with rationale.",
"write_boundary": "one ledger transaction under the project research lock",
},
{
"command": "loopx deepresearch close [--note <text>]",
"purpose": "Terminal transition marking this run closed and its ledger immutable; the next start archives it.",
"write_boundary": "marks the run closed; the next start performs the archive rotation",
},
{
"command": "loopx deepresearch report",
"purpose": "Render the citation-auditable markdown report from the ledgers.",
"write_boundary": "writes .loopx/deepresearch/report.md only",
},
],
"implemented_protocols": [
{
"schema_version": "loopx_deepresearch_state_v0",
"module": "loopx.capabilities.deep_research.runtime",
"doc": "loopx/capabilities/deep_research/README.md",

Check failure on line 74 in loopx/capabilities/deep_research/catalog_entry.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "loopx/capabilities/deep_research/README.md" 3 times.

See more on https://sonarcloud.io/project/issues?id=huangruiteng_loopx&issues=AaAqvgKGObA3UIzKZdlO&open=AaAqvgKGObA3UIzKZdlO&pullRequest=3453
},
{
"schema_version": "loopx_deepresearch_packet_v0",
"module": "loopx.capabilities.deep_research.runtime",
"doc": "loopx/capabilities/deep_research/README.md",
},
],
"smokes": [
"python3 -m pytest tests/test_deepresearch_command.py -q",
],
"docs": [
"loopx/capabilities/deep_research/README.md",
],
"boundaries": [
(
"Distinct from the auto-research capability: auto-research launches "
"role-scoped workers inside a LoopX goal for open exploration; "
"deep-research is a single-session, user-facing evidence ledger whose "
"truth is the .loopx/deepresearch state, not goal todos."
),
(
"Distinct from the explore capability: explore owns the goal-scoped, "
"public-safe, cross-session question/finding topology; deep-research "
"owns this private, session-scoped ledger — raw source locators, claim "
"lineage, contradiction adjudication, stop/close/report. Any future "
"bridge is a one-way, idempotent, public-safe projection: explore "
"never reads the raw ledger, never dual-writes, and graph resolution "
"never closes a research run."
),
(
"The packet owns research progression and stop decisions; the model "
"executes bounded expeditions and records typed ledger transitions only. "
"No contradiction adjudication — in-call or standalone — may overrule "
"a claim an already-answered question cites as evidence."
),
(
"Claims carry caller-declared provenance: --tool records the tool the "
"caller says produced the evidence, and the CLI records it without "
"attesting execution; reports must keep every citation resolvable to a "
"recorded source."
),
(
"One active run per project; finished or closed runs are archived under "
".loopx/deepresearch/archive/ by typed transitions, never by hand edits."
),
],
"next_real_step": (
"Keep the deep-research entrypoint as the single evidence-ledger authority and "
"reuse LoopX goal/todo/quota projections rather than adding a second scheduler."
),
}
Loading