\[Unreleased] — 2026-04 — "Model Sovereignty (AI1–AI3)"
First shipped milestone of the XESO AI Strategy. See
[`docs/ai/strategy.md`](docs/ai/strategy.md) for the full plan; the
confidential canonical lives in `documentation/AI_STRATEGY.md`.
### Added — Agentic surfaces
- **Research Agent** (`/research`, `POST /api/agents/research`) — plans
sub-queries, fan-outs `graduatedSearch` in parallel, de-duplicates
passages, drafts a cited synthesis, self-reviews with a critic agent,
and revises up to 2× before returning. Counts toward the chat rate-
limit bucket. Runs over the user's own brain only.
- **Teacher Agent** (`POST /api/agents/teach`, `StudyPanel` on every
note) — generates flashcards, multiple-choice quizzes, or Socratic
dialogues strictly grounded in the note's content and source
locations. Ephemeral today; a future pass can persist into the
existing `review_cards` deck.
- **YouTube batch ingest** — pasting a playlist, `@handle`, `/channel/`,
`/c/`, or `/user/` URL into Quick Add now opens a preview dialog
listing up to 25 videos and fans out to `POST /api/ingest` at 500 ms
between calls so the rate limiter and per-source budget check apply
per video.
- **Model Context Protocol tools** (`/api/mcp`) expanded to include
`get_facts`, `research`, `generate_flashcards`, `generate_quiz` so
external clients (Cursor, Claude Desktop, a future XESO CLI) reach the
same agents as the web UI.
- **Training-data consent** (`Settings → Privacy`) — explicit opt-in for
"help improve XESO models". Off by default. Backed by
`users.consent_training` + `consent_training_at` columns (migration
`0053_training_consent.sql`), enforced by the distillation script
(`scripts/distill/generate-dataset.ts`) which also hashes user IDs and
excludes vaulted content.
### Added — Fine-tuning scaffolding (not yet in prod)
- `scripts/distill/generate-dataset.ts` — replays retrieval for consenting
users and asks Gemini 2.5 Pro as a teacher model to produce Alpaca-
style JSONL for QLoRA fine-tuning.
- `scripts/finetune/xeso-mind-v1.axolotl.yaml` — Axolotl QLoRA config
targeting `google/gemma-2-9b-it`.
- `docs/ai/per-user-lora-adapters.md` — design doc for the vLLM/LoRAX
per-user adapter rollout. Not yet deployed.
### Added — Docs
- `docs/ai/strategy.md`, `docs/ai/wedge-strategy.md`,
`docs/ai/claude-code-learnings.md` committed to the repo.
- `documentation/adr/ADR-AI-003-model-sovereignty-program.md` and
updates to `documentation/XESO-MASTER.md`,
`documentation/INVESTOR_ONE_PAGER.md`, and
`documentation/ROADMAP.md` (confidential tier, not committed).
### Changed — Navigation
- Nav rail now includes a **Research** entry between Chat and Review.
- Command palette adds **"Go to research"** to the quick-nav section.
### Added — 90-day plan execution (Wk2–Wk10)
Concrete shipped work against §21 of the AI Strategy. Each item is wired
end-to-end (API + UI + tests where applicable) and guarded with unit
tests under `tests/`.
- **Wk3 — Ultraplan pattern on `/research`.** `POST /api/agents/research`
now accepts `reviewPlan: true` (returns just the sub-query plan,
`~1s`) or `approvedPlan` (skips the planner LLM on the real run). The
UI exposes two modes — "Review plan first" (default, differentiating)
and "Express" (original one-shot behavior). Users can add, remove,
rephrase sub-queries and flip intent before approving. This is the
single biggest Claude-Code-learning adoption in the agent surface.
- **Wk5 — User-editable facts UI.** New `/settings/memory` page + CRUD
at `/api/memory/facts` (GET/POST/PATCH/DELETE, audit-logged, rate-
limited, owner-scoped). Low-confidence filter, optimistic updates,
deletion uses the existing `supersededBy = id` retirement sentinel so
audit trail is preserved. Closes R5 ("false-positive inferred facts").
- **Wk6 — Kairos-style memory consolidation cron.** New
`lib/services/memory/consolidate.ts` + `/api/cron/memory-consolidate`
- `infra/cloud-scheduler/jobs.yaml` entry (`17 3 * * *` UTC). Three
passes per user per run: merge duplicate `(subject, predicate)` rows,
retire low-confidence stale rows, repair transitive supersession
chains. Idempotent; per-user transactional; caps at 200 users per
tick.
- **Wk8 — Prompt-injection-via-source sanitizer.** New
`lib/services/ingestion/sanitize.ts` (10 attack-pattern regexes:
ignore-previous, disregard-previous, forget-everything, new-
instructions, you-are-now-DAN, role-switch, hidden-system-tag,
exfiltrate-key, http-sink, markdown-autofetch). Wired into
`lib/services/ingestion/pipeline.ts` for `youtube | web | pdf`
sources before hash + LLM. Plus `fenceUntrustedSource` helper for
prompt-time wrapping. Covered by `tests/sanitize-source-text.test.ts`
(11 assertions, all passing). Three new `leakage-v0.jsonl` rows
(l011–l013) test the same attack classes against the chat path.
- **Wk9 — Memory-evolution surface.** `GET /api/memory/evolution`
returns pairs of (superseded, current) facts for the same
`(subject, predicate)` key. Rendered inside `/settings/memory` as
"How your thinking evolved" cards (from → to + confidence delta +
timestamp). The trust payoff of the supersession mechanic becomes
visible.
- **§6.1 — Per-turn USD tracking.** `chatMonitor.recordTurnCost({
model, inputTokens, outputTokens })` with a vendor-priced lookup
(`estimateTurnUsd`) for Gemini 2.5 Pro/Flash, Claude 3.5 Sonnet, GPT-
4o, and a default floor. Wired into `onFinish` of `/api/chat/route.ts`
so every successful turn contributes to the burn-rate series.
`getStats()` now returns `cost: { totalUsd, avgUsd, p95Usd, perModel
}`. Covered by three new tests in `tests/chat-monitor.test.ts`.
- **Wk2 — Burn-rate dashboard.** `/admin/slo` (ops RBAC) now renders
success rate, p95 latency, health grade, and a full burn-rate card:
spent (15m), projected hourly, projected daily, per-model breakdown,
and recent errors. Replaces the prior placeholder.
- **Wk10 — Research-agent SSE worklog.** `POST /api/agents/research`
now supports `?stream=1` (or `Accept: text/event-stream`) to return a
Server-Sent Events stream of lifecycle events (`plan`,
`subquery:start`, `subquery:done`, `merged`, `synth:start`,
`critic:start`, `critic:done`, `revision:start`, `synth:done`,
`done`). A 15-second keep-alive comment prevents intermediary 60-s
timeouts. The JSON transport still works for JSON consumers. `/research`
uses streaming by default and renders a live worklog (last 8 events +
elapsed counter) during the 10–25s run. Cancel-on-unmount via
`AbortController` so closing the tab frees server resources.
- **Audit events.** Added `memory_fact_created`,
`memory_fact_updated`, `memory_fact_retired` to
`lib/services/logging/audit.ts` for the new facts CRUD.
### Hardening (10/10 pressure-test pass)
- **`fenceUntrustedSource` attribute injection.** The `sourceKind`
argument is now validated against `^[a-z0-9_-]{1,24}$` and falls back
to `unknown`; a malicious caller can no longer break out of the
`kind=""` attribute. Transcripts that literally contain
`</untrusted_source>` are also neutralized inside the inner payload
so an injection can't close the frame early.
- **`PATCH /api/memory/facts` requires a change.** The schema now
refuses `{ id }`-only bodies (at least one of subject, predicate,
object must be present) so the audit log and confidence bump fire
only on genuine edits.
- **`estimateTurnUsd` longest-prefix match.** Regression: naive
prefix-matching billed `gpt-4o-mini-preview-2024` at `gpt-4o`
rates. Now sorts prefixes by length DESC so
`gpt-4o-mini` wins. Covered by dedicated test.
- **Memory nav discoverability.** `/settings/memory` is now linked
from the settings page top nav (Memory button → deep route) and
from the top of `/settings/privacy` (Memory card with "Manage
memory" CTA). Previously the page existed but was only reachable
via direct URL.
Verification:
- `npx tsc --noEmit` — exit 0.
- 822+ vitest assertions green. New files: `tests/memory-consolidate.test.ts`
(8 tests for `normKey` edge cases), `tests/research-events.test.ts`
(5 tests for agent worklog contract), `tests/research-sse.test.ts`
(4 tests for the SSE transport), plus 11 additional sanitizer
pressure rows and 5 new cost tracking edge cases.
- `npm run docs:check` — 0 broken internal links.
### Added — AI Strategy v2.0 pressure-test rewrite + agent eval harness
Follow-up pass on the v1.0 strategy doc after an explicit pressure-
test. Every weak section was rated honestly; the rewrite addresses
each gap and ships the concrete eval code the strategy references.
- **`documentation/AI_STRATEGY.md` v2.0** (confidential, not
committed) — 11 sections → 21 sections + 3 appendices. New: north-
star metric (GAC), self-audit rating table, hypothesis + falsifier,
per-initiative kill-conditions, baseline benchmark numbers,
burn-rate telemetry, 5 new competitor teardowns, 5 new risks with
early-warning signals, metrics-as-a-contract rewrite, unit economics
P&L, 11-row decision log, AI-specific security posture, org design,
three-audience framings, capital scenarios, comparative benchmarks,
pre-mortem, and a week-by-week 90-day execution plan.
- **`docs/ai/strategy.md`** (committed) updated with an **Eval harness
(the contract)** table and a **North-star metric** section that
mirror the Tier-1 canon.
- **`docs/ai/eval-harness.md`** (new, committed) — engineering-facing
spec for the eval harness: 10-corpus inventory, how-to-run recipes,
PR-scoped vs nightly-scoped runs, grading rules, ship protocol,
ownership.
- **`scripts/evals/research-v0.jsonl`** (new, 12 rows) — first agent-
level eval corpus. Grades the research agent on citation adherence,
sub-query plan size, abstain behavior, prompt-injection resistance,
and word-count discipline.
- **`scripts/evals/teacher-v0.jsonl`** (new, 10 rows) — grades the
teacher agent on structural correctness (flashcard count, quiz 4-
choice shape + correctIndex range, Socratic `idealAnswerOutline`
presence) plus optional phrase-grounding on note content.
- **`scripts/evals/run-agents.ts`** (new) — self-contained runner that
hits `POST /api/agents/research` and `POST /api/agents/teach`,
grades against the expect-block spec, writes
`results-agents-<corpus>-<ts>.json`, and exits non-zero on any
corpus miss. Teacher rows whose `noteRef` isn't resolved via
`EVAL_TEACHER_NOTE_IDS` are SKIPPED (not failed) so the harness can
run incrementally.
- **`npm run evals:agents`** wired (runs both corpora by default;
`CORPUS=research|teacher|all` selects).
- **`documentation/index.md §14.5`** updated to reflect the v2.0
rewrite's full section map.
---