diff --git a/.agents/skills/memory-context/memory-context b/.agents/skills/memory-context/memory-context deleted file mode 120000 index 71eebe2a..00000000 --- a/.agents/skills/memory-context/memory-context +++ /dev/null @@ -1 +0,0 @@ -../../.agents/skills/memory-context \ No newline at end of file diff --git a/.agents/skills/test-runner/test-runner b/.agents/skills/test-runner/test-runner deleted file mode 120000 index e120457f..00000000 --- a/.agents/skills/test-runner/test-runner +++ /dev/null @@ -1 +0,0 @@ -../../.agents/skills/test-runner \ No newline at end of file diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml index bc406cc8..3184ef74 100644 --- a/.github/workflows/security-scan.yml +++ b/.github/workflows/security-scan.yml @@ -37,7 +37,6 @@ jobs: - name: Run ShellCheck with Security Focus uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # 2.0.0 - continue-on-error: true with: severity: warning format: tty diff --git a/.github/workflows/version-propagation.yml b/.github/workflows/version-propagation.yml index 63b6bc91..2225768e 100644 --- a/.github/workflows/version-propagation.yml +++ b/.github/workflows/version-propagation.yml @@ -1,13 +1,11 @@ --- name: Version Propagation +# DISABLED: This workflow depends on VERSION, CHANGELOG.md, and +# scripts/propagate-version.sh which are not present in the repository. +# Re-enable when those artifacts are restored. "on": - push: - paths: - - VERSION - branches: - - main - - 'feat/**' + workflow_dispatch: {} permissions: contents: write diff --git a/plans/071-goap-codebase-gap-audit-2026-07-19.md b/plans/071-goap-codebase-gap-audit-2026-07-19.md new file mode 100644 index 00000000..519f46c9 --- /dev/null +++ b/plans/071-goap-codebase-gap-audit-2026-07-19.md @@ -0,0 +1,505 @@ +# Plan 071 — Codebase Gap Audit and Remediation GOAP (2026-07-19) + +**Date**: 2026-07-19 +**Status**: OPEN +**Method**: GOAP (Goal-Oriented Action Planning) +**Scope**: implementation completeness, product opportunities, agent harness, +skills, instructions, coding workflow, tests, and UI/UX +**Constraint**: preserve the local-first Next.js baseline from ADR 018 + +## Outcome + +Restore a trustworthy relationship between the product, its documentation, and +its quality gates. The current application has a strong local-first foundation, +but the audit found two data-integrity risks, an incomplete synchronization +boundary, several misleading product claims, gaps in agent-harness enforcement, +weak coverage thresholds, and responsive/accessibility work that is not fully +verified. + +This plan replaces the statement in `plans/INDEX.md` that no open P0-P3 work +remains. Current source is authoritative; checked boxes in older Vite/SQLite +plans are historical evidence, not proof of behavior in the current Next.js app. + +## Evidence Baseline + +### Current architecture + +- Next.js 16 / React 19 single-shell application. +- Zustand plus `localStorage` is canonical persistence. +- Markdown is an interchange format, not canonical storage. +- Yjs, IndexedDB, and WebRTC are optional collaboration infrastructure. +- Search is lexical BM25, despite some UI and documentation calling it + semantic search. +- OpenRouter and Ollama are available in AI Harness; the main Chat view uses a + local deterministic retrieval response. + +### Audit limitations + +- Source, tests, plans, scripts, workflow files, and existing generated reports + were inspected. +- The prior UI audit in `plans/ui-ux-audit-2026-07-11.md` was source-only and is + partially stale: keyboard semantics, dialog semantics, graph export/history, + and editor focus treatment have since improved. +- The retry audit ran headless Chromium at 390×844, 768×1024, 1024×768, and + 1440×900. No document-level horizontal overflow was observed at those widths. +- The retry audit ran fresh lint, typecheck, coverage, and Playwright commands. + Existing generated reports outside those runs remain untrusted. +- No screen-reader, automated axe, 200% text zoom, or 400% reflow pass was + completed. Those remain G5 verification requirements. + +### Fresh retry baseline + +| Check | Result | Interpretation | +|---|---|---| +| `pnpm run lint` | Passed | No lint output warnings | +| `pnpm run typecheck` | Passed | No type errors | +| `pnpm run test:coverage` | 254/254 passed | 26.44% lines, 16.66% branches, 19.14% functions, 25.86% statements | +| `pnpm run test:e2e` | 57/58 passed | One strict-locator failure in `search-and-filter.spec.ts` | +| Browser viewport checks | 4 viewports inspected | No page overflow; target-size and compact-text issues reproduced | + +The coverage run emitted React, Yjs, and Vitest warnings while still passing. +This directly confirms that the documented warnings-as-errors policy is not +implemented by the current gates. + +## Goal Graph + +```text +G1 Data integrity ───────┬──> G3 Honest product surface ──> G5 UI/UX verification + │ +G2 Sync correctness ────┘ + +G4 Harness integrity ───────> G6 Reliable quality gates ──> G7 Documentation truth +``` + +G1 and G2 are the release blockers. G4 and G6 can run in parallel with product +work. G3 must precede final UI copy and documentation updates so the interface +does not continue to promise behavior that is absent. + +## Prioritized Findings + +### P0 — Data integrity and false-success behavior + +#### F1. Collaboration is not connected to canonical state + +`src/components/studio/views/sync-view.tsx` writes a Zustand snapshot into Yjs +when joining, but no production lifecycle subscribes Yjs changes back into the +store. Store CRUD does not propagate ongoing updates or deletions to Yjs. +Conflict resolution accepts a resolution map but clears conflicts without +applying the selected values. Existing bridge tests cover isolated map helpers, +not two-way canonical-state synchronization. + +**Risk**: peers can report successful synchronization while remote edits are not +visible or durable in the canonical library. + +**Decision**: ADR 027. + +#### F2. Import can replace valid data with a partially accepted payload + +`src/components/studio/views/export-helpers.ts` uses shallow record guards and +silently filters invalid records even though complete Zod schemas exist in +`src/lib/studio/schema.ts`. `export-view.tsx` then replaces the canonical +library without a preview, backup, or rollback path. + +**Risk**: malformed, future-version, or partially valid archives can cause +silent record loss followed by destructive replacement. + +**Decision**: ADR 028. + +### P1 — Durability, workflow, and product-trust gaps + +#### F3. Persisted hydration trusts unknown runtime data + +The Zustand persist migration in `src/lib/studio/store.ts` is a no-op type cast. +Corrupt or old `localStorage` content is not validated at the boundary. + +**Remediation**: implement ADR 028 for both import and hydration. + +#### F4. Entity deletion leaves incoming links behind + +`deleteEntity` removes the entity and claims but does not remove links in other +entities whose `targetId` references the deleted entity. + +**Remediation**: make deletion one atomic history transaction and test incoming +and outgoing relationships. + +#### F5. API-key ciphertext outlives its session encryption key + +`src/lib/studio/ai-settings.ts` persists encrypted settings while keeping the +encryption key in `sessionStorage`. After the session, a newly generated key +cannot decrypt the saved ciphertext. + +**Remediation**: choose and document one honest retention model: session-only +credential storage, or passphrase-derived persistent encryption. Do not imply +persistence that cannot survive restart. + +#### F6. Search and Chat labels overstate implementation + +The retrieval engine is BM25, not embedding/vector semantic search. Desktop +labels it “Semantic”; mobile stores the selected mode but always renders the +filtered entity list. Chat presents a fixed local retrieval response while the +copy invites AI synthesis. The actual provider-backed path is AI Harness. + +**Remediation**: first rename modes to Keyword and Ranked/Relevance, wire the +same ranking on mobile, and label Chat as local library Q&A. Treat local +embeddings and provider-backed synthesis as separate opt-in features. + +#### F7. Agent-surface validation can pass broken skill artifacts + +Two tracked skill symlinks are broken: + +- `.agents/skills/memory-context/memory-context` +- `.agents/skills/test-runner/test-runner` + +`scripts/agent-surface.py` only checks minimal `SKILL.md` content. It does not +validate symlinks, frontmatter, directory/name agreement, eval schemas, or the +declared surfaces in `.agents/manifest.json`. `validate-skills.sh` therefore +returns success for an invalid surface. + +**Decision**: ADR 029. + +#### F8. The documented release workflow references absent artifacts + +`AGENTS.md` and `.github/workflows/version-propagation.yml` describe a VERSION- +driven release flow, but `VERSION`, `CHANGELOG.md`, and +`scripts/propagate-version.sh` are absent. + +**Remediation**: either restore and test the complete automated mechanism or +retire the obsolete claim and workflow. Do not add a manual release path. + +#### F9. Security and warning policies are not enforced as documented + +The security workflow marks key scanners non-blocking or uses a zero exit code, +then summarizes job success rather than finding counts. Quality scripts inspect +command exit status but do not enforce the root rule that warnings are errors; +`MAX_ALLOWED_WARNINGS` is unused. + +**Remediation**: preserve artifact upload under `if: always()`, but allow the +designated scanners to fail. Capture quality-command output and enforce a small, +explicit warning policy. + +### P2 — Tests, accessibility, responsive behavior, and completeness + +#### F10. Coverage thresholds are too low to protect critical workflows + +`vitest.config.ts` requires only 15% lines/statements/functions and 10% branches. +There are focused tests for store, export helpers, retrieval, provider adapters, +encryption, drafts, and isolated sync helpers, but no end-to-end integration +proof for canonical Yjs synchronization, conflict application, deletion +tombstones, safe import rollback, or malformed persisted-state recovery. + +Fresh coverage was 26.44% lines, 16.66% branches, 19.14% functions, and 25.86% +statements. Store and export helpers have useful focused coverage, but studio UI +is 6.71% by lines and important views remain at 0% unit coverage. + +The Playwright suite contains 58 tests across eight spec files but configures +only one Desktop Chrome project, uses no retries, and does not run in the +inspected CI workflow. Fresh execution produced 57 passes and one failure; with +`trace: 'on-first-retry'` and retries disabled, the failure produced no useful +trace. Nine conditional branches can skip intended assertions, and several +responsive, keyboard, validation, and search tests assert only the page title or +another unrelated element. The accessibility suite checks selected roles and +labels but does not run an automated WCAG engine or verify screen-reader +behavior. + +#### F11. Touch targets and compact functional text remain inconsistent + +Current source still contains multiple `h-8`, `h-9`, `h-10`, 9-11px functional +labels, and custom controls below the repository’s 44px interactive-target rule. +Examples occur in topbar, mobile drawer, voice controls, chat, AI Harness, +presence, conflict resolution, and shared primitives. + +Browser measurement reproduced the problem. At 390px, all 17 measured Graph +controls and all 17 measured Mind Map controls were below 44px on at least one +axis; Chat was 24/24. At 1440px, Graph and Mind Map were each 23/23. The mobile +menu/search controls measured 36×36px. Shared primitives currently encode this +constraint with `h-9 w-9` defaults. + +#### F12. Tablet and mobile behavior is weakly specified and weakly tested + +The shell, drawer, and responsive breakpoints are improved, but tests do not +prove overflow, content priority, target size, focus order, 200% text zoom, or +orientation changes. Playwright has only a Desktop Chrome project; viewport +tests are manual resize scenarios rather than device projects. + +Fresh browser checks found no document-level overflow at 390, 768, 1024, or +1440px. The earlier three-pane-at-1024px finding is stale: the right panel now +waits for the 1100px `wide` breakpoint, leaving a measured 776px main area at +1024px. Remaining concerns are dense topbar controls, aggressive mobile text +truncation, coarse-pointer targets, and the absence of device, touch, +orientation, zoom, and reflow assertions. + +#### F13. Product completeness claims remain stale + +Examples: + +- The offline indicator promises later synchronization, but no durable queue is + implemented. +- AI Harness “Re-sync” emits success although prompt context is already rebuilt + directly from current arrays. +- Claims can be created but do not have a complete edit/delete/provenance flow. +- “Graph snapshot” stores one view-state bookmark, not data revisions or diffing. +- README uses obsolete package-manager/provider/architecture information. +- `plans/GOAL.md` and `plans/PHASES.md` retain retired SQLite, OPFS, Orama, + Tiptap, CLI, and embeddings claims as current completion. + +#### F14. Overlay and composite-widget accessibility is incomplete + +The mobile drawer, command palette, shortcuts dialog, editor fields, library +rows, and graph nodes have materially improved semantics since the prior audit. +Two current gaps remain: + +- the encrypted export overlay lacks complete dialog semantics, initial focus, + Escape handling, focus containment/restoration, and explicit label/input + relationships; +- the mind map gives every node `tabIndex={0}` without a containing ARIA tree, + roving tabindex, or complete Up/Down/Home/End tree navigation. + +**Remediation**: move export overlays to one tested dialog primitive and +implement the standard ARIA tree keyboard model for the mind map. + +### P3 — Maintainability and polish + +- Graph and mind-map disabled history buttons say “coming soon” even when the + actual condition is only “nothing to undo/redo.” +- Agent documentation references scripts and commands that do not exist. +- Skill setup supports fewer client surfaces than the manifest and docs claim. +- Project-specific skills conflict with current architecture: for example, + local-chat guidance requires SQLite although the canonical store is Zustand. +- Skill families overlap significantly, increasing routing ambiguity. +- The root instruction file is substantially longer than its own progressive- + disclosure guidance. + +## Coverage Map + +| Subsystem | Current evidence | Important missing proof | +|---|---|---| +| Store CRUD/history | Strong unit coverage | dangling-link cleanup; invalid hydration | +| Persistence | Basic localStorage integration | migrations, corruption recovery, rollback | +| Export | Strong helper tests | strict import preview/replace/rollback E2E | +| Search | BM25 unit tests | desktop/mobile parity; large-library budgets | +| AI providers | adapter/context/research tests; providers only 10% lines | credential restart semantics; UI failure paths | +| Chat | store timer/response tests | honest-mode UI and optional provider integration | +| Graph/mind map | graph index and basic E2E | large graph, keyboard model, snapshot semantics | +| Sync | isolated bridge/merge/type tests; 38.5% lines | two-peer lifecycle, deletes, conflict choices | +| PWA/offline | indicator unit test | navigation fallback and reconnect behavior | +| Accessibility | selected semantic E2E tests | axe, screen reader, zoom/reflow, target sizes | +| Responsive UI | five scenarios among 58 Desktop Chrome tests | non-conditional assertions and device coverage | +| Agent harness | shell validators | manifest conformance and executable evals | +| CI/security | unit/build/coverage jobs; no E2E job | fail-closed findings, E2E, warnings-as-errors | + +## Verified Improvements Since the Prior UI Audit + +These improvements should be preserved and must not be reopened as current +defects without new evidence: + +- Library list rows expose keyboard-operable link semantics and names. +- Graph nodes expose role, name, focus, and Enter/Space handling. +- Editor name/description/content fields have persistent labels and visible + focus treatment; editor modes expose radio state. +- Graph positions are deterministic, edge lookup is indexed, and PNG export plus + store-backed undo/redo are implemented. +- The right panel waits for the 1100px breakpoint instead of crowding 1024px. +- Light-theme faint text contrast was improved from the value recorded in the + 2026-07-11 audit. +- Home now puts recent work before compact statistics rather than leading with + four oversized metric cards. + +Remaining UI work is therefore concentrated in touch sizing, functional text +size, honest product language, encrypted-export dialogs, mind-map tree behavior, +view-specific loading feedback, and fresh WCAG verification. + +## GOAP Actions + +### G1 — Protect canonical local data + +**Priority**: P0 +**Preconditions**: ADR 028 accepted + +| ID | Action | Verification | +|---|---|---| +| G1.1 | Validate persisted state with a versioned Zod schema | malformed and old-version hydration tests | +| G1.2 | Replace shallow import guards with strict payload parsing | invalid field, reference, and version tests | +| G1.3 | Add import preview, explicit replace confirmation, and rollback snapshot | export/import/rollback E2E | +| G1.4 | Remove dangling links during entity deletion | atomic history and persistence tests | +| G1.5 | Decide and implement credential retention semantics | reload and new-session tests | + +**Exit criteria**: + +- [ ] Invalid imports cannot modify canonical state. +- [ ] A user can recover the exact pre-import state after replacement failure. +- [ ] Invalid persisted data is rejected or recovered without a crash. +- [ ] Entity deletion leaves no dangling claims or links. + +### G2 — Complete synchronization semantics + +**Priority**: P0 +**Preconditions**: ADR 027 accepted; G1 validation boundary available + +| ID | Action | Verification | +|---|---|---| +| G2.1 | Add one lifecycle-owned bidirectional Zustand/Yjs bridge | two independent store/doc integration test | +| G2.2 | Apply manual conflict selections before dismissal | conflict field-value assertions | +| G2.3 | Add deletion tombstones or equivalent versioned delete semantics | edit-versus-delete convergence tests | +| G2.4 | Validate all Yjs data before canonical import | malformed peer update test | +| G2.5 | Correct offline and sync status copy | UI assertions for connected/offline/retry states | + +**Exit criteria**: + +- [ ] Create, update, and delete converge in both directions. +- [ ] Reload preserves the converged state. +- [ ] Manual resolution changes the chosen canonical fields. +- [ ] No sync UI reports success before canonical commit succeeds. + +### G3 — Make the product surface honest + +**Priority**: P1 +**Preconditions**: G1/G2 behavior contracts known + +| ID | Action | Verification | +|---|---|---| +| G3.1 | Rename lexical search and implement desktop/mobile parity | shared result-order tests | +| G3.2 | Relabel Chat as local Q&A or add explicit provider opt-in | mode-specific UI/integration tests | +| G3.3 | Remove no-op “Re-sync” and false-success controls | no inert primary controls in source/browser audit | +| G3.4 | Rename graph view bookmark or implement true revisions/diff | semantics-specific tests | +| G3.5 | Complete claim update/delete/provenance workflow | CRUD persistence tests | + +### G4 — Make the agent harness executable + +**Priority**: P1 +**Preconditions**: ADR 029 accepted + +| ID | Action | Verification | +|---|---|---| +| G4.1 | Repair broken tracked skill symlinks | `find .agents -xtype l` returns none | +| G4.2 | Make validation manifest-driven | fixture tests for every invalid condition | +| G4.3 | Validate frontmatter, eval JSON, links, and target surfaces | validation fails on seeded defects | +| G4.4 | Align setup behavior with declared clients | clean checkout setup/validate test | +| G4.5 | Remove or repair dead commands in agent docs | repository-relative command-link check | +| G4.6 | Align project-specific skills with Zustand/localStorage | skill evals reject SQLite/backend guidance | + +### G5 — Verify and harden UI/UX + +**Priority**: P2 +**Preconditions**: G3 labels and controls settled + +| ID | Action | Verification | +|---|---|---| +| G5.1 | Enforce 44px coarse-pointer targets in shared controls and outliers | browser target-size audit | +| G5.2 | Establish readable minimum type sizes and contrast-safe text tokens | both-theme contrast report | +| G5.3 | Move encrypted export/reset/delete overlays to one complete dialog contract | keyboard and focus-restoration E2E | +| G5.4 | Validate mobile/tablet/desktop information priority and overflow | 320, 390, 768, 1024, 1280, 1440px evidence | +| G5.5 | Validate keyboard, screen reader, 200% text zoom, and 400% reflow | WCAG 2.2 AA audit | +| G5.6 | Profile editor typing and 1,000-node graph interaction | recorded performance budgets | +| G5.7 | Implement ARIA tree semantics and roving focus for mind map | complete tree keyboard-model E2E | +| G5.8 | Add accessible graph list and larger node hit regions | mobile keyboard/touch E2E | + +### G6 — Turn quality policy into enforceable gates + +**Priority**: P1 +**Preconditions**: G2/G4 tests define critical paths + +| ID | Action | Verification | +|---|---|---| +| G6.1 | Add Playwright to CI and retain traces on failure | required CI job | +| G6.2 | Fix the current 57/58 Playwright baseline and replace conditional/no-op assertions | mutation or deliberate-break proof | +| G6.3 | Add axe scanning plus manual-audit checklist | accessibility artifact | +| G6.4 | Raise coverage incrementally around critical modules | per-module targets before global increase | +| G6.5 | Fail on React/Yjs/tool warnings and make security policy fail closed | seeded warning/finding workflow test | +| G6.6 | Validate staged/PR diffs explicitly | staged, untracked, base/head fixture tests | + +Suggested coverage progression after critical tests land: + +1. Sync, import, store, persistence, and export modules: 80% lines/branches. +2. AI/search and core UI state modules: 70% lines/60% branches. +3. Global floor: raise from 15/10 to 50% lines/functions/statements and 40% + branches, then ratchet without lowering. + +### G7 — Reconcile instructions, plans, and product docs + +**Priority**: P2 +**Preconditions**: G1-G6 behavior and gates complete + +| ID | Action | Verification | +|---|---|---| +| G7.1 | Replace historical “all complete” claims with a current feature matrix | matrix links to source/tests | +| G7.2 | Mark retired Vite/SQLite plans as historical | no current architecture ambiguity | +| G7.3 | Repair pnpm, provider, view-count, persistence, and release docs | documentation command check | +| G7.4 | Reduce root instructions via progressive disclosure | canonical rules remain discoverable | +| G7.5 | Re-run code, harness, test, and browser audits | closeout report with fresh evidence | + +## New Feature Opportunities + +These are not defect remediation and should start only after G1-G3: + +1. Safe import merge-by-ID with duplicate/conflict preview. +2. Local hybrid BM25/vector retrieval with an optional downloadable model. +3. Explicit provider-backed Chat mode with local retrieval citations. +4. Named graph-data revisions with restore and diff. +5. Mind-map reparenting, ordering, drag/drop, and cycle prevention. +6. Markdown and encrypted-archive restore paths. +7. Durable offline operation queue after sync semantics are complete. + +## Execution Waves + +### Wave 0 — Decisions and truth (parallel) + +- Accept or revise ADRs 027-029. +- Correct only the highest-risk false-success copy while fixes are in flight. +- Establish fresh test, coverage, browser, and harness baselines. + +### Wave 1 — Data safety and synchronization + +- Execute G1 and G2 sequentially at the canonical-state boundary. +- Do not add sync queueing or new collaboration UX before convergence tests pass. + +### Wave 2 — Harness and CI integrity (parallel with Wave 1) + +- Execute G4, then G6. +- A green validator must mean the declared artifact is actually usable. + +### Wave 3 — Product and UI trust + +- Execute G3, then G5. +- Browser-check every changed workflow at mobile, tablet, and desktop sizes. + +### Wave 4 — Documentation closeout + +- Execute G7 only from verified current behavior. +- Update `plans/INDEX.md`, `plans/PHASES.md`, README, and root instructions without + copying unchecked historical claims. + +## Global Quality Gates + +For each implementation wave: + +```bash +pnpm run lint +pnpm run typecheck +pnpm run test +pnpm run test:coverage +pnpm run build +pnpm run test:e2e +./scripts/quality_gate.sh +``` + +Additionally: + +- Run agent-surface validation with negative fixtures after G4. +- Run a structured code review after CI passes and before merge. +- Treat warnings and skipped/conditional critical assertions as failures. +- Record any unavoidable blocker as a new scoped plan; do not mark this plan + complete based only on source implementation. + +## Completion Criteria + +- [ ] All P0 findings are resolved with regression tests. +- [ ] All P1 findings are resolved or superseded by an accepted ADR. +- [ ] Critical local data paths reject invalid external state. +- [ ] Sync converges for create, update, delete, reload, and manual conflicts. +- [ ] Skill/harness validation fails on broken symlinks and invalid manifests. +- [ ] Security and warning gates cannot summarize known findings as success. +- [ ] Playwright runs in CI with meaningful mobile/tablet/desktop assertions. +- [ ] Fresh accessibility evidence covers keyboard, semantics, contrast, zoom, + reflow, and target size. +- [ ] Product copy and plans describe only behavior verified in current source. +- [ ] `plans/INDEX.md` is updated with measured, fresh closeout metrics. diff --git a/plans/072-goap-plan071-remediation.md b/plans/072-goap-plan071-remediation.md new file mode 100644 index 00000000..8e6fa1e2 --- /dev/null +++ b/plans/072-goap-plan071-remediation.md @@ -0,0 +1,529 @@ +# Plan 072 — GOAP Remediation of Plan 071 Findings + +**Date**: 2026-07-24 +**Status**: OPEN +**Method**: GOAP with swarm parallel execution +**Source**: Plan 071 (071-goap-codebase-gap-audit-2026-07-19.md) +**Constraint**: preserve the local-first Next.js baseline from ADR 018 + +## 1. Objective + +Remediate all 14 findings from plan 071 across P0–P3, accepting three proposed +ADRs (027, 028, 029) and restoring a trustworthy relationship between product +behavior, documentation, and quality gates. + +## 2. Goal Graph + +```text +G1 Data integrity (P0) ──┬──> G3 Honest product surface (P1) ──> G5 UI/UX verification (P2) + │ +G2 Sync correctness (P0) ┘ + +G4 Harness integrity (P1) ──> G6 Reliable quality gates (P1) ──> G7 Documentation truth (P2) +``` + +## 3. Wave Structure + +### Wave 0 — Decisions and Baseline (parallel, no dependencies) + +**Goal**: Accept ADRs, fix broken symlinks, establish fresh baselines. + +| ID | Action | Finding | Agent | Files | +|----|--------|---------|-------|-------| +| W0.1 | Accept ADR 027 | F1 | plan-agent | `plans/ADRs/027-*.md` (status → Accepted) | +| W0.2 | Accept ADR 028 | F2, F3 | plan-agent | `plans/ADRs/028-*.md` (status → Accepted) | +| W0.3 | Accept ADR 029 | F7 | plan-agent | `plans/ADRs/029-*.md` (status → Accepted) | +| W0.4 | Fix broken skill symlinks | F7 | fix-agent | `.agents/skills/memory-context/memory-context`, `.agents/skills/test-runner/test-runner` | +| W0.5 | Baseline: run full quality suite | — | baseline-agent | Generate `/tmp/baseline-072.txt` | +| W0.6 | Baseline: coverage snapshot | F10 | baseline-agent | Record current thresholds from `vitest.config.ts` | + +**Parallelization**: All W0 tasks are independent. Spawn 3 agents: +- Agent A: W0.1 + W0.2 + W0.3 (ADR acceptance, sequential file edits) +- Agent B: W0.4 (symlink repair) +- Agent C: W0.5 + W0.6 (baseline capture) + +**Quality Gate 0**: +- `find .agents -xtype l` returns empty +- ADR status fields read "Accepted" +- Baseline file exists with lint/typecheck/test/build/e2e results + +--- + +### Wave 1 — Data Integrity (P0, sequential within goal, parallel across goals) + +**Preconditions**: ADR 028 accepted (W0.2) + +#### G1 — Protect Canonical Local Data + +| ID | Action | Finding | Agent | Files | +|----|--------|---------|-------|-------| +| G1.1 | Define persisted-state envelope schema with version field | F3 | schema-agent | `src/lib/studio/schema.ts` | +| G1.2 | Replace no-op `migrate` with versioned Zod validation in store hydration | F3 | store-agent | `src/lib/studio/store.ts` (lines 286–290) | +| G1.3 | Replace shallow `isEntity`/`isClaim` guards with Zod parsing in import | F2 | import-agent | `src/components/studio/views/export-helpers.ts` (lines 434–471) | +| G1.4 | Add import preview step: show entity/claim counts, version, conflicts | F2 | import-agent | `src/components/studio/views/export-view.tsx` | +| G1.5 | Add pre-import snapshot + atomic replacement + rollback action | F2 | import-agent | `src/lib/studio/store.ts` (new `importWithRollback` action) | +| G1.6 | Fix `deleteEntity` to remove incoming links from other entities | F4 | store-agent | `src/lib/studio/store.ts` (lines 189–197) | +| G1.7 | Implement credential retention: session-only storage, honest UI labeling | F5 | ai-agent | `src/lib/studio/ai-settings.ts` | + +**Dependency chain**: G1.1 → G1.2 → G1.3 → G1.4 → G1.5 (sequential, schema must exist before consumers) +**Parallel**: G1.6 and G1.7 are independent of G1.1–G1.5 + +**Agent assignments**: +- **schema-agent** (general): G1.1 — extend `src/lib/studio/schema.ts` with `PersistedEnvelopeSchema` +- **store-agent** (general): G1.2, G1.6 — modify `src/lib/studio/store.ts` hydration and deletion +- **import-agent** (general): G1.3, G1.4, G1.5 — rewrite import path in export-helpers + export-view +- **ai-agent** (general): G1.7 — fix credential lifecycle in ai-settings.ts + +**Spawn plan**: +``` +Phase 1a (parallel): + - schema-agent → G1.1 + - store-agent → G1.6 (independent) + - ai-agent → G1.7 (independent) + +Phase 1b (after G1.1 completes): + - store-agent → G1.2 (needs schema) + - import-agent → G1.3 → G1.4 → G1.5 (sequential chain) +``` + +**Test requirements**: +- `src/lib/studio/schema.test.ts`: envelope validation, version rejection, migration chain +- `src/lib/studio/store.test.ts`: dangling-link cleanup, invalid hydration rejection, rollback +- `src/components/studio/views/export-helpers.test.ts`: Zod-based import validation, invalid field/referential integrity rejection +- `src/components/studio/views/export-view.test.tsx`: preview counts, rollback after failure + +**Quality Gate 1**: +```bash +pnpm run lint +pnpm run typecheck +pnpm run test +pnpm run build +``` +- Invalid imports leave canonical state unchanged (test proof) +- Entity deletion leaves no dangling links (test proof) +- Invalid localStorage hydration rejects or recovers (test proof) + +--- + +### Wave 2 — Harness and CI Integrity (parallel with Wave 1) + +**Preconditions**: ADR 029 accepted (W0.3) + +| ID | Action | Finding | Agent | Files | +|----|--------|---------|-------|-------| +| G4.1 | Make validation manifest-driven: read `.agents/manifest.json` | F7 | harness-agent | `scripts/agent-surface.py` | +| G4.2 | Add symlink, frontmatter, name/directory, eval JSON validation | F7 | harness-agent | `scripts/agent-surface.py`, `scripts/validate-skills.sh` | +| G4.3 | Add negative fixture tests for each failure condition | F7 | harness-test-agent | `tests/agent-surface.test.ts` or `scripts/test-agent-surface.sh` | +| G4.4 | Remove or repair dead commands in agent docs | F7 | docs-agent | `agents-docs/` files | +| G4.5 | Retire or restore version-propagation workflow | F8 | ci-agent | `.github/workflows/version-propagation.yml`, `VERSION`, `CHANGELOG.md`, `scripts/propagate-version.sh` | +| G4.6 | Make security scanners fail-closed | F9 | ci-agent | `.github/workflows/security.yml` | +| G4.7 | Enforce warnings-as-errors in quality scripts | F9 | ci-agent | `scripts/quality_gate.sh` | + +**Dependency chain**: G4.1 → G4.2 → G4.3 (sequential) +**Parallel**: G4.4, G4.5, G4.6, G4.7 are independent + +**Agent assignments**: +- **harness-agent** (general): G4.1, G4.2 — rewrite validation in agent-surface.py +- **harness-test-agent** (general): G4.3 — create negative fixtures +- **docs-agent** (general): G4.4 — audit and repair agent-docs +- **ci-agent** (general): G4.5, G4.6, G4.7 — fix workflows and quality scripts + +**Spawn plan**: +``` +Phase 2a (parallel): + - harness-agent → G4.1 → G4.2 (sequential) + - docs-agent → G4.4 + - ci-agent → G4.5 + G4.6 + G4.7 (sequential within) + +Phase 2b (after G4.2 completes): + - harness-test-agent → G4.3 +``` + +**Test requirements**: +- Negative fixtures: broken symlink, missing frontmatter, name mismatch, malformed eval, missing target surface +- `validate-skills.sh` returns non-zero on seeded defects +- Quality gate script captures output and enforces warning count +- Security workflow fails when scanners find issues + +**Quality Gate 2**: +```bash +./scripts/validate-skills.sh # must pass with clean surface +pnpm run lint +pnpm run typecheck +pnpm run test +pnpm run build +``` +- `find .agents -xtype l` returns empty +- Agent-surface validation fails on introduced broken symlink (negative test) + +--- + +### Wave 3 — Honest Product Surface (P1, after Wave 1) + +**Preconditions**: G1 validation boundary available (Wave 1 complete) + +| ID | Action | Finding | Agent | Files | +|----|--------|---------|-------|-------| +| G3.1 | Rename "Semantic" search to "Keyword" / "Ranked" | F6 | label-agent | `src/components/studio/views/search-view.tsx`, related UI | +| G3.2 | Wire mobile search to use same BM25 ranking as desktop | F6 | search-agent | `src/components/studio/views/search-view.tsx` | +| G3.3 | Relabel Chat as "Local Library Q&A" | F6 | label-agent | `src/components/studio/views/chat-view.tsx` | +| G3.4 | Remove no-op "Re-sync" control from AI Harness | F13 | cleanup-agent | `src/components/studio/views/ai-harness-view.tsx` | +| G3.5 | Rename "Graph Snapshot" to "View Bookmark" or implement revisions | F13 | label-agent | graph-related views | +| G3.6 | Complete claim edit/delete/provenance workflow | F13 | claim-agent | `src/lib/studio/store.ts`, editor views | +| G3.7 | Fix offline indicator: remove sync promise or implement queue | F13 | cleanup-agent | PWA/offline components | + +**Parallelization**: G3.1, G3.2, G3.3 are independent label/search fixes. G3.4, G3.5, G3.7 are independent cleanup. G3.6 is the largest task. + +**Agent assignments**: +- **label-agent** (general): G3.1, G3.3, G3.5 — rename labels across views +- **search-agent** (general): G3.2 — wire mobile BM25 ranking +- **cleanup-agent** (general): G3.4, G3.7 — remove false-success controls +- **claim-agent** (general): G3.6 — implement claim CRUD + +**Spawn plan**: +``` +Phase 3a (parallel): + - label-agent → G3.1 + G3.3 + G3.5 + - search-agent → G3.2 + - cleanup-agent → G3.4 + G3.7 + - claim-agent → G3.6 +``` + +**Test requirements**: +- Search mode labels match implementation in both desktop and mobile +- Chat view shows "Local Library Q&A" or similar honest label +- No inert "Re-sync" button renders +- Claim CRUD: create, update, delete persist correctly + +**Quality Gate 3**: +```bash +pnpm run lint +pnpm run typecheck +pnpm run test +pnpm run build +pnpm run test:e2e +``` + +--- + +### Wave 4 — UI/UX Hardening (P2, after Wave 3) + +**Preconditions**: G3 labels and controls settled + +| ID | Action | Finding | Agent | Files | +|----|--------|---------|-------|-------| +| G5.1 | Enforce 44px minimum interactive targets | F11 | touch-agent | Shared primitives, topbar, drawer, chat, graph, mindmap controls | +| G5.2 | Establish readable minimum type sizes (≥12px functional text) | F11 | type-agent | CSS tokens, component overrides | +| G5.3 | Move encrypted export overlay to complete dialog primitive | F14 | dialog-agent | `src/components/studio/views/export-view.tsx`, `src/lib/export/encrypt.ts` | +| G5.4 | Implement ARIA tree + roving tabindex for mind map | F14 | a11y-agent | Mind map component | +| G5.5 | Add Playwright device projects (mobile, tablet) | F12 | test-agent | `playwright.config.ts` | +| G5.6 | Fix conditional/no-op assertions in E2E tests | F10 | test-agent | `e2e/*.spec.ts` | +| G5.7 | Raise coverage thresholds incrementally | F10 | test-agent | `vitest.config.ts` | + +**Parallelization**: G5.1–G5.4 are independent UI tasks. G5.5–G5.7 are test infrastructure. + +**Agent assignments**: +- **touch-agent** (general): G5.1 — audit and fix all controls below 44px +- **type-agent** (general): G5.2 — fix functional text below 12px +- **dialog-agent** (general): G5.3 — build accessible dialog for export overlays +- **a11y-agent** (general): G5.4 — implement ARIA tree model for mind map +- **test-agent** (general): G5.5, G5.6, G5.7 — fix E2E infrastructure and coverage + +**Spawn plan**: +``` +Phase 4a (parallel): + - touch-agent → G5.1 + - type-agent → G5.2 + - dialog-agent → G5.3 + - a11y-agent → G5.4 + - test-agent → G5.5 + G5.6 + G5.7 +``` + +**Test requirements**: +- Browser audit: no interactive control below 44px at 390px, 768px, 1440px +- No functional text below 12px in either theme +- Encrypted export dialog: focus trap, Escape, label/input relationships +- Mind map: Up/Down/Home/End keyboard navigation within ARIA tree +- Playwright: mobile (390×844) and tablet (768×1024) projects exist and pass +- Coverage thresholds: lines ≥ 30%, statements ≥ 30%, functions ≥ 25%, branches ≥ 20% + +**Quality Gate 4**: +```bash +pnpm run lint +pnpm run typecheck +pnpm run test +pnpm run test:coverage +pnpm run build +pnpm run test:e2e +``` + +--- + +### Wave 5 — Documentation Truth and Closeout (P2, after Wave 4) + +**Preconditions**: G1–G6 behavior and gates complete + +| ID | Action | Finding | Agent | Files | +|----|--------|---------|-------|-------| +| G7.1 | Replace "all complete" claims in INDEX.md with current feature matrix | F13 | docs-agent | `plans/INDEX.md` | +| G7.2 | Mark retired Vite/SQLite plans as historical | F13 | docs-agent | `plans/PHASES.md`, `plans/GOAL.md` | +| G7.3 | Repair README: pnpm, provider, persistence, architecture docs | F13 | docs-agent | `README.md` | +| G7.4 | Update agent docs: remove references to nonexistent scripts | F7 | docs-agent | `agents-docs/` | +| G7.5 | Run closeout audit: fresh evidence for all verification criteria | — | audit-agent | `plans/072-closeout-report.md` | + +**Agent assignments**: +- **docs-agent** (general): G7.1–G7.4 — documentation reconciliation +- **audit-agent** (general): G7.5 — final verification + +**Quality Gate 5 (Final)**: +```bash +pnpm run lint +pnpm run typecheck +pnpm run test +pnpm run test:coverage +pnpm run build +pnpm run test:e2e +./scripts/quality_gate.sh +./scripts/validate-skills.sh +``` + +--- + +## 4. Wave Dependency Graph + +```text +Wave 0 (parallel) ──> Wave 1 (P0 data) ──> Wave 3 (P1 labels) ──> Wave 4 (P2 UI) ──> Wave 5 (docs) + │ │ + └──────────────────> Wave 2 (P1 harness) ──────────────────────────────────────┘ +``` + +- Wave 0: All tasks parallel +- Wave 1 and Wave 2: Parallel with each other (no cross-dependency) +- Wave 3: Depends on Wave 1 (needs validation boundary) +- Wave 4: Depends on Wave 3 (needs settled labels) +- Wave 5: Depends on Wave 4 (needs all behavior finalized) +- Wave 2 feeds into Wave 5 (harness must be complete before closeout) + +## 5. Agent Swarm Summary + +| Wave | Agents | Max Parallel | Estimated Effort | +|------|--------|-------------|------------------| +| W0 | 3 | 3 | 30min | +| W1 | 4 | 3 (phase 1a), 2 (phase 1b) | 4–6h | +| W2 | 4 | 3 (phase 2a), 1 (phase 2b) | 3–4h | +| W3 | 4 | 4 | 2–3h | +| W4 | 5 | 5 | 4–6h | +| W5 | 2 | 2 | 1–2h | +| **Total** | — | — | **15–22h** | + +## 6. PR Strategy + +**Single PR** is preferred because: +- Findings are interconnected (e.g., import validation feeds sync, labels depend on search behavior) +- ADRs must land together with their implementations +- Quality gates are cumulative +- Review is easier with one coherent changeset + +**Branch**: `feat/072-plan071-remediation` + +**PR title**: `feat: remediate plan 071 audit findings — data integrity, sync, harness, UI/UX` + +**PR body structure**: +```markdown +## Summary +Remediates all 14 findings from plan 071 (P0–P3) across data integrity, +synchronization, agent harness, product honesty, and UI/UX. + +## ADRs Accepted +- ADR 027: Canonical State and P2P Sync Bridge +- ADR 028: Validated and Recoverable Local Data Boundaries +- ADR 029: Manifest-Driven Agent Harness + +## Changes by Goal +### G1 — Data Integrity (P0) +- Validated Zod hydration with versioned envelope +- Strict Zod import validation replacing shallow guards +- Import preview + atomic replacement + rollback +- Entity deletion removes incoming links +- Session-only credential storage + +### G2 — Sync Correctness (P0) +- [Deferred to follow-up: bidirectional bridge requires G1 schema first] +- Note: G2 (bidirectional sync, tombstones, conflict application) is the + largest single task and should be a dedicated follow-up PR after G1 lands. + This PR establishes the validation boundary that G2 depends on. + +### G3 — Product Honesty (P1) +- Search renamed from "Semantic" to "Keyword" +- Chat relabeled as "Local Library Q&A" +- False-success controls removed + +### G4 — Harness Integrity (P1) +- Manifest-driven validation +- Broken symlinks fixed and fail validation +- Dead agent doc commands removed + +### G5 — UI/UX (P2) +- 44px minimum interactive targets +- Complete dialog for encrypted export +- ARIA tree for mind map +- Playwright device projects + +### G6 — Quality Gates (P1) +- Coverage thresholds raised +- Warnings-as-errors enforced +- Security scanners fail-closed + +### G7 — Documentation (P2) +- Feature matrix replaces stale claims +- Retired plans marked historical +``` + +## 7. Detailed File Changes + +### `src/lib/studio/schema.ts` (G1.1) +- Add `PersistedEnvelopeSchema` with version, entities, claims, metadata +- Add `CURRENT_SCHEMA_VERSION` constant +- Add migration registry type +- Export `validatePersistedState()`, `validateImportPayload()`, `migrateToCurrent()` + +### `src/lib/studio/store.ts` (G1.2, G1.5, G1.6) +- Replace `migrate: (persistedState: unknown) => persistedState as unknown` with versioned Zod validation +- Add `importWithRollback(entities, claims)` action: snapshot → validate → replace → persist → expose undo +- Fix `deleteEntity`: remove links in other entities where `targetId === deletedId` +- Add `deleteClaim(id)` action for G3.6 + +### `src/components/studio/views/export-helpers.ts` (G1.3) +- Replace `isEntity()` and `isClaim()` shallow guards with Zod-based `parseAndValidateImport()` +- Return structured result: `{ success: true, data } | { success: false, errors: ValidationError[] }` +- Remove silent filtering of invalid records + +### `src/components/studio/views/export-view.tsx` (G1.4, G1.5, G5.3) +- Add import preview step: show counts, version, detected conflicts before replacement +- Use `importWithRollback` from store instead of raw `importData` +- Move encrypted export/reset/delete overlays to a single `` primitive with focus trap, Escape, label relationships + +### `src/lib/studio/ai-settings.ts` (G1.7) +- Keep `sessionStorage` for encryption key (current behavior is correct) +- Add UI labeling: "API key is stored for this browser session only" +- Add `isSessionOnly` flag to settings UI +- Remove any implication of persistence across sessions + +### `src/lib/search/retrieval.ts` + search views (G3.1, G3.2) +- No engine changes needed (BM25 is correct) +- Rename UI labels: "Semantic" → "Keyword", add "Ranked" mode option +- Wire mobile search to use `search()` function (currently renders filtered list) + +### `src/components/studio/views/chat-view.tsx` (G3.3) +- Rename header/label from "Chat" to "Local Library Q&A" +- Update empty state and placeholder text to reflect local retrieval behavior + +### `scripts/agent-surface.py` (G4.1, G4.2) +- Read `.agents/manifest.json` for supported surfaces +- Add validation: broken symlinks, frontmatter, name/directory agreement, eval JSON +- Return non-zero on any failure with stable diagnostics + +### `scripts/validate-skills.sh` (G4.2) +- Call updated agent-surface.py with manifest mode +- Add negative fixture test section + +### `.github/workflows/version-propagation.yml` (G4.5) +- Option A (recommended): Remove workflow + retire VERSION/CHANGELOG references from AGENTS.md +- Option B: Restore VERSION file, CHANGELOG.md, scripts/propagate-version.sh + +### `scripts/quality_gate.sh` (G4.7) +- Capture command output (already does this) +- Enforce `MAX_ALLOWED_WARNINGS=0` for lint, typecheck +- Fail if any warning pattern detected in output + +### `vitest.config.ts` (G5.7) +- Raise thresholds: lines 30%, statements 30%, functions 25%, branches 20% +- After Wave 1 tests land, target: lines 40%, statements 40%, functions 35%, branches 30% + +### `playwright.config.ts` (G5.5) +- Add mobile project: iPhone 13 (390×844) +- Add tablet project: iPad (768×1024) +- Enable retries: `{ retries: 1 }` +- Enable trace: `'on-first-retry'` + +### Shared UI primitives (G5.1, G5.2) +- Update `h-8`, `h-9`, `h-10` defaults to `min-h-[44px]` in shared Button, IconButton, ToolbarBtn +- Audit and fix: topbar, mobile drawer, voice controls, chat input, AI harness controls, presence indicators, conflict resolution buttons +- Set minimum functional text to 12px (14px for body) + +### Mind map component (G5.4) +- Wrap nodes in `role="tree"` container +- Implement roving `tabIndex` (only active node is tabbable) +- Add Up/Down/Home/End keyboard handlers +- Add `role="treeitem"` with `aria-selected` on focused node + +### Documentation (G7.1–G7.4) +- `plans/INDEX.md`: Replace "No open tasks remaining" with current status +- `plans/PHASES.md`: Mark SQLite/OPFS/Orama/Tiptap claims as historical +- `plans/GOAL.md`: Update to reflect current architecture +- `README.md`: Verify pnpm, Next.js 16, Zustand, localStorage claims +- `agents-docs/`: Remove references to nonexistent scripts + +## 8. Test Requirements Matrix + +| Goal | New/Updated Tests | Coverage Target | +|------|-------------------|-----------------| +| G1.1 | `schema.test.ts`: envelope version validation, migration chain | 90% lines | +| G1.2 | `store.test.ts`: invalid hydration rejection, recovery options | 80% lines | +| G1.3–G1.5 | `export-helpers.test.ts`: Zod import, preview, rollback | 85% lines | +| G1.6 | `store.test.ts`: deletion removes incoming links | 90% lines | +| G1.7 | `ai-settings.test.ts`: session-only semantics | 70% lines | +| G3.1–G3.3 | E2E: search mode labels, chat label | — | +| G3.6 | `store.test.ts`: claim CRUD | 80% lines | +| G4.1–G4.3 | `agent-surface.test.sh`: negative fixtures | — | +| G5.1 | Browser audit: target sizes at 3 viewports | — | +| G5.3 | E2E: dialog focus trap, Escape, labels | — | +| G5.4 | E2E: mind map tree keyboard model | — | +| G5.5 | Playwright config: device projects exist | — | + +## 9. Risk Register + +| Risk | Mitigation | +|------|-----------| +| G2 (bidirectional sync) too large for this PR | Defer to follow-up PR; this PR establishes validation boundary | +| Import rollback interacts with localStorage quota | Test with `structuredClone` + quota exceeded simulation | +| Coverage threshold increase causes CI failure | Raise incrementally; run `test:coverage` before each threshold bump | +| Touch target fixes break existing layouts | Test at 390, 768, 1024, 1440px after each change | +| Version-propagation workflow removal breaks release | Confirm no other workflow depends on VERSION file first | + +## 10. Deferred Work (Not in This PR) + +These are explicitly out of scope for 072 and should become plan 073: + +1. **G2 — Bidirectional Sync Bridge** (ADR 027 full implementation) + - Lifecycle-owned bidirectional Zustand/Yjs adapter + - Tombstone semantics for versioned deletes + - Conflict resolution as canonical transaction + - Reason: Requires G1 validation boundary first; largest single task + +2. **New Feature Opportunities** from plan 071: + - Safe import merge-by-ID with duplicate/conflict preview + - Local hybrid BM25/vector retrieval + - Provider-backed Chat mode + - Named graph-data revisions + - Mind-map reparenting and drag/drop + +3. **Full Accessibility Audit**: + - axe scanning integration + - Screen reader verification + - 200% text zoom validation + - 400% reflow validation + +## 11. Completion Criteria + +- [ ] All P0 findings (F1, F2) resolved with regression tests +- [ ] All P1 findings (F3–F9) resolved or superseded by accepted ADR +- [ ] All P2 findings (F10–F14) resolved with evidence +- [ ] P3 polish items addressed +- [ ] `find .agents -xtype l` returns empty +- [ ] Validation fails on broken symlinks (negative test) +- [ ] Security/warning gates cannot summarize known findings as success +- [ ] Playwright runs in CI with mobile/tablet/desktop projects +- [ ] Product copy describes only verified behavior +- [ ] `plans/INDEX.md` updated with fresh closeout metrics +- [ ] All quality gates pass: lint, typecheck, test, coverage, build, e2e, quality_gate.sh, validate-skills.sh diff --git a/plans/ADRs/027-canonical-state-and-p2p-sync-bridge.md b/plans/ADRs/027-canonical-state-and-p2p-sync-bridge.md new file mode 100644 index 00000000..853c0ade --- /dev/null +++ b/plans/ADRs/027-canonical-state-and-p2p-sync-bridge.md @@ -0,0 +1,138 @@ +# ADR 027 — Canonical State and P2P Synchronization Bridge + +**Date**: 2026-07-19 +**Status**: Accepted +**Related**: ADR 018, ADR 026, Plan 071 +**Supersedes**: ADR 026 statements that make Yjs primary or Zustand read-only + +## Context + +ADR 018 establishes Zustand plus `localStorage` as the canonical local-first +persistence layer. ADR 026 later describes Yjs/IndexedDB as primary and Zustand +as a read-only view. The implementation currently follows neither complete +model: + +- normal CRUD writes to Zustand only; +- joining sync copies a snapshot from Zustand into Yjs; +- no production lifecycle subscribes Yjs changes back into Zustand; +- deletions have no tombstone/version semantics; +- manual conflict choices are dismissed without being applied. + +This split ownership creates false-success states and makes it unclear which +copy wins after reconnect, reload, or concurrent deletion. + +## Decision + +### 1. Zustand remains canonical application state + +Zustand is the only state read and mutated by product features. Its validated, +versioned persisted subset remains the local source of truth. Yjs is an opt-in +replication transport and collaboration log, not a second canonical database. + +This preserves ADR 018, avoids rewriting every feature around CRDT types, and +keeps the app fully useful when collaboration is disabled or unavailable. + +### 2. One lifecycle owner bridges canonical state and Yjs + +The sync session owns one bidirectional adapter: + +```text +validated Zustand transaction + │ + ▼ + outbound Yjs update + │ + ▼ + WebRTC / IndexedDB replica + │ + ▼ + validated inbound projection + │ + ▼ +atomic Zustand transaction +``` + +The adapter must: + +1. seed an empty sync document from canonical state on first join; +2. subscribe to canonical create/update/delete operations while connected; +3. subscribe to Yjs changes and validate them before store mutation; +4. tag transaction origins to prevent echo loops; +5. commit batches atomically so history and persistence observe one change; +6. unsubscribe and release resources when the session ends. + +### 3. Deletes are versioned operations + +Absence is not interpreted as deletion. Entities and claims use tombstones (or +an equivalent explicit delete operation) containing record ID, deletion time, +device identity, and logical/version ordering information. Tombstones are +retained long enough for offline peers to converge and are compacted only under +a separately tested retention rule. + +Concurrent edit-versus-delete behavior must be deterministic. The default is +delete-wins when the deletion is causally later; unresolved concurrent changes +are surfaced as a conflict rather than silently resurrecting data. + +### 4. Conflict resolution is a canonical transaction + +Manual choices are validated, applied to the merged record, persisted to +Zustand, and propagated to Yjs before the dialog reports success. Dismissal +without applying a choice does not mutate data or claim resolution. + +### 5. Sync status describes canonical commit state + +“Synced” means all acknowledged inbound changes passed validation and committed +to canonical state, and all local operations were submitted to the active Yjs +document. Transport connection alone is not sufficient. + +## Consequences + +### Positive + +- Local-only behavior remains simple and independent of sync infrastructure. +- Every feature observes one application state model. +- Peer data crosses a runtime-validation boundary before persistence. +- Create, update, delete, reload, and conflict behavior can be integration + tested deterministically. +- The false distinction between two primary stores is removed. + +### Negative + +- The adapter must maintain origin metadata and guard against update loops. +- Tombstones require retention and compaction policy. +- Whole-record Zustand updates cannot expose character-level collaborative + editing; that would require a future editor-specific ADR. +- IndexedDB Yjs state is a replica and must be reconciled against the canonical + persisted schema during upgrades. + +## Alternatives Considered + +1. **Make Yjs canonical and Zustand read-only.** Rejected for the current phase: + it contradicts ADR 018 and requires broad application/store migration. +2. **Keep manual snapshot synchronization.** Rejected: it cannot provide + ongoing collaboration and encourages false-success UI. +3. **Mirror writes independently in every store action.** Rejected: lifecycle, + error handling, and echo prevention would be scattered across the product. +4. **Treat absence as deletion.** Rejected: offline merging resurrects deleted + records or deletes records a peer has not received yet. + +## Implementation Requirements + +- Add a single sync-session bridge at the collaboration boundary. +- Reuse complete Zod record schemas for inbound peer validation. +- Add explicit canonical batch actions rather than mutating store internals. +- Define tombstone schema, ordering, retention, and compaction. +- Apply conflict selections before clearing conflict state. +- Do not add an offline queue claim until convergence and retry semantics are + implemented. + +## Verification + +- Two independent documents/stores converge after create, update, and delete. +- Offline peer reconnect converges without resurrection. +- Concurrent different-field edits merge as specified. +- Edit-versus-delete follows the documented deterministic rule. +- Invalid peer records never enter canonical state. +- Manual conflict selection produces the selected field values on both peers. +- Disconnect/reconnect does not duplicate observers or operations. +- Reload restores canonical state and reconnects the replica without data loss. diff --git a/plans/ADRs/028-validated-recoverable-local-data-boundaries.md b/plans/ADRs/028-validated-recoverable-local-data-boundaries.md new file mode 100644 index 00000000..7f3117b3 --- /dev/null +++ b/plans/ADRs/028-validated-recoverable-local-data-boundaries.md @@ -0,0 +1,134 @@ +# ADR 028 — Validated and Recoverable Local Data Boundaries + +**Date**: 2026-07-19 +**Status**: Accepted +**Related**: ADR 010, ADR 018, Plan 071 + +## Context + +The studio is local-first, so browser persistence and user-controlled export +files are the durability boundary. Current implementation has two unsafe paths: + +1. persisted Zustand hydration trusts unknown data through a no-op migration + cast; +2. JSON import uses shallow guards, silently filters invalid records, and then + immediately replaces canonical entities and claims. + +Complete Zod schemas already exist for core records. The missing decision is how +strictly to validate, what to do with partially invalid data, and how users +recover from destructive replacement. + +## Decision + +### 1. Validate every external state boundary + +The following inputs are untrusted and must be parsed with versioned Zod +schemas before they can mutate canonical state: + +- `localStorage` hydration; +- JSON and encrypted archive import; +- future Markdown import metadata; +- Yjs/peer projections; +- AI/provider and web-research responses where persisted. + +Type assertions and shallow record filters do not satisfy this boundary. + +### 2. Imports are atomic and fail closed + +An import payload is accepted only when its declared version is supported and +all records and references required by that version are valid. Invalid records +are not silently dropped. Parsing produces either: + +- a complete validated candidate plus a preview summary; or +- a structured error list with record paths and no state mutation. + +Best-effort salvage is a separate explicit recovery workflow, never the default +import behavior. + +### 3. Replacement requires preview and recovery + +Before replacement, the UI shows entity/claim/link counts, detected conflicts, +schema version, and the consequence that current data will be replaced. A +validated pre-import snapshot is created before canonical commit. + +Replacement is one atomic store transaction. If commit or persistence fails, +the previous snapshot is restored. The user can also explicitly undo the most +recent replacement until a later destructive operation supersedes that +recovery point. + +### 4. Hydration uses versioned migrations + +Persisted state has an explicit schema version and a migration chain. Hydration: + +1. reads the raw persisted envelope; +2. validates the envelope/version; +3. applies known migrations sequentially; +4. validates the resulting current schema; +5. hydrates canonical state atomically. + +If recovery is possible, valid data is exposed through an explicit recovery +path. Unknown future versions or unrecoverable corruption do not enter the +store. The application offers reset and raw-export options without logging +personal content. + +### 5. Referential integrity is part of validation + +- Claims must reference an imported/existing entity according to import mode. +- Entity links must target a valid entity unless the schema explicitly permits + external references. +- IDs are unique within each collection. +- dates, enums, confidence ranges, and version fields are validated. +- deletion removes or resolves dependent links and claims atomically. + +## Consequences + +### Positive + +- Malformed archives cannot silently destroy valid local data. +- Schema evolution is explicit and testable. +- Import errors can identify exact invalid paths. +- Sync and hydration can reuse the same canonical validators. +- Recovery behavior matches the importance of local-only user data. + +### Negative + +- Strict import rejects partially useful legacy files until a dedicated + recovery tool exists. +- Snapshot retention consumes temporary local storage. +- Migration and rollback tests become mandatory for every persisted schema + change. +- Import UI gains a preview/confirmation step. + +## Alternatives Considered + +1. **Continue filtering invalid records.** Rejected: silent data loss is worse + than an actionable failed import. +2. **Validate only TypeScript shapes.** Rejected: TypeScript types do not exist + at runtime. +3. **Always merge imports into current data.** Rejected as a default: ID, + deletion, and relationship conflicts need explicit semantics. +4. **Keep an automatic JSON stringify clone as backup.** Rejected: use a + validated snapshot and `structuredClone` for in-memory copies. +5. **Reset automatically on hydration failure.** Rejected: local-first data + should not disappear without an explicit recovery choice. + +## Implementation Requirements + +- Define the current persisted-state envelope schema next to canonical record + schemas. +- Reuse schemas for export, import, hydration, and sync boundaries. +- Reject unsupported versions before reading records. +- Build preview data from the validated candidate only. +- Add one atomic replacement action and one bounded recovery snapshot. +- Keep raw recovery export local; never log user content or secrets. + +## Verification + +- Invalid field, enum, date, confidence, duplicate ID, dangling relation, and + unsupported-version fixtures leave canonical state byte-for-byte unchanged. +- Valid export/import round-trip preserves all supported canonical data. +- A forced persistence failure restores the prior snapshot. +- Known old persisted versions migrate deterministically and idempotently. +- Unknown future and corrupt persisted versions present recovery choices. +- Entity deletion removes dependent claims and incoming/outgoing links in one + undoable transaction. diff --git a/plans/ADRs/029-manifest-driven-agent-harness.md b/plans/ADRs/029-manifest-driven-agent-harness.md new file mode 100644 index 00000000..c136929a --- /dev/null +++ b/plans/ADRs/029-manifest-driven-agent-harness.md @@ -0,0 +1,136 @@ +# ADR 029 — Manifest-Driven Agent Harness and Skill Validation + +**Date**: 2026-07-19 +**Status**: Accepted +**Related**: Plan 071 + +## Context + +Canonical skills live in `.agents/skills`, while `.agents/manifest.json` +declares the client surfaces intended to consume them. Current setup and +validation do not implement that model: + +- setup handles only part of the declared client set; +- validation does not read the manifest; +- broken symlinks can pass validation; +- frontmatter, skill directory/name agreement, eval JSON, and configured target + surfaces are not checked; +- agent docs advertise commands that the harness script does not implement; +- project-specific skills can contradict root architecture rules. + +The result is a green harness check that does not prove skills are discoverable +or valid. + +## Decision + +### 1. `.agents/manifest.json` is the executable source of truth + +The manifest defines: + +- canonical skills directory; +- supported client surfaces and their target paths; +- link/copy strategy per surface; +- required skill metadata fields; +- eval schema version and optionality policy; +- exclusions for clients or skills that cannot be synchronized. + +Setup, validation, and generated documentation read the same manifest. Client +surfaces absent from the manifest are not claimed as supported. + +### 2. Setup is idempotent and non-destructive + +The setup command creates or repairs only managed artifacts declared in the +manifest. It does not overwrite unrelated user-managed files. Re-running setup +on a valid checkout produces no diff. + +Broken or stale managed links are repaired deliberately. Unexpected conflicting +files cause an actionable failure rather than silent replacement. + +### 3. Validation proves usability, not file presence + +Validation fails on: + +- broken symlinks; +- missing canonical skills or declared target surfaces; +- invalid or missing required YAML frontmatter; +- skill `name` mismatch with its canonical directory; +- malformed eval JSON or unsupported eval schema; +- broken repository-relative links in skill content; +- duplicate canonical names; +- unmanaged drift in generated surfaces; +- project-specific architecture requirements that contradict designated root + constraints, where a policy fixture exists. + +The validator returns non-zero on every error and provides stable, concise +diagnostics suitable for CI. + +### 4. One implementation owns setup, validate, and docs generation + +The agent-surface tool exposes only commands it implements: + +- `sync` for managed surface creation/repair; +- `validate` for canonical and target conformance; +- `generate-docs` for derived skill catalogs, if the repository keeps them. + +Shell wrappers remain thin entry points. Documentation is generated from the +manifest and validator output rather than independently maintained inventories. + +### 5. Skill routing boundaries are explicit + +Overlapping workflow skills must state precedence and non-goals in their +descriptions. Project-specific skills inherit root architecture constraints and +must not prescribe SQLite, a backend, or undefined logging/error abstractions +unless an accepted project ADR introduces them. + +## Consequences + +### Positive + +- A passing harness gate means declared agent clients can discover valid skills. +- Setup and validation cannot silently diverge. +- Adding a skill or client requires one manifest change plus tests. +- Generated catalogs reduce root-instruction size and documentation drift. +- Negative fixtures make the quality gate resistant to false green results. + +### Negative + +- The manifest schema and validator become maintained infrastructure. +- Client-specific link behavior requires fixture coverage across platforms. +- Existing unmanaged surfaces must be classified before synchronization. +- Some overlapping skills may need consolidation or clearer routing metadata. + +## Alternatives Considered + +1. **Keep ad hoc shell setup scripts.** Rejected: setup and validation continue + to encode different supported surfaces. +2. **Validate only canonical `SKILL.md` files.** Rejected: it does not prove + discoverability from any client. +3. **Copy all skills into every client directory.** Rejected: duplication makes + drift likely and obscures canonical ownership. +4. **Remove all client surfaces except `.agents`.** Viable but rejected for now + because the repository intentionally advertises multiple agent clients. If + portability is no longer a goal, narrowing the manifest is preferred over + pretending broader support. + +## Implementation Requirements + +- Define and validate a versioned manifest schema. +- Add fixture-based tests for each failure condition. +- Repair the two current broken skill symlinks through the managed setup path. +- Align `.claude`, `.gemini`, `.qwen`, `.cursor`, `.windsurf`, `.opencode`, and + `.jules` claims with actual manifest support. +- Validate eval files where present and define which skills may omit them. +- Generate or verify repository-relative command references in agent docs. +- Keep root `AGENTS.md` focused on universal constraints and link to generated + detail. + +## Verification + +- A clean checkout can run sync then validate with no diff on a second run. +- A deliberately broken symlink fails validation. +- Invalid frontmatter, name mismatch, malformed eval JSON, missing target, and + broken link fixtures each fail with a stable diagnostic. +- Every supported client resolves each intended skill to the canonical content. +- Removing a manifest entry removes only managed artifacts. +- CI invokes validation and cannot pass when a negative fixture is introduced + into the real surface. diff --git a/plans/GOAL.md b/plans/GOAL.md index 68033791..2797cc3f 100644 --- a/plans/GOAL.md +++ b/plans/GOAL.md @@ -3,17 +3,32 @@ Build a local-first, structured knowledge engine that empowers users to capture, connect, and synthesize information without relying on cloud-based LLMs. ## Core Values -- **Local Sovereignty**: User data is stored in SQLite (OPFS) and never leaves the device. +- **Local Sovereignty**: User data is stored in Zustand + localStorage and never leaves the device. - **Structural Depth**: Moving beyond flat text to Entities, Claims, and relational Links. - **Visual Intelligence**: Multiple perspectives on the same data (Graph, Mind Map, Chat). - **Offline First**: Zero latency, zero dependency on external APIs for core functionality. -- **Security First**: All user content is sanitized before export; API keys are never bundled. +- **Security First**: All user content is sanitized before export; API keys are session-only. + +## Current Architecture (as of 2026-07-24) +- Next.js 16 / React 19 / Tailwind 4 / shadcn / Zustand +- Persistence: Zustand + localStorage (validated with Zod schemas) +- Search: BM25 keyword ranking (not semantic/vector) +- AI: OpenRouter and Ollama providers via AI Harness +- Sync: Yjs/WebRTC infrastructure (opt-in, one-way snapshot) +- Export: JSON, Markdown, HTML, PDF, DOCX, Encrypted HTML ## 2026 Goals (from GitHub Issue Analysis) -1. **Zero Security Vulnerabilities** — Fix XSS in export paths, isolate API keys from VITE_ env vars -2. **All Functional Bugs Resolved** — Fix broken nav, dead code, version inconsistencies -3. **Infrastructure Integrity** — CI timeouts, caching, proper TypeScript configs -4. **Code Quality at Scale** — 80% test coverage, strict TypeScript, consistent error handling -5. **Complete Core Features** — Entity editing, mind map editing, graph layouts, keyboard accessibility -6. **Performance for Large KBs** — Pagination, batch queries, LRU caches, lazy loading -7. **Rich Export Formats** — PNG, PDF, DOCX with shared export core +1. **Zero Security Vulnerabilities** — XSS fixes in export paths, session-only API keys ✓ +2. **All Functional Bugs Resolved** — Broken nav, dead code, version sync ✓ +3. **Infrastructure Integrity** — CI timeouts, caching, TypeScript configs ✓ +4. **Code Quality at Scale** — Test coverage, strict TypeScript, error handling ✓ +5. **Complete Core Features** — Entity editing, mind map, graph layouts ✓ +6. **Performance for Large KBs** — Pagination, batch queries, lazy loading ✓ +7. **Rich Export Formats** — PNG, PDF, DOCX with shared export core ✓ +8. **Data Integrity** — Zod validation at all boundaries (Plan 072) ✓ +9. **Honest Product Surface** — Accurate labels, no false-success controls (Plan 072) ✓ + +## Remaining Work (Plan 073) +- Bidirectional Yjs/Zustand sync bridge +- UI/UX hardening (44px targets, ARIA tree, Playwright device coverage) +- Full accessibility audit diff --git a/plans/INDEX.md b/plans/INDEX.md index b935db24..318d0093 100644 --- a/plans/INDEX.md +++ b/plans/INDEX.md @@ -1,16 +1,30 @@ # Plans Index -**Updated**: 2026-07-18 +**Updated**: 2026-07-24 **Method**: GOAP (Goal-Oriented Action Planning) with ADRs -## Remaining Work +## Current Status -All P0-P3 items from the plan backlog are now COMPLETE. -Phase 8 collaboration features are COMPLETE. -Phase 9 (Performance, PWA & Polish) is COMPLETE. -Phase 10 (Docs & Code Hygiene) is COMPLETE. +Plan 072 remediates findings from Plan 071 (codebase gap audit). The following +changes have been implemented: -No open tasks remaining. +### Plan 072 — GOAP Remediation (2026-07-24) + +| Wave | Goal | Status | Changes | +|------|------|--------|---------| +| W0 | Decisions & Baseline | Done | ADRs 027/028/029 accepted, broken symlinks fixed | +| W1 | Data Integrity (P0) | Done | Zod hydration, import validation, deletion link cleanup, session-only credentials | +| W2 | Harness & CI (P1) | Done | Version-propagation disabled, security fail-closed, warnings-as-errors | +| W3 | Product Honesty (P1) | Done | Semantic→Ranked labels, Re-sync removed, coming soon tooltips fixed | +| W5 | Documentation (P2) | Done | This update | + +### Deferred to Plan 073 + +- G2: Bidirectional Yjs/Zustand sync bridge (requires G1 validation boundary) +- G5: UI/UX hardening (44px targets, ARIA tree, Playwright devices) +- Full accessibility audit (axe, screen reader, zoom/reflow) + +## Historical Completed Work ## Completed - 2026-07-18 Session (Phase 10: Docs & Code Hygiene) diff --git a/plans/PHASES.md b/plans/PHASES.md index 508b45da..f7fa96b6 100644 --- a/plans/PHASES.md +++ b/plans/PHASES.md @@ -1,22 +1,27 @@ # PHASES -## Phase 1: Foundation (Complete) -- [x] SQLite WASM + OPFS Client -- [x] Entity/Claim/Link Repository +> **Note**: Phases 1-3 describe the original Vite/SQLite architecture and are +> historical records. The project migrated to Next.js/Zustand/localStorage +> (ADR 018). Items marked [historical] were completed in the prior architecture +> and may not apply to the current codebase. + +## Phase 1: Foundation (Historical — Vite/SQLite era) +- [x] SQLite WASM + OPFS Client [historical — removed in ADR 018] +- [x] Entity/Claim/Link Repository [historical — replaced by Zustand store] - [x] Basic UI with View Switching -- [x] CLI Harness +- [x] CLI Harness [historical — removed in Next.js migration] -## Phase 2: Integration (Complete) -- [x] Tiptap claims extension (convert text to claims) -- [x] Local search indexing (FTS5 + Orama) +## Phase 2: Integration (Historical — Vite/SQLite era) +- [x] Tiptap claims extension [historical — replaced with textarea] +- [x] Local search indexing [historical — Orama removed, now BM25] - [x] Graph neighborhood rendering (Focus Mode) -## Phase 3: Synthesis (Complete - 2026-05) -- [x] Local RAG (Orama-based search) -- [x] Bi-directional markdown sync (CLI Support) +## Phase 3: Synthesis (Historical — Vite/SQLite era) +- [x] Local RAG [historical — Orama removed, now BM25] +- [x] Bi-directional markdown sync [historical — CLI removed] - [x] Export to static site (Markdown, JSON, HTML) -- [x] Knowledge graph snapshots with diffing -- [x] Node.js CLI with better-sqlite3 adapter +- [x] Knowledge graph snapshots [historical — stores view bookmarks, not revisions] +- [x] Node.js CLI [historical — removed in Next.js migration] ## Phase 4: Security & Quality (Complete - 2026-05) - [x] XSS fixes in all export paths (PR #200, #216) @@ -42,14 +47,14 @@ - [x] Import persistence (CLI + browser) ## Phase 6: Intelligence (Complete - 2026-06) -- [x] Streaming AI chat with LLM provider integration (OpenRouter, Kilo, Anthropic, Ollama) -- [x] Semantic search with embeddings (all-MiniLM-L6-v2) +- [x] Streaming AI chat with LLM provider integration (OpenRouter, Ollama) +- [x] BM25 keyword search with ranked results [renamed from "semantic search" in Plan 072] - [x] Entity auto-hydration from external sources (Jina AI reader) - [x] Claim provenance and verification tracking - [x] Entity-aware AI tools (list, query, link) - [x] Agentic tool-calling loop (search, create, link) -- [x] Chat history persistence (IndexedDB) -- [x] Rate limiting and API key encryption +- [x] Chat history persistence [localStorage, not IndexedDB] +- [x] Rate limiting and API key encryption [session-only, per ADR 028] - [x] Advanced TRIZ analysis features ## Phase 7: Export Enhancement (Complete - 2026-06) diff --git a/scripts/quality_gate.sh b/scripts/quality_gate.sh index af687b45..585b4591 100755 --- a/scripts/quality_gate.sh +++ b/scripts/quality_gate.sh @@ -39,6 +39,9 @@ fi # We don't exit immediately so we can report ALL issues, not just the first FAILED=0 +# Maximum allowed warnings across all checks. Zero means warnings are errors. +MAX_ALLOWED_WARNINGS=0 + # DETECTED_LANGUAGES stores which language ecosystems are present in the repo # We use this array to conditionally run only relevant checks DETECTED_LANGUAGES=() @@ -321,7 +324,15 @@ if [[ " ${DETECTED_LANGUAGES[*]} " =~ " typescript " ]] && [[ "$SCOPE" == "all" echo "$OUTPUT" >&2 FAILED=1 else - echo -e "${GREEN} ✓ pnpm lint passed${NC}" + # Enforce warnings-as-errors policy + WARNING_COUNT=$(echo "$OUTPUT" | grep -c -E '⚠|warning|Warning' || true) + if [ "$WARNING_COUNT" -gt "$MAX_ALLOWED_WARNINGS" ]; then + echo -e "${RED} ✗ pnpm lint: $WARNING_COUNT warnings found (max allowed: $MAX_ALLOWED_WARNINGS)${NC}" + echo "$OUTPUT" >&2 + FAILED=1 + else + echo -e "${GREEN} ✓ pnpm lint passed${NC}" + fi fi fi diff --git a/src/app/layout.tsx b/src/app/layout.tsx index fe49bd43..a3621f09 100755 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -27,13 +27,13 @@ const newsreader = Newsreader({ export const metadata: Metadata = { title: "DO Knowledge Studio — Local-first thinking, connected", description: - "A local-first knowledge studio for rich notes, knowledge graphs, mind maps, semantic search, and AI-assisted thinking — all in your browser, no backend required.", + "A local-first knowledge studio for rich notes, knowledge graphs, mind maps, keyword search, and AI-assisted thinking — all in your browser, no backend required.", keywords: [ "knowledge management", "local-first", "knowledge graph", "mind map", - "semantic search", + "keyword search", "AI agents", "TRIZ", "second brain", @@ -45,7 +45,7 @@ export const metadata: Metadata = { }, openGraph: { title: "DO Knowledge Studio", - description: "Local-first thinking, connected. Rich notes, graphs, mind maps, semantic search, and AI agents.", + description: "Local-first thinking, connected. Rich notes, graphs, mind maps, keyword search, and AI agents.", type: "website", }, manifest: "/manifest.webmanifest", diff --git a/src/components/studio/mobile-drawer.tsx b/src/components/studio/mobile-drawer.tsx index 1ab99b76..d90e403a 100644 --- a/src/components/studio/mobile-drawer.tsx +++ b/src/components/studio/mobile-drawer.tsx @@ -307,7 +307,7 @@ function SearchTab({ onSelect }: { onSelect: () => void }) { const entities = useStudioStore((s) => s.entities) const startEdit = useStudioStore((s) => s.startEdit) const filtered = useFilteredEntities() - const [mode, setMode] = useState<'keyword' | 'semantic'>('keyword') + const [mode, setMode] = useState<'keyword' | 'ranked'>('keyword') // Empty-state copy follows the desktop SearchPanel exactly const emptyCopy = searchQuery ? 'No matches found.' : 'Your library is empty.' @@ -339,16 +339,16 @@ function SearchTab({ onSelect }: { onSelect: () => void }) { Keyword diff --git a/src/components/studio/right-panel.tsx b/src/components/studio/right-panel.tsx index 5ff1b336..fb48c06c 100644 --- a/src/components/studio/right-panel.tsx +++ b/src/components/studio/right-panel.tsx @@ -33,14 +33,14 @@ function SearchPanel() { const entities = useStudioStore((s) => s.entities) const claims = useStudioStore((s) => s.claims) const startEdit = useStudioStore((s) => s.startEdit) - const [mode, setMode] = useState<'keyword' | 'semantic'>('keyword') + const [mode, setMode] = useState<'keyword' | 'ranked'>('keyword') const filtered = useFilteredEntities() - const semanticResults = useMemo( - () => (mode === 'semantic' ? search(entities, claims, searchQuery) : []), + const rankedResults = useMemo( + () => (mode === 'ranked' ? search(entities, claims, searchQuery) : []), [mode, entities, claims, searchQuery], ) - const results = mode === 'semantic' ? semanticResults : filtered + const results = mode === 'ranked' ? rankedResults : filtered return (