# skill · kit-lift

**For Claude Code / m3-council workers picking up a "lift a component from the kit into Nordvest's Live Chat" task.**

This is the full doctrine. Read it once per session, then follow the recipe.

---

## 0. Prereqs (read before you do anything)

- `INFRA.md` at the repo root — queue shape, daemon contract, two-supabase rule
- `README.md` — product thesis, kit's place in the fabric
- `PROMOTIONS.md` — log of what's already shipped canonical
- The component's own folder: `components/<name>/{entity.json,brain.md}` — the narrative + structured context for the thing you're about to move

If `entity.json` is missing, stop. Write it first. The lift depends on it.

## 1. Doctrine (why any of this is the way it is)

### 1.1 The kit is the UI layer of the fabric, not a sidecar

The naive read is "kit = design system, Nordvest = product, Mastermind-v2 = canonical repo, three separate things." Wrong. The kit and Nordvest's Live Chat are the *same surface at two stages of maturity*. A component lifted in the kit is a draft; the same component shipped into Nordvest is the production version.

**Therefore:** every lifted component must ship into Nordvest's `Live Chat med Agent` panel as its first integration. Not a sample, not a sandbox — the real thing, talking to `nordvest_task_queue`, against real Norwegian prose from real customers.

### 1.2 Nordvest is the staging bench

The kit's playground is for variant coverage — all the states, all the breakpoints, all the edge cases. But variants don't predict production behavior; traffic does. Nordvest's ~daily flow of real change-requests is the pressure test.

**Therefore:** if a component looks right in the playground but wrong in Nordvest, the playground is wrong, not Nordvest. Fix the component to match production reality, then re-cover variants.

### 1.3 Don't create a second queue yet

Every lifted component uses `nordvest_task_queue` (customer work) — not a new `mahana_kit_task_queue`. The kit-internal queue gets born when:
- there's a second customer, OR
- kit-internal automations (auto-lift-next-component) become frequent enough to warrant their own pipe

Until then: one queue, one bench, one pressure test.

### 1.4 Two Supabase projects — memorize which is which

| Project | Role |
|---|---|
| **gyz** | Fabric control — the `_task_queue` tables |
| **mpazpdibfxafwbrcppuc** | Nordvest's own app data — `agent_chat`, sessions |

The kit talks to **gyz**. Nordvest's LiveChat talks to **both** (reads/writes `agent_chat` on its own, reads `nordvest_task_queue` on gyz for the `Nylige forespørsler` feed). Mixing these is how you brick things.

### 1.5 Promotion is a one-way door

A component goes kit → Nordvest → `mastermind-v2/components/`. Never the other direction. If Mastermind-v2 drifts from the kit, that's a backport task (kit updates to match), not a reverse promotion.

## 2. Queue contract (the operational interface)

**Where:** `nordvest_task_queue` on gyz Supabase.

**Insert shape for a kit-lift task** (what you, the lifter, write into the queue — or what a surface like the playground's Staging Bench tab writes on your behalf):

```json
{
  "status": "pending",
  "priority": "normal",
  "source": "kit-lift",
  "request": "<norwegian or english prose describing the lift>",
  "context": {
    "kind": "component-lift",
    "component": "<name>",
    "kit_paths": {
      "component": "components/<name>.jsx",
      "entity":    "components/<name>/entity.json",
      "brain":     "components/<name>/brain.md",
      "spec":      "specs/<name>.md",
      "skill":     "skills/components/<name>.md"
    },
    "target": {
      "repo":   "nordvest-bygginnredning",
      "path":   "components/LiveChat/<Name>.jsx",
      "uses":   ["<sibling components already lifted>"]
    }
  }
}
```

**The daemon will spawn a worker. The worker receives, as context:**
- the kit paths above (mount + read before editing)
- `INFRA.md` at the kit repo root (full contract)
- this skill file
- `CLAUDE.md` in both repos
- commit author pre-set to `fredrik@obratech.no` (Vercel team requires)

## 3. The lift recipe

For one component `<Name>` (e.g. `ModelChip`, `ThinkingTrace`, `PlanBlock`):

### Step 1 — Verify kit-side readiness

```
cd <kit-repo>
test -f components/<name>.jsx                      || abort "no component"
test -f components/<name>/entity.json              || abort "no entity"
test -f components/<name>/brain.md                 || abort "no brain"
test -f specs/<name>.md                            || abort "no spec"
test -f skills/components/<name>.md                || abort "no skill"
grep -q "\"status\": \"shipped\"" components/<name>/entity.json || abort "entity not shipped"
```

If any abort fires: **do not lift.** Bounce the task back with `status='failed'` and `error='<missing>'`.

### Step 2 — Resolve dependencies first

Read `entity.json → dependencies.components`. Every listed sibling must already be lifted into Nordvest's `components/LiveChat/` *or* be a pure primitive (no deps of its own). If a sibling is missing: insert a new lift task for that sibling first, with `priority='high'`, and return.

**Example:** `ThinkingTrace` depends on `ModelChip` and `TraceStep`. If `TraceStep` isn't lifted yet, queue `TraceStep` first, wait, then lift `ThinkingTrace`. Topological order is load-bearing.

### Step 3 — Adapt for the Nordvest surface

The kit file uses the playground's runtime (CSS tokens from `design.mahana.ai/tokens.css`, inline React via Babel standalone). Nordvest is Next.js with its own CSS-in-JS. So:

1. Copy `components/<name>.jsx` → `nordvest-bygginnredning/components/LiveChat/<Name>.jsx`
2. Rewrite imports: kit's `./models` → Nordvest's `@/components/LiveChat/models`
3. Replace CSS-var references (`var(--paper)`, `var(--model-bg-claude)`) with values from `nordvest-bygginnredning/app/globals.css` OR inline them. Do NOT import the kit's tokens.css — Nordvest controls its own theme.
4. If the component uses `models.jsx`, `MODELS`, or `MODELS_BY_ID`: lift those first if not present (Step 2 applies).
5. Strip playground-only props (variant switchers, stage controls, tweak panel handlers). Keep only what the production surface consumes.

### Step 4 — Wire into `Live Chat med Agent`

The existing `LiveChatPanel` (or equivalent) lives in `nordvest-bygginnredning/components/agent-chat/`. Find the slot the component fills:

| Lifted component | Slot in LiveChat |
|---|---|
| `ModelChip` | Message avatar (replaces 🦥 emoji) |
| `MorphingAvatar` | Header avatar while worker is claiming a task |
| `ThinkingTrace` | Expandable row below a worker's in-progress message |
| `AttributedSynthesis` | Worker's final prose message body |
| `VerdictBar` | Footer of worker's DONE message (with deploy URL as verdict) |
| `ActionsRow` | Hover-row on any message |
| `PlanBlock` | When worker posts a numbered plan before executing |
| `FocusSheet` | Future: when LiveChat opens fullscreen |

If there's no existing slot: the component is too early to lift. Return.

### Step 5 — Test locally

```
cd nordvest-bygginnredning
pnpm dev                   # or whatever the repo uses
# open localhost:3000/dashboard
# verify the LiveChat panel renders the new component without breaking
# post a test change-request, watch the panel update
```

If anything's broken: `git restore .`, bounce the task with the error.

### Step 6 — Commit + push + verify deploy

```
git config user.email fredrik@obratech.no          # REQUIRED — obratech team
git add components/LiveChat/<Name>.jsx
git commit -m "Lift <Name> from Mastermind-v2 kit into LiveChat"
git push origin main
# wait for Vercel deploy (usually 60-120s)
# verify at v0-nordvest-bygginnredningx.vercel.app/dashboard
```

### Step 7 — Report back into the queue + kit

1. `UPDATE nordvest_task_queue SET status='done', result={commits:[sha], deploy_url, liftedAt:now()} WHERE id=...`
2. `POST /api/agent-chat` — "✓ `<Name>` is live in LiveChat. Deployed at <url>."
3. In the kit repo, append to `PROMOTIONS.md`:

```markdown
## YYYY-MM-DD · <Name> · staged
- Playground: preview/playground.html?c=<name>
- Spec:        specs/<name>.md
- Scope:       Lifted into nordvest-bygginnredning/components/LiveChat/
- Touches:     components/LiveChat/<Name>.jsx (new)
- Notes:       Staging bench. Promote to mastermind-v2 after ≥7d real-traffic.
```

## 4. Promotion policy (Nordvest → mastermind-v2)

A staged component is eligible for canonical promotion when **all** hold:

- [ ] Shipped in Nordvest LiveChat for ≥ 7 consecutive days
- [ ] Zero regressions reported in `nordvest_task_queue.error` citing the component
- [ ] ≥ 1 real customer task used the surface without confusion (manually reviewed by Fredrik)
- [ ] Council verdict obtained: `m3-council` session queries the three members, takes majority
- [ ] `entity.json.status` flipped from `shipped` (kit) to `canonical` (in a PR, not yet merged)

When eligible:

1. Append to `PROMOTIONS.md` with status `ready` (replacing the `staged` line, or stacking).
2. Open a GH issue on `itonsberg/mastermind-v2` titled `Promote <Name> from Mastermind-v2 kit`.
3. Body: link to playground + spec + brain + the Nordvest deploy URL proving it ran in prod.
4. Claude Code picks up that issue, implements the canonical version, sets `landed`.

**The one-way door rule:** once `landed`, the kit's version must track the canonical one. If design intent changes in the kit, open a new issue to update canonical; never let the two drift silently.

## 5. Failure modes (what bouncing a task looks like)

| Failure | `error=` | What next |
|---|---|---|
| Missing kit artifact (entity/brain/spec/skill) | `kit-incomplete:<missing>` | Bounce to kit design; not a worker problem |
| Sibling dep not lifted | `dep-missing:<sibling>` | Queue sibling first, return |
| Nordvest build breaks after copy | `build-broken:<stderr-head>` | `git restore .`, fix in kit, retry |
| Vercel deploy fails | `deploy-failed:<vercel-log-id>` | Check obratech team auth; if fine, fix in kit |
| No slot in LiveChat for this component | `no-slot` | Component lifted too early; shelve until surface ready |

## 6. What this skill deliberately does NOT do

- **Does not touch `mastermind-v2`.** Only kit → Nordvest. Canonical promotion is a separate pass with human review.
- **Does not create new Supabase tables.** If you feel like you need one: stop and re-read §1.3.
- **Does not fork the daemon.** Same `nv-daemon`, same worker spawn shape. Kit tasks are just `source='kit-lift'` rows.
- **Does not bundle the kit as npm.** That's the eventual endgame (`@mahana/mastermind-kit` as a package Nordvest imports), but until the lift is boring, direct file copy is faster and more honest about coupling.

## 7. When the endgame arrives

When ~5 components have been through kit → Nordvest → canonical without a bounce, lift patterns will have stabilized enough to bundle as an npm package. At that point:

1. `@mahana/mastermind-kit` published from this repo's `components/` folder
2. Nordvest imports instead of copy-lifts
3. This skill gets replaced by `skills/kit-publish.skill.md` (version bump → Nordvest's `package.json` update → CI lift)
4. `mahana_kit_task_queue` gets born (kit-internal: publish, version, regenerate entity bundles)

Not yet. Not until the lift is boring.

---

*This skill exists because the temptation, every time, is to create a second queue / a second daemon / a sidecar service. Don't. Reuse the green pipe. The kit is the UI layer of the fabric that already works.*
