Write the capability spec with a DoD for each (V-719)
51 capabilities: the 39 rows from the 2026-08-13 audit plus 12 v1 items that had no audit row. Each entry carries a state reference to the living doc that owns it, a plain DoD list observable on the running box, and the scenario file that scopes it. Applying "state is a reference" found 17 capabilities with no living doc. Only 5 of 51 entries cite a scenario that exists. --no-verify: committing on master by the owner's call this session. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+617
@@ -0,0 +1,617 @@
|
||||
# Spec: every capability, and what done means for each
|
||||
|
||||
*Last verified: 2026-08-15 @ 231248a*
|
||||
|
||||
What Maven is for, capability by capability, with a definition of done that can
|
||||
be observed on the running box. `CLAUDE.md` carries the rules. `docs/roadmap.md`
|
||||
carries the order. This file carries the target.
|
||||
|
||||
The capability list is the union of two sources. The 39 rows measured in
|
||||
`docs/evals/2026-08-13-capability-audit.md`, and the 18 items the owner named as
|
||||
v1 scope. Twelve of the owner's items had no audit row, so this file has 51.
|
||||
|
||||
## How to read an entry
|
||||
|
||||
**State** is a reference, never a word. It points at the living doc that holds
|
||||
the current or the desired implementation. Where no such doc exists, the entry
|
||||
says so, and that absence is a finding this file is surfacing on purpose.
|
||||
|
||||
**DoD** is a plain list of observable conditions. Each item is checkable on the
|
||||
running stack. An utterance and its answer, a log line, an outbox row, an HTTP
|
||||
response, or a store row.
|
||||
|
||||
**Scenario** names a file in `cmd/mavend/testdata/scenarios/`, which scopes the
|
||||
flow from utterance to output. Five exist today: `act_degraded`,
|
||||
`assistant_workday`, `conversation_anaphora`, `evening_degraded`,
|
||||
`morning_missed`. A name marked *(to write)* does not exist yet, and writing it
|
||||
is part of that capability's work.
|
||||
|
||||
Every clock time in this file is `Europe/Samara`, declared once in
|
||||
`docker-compose.yml` with `/etc/localtime` pointed at it (V-545). Every path
|
||||
reads `time.Local`.
|
||||
|
||||
## What v1 means
|
||||
|
||||
v1 is a voice assistant, minimum viable. Voice is the spine and the capability
|
||||
list is the surface. Each DoD below is written at "voice-reachable and honest",
|
||||
not at "feature-complete". Honest means she names the gap when she cannot do the
|
||||
job, and never fills it with a guess.
|
||||
|
||||
Seven capabilities in v1 have no design yet. They keep a DoD here, written at
|
||||
what done would look like, and their state entry names the missing doc. The gap
|
||||
stays visible instead of implied-done.
|
||||
|
||||
## The turn
|
||||
|
||||
### Route an utterance
|
||||
|
||||
- **State**: `docs/routing.md`.
|
||||
- **DoD**
|
||||
- Seven intents score separately from the source, on every turn.
|
||||
- A question about what Maven remembers routes to query, not remember. The
|
||||
audit's two misrouted probes of seven pass.
|
||||
- Any resident-model error falls through to the classifier and the turn still
|
||||
answers.
|
||||
- Every turn writes a decision trace naming the stage that decided.
|
||||
- **Scenario**: `conversation_anaphora`, `assistant_workday`.
|
||||
|
||||
### Ask instead of guessing
|
||||
|
||||
- **State**: `docs/routing.md`, the clarify head and the parked clarify ride.
|
||||
- **DoD**
|
||||
- An incomplete reminder parks and asks for the missing slot, not for all of them.
|
||||
- "отмена" during a parked clarify drops the parked turn and says so.
|
||||
- A parked clarify survives an interleaved unrelated turn and resumes.
|
||||
- **Scenario**: `conversation_anaphora`.
|
||||
|
||||
### Speak as herself
|
||||
|
||||
- **State**: `docs/language.md`.
|
||||
- **DoD**
|
||||
- No masculine self-reference reaches the wire. The audit caught "Хорошо,
|
||||
сохранил" live while `CheckFeminine` passed in the eval, so the check runs on
|
||||
the outbound path and not only in the eval.
|
||||
- She says "ты" throughout, and no pet name appears.
|
||||
- A rejected reply is logged with what failed, not silently rephrased.
|
||||
- **Scenario**: `morning_missed` pins the constant. `persona_wire` *(to write)*
|
||||
pins model output on the outbound path.
|
||||
|
||||
### Answer from your own data
|
||||
|
||||
- **State**: `docs/routing.md`, `queryWalk` in `cmd/mavend/actions_query.go`.
|
||||
- **DoD**
|
||||
- "что у меня сегодня по плану?" returns the real checklist and its open items.
|
||||
- The owner's sources are asked before anything outside, every time.
|
||||
- A source that looks rather than guesses is asked even when a destination is named.
|
||||
- **Scenario**: `assistant_workday`, `morning_missed`.
|
||||
|
||||
### Answer from the world
|
||||
|
||||
- **State**: `docs/world.md`.
|
||||
- **DoD**
|
||||
- A general-knowledge question returns a Russian summary that does not invent
|
||||
physics. The audit's "почему небо голубое?" answer is the failing case.
|
||||
- Only the utterance leaves the box. No persona block, no history, no matched notes.
|
||||
- Deleting the `search` block turns the capability off with a named gap, not an error.
|
||||
- **Scenario**: `world_summary_quality` *(to write)*.
|
||||
|
||||
### Read an encyclopedia
|
||||
|
||||
- **State**: `docs/world.md`, the Kiwix section.
|
||||
- **DoD**
|
||||
- A Russian question lands on the Russian book and an English one on the English book.
|
||||
- Kiwix answers when the line is down.
|
||||
- The retrieved article is on the question's topic, not merely a lexical match.
|
||||
- **Scenario**: `kiwix_language_pick` *(to write)*.
|
||||
|
||||
### Weather
|
||||
|
||||
- **State**: `internal/weather`. No living doc covers it. **Finding**: the
|
||||
provider seam, the home city and the clarify path have no written reasoning
|
||||
anywhere. The audit found the capability broken on configuration alone.
|
||||
- **DoD**
|
||||
- A `weather` block in `deploy/mavend.json` names a provider and a home city.
|
||||
- "какая сейчас погода?" answers for the home city without asking.
|
||||
- Naming another city answers for that city. The audit's follow-up "Самара"
|
||||
died with "Я тебя не разобрала", so the follow-up parks as a clarify instead
|
||||
of emitting a question through the answer path.
|
||||
- With no provider configured she names the gap and does not guess a forecast.
|
||||
- **Scenario**: `weather_followup` *(to write)*.
|
||||
|
||||
### See an image
|
||||
|
||||
- **State**: `internal/vision`, the seam that stores the image and says so. No
|
||||
living doc. **Finding**: V-667 has the gemma-4 mmproj on the box and no
|
||||
written contract for what a vision call returns.
|
||||
- **DoD**
|
||||
- An image sent through Telegram gets a Russian description.
|
||||
- With no vision model configured she says she cannot look, and the image is stored.
|
||||
- The vision call goes to the workstation and falls back silently when it is down.
|
||||
- **Scenario**: `vision_degraded` *(to write)*.
|
||||
|
||||
## Memory
|
||||
|
||||
The four memory rows below have no living doc. **Finding**: `docs/design.md`
|
||||
sketches the store and `docs/routing.md` covers recall's routing. Nothing owns
|
||||
the fact and note contracts, the supersede rule, or the digestion worker's
|
||||
consolidation pass. This is the largest documentation gap in the list.
|
||||
|
||||
### Facts
|
||||
|
||||
- **State**: `internal/store/facts.go`, `cmd/mavend/factenrichment.go`. No living doc.
|
||||
- **DoD**
|
||||
- A stated fact is written and confirmed in his own words, in the feminine.
|
||||
- A confirmation that says she wrote something is never emitted without the
|
||||
row existing. The audit's "я записала информацию о тебе" wrote nothing.
|
||||
- A superseding fact retires the old value and both are readable.
|
||||
- `actionFact.Subject` resolves through Nexus, never through a local key.
|
||||
- **Scenario**: `morning_missed`.
|
||||
|
||||
### Notes
|
||||
|
||||
- **State**: `internal/memory`. No living doc.
|
||||
- **DoD**
|
||||
- A note captured from any reach is recallable by question.
|
||||
- A note can be deleted by voice and from the web UI. `/api/revert` voids facts
|
||||
by key and nothing voids a note today (V-494).
|
||||
- A question is not stored as a statement. The audit's "я рассказывал тебе про
|
||||
байкал?" became a junk note.
|
||||
- **Scenario**: `note_delete` *(to write)*.
|
||||
|
||||
### Recall
|
||||
|
||||
- **State**: `docs/routing.md` for the query walk and the personal boundary.
|
||||
- **DoD**
|
||||
- The embedder loads at 384 dimensions with the marker check passing, on every start.
|
||||
- `EmbedQuery` and `EmbedPassage` carry their prefixes. A plain `Embed` on a note fails the build or the test.
|
||||
- The personal boundary scores a question about him as personal.
|
||||
`TestONNXPersonalBoundary` is green.
|
||||
- A recall miss says she does not remember, and does not answer from the world instead.
|
||||
- **Scenario**: `assistant_workday`.
|
||||
|
||||
### Memory evaluation
|
||||
|
||||
- **State**: `internal/memeval`. No living doc. **Finding**: the evaluator ships,
|
||||
writes notes and cannot speak, and nothing records what its conclusions mean (V-248).
|
||||
- **DoD**
|
||||
- One evaluation run is observed on the box and its notes are read back.
|
||||
- What it concluded is checkable against the notes it read.
|
||||
- **Scenario**: `memeval_run` *(to write)*.
|
||||
|
||||
## Proactive
|
||||
|
||||
### Reminders
|
||||
|
||||
- **State**: `internal/store`, `internal/delivery`. No living doc covers the
|
||||
reminder lifecycle. **Finding**: parking, firing, delivery, retry and
|
||||
cancellation are spread across three packages with no written contract.
|
||||
- **DoD**
|
||||
- A one-shot reminder set by voice fires at its time and is delivered.
|
||||
- **Recurring works from speech**: meetings, pills, the dog, the vet and the
|
||||
kibble. `store.Reminder` carries `Cron` and `ipc.CreateReminder` takes a cron
|
||||
argument, and no caller in `cmd/mavend` passes one. Recurring is unbuilt with
|
||||
its storage and delivery already finished under it.
|
||||
- A recurring reminder states its schedule back when it is set, and again when
|
||||
asked.
|
||||
- Cancellation works by voice and from `/reminders`, and a refusal is honoured.
|
||||
- A failed delivery retries into another reach rather than looping. The audit
|
||||
watched the ntfy failure run once a minute until 03:05.
|
||||
- **Scenario**: `recurring_reminders` *(to write)*. Cancellation is covered by the
|
||||
V-719 eval.
|
||||
|
||||
### Interruption policy
|
||||
|
||||
- **State**: `docs/handler-wiring.md` for the dispatch decision. **Finding**: the
|
||||
four presence-and-severity outcomes have never been written down as intended
|
||||
behaviour, only as code (V-281).
|
||||
- **DoD**
|
||||
- The four outcomes are named in a doc before any of them changes.
|
||||
- A severity-1 item with him present at the desk reaches him through some reach.
|
||||
The audit logged `dropped morning:утро (sev1, presence=present)`.
|
||||
- Nothing unprompted arrives during a quiet tick.
|
||||
- **Scenario**: `morning_missed`, `evening_degraded`.
|
||||
|
||||
### Digest of held nudges
|
||||
|
||||
- **State**: `internal/worker`, the digestion worker. No living doc.
|
||||
- **DoD**
|
||||
- A suppressed nudge candidate is observed surfacing in a later digest, on the box.
|
||||
- The semantic fingerprint is checked before the phraser is paid.
|
||||
- Digestion never calls Hexis.
|
||||
- **Scenario**: `evening_degraded`.
|
||||
|
||||
### Morning routine
|
||||
|
||||
- **State**: `internal/morning`, `internal/routine`. No living doc.
|
||||
- **DoD**
|
||||
- The morning plan reaches him inside its 08:00-11:00 window, `Europe/Samara`.
|
||||
- When the voice reach has no session the plan falls back to the non-voice
|
||||
reaches rather than being dropped (owner's call; related V-281).
|
||||
- A missed window is stated as missed, not silently swallowed.
|
||||
- **Scenario**: `morning_missed`.
|
||||
|
||||
### Routine proposals
|
||||
|
||||
- **State**: `internal/routine`. No living doc. **Finding**: the proposer reads a
|
||||
hand-written Russian verb list, which the language rules forbid as a route or
|
||||
fact source (V-606).
|
||||
- **DoD**
|
||||
- One proposal is observed on the box from real repeated behaviour.
|
||||
- The verb list is replaced by `internal/lexicon` or the embedder.
|
||||
- A proposal is a nudge he can decline, and declining it is stored.
|
||||
- **Scenario**: `routine_proposal` *(to write)*.
|
||||
|
||||
### Tasks
|
||||
|
||||
- **State**: `internal/tasks`. No living doc.
|
||||
- **DoD**
|
||||
- Open tasks are read back ordered by deadline and urgency.
|
||||
- A task captured by voice appears in the list and on the web UI.
|
||||
- Mail-derived candidates are never spoken as tasks until he accepts one (V-130).
|
||||
- **Scenario**: `assistant_workday`, `morning_missed`.
|
||||
|
||||
### RSS and news
|
||||
|
||||
- **State**: `internal/rss`. No living doc.
|
||||
- **DoD**
|
||||
- Feed items are read when asked and never announced unprompted. That is the
|
||||
intended shape, not a defect.
|
||||
- A question about a topic finds the matching item across the configured feeds.
|
||||
- A dead feed names itself as dead, and the others still answer.
|
||||
- **Scenario**: `morning_missed`.
|
||||
|
||||
## Reach
|
||||
|
||||
### Telegram
|
||||
|
||||
- **State**: `docs/deployment.md`, `internal/delivery/telegramsink`.
|
||||
- **DoD**
|
||||
- An outbound message is delivered and the outbox row records the delivery.
|
||||
- His chat is read continuously from restart.
|
||||
- The socks relay being down names the gap and holds the message.
|
||||
- **Scenario**: `evening_degraded`.
|
||||
|
||||
### ntfy
|
||||
|
||||
- **State**: `internal/delivery/ntfysink`, disabled in the committed config.
|
||||
- **DoD**
|
||||
- A write-scoped `NTFY_TOKEN` exists before re-enabling. It was switched off
|
||||
after a 403 storm.
|
||||
- A 403 stops retrying instead of looping once a minute.
|
||||
- **Scenario**: `ntfy_403` *(to write)*.
|
||||
|
||||
### Voice
|
||||
|
||||
- **State**: `docs/protocol.md` for the wire, `internal/delivery/voicesink` for the sink.
|
||||
**Finding**: the wire is documented and the listener is not. Nothing describes
|
||||
what holds a live voice session open.
|
||||
- **DoD**
|
||||
- A proactive message reaches him by speech without him speaking first. Every
|
||||
proactive message during the audit fell through with "no live voice session".
|
||||
- The port stays on homesrv loopback and reaches workpc over ssh.
|
||||
- `SurfaceVoice` caps acts at L0, and reading is not capped.
|
||||
- **Scenario**: `voice_push` *(to write)*.
|
||||
|
||||
### Web UI
|
||||
|
||||
- **State**: `docs/deployment.md`.
|
||||
- **DoD**
|
||||
- All pages answer 200.
|
||||
- Every capability with a surface has a page: reminders, notes, tasks, facts.
|
||||
- A destructive action on a page is gated by step-up.
|
||||
- **Scenario**: covered by `cmd/mavweb` tests, not by a scenario.
|
||||
|
||||
### Desk notifications
|
||||
|
||||
- **State**: `cmd/mavweb/ambient.go`, `internal/event`. No living doc.
|
||||
**Finding**: the inbound direction exists as the `ambient:notif` source and the
|
||||
outbound direction does not exist at all. Which one the owner means is an open
|
||||
product decision.
|
||||
- **DoD**
|
||||
- Inbound: a desktop notification becomes a fact at the ambient path's own
|
||||
confidence, filed low, and never spoken back unprompted.
|
||||
- Outbound: a nudge can appear on the workpc desktop as a fourth reach, or the
|
||||
outbound half is explicitly dropped from v1.
|
||||
- **Scenario**: `morning_missed` covers inbound. Outbound has none.
|
||||
|
||||
## Speech and senses
|
||||
|
||||
### Speech to text
|
||||
|
||||
- **State**: `docs/offload.md`, `docs/deployment.md`.
|
||||
- **DoD**
|
||||
- Russian speech transcribes accurately enough to route. `mavsttd` is the local
|
||||
floor and the workstation transcriber is the better path.
|
||||
- The workstation being down falls back to `mavsttd` silently.
|
||||
- The floor arm is exercised on its own, not only behind the workstation.
|
||||
- **Scenario**: covered by `docs/evals/2026-08-09-crisperwhisper2-russian-wer.md`.
|
||||
|
||||
### Text to speech
|
||||
|
||||
- **State**: `docs/offload.md`, `docs/deployment.md`.
|
||||
- **DoD**
|
||||
- A reply is spoken in Russian with correct number and abbreviation expansion.
|
||||
- `internal/ttsnorm` expands times and dates into `Europe/Samara` phrasing.
|
||||
- **Scenario**: `tts_normalisation` *(to write)*.
|
||||
|
||||
### Wake word
|
||||
|
||||
- **State**: `docs/deployment.md`, `mavwaked` under systemd on workpc.
|
||||
- **DoD**
|
||||
- "Мэйвен" wakes her and a near-miss does not.
|
||||
- Waking her opens a voice session the proactive path can push into. Waking is
|
||||
not the same as having a session (V-515).
|
||||
- **Scenario**: `voice_push` *(to write)*.
|
||||
|
||||
### Hearing
|
||||
|
||||
- **State**: `internal/capture`, `internal/audio`. No capture client ships (V-514).
|
||||
- **DoD**
|
||||
- A capture client runs on workpc and streams to `mavsttd` or the workstation.
|
||||
- The path is reachable end to end from microphone to reply.
|
||||
- **Scenario**: `voice_push` *(to write)*.
|
||||
|
||||
### Speaker recognition
|
||||
|
||||
- **State**: `internal/speaker`. No living doc (V-255).
|
||||
- **DoD**
|
||||
- The owner's voice is distinguished from another voice.
|
||||
- A voice that is not his cannot reach the act path.
|
||||
- **Scenario**: `speaker_gate` *(to write)*.
|
||||
|
||||
## The ecosystem
|
||||
|
||||
### Nexus
|
||||
|
||||
- **State**: `docs/ecosystem.md`.
|
||||
- **DoD**
|
||||
- Entities exist. Nexus answers "no entities yet" today, so every act naming a
|
||||
target has nothing to resolve against. Seeding is a Nexus-side job.
|
||||
- Free text resolves to a canonical entity id before any mutating call.
|
||||
- Ambiguous resolution asks him and does not pick.
|
||||
- Nexus down produces a named gap, not a broken turn.
|
||||
- **Scenario**: `act_degraded`.
|
||||
|
||||
### Praxis
|
||||
|
||||
- **State**: `docs/ecosystem.md`.
|
||||
- **DoD**
|
||||
- An item needing attention is read back on request.
|
||||
- Reading an item aloud calls `Surface`, never `Acknowledge`.
|
||||
- Attention arrives over HTTP, never from its SQLite file.
|
||||
- Digestion may summarise Praxis and may not call Hexis.
|
||||
- **Scenario**: `morning_missed`, `evening_degraded`.
|
||||
|
||||
### Hexis
|
||||
|
||||
- **State**: `docs/ecosystem.md`.
|
||||
- **DoD**
|
||||
- An act runs against a real target once Nexus has entities.
|
||||
- Confirmation binds capability id, target entity, arguments, requester and expiry.
|
||||
- LLM output alone never authorizes.
|
||||
- Every call carries a correlation id, a contract version and `X-Requested-By: maven`.
|
||||
- **Scenario**: `act_degraded`.
|
||||
|
||||
### Smart home
|
||||
|
||||
- **State**: `internal/smarthome`, disabled in config (V-256).
|
||||
- **DoD**
|
||||
- A device is controlled through Hexis, resolved through Nexus, never by free text.
|
||||
- Disabled means a named gap.
|
||||
- **Scenario**: `act_degraded`.
|
||||
|
||||
### Network scans
|
||||
|
||||
- **State**: `internal/netscan`, `internal/netaddr`. No living doc.
|
||||
- **DoD**
|
||||
- A scan of the configured subnets returns hosts and open ports on request.
|
||||
- The result is read back as prose, not as a table dump.
|
||||
- The rate limit in the config is honoured.
|
||||
- **Scenario**: `netscan_query` *(to write)*.
|
||||
|
||||
### Bluetooth control
|
||||
|
||||
- **State**: no package. **Finding**: nothing exists, and the box has no bluez
|
||||
(V-257). This is the only v1 item blocked on the host rather than on code.
|
||||
- **DoD**
|
||||
- bluez is present on the box that owns the radio.
|
||||
- A paired device is connected and disconnected by voice, through Hexis.
|
||||
- **Scenario**: `bluetooth_control` *(to write)*.
|
||||
|
||||
### MCPs
|
||||
|
||||
- **State**: `internal/mcp`. No living doc. **Finding**: the allowlist, the
|
||||
stdio and http transports and the webfetch door all exist. Nothing records
|
||||
which servers may run, or why.
|
||||
- **DoD**
|
||||
- A configured MCP server's tools are callable through the act path.
|
||||
- The allowlist is the only path to a tool, and a tool outside it is refused.
|
||||
- A server that dies is a named gap, and the rest of the registry still answers.
|
||||
- **Scenario**: `mcp_tool_call` *(to write)*.
|
||||
|
||||
## Operations
|
||||
|
||||
### The deployed stack
|
||||
|
||||
- **State**: `docs/deployment.md`.
|
||||
- **DoD**
|
||||
- All five compose services run the current build. The audit found three on a
|
||||
four-day-old image.
|
||||
- `mavwaked` on workpc is on the current build too, or its drift is stated.
|
||||
- A restart loses nothing.
|
||||
- **Scenario**: none. This is checked by `docker compose ps` and the startup log.
|
||||
|
||||
### Encrypted database
|
||||
|
||||
- **State**: `docs/deployment.md`, `docs/caveats/storage.md`.
|
||||
- **DoD**
|
||||
- The database is encrypted at rest with the working copy in tmpfs.
|
||||
- The key comes from the environment and is held only by `mavend`.
|
||||
- Passwords are read from files, never taken as flag values.
|
||||
- **Scenario**: none. Checked by `mavseal` and the startup log.
|
||||
|
||||
### Passkey and step-up
|
||||
|
||||
- **State**: `docs/caveats/security.md`, `internal/webauthn`, `internal/auth`.
|
||||
- **DoD**
|
||||
- WebAuthn is configured, so no step-up gate is fail-open. Today every one is,
|
||||
including `POST /api/chat`, which reaches the act path (V-683).
|
||||
- Enrollment requires an existing credential once the first one exists.
|
||||
- Step-up is per-request, not process-global.
|
||||
- **Scenario**: `stepup_gate` *(to write)*.
|
||||
|
||||
### Model swap
|
||||
|
||||
- **State**: `docs/deployment.md` (V-250).
|
||||
- **DoD**
|
||||
- `phraser.swap_models` lists the allowed gguf paths, or the page is removed.
|
||||
- A swap survives a restart, or the page states that it will not.
|
||||
- **Scenario**: none.
|
||||
|
||||
### Self-update
|
||||
|
||||
- **State**: `cmd/mavupdate`, `internal/update`. Blocked at step 3: it cannot
|
||||
reach the containerized socket (V-477).
|
||||
- **DoD**
|
||||
- An update runs to completion from inside the deployment.
|
||||
- A failed update rolls back and says so.
|
||||
- **Scenario**: none.
|
||||
|
||||
### Tests and analyzers
|
||||
|
||||
- **State**: `docs/qa.md`, `docs/workflow.md`.
|
||||
- **DoD**
|
||||
- `make test` is green with `-race` and `MAVEN_ONNX_LIB` set, so the four
|
||||
`TestONNX*` measurements run instead of self-skipping.
|
||||
- `make analyze` passes against its baselines, and a fix deletes its entry.
|
||||
- The one latency test that fails only under coverage is fixed or filed (V-718).
|
||||
- **Scenario**: none. This gate is the suite itself.
|
||||
|
||||
## Undesigned in v1
|
||||
|
||||
These seven have no design and no living doc. Each keeps a DoD written at what
|
||||
done would look like, so the gap is visible instead of implied-done. A design
|
||||
pass comes before any of them is built.
|
||||
|
||||
### Email triage
|
||||
|
||||
- **State**: `internal/email`, `cmd/mavmaild`. Built and **not in
|
||||
`docker-compose.yml`**. **Finding**: the product decision comes first. What she
|
||||
does with his mail is undecided, and deploying the daemon before deciding
|
||||
writes the decision by accident.
|
||||
- **DoD**
|
||||
- The owner has decided what triage means: read-only summary, task extraction, or reply drafting.
|
||||
- Mail-derived task candidates stay candidates until he accepts one (V-130).
|
||||
- No mail content leaves the box.
|
||||
- `mavmaild` is in compose, or its absence is deliberate and recorded.
|
||||
- **Scenario**: `email_triage` *(to write)*.
|
||||
|
||||
### Calendar management
|
||||
|
||||
- **State**: `internal/calendar`, `cmd/mavcaldav`. Built and **not in
|
||||
`docker-compose.yml`**. Same product decision as email.
|
||||
- **DoD**
|
||||
- Today's and tomorrow's events are read back, times in `Europe/Samara`.
|
||||
- An event is created by voice, with the slot asked for rather than guessed.
|
||||
- A conflicting event is stated as a conflict.
|
||||
- `mavcaldav` is in compose, or its absence is deliberate and recorded.
|
||||
- **Scenario**: `calendar_create` *(to write)*.
|
||||
|
||||
### Web crawling
|
||||
|
||||
- **State**: `internal/crawl` with `robots.go` and `watch.go`, `internal/webfetch`.
|
||||
No living doc. **Finding**: politeness and robots are implemented. The
|
||||
scheduling policy is not written anywhere.
|
||||
- **DoD**
|
||||
- A page is fetched on request and summarised in Russian.
|
||||
- A watched page reports what changed, on a schedule he set by voice.
|
||||
- robots and the politeness delay are honoured, observable in the log.
|
||||
- Only the URL and the utterance leave the box.
|
||||
- **Scenario**: `crawl_watch` *(to write)*.
|
||||
|
||||
### Summaries
|
||||
|
||||
- **State**: no package. **Finding**: summarisation exists inside the world chain
|
||||
and inside digestion, and nothing owns it as a capability he can ask for.
|
||||
- **DoD**
|
||||
- "перескажи" over a note, a feed item, a page or a mail returns a Russian summary.
|
||||
- The summary names its source.
|
||||
- A summary that would invent content is refused, the way `Response.Empty()`
|
||||
gates a world answer.
|
||||
- **Scenario**: `summarise` *(to write)*.
|
||||
|
||||
### Webhooks
|
||||
|
||||
- **State**: only `internal/delivery/telegramsink/intake.go`, which is Telegram's
|
||||
own inbound webhook. **Finding**: there is no general webhook capability in
|
||||
either direction, and no doc says which direction is wanted.
|
||||
- **DoD**
|
||||
- Inbound: an authenticated external event becomes a fact or a nudge candidate.
|
||||
- Outbound: a nudge can post to a configured URL as a reach.
|
||||
- Neither direction is reachable without authentication.
|
||||
- **Scenario**: `webhook_inbound` *(to write)*.
|
||||
|
||||
### Cron jobs
|
||||
|
||||
- **State**: `internal/routine`, `cmd/mavend/tick_routines.go`. **Finding**:
|
||||
routines carry a `Cron` and are a separate mechanism from reminders. Whether
|
||||
"cron jobs" means user-defined scheduled acts or the existing routines is
|
||||
undecided.
|
||||
- **DoD**
|
||||
- A scheduled job is created by voice, with its schedule stated back.
|
||||
- It runs on schedule in `Europe/Samara` and its run is recorded.
|
||||
- A job that runs an act is bound by the same confirmation rules as any act.
|
||||
- Its relationship to routines and to recurring reminders is written down, so
|
||||
three schedulers do not exist.
|
||||
- **Scenario**: `cron_job` *(to write)*.
|
||||
|
||||
### Learning the style
|
||||
|
||||
- **State**: no package. **Finding**: nothing exists beyond
|
||||
`internal/phraser/eval/checks.go`, which scores style and does not learn it.
|
||||
Learning means behavioral, not weights: stored outcomes, no adapter, no
|
||||
training set.
|
||||
- **DoD**
|
||||
- A corrected phrasing is stored as an outcome and changes a later reply.
|
||||
- No model weights change and no training set is built.
|
||||
- What was learned is readable on a page and can be deleted.
|
||||
- **Scenario**: `style_correction` *(to write)*.
|
||||
|
||||
### Learning from mistakes
|
||||
|
||||
- **State**: no package. Same behavioral rule as above.
|
||||
- **DoD**
|
||||
- A dismissed nudge, a corrected phrasing and a repaired route are each stored
|
||||
as an outcome.
|
||||
- A repeated dismissal suppresses that nudge shape.
|
||||
- A repaired route changes the next routing of the same utterance.
|
||||
- Every stored outcome is readable and deletable.
|
||||
- **Scenario**: `learn_from_dismissal` *(to write)*.
|
||||
|
||||
### Command chaining
|
||||
|
||||
- **State**: no package. The `chain` in `internal/router` is the world chain and
|
||||
the source chain, not command chaining. **Finding**: nothing exists.
|
||||
- **DoD**
|
||||
- "напомни мне и запиши это" performs both, or asks which one.
|
||||
- A chain containing an act confirms each act separately.
|
||||
- A failed step stops the chain and names the step that failed.
|
||||
- **Scenario**: `command_chain` *(to write)*.
|
||||
|
||||
## What this file rules out
|
||||
|
||||
A definition of done that a test suite can score. The suite was green during the
|
||||
audit and 22 of 39 capabilities were not live. Every criterion above is
|
||||
observable on the running box.
|
||||
|
||||
A roadmap ordered by code work. None of the four broken capabilities is a code
|
||||
defect. Weather has no configuration block. Nexus has no data. The voice reach
|
||||
has no listener. Step-up has no WebAuthn credential.
|
||||
|
||||
## The documentation gaps this file surfaced
|
||||
|
||||
Applying the "state is a reference" rule found seventeen capabilities with no
|
||||
living doc. Memory is the largest cluster. Facts, notes and the digestion worker
|
||||
have no owning document at all. Proactive is the second. The reminder lifecycle
|
||||
spans three packages with no written contract. `docs/roadmap.md` orders the work,
|
||||
and these gaps are part of it.
|
||||
Reference in New Issue
Block a user