# Recherche CdC realign — Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans. Steps use checkbox (`- [ ]`).

**Goal:** Make `/keywords/recherche` show the whole Réservoir (drop the `stages.includes("research")` gate, per the CdC), keep the old staged behaviour live at `/keywords/recherche-staged`, via a shared `ResearchContainer` with a `stageFilter` prop.

**Architecture:** One `ResearchContainer` gains `stageFilter?: boolean`. `/keywords/recherche` passes `false` (all Réservoir) + fetches unfiltered; a new `/keywords/recherche-staged` passes `true` + fetches the staged subset (copy of the current page). Nav gains the staged entry. Everything else (targeting matrix, GSC sync, exclusions, export, consensus, pagination) is untouched.

**Tech Stack:** Next 16 App Router (RSC pages + client container), bun, biome.

Spec: `docs/superpowers/specs/2026-06-09-recherche-cdc-realign-design.md`.

**Branch:** `git checkout dev && git checkout -b feature/recherche-cdc-realign`

---

## Task 1: `stageFilter` prop on ResearchContainer

**Files:** Modify `src/components/keywords/research/research-container.tsx`.

The current `research-container.tsx` filters keywords to the research stage in exactly two spots: `load()` (line ~133) and the `KeywordSheet` `onSaved` refetch (line ~431), both `data.filter((k) => k.stages.includes("research"))`. The `rows` state is seeded from `initialKeywords` (which the page provides already-scoped — Task 2/3). Gate the two refetch filters on the new prop.

- [ ] **Step 1: Add the prop**

In the props type (where `initialExclusions: Exclusion[];` is declared), add:
```tsx
  /** When true, restrict to keywords carrying the "research" stage (legacy
   *  staged view). When false/undefined, show the whole Réservoir (CdC). */
  stageFilter?: boolean;
```
And add `stageFilter` to the destructured params of `ResearchContainer({ … })`.

- [ ] **Step 2: Gate the `load()` refetch**

Replace (in `load()`):
```tsx
    setRows(data.filter((k) => k.stages.includes("research")));
```
with:
```tsx
    setRows(
      stageFilter ? data.filter((k) => k.stages.includes("research")) : data,
    );
```
Add `stageFilter` to the `load` `useCallback` dependency array (currently `[activeSiteId]` → `[activeSiteId, stageFilter]`).

- [ ] **Step 3: Gate the `onSaved` refetch**

Replace (in the `KeywordSheet` `onSaved` handler, ~line 431):
```tsx
            const next = data.filter((k) => k.stages.includes("research"));
```
with:
```tsx
            const next = stageFilter
              ? data.filter((k) => k.stages.includes("research"))
              : data;
```

- [ ] **Step 4: Typecheck + lint + commit**

Run: `npx tsc --noEmit` → `No errors found`.
Run: `bunx biome check --write src/components/keywords/research/research-container.tsx` → clean.
```bash
git add src/components/keywords/research/research-container.tsx
git commit -m "feat(keywords): ResearchContainer stageFilter prop (gate research-stage view)"
```

---

## Task 2: `/keywords/recherche` shows the whole Réservoir

**Files:** Modify `src/app/(app)/keywords/recherche/page.tsx`.

Currently the initial fetch filters to the research stage and the container is rendered without `stageFilter`. Make it fetch unfiltered and pass `stageFilter={false}`.

- [ ] **Step 1: Drop the initial stage filter**

Replace:
```tsx
        fetchKeywordsForSiteAction(siteId).then((ks) =>
          ks.filter((k) => k.stages.includes("research")),
        ),
```
with:
```tsx
        fetchKeywordsForSiteAction(siteId),
```

- [ ] **Step 2: Pass `stageFilter={false}`**

On the `<ResearchContainer … />`, add the prop:
```tsx
        stageFilter={false}
```

- [ ] **Step 3: Typecheck + lint + commit**

Run: `npx tsc --noEmit` → clean. `bunx biome check --write "src/app/(app)/keywords/recherche/page.tsx"` → clean.
```bash
git add "src/app/(app)/keywords/recherche/page.tsx"
git commit -m "feat(keywords): Recherche shows the whole Réservoir (CdC realign)"
```

---

## Task 3: `/keywords/recherche-staged` keeps the old behaviour

**Files:** Create `src/app/(app)/keywords/recherche-staged/page.tsx`.

A copy of the *original* recherche page (keeps the stage filter), passing `stageFilter={true}` and a distinct breadcrumb.

- [ ] **Step 1: Create the page**

```tsx
// src/app/(app)/keywords/recherche-staged/page.tsx
import { headers } from "next/headers";
import { DashboardHeader } from "@/components/dashboard-header";
import { ResearchContainer } from "@/components/keywords/research/research-container";
import {
  Breadcrumb,
  BreadcrumbItem,
  BreadcrumbList,
  BreadcrumbPage,
  BreadcrumbSeparator,
} from "@/components/ui/breadcrumb";
import { getActiveOrgId } from "@/lib/active-org";
import { auth } from "@/lib/auth";
import {
  fetchExclusionsAction,
  fetchKeywordsForSiteAction,
  getTargetsForSiteAction,
} from "@/lib/keywords/actions";
import type { Exclusion } from "@/lib/keywords/queries";
import type { TargetsState } from "@/lib/keywords/targets";
import type { Keyword } from "@/lib/keywords/types";
import { getActiveSiteId } from "@/lib/sites/active-site";
import { getSitesForOrg } from "@/lib/sites/queries";

export default async function Page() {
  const member = await auth.api.getActiveMember({ headers: await headers() });
  const sites = await getSitesForOrg(await getActiveOrgId());
  const siteId = await getActiveSiteId(sites.map((s) => s.id));
  // Legacy staged view: only keywords pushed into the "research" stage.
  const [initialKeywords, initialTargets, initialExclusions] = siteId
    ? await Promise.all([
        fetchKeywordsForSiteAction(siteId).then((ks) =>
          ks.filter((k) => k.stages.includes("research")),
        ),
        getTargetsForSiteAction(siteId),
        fetchExclusionsAction(siteId),
      ])
    : [[] as Keyword[], {} as TargetsState, [] as Exclusion[]];
  return (
    <>
      <DashboardHeader>
        <Breadcrumb>
          <BreadcrumbList>
            <BreadcrumbItem>Keywords</BreadcrumbItem>
            <BreadcrumbSeparator />
            <BreadcrumbItem>
              <BreadcrumbPage>Recherche (staged)</BreadcrumbPage>
            </BreadcrumbItem>
          </BreadcrumbList>
        </Breadcrumb>
      </DashboardHeader>
      <ResearchContainer
        key={siteId}
        canSync={member?.role === "owner"}
        initialKeywords={initialKeywords}
        initialSiteId={siteId}
        initialTargets={initialTargets}
        initialExclusions={initialExclusions}
        stageFilter={true}
      />
    </>
  );
}
```

> If `ResearchContainer`'s prop names differ from the current `/keywords/recherche/page.tsx`, mirror that file exactly (read it first) and only add `stageFilter={true}` + change the breadcrumb. Do NOT invent prop names.

- [ ] **Step 2: Typecheck + lint + commit**

Run: `npx tsc --noEmit` → clean. `bunx biome check --write "src/app/(app)/keywords/recherche-staged/page.tsx"` → clean.
```bash
git add "src/app/(app)/keywords/recherche-staged/page.tsx"
git commit -m "feat(keywords): keep legacy staged Recherche at /keywords/recherche-staged"
```

---

## Task 4: Nav entry

**Files:** Modify `src/components/app-sidebar.tsx`.

The Keywords sub-items are an array around line 57-60 (`Overview`, `Reservoir`, `Recherche`, `Planning`). Add the staged entry right after `Recherche`.

- [ ] **Step 1: Add the item**

After the `{ title: "Recherche", url: "/keywords/recherche" },` entry, add:
```tsx
        { title: "Recherche (staged)", url: "/keywords/recherche-staged" },
```

- [ ] **Step 2: Typecheck + lint + commit**

Run: `npx tsc --noEmit` → clean. `bunx biome check --write src/components/app-sidebar.tsx` → clean.
```bash
git add src/components/app-sidebar.tsx
git commit -m "feat(nav): add Recherche (staged) under Keywords"
```

---

## Task 5: Live verification + finish

- [ ] **Step 1: tsc + biome sweep** — `npx tsc --noEmit` clean; `bunx biome check` clean on all four files.

- [ ] **Step 2: Live (d3k, dev server :3000, logged in on Acieb)**
  - `/keywords/recherche` → the row/“N résultats” count equals the **Réservoir** count (all keywords), not the small staged subset. Targeting matrix, Synchroniser GSC, Exporter, Exclus, consensus columns, pagination all still work.
  - `/keywords/recherche-staged` → the count equals the **old** staged subset only.
  - The sidebar shows both **Recherche** and **Recherche (staged)** under Keywords; both navigate correctly.

- [ ] **Step 3: Finish** — Use superpowers:finishing-a-development-branch (ff-merge `feature/recherche-cdc-realign` → `dev`, push, delete branch).

---

## Notes
- The GSC sync still sets the `research` stage (untouched) → it keeps feeding the staged page. No GSC/exclusions/consensus change.
- "Import into Recherche" is now automatic (importing into the Réservoir surfaces in Recherche) — no separate work.
- Out of scope (backlog): R2 drag-and-drop transitions, R1 import auto-detect/destination, CdC F6/F7/F8 (gaps/seasonal/shortlist).
