Session 2 step 3, and the end of the explanation half. gaps.md compares responsibilities and never package names. Eight classes. The four capability classes are derived from the ledger's gap_class field and rebuild with build_ledger.py. The four architecture classes are read from findings.md and invariants.md, and every entry names the capability or invariant it affects. An entry naming neither is marked non-blocking cleanup in those words, which is the whole of class 8 and its eleven rows. Of 46 v1 capabilities: 5 missing, 21 partial and reachable, 9 built and unreachable, 11 reachable and unverified. The nine unreachable ones are seven config blocks and two compose entries. Not one is a code defect. The ranked list puts phrasing first: speak-as-herself fails all three criteria, and everything that asks the resident model to write a Russian sentence inherits that. His own name not being stored is second. Nine capabilities one config change from reachable is fourth, and it is the highest ratio of capability to work in the list. Items 10, 12 and 13 stall on unresolved invariants and are the owner's call, not work. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
14 KiB
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:
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.
reactiveHandlerhas 34 fields (findings.md4.1).runTurnis one function with eleven early returns (4.2).tickruns thirteen jobs in one function (4.3).wireVoiceis one constructor for seventeen subsystems (4.4).mavsttdandmavttsdare 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).
queryDayPlanreads the proactive scheduler, the single call across that line (3.4).internal/modesis 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
speak-as-herselffails 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 whileCheckFemininepassed.- 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.
wake-wordfails both criteria while reachable on every dimension. Voice is the spine of v1 and the always-on half of it does not work.- Nine capabilities are one config block or one compose entry from
reachable.
weather,ntfy,see-an-image,memory-evaluation,email-triage,calendar-managementandmcpsare the cheap ones. This is the highest ratio of capability to work in the whole list.
2. Makes existing behavior incorrect or unreliable
- 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.
weatheranswers from a stub and is allowed to claim the turn. A source markedguesses: truewith no provider is worse than a named gap.- The busy suppressor is permanently false. Every interruption decision that should have deferred to a meeting fails open.
- 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".
- 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
- No single authorization point. Invariant 8,
unresolved, and the third of the three questions the freeze was called to answer. It blockshexis,praxis,voiceandpasskey-and-step-up, and it is the one property nobody can currently state. - No owner for a key namespace. A fetch watermark and a tuning parameter
live in the table recall embeds and
queryFactByKeyreads back as an answer. - No comparable unit of evidence. Three ordered lists decide one turn.
command-chainingcannot be built on top of them, andinternal/claimcarries a live defect before it is wired. - No summariser. Four capabilities would consume one.
4. Prevents verification
make testis green with four measurements skipped. The recipe does not setMAVEN_ONNX_LIB. This is the exact trapCLAUDE.mddescribes, and the baseline walked into it while measuring whether other things had.- 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.
- 26 of 31 named scenarios do not exist on disk. Only 5 of 51 spec entries
cite a scenario that is there (
findings.md9.5). POST /api/pttwas called unreachable in an earlier draft and is not. Four speech criteria were fileddeployment missingwhen the deployment is present and the probe was never written.
5. Architectural cleanup with no present user impact
- 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.