---
phase: 08-stack-upgrade-backend-decomposition
plan: 08
type: execute
wave: 3
depends_on: [08-04, 08-05, 08-06, 08-07]
files_modified:
  - frontend/package.json
  - frontend/package-lock.json
  - frontend/tailwind.config.js
  - backend/requirements.txt
autonomous: false
requirements: [PERF-01]
tags: [dependencies, perf, requirements-pinning, vite-6, tailwind-forms]
user_setup:
  - service: npm-registry-verification
    why: "Two PERF-01 packages are install-only verified; the rest must be manually verified on npmjs.com because slopcheck was unavailable at research time"
    dashboard_config:
      - task: "Verify @vueuse/core, @vueuse/integrations, sortablejs, @tailwindcss/forms, rollup-plugin-visualizer, @types/sortablejs, vite, @vitejs/plugin-vue on npmjs.com — each must show legitimate maintainer, weekly downloads > 10k, and no recent malware advisories"
        location: "npmjs.com/package/{name}"
must_haves:
  truths:
    - "frontend/package.json declares all PERF-01 packages at the locked version constraints"
    - "frontend/tailwind.config.js wires the @tailwindcss/forms plugin so VISUAL-02 in Phase 11 can rely on its base styles"
    - "frontend/vite.config.js works with vite@^6.4.3 (no breaking changes apply per RESEARCH.md analysis)"
    - "backend/requirements.txt uses exact == pins for every package (no floating >= ranges)"
    - "cd backend && pytest -v exits 0 (no regression from pinning)"
    - "cd frontend && npm install completes cleanly with no peer-dep warnings on vite ↔ @vitejs/plugin-vue"
    - "cd frontend && npm run build succeeds with the new Vite 6"
  artifacts:
    - path: "frontend/package.json"
      provides: "Updated dependency manifest with PERF-01 packages"
      contains: "@vueuse/core"
    - path: "frontend/tailwind.config.js"
      provides: "Tailwind config with @tailwindcss/forms plugin wired"
      contains: "@tailwindcss/forms"
    - path: "backend/requirements.txt"
      provides: "Exact-pinned backend dependency list per D-17"
      contains: "fastapi=="
  key_links:
    - from: "frontend/tailwind.config.js"
      to: "@tailwindcss/forms"
      via: "plugins array"
      pattern: "plugins:.*forms"
    - from: "frontend/package.json"
      to: "vite, @vitejs/plugin-vue, @vueuse/core, sortablejs, @tailwindcss/forms, rollup-plugin-visualizer"
      via: "dependencies / devDependencies"
      pattern: "(vite|@vueuse|sortablejs|@tailwindcss/forms|rollup-plugin-visualizer)"
---

<objective>
Land PERF-01: bump Vite 5 → 6 (and its plugin), install the new frontend packages (`@vueuse/core`, `@vueuse/integrations`, `sortablejs`, `@tailwindcss/forms`, `rollup-plugin-visualizer`, `@types/sortablejs`), wire `@tailwindcss/forms` into `tailwind.config.js`, and pin `backend/requirements.txt` from floating `>=` ranges to exact `==` pins per D-17. Includes a blocking human checkpoint to manually verify the 8 npm packages on npmjs.com because slopcheck was unavailable at research time.

Purpose: PERF-01 plus the reproducibility benefit of exact pinning. Tailwind plugin wiring is required NOW so Phase 11's VISUAL-02 can rely on `@tailwindcss/forms` base styles being active.

Output: Updated `package.json`, `package-lock.json`, `tailwind.config.js`, and `requirements.txt`.
</objective>

<execution_context>
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/08-stack-upgrade-backend-decomposition/08-CONTEXT.md
@.planning/phases/08-stack-upgrade-backend-decomposition/08-RESEARCH.md
@frontend/package.json
@frontend/vite.config.js
@frontend/tailwind.config.js
@backend/requirements.txt

<interfaces>
PERF-01 package set (locked):

dependencies (production):
  vue                       ^3.5.0     (currently 3.5.34 — already satisfies; no install needed but bump declared range)
  @vueuse/core              ^14.3.0    (install)
  @vueuse/integrations      ^14.3.0    (install)
  sortablejs                ^1.15.7    (install)
  @tailwindcss/forms        ^0.5.11    (install + wire in tailwind.config.js)

devDependencies:
  vite                      ^6.4.3     (upgrade from ^5.2.0)
  @vitejs/plugin-vue        ^6.0.7     (upgrade from ^5.0.0 — required for Vite 6 compatibility)
  rollup-plugin-visualizer  ^7.0.1     (install — Phase 11 wires it into vite.config.js for PERF-02 bundle analysis)
  @types/sortablejs         latest     (install — TypeScript types for sortablejs)

tailwind.config.js wiring (per @tailwindcss/forms docs):

  /** @type {import('tailwindcss').Config} */
  import forms from '@tailwindcss/forms'
  export default {
    content: ['./index.html', './src/**/*.{vue,js}'],
    theme: { extend: {} },
    plugins: [forms],
  }

backend/requirements.txt: convert every `>=` constraint to `==` using the currently installed version per D-17. RESEARCH.md confirmed installed versions for: fastapi==0.128.8, anthropic==0.104.0, sqlalchemy[asyncio]==2.0.49, psycopg[binary]==3.2.13, alembic==1.16.5 (note: lower than the previous floating min of 1.18.4 — pin to INSTALLED per D-17), celery[redis]==5.6.3, PyJWT==2.13.0, cryptography==48.0.0, structlog==25.5.0. All other packages tagged [ASSUMED] in RESEARCH.md — discover their installed version via `pip show <pkg>` during execution.
</interfaces>
</context>

<tasks>

<task type="checkpoint:human-verify" gate="blocking-human">
  <name>Task 1: Package Legitimacy Checkpoint — manually verify 8 npm packages on npmjs.com</name>
  <what-built>
    The researcher tagged all 8 PERF-01 npm packages as [ASSUMED] because slopcheck was unavailable in the research environment. Before any install runs, manually verify on npmjs.com that each package is legitimate.
  </what-built>
  <how-to-verify>
    For each package below, open the link, check (a) maintainer is a known org or individual not a fresh account, (b) weekly downloads &gt; 10,000, (c) no malware advisory banner, (d) most recent publish date is reasonable (not "1 day ago" for an established lib), (e) the GitHub repo linked in "Repository" exists and is active:

    1. https://www.npmjs.com/package/vite                     — expect: vitejs maintainer org; millions of weekly downloads
    2. https://www.npmjs.com/package/@vitejs/plugin-vue       — expect: vitejs org; millions of weekly downloads
    3. https://www.npmjs.com/package/@vueuse/core             — expect: antfu/vueuse maintainer; millions of weekly downloads
    4. https://www.npmjs.com/package/@vueuse/integrations     — expect: antfu/vueuse maintainer
    5. https://www.npmjs.com/package/sortablejs               — expect: SortableJS org; millions of weekly downloads
    6. https://www.npmjs.com/package/@tailwindcss/forms       — expect: tailwindlabs org
    7. https://www.npmjs.com/package/rollup-plugin-visualizer — expect: btd/btmurrell maintainer; hundreds of thousands weekly
    8. https://www.npmjs.com/package/@types/sortablejs        — expect: DefinitelyTyped org

    If any package fails any check, ABORT and re-evaluate the package choice with the project owner.
  </how-to-verify>
  <resume-signal>Type "approved" to proceed with installs, or list any rejected package by name.</resume-signal>
</task>

<task type="auto">
  <name>Task 2: Install PERF-01 frontend packages and wire @tailwindcss/forms</name>
  <files>frontend/package.json, frontend/package-lock.json, frontend/tailwind.config.js</files>
  <read_first>
    - frontend/package.json (current dependency versions)
    - frontend/tailwind.config.js (currently `plugins: []` with no extensions)
    - frontend/vite.config.js (confirm it has no custom `resolve.conditions` — RESEARCH.md verified Vite 6 migration is clean for this minimal config)
  </read_first>
  <action>
Run two npm install commands in `frontend/`:

  cd frontend && npm install vue@^3.5.0 @vueuse/core@^14.3.0 @vueuse/integrations@^14.3.0 sortablejs@^1.15.7 @tailwindcss/forms@^0.5.11
  cd frontend && npm install -D vite@^6.4.3 @vitejs/plugin-vue@^6.0.7 rollup-plugin-visualizer@^7.0.1 @types/sortablejs

These two commands update both `package.json` (declared ranges) and `package-lock.json` (resolved exact versions). Do NOT install `vue@^3.5.0` separately if npm warns the existing 3.5.34 satisfies the new range — the bump-then-deduplicate behavior is automatic.

After npm install completes, modify `frontend/tailwind.config.js` to wire the forms plugin. The CURRENT file uses ESM with `export default`. Update it to:

  /** @type {import('tailwindcss').Config} */
  import forms from '@tailwindcss/forms'
  export default {
    content: ['./index.html', './src/**/*.{vue,js}'],
    theme: { extend: {} },
    plugins: [forms],
  }

Do NOT modify `frontend/vite.config.js` — RESEARCH.md verified the existing config is compatible with Vite 6 (no `resolve.conditions`, no Sass, no library mode). `rollup-plugin-visualizer` is installed but NOT wired into `vite.config.js` yet — Phase 11's PERF-02 will wire it when bundle measurements are taken.

After config wiring: run `cd frontend && npm test` to confirm tests still pass on Vite 6, then `cd frontend && npm run build` to confirm a clean production build. If either fails, diagnose and fix before proceeding.
  </action>
  <verify>
    <automated>cd frontend && grep -c '"vite": "\\^6' package.json && grep -c '"@vitejs/plugin-vue": "\\^6' package.json && grep -c '"@vueuse/core": "\\^14' package.json && grep -c '"sortablejs"' package.json && grep -c '"@tailwindcss/forms"' package.json && grep -c '"rollup-plugin-visualizer"' package.json && grep -c "@tailwindcss/forms" tailwind.config.js && grep -c "plugins: \\[forms\\]" tailwind.config.js && npm test 2>&1 | tail -5 && npm run build 2>&1 | tail -5</automated>
  </verify>
  <acceptance_criteria>
    - `grep -c '"vite": "\^6' frontend/package.json` returns 1
    - `grep -c '"@vitejs/plugin-vue": "\^6' frontend/package.json` returns 1
    - `grep -c '"@vueuse/core": "\^14' frontend/package.json` returns 1
    - `grep -c '"@vueuse/integrations": "\^14' frontend/package.json` returns 1
    - `grep -c '"sortablejs": "\^1.15' frontend/package.json` returns 1
    - `grep -c '"@tailwindcss/forms": "\^0.5' frontend/package.json` returns 1
    - `grep -c '"rollup-plugin-visualizer": "\^7' frontend/package.json` returns 1
    - `grep -c '"@types/sortablejs"' frontend/package.json` returns 1
    - `grep -c "import forms from '@tailwindcss/forms'" frontend/tailwind.config.js` returns 1
    - `grep -c "plugins: \[forms\]" frontend/tailwind.config.js` returns 1
    - `cd frontend && npm test` exits 0
    - `cd frontend && npm run build` exits 0
    - `cd frontend && npm list vite | grep -c "vite@6"` returns at least 1
  </acceptance_criteria>
  <done>All PERF-01 packages installed; tailwind forms plugin wired; tests + production build pass on Vite 6.</done>
</task>

<task type="auto">
  <name>Task 3: Pin backend/requirements.txt to exact installed versions (D-17)</name>
  <files>backend/requirements.txt</files>
  <read_first>
    - backend/requirements.txt (current floating-range constraints)
    - .planning/phases/08-stack-upgrade-backend-decomposition/08-RESEARCH.md §"requirements.txt: Exact Pinning (D-17)" (already-verified versions: fastapi==0.128.8, anthropic==0.104.0, sqlalchemy[asyncio]==2.0.49, psycopg[binary]==3.2.13, alembic==1.16.5, celery[redis]==5.6.3, PyJWT==2.13.0, cryptography==48.0.0, structlog==25.5.0)
  </read_first>
  <action>
For each package in `backend/requirements.txt` that is tagged [ASSUMED] in RESEARCH.md (uvicorn, python-multipart, pydantic-settings, pydantic, openai, PyMuPDF, python-docx, pytesseract, Pillow, aiofiles, httpx, pytest, pytest-asyncio, minio, redis, aiosqlite, pwdlib, pyotp, slowapi, google-auth-oauthlib, google-api-python-client, msal, webdavclient3, cachetools), run `cd backend && pip show <pkg> 2>/dev/null | grep '^Version:'` to capture the installed version. If a package returns no output (not installed in the local env), run `cd backend && python -c "import importlib.metadata as m; print(m.version('<pkg>'))"` as a fallback. Record each version.

Then rewrite `backend/requirements.txt` so every line uses an exact `==` pin. Preserve the existing section comments (e.g. "# Cloud Storage Backends (Phase 5)", "# Observability (Phase 6 — D-01)"). For the already-verified packages from RESEARCH.md, use the locked versions: fastapi==0.128.8, anthropic==0.104.0, sqlalchemy[asyncio]==2.0.49, psycopg[binary]==3.2.13, alembic==1.16.5, celery[redis]==5.6.3, PyJWT==2.13.0, cryptography==48.0.0, structlog==25.5.0. For everything else, use the version captured by `pip show`. If `alembic` is shown as 1.16.5 (lower than the previous floating min of 1.18.4), pin to 1.16.5 per D-17 — but also append a single-line comment immediately before the alembic line: `# alembic pinned to currently-installed 1.16.5 (was >=1.18.4); D-17 mandates pinning to installed`.

After rewriting, run `cd backend && pip install -r requirements.txt --dry-run 2>&1 | tail -20` to confirm pip considers the file valid and all pins are satisfiable. Then run `cd backend && pytest -v` to confirm no regression from re-resolving deps.
  </action>
  <verify>
    <automated>cd backend && grep -cE '>=' requirements.txt; cd backend && grep -cE '==' requirements.txt; cd backend && pip install -r requirements.txt --dry-run 2>&1 | tail -5; cd backend && pytest -v --tb=no -q 2>&1 | tail -5</automated>
  </verify>
  <acceptance_criteria>
    - `grep -c '^[^#].*>=' backend/requirements.txt` returns 0 (every non-comment line uses == or is bare)
    - `grep -cE '^[a-zA-Z][a-zA-Z0-9_.\\[\\]-]*==' backend/requirements.txt` returns at least 30 (count of pinned packages — file has ~32 packages)
    - `grep -c "^fastapi==0.128.8" backend/requirements.txt` returns 1
    - `grep -c "^alembic==1.16.5" backend/requirements.txt` returns 1
    - `grep -c "^sqlalchemy\\[asyncio\\]==2.0.49" backend/requirements.txt` returns 1
    - `grep -c "^PyJWT==2.13.0" backend/requirements.txt` returns 1
    - `grep -c "^cryptography==48.0.0" backend/requirements.txt` returns 1
    - `grep -c "alembic pinned to currently-installed" backend/requirements.txt` returns 1
    - `cd backend && pytest -v` exits 0 with no new failures vs. baseline
    - `cd backend && pip install -r requirements.txt --dry-run` reports no resolution conflicts (exit 0)
  </acceptance_criteria>
  <done>requirements.txt fully pinned with == ; alembic discrepancy commented; pytest still green.</done>
</task>

</tasks>

<threat_model>
## Trust Boundaries

| Boundary | Description |
|----------|-------------|
| npmjs.com → frontend node_modules | Supply chain: any of the 8 packages could be malicious if a name was typosquatted or a maintainer account was compromised |
| pypi.org → backend venv | Same supply-chain consideration for any unpinned package; D-17 pinning narrows the attack window |
| Vite 6 build pipeline → bundled JS | Major version bump may introduce regressions; npm test + production build verify integrity |

## STRIDE Threat Register

| Threat ID | Category | Component | Disposition | Mitigation Plan |
|-----------|----------|-----------|-------------|-----------------|
| T-08-08-01 | Supply Chain (Tampering) | 8 npm package installs | mitigate | Blocking human checkpoint (task 1) requires manual npmjs.com verification because slopcheck was unavailable; gate flagged blocking-human and never auto-approvable. T-{phase}-SC retained per Package Legitimacy Gate protocol. |
| T-08-08-02 | Tampering | Vite 6 migration breaking behavior | mitigate | RESEARCH.md analyzed all Vite 5→6 breaking changes against the project's `vite.config.js` and confirmed none apply (no custom `resolve.conditions`, no Sass, no library mode); npm test + npm run build acceptance criteria verify post-install. |
| T-08-08-03 | Information Disclosure | requirements.txt leaks installed versions | accept | Versions are not secrets; reproducibility benefit outweighs disclosure |
| T-08-08-04 | Denial of Service | alembic 1.16.5 lower than 1.18.4 | mitigate | D-17 mandates pinning to installed; the inline comment documents the discrepancy so a future developer can decide to bump deliberately |
| T-08-08-05 | Tampering | @vitejs/plugin-vue peer-dep mismatch | mitigate | Both vite@^6 and @vitejs/plugin-vue@^6 installed in same `npm install -D` command per RESEARCH.md §Pitfall 6 |
| T-08-08-SC | Supply Chain | npm packages [ASSUMED] | mitigate | Blocking human checkpoint (task 1) is the gate; checkpoint is blocking-human and cannot be auto-approved |
</threat_model>

<verification>
- `cd frontend && npm test` — zero failures
- `cd frontend && npm run build` — succeeds with Vite 6
- `cd frontend && npm list vite @vitejs/plugin-vue @vueuse/core @vueuse/integrations sortablejs @tailwindcss/forms rollup-plugin-visualizer @types/sortablejs` — all listed
- `cd backend && pytest -v` — zero failures
- `cd backend && pip install -r requirements.txt --dry-run` — no resolution errors
- `grep -v '^#' backend/requirements.txt | grep -cE '>='` returns 0 (no remaining floating constraints in production-package lines)
</verification>

<success_criteria>
- 8 PERF-01 packages installed and verified
- `@tailwindcss/forms` wired in tailwind.config.js (ready for Phase 11 VISUAL-02)
- Vite 6 production build succeeds
- backend/requirements.txt fully `==` pinned per D-17
- All tests pass
- Human checkpoint approved on npm package legitimacy
</success_criteria>

<output>
Create `.planning/phases/08-stack-upgrade-backend-decomposition/08-08-SUMMARY.md` when done. Include: (a) before/after `package.json` dependency table, (b) full pinned `requirements.txt` content, (c) the human checkpoint approval timestamp and any package that required follow-up.
</output>
