# Gaps: what is missing, and what the architecture does about it Hand-written, except the four capability classes, which are derived. This file compares responsibilities. It never compares package names. A package existing is not a capability, and a capability can be spread over six packages and still be missing. ## Where each class comes from Classes 1 through 4 are the `gap_class` field in `docs/capabilities/ledger.yaml`, derived from the seven implementation dimensions and the criteria verdicts. Rebuild them: ```sh python3 docs/capabilities/build_ledger.py ``` Classes 5 through 8 are read from `docs/architecture/findings.md` and `docs/capabilities/invariants.md`. **Every entry names the capability or invariant it affects.** An entry affecting neither is marked non-blocking cleanup, in those words, and it is the whole of class 8. Counts are over the 46 v1 capabilities. The 5 deferred ones are excluded. --- ## 1. Capability missing (5) Nothing carries it. `code_present: no`. | capability | criteria | note | | --- | --- | --- | | `summaries` | 0 pass, 3 fail | no package. Wanted by `email-triage`, `web-crawling`, `hearing` and `rss-and-news`, each of which would consume it | | `webhooks` | 0 pass, 3 fail | no package. Telegram's own inbound channel is mapped to `telegram`, not here | | `command-chaining` | 0 pass, 3 fail | no package. The `chain` in `internal/router` is the world chain and the source chain | | `learning-the-style` | 1 pass, 2 fail | the pass is a negative criterion satisfied by absence. The build reports it as an anomaly | | `learning-from-mistakes` | 0 pass, 2 fail | no package | The last two are invariant 10, and it is `unresolved`. Whether behavioural learning is wanted is a product question, so these two are not automatically work. ## 2. Capability partial (21) Reachable, and at least one criterion was observed failing. This is the class that matters most, because a user can get to all 21 today and 21 misbehave. `route-an-utterance`, `ask-instead-of-guessing`, `answer-from-your-own-data`, `answer-from-the-world`, `read-an-encyclopedia`, `facts`, `notes`, `recall`, `reminders`, `voice`, `web-ui`, `desk-notifications`, `wake-word`, `nexus`, `praxis`, `the-deployed-stack`, `encrypted-database`, `passkey-and-step-up`, `tests-and-analyzers`, `web-crawling`, `cron-jobs`. `wake-word` is the sharpest: reachable on every dimension and 0 of 2 criteria pass. ## 3. Capability exists but unreachable (9) Built, and the deployed configuration does not reach it. | capability | why | class of fix | | --- | --- | --- | | `speak-as-herself` | `reachable: partial` on `core.model_seam` | configuration | | `weather` | no `weather` key in the deployed `voice` block | configuration | | `see-an-image` | no media block | configuration | | `memory-evaluation` | worker is `configured-off` | configuration | | `ntfy` | present in the config and disabled there | configuration | | `hearing` | `capture.enabled` false and no capture client ships (V-514) | configuration and code | | `mcps` | no MCP server configured, and V-478 blocks the one candidate | deployment | | `email-triage` | `mavmaild` is not in `docker-compose.yml` | deployment | | `calendar-management` | `mavcaldav` is not in `docker-compose.yml` | deployment | None of these nine is a code defect. Seven are one config block and two are one compose entry. `docs/spec.md` says this about the audit's four and it still holds for these nine. ## 4. Capability exists but unverified (11) Reachable, nothing observed failing, and not all criteria pass. These are measurement gaps, not defects. `interruption-policy`, `digest-of-held-nudges`, `morning-routine`, `routine-proposals`, `tasks`, `rss-and-news`, `telegram`, `speech-to-text`, `text-to-speech`, `hexis`, `network-scans`. Four of them, the whole Proactive cluster, are untested on every criterion, because a proactive behaviour cannot be probed by sending an utterance. That is the shape of the gap and it needs a different harness, not more probes. --- ## 5. Duplicated mechanism (6) | what | affects | owned? | | --- | --- | --- | | Two independent arbitrations decide one turn: seven intents, then twenty-two ordered query sources (`findings.md` 2.1) | invariant 11, `route-an-utterance`, `answer-from-your-own-data` | no | | A third arbitration runs before both: eleven stateful pre-emptors in the pre-route ladder (`findings.md` 2.2) | invariant 11, invariant 6, `ask-instead-of-guessing` | no | | Two tier systems. `internal/auth` does not bind the turn path, `internal/tool` is not keyed on the reach (`findings.md` 6.3, 6.3b) | invariant 8, `hexis`, `passkey-and-step-up` | no | | Two representations of reach, both ignored (`findings.md` 6.3) | invariant 8, `voice` | no | | Two digest mechanisms with the same word in the name, flushed six lines apart (`findings.md` 2.4) | invariant 5, `digest-of-held-nudges` | no | | Restraint decided twice: the gate decides whether a rule emits, delivery decides where it lands (`findings.md` 2.3) | `interruption-policy` | **yes**, argued in `channel.go` | The last row is duplication that is owned. It is listed so it is not rediscovered as a defect. ## 6. Missing shared mechanism (6) | what is missing | affects | | --- | --- | | A single point that decides whether this origin may perform this effect with this evidence. `origin × effect × evidence → permit` is the target and nothing computes it | invariant 8, `hexis`, `praxis`, `voice`, `passkey-and-step-up` | | An owner for a key namespace. `facts` has nine writers, `notes` six, `tools` three unrelated proposers (`findings.md` 1.1, 1.2, 1.3) | invariant 2, `facts`, `notes`, `recall` | | A comparable unit of evidence, so claimants can answer "is this more mine than yours?". `internal/claim` is that unit, written, tested and called by nothing (`findings.md` 6.1) | invariant 11, `command-chaining` | | A conversation that spans reaches. `mavweb` instead hardcodes one conversation id for the whole web reach | invariant 1, `web-ui`, `voice`, `telegram` | | A stated rule for what survives a restart. Six stores, six independent choices, two of them argued | invariant 12, `ask-instead-of-guessing` | | A summariser. Four capabilities would consume one and none exists | `summaries`, `email-triage`, `web-crawling`, `hearing` | ## 7. Current architecture conflicts with target behavior (10) The class where the code works as written and the written thing is not what the spec asks for. | conflict | affects | | --- | --- | | Four silent degradations stack on one turn, and `docs/spec.md` writes every v1 DoD at "honest" (`findings.md` 8.1) | invariant 7, `answer-from-the-world`, `speech-to-text`, `route-an-utterance`, `speak-as-herself` | | `praxisItemAction.handle` calls straight through: acknowledge, resolve, ignore and pin run on first hearing with no tier and no confirm turn (`findings.md` 6.3c, `cmd/mavend/ecosystem_acts.go:158`) | invariant 8, `praxis` | | `weather` is a live query source with `guesses: true` and the deployed config selects no provider, so it can claim a turn and answer from a stub (`findings.md` 11.3) | invariant 7, `weather` | | `loop.State.CalendarBusy` reads facts `mavcaldav` never writes, so the do-not-nag-mid-meeting suppressor is permanently false (`findings.md` 8.2) | invariant 3, `interruption-policy`, `calendar-management` | | Recurring reminders have a column, an IPC parameter and no caller. `actionReminder` passes `""` (`findings.md` 8.3) | `reminders`, `cron-jobs` | | The clarify store is not persisted and the expired-clarify notice reads the store that is gone (`findings.md` 7.5) | invariant 6, invariant 12, `ask-instead-of-guessing` | | `Claim.Coverage` returns 1.0 for a claim that extracted nothing (`findings.md` 6.3d) | invariant 11. Latent: it corrupts the fix for class 6 row 3 before that fix ships | | The act executor runs inside the key holder, and the process boundary is not one of the controls (`findings.md` 5.4) | invariant 8, `hexis`, `encrypted-database` | | The voice wire's whole security argument is external: loopback publish plus an ssh tunnel, so one compose edit removes it (`findings.md` 5.5) | invariant 8, `voice` | | `make test` is green with the four `TestONNX*` measurements silently skipped, because the recipe does not set `MAVEN_ONNX_LIB` | `tests-and-analyzers`, `recall`. It is why the predecessor audit had a green suite and 22 dead capabilities | ## 8. Architecture concern with no current product impact (11) **Every row here is non-blocking cleanup.** None names a capability or an invariant, which is the test for belonging in this class rather than in 5, 6 or 7. - `reactiveHandler` has 34 fields (`findings.md` 4.1). - `runTurn` is one function with eleven early returns (4.2). - `tick` runs thirteen jobs in one function (4.3). - `wireVoice` is one constructor for seventeen subsystems (4.4). - `mavsttd` and `mavttsd` are separate processes at a scale that does not need it (5.1). - Three IPC connections from one process (5.2). - A construction cycle between the API layer and the turn layer (3.1). - The handler holds the raw store beside the mediated one (3.2). - `queryDayPlan` reads the proactive scheduler, the single call across that line (3.4). - `internal/modes` is imported by nothing outside itself (6.2). - The daemon is wired twice, in two places (8.6). Two entries were considered for this class and moved out. `queryNetwork` triggering a live LAN scan inside a read path (6.4) affects `network-scans`, whose two remaining criteria are `unknown`. `actionFact` re-routing into the query chain (6.5) affects `facts` and `route-an-utterance`, which is where the "меня зовут Ками" misroute lives. --- # Priority One list. The rank is the plan's, and it is about impact today, not about how ugly the code is. An unwired or unreachable future defect never outranks a live user-visible failure because its architecture is offensive. ## 1. Prevents intended everyday use today 1. **`speak-as-herself` fails all three criteria.** The deployed resident model, `maven-instruct-b2-Q4_K_XL`, produces Russian sentences that no longer hold together, and the phrasing checks that would catch it run in the eval and not on the outbound path. Everything that asks the model to write a sentence inherits this. Formal `вас` and `вы` reached the wire while `CheckFeminine` passed. 2. **His own name is not stored as a fact.** "меня зовут Ками" routes to chat, so nothing is written, and "что ты помнишь обо мне?" routes to chat too. Two of the seven audit probes, still broken and now broken differently. 3. **`wake-word` fails both criteria while reachable on every dimension.** Voice is the spine of v1 and the always-on half of it does not work. 4. **Nine capabilities are one config block or one compose entry from reachable.** `weather`, `ntfy`, `see-an-image`, `memory-evaluation`, `email-triage`, `calendar-management` and `mcps` are the cheap ones. This is the highest ratio of capability to work in the whole list. ## 2. Makes existing behavior incorrect or unreliable 5. **The Praxis lifecycle path has no gate.** Four remote mutations run on first hearing. This is live today and needs no new wiring to matter. 6. **`weather` answers from a stub and is allowed to claim the turn.** A source marked `guesses: true` with no provider is worse than a named gap. 7. **The busy suppressor is permanently false.** Every interruption decision that should have deferred to a meeting fails open. 8. **Four silent degradations stack**, and nothing in a reply distinguishes the worst case from the best. Invariant 7 has no written boundary between "only better" and "cannot do the job". 9. **A parked clarify survived five consecutive turns** and was released by a path other than `отмена`. Invariant 6 says nobody owns closing it. ## 3. Blocks multiple capabilities 10. **No single authorization point.** Invariant 8, `unresolved`, and the third of the three questions the freeze was called to answer. It blocks `hexis`, `praxis`, `voice` and `passkey-and-step-up`, and it is the one property nobody can currently state. 11. **No owner for a key namespace.** A fetch watermark and a tuning parameter live in the table recall embeds and `queryFactByKey` reads back as an answer. 12. **No comparable unit of evidence.** Three ordered lists decide one turn. `command-chaining` cannot be built on top of them, and `internal/claim` carries a live defect before it is wired. 13. **No summariser.** Four capabilities would consume one. ## 4. Prevents verification 14. **`make test` is green with four measurements skipped.** The recipe does not set `MAVEN_ONNX_LIB`. This is the exact trap `CLAUDE.md` describes, and the baseline walked into it while measuring whether other things had. 15. **The whole Proactive cluster is untested on every criterion.** A proactive behaviour cannot be probed by sending an utterance. It needs a clock-driving harness, not more probes. 16. **26 of 31 named scenarios do not exist on disk.** Only 5 of 51 spec entries cite a scenario that is there (`findings.md` 9.5). 17. **`POST /api/ptt` was called unreachable in an earlier draft and is not.** Four speech criteria were filed `deployment missing` when the deployment is present and the probe was never written. ## 5. Architectural cleanup with no present user impact 18. Everything in class 8, in any order. None of it blocks a capability or an invariant, and that is why it is last. --- ## What this file does not do It does not schedule. `docs/roadmap.md` orders the work and this file feeds it. It does not decide the four `unresolved` invariants. Authority and confirmation, learning from outcomes, capability composition and the shelf life of a held nudge are the owner's, and items 10, 12 and 13 above stall on them.