# Priority Forge Task Tracking Protocol (Codex)

You are connected to the Priority Forge MCP server for task tracking. The MCP server is configured in `~/.codex/config.toml` under `[mcp_servers.priority-forge]` and reaches the local backend at `http://127.0.0.1:3456/mcp` over HTTP-streamable MCP transport.

This file adapts the canonical task-tracking protocol in `AGENT_RULES.md` for Codex's MCP tool naming and configuration model.

## INFRASTRUCTURE (know this before anything else)

| Service | Port | Check |
|---------|------|-------|
| Backend (MCP + REST) | **3456** | `curl http://localhost:3456/health` |
| Frontend dashboard | **5173** | http://localhost:5173 |

Both run as persistent background services (systemd on Linux, launchd on macOS) that auto-start on boot.
If the backend is down: `systemctl --user restart priority-forge-backend` (Linux) or `launchctl kickstart gui/$(id -u)/com.priority-forge.backend` (macOS).
Run `npm run verify` from the priority-forge directory for a full health check.

### If you need to start the stack yourself (onboarding)

Use, in order of preference:

1. **Persistent services** — `./setup.sh install-systemd` (Linux) or `./setup.sh install` and accept the launchd prompt (macOS). Services live independently of any shell and auto-start on boot.
2. **Detached dev mode** — `./setup.sh start`. Uses `nohup` so the processes survive the terminal closing.

**Never** run `npm run dev` directly in an editor's integrated terminal (VS Code, Cursor, etc.) for onboarding. The backend becomes a child of the editor's shell and dies with SIGHUP the moment the editor closes or crashes. If you find yourself about to run `npm run dev` as part of onboarding a user, stop and use `./setup.sh start` instead.

## MCP-FIRST, REST FALLBACK (do not retry MCP if it fails)

The Priority Forge MCP tools are exposed through your `priority-forge` MCP connection. Tool names in Codex are unprefixed:

| Operation | MCP tool |
|-----------|----------|
| Get top task | `get_top_priority` |
| List queue | `get_priorities` |
| Create task | `create_task` |
| Update task | `update_task` |
| Complete task | `complete_task` |
| Log decision | `log_decision` |
| Suggest tasks | `suggest_tasks` |
| Log context switch | `log_context_switch` |
| ML summary | `get_ml_summary` |

If MCP tools are unavailable or hang mid-session, switch to REST immediately (do not retry MCP):

```bash
curl -X POST http://127.0.0.1:3456/tasks \
  -H "Content-Type: application/json" \
  -d '{"task":"title","priority":"P1","project":"project-name","effort":"medium"}'
```

For task updates, use PUT (not PATCH):

```bash
curl -X PUT http://127.0.0.1:3456/tasks/TASK-ID \
  -H "Content-Type: application/json" \
  -d '{"status":"in_progress"}'
```

If MCP is missing entirely: check (1) backend is running (`curl http://127.0.0.1:3456/health`), (2) `~/.codex/config.toml` contains `[mcp_servers.priority-forge]`. If not, run `npm run setup:mcp` from the priority-forge directory and choose `[4] OpenAI Codex CLI`, then restart Codex.

## MANDATORY: ON EVERY CONVERSATION START

Before responding to the user's first message, you MUST:

1. Call `get_top_priority`
2. If the user request differs from top priority, call `log_context_switch`

## DURING CONVERSATION

- Proactively identify tasks from what the user says; do not wait for "add a task"
- When the user mentions bugs, features, TODOs, blockers, call `create_task`
- When uncertain, call `suggest_tasks` to extract candidates

## FEATURE EXTRACTION PROTOCOL

When calling `create_task`, you are responsible for turning the user's prose into structured feature values. The scoring math downstream is fully deterministic — the only thing you decide is **what inputs to hand it**. Full contract: [docs/FEATURE_EXTRACTION.md](docs/FEATURE_EXTRACTION.md).

**Always set:**

| Field      | How to extract                                                                                                  |
|------------|-----------------------------------------------------------------------------------------------------------------|
| `task`     | Short imperative title, lead with a verb.                                                                       |
| `project`  | Match against `list_projects`. Use `"general"` only if there's no reasonable match and no new project warranted. |
| `priority` | P0 = outage/data loss. P1 = "can't ship until", named near-term deadline. P2 = default. P3 = "nice to have".    |
| `effort`   | low = <1h/single file. medium = 1-4h/a few files. high = day+/multi-system. Never omit — default `medium`.      |

**Set only when prose supports a confident value, else omit:**

| Field                | Signal                                                                                                     |
|----------------------|------------------------------------------------------------------------------------------------------------|
| `blockingCount`      | User names downstream tasks that depend on this one. **Prefer** setting `dependsOn` on those downstream tasks pointing at this one — the server recomputes `blockingCount` from the graph. |
| `crossProjectImpact` | `1` if completing this moves work forward in ≥2 distinct projects, else `0`.                               |
| `timeSensitivity`    | 10 = overdue / blocks something due now. 7-9 = deadline within a week. 4-6 = within a month. 1-3 = "eventually". 0 = no deadline. |
| `dependsOn`          | User explicitly names an upstream ("after X lands"). Resolve to a real task ID via `get_priorities` — never invent IDs. |

**Never set:** `dependencyDepth`, `priorityScore`, `queuePosition`. The server computes these from the graph.

**When in doubt:** omit optional fields rather than guess. Omission preserves the training signal; a wrong guess degrades it unless the user happens to correct it.

**Example — translating prose to features:**

> User: "the login button is broken on mobile, we can't ship the v2 rollout until that's fixed"

- `task`: "Fix login button on mobile"
- `project`: (match against `list_projects`)
- `priority`: `P1` ("can't ship until")
- `effort`: `medium`
- `timeSensitivity`: `7` ("can't ship" implies near-term)
- If "v2 rollout" is an existing task, add `dependsOn: ["<v2-rollout-id>"]` to that task pointing at the new one.

## CRITICAL: MARK TASKS "IN PROGRESS" WHEN STARTING WORK

Before you begin actual work on a task, call:

```text
update_task(id: "TASK-ID", status: "in_progress")
```

This captures the `startedAt` timestamp and logs the task selection event needed for later ML learning.

## ON COMPLETING WORK

Always call `complete_task` with:

- `outcome: "completed"`
- `outcome: "deferred"`
- `outcome: "cancelled"`

## DECISION LOGGING PROTOCOL (V4.2)

The ML pipeline tracks decisions to learn why tasks are skipped, deprioritized, or deferred.

Auto-logged:

- Skip decisions created when `update_task(status: "in_progress")` selects a non-top-ranked task

Agent must ask the user for reasoning, then call `log_decision`, when:

| Trigger | When | decisionType |
|---------|------|--------------|
| Priority downgrade | `update_task` lowers priority | `prioritization` |
| Deferral | `complete_task(outcome: "deferred")` | `defer` |
| Cancellation | `complete_task(outcome: "cancelled")` | `defer` |

## END OF CONVERSATION

- Review the conversation for untracked work items
- Update any task still marked `in_progress`
- Suggest tasks for anything discussed but not tracked

## V3+: ML TRAINING DATA COLLECTION

When starting work on any task, call `update_task(status: "in_progress")`. This automatically:

- Captures `startedAt`
- Logs a task selection event
- Logs a skip decision if the task was not top-ranked

Fill in `effort` on task creation:

```text
create_task(task: "...", priority: "P1", effort: "medium", ...)
```

Data collection targets:

| Metric | Target | Why |
|--------|--------|-----|
| Completions | 10+ | Outcome signal |
| Selections | 20+ | User preference signal |
| Priority changes | 5+ | Override/correction signal |
| Completions with `actualWorkTime` | 10+ | Work duration learning |
| Decisions | 10+ | Decision reasoning signal |

## Cross-workspace queue queries (never single-workspace)

When the user asks "what's next" / "what's in the forge" / "what's my top priority" / any queue-shaped question, enumerate **all non-dormant workspaces** and collate, not just the currently-active one. An active P0 in another workspace is hidden behind whatever workspace happens to be switched-to.

**Dormancy rule** (centralised in the backend; do not invent your own): a workspace is **active** iff EITHER (a) any task created/updated within the last `interactionWindowDays` (default 14), OR (b) at least one open task with a deadline within the next `futureDeadlineHorizonDays` (default 60). Otherwise dormant.

**How to query**: `GET /workspaces?include_freshness=true` returns each workspace with a `freshness` block (`active`, `dormant`, `activeReason`, `lastTouchedAt`, `daysSinceTouch`, `openTaskCount`, `futureDeadlineCount`, `freshnessScore`). Filter on `freshness.dormant === false`, iterate each active workspace's tasks via `POST /workspaces/:id/switch` + `GET /tasks` (or the MCP equivalents), and restore the user's original workspace when done.

## Updating workspace metadata (V4.3)

If a workspace's `name` or `description` becomes stale (e.g., a crate gets renamed, a scope pivots, an identifier deprecates), update it in place. Workspace IDs are immutable; tasks and ML data stay attached.

- **MCP**: `update_workspace(workspaceId, name?, description?)`
- **REST**: `PATCH /workspaces/:id` with `{name?, description?}`

Do NOT delete + recreate to fix metadata — that detaches ML records from the original `workspaceId` and corrupts cross-workspace queries.

## Reference Files

If you need deeper context:

- `AGENT_RULES.md` — canonical task-tracking protocol
- `CLAUDE.md` — Claude-specific setup and MCP notes
- `docs/mcp-hang-rca.md` — HTTP migration background and rationale
- `docs/sqlite-migration.md` — deferred storage design
- `docs/DEVELOPMENT_ROADMAP.md` — future data and ML roadmap
- `scripts/configure-mcp.ts` — MCP and agent-rules installer
- `src/storage/jsonStorage.ts` and `src/types/schema.ts` — storage and data shapes
