Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
1c1a3cf
docs(admin): complete the port table — LM Studio, Piper TTS, memory wiki
arthware-dev Jun 11, 2026
dabea71
docs: add Open Knowledge Format conformance design
arthware-dev Jun 16, 2026
60c24ae
refactor: single source for mirror frontmatter and rendering
arthware-dev Jun 19, 2026
64b6ee2
feat: declare OKF type on document and capture mirrors
arthware-dev Jun 19, 2026
7002ebb
feat: declare OKF type on member pages
arthware-dev Jun 19, 2026
4a1d259
docs: record the tags convention decision (present-when-nonempty, not…
arthware-dev Jun 19, 2026
abd18ea
refactor: rename mirror_format to vault_entry
arthware-dev Jun 19, 2026
35b9f5b
feat: rename frontmatter added to OKF timestamp
arthware-dev Jun 19, 2026
86de6e3
feat: rename capture source_uri to OKF resource
arthware-dev Jun 19, 2026
1227d0e
feat: promote capture kind to OKF type
arthware-dev Jun 19, 2026
82f8e2c
feat: add OKF resource (per-document URL) to document mirrors
arthware-dev Jun 19, 2026
985c869
docs: align brain design docs with the OKF Phase 1 renames
arthware-dev Jun 19, 2026
bf8e3a7
refactor: move vault-layout conventions into stack.vault
arthware-dev Jun 19, 2026
e651ad4
feat: relative markdown links in entity headers (drop wikilinks)
arthware-dev Jun 19, 2026
10f0efc
docs: reflect the native-relative-links decision
arthware-dev Jun 19, 2026
8ac2541
feat: generate correspondent entity pages
arthware-dev Jun 19, 2026
0be51b5
docs: replace real location Friedrichshafen with fictional Springfield
arthware-dev Jun 19, 2026
fc847bf
refactor: fictionalize real data in code and shipped seeds
arthware-dev Jun 19, 2026
3516140
test: fictionalize real data in test fixtures
arthware-dev Jun 19, 2026
797adc4
docs: fictionalize real data in design docs
arthware-dev Jun 19, 2026
d019841
Merge remote-tracking branch 'origin/main' into okf-conformance
arthware-dev Jun 20, 2026
cffbb75
docs(agent): check branch state vs origin/main before major work
arthware-dev Jun 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/admin-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -489,8 +489,8 @@ Your family members. Generated by the installer. User accounts are seeded on the

```toml
[[users]]
name = "Arthur"
email = "arthur@home.local"
name = "Homer"
email = "homer@home.local"
role = "admin"

[[users]]
Expand Down
2 changes: 1 addition & 1 deletion docs/adr/adr-002-port-mode-first.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Caddy is opt-in, activated when `domain` is set in stack.toml.

**Port mode (domain empty):**
- Services bind to `0.0.0.0:<port>` — reachable from the network
- URLs shown as `hostname:port` (e.g. `mac-arthur.local:42010`)
- URLs shown as `hostname:port` (e.g. `mac-homer.local:42010`)
- Caddy does not start, core only runs Watchtower
- Zero DNS setup required

Expand Down
8 changes: 4 additions & 4 deletions docs/adr/adr-005-bonjour-discovery.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ One instance per famstack server. The service name is the server's
human-readable name from `stack.toml`:

```
Arthur's famstack._famstack._tcp.local.
Homer's famstack._famstack._tcp.local.
```

### Port
Expand All @@ -79,7 +79,7 @@ first API call:
|---|---|---|
| `uuid` | `a1b2c3d4-e5f6-7890-abcd-ef1234567890` | Persistent server identity |
| `version` | `0.1.0` | famstack version (for compatibility checks) |
| `name` | `Arthur's famstack` | Human-readable display name |
| `name` | `Homer's famstack` | Human-readable display name |
| `api` | `/api` | API base path (future-proofing) |

**TXT record size:** ~150 bytes. Well within the DNS-SD 8900-byte limit
Expand Down Expand Up @@ -229,7 +229,7 @@ The QR code fallback is more important on Android.

Browsers cannot perform mDNS discovery. The web dashboard has two paths:

1. **Direct URL.** User navigates to `http://mac-arthur.local:42000` or
1. **Direct URL.** User navigates to `http://mac-homer.local:42000` or
the server's IP. Works on Apple devices (Safari resolves `.local` via
mDNS). Inconsistent on Android browsers.

Expand All @@ -251,7 +251,7 @@ camera, requires no mDNS support, and transfers the API key in one step.
{
"famstack": 1,
"uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"host": "mac-arthur.local",
"host": "mac-homer.local",
"ip": "192.0.2.10",
"port": 42000,
"key": "fs_ak_..."
Expand Down
4 changes: 2 additions & 2 deletions docs/adr/rfc-001-dashboard-and-companion-apps.md
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ Key properties broadcast in the TXT record:
_famstack._tcp.local.
uuid = "a1b2c3d4-..." # persistent, survives IP changes
version = "0.1.0"
name = "Arthur's famstack"
name = "Homer's famstack"
api_port = 42000
```

Expand Down Expand Up @@ -508,7 +508,7 @@ How does a new device find the famstack server? Full protocol in
Not found? → Show "Scan QR code" or "Enter address"
4. App stores the server UUID in Keychain (persistent pairing)
5. Dashboard shows a QR code at /onboard containing:
{ "uuid": "a1b2c3d4-...", "host": "mac-arthur.local",
{ "uuid": "a1b2c3d4-...", "host": "mac-homer.local",
"port": 42000, "key": "..." }
6. Phone scans QR → paired, UUID + API key stored in Keychain
```
Expand Down
1 change: 1 addition & 0 deletions docs/agent/dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ Testing rules:

## Commit & branch rules

- **Check branch state before major work.** `git fetch origin` and see how the working branch relates to `origin/main` (`git log --oneline origin/main..HEAD` and `HEAD..origin/main`) before starting anything substantial, new branch or existing one alike. The branch may be older than you think: `main` advances, and a local `main` can itself be stale. Branch off (or rebase/merge onto) the latest `origin/main`; a branch left behind silently diverges and lands the PR in merge conflicts.
- **Feature branches only.** Never commit to `main`.
- **Semantic prefix required:** `feat:`, `fix:`, `docs:`, `refactor:`, `chore:`, `test:`, `ci:`, `style:`.
- **Message style:** short, end-user POV, present tense. *What changed and why a user cares* - not the internal refactor narrative.
Expand Down
40 changes: 20 additions & 20 deletions docs/design/brain/engram-prototype.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

> Status: Design document
> Created: 2026-04-29
> Author: Arthur + Claude
> Author: Homer + Claude
> Depends on: [knowledge-architecture.md](knowledge-architecture.md), [knowledge-implementation.md](knowledge-implementation.md)
> External: https://github.com/Gentleman-Programming/engram

Expand Down Expand Up @@ -44,7 +44,7 @@ services, knowledge typing with decay, and proactive intelligence.
v
engram (Go binary, MCP + HTTP)
├── SQLite + FTS5 full-text search
├── Project-scoped storage (shared, arthur, sabrina, calendar)
├── Project-scoped storage (shared, homer, marge, calendar)
├── Topic-key upserts (evolving knowledge updates in place)
├── Deduplication via normalized hash
├── Conflict detection + judgment flow
Expand All @@ -68,9 +68,9 @@ How Family Brain concepts land on engram's data model.

| Family Brain | engram | How it works |
|---|---|---|
| Knowledge domain (`shared`, `arthur`, `calendar`) | **Project** | Each domain is a project. `mem_search` filters by project. `mem_context` returns per-project. |
| Knowledge domain (`shared`, `homer`, `calendar`) | **Project** | Each domain is a project. `mem_search` filters by project. `mem_context` returns per-project. |
| Knowledge type (`rule`, `fact`, `habit`, `goal`, `preference`, `context`, `event`) | **Type field** | The `type` field is a string, not an enforced enum. We use our own values. |
| Wiki entry that evolves over time | **Observation + topic_key** | `topic_key="fact/insurance-adac"` -- saving with the same key upserts the existing observation, increments `revision_count`. |
| Wiki entry that evolves over time | **Observation + topic_key** | `topic_key="fact/insurance-duff-insurance"` -- saving with the same key upserts the existing observation, increments `revision_count`. |
| Pointer brain / compact index | **`mem_context` per project** | Returns recent sessions + observations. The agent's map of what's known. |
| Cross-domain search | **`mem_search`** | FTS5 across all projects or filtered to one. Supports type/scope filters. |
| Short-term memory | **Session-scoped observations** | Tied to a Kit Bot conversation session. Temporal context via `mem_timeline`. |
Expand All @@ -91,11 +91,11 @@ How Family Brain concepts land on engram's data model.
id INTEGER auto-increment
session_id TEXT which session created this
type TEXT "rule" | "fact" | "habit" | "goal" | "preference" | "context" | "event"
title TEXT short, searchable -- "Car insurance ADAC"
title TEXT short, searchable -- "Car insurance Duff Insurance"
content TEXT freeform -- structured however we want
project TEXT "shared" | "arthur" | "sabrina" | "calendar"
project TEXT "shared" | "homer" | "marge" | "calendar"
scope TEXT "project" (shared within domain) | "personal"
topic_key TEXT canonical ID for upserts -- "fact/adac/car-insurance"
topic_key TEXT canonical ID for upserts -- "fact/duff-insurance/car-insurance"
normalized_hash TEXT deduplication fingerprint
revision_count INTEGER incremented on topic-key upsert
duplicate_count INTEGER rolled-up exact duplicates
Expand All @@ -109,7 +109,7 @@ deleted_at TIMESTAMP soft-delete (used by decay)

**Topic-key upsert.** When `mem_save` receives a `topic_key` that already exists
within the same project + scope, it updates the existing observation instead of
creating a new one. `revision_count` increments. This is how "ADAC sent a new invoice"
creating a new one. `revision_count` increments. This is how "Duff Insurance sent a new invoice"
updates the existing insurance knowledge rather than creating a duplicate.

**Conflict detection.** When `mem_save` finds observations with similar content,
Expand All @@ -132,7 +132,7 @@ Core tools available to Kit Bot via MCP:
| Tool | What Kit does with it |
|---|---|
| `mem_search` | "What do we know about insurance?" -- FTS5 query, filter by project/type/scope |
| `mem_save` | "Remember: Sabrina is allergic to peanuts" -- save with type=rule, topic_key |
| `mem_save` | "Remember: Marge is allergic to peanuts" -- save with type=rule, topic_key |
| `mem_context` | Session start -- load recent knowledge for the current domain |
| `mem_get_observation` | Drill into a search result for full untruncated content |
| `mem_timeline` | Chronological context around an observation |
Expand Down Expand Up @@ -165,22 +165,22 @@ Premium: EUR 340/year
Policy: KFZ-2024-XXXXX
Coverage: Vollkasko + Haftpflicht
Expires: 2026-06-30
Source: Paperless #247, ADAC Rechnung 2026-03
Source: Paperless #247, Duff Insurance Rechnung 2026-03
```

### Rules (from conversations or manual)

```
Sabrina is allergic to peanuts.
Source: Arthur via Kit Bot, 2026-04-29
Marge is allergic to peanuts.
Source: Homer via Kit Bot, 2026-04-29
```

### Events (from calendar, transient)

```
Dentist appointment for Sabrina
Dentist appointment for Marge
When: 2026-04-17 10:00
Where: Dr. Weber, Friedrichshafen
Where: Dr. Hibbert, Springfield
Source: Calendar sync
```

Expand All @@ -194,9 +194,9 @@ Source: Dream cycle promotion, 2026-04-29
```

Topic keys follow a `{type}/{entity}/{slug}` convention:
- `fact/adac/car-insurance`
- `rule/sabrina/allergy-peanuts`
- `event/calendar/dentist-sabrina-20260417`
- `fact/duff-insurance/car-insurance`
- `rule/marge/allergy-peanuts`
- `event/calendar/dentist-marge-20260417`
- `habit/family/friday-pizza`


Expand Down Expand Up @@ -255,7 +255,7 @@ After classification, POST each fact to engram:
```python
async def _save_knowledge(self, classification: dict, doc_id: int):
person = classification.get("person", "").lower()
project = person if person in ("arthur", "sabrina") else "shared"
project = person if person in ("homer", "marge") else "shared"

for fact in classification.get("facts", []):
slug = slugify(fact[:60])
Expand Down Expand Up @@ -326,7 +326,7 @@ $ fk knowledge show

$ fk knowledge search "insurance" -d personal
[error] unknown domain: personal
Available domains: shared, arthur, sabrina, calendar
Available domains: shared, homer, marge, calendar
```


Expand Down Expand Up @@ -376,7 +376,7 @@ store later means reimplementing the API contract, not rewriting the whole syste

1. Add engram to Kit Bot's MCP server config
2. Kit Bot system prompt: explain available memory tools and when to use them
3. Test: "Kit, remember Sabrina is allergic to peanuts" → `mem_save`
3. Test: "Kit, remember Marge is allergic to peanuts" → `mem_save`
4. Test: "What do we know about insurance?" → `mem_search`
5. Test: "Kit, our insurance went up to EUR 380" → conflict detection + judgment

Expand Down
45 changes: 24 additions & 21 deletions docs/design/brain/family-memory.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ family/memory.git (Forgejo repo, seeded by `memory` stackl
│ │ │ └── YYYY-MM-DD-<slug>-p<paperless_id>.md
│ │ └── _unfiled/
│ │ └── p<paperless_id>.md (when the doc has no usable date)
│ └── correspondents/ (wiki pages for senders: ADAC, Booking.com, …)
│ └── correspondents/ (wiki pages for senders: Duff Insurance, Booking.com, …)
│ └── <slug>.md
├── <entity>/ (one bucket per family member; sender mxid → entity slug)
Expand Down Expand Up @@ -74,24 +74,26 @@ family/memory.git (Forgejo repo, seeded by `memory` stackl

```markdown
---
title: ADAC Kfz-Versicherung 2026
type: document
title: Duff Insurance Kfz-Versicherung 2026
date: 2026-03-15
correspondent: ADAC
correspondent: Duff Insurance
document_type: Rechnung
category: Versicherung
persons: [Homer]
tags: [Versicherung, Fahrzeug, "Person: Homer"]
paperless_id: 247
paperless_url: http://docs.local
resource: http://docs.local/documents/247/details
processing: ai_formatted
model: qwen3.5-vl-7b
source: paperless
added: 2026-05-20T14:23:00Z
timestamp: 2026-05-20T14:23:00Z
---

# ADAC Kfz-Versicherung 2026
# Duff Insurance Kfz-Versicherung 2026

> **From:** [[ADAC]] · **About:** [[Homer]]
> **From:** [Duff Insurance](../../../correspondents/duff-insurance.md) · **About:** [Homer](../../../../homer/about.md)

> [!summary]
> Jährliche Erneuerung der Kfz-Vollkasko, Police KFZ-2026-987.
Expand All @@ -113,8 +115,9 @@ Notes:
in Obsidian; labeled blockquote in Forgejo). Visually distinct from
the OCR body below.
- `Show Document` is a per-document link composed from `paperless_url`
+ `/documents/<id>/details`; frontmatter `paperless_url` keeps the
base URL so scripts can compose other deep links.
+ `/documents/<id>/details`. The same URL is stamped into the OKF
`resource` field; frontmatter `paperless_url` keeps the base URL so
scripts can compose other deep links.
- Sections inside the callout use `**Bold labels**` instead of `##`
headings — Obsidian renders them as section starts within the callout,
and callouts don't nest H2s cleanly.
Expand All @@ -123,19 +126,19 @@ Notes:

```markdown
---
type: bookmark
title: Local-LLM benchmarks roundup
kind: bookmark
date: 2026-05-17
persons: [Arthur]
tags: [local-llms, benchmarks, "Person: Arthur"]
source_uri: https://example.com/llms
persons: [Homer]
tags: [local-llms, benchmarks, "Person: Homer"]
resource: https://example.com/llms
model: qwen3.5-vl-7b
added: 2026-05-17T09:00:00Z
timestamp: 2026-05-17T09:00:00Z
---

# Local-LLM benchmarks roundup

> **About** [[Arthur]]
> **About** [Homer](../../../about.md)
> **Captured** 2026-05-17 · **Kind** bookmark
> **Source** <https://example.com/llms>

Expand All @@ -148,8 +151,8 @@ added: 2026-05-17T09:00:00Z
```

Notes:
- `kind: note` keeps the user's pasted body in a collapsed
`> [!quote]- Original paste` callout below the summary. `kind: bookmark`
- `type: note` keeps the user's pasted body in a collapsed
`> [!quote]- Original paste` callout below the summary. `type: bookmark`
has no body — the URL plus the digest IS the entry.
- No "Action items" section on captures. A bookmark to a Reddit thread
is not a todo; pasting links shouldn't flood the system with chores.
Expand All @@ -160,14 +163,14 @@ The classifier writes one note per filed document into Paperless's
note slot (FTS-searchable):

```
Jährliche Erneuerung der Kfz-Vollkasko bei ADAC. Police KFZ-2026-987,
Jährliche Erneuerung der Kfz-Vollkasko bei Duff Insurance. Police KFZ-2026-987,
Beitrag EUR 340 jährlich, fällig zum 01.04.

- Versicherungsnummer: KFZ-2026-987
- Beitrag: EUR 340,00
- Fälligkeit: 2026-04-01

ADAC → Homer
Duff Insurance → Homer

<!-- archivist-bot -->
```
Expand Down Expand Up @@ -196,7 +199,7 @@ shapes:
{
"source": "docs",
"type": "document.filed",
"summary": "ADAC Kfz-Versicherung 2026 filed (#247)",
"summary": "Duff Insurance Kfz-Versicherung 2026 filed (#247)",
"actor": "@homer:home",
"ts": "2026-05-20T14:23:00Z",
"data": {"paperless_id": 247, "title": "…", "topics": [...], ...}
Expand All @@ -213,7 +216,7 @@ envelope as a content field:
"type": "m.room.message",
"content": {
"msgtype": "m.text",
"body": "Filed: ADAC Kfz-Versicherung 2026 (#247)…",
"body": "Filed: Duff Insurance Kfz-Versicherung 2026 (#247)…",
"format": "org.matrix.custom.html",
"formatted_body": "<p>…</p>",
"dev.famstack.event": { …envelope… }
Expand Down Expand Up @@ -361,7 +364,7 @@ classifier `persons:` list takes precedence.
User replies to a bot's filing message with a correction:

```
[bot] ✅ Filed: ADAC Kfz-Versicherung 2026 (#247) — Versicherung | Homer | …
[bot] ✅ Filed: Duff Insurance Kfz-Versicherung 2026 (#247) — Versicherung | Homer | …
[user reply]: das ist eigentlich Marges Versicherung, nicht Homers
```

Expand Down
10 changes: 5 additions & 5 deletions docs/design/brain/interaction-patterns.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

> Status: Design note — future direction, no implementation yet
> Created: 2026-06-09
> Author: Arthur + Claude
> Author: Homer + Claude
> Depends on: [topic-rooms.md](topic-rooms.md) (room-state-as-intent, the seed of this design), [knowledge-architecture.md](knowledge-architecture.md), [wiki-engine.md](wiki-engine.md)

## Why this exists
Expand Down Expand Up @@ -50,7 +50,7 @@ Reactions run in **both directions** — the user reacts to signal intent to the
| 👎 | "Bot, your classification was wrong" — opens a reply-thread correction prompt |
| 📅 | Surface this for the calendar / reminder bot (once it lands) |

A 🔖 on Homer's message means "save this." A 👎 on the bot's `Filed: ADAC camping addendum` means "you got the topic wrong." A 🗑 on the bot's own capture confirmation means "undo." Each binding maps to an existing handler the archivist already implements; the reaction is just a different trigger.
A 🔖 on Homer's message means "save this." A 👎 on the bot's `Filed: Duff Insurance camping addendum` means "you got the topic wrong." A 🗑 on the bot's own capture confirmation means "undo." Each binding maps to an existing handler the archivist already implements; the reaction is just a different trigger.

#### Bot reacts to the user's message to signal processing state

Expand Down Expand Up @@ -86,10 +86,10 @@ Bounded intent set (so the prompt stays tight and dispatch stays deterministic):
| Intent | Example | Dispatch |
|---|---|---|
| save | `@archivist save this https://...` | `_handle_capture` |
| forget | `@archivist forget that ADAC stuff` | redaction + tombstone |
| forget | `@archivist forget that Duff Insurance stuff` | redaction + tombstone |
| search | `@archivist what did we note about camping?` | `_handle_search` (existing) |
| reclassify | `@archivist this is actually Marge's, not Homer's` | `_handle_reply_reprocess` (existing) |
| remind | `@archivist remind me about the ADAC renewal in November` | reminder bot when it lands |
| remind | `@archivist remind me about the Duff Insurance renewal in November` | reminder bot when it lands |
| status | `@archivist what have you filed today?` | digest |

A thin LLM pass classifies the mention's intent into one of those slots, then dispatches to the existing handler. The user does not learn a command syntax — they ask naturally; the LLM bridges to the existing surface.
Expand Down Expand Up @@ -151,7 +151,7 @@ That alone gives the family a real-feeling per-message knob without ripping out

## Status of this document

A design note, not a prescriptive plan. Captures the direction agreed in the 2026-06-09 session as a marker so future-Arthur and future-Claude do not have to rederive the conclusion. When the first layer ships, this document graduates into a proper design doc — until then, it sits here as the canonical reference for "we know the routing is fragile; here's the direction we are taking when we get to it."
A design note, not a prescriptive plan. Captures the direction agreed in the 2026-06-09 session as a marker so future-Homer and future-Claude do not have to rederive the conclusion. When the first layer ships, this document graduates into a proper design doc — until then, it sits here as the canonical reference for "we know the routing is fragile; here's the direction we are taking when we get to it."

## Related

Expand Down
Loading
Loading