Blame

16dc7c Claude (MCP) 2026-03-17 16:47:00
[mcp] Add disk usage caps implementation plan
1
---
2
category: spec
3
tags: [quota, disk-usage, plugin, plan]
4
last_updated: 2026-03-17
5
confidence: high
6
---
7
8
# Per-Wiki Disk Usage Caps Plan
9
10
Quota enforcement code already exists in `resolver.py` (50MB limit, write rejection) but reads from `robot.db` where `disk_usage_bytes` is always 0. This plan wires up real measurements.
11
12
## Architecture
13
14
- **Measurement:** `du -sb` on entire wiki directory (repo + wiki.db + FAISS index)
15
- **Storage:** New `stats` table in per-wiki `wiki.db` (not robot.db) — resolver already operates per-wiki-DB
16
- **Update timing:** Page count on every save/delete (via otterwiki plugin hook). Disk usage via cron only (15-min interval, acceptable lag).
17
- **Enforcement:** Existing resolver code — reads stats from `wiki.db` after DB swap, strips WRITE permission or returns 413 for API requests.
18
19
## Components
20
21
### 1. `stats` table in `wiki.db`
22
23
```sql
24
CREATE TABLE IF NOT EXISTS stats (
25
name VARCHAR(64) PRIMARY KEY,
26
value TEXT,
27
updated_at DATETIME
28
);
29
```
30
31
Keys: `page_count`, `disk_usage_bytes`. Seeded in `_init_wiki_db()`.
32
33
### 2. New plugin: `otterwiki-wikistats`
34
35
Follows `otterwiki-api` pattern. Hooks: `page_saved`, `page_deleted` → update `page_count` via `storage.list()`. Disk usage left to cron. Must use `_state["storage"]` dict (not `self.storage`) for multi-tenant correctness — resolver patches this in `_swap_storage()`.
36
37
### 3. Resolver changes
38
39
- Add `_get_wiki_stats(wiki_dir)` — reads `stats` table from `wiki.db`
40
- Move quota check to after `_swap_database()` — read real disk_usage from `wiki.db` with fallback to `robot.db`
41
- Add `otterwiki_wikistats._state["storage"]` patch in `_swap_storage()`
42
- Seed `stats` table in `_init_wiki_db()`
43
44
### 4. Cron backstop update
45
46
Update `wiki-quota.sh.j2` to write to each wiki's `wiki.db` stats table (primary) AND keep updating `robot.db` for the dashboard API.
47
48
## User Experience
49
50
- **API/MCP:** HTTP 413 with `"Wiki quota exceeded (50MB limit)"`
51
- **Web UI:** 403 on save (WRITE stripped). Generic permission denied — quota-specific message is a follow-up.
52
- **Dashboard:** Reads `disk_usage_bytes` from `robot.db` via management API (15-min lag from cron).
53
54
## Files to Modify
55
56
| Repo | File | Change |
57
|---|---|---|
58
| robot.wtf | `app/resolver.py` | `_get_wiki_stats()`, quota check after swap, stats table in init, storage patch |
59
| robot.wtf | `ansible/roles/quota/templates/wiki-quota.sh.j2` | Write to wiki.db + robot.db |
60
| robot.wtf | `ansible/roles/deploy/tasks/main.yml` | pip install otterwiki-wikistats |
61
| new repo | `otterwiki-wikistats` | Plugin: setup, page_saved, page_deleted hooks |
62
63
## Test Plan
64
65
1. Quota read from wiki.db — over limit → WRITE stripped
66
2. Under limit → pass-through
67
3. API over-quota → 413
68
4. Fallback to robot.db when stats table missing
69
5. Plugin: page count updated on save/delete
70
6. Plugin: uses `_state["storage"]` not stale reference
71
72
## Open Questions
73
74
1. Quota check must move to after `_swap_database()` — verify no regression on API 413 path
75
2. Cron user permissions on wiki.db files
76
3. Plugin `_state` dict convention — follow existing pattern from `otterwiki-api`