diff --git a/docs/todos/2026-06-19-issue-148-viewer-vue-vite-frontend/plan.md b/docs/todos/2026-06-19-issue-148-viewer-vue-vite-frontend/plan.md
new file mode 100644
index 000000000..33ef63ba8
--- /dev/null
+++ b/docs/todos/2026-06-19-issue-148-viewer-vue-vite-frontend/plan.md
@@ -0,0 +1,1822 @@
+# Viewer Vue/Vite Frontend Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** Add a typed Vue 3 + Vite hidden-preview viewer slice that preserves the current shipped viewer/server/proxy contract while establishing route, API, Dashboard, fixture, and build boundaries for issue #148.
+
+**Architecture:** Keep `src/viewer/server.ts`, `src/viewer/document.ts`, and `src/auth.ts` as the production trust boundary. Add Vue/Vite source under `src/viewer/app/` and build it into a non-default preview artifact under `dist/viewer-next/`; the existing `dist/viewer/index.html` remains the shipped viewer in this slice. Use local browser validators/type guards, hash routing, and fixture-backed Dashboard tests before any production cutover.
+
+**Tech Stack:** TypeScript ESM, Vue 3, Vite, `@vitejs/plugin-vue`, Vitest, Node `http/fs/path`, existing viewer server/document/security tests, pnpm with repo hardening.
+
+---
+
+## Context And Boundaries
+
+Spec: `docs/todos/2026-06-19-issue-148-viewer-vue-vite-frontend/spec.md`
+
+Task record: `docs/todos/2026-06-19-issue-148-viewer-vue-vite-frontend/todo.md`
+
+Working branch: `issue/148-viewer-vue-vite-frontend`
+
+Remote target: `origin` (`https://github.com/wbugitlab1/agentmemory.git`) only.
+
+Remote, credentialed, and terminal actions still requiring explicit current-turn approval:
+
+- fetch, pull, push, PR creation, PR merge, issue closure, thread archival, deployment, publishing, destructive cleanup, migrations, credentialed GitHub reads, and remote/project/account state changes.
+
+Full GitHub feature-loop local PR-prep authorization applies only inside the later `$github-push-prepare` phase and only to task-owned local branch-prep surfaces.
+
+## Candidate Dependency Intake
+
+Public npm registry metadata was checked on 2026-06-19. Direct dependency candidates for the first slice:
+
+| Package | Candidate version | Role | Decision |
+| --- | --- | --- | --- |
+| `vue` | `3.5.38` | Vue runtime/compiler peer for SFC app source | Accept as devDependency only if install/OSV/lifecycle review passes |
+| `vite` | `8.0.16` | Viewer build/dev tooling | Accept if install/OSV/lifecycle review passes |
+| `@vitejs/plugin-vue` | `6.0.7` | Official Vue SFC support for Vite | Accept if install/OSV/lifecycle review passes |
+| `vue-tsc` | `3.3.5` | Vue SFC type checking | Accept if install/OSV/lifecycle review passes |
+
+Rejected/deferred for first slice:
+
+- `vue-router`: defer; hash-route adapter is enough.
+- `pinia`: defer; module/composable state is enough.
+- browser-bundled `zod`: defer; local validators first.
+- Playwright as a package dependency: defer; use existing browser tooling/manual smoke for this slice unless a later review approves heavier browser test dependencies.
+- UI component libraries, icon libraries, graph libraries, and single-file Vite plugins: reject for this slice.
+
+Dependency rules for implementation:
+
+- Pin direct dependency versions exactly in `package.json`.
+- Keep Vue/Vite/SFC tooling in `devDependencies`; the preview artifact is inlined into `dist/` and does not require Vue as an installed runtime dependency.
+- Preserve `pnpm-workspace.yaml` hardening.
+- Do not approve lifecycle builds without separate recorded review.
+- Run dependency setup commands from a sanitized environment where practical by unsetting token variables such as `NPM_TOKEN`, `NODE_AUTH_TOKEN`, `GITHUB_TOKEN`, `GH_TOKEN`, `GITLAB_TOKEN`, and `CI_JOB_TOKEN`.
+- After dependency/lockfile changes, run OSV and manually inspect lockfile churn.
+
+## File Map
+
+Create:
+
+- `src/viewer/app/index.html`: Vite preview HTML shell with viewer placeholders.
+- `src/viewer/app/main.ts`: Vue mount entry.
+- `src/viewer/app/App.vue`: Vue app shell.
+- `src/viewer/app/vue-shim.d.ts`: TypeScript shim for Vue SFC imports.
+- `src/viewer/app/env.ts`: typed bootstrap and URL resolution.
+- `src/viewer/app/routes.ts`: hash tab routing.
+- `src/viewer/app/api/client.ts`: typed fetch wrapper.
+- `src/viewer/app/api/validators.ts`: local validators/type guards.
+- `src/viewer/app/api/types.ts`: viewer API types.
+- `src/viewer/app/i18n/index.ts`: typed locale helper.
+- `src/viewer/app/realtime/events.ts`: typed realtime event placeholders.
+- `src/viewer/app/realtime/client.ts`: realtime client stub boundary.
+- `src/viewer/app/state/viewer.ts`: shell state helpers.
+- `src/viewer/app/state/dashboard.ts`: Dashboard state loader.
+- `src/viewer/app/components/TabBar.vue`: tab navigation.
+- `src/viewer/app/components/ToastHost.vue`: toast UI.
+- `src/viewer/app/components/ViewerAuthPrompt.vue`: auth prompt UI.
+- `src/viewer/app/components/FlagBanners.vue`: flag banner placeholder.
+- `src/viewer/app/components/EmptyState.vue`: reusable empty state.
+- `src/viewer/app/components/ViewerFooter.vue`: footer/status chrome.
+- `src/viewer/app/pages/DashboardPage.vue`: first migrated page.
+- `src/viewer/app/pages/PlaceholderPage.vue`: unmigrated tab placeholder.
+- `src/viewer/app/styles/tokens.css`: extracted current tokens/theme.
+- `src/viewer/app/styles/chrome.css`: shell layout.
+- `src/viewer/app/styles/primitives.css`: shared primitives.
+- `src/viewer/vite.config.ts`: viewer-local Vite config.
+- `scripts/build-viewer.ts`: build helper that produces `dist/viewer-next/index.html`.
+- `test/fixtures/viewer/dashboard.json`: deterministic Dashboard fixture.
+- `test/fixtures/viewer/dashboard-partial-failure.json`: partial failure fixture.
+- `test/viewer-app-routes.test.ts`: route tests.
+- `test/viewer-app-api-client.test.ts`: API client tests.
+- `test/viewer-app-dashboard.test.ts`: Dashboard state/validator tests.
+- `test/viewer-vite-build.test.ts`: preview artifact tests.
+
+Modify:
+
+- `package.json`: add exact dependency pins and viewer scripts.
+- `pnpm-lock.yaml`: update after intentional dependency install.
+- `test/build-package-contract.test.ts`: keep shipped viewer contract and add preview contract.
+- `docs/todos/2026-06-19-issue-148-viewer-vue-vite-frontend/todo.md`: progress, dependency intake, verification evidence.
+
+Do not modify in this slice except to fix a directly failing contract:
+
+- `src/viewer/server.ts`
+- `src/viewer/document.ts`
+- `src/auth.ts`
+- `src/viewer/index.html`
+
+## Task 1: Dependency Manifest And Package Contract
+
+**Files:**
+- Modify: `package.json`
+- Modify: `pnpm-lock.yaml`
+- Modify: `test/build-package-contract.test.ts`
+- Modify: `docs/todos/2026-06-19-issue-148-viewer-vue-vite-frontend/todo.md`
+
+- [ ] **Step 1: Write failing package contract assertions**
+
+In `test/build-package-contract.test.ts`, add assertions inside `runtime assets copied by the build are included in the npm package files allowlist`:
+
+```ts
+ expect(buildScript).toContain("corepack pnpm run viewer:build");
+ expect(buildScript).toContain("cp src/viewer/index.html dist/viewer/");
+ expect(pkg.scripts?.["viewer:build"]).toBe("tsx scripts/build-viewer.ts");
+ expect(pkg.scripts?.["viewer:test"]).toBe(
+ "vitest run test/viewer-app-routes.test.ts test/viewer-app-api-client.test.ts test/viewer-app-dashboard.test.ts test/viewer-vite-build.test.ts",
+ );
+ expect(pkg.scripts?.["viewer:typecheck"]).toBe("vue-tsc --noEmit -p src/viewer/app/tsconfig.json");
+ expect(pkg.dependencies?.vue).toBeUndefined();
+ expect(pkg.devDependencies?.vue).toBe("3.5.38");
+```
+
+Keep the existing `cp src/viewer/index.html dist/viewer/` assertion because the first slice is preview-only.
+
+- [ ] **Step 2: Run the focused test and confirm red**
+
+Run:
+
+```bash
+corepack pnpm exec vitest run test/build-package-contract.test.ts
+```
+
+Expected: FAIL because `viewer:build`, `viewer:test`, and `corepack pnpm run viewer:build` do not exist yet. If pnpm ignored-build hardening blocks execution before Vitest runs, run:
+
+```bash
+env -u NPM_TOKEN -u NODE_AUTH_TOKEN -u GITHUB_TOKEN -u GH_TOKEN -u GITLAB_TOKEN -u CI_JOB_TOKEN corepack pnpm install --frozen-lockfile --ignore-scripts
+```
+
+Then rerun the focused command.
+
+- [ ] **Step 3: Update package scripts and dependency pins**
+
+In `package.json`, keep existing production dependencies unchanged and add exact Vue/Vite/SFC tooling under `devDependencies`:
+
+```json
+ "@vitejs/plugin-vue": "6.0.7",
+ "vite": "8.0.16",
+ "vue": "3.5.38",
+ "vue-tsc": "3.3.5",
+```
+
+Update scripts:
+
+```json
+ "build": "tsdown && corepack pnpm run viewer:build && (cp iii-config.yaml dist/ 2>/dev/null || true) && (cp iii-config.docker.yaml dist/ 2>/dev/null || true) && (cp docker-compose.yml dist/ 2>/dev/null || true) && (cp .env.example dist/ 2>/dev/null || true) && mkdir -p dist/viewer/locales && cp src/viewer/index.html dist/viewer/ && cp src/viewer/favicon.svg dist/viewer/ && cp src/viewer/locales/*.json dist/viewer/locales/",
+ "viewer:build": "tsx scripts/build-viewer.ts",
+ "viewer:test": "vitest run test/viewer-app-routes.test.ts test/viewer-app-api-client.test.ts test/viewer-app-dashboard.test.ts test/viewer-vite-build.test.ts",
+ "viewer:typecheck": "vue-tsc --noEmit -p src/viewer/app/tsconfig.json",
+```
+
+Keep JSON ordering compatible with the existing style: scripts stay grouped with other scripts, Vue/Vite tooling stays under `devDependencies`, and build remains a single script string.
+
+- [ ] **Step 4: Materialize lockfile without lifecycle scripts**
+
+Run:
+
+```bash
+env -u NPM_TOKEN -u NODE_AUTH_TOKEN -u GITHUB_TOKEN -u GH_TOKEN -u GITLAB_TOKEN -u CI_JOB_TOKEN corepack pnpm install --frozen-lockfile --ignore-scripts
+```
+
+Expected: FAIL because the manifest changed and the lockfile is stale.
+
+Then run the intentional lockfile update:
+
+```bash
+env -u NPM_TOKEN -u NODE_AUTH_TOKEN -u GITHUB_TOKEN -u GH_TOKEN -u GITLAB_TOKEN -u CI_JOB_TOKEN corepack pnpm install --lockfile-only --ignore-scripts
+```
+
+Expected: `pnpm-lock.yaml` updates; no lifecycle builds are approved.
+
+Then run:
+
+```bash
+env -u NPM_TOKEN -u NODE_AUTH_TOKEN -u GITHUB_TOKEN -u GH_TOKEN -u GITLAB_TOKEN -u CI_JOB_TOKEN corepack pnpm install --frozen-lockfile --ignore-scripts
+```
+
+Expected: PASS or a recorded hardening blocker.
+
+- [ ] **Step 5: Review dependency churn**
+
+Run:
+
+```bash
+git diff -- package.json pnpm-lock.yaml
+```
+
+Expected: direct additions are limited to `vue`, `vite`, `@vitejs/plugin-vue`, and `vue-tsc`; transitive additions are registry packages required by those tools. Record any lifecycle-script or non-registry surprise in `todo.md` before proceeding.
+
+- [ ] **Step 6: Run package contract green**
+
+Run:
+
+```bash
+corepack pnpm exec vitest run test/build-package-contract.test.ts
+```
+
+Expected: PASS after `scripts/build-viewer.ts` exists in a later task; at this point it may still fail on missing script path. If it fails only because `scripts/build-viewer.ts` is not created yet, record that as expected until Task 2.
+
+- [ ] **Step 7: Update task record**
+
+In `todo.md`, add a progress note:
+
+```markdown
+- 2026-06-19: Dependency manifest plan accepted candidate devDependency versions `vue@3.5.38`, `vite@8.0.16`, `@vitejs/plugin-vue@6.0.7`, and `vue-tsc@3.3.5`; lockfile materialization used sanitized `env -u ... corepack pnpm install ... --ignore-scripts`; lifecycle build approvals were not granted.
+```
+
+## Task 2: Viewer Build Helper And Preview Artifact Contract
+
+**Files:**
+- Create: `src/viewer/app/index.html`
+- Create: `src/viewer/app/main.ts`
+- Create: `src/viewer/app/App.vue`
+- Create: `src/viewer/app/vue-shim.d.ts`
+- Create: `src/viewer/vite.config.ts`
+- Create: `scripts/build-viewer.ts`
+- Create: `test/viewer-vite-build.test.ts`
+- Modify: `test/build-package-contract.test.ts`
+
+- [ ] **Step 1: Write failing preview artifact test**
+
+Create `test/viewer-vite-build.test.ts`:
+
+```ts
+import { mkdirSync, readFileSync, rmSync } from "node:fs";
+import { join, resolve } from "node:path";
+import { afterEach, describe, expect, it } from "vitest";
+import { buildViewerPreview } from "../scripts/build-viewer.js";
+
+const repoRoot = resolve(__dirname, "..");
+const outDir = join(repoRoot, "dist", "viewer-next-test");
+
+describe("viewer Vite preview build", () => {
+ afterEach(() => {
+ rmSync(outDir, { recursive: true, force: true });
+ });
+
+ it("emits a preview HTML artifact without replacing the shipped viewer", async () => {
+ mkdirSync(outDir, { recursive: true });
+ await buildViewerPreview({ outDir });
+
+ const html = readFileSync(join(outDir, "index.html"), "utf8");
+ expect(html).toContain("__AGENTMEMORY_VIEWER_NONCE__");
+ expect(html).toContain("__AGENTMEMORY_VERSION__");
+ expect(html).toContain("__AGENTMEMORY_LOCALE__");
+ expect(html).toContain('id="app"');
+ expect(html).not.toContain("
+
+