Settle identity (single user), routing (rules + LLM + clarify), observability (capped log)

This commit is contained in:
2026-10-10 15:58:01 +11:00
parent fb0210743f
commit 1f27c34e80
4 changed files with 90 additions and 15 deletions

View File

@@ -54,6 +54,11 @@ See `docs/ask-contract.md`. The Hub never learns a project's data model.
10. A data project answers `/ask` read-only. Never change state on a query. 10. A data project answers `/ask` read-only. Never change state on a query.
11. High-consequence control does not route through the Hub. The admin app calls 11. High-consequence control does not route through the Hub. The admin app calls
the service directly. the service directly.
12. Route with the capability list, `routing.md`, and the LLM. Ask the user when
unsure. Never route a control action.
13. Log every request. Append only. Cap the size. No secrets, no full personal
data.
14. There is one identity: Sam. Keep the `asker` field for a future second person.
## Build order ## Build order

View File

@@ -23,13 +23,25 @@ own interface, or through an approval flow. Never through `/ask`.
## Endpoint ## Endpoint
Every data project exposes two endpoints on its own port: Every data project exposes three endpoints on its own port:
``` ```
GET /health -> {"ok": true} GET /health -> {"ok": true}
GET /capabilities -> the topics this project can answer
POST /ask -> answer a question about this project's data POST /ask -> answer a question about this project's data
``` ```
## Capabilities (for routing)
The Hub reads this list to choose a project.
```json
{ "project": "email_agent", "topics": ["email", "inbox", "mail"], "people": ["sam"] }
```
Keep the topic list short and plain. The Hub uses these topics plus `routing.md`.
Do not put private data in this list.
### Request ### Request
```json ```json
@@ -74,7 +86,7 @@ Never guess. Never invent. Say `reason` instead.
## Rules for the project ## Rules for the project
1. **Read only.** `/ask` never writes anything. 1. **Read only.** `/ask` never writes anything. `/capabilities` never writes.
2. **Answer for one person at most.** Check `asker` may see `person`. 2. **Answer for one person at most.** Check `asker` may see `person`.
3. **One short sentence.** No lists unless asked. 3. **One short sentence.** No lists unless asked.
4. **Fast.** Answer in under 3 seconds, or return `answer: null`. 4. **Fast.** Answer in under 3 seconds, or return `answer: null`.

View File

@@ -64,6 +64,18 @@ interface. Each works without the Hub.
A high-consequence control does not route through the Hub. A high-consequence control does not route through the Hub.
## Routing
The Hub picks a project with three things: a capability list from each project, a
human-editable `routing.md`, and an LLM. The LLM returns one of four answers:
route, fanout, clarify, or no_match. When it is unsure, it asks the user a short
question. It never routes a control action.
## Observability
The Hub writes one append-only log. It records the request, the route, the project
asked, and the latency. The log is capped. It holds no secrets.
## Reply Shapes ## Reply Shapes
The Hub returns one reply. The reply can hold text, spoken audio (TTS), Artemis The Hub returns one reply. The reply can hold text, spoken audio (TTS), Artemis

View File

@@ -108,6 +108,37 @@ A query goes through the Hub. A high-consequence control does **not**. The admin
app talks to the internet control service directly. Voice may start it. Only the app talks to the internet control service directly. Voice may start it. Only the
admin app confirms. admin app confirms.
### Routing
The Hub must decide which project to ask. It uses three things:
1. **A capability list.** Each project declares the topics it can answer. The Hub
collects this automatically. See `docs/ask-contract.md`.
2. **A rules file.** `routing.md` in the Hub. A human edits it. It holds the
judgment calls: topic → project, the preferred project when two overlap, and
things the Hub must never route.
3. **An LLM.** It reads the question, the capability list, and the rules file. It
returns one of four answers:
- **route** — send to one project.
- **fanout** — ask several projects, then merge ("what do I have on today?").
- **clarify** — ask the user a short question ("school or email?").
- **no_match** — say it does not know.
The rules file is **not** `AGENTS.md`. `AGENTS.md` tells coding agents how to work.
`routing.md` tells the running Hub how to choose. Keep them separate.
The Hub never routes a control action. Those go through the admin app.
### Observability
The Hub writes one append-only log. It records every request: time, source,
question, the routing answer, the project asked, the latency, and the reply shape.
- **Append only.** Never edit a line.
- **Cap the size.** Rotate at a fixed size. Keep a fixed number of days.
- **No secrets.** Never log tokens.
- **No full personal data.** Log the question and the route, not the inbox.
--- ---
## Where each system fits ## Where each system fits
@@ -159,8 +190,10 @@ Each project is a **peer**. These are talk directions, not ownership.
- **A spoken keycode is not the lock.** It can be overheard and misheard. Voice - **A spoken keycode is not the lock.** It can be overheard and misheard. Voice
may *ask*; the admin app **confirms** with "Did you ask for this?". may *ask*; the admin app **confirms** with "Did you ask for this?".
- **Approval is required** for parental controls and Paperclip jobs. - **Approval is required** for parental controls and Paperclip jobs.
- **Identity is not solved yet.** Voice has no identity. Personal data (inbox, - **One identity: Sam.** The Hub serves Sam only. The `/ask` contract keeps the
school, screen time) needs it. See open items. `asker` field, so a second person can be added later.
- **Voice is spoken aloud.** With one identity, anyone in the house could imitate
Sam. Consequential actions are protected by the admin-app tap, not by the voice.
--- ---
@@ -185,14 +218,27 @@ Each project is a **peer**. These are talk directions, not ownership.
## What is still open ## What is still open
1. **Identity.** How does the Hub know who is speaking? Voice has none. This is 1. **Approval service.** One shared mechanism, or one per project? (See below.)
the top gap, because personal data now flows. 2. **Service tokens.** One bearer token per project, stored in `environment.d`.
2. **Routing.** How does the Hub pick the project to ask? A small capability 3. **Job contract.** Exact fields for a Paperclip job (title, brief, project, agent).
registry, or an LLM choice. 4. **Artemis voice.** Stream or file. When does phone TTS get used?
3. **Overlap.** "What do I have on today?" may match several projects. Ask one, 5. **Paperclip layout.** One company or several. Which projects.
or ask all and merge? 6. **Routing rules.** The first draft of `routing.md`.
4. **Approval service.** One shared mechanism, or one per project?
5. **Service tokens.** One bearer token per project, stored in `environment.d`. ### The approval question
6. **Job contract.** Exact fields for a Paperclip job (title, brief, project, agent).
7. **Artemis voice.** Stream or file. When does phone TTS get used? Three things need a human "yes":
8. **Paperclip layout.** One company or several. Which projects.
- cutting the kids' internet (admin app),
- filing a Paperclip job (Hub → Paperclip),
- a project writing to the calendar (email adds an event, school books an
interview).
Two options:
- **One shared approval.** The Hub (or a tiny approvals service) sends the request
to the admin app, you tap, it runs. One mechanism, one UI.
- **One per project.** Each project asks in its own way. Simpler now, more to
maintain later.
Recommended: **one shared approval.** The admin app already exists.