> ## Documentation Index
> Fetch the complete documentation index at: https://docs.usesynth.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Research Factories

> Group repeated Managed Research efforts, schedule follow-up runs, and inspect factory status.

Research Factories group repeated Managed Research work under one operating
unit. A Factory owns policies and status; each Effort describes one recurring or
long-lived line of work inside that Factory.

Use Factories when a team wants repeated runs around the same goal: benchmark
improvement, dataset generation, weekly repo maintenance, or an open research
campaign. Use a one-off [run](/managed-research/runs-and-evidence) when the work
is isolated.

Use [Synth Tag](/managed-research/tag) when you want a lighter delegate surface:
one SDK or MCP call starts a session, `send_message` steers the same run, and
`get_session` returns the terminal receipt. Tag is the task mode; Factory is the
program mode.

## Concepts

| Object  | Role                                                                                                                                                             |
| ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Factory | The parent operating unit. It has a name, lifecycle status, budget policy, cap policy, publication policy, and metadata.                                         |
| Effort  | One line of work under a Factory and project. It carries a hypothesis or topic, status, optional recurrence policy, latest run/report links, and decision notes. |
| Run     | One execution launched from an Effort. Runs still use the normal Managed Research run lifecycle and evidence model.                                              |

## Python SDK

```python theme={null}
from synth_ai import SynthClient

client = SynthClient()
control = client.research.control()

project = control.projects.create_runnable({"name": "DungeonGrid policy work"})

factory = control.factories.create(
    {
        "name": "DungeonGrid optimizer factory",
        "description": "Repeated benchmark-improvement runs with reviewable evidence.",
        "cap_policy": {"max_active_runs": 1},
        "publication_policy": {"visibility": "private"},
    }
)

effort = control.efforts.create(
    {
        "factory_id": factory.factory_id,
        "project_id": project.project_id,
        "name": "Improve heldout score",
        "hypothesis_or_topic": "Search for policy changes that improve heldout DungeonGrid score.",
        "effort_type": "optimizer",
    }
)

run = control.efforts.launch(
    effort.effort_id,
    "Run the configured optimizer lane, require heldout scoring, and publish a final report.",
    host_kind="daytona",
    work_mode="directed_effort",
    providers=[{"provider": "openrouter"}],
    runbook="lite",
)

result = run.wait(timeout=60 * 60, poll_interval=15)
print(result.public_state.value)

status = control.factories.status(factory.factory_id)
print(status.efforts_by_status)
```

## MCP

Ask your MCP client to create and inspect a Factory:

```text theme={null}
Create a Research Factory named "DungeonGrid optimizer factory", create an optimizer Effort under my project, then show the Factory status projection.
```

Useful tools:

* `smr_create_factory`
* `smr_list_factories`
* `smr_get_factory`
* `smr_patch_factory`
* `smr_create_effort`
* `smr_list_factory_efforts`
* `smr_get_effort`
* `smr_get_factory_status`
* `smr_wake_due_factory_efforts`

## Scheduling

An Effort can wait for a later wake time or recurrence policy:

```python theme={null}
control.efforts.schedule(
    effort.effort_id,
    next_wake_at="2026-06-16T13:00:00Z",
    recurrence_policy={"cadence": "daily", "max_active_runs": 1},
    launch_request={
        "host_kind": "daytona",
        "work_mode": "directed_effort",
        "providers": [{"provider": "openrouter"}],
        "runbook": "lite",
    },
)

due = control.factories.wake_due(factory.factory_id, dry_run=True)
print(due.evaluated_count)
```

Use `dry_run=True` to inspect which Efforts would launch before starting runs.
Use `allow_overlap=False` unless overlapping runs are intentional.

## Status and decisions

Factory status is the read model for operators. It includes project summaries,
Efforts by status, latest runs, latest reports and work products, open decisions,
paused items, wake metadata, publication summaries, and cost summaries.

When an Effort needs human input, mark it explicitly:

```python theme={null}
control.efforts.mark_ready_for_review(
    effort.effort_id,
    note="Heldout score improved; review report before publishing.",
)

open_decisions = control.factories.list_open_decisions(factory.factory_id)
```

Resolve decisions only after the operator has acted:

```python theme={null}
control.efforts.resolve_decision(
    effort.effort_id,
    note="Approved next run with the same heldout gate.",
)
```

## Privacy

Factory and Effort details are private Managed Research control-plane context by
default. Public Open Research pages should show only intentionally published run,
report, or work-product summaries.

## Next

* [Managed Research Quickstart](/managed-research/quickstart)
* [Runs and Evidence](/managed-research/runs-and-evidence)
* [Usage and Budgets](/managed-research/usage)
