# Unified extension work (`extension_work_items`)

**Purpose:** One operator-facing queue for *all* work that extends or repairs the system — delegations, repairs, and skill (self-modification) proposals — backed by **`extension_work_items`** in `~/furoshiki/memory/longmemory.sqlite`.

## Work tracks

All three tracks write to `extension_work_items` at creation and keep the phase in sync as work progresses:

| Track | Source table | Category | How it starts |
|-------|-------------|----------|---------------|
| **Delegation** | `delegation_jobs` | `code_change`, `research`, `create_script`, `system_check` | Telegram message classified as delegation intent; `classify_intent.create_delegation_job()` |
| **Skill creation (self-modification)** | `delegation_jobs` | `self_modification` | `furoshiki extensions skills create "…"`, Telegram routing, or any caller of `create_delegation_job(task_type='self_modification')` |
| **Repair** | `repair_jobs` | `repair` | `doctor.py` detects recurring error; inserts `repair_jobs` row and links `extension_work_items` |

## Relationship to core tables

- Each **delegation** or **skill creation** job creates or links an extension row (`delegation_jobs.extension_work_item_id`, `extension_work_items.delegation_job_id`).
- Each **repair** job links an extension row (`repair_jobs.extension_work_item_id`, `extension_work_items.repair_job_id`).
- **Phases** (`queued`, `running`, `proposed`, `live`, `failed`, `done`, `cancelled`) are stored on `extension_work_items` and synced from the underlying job status via `scripts/extension_work.py` (`sync_extension_from_delegation_job` / `sync_extension_from_repair_job`).
- **Dashboard `display_phase`**: same sync for most rows; for **`research`** / **`system_check`** with `delegation_jobs.status = proposed`, the dashboard shows **`complete`** (report artifact done — DB status stays `proposed`). See `delegation_display_phase_for_job()` in `scripts/dashboard_data.py` and **`docs/DELEGATION-AND-SKILLS.md`** (state diagram).
- **Dispatch track** (`delegation_track`): for linked delegation jobs, `classify_delegation_dispatch()` labels rows as `running` (inside the SLA), **`orphaned`** (stale `dispatched`), `failed`, `queued`, or **`idle`** (terminal non-failed: no dispatcher expected). The **Track** column shows this; **orphaned** also overrides the phase pill. Recovery: **`furoshiki extensions jobs redispatch <id>`** (API: `POST …/delegations/{id}/redispatch`, optional `{ "force": true }`).
- **Apply → same extension row:** `extensions jobs apply` continues the **same** `extension_work_items` id; it does not add a second queue line. `metadata_json` carries `delegation_chain` / `root_delegation_job_id` for the full spine.
- **`extension_events`** — append-only trail written when an extension row is created and whenever its **phase** changes (plus `error` / `warning` for failed or stale repair outcomes). Surfaced as the **Dashboard → Logs → extensions** tab (live refresh).

## Proposal files

When an outcome requires human review, a human-readable proposal document is written. The directory depends on the track:

| Track | Proposal location | Notes |
|-------|------------------|-------|
| **Delegation** | `~/.furoshiki/proposals/YYYY-MM-DD-HHmm-job<N>-<slug>.md` | Research findings or code proposals; apply with `furoshiki extensions jobs apply <id>` |
| **Skill creation** | `~/.furoshiki/proposals/YYYY-MM-DD-HHmm-<slug>.md` | Created by `self_modification_dispatcher.py` on APPROVED or REJECTED; install with `furoshiki extensions plugins apply <file>` |
| **Repair** | `~/.furoshiki/proposals/repair-YYYY-MM-DD-HHmm-job<id>-<slug>.md` | Created by `repair_dispatcher.py` when auto-fix is not safe (product mode: **never** edits shipped core; proposes upgrade or instance plugin fix). Optional dev path: `docs/repair-proposals/` under `SRC_DIR` when `FUROSHIKI_REPAIR_GIT_MODE=1`. |

The **Extensions → Proposal files** section of the dashboard (and `furoshiki extensions skills list`) shows all `*.md` files in **`~/.furoshiki/proposals/`** — this covers delegation, skill, and **repair** proposals. Paths are stored in `repair_jobs.proposal_path` and visible via **Dashboard → Repair jobs** and `furoshiki extensions view <id>` when linked.

## Telegram when extension work completes

When a **delegation** or **self-modification** worker **finalizes**, Python enqueues **`mind_queue`** (`delegation_proposed`, `delegation_fixed`, `delegation_failed`, or `skill_draft_ready`). Inner loops may enqueue **`skill_build_invite`** (offer to start a build). **`outreach_pulse.py`** (cron) invokes **`voice_dispatcher.py`**, which composes a **short in-character Telegram** from that row (not synchronous with the worker). See **`docs/DELEGATION-AND-SKILLS.md`** (“Telegram when a job finishes”) for kinds, gates, and orphaned-worker limits.

## Operator surfaces

| Surface | What it does |
|---------|----------------|
| **Dashboard → Extensions** | Unified page: work queue (`extension_work_items`), pending proposal files (`~/.furoshiki/proposals/`), installed plugins; job actions invoke `furoshiki extensions jobs apply|delete|redispatch` via the API. |
| **`furoshiki extensions`** | CLI hub: `list` / `view <id>` (queue), `extensions jobs …` (delegation lifecycle), `extensions skills …` (proposals + skill creation), `extensions plugins …` (installed plugins). |
| **Dashboard → Repair jobs** | Technical deep-dive for `repair_jobs`: dispatch counts, log excerpts, attempt counts, circuit breaker state. Complements the unified Extensions view. |
| **`furoshiki doctor`** | Repair pipeline; `extension_work_items` rows stay in sync when dispatchers finalize work. |
| **Dashboard → Inject / test** | Interactive smoke-test panel: send a message through the full classification pipeline (`POST /inject/conversation`) or create a delegation job directly (`POST /inject/delegation`). |
| **Dashboard → Logs → extensions** | Timestamped activity feed for all extension work (`extension_events`); lines prefixed with `[EXT#<id> <category>]` for end-to-end tracing. |

## Smoke-test endpoints (operator / CI use)

Both endpoints require the bearer token. No Telegram message is sent and no LLM reply is generated.

### `POST /inject/conversation`

Runs a message through `classify_delegation_intent()` and follows the same routing as `telegram_listener.handle_message()`. Optional **`history`** (list of `{ "role": "user"|"assistant", "content": "…" }`) supplies prior turns for the same transcript + MICRO task-brief path as Telegram.

| Result | Action |
|--------|--------|
| `confidence >= 0.85` + read-only type (`research`, `system_check`) | `action: dispatched` — creates delegation job + spawns dispatcher |
| `confidence >= 0.85` + code-modifying type (`code_change`, `create_script`) | `action: clarification_required` — returns the question Furoshiki would ask |
| Borderline `0.75–0.84` with clarifying question | `action: clarification_required` |
| Below threshold | `action: no_delegation` |

Options: `dry_run: true` (classify only, no job), `force_dispatch: true` (skip threshold).

```json
POST /inject/conversation
{ "text": "Can you check whether the soul engine cron has run in the last hour?",
  "source": "smoke_test", "dry_run": false, "force_dispatch": false }
```

### `POST /inject/delegation`

Creates a delegation job directly — bypasses classification entirely. Good for testing the worker → proposal → apply pipeline in isolation.

```json
POST /inject/delegation
{ "task_type": "research",
  "task_description": "List all cron jobs that ran in the last 24h and identify any with errors.",
  "source": "smoke_test", "dispatch": true }
```

`task_type` must be one of: `code_change`, `research`, `create_script`, `system_check`, `self_modification`.
Use `research` or `system_check` for safe smoke tests — these always propose, never auto-apply.

## CLI quick reference

```bash
furoshiki extensions                        # list extension_work_items
furoshiki extensions view <id>              # full row + joined job fields
furoshiki extensions jobs list              # delegation_jobs detail view
furoshiki extensions jobs apply <id>        # apply a proposed or fixed job
furoshiki extensions jobs merge <id>        # merge fixed branch to main
furoshiki extensions jobs delete <id>       # remove job + proposal + mind_queue refs
furoshiki extensions skills list            # proposal files (pending by default)
furoshiki extensions skills list --status all  # all proposals incl. rejected/applied
furoshiki extensions skills create "…"     # new self-modification job
furoshiki extensions skills delete <n>     # remove proposal #n + matching job rows
furoshiki extensions skills callable       # installed callable plugin tools
furoshiki extensions plugins list          # installed plugins
furoshiki extensions plugins apply <file>  # install approved skill proposal
furoshiki extensions plugins enable/disable <name>
furoshiki extensions plugins reload        # merge plugin ``cron`` hooks into memory/schedules.json (Brain)
furoshiki extensions purge --yes           # clear all queue tables (irreversible)
furoshiki extensions purge --yes --proposals  # also remove ~/.furoshiki/proposals/*.md
```

## Migration note

The one-time migration **`extension_work_v1`** (applied on first `furoshiki db migrate` after the schema exists) **clears** `delegation_jobs`, `repair_jobs`, related `mind_queue` references, and `extension_work_items` so the unified model starts clean. Proposal files under `~/.furoshiki/proposals/` are not deleted by the migration.

To wipe the same tables again later (e.g. operator reset): **`furoshiki extensions purge --yes`**. Add **`--proposals`** to remove every `*.md` under `~/.furoshiki/proposals/` as well.

## See also

- `docs/PLUGIN-SPEC.md` — runtime `PLUGIN.md` plugins (hooks, Brain scheduler, callable tools); `https://furoshiki.ai/plugins-extensions.html` for a concise overview
- `docs/DELEGATION-AND-SKILLS.md` — full delegation, skill creation, and callable skills flows with sequence diagrams
- `scripts/extension_work.py` — `insert_extension_for_*`, `sync_extension_from_*`, `delete_extension_*`, `purge_all_extension_data`
- `scripts/classify_intent.py` — `create_delegation_job` (inserts delegation + extension row atomically)
- `scripts/doctor.py` — `insert_extension_for_repair` + `sync_extension_from_repair_job`
