diff --git a/AGENTS.md b/AGENTS.md index ad6c750..944ed46 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,6 +5,46 @@ This repo maps to **Maven** (project ID: 2) in Vikunja. Feature work, bugs, deployment tasks all go here. MCP endpoint: `http://localhost:9100/mcp` (or `http://192.168.1.104:9100/mcp` from workpc) +## The sibling services (Nexus, Praxis, Hexis) + +Maven is the conversational front end of a four-service ecosystem. The other three +live in sibling repos next to this one. + +| Service | Repo | Port | Answers | +|---|---|---|---| +| Nexus | `../nexus` | 9740 | who or what is this name | +| Praxis | `../praxis` | 8989 | what needs attention | +| Hexis | `../hexis` | 9741 | what can be run, and running it | + +Division of labour: Nexus identifies, Praxis observes, Hexis acts, Maven understands +and coordinates. Maven is not the source of truth for any of the three. The full +contract is `MAVEN_ECOSYSTEM_ARCHITECTURE.md`, and the constraints that bite during +implementation are summarised in `CLAUDE.md`. + +Where things are in this repo: + +- `cmd/mavend/ecosystem.go` holds `nexusClient` and `praxisClient`. The Hexis client + is vendored from `github.com/kami/hexis/pkg/client`. +- `cmd/mavend/ecosystem_acts.go` routes an act through capability discovery. +- `cmd/mavend/factenrichment.go` resolves each stored fact's `Subject` against Nexus + on a background poll loop, with backoff and no give-up. +- `internal/store/entityfacts.go` holds the entity-tagged fact rows. +- Config blocks are `nexus`, `praxis` and `hexis` in `deploy/mavend.json`. Each is + optional. Absent means that integration is dark, not broken. + +Bring the whole ecosystem up locally: + +```sh +docker compose -f deploy/ecosystem/docker-compose.yml up -d +``` + +That builds all three from the sibling working trees, so commit or stash there first. +Each publishes on loopback at the port above. Maven reaches them by service name on +the shared compose network. + +Testing without them running: `cmd/mavend/fakeecosystem_test.go` provides stubs, and +`cmd/mavend/ecosystem_degraded_test.go` covers each service being unreachable. + ## Rendering / previewing the web UI locally To see mavweb pages with real data without touching the production stack: diff --git a/CLAUDE.md b/CLAUDE.md index 0ad7e27..5fcbe2a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -68,6 +68,45 @@ Daemons are wired socket-to-socket, not linked. `internal/ipc` is the client/ser protocol; the config in `deploy/mavend.json` (with `${VAR}` env expansion from gitignored `deploy/telegram.env`) sets socket paths, model paths, and the phraser/embedder blocks. +## The ecosystem: Nexus, Praxis, Hexis + +Maven is one of four services. It owns conversation and personal memory. It does not +own identity, operational state, or execution. Full contract in +`MAVEN_ECOSYSTEM_ARCHITECTURE.md`. + +```text +Nexus identifies. Praxis observes. Hexis acts. Maven understands and coordinates. +``` + +| Service | Owns | Maven's client | Configured at | +|---|---|---|---| +| **Nexus** | Canonical entity ids, names, aliases, relationships. Projects, services, devices, people, pets, places. | `nexusClient` in `cmd/mavend/ecosystem.go`, `POST /api/v1/resolve` | `nexus.url` (`http://nexus:9740`) | +| **Praxis** | Operational attention and item lifecycle. What needs looking at, what changed, what is still unresolved. | `praxisClient`, the HTTP tools API under `/api/v1/tools/` | `praxis.url` (`http://praxis:8989`) | +| **Hexis** | The capability registry and the only path to executing anything. | vendored `github.com/kami/hexis/pkg/client` | `hexis.url` (`http://hexis:9741`) | + +All three are `nil` unless configured, and every one of them degrades on its own. +An outage means a named gap in the answer, never a broken turn and never a guess. + +Rules that are not negotiable: + +- **No component reads another component's database.** Praxis attention comes over + HTTP, never from its SQLite file. +- **Identity lives in Nexus.** Do not invent a local fact key for something Nexus + resolves. `actionFact` already sets `Subject`, and `cmd/mavend/factenrichment.go` + resolves it in the background against Nexus. +- **Free text never reaches a mutating Hexis call.** Resolve to a canonical entity id + first. Ambiguous resolution asks the owner, it does not pick. +- **LLM output is not authorization.** Confirmation binds capability id, target + entity, arguments, requester and expiry. See `cmd/mavend/confirm.go`. +- **Praxis lifecycle words mean different things.** Surfaced is not acknowledged, + acknowledged is not resolved, execution success is not recovery. Reading an item + aloud calls `Surface`, never `Acknowledge`. +- **No automatic attention-to-action path.** Digestion may summarise Praxis. It may + not call Hexis. + +Every cross-service call carries a correlation id minted once per action +(`withCorrelationID`), a contract version header, and `X-Requested-By: maven`. + ## Routing — read this before touching the router `internal/router/` has TWO layered engines. **The LLM router is now the default and it is @@ -90,8 +129,11 @@ fallback. Any LLM error falls through to the classifier so a turn never breaks o Measured on the 77-case RU fixture (`MODEL-BAKEOFF-31-07-2026.md`): the classifier scores 36.8% full accuracy at p50 31ms; Qwen3-1.7B scores 67.5% intent-only / 72.7% through the -cascade at p50 ≈2.7s. Accuracy roughly doubled, latency is ~90× worse, and that trade was -accepted deliberately. `Confidence: 1.0` used to be hardcoded in `llmrouter.go`, so the LLM +cascade at p50 ≈825ms. Accuracy roughly doubled, latency is ~27× worse, and that trade was +accepted deliberately. **The ≈2.7s figure that stood here until 2026-08-02 was contention, +not the model.** See `ROUTING-EVAL-31-07-2026.md` line 61, which measures the LLM router at +p50 825ms / p95 1.2s / max 3.0s and the full cascade at p50 0.80-1.04s. Do not plan latency +work off the bakeoff table. `Confidence: 1.0` used to be hardcoded in `llmrouter.go`, so the LLM path could never ask for clarification (6/6 refusal cases missed on the fixture) — Vikunja #359. Fixed 31-07-2026 with structural signal (single-token utterance, keyless fact, act with no allowlisted fn) feeding the same stage-3 gate the classifier path already had — see diff --git a/HANDOFF.md b/HANDOFF.md deleted file mode 100644 index b2ad4d6..0000000 --- a/HANDOFF.md +++ /dev/null @@ -1,120 +0,0 @@ -# Handoff - -Uncommitted scratch file. Delete it once the work below is finished. - -Written 2026-08-01. Kami is away for about an hour and will start a new session. - -## Where things stand - -The 35-PR stack (50 to 84 on gitea, one linear chain) has been reviewed and the -findings have been fixed. Two things are done and one is not. - -**Done: reviews.** One review posted on every PR from 50 to 84, as the `claude` -login. Review ids 58 to 92. - -**Done: fixes.** Eleven agents fixed the findings in parallel git worktrees. -All eleven branches are merged onto **`fix/integrated`**, which is 68 commits -ahead of the tip. `make test` and `make build` both pass on it. - -**Not done: landing them.** `overnight/eco-versioned-traces` (the stack tip) has -NOT been moved. Kami chose to land the fixes as commits on that tip. The final -step is a fast-forward, once he has answered the open questions below. - - git checkout overnight/eco-versioned-traces - git merge --ff-only fix/integrated - -Do not push. He merges the stack himself. - -## Ask him these first - -He asked for the list and then left, so none of it is answered. Nothing below -is a blocker for the fast-forward, but all of it is easier to change before the -tip moves than after. - -1. **Hexis refuses to wire when a token is configured.** It used to log once and - then call unauthenticated forever. Fail closed, or warn and continue? -2. **`MethodIngestMail` stayed at `AuthRead`** while `SetTaskStatus` moved to - `AuthWrite`. Those two now disagree about the same kind of question. -3. **"Прости" in `clarifyGaveUp` and one expiry variant.** `CheckCringe` bans - apologies. The fix scoped the ban to nudges and kept his wording, with the - reasoning in a test skip. Absolute ban is the other reading. -4. **Memory evaluation timeout is 60s.** Two agents disagreed here. The merge - kept the short budget plus the gate that yields the slot to voice turns. Five - minutes is safe again now that the gate exists. -5. **Cold-start v1 recovery is narrow.** It fires only when an assertion carries - a PRF secret and that unlock fails. A pre-existing box whose only - authenticator lacks PRF still needs `MAVEN_DB_KEY`. - -## Two things to know before deploy - -**The update block.** The self-update fix refuses at startup any -build-from-source deployment that cannot say how to undo the source. -`deploy/mavend.json` has no `update` block today, so nothing breaks. Adding one -for the docker layout without `source_rollback` will stop the daemon booting. -That is intended. It should not be a surprise. - -**Three migrations became four numbers.** Three agents each wrote a migration -15. Tasks kept 15, ecosystem traces became 16, MCP tool fingerprints became 17. -Any box already carrying an unreleased 15 from a worktree build needs its -schema version checked by hand. - -## Merge decisions already made - -Two agents independently added `llm.Gate`. One is priority between a voice turn -and background work. The other is admission control while the resident model is -swapped. Both were kept. The swap one is now `SwapGate` with `SetSwapGate`. -`Complete` takes priority first and the drain second, so a background request -waiting on priority cannot stall a swap. - -## Still open in the code, on purpose - -- Ambient calendar events never reach the `calendar_busy` gate. `calendar_busy` - is a level, so an ambient writer needs an expiry. Recorded as a comment at the - top of `cmd/mavweb/ambient.go`. -- Hands-free has no session-scoped voice assertion. Noted in the mavweb route - table. -- The Hexis capabilities call sends no correlation id. The vendored client has - no header hook, so closing it means re-vendoring. -- Re-running a stored audio blob by id does not exist. The comments that claimed - the wire offered it were corrected. -- Simulator persona checks read a Go constant on one step, because chat there - goes through `phraser.NewStub()` rather than the replier. -- The PRF value is client-supplied and unbound to the assertion signature. This - is inherent to PRF key wrapping, which needs a fixed salt. Documented in - `cmd/mavweb/webauthn.go`. Do not try to bind it. -- `safeKey` drops Cyrillic, so Russian calendar events on one day collide. Filed - as Vikunja #443 with three fix options. It is a migration, not a patch. - -## The one task left - -Audit what the agents filed in Vikunja. They created QA tasks on their own -initiative, which nobody asked them to do. Kami saw them and said some are new -bugs worth a later sweep. Dedupe against existing tasks, and separate genuine -new bugs from restatements of findings the commits already fixed. That produces -the second sweep list without re-reading 35 PRs. - -## Recipes - -Gitea is `https://gitea.kvmx.ru`, repo `kami/Maven`. Tokens are in -`~/.config/tea/config.yml`: the `homesrv` login reads, the `claude` login posts. -`tea` cannot list review comments, so use the REST API. - -Read the reviews for one PR: - - curl -s -H "Authorization: token " \ - "https://gitea.kvmx.ru/api/v1/repos/kami/Maven/pulls//reviews" - -Do not fetch `/issues/comments` without a `since` filter. It returns 50 stale -comments from a superseded stack. - -The scratchpad from this session holds `RECIPE.md` (how the reviews were done), -`FIXBRIEF.md` (how the fixes were done) and `postreview.sh`. Worktrees are under -`scratchpad/wt/g01` to `g11` and can be removed with `git worktree remove` once -the tip has moved. - -## Constraints that do not change - -Read `CLAUDE.md`. Maven is feminine and calls him "ты". The eval enforces both. -Not a nag, not autonomous. No telemetry, no cloud model, no third-party account. -His notes and facts are never search input. Use `make`, never bare `go build` on -a CGO daemon. diff --git a/MODEL-BAKEOFF-31-07-2026.md b/MODEL-BAKEOFF-31-07-2026.md index 1c55f2c..ecf897d 100644 --- a/MODEL-BAKEOFF-31-07-2026.md +++ b/MODEL-BAKEOFF-31-07-2026.md @@ -162,6 +162,11 @@ The 1.7B does that 0-2 times. ## Latency — the long tail is not the Thinking block +> **Stale, corrected 2026-08-02.** The p50 figures in this table are contention on a +> shared llama-server, not the model's cost. The router measures p50 825ms / p95 1.2s / +> max 3.0s in `ROUTING-EVAL-31-07-2026.md`, which says so at line 61. Read this table for +> the shape of the tail only. Take absolute latency from the routing eval. + | | p50 | p95 | |---|---|---| | Qwen3.5-0.8B | 2.4s, 2.9s, 2.0s | 17.4s, 17.6s, 17.4s | @@ -213,8 +218,10 @@ swapped again when the CPT lands. - ~~The routing numbers only reach production once the LLM router is wired on. It is still `nil`.~~ **Resolved the same evening:** the LLM router is wired at `voice.go:214` behind `voice.llm_router`, the default is on, and `deploy/mavend.json` sets it `true`. - These numbers are the production path now, so the p50 ≈2.7s is a real per-turn cost and - not a bench artifact. + These numbers are the production path now. **Corrected 2026-08-02: the p50 ≈2.7s in the + latency table above WAS a bench artifact.** It is contention on the shared llama-server, + not the model. `ROUTING-EVAL-31-07-2026.md` line 61 says so, and measures the router at + p50 825ms / p95 1.2s / max 3.0s. Cite that file for latency, not this one. - ~~`/mnt/hdd1/llms/LFM2.5/Qwen3-1.7B-UD-Q4_K_XL.gguf` is a 293 MB truncated download in the wrong directory.~~ **Deleted 2026-07-31.** The good 1.13 GB copy in `qwen3/` is what `deploy/mavend.json` loads. diff --git a/PLAN-DETERMINISM-02-08-2026.md b/PLAN-DETERMINISM-02-08-2026.md new file mode 100644 index 0000000..b455285 --- /dev/null +++ b/PLAN-DETERMINISM-02-08-2026.md @@ -0,0 +1,270 @@ +# Deterministic logic around a small model + +Written 2026-08-02. Branch `fix/integrated`. + +## The question + +Where does deterministic code attach, so that it helps the resident 1.7B now and +does not fight a larger model later. + +## The mistake to avoid + +Everything deterministic we have added so far sits in front of the model and +preempts it. Stage 0 matches, the model never sees the turn. That shape helps a +weak model and blocks a strong one, silently. + +The fix is not to remove it. The fix is to know which rules are safe in that +position and to have a way to measure the rest. + +## Four attachment points + +**Bypass, before the model.** The only shape that saves the 2.7s p50. Safe when +the rule is a decision procedure over a closed set, not a guess over an open one. +Exact match and clock queries qualify. + +**Evidence, beside the model.** Extractors emit candidate slots as a prior, not a +verdict. The prompt carries the prior and the validator reuses it. A small model +leans on it, a large one overrides it correctly. + +**Grammar, around the model.** GBNF built from live state rather than hardcoded. +Costs nothing at runtime and prevents the error instead of catching it. + +**Repair, after the model.** Validation failure re-asks with the specific error +rather than overriding. Self-retiring, because a better model trips it less. + +## The constraint that ranks them + +Latency must stay minimal. That demotes repair and promotes grammar. + +- Grammar first. Zero runtime cost, immediate gain. +- Bypass keeps its place. It is the only thing that avoids a model call at all. +- Repair only where failure is rare, capped at one retry. +- Evidence is correct but costs a model call where a bypass costs none. +- Ecosystem calls belong in the snapshot path, in parallel, on strict deadlines. + Never serial before routing. + +## The line that never moves + +Separate policy from capability compensation. They look alike and age oppositely. + +Capability compensation exists because the model is weak. It should be measurable +and retirable. + +Policy exists because we decided. The personal boundary, the feminine persona, the +never-search-his-notes rule, the Hexis allowlist and confirmation binding. None of +those yield to a smarter model. A larger model is more dangerous there, not less. + +## What we keep + +Every stage-0 rule stays exactly as it is. Retiring them was the wrong call and it +would throw away a day of measured gains. + +- exact-match fast path +- clock rules +- `SystemTimeDateGrammars` +- `AgendaQueryGrammars` +- `thinSingleToken`, including the social lexicon and the verb-ending test + +Three additions, none of which change behaviour: + +1. Each rule gets an id and a fixture subset. +2. Each rule writes one trace line when it fires. +3. Each rule carries a comment saying whether its set is closed or open. + +That preserves today's accuracy and buys the option to revisit later with numbers. + +## What we build + +**Dynamic grammars from live state.** The grammars today are static: `routeGrammar` +fixes the 7 intents, `responseGrammar` fixes the mood enum, kiwix `queryGrammar` +fixes word shape. Everything else is a free string. + +Candidates in order of payoff: + +- **Hexis capability ids.** After discovery the exact list is known. As an enum, + the model cannot name a capability that does not exist. +- **Act fn allowlist.** Hits the two remaining false clarifies directly. They are + the act-with-no-allowlisted-fn arm of `gateLLMDecision`, firing on invented verbs. +- **Calendar names.** Enumerate the real ones for agenda and query slots. +- **Known fact keys.** A read-back matches a stored key instead of inventing a + synonym. This is the general form of the read side we scrapped on 2026-08-01. +- **Nexus display names as act targets.** Only while the list stays small. + +Two rules or it backfires: + +- **Always include an escape value.** A closed enum with no `other` forces a wrong + pick instead of a decline. The escape is what feeds the clarify gate. +- **Cache the grammar string, keyed on the state that built it.** Rebuilding per + turn is fine. Recompiling a large grammar per turn is not. + +## Retrieval over regex + +Resolve against stores that already exist rather than adding patterns. + +Identity is the worked example. Nexus is authoritative, `actionFact` already sets +`Subject`, and `cmd/mavend/factenrichment.go` resolves it in the background. The +scrapped work invented a parallel key namespace with nothing reconciling the two. + +A table that grows with real data beats patterns that grow with our patience. + +Note a real gap: the personal boundary in `cmd/mavend/actions_query.go` guards +Maven's own store only. It does not know Nexus or Praxis exist. + +## Prerequisites + +Both are offline and need no deploy. Neither existed on 2026-08-01, and that is +why the day cost what it did. + +**Failure taxonomy over the 77-case fixture.** Classify every miss as model +ignorance, contract loss, prompt ambiguity, or our own bug. Only model ignorance +deserves deterministic compensation. The other three get fixed once, for every +model size. Inference, not verified: much of what we patched was the last two. + +**Per-assist ablation runner.** One command toggles each assist off and reports the +accuracy delta on its fixture subset. Then retiring an assist is a config flip and +a number, not an argument. + +## Held + +Not now, and nothing gets built for it. + +- A 12B or 35B model. It may be cloud or the 16GB workstation, and cloud crosses + the current no-third-party line. +- The escalation tier in `gateLLMDecision`. +- Converting the stage-0 heuristics to evidence. + +One thing carries forward for free: deterministic assists emit confidence, never a +verdict. A verdict cannot escalate. + +## Ruling on the idea list + +Nineteen ideas, judged on whether they earn a place in Maven. Checked against the +tree on 2026-08-02, not from memory. + +### Build. Absent, and worth it. + +**SQLite FTS over embeddings.** No `fts5` anywhere in the tree. Lexical search is +faster than the ONNX embedder, deterministic, and strongest exactly where the +embedder is weakest, which is exact Russian names. Semantic search becomes the +fallback rather than the gate. This is the highest-value absent item. + +**Cached TTS phrases.** No cache in `internal/tts`. Confirmations, clarifies and +refusals repeat constantly and their text is already fixed. Pre-rendering them is +cheap and pays straight into the minimal-latency constraint. + +**Synthetic router dataset.** Generate Russian tool-calling examples from the same +schemas that will build the dynamic grammars. One source of truth for both, so the +model is trained on exactly the shapes it will be constrained to at inference. + +**Command STT separate from dictation STT.** One path today. Commands want latency +and a small vocabulary, meeting capture wants accuracy and can take its time. +`internal/capture` already spools to disk, so the split follows the existing seam. +Medium priority, behind the four above. + +### Polish. Present, incomplete. + +**Strict JSON everywhere.** Six call sites already carry a grammar: the router, +both phraser paths, kiwix, `internal/memeval`, `internal/email/extract`. Two do +not: `cmd/mavend/capture.go` and `cmd/mavend/replier_llm.go`. Close those two and +the rule holds everywhere. + +**Evidence-first prompting.** The world snapshot, the embedder RAG hint and matched +notes all exist. What is missing is the discipline that the model verbalises +retrieved evidence rather than recalling. This is the direct fix for the Левитан +fabrication. High value. + +**Progressive inference.** What exists is a fallback cascade, not escalation. On +error it drops to something weaker. It never escalates on ambiguity. The upgrade is +held with the bigger-model question, and `gateLLMDecision` is the hook. + +**Entity dictionaries.** Nexus is the canonical store, `behavior_ru.go` has +`KeyAliases`, `ecosystem_acts.go` has verb aliases. Morphology is the gap, and the +code says so in three places. Russian needs it and there is no stemmer in the repo. + +**Assistant state machine.** `confirm.go` models pending confirmation and +`dialogue.Session` models the turn. There is no unified task state. Reuse the +Praxis vocabulary rather than inventing one, because surfaced, acknowledged and +resolved already mean something precise here. + +**Session memory compiler.** The digestion tick, `internal/memory` and +`followUpMerge` do parts of this. Not a coherent compile step. + +**Background memory maintenance.** Mostly present, and the absent part is small. +What exists: `internal/memeval` reads recent memory on a loop and writes +observations, deduped against its own prior output, unable to speak or act. Facts +supersede at write time through `voidsID`, `CorrectValue` and `VoidLatestFact`, +and `RecentActiveFactsByKind` reads only live rows. Digests, ecosystem traces and +media all prune. + +Three gaps remain, all in the fact store: + +- Superseding is turn-driven. Nothing reconciles two live facts that contradict + unless a turn corrects one of them. +- Duplicates written by different sources or phrasings stay as separate live rows. + There is no key-level merge pass. +- Nothing expires. The log is append-only and grows without bound, and no fact + ever ages out on its own. + +Worth doing, and smaller than it looked. Not urgent. + +**Qwen CPT then narrow SFT.** In flight as #122. One disagreement with the idea as +written: do not drop persona from the SFT. Persona is the stated reason the CPT +exists, because stock writes `рад` where Maven needs `рада`. Keep the joint +router-plus-persona tune. + +**Offline job queue.** The tick loop, `factEnrichmentWorker` with backoff, and the +media prune already defer work. A general queue is tidier, not more capable. Low +priority. + +**Response templates.** Present in `clarify.go`, in the `replySystem` arms and in +`StubPhraser`. Worth extending to high-frequency confirmations, where latency and +persona correctness both matter. Do not extend further. Templating the +conversational reply removes the reason she is worth having. + +### Reject. + +**Grammar-first routing as a replacement for the LLM router.** Already measured. +The classifier scores 36.8% full accuracy against 72.7% through the cascade. +Replacing the model with rules halves the accuracy. Grammar-first as an ordering is +what stage 0 already is, and that stays. + +**Hierarchical intent classification.** Seven intents is already the coarse layer. +The specialisation stage exists as per-intent slot filling in `Extractor.Extract`. +Adding a tier buys structure, not accuracy. + +**Tool-specific micro-models.** Contradicts the one-resident-model constraint, +needs per-domain training data nobody has, and multiplies model loads on a single +Vega iGPU. Dynamic grammars give the same domain narrowing at zero runtime cost. + +**Local knowledge graph.** Nexus owns entities and relationships. Building a second +graph in Maven breaks the ecosystem line and creates two answers to one question. +If graph traversal is wanted, it is a Nexus feature request. + +**Preemptible training.** Training runs in a separate workspace, not on the serving +box. This only becomes real if CPT moves onto homesrv, and that is not the plan. + +## Known open, carried over from the deleted handoff + +Found live on 2026-08-01, not fixed. Everything else in that file was stale. + +- **Kiwix ranks badly on a correct query.** The stop-word pass eats the "and". So + "кто написал войну и мир" reaches Kiwix as "war peace author". The top hit is + "List of peace activists" and she summarises that as the answer. The tag + `scrapped/fact-and-kiwix-phrases` fixes the query text. The ranking is the ZIM + search and is untouched either way. +- **Chat drags prior turns into an answer.** One live reply mixed the greeting, the + height statement and a world question. It named Левитан as the author of Война и + мир. +- **`safeKey` drops Cyrillic**, so Russian calendar events on one day collide. + Vikunja #443 with three fix options. It is a migration, not a patch. + +## Sequence + +1. Failure taxonomy over the fixture. +2. Ablation runner, plus ids and trace lines for the existing rules. +3. Dynamic grammar for the act fn allowlist. +4. Dynamic grammar for Hexis capability ids. +5. Dynamic grammar for calendar names and known fact keys. +6. Extend the personal boundary to the ecosystem stores. + +Steps 1 and 2 come before anything is written in the router.