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

# Research modes

> The three historical local modes (basic, mvp1, relational) that run the pre-product research stack — when to reach for one instead of the product Compose stack.

<Warning>
  None of the modes on this page are the deployed product. Causeloop in production, and its local parity environment, is the container stack documented in [Local product stack](/onboarding/local-product-stack) (`infra/docker-compose.product.yml`, Next.js console on `13000`, product API on `18000`). The modes below run `services/api/main.py` — the legacy research application with roughly 258 endpoints — and the Vite-based workbench in `apps/web`. Reach for them only when the work is actually inside that legacy surface: the MVP1 strict-analysis pipeline, the relational clustering pipeline, or the workbench UI that renders them.
</Warning>

## Why this surface still exists

`services/api/main.py` predates the tenant-scoped product API in `services/api/product_app.py`. It hosts the local research workbench, file-backed client onboarding, and the process-global `/mvp1` routes that the current product does not need. The product boundary (`services/api/product_app.py`) explicitly does not import it — the module's own docstring calls out that importing the legacy app "creates stores and exposes operations the customer/staff product does not need." Treat everything on this page as a research and pipeline-development environment, not a place to prototype product features.

```mermaid theme={null}
flowchart LR
    subgraph Legacy research app
        M["services/api/main.py<br/>~258 endpoints"]
        W["apps/web<br/>Vite workbench :5173"]
    end
    subgraph Product boundary
        P["services/api/product_app.py<br/>44 allowlisted operations"]
        F["frontend console<br/>Next.js :13000 (Compose) / :3000 (dev)"]
    end
    M -->|"legacy /mvp1 routes,<br/>file-backed clients"| W
    P --> F
    M -.->|explicitly not imported by| P
```

## Choosing a mode

Pick the smallest mode that covers the work at hand:

| Mode         | What it runs                                                 | Docker   | OpenAI key | Use it for                                                                                               |
| ------------ | ------------------------------------------------------------ | -------- | ---------- | -------------------------------------------------------------------------------------------------------- |
| `basic`      | FastAPI + React, built-in read-only MVP1 data                | No       | No         | UI/API changes to the workbench that don't need a fresh analysis run                                     |
| `mvp1`       | Basic mode plus a new strict 250-issue analysis              | No       | Yes        | Exercising the strict fishbone/severity pipeline end to end                                              |
| `relational` | PostgreSQL-backed analysis through a reviewed Excel workbook | Postgres | Yes        | Work on the relational clustering pipeline, the workbook exporter, or Alembic migrations for that schema |

All three assume Python 3.12+, [`uv`](https://docs.astral.sh/uv/getting-started/installation/), and Node.js 20+/npm 10+ are installed. From the repository root, install locked dependencies once:

```bash theme={null}
uv sync --dev
npm ci --prefix apps/web
uv run python scripts/check_local_setup.py --mode basic
```

`check_local_setup.py` is local and non-mutating — it never calls OpenAI, never contacts a remote service, and never prints a secret value. Run it for the mode you're about to use before claiming that mode is ready.

## Mode 1: basic UI and API

No Docker, no API key. Start both processes together:

```bash theme={null}
uv run python scripts/run_local.py
```

or in two terminals:

```bash theme={null}
uv run uvicorn services.api.main:app --host 127.0.0.1 --port 8000 --reload
npm --prefix apps/web run dev -- --host 127.0.0.1
```

Open:

* Web UI: `http://127.0.0.1:5173`
* API docs: `http://127.0.0.1:8000/docs`
* API schema: `http://127.0.0.1:8000/openapi.json`

The tracked synthetic banking dataset (`docs/issue-set-2-records.json`, 250 records) is available in this mode. A fresh clone has no generated strict `latest` report, so report endpoints correctly return `404` until an analysis is run — that is not a setup failure (see [Troubleshooting](/onboarding/troubleshooting)).

<Note>
  `scripts/run_local.py` builds its child environment by loading `.env` then `.env.local`, but the real process environment takes precedence over both — a variable already set in your shell overrides whatever either dotenv file has. It then launches uvicorn on `services.api.main:app` and `npm --prefix apps/web run dev` as child processes. It requires `npm` to be on `PATH`.
</Note>

## Mode 2: strict MVP1 analysis

<Steps>
  <Step title="Create the local env file">
    ```bash theme={null}
    cp .env.example .env.local
    ```

    <Warning>
      At the time of writing, `.env.example` is not present in a fresh clone of this repository, even though `scripts/check_local_setup.py` and this historical instruction both reference it. If the copy fails, create `.env.local` directly and set `OPENAI_API_KEY=` in it through a secret-aware local workflow — never paste the key into chat, logs, commits, or shell history. The doctor only checks that the variable is non-empty; it never prints the value.
    </Warning>
  </Step>

  <Step title="Run the doctor for mvp1">
    ```bash theme={null}
    uv run python scripts/check_local_setup.py --mode mvp1
    ```
  </Step>

  <Step title="Start the stack">
    ```bash theme={null}
    uv run python scripts/run_local.py
    ```
  </Step>

  <Step title="Approve and run the strict analysis in the UI">
    In the web UI, choose the Operator or Developer role, enable the two separate OpenAI transfer approvals, and start the strict run.
  </Step>
</Steps>

Starting the strict run sends governed, de-identified issue fields to OpenAI and may incur API cost. A code agent must never perform that live action without the user's explicit, informed approval — this is one of the safety boundaries in `AGENTS.md`.

The strict pipeline persists its reusable analysis state locally; PostgreSQL is not required for `mvp1` mode. The UI/API shape for this mode is specified in `docs/MVP1_API_CONTRACT.md`.

## Mode 3: relational DB-to-Excel

This path clusters the same tracked `docs/issue-set-2-records.json` dataset through PostgreSQL and a reviewed Excel workbook. It does not depend on a workbook in `~/Downloads` or any machine-specific path.

<Steps>
  <Step title="Prepare local configuration and start Postgres">
    ```bash theme={null}
    cp .env.example .env.local
    cp infra/causeloop.local.env.example infra/causeloop.local.env
    docker compose -f infra/docker-compose.yml up -d postgres
    set -a
    source .env.local
    source infra/causeloop.local.env
    set +a
    uv run alembic upgrade head
    ```

    Both `.env.local` and `infra/causeloop.local.env` are git-ignored. Never read, print, log, or commit either file — that rule is explicit in `AGENTS.md`.
  </Step>

  <Step title="Point Node at the bundled @oai/artifact-tool package">
    The reviewed workbook builder (`workers/artifacts/build_mvp1_clustering_workbook.mjs`) imports `@oai/artifact-tool`. If you are a Codex agent, call the workspace-dependency discovery tool and export its bundled Node executable:

    ```bash theme={null}
    export CAUSELOOP_NODE_PATH=/absolute/path/from/workspace-dependency-discovery/node
    ```

    `scripts/check_local_setup.py` resolves this as either the configured path or whatever `node` is on `PATH`, then looks for `@oai/artifact-tool` next to `workers/artifacts/node_modules` or next to that Node binary's own `node_modules`. Do not point it at an arbitrary Node install that doesn't carry the package — the check will fail with "Node runtime does not expose @oai/artifact-tool."
  </Step>

  <Step title="Verify prerequisites without making an API call">
    ```bash theme={null}
    uv run python scripts/check_local_setup.py --mode relational
    ```
  </Step>

  <Step title="Run the pipeline (only after explicit user approval of cost)">
    ```bash theme={null}
    uv run python workers/run_banking_relational_e2e.py \
      --approve-openai-llm \
      --openai-authorization \
      "I explicitly authorize sending these 250 de-identified banking issue descriptions and derived severity context to the OpenAI API."
    ```

    The runner enforces this exact authorization string in addition to the approval flag — it is not a formality.
  </Step>
</Steps>

Outputs are written under the ignored `outputs/mvp1/` directory and must never be committed. Validated severity, embedding, and cluster-label results are content-addressed in PostgreSQL: an unchanged rerun reuses them and should make zero new OpenAI calls. The full database and workbook contract is in `docs/MVP1_RELATIONAL_PIPELINE.md`.

To bring up every optional local infrastructure service instead of Postgres alone:

```bash theme={null}
docker compose -f infra/docker-compose.yml up -d
```

<Tip>
  If a sandbox redirects user caches and `uv` commands fail to write to their default cache directory, prefix them with `UV_CACHE_DIR=/tmp/uv-cache`.
</Tip>

## Related

* [Local product stack](/onboarding/local-product-stack)
* [Testing and verification](/onboarding/testing)
* [Troubleshooting](/onboarding/troubleshooting)
* [MVP1 pipeline](/research/mvp1-pipeline)
