decision_cockpit 0.1.0

Layer — product decision memory with MCP tools and an embedded review dashboard
Documentation
# Layer

**A local product-decision memory system for AI coding agents.**

[GitHub](https://github.com/Pulko/layer) · `cargo install decision_cockpit --bin mcp`

Layer stores documents, decisions, assumptions, evidence, drift signals, and memos. An MCP-capable agent proposes structured **candidates** and **signals**; you review and accept them in a built-in dashboard.

> The app is not the agent. It is the agent's product-memory operating system.

```
  Cursor / MCP client
    ┌─────────┐     ┌──────────────────┐
    │   mcp   │────▶│ Postgres (Docker)│
    │ binary  │     └──────────────────┘
    └────┬────┘
         │  http://localhost:3000
   embedded dashboard
```

One self-contained binary includes the UI, API, MCP server, and Docker Compose definition. No separate frontend deploy. No env-file required.

---

## Install

**Requirements:** [Docker Desktop](https://www.docker.com/products/docker-desktop/) (running).

```bash
cargo install decision_cockpit --bin mcp
```

The crates.io package is `decision_cockpit`; the installed binary is `mcp` (typically `~/.cargo/bin/mcp`).

### Connect to Cursor

Add to `.cursor/mcp.json` (project or global):

```json
{
  "mcpServers": {
    "layer": {
      "command": "/Users/you/.cargo/bin/mcp"
    }
  }
}
```

Use the full path to your `mcp` binary. Then enable the server in **Cursor Settings → MCP**.

On first connect the binary will:

1. Start Postgres via embedded Docker Compose (or reuse an existing container)
2. Run database migrations
3. Serve the review dashboard at **http://localhost:3000**

Ask the agent to **open the dashboard** (`open_dashboard` tool), or visit that URL yourself.

---

## Workflow

| Step | Who | What |
|------|-----|------|
| 1. Ingest context | You or agent | Paste meeting notes, specs, feedback as **documents** |
| 2. Extract | Agent (MCP) | Creates **extraction candidates** — decisions, assumptions, actions, evidence |
| 3. Review | You (dashboard) | Accept or reject candidates → **canonical graph** |
| 4. Detect drift | Agent (MCP) | Compares new context to existing decisions → **drift signals** |
| 5. Respond | You + agent | Acknowledge drift, edit **memo** drafts, mark memos final |

**Key rule:** the agent never writes canonical truth directly. It creates candidates, drift signals, and memo drafts. You confirm what becomes real.

### Linking the decision graph

When extracting assumptions, actions, or evidence, include `relates_to_decision_id` in the candidate payload so accepting auto-links the entity to a decision. Or call `create_relation` / use the dashboard linker afterward.

### Document status

Documents move through `new` → `extracted` → `archived`. Agents can fetch only `new` documents to avoid re-mining context that was already processed.

---

## MCP tools

| Tool | Purpose |
|------|---------|
| `open_dashboard` | Open the review UI in your browser |
| `create_document` | Store product context |
| `get_document` | Fetch a document with full text |
| `list_recent_documents` | List documents (`status`: `new`, `extracted`, `archived`) |
| `create_extraction_candidate` | Propose a decision, assumption, action, evidence, etc. |
| `list_decisions` | List canonical decisions |
| `get_decision_context` | Decision + linked assumptions, actions, evidence, drift |
| `create_relation` | Link two entities in the graph |
| `create_drift_signal` | Flag when new context may challenge a decision |
| `list_open_drift_signals` | List unreviewed drift |
| `create_memo` | Save a memo (`draft` or `final`) |
| `update_memo` | Edit a memo or mark it final |
| `list_memos` / `get_memo` | Read existing memos for context |

Enum values are passed as strings (e.g. `candidate_type`: `decision`, `assumption`, `action`, `evidence`).

### Example agent prompts

**Extraction**

```
Read documents with status "new". For each item found, create extraction candidates.
Do not create canonical decisions directly. For assumptions/actions/evidence, set
relates_to_decision_id in the payload when you know which decision they support.
```

**Drift**

```
Read the latest document, list decisions, inspect their context, and create drift
signals where new evidence contradicts or challenges existing decisions.
```

**Memo**

```
Read relevant memos and decision context, then create or update a memo draft
addressing the acknowledged drift signal. Mark final when complete.
```

---

## Configuration

All settings are optional — defaults work for local development.

| Variable | Default | Description |
|----------|---------|-------------|
| `DATABASE_URL` | `postgres://cockpit:cockpit@localhost:5432/decision_cockpit` | Postgres connection |
| `API_BIND_ADDR` | `0.0.0.0:3000` | HTTP bind address |
| `COCKPIT_AUTO_DOCKER` | `true` | Start Postgres via Docker on launch |
| `COCKPIT_SERVE_DASHBOARD` | `true` | Host API + embedded UI |
| `COCKPIT_FRONTEND_DIST` | *(embedded)* | Dev override: serve UI from disk |
| `CORS_ALLOWED_ORIGINS` | `http://localhost:5173` | CORS for Vite dev mode |

Copy `.env.example` to `.env` when developing from source.

---

## HTTP API

The dashboard and `api` binary share the same REST API at `http://localhost:3000`.

<details>
<summary>Endpoints</summary>

| Method | Path | Description |
|--------|------|-------------|
| GET | `/health` | Health check |
| POST / GET | `/documents` | Create / list documents |
| GET | `/documents/:id` | Get document |
| POST | `/documents/:id/status` | Set document status |
| GET | `/documents/:id/candidates` | Candidates for a document |
| GET | `/candidates` | List candidates |
| POST | `/candidates/:id/accept` | Accept into canonical graph |
| POST | `/candidates/:id/reject` | Reject candidate |
| GET | `/decisions` | List decisions |
| GET | `/decisions/:id` | Decision + linked context |
| GET | `/assumptions`, `/actions`, `/evidence` | Entity lists (for linking) |
| POST | `/relations` | Create a graph relation |
| GET | `/graph` | Full graph nodes + edges |
| GET | `/drift-signals` | List drift signals |
| POST | `/drift-signals/:id/accept` | Acknowledge drift |
| POST | `/drift-signals/:id/dismiss` | Dismiss drift |
| GET / POST | `/memos` | List / create memos |
| GET / PUT | `/memos/:id` | Get / update memo |
| POST | `/memos/from-drift/:id` | Create memo draft from drift |

</details>

```bash
curl -X POST http://localhost:3000/documents \
  -H "content-type: application/json" \
  -d '{"title":"Sprint notes","source_type":"meeting_note","raw_text":"We agreed to ship MCP-first distribution."}'
```

---

## Build from source

For contributors or pre-release builds:

```bash
git clone https://github.com/Pulko/layer
cd layer

# Dashboard must be built before cargo build (embedded at compile time)
cd frontend && npm install && npm run build && cd ..

cargo build --release --bin mcp
```

Other binaries:

```bash
cargo run --bin api      # HTTP API + dashboard only
cargo run --bin mcp      # MCP + dashboard (stdio)
```

**Dev tips**

- Frontend hot-reload: `cd frontend && npm run dev` with `COCKPIT_FRONTEND_DIST=./frontend/dist`
- Skip Docker bootstrap: `COCKPIT_AUTO_DOCKER=false` (bring your own Postgres)

---

## Architecture

```
src/
  assets.rs       # Dashboard embedded via rust-embed
  bootstrap.rs    # Docker Compose + DB readiness
  domain/         # Entities and enums
  services/       # Business logic (shared by HTTP + MCP)
  http/           # Axum REST API
  mcp/            # MCP tool server (rmcp)
  bin/mcp.rs      # Primary entrypoint
  bin/api.rs      # Standalone HTTP server
migrations/       # SQLx migrations
frontend/         # React dashboard (built into binary)
```

Both `mcp` and `api` link the same library crate. MCP is the intended runtime for agent-driven workflows.

---

## Security

Layer is designed for **local, single-user** use:

- Dashboard URL is `http://localhost:3000`; API binds to `0.0.0.0:3000` by default (set `API_BIND_ADDR` to restrict)
- Uses default Postgres credentials (`cockpit` / `cockpit`) suitable for local Docker only
- No authentication layer

Do not expose the HTTP port or database to untrusted networks without hardening.

---

## License

MIT — see [LICENSE](LICENSE).