@basicmemory/openclaw-basic-memory 0.2.4 → 0.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -1
- package/bm-client.ts +55 -21
- package/commands/cli.ts +0 -1
- package/commands/skills.ts +51 -2
- package/config.ts +0 -17
- package/context-engine/basic-memory-context-engine.ts +25 -74
- package/dist/bm-client.d.ts +2 -0
- package/dist/bm-client.js +42 -32
- package/dist/commands/cli.js +0 -1
- package/dist/commands/skills.js +41 -2
- package/dist/config.d.ts +0 -5
- package/dist/config.js +0 -10
- package/dist/context-engine/basic-memory-context-engine.d.ts +13 -73
- package/dist/index.d.ts +2 -1
- package/dist/index.js +21 -11
- package/dist/tools/list-memory-projects.js +2 -1
- package/index.ts +23 -12
- package/openclaw.plugin.json +2 -15
- package/package.json +5 -4
- package/scripts/fetch-skills.ts +62 -67
- package/scripts/setup-bm.sh +27 -10
- package/skills/manifest.json +25 -0
- package/skills/memory-capture/SKILL.md +275 -0
- package/skills/memory-ci-capture/SKILL.md +56 -0
- package/skills/memory-continue/SKILL.md +148 -0
- package/skills/memory-curate/SKILL.md +243 -0
- package/skills/memory-literary-analysis/SKILL.md +15 -12
- package/skills/memory-notes/SKILL.md +22 -6
- package/skills/memory-onboarding/SKILL.md +142 -0
- package/skills/memory-onboarding/evals/evals.json +29 -0
- package/skills/memory-onboarding/references/assistant-setup.md +64 -0
- package/skills/memory-onboarding/references/conventions.md +118 -0
- package/skills/memory-onboarding/references/domain-playbooks.md +159 -0
- package/skills/memory-onboarding/references/schema-guide.md +136 -0
- package/skills/memory-schema/SKILL.md +7 -5
- package/skills/memory-tasks/SKILL.md +1 -1
- package/tools/list-memory-projects.ts +2 -1
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Assistant Setup — Loading Instructions Every Session
|
|
2
|
+
|
|
3
|
+
The knowledge base is self-describing: its rules live in instruction notes inside it. But a fresh assistant session doesn't know those notes exist. This reference covers the last mile — a **persistent instruction stub** in whatever always-loaded mechanism the user's platform provides, pointing at the startup router.
|
|
4
|
+
|
|
5
|
+
## The stub pattern
|
|
6
|
+
|
|
7
|
+
The stub is deliberately tiny. It carries only:
|
|
8
|
+
|
|
9
|
+
1. Which Basic Memory **project** to use (always, explicitly)
|
|
10
|
+
2. The instruction to **read the startup router before any knowledge-base work** and follow its dispatch table
|
|
11
|
+
3. The 3–5 rules that must hold **before** the router loads (because they govern the loading itself)
|
|
12
|
+
|
|
13
|
+
Everything else — conventions, schemas, workflows, reference data — lives in the router and the notes it dispatches to. This keeps the always-loaded footprint small and means the user edits notes, not platform settings, when conventions change.
|
|
14
|
+
|
|
15
|
+
### Stub template
|
|
16
|
+
|
|
17
|
+
Adapt the wording; keep the shape:
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
You maintain my knowledge base in Basic Memory, project "{project-name}".
|
|
21
|
+
|
|
22
|
+
Rules that apply before anything else:
|
|
23
|
+
1. Every Basic Memory call (read, search, write, list) uses project "{project-name}" explicitly.
|
|
24
|
+
2. At the start of every session, before any knowledge-base work, read the note
|
|
25
|
+
"Startup Router" (memory://instructions/startup-router) and follow its dispatch
|
|
26
|
+
table for the task at hand. If that reference doesn't resolve, search for the
|
|
27
|
+
note by title before doing anything else. Take no write actions until the
|
|
28
|
+
notes it lists are loaded.
|
|
29
|
+
3. Folder paths use exact casing as given in the instruction notes.
|
|
30
|
+
4. Before overwriting any existing note, read it in full first.
|
|
31
|
+
5. When unsure where something belongs, ask me instead of guessing.
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Rules 1, 3, 4 are in the stub rather than the router because they protect the system even if the router fails to load or has been corrupted.
|
|
35
|
+
|
|
36
|
+
## Where the stub goes
|
|
37
|
+
|
|
38
|
+
Determine what platform and mechanism **you** are running in, and give the user concrete steps for it. Common mechanisms, by shape rather than brand:
|
|
39
|
+
|
|
40
|
+
| Mechanism shape | Typical examples | How to place the stub |
|
|
41
|
+
|:--|:--|:--|
|
|
42
|
+
| **Project-level instructions** | A "project" or "space" in a chat app with a custom-instructions field | Paste the stub into the project instructions. Best option when available — scoped, persistent, and editable. |
|
|
43
|
+
| **Global custom instructions / personalization** | Account-level "how should I respond" settings | Works, but applies to every conversation — wrap the stub in "When I ask about my knowledge base: ...". |
|
|
44
|
+
| **Agent context files** | A markdown file the agent auto-loads from the working directory or home directory (many CLI/desktop agents support one) | Add the stub as a section of that file. |
|
|
45
|
+
| **System prompt** | Self-hosted assistants, API integrations, custom apps | Prepend the stub to the system prompt. |
|
|
46
|
+
| **No persistent mechanism** | Bare chat with MCP tools | Fall back: create a note titled something unmissable like `START HERE — Assistant Instructions` at the KB root, and teach the user to open each session with "read Start Here first". Weakest option; say so honestly. |
|
|
47
|
+
|
|
48
|
+
If you can't determine the platform, show the stub and this table and let the user place it. If the user runs the KB from multiple assistants/platforms (desktop app + CLI + phone), place the stub in each — the router being shared is exactly what keeps them consistent.
|
|
49
|
+
|
|
50
|
+
## The router reference must be durable
|
|
51
|
+
|
|
52
|
+
Inside the stub, give the router's **title and `memory://` path together, with a title-search fallback** — never a raw permalink string copied from a tool result. No single identifier is unconditionally durable: a retitled note breaks title lookups, a recreated note derives a fresh permalink, and a moved note's path changes when `update_permalinks_on_move` is enabled (it defaults to off). The stub is the one piece of the system that isn't a note and won't be caught by note-level maintenance, so it carries both identifiers plus the fallback — that combination self-heals when any one of them goes stale.
|
|
53
|
+
|
|
54
|
+
## Verification test
|
|
55
|
+
|
|
56
|
+
Never call Phase 4 done without testing. With the user:
|
|
57
|
+
|
|
58
|
+
1. **Simulate a fresh session** — new conversation on their platform (or in this session, deliberately act as if starting cold).
|
|
59
|
+
2. Give a natural task from one of their domains: "add a task: call the dentist Tuesday."
|
|
60
|
+
3. **Watch the order of operations.** Pass = the assistant reads the router, loads the dispatched instruction note(s), THEN writes — correct folder, correct schema fields, correct naming. Fail = it writes first or freelances the format.
|
|
61
|
+
4. If it fails: is the stub actually saved? Does the router reference resolve (test with `read_note`)? Is the "before any knowledge-base work" wording strong enough? Fix and re-test.
|
|
62
|
+
5. Also verify the **escape hatch**: ask something ambiguous ("save this somewhere") and confirm the assistant asks rather than guesses.
|
|
63
|
+
|
|
64
|
+
Leave the user with this framing: the stub + router is the system's ignition. If future sessions ever start behaving inconsistently, the first diagnostic is always "did the router get loaded?"
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Conventions & Discipline
|
|
2
|
+
|
|
3
|
+
The structural patterns that keep a Basic Memory knowledge base coherent as it grows. These are defaults, not laws — but each exists because its absence causes a specific, recurring failure. When you relax one for a light-use setup, tell the user what they're trading away.
|
|
4
|
+
|
|
5
|
+
## Contents
|
|
6
|
+
|
|
7
|
+
1. [The startup router](#the-startup-router)
|
|
8
|
+
2. [Instruction notes](#instruction-notes)
|
|
9
|
+
3. [Changelog tables](#changelog-tables)
|
|
10
|
+
4. [Index notes](#index-notes)
|
|
11
|
+
5. [Linking discipline](#linking-discipline)
|
|
12
|
+
6. [Write discipline](#write-discipline)
|
|
13
|
+
7. [Known failure modes](#known-failure-modes)
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## The startup router
|
|
18
|
+
|
|
19
|
+
One note — suggested location `Instructions/Startup Router` — that any assistant reads at the start of every session, before doing any knowledge-base work. It is the single entry point to the whole system. It contains:
|
|
20
|
+
|
|
21
|
+
1. **A read-me-first banner** — "Read this note at the start of every session, before any task work."
|
|
22
|
+
2. **Non-negotiable rules** — the handful of rules that apply to every task type and can't be overridden by anything else (e.g. "always search before creating", "always pass the project name explicitly"). Keep this list short — five to eight items. A rule that only matters for one domain belongs in that domain's instruction note instead.
|
|
23
|
+
3. **A dispatch table** — task type → which notes to load, in order:
|
|
24
|
+
|
|
25
|
+
| Task type | Load in order |
|
|
26
|
+
|:--|:--|
|
|
27
|
+
| Task / project work | 1. `memory://instructions/task-instructions` · 2. `[[Task Board]]` |
|
|
28
|
+
| New contact / person update | 1. `memory://instructions/people-instructions` |
|
|
29
|
+
| Expense / subscription | 1. `memory://instructions/finance-instructions` · 2. `[[Subscriptions Index]]` |
|
|
30
|
+
| Anything else / ambiguous | load all instruction notes |
|
|
31
|
+
|
|
32
|
+
4. **Known failure modes** — a running list of documented mistakes to check before writing (see below).
|
|
33
|
+
5. **Canonical reference data** — if the system has a roster-like table (accounts, categories, family members, clients), keep the ONE canonical copy here and make other notes point to it. Duplicated rosters drift apart; nobody notices until something routes to the wrong place.
|
|
34
|
+
|
|
35
|
+
**Why a router instead of one big rules note:** context is finite. The router is small and always loaded; everything else loads only when the task needs it. It also gives the system one place to grow — a new domain is a new dispatch row, not a rewrite.
|
|
36
|
+
|
|
37
|
+
The router itself is a note, so it gets a changelog table and observations like any other. When the user changes a convention, the change lands here or in a domain instruction note — with a changelog row — so the system stays self-describing.
|
|
38
|
+
|
|
39
|
+
## Instruction notes
|
|
40
|
+
|
|
41
|
+
One note per domain (`Instructions/Task Instructions`, `Instructions/Finance Instructions`, ...) holding everything an assistant needs to work in that domain:
|
|
42
|
+
|
|
43
|
+
- **Scope** — what this note covers and what it doesn't ("for vendor stuff, see [[Vendor Instructions]]")
|
|
44
|
+
- **Folder map** — where each note type lives, with exact casing
|
|
45
|
+
- **Schemas in play** — table of note types, their schema notes, and required observations
|
|
46
|
+
- **Naming conventions** — title formats with examples, date formats, status vocabularies
|
|
47
|
+
- **Workflows** — step-by-step for the recurring operations ("closing a task: 1. set `[status] done` 2. add close date 3. append row to [[Task Board]]")
|
|
48
|
+
- **Domain-specific rules** — anything true here that isn't true globally
|
|
49
|
+
|
|
50
|
+
Write instruction notes for a reader with zero context: the assistant loading it next month is effectively a stranger. Explain *why* behind rules that aren't obvious — an assistant that understands the reason applies the rule correctly in situations the rule didn't anticipate.
|
|
51
|
+
|
|
52
|
+
## Changelog tables
|
|
53
|
+
|
|
54
|
+
Every structural or long-lived note carries exactly one changelog table, immediately below the title:
|
|
55
|
+
|
|
56
|
+
```markdown
|
|
57
|
+
| Notes | Modification Date | Approved By |
|
|
58
|
+
|:--|:--|:--|
|
|
59
|
+
| Initial Document | July 12, 2026 | {user's name} |
|
|
60
|
+
| Added renewal-date field to all subscription rows | August 3, 2026 | {user's name} |
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Rules:
|
|
64
|
+
- **Append, never edit.** Existing rows are history; new rows record change.
|
|
65
|
+
- **One table per note.** Before overwriting any existing note, read the full note first, carry the existing changelog forward verbatim, and append your row. If you ever find two tables, merge them into one at the top before writing.
|
|
66
|
+
- **Consistent date format** — pick one (`Month D, YYYY` works well) and use it everywhere.
|
|
67
|
+
|
|
68
|
+
Why: an assistant (or the user) reading a note months later can see what changed, when, and on whose authority — without any external version control. It also forces the read-before-overwrite habit that prevents accidental content loss.
|
|
69
|
+
|
|
70
|
+
For a light setup, changelogs on *instruction notes, schemas, and indexes* are the non-negotiable core; changelogs on every individual content note are optional.
|
|
71
|
+
|
|
72
|
+
## Index notes
|
|
73
|
+
|
|
74
|
+
An index is a note containing a table of contents for one domain — one row per note, with wiki-links and a few key columns (status, date, owner):
|
|
75
|
+
|
|
76
|
+
```markdown
|
|
77
|
+
# Task Board
|
|
78
|
+
|
|
79
|
+
| Task | Status | Due |
|
|
80
|
+
|:--|:--|:--|
|
|
81
|
+
| [[Fix gutter downspout]] | in-progress | July 20, 2026 |
|
|
82
|
+
| [[Renew car registration]] | open | August 1, 2026 |
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Rules:
|
|
86
|
+
- **Update the index as part of any change to its members** — a stale index is worse than none, because it gets trusted.
|
|
87
|
+
- **Full overwrite, not section edits.** When updating an index with `write_note`, always rewrite the whole note (read → modify → overwrite). Piecemeal section edits on tables are easy to get wrong — duplicated or mis-scoped sections — while a full overwrite is deterministic.
|
|
88
|
+
- Not every domain needs one. Indexes earn their keep where the user will ask "show me all X with status Y" or wants a glanceable board. Journals and reference notes usually don't need one — search covers them.
|
|
89
|
+
|
|
90
|
+
## Linking discipline
|
|
91
|
+
|
|
92
|
+
- **Bidirectional links for paired entities.** If a task note links its project, the project note links its tasks. If a device links its owner, the owner links the device. One-directional links rot: graph traversal from the unlinked side finds nothing. Make "both directions, always" a rule for whichever entity pairs the user's system has.
|
|
93
|
+
- **Relations sections for typed links, inline wiki-links for prose.** `- part_of [[Kitchen Renovation]]` in Relations; "discussed with [[Dana Reyes]]" in the body. Both create graph edges.
|
|
94
|
+
- **Reference notes by title or `memory://` path — never by raw permalink strings.** A permalink is derived from the note's title and location, so a note that gets recreated or re-derived ends up with a different one and hardcoded strings silently break. `[[Title]]` resolves by title and `memory://` URLs match on path, so both survive reorganization.
|
|
95
|
+
- **Link targets may not exist yet.** `[[Future Note]]` is valid and resolves when the note is created — use this to sketch structure ahead of content.
|
|
96
|
+
|
|
97
|
+
## Write discipline
|
|
98
|
+
|
|
99
|
+
The habits that prevent 90% of knowledge-base corruption:
|
|
100
|
+
|
|
101
|
+
1. **Search before create.** Always search (try 2–3 query variations: full name, abbreviation, keywords) before writing a new note. Duplicates fragment the graph. If the entity exists, `edit_note` it instead. For people and other proper nouns, use **title search** — semantic search is unreliable for names.
|
|
102
|
+
2. **Exact casing, exact paths.** `Tasks/` and `tasks/` are different directories. Take folder paths from the instruction note, character for character. If a write would create a new top-level folder you didn't plan, that's a casing error — stop and fix.
|
|
103
|
+
3. **Explicit project on every call.** In multi-project setups, pass the project explicitly on every read, search, and write. Writes to the wrong project are invisible until much later.
|
|
104
|
+
4. **Read before overwrite.** Full-note overwrites must start from the current content — never reconstruct a note from memory of what it contained.
|
|
105
|
+
5. **Watch write results.** If a write returns a permalink ending in `-1` or `-2`, you just created a duplicate: delete it and re-write the original with overwrite semantics.
|
|
106
|
+
6. **Prefer targeted edits.** `edit_note` (append an observation, find-and-replace a status) over full overwrites wherever possible — smaller blast radius.
|
|
107
|
+
|
|
108
|
+
## Known failure modes
|
|
109
|
+
|
|
110
|
+
Keep a "Known Failure Modes" section in the startup router and **add to it whenever a mistake happens twice**. Documented mistakes stop recurring; undocumented ones don't. Seed it during onboarding with the universal ones:
|
|
111
|
+
|
|
112
|
+
- Duplicate notes from skipping search-before-create (or from name-search using semantic instead of title matching)
|
|
113
|
+
- Wrong-casing paths creating parallel folder trees
|
|
114
|
+
- Piecemeal section edits mangling index tables (duplicated or mis-scoped sections)
|
|
115
|
+
- Second changelog table created by overwriting without reading first
|
|
116
|
+
- Writes landing in the wrong project when project isn't passed explicitly
|
|
117
|
+
|
|
118
|
+
Each entry: the mistake, how to recognize it, what to do instead. This list is the system's immune memory — it's often the highest-value section in the whole knowledge base.
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# Domain Playbooks
|
|
2
|
+
|
|
3
|
+
Starter kits for the common things people track. Each gives folders, a schema sketch, naming, an index decision, and expansion ideas. These are starting points — adapt every one to the user's Phase 1 answers rather than installing verbatim. Statuses, folder names, and required fields should use the user's own vocabulary where they have one.
|
|
4
|
+
|
|
5
|
+
Every domain also gets: an instruction note (`Instructions/{Domain} Instructions`), a row in the startup router's dispatch table, and templates for its note types.
|
|
6
|
+
|
|
7
|
+
## Contents
|
|
8
|
+
|
|
9
|
+
1. [Tasks & projects](#tasks--projects)
|
|
10
|
+
2. [Notes & journal](#notes--journal)
|
|
11
|
+
3. [People & contacts](#people--contacts)
|
|
12
|
+
4. [Research](#research)
|
|
13
|
+
5. [Finances](#finances)
|
|
14
|
+
6. [Procedures](#procedures)
|
|
15
|
+
7. [Health & habits](#health--habits)
|
|
16
|
+
8. [Assets](#assets)
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Tasks & projects
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
Tasks/
|
|
24
|
+
├── Task Board.md ← index: open/in-progress tasks
|
|
25
|
+
└── {task notes}
|
|
26
|
+
Projects/
|
|
27
|
+
├── {Project Name}/ ← one folder per active multi-step project
|
|
28
|
+
│ └── {Project Name}.md ← project note; links its tasks
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
- **Task schema:** `status(enum): [open, in-progress, blocked, done]` · `due?` · `priority?(enum)` · `project?: Project`
|
|
32
|
+
- **Project schema:** `status(enum): [active, paused, done]` · `goal` · `target_date?`
|
|
33
|
+
- **Naming:** tasks get short imperative titles (`Renew car registration`); completed tasks can be retitled with a date prefix (`2026-07-12 — Renew car registration`) when archived, so archives sort chronologically.
|
|
34
|
+
- **Index:** yes — the Task Board is usually the single most-used note in the system. Open items only; done tasks drop off the board (search finds them).
|
|
35
|
+
- **Bidirectional rule:** task ↔ project, both directions. The `project?: Project` field is a relation field — satisfied by `- project [[Project Name]]` in the task's Relations (the relation type must match the field name for validation).
|
|
36
|
+
- **Key workflow to document:** closing a task (set status, add close date, update board, move to archive folder if using one).
|
|
37
|
+
- **Expansions:** meeting notes with action items that become tasks; a weekly-review note; recurring-task conventions.
|
|
38
|
+
|
|
39
|
+
## Notes & journal
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
Journal/
|
|
43
|
+
└── {YYYY-MM-DD}.md ← one note per day
|
|
44
|
+
Notes/
|
|
45
|
+
└── {topic notes} ← ideas, things learned, free-form
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
- **Journal Entry schema (keep it tiny — one required field):** `date` · `mood?(enum)` · `highlight?`. Even the loosest domain gets a schema and observations — but here the observation load is deliberately one line (`- [date] 2026-07-12`, plus `[highlight]`/`[idea]`/`[mood]` when they apply), so capture stays frictionless.
|
|
49
|
+
- **Note schema (topic notes):** `note_type?(enum): [idea, reference, learning]` — one optional field; the requirement is simply that every note carries at least one observation (`[idea]`, `[source]`, `[lesson]`...).
|
|
50
|
+
- **Naming:** `YYYY-MM-DD` for journal (sorts itself); descriptive titles for topic notes.
|
|
51
|
+
- **Index:** no. Date-named journals self-organize; topic notes are found by search.
|
|
52
|
+
- **The one convention that matters:** when a journal entry mentions a person, project, or topic that has (or deserves) a note, wiki-link it. Journals become the connective tissue of the graph.
|
|
53
|
+
- **Expansions:** weekly reflection summarizing the week's entries; automatic extraction of `[idea]` observations into topic notes.
|
|
54
|
+
|
|
55
|
+
## People & contacts
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
People/
|
|
59
|
+
└── {Full Name}.md ← one note per person
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
- **Schema:** `relationship?(enum): [family, friend, coworker, professional, acquaintance]` · `email?` · `phone?` · `company?` · `last_contact?`
|
|
63
|
+
- **Naming:** full name, consistently — `Dana Reyes`, never `dana` in one note and `Dana R.` in another. Titles are graph identifiers.
|
|
64
|
+
- **Index:** optional — useful if the user wants a follow-up board (`last_contact` older than N months).
|
|
65
|
+
- **Critical rule:** ALWAYS title-search (full name, then last name) before creating a person note — people are the most-duplicated entity type, and semantic search misses names.
|
|
66
|
+
- **Body guidance:** this is where prose shines — how they met, what they care about, gift ideas, kids' names. Observations for the queryable bits (`- [birthday] March 12`).
|
|
67
|
+
- **Expansions:** interaction log (`- [contact] July 12, 2026 — coffee, discussed job change` appended per touch); linking people to meetings, projects, gifts.
|
|
68
|
+
|
|
69
|
+
## Research
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
Research/
|
|
73
|
+
├── {Topic}/
|
|
74
|
+
│ ├── {Topic}.md ← topic hub: question, current understanding, links to sources
|
|
75
|
+
│ └── {source notes} ← one note per significant source
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
- **Topic schema:** `status(enum): [active, parked, concluded]` · `question` (what they're trying to answer)
|
|
79
|
+
- **Source schema:** `source_type(enum): [article, paper, video, book, conversation]` · `url?` · `date_consumed?`
|
|
80
|
+
- **Naming:** topic hubs by topic name; sources by title of the source.
|
|
81
|
+
- **Index:** the topic hub IS the index for its sources.
|
|
82
|
+
- **Key conventions:** every source note ends with `- part_of [[Topic]]`; the hub's "current understanding" section gets rewritten (with changelog row) as understanding evolves — the hub is a living synthesis, not an append-only log.
|
|
83
|
+
- **Expansions:** a reading queue; `[claim]`/`[evidence]`/`[contradiction]` observation categories for contested topics; concluded-topic summaries.
|
|
84
|
+
|
|
85
|
+
## Finances
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
Finances/
|
|
89
|
+
├── Subscriptions Index.md ← index: every recurring charge, cost, renewal date
|
|
90
|
+
├── Subscriptions/
|
|
91
|
+
│ └── {Service}.md ← one note per subscription/recurring bill
|
|
92
|
+
├── Accounts/
|
|
93
|
+
│ └── {Account}.md ← one note per account (no credentials — see below)
|
|
94
|
+
└── Records/
|
|
95
|
+
└── {YYYY-MM-DD} — {Description}.md ← one-off: big purchases, tax docs, claims
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
- **Subscription schema:** `cost` · `billing_cycle(enum): [monthly, annual, quarterly]` · `renewal_date` · `payment_method?` · `status(enum): [active, cancelled, trial]`
|
|
99
|
+
- **Naming:** records get `{YYYY-MM-DD} — {Description}`, dated by the document date, not the filing date.
|
|
100
|
+
- **Index:** yes — the Subscriptions Index (name, cost, cycle, renewal, method) is the payoff note: total monthly spend at a glance, upcoming renewals visible.
|
|
101
|
+
- **Two hard rules for the instruction note:** (1) **no secrets ever** — no account numbers beyond last-4, no passwords, no full card numbers; notes are plaintext markdown. (2) **amounts and payment methods come from the user or a document — never inferred or guessed.**
|
|
102
|
+
- **Expansions:** annual-cost review workflow; warranty/receipt records linking to Assets; a renewals-this-month recurring check.
|
|
103
|
+
|
|
104
|
+
## Procedures
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
Procedures/
|
|
108
|
+
├── {area}/ ← optional grouping: Home/, Tech/, Work/
|
|
109
|
+
└── {Procedure Name}.md ← one note per how-to
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
- **Schema:** `procedure_type(enum): [how-to, troubleshooting, checklist, reference]` · `applies_to?` · `last_verified?`
|
|
113
|
+
- **Naming:** descriptive, search-first titles — the phrase the user would actually say (`Reset the water softener`, not `Softener maintenance procedure v2`).
|
|
114
|
+
- **Index:** optional per area once a folder exceeds ~15 notes.
|
|
115
|
+
- **Key conventions:** numbered steps; prerequisites up front; `last_verified` observation updated whenever the procedure is confirmed still-correct — and when a procedure is used and found wrong, fixing the note is part of finishing the task.
|
|
116
|
+
- **Expansions:** seasonal checklists; linking procedures to the assets they service.
|
|
117
|
+
|
|
118
|
+
## Health & habits
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
Health/
|
|
122
|
+
├── Log/
|
|
123
|
+
│ └── {YYYY-MM-DD} — {type}.md ← workouts, symptoms, appointments
|
|
124
|
+
└── {condition/goal notes} ← ongoing threads: an injury, a training goal
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
- **Log schema:** `entry_type(enum): [workout, symptom, appointment, measurement]` · `date`
|
|
128
|
+
- **Index:** optional — a current-goals note works better than a full log index.
|
|
129
|
+
- **Key conventions:** log entries are quick and low-friction (two observations and a sentence beats an unfilled template); ongoing threads link their log entries so `build_context` on "left knee" pulls the full history.
|
|
130
|
+
- **Sensitivity note for the instruction file:** health data in plaintext markdown — the user should decide consciously what level of detail they're comfortable storing, and where the project syncs to.
|
|
131
|
+
- **Expansions:** habit streaks; pre-appointment summaries generated from the log.
|
|
132
|
+
|
|
133
|
+
## Assets
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
Assets/
|
|
137
|
+
├── Asset Index.md ← index: every asset, location, value band
|
|
138
|
+
└── {Asset Name}.md ← one note per significant item
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
- **Schema:** `asset_type(enum): [electronics, appliance, vehicle, furniture, tool, other]` · `serial_number?` · `purchase_date?` · `purchase_price?` · `warranty_until?` · `location?` · `status(enum): [in-use, stored, loaned, sold, disposed]`
|
|
142
|
+
- **Naming:** `{Make} {Model}` or a distinguishing name (`Garage Freezer`); serial in observations, not the title.
|
|
143
|
+
- **Index:** yes — this is the insurance-claim / "where is the..." note.
|
|
144
|
+
- **Bidirectional rule:** if assets are assigned to people (family laptops), asset ↔ person, both directions.
|
|
145
|
+
- **Expansions:** warranty-expiry checks; maintenance logs linking to Procedures; purchase records linking to Finances.
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Combining domains
|
|
150
|
+
|
|
151
|
+
Domains compound through relations — mention these links when the user picks the pairs:
|
|
152
|
+
|
|
153
|
+
- Tasks ↔ People (`waiting_on [[Dana Reyes]]`) · Tasks ↔ Projects
|
|
154
|
+
- Finances ↔ Assets (purchase records ↔ asset notes)
|
|
155
|
+
- Procedures ↔ Assets (`services [[Garage Freezer]]`)
|
|
156
|
+
- Journal → everything (daily entries wiki-link whatever they mention)
|
|
157
|
+
- Research ↔ Notes (ideas graduate into research topics)
|
|
158
|
+
|
|
159
|
+
The cross-domain links are where a knowledge *graph* starts beating a folder of documents — surface one concrete example using the user's own notes during Phase 5.
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Schemas, Observations & Relations
|
|
2
|
+
|
|
3
|
+
How to make notes structured enough to query and consistent enough to trust. This is the mechanical core of the system the onboarding builds.
|
|
4
|
+
|
|
5
|
+
## Contents
|
|
6
|
+
|
|
7
|
+
1. [Note anatomy](#note-anatomy)
|
|
8
|
+
2. [Observations](#observations)
|
|
9
|
+
3. [Relations](#relations)
|
|
10
|
+
4. [Picoschema syntax](#picoschema-syntax)
|
|
11
|
+
5. [Creating a schema](#creating-a-schema)
|
|
12
|
+
6. [Validation & drift](#validation--drift)
|
|
13
|
+
7. [Design guidelines](#design-guidelines)
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Note anatomy
|
|
18
|
+
|
|
19
|
+
Every well-formed note has: frontmatter (title, type, tags — generated by `write_note` from its parameters), a **body** of substantive prose, an **Observations** section, and a **Relations** section.
|
|
20
|
+
|
|
21
|
+
```markdown
|
|
22
|
+
# Fix Gutter Downspout
|
|
23
|
+
|
|
24
|
+
| Notes | Modification Date | Approved By |
|
|
25
|
+
|:--|:--|:--|
|
|
26
|
+
| Initial Document | July 12, 2026 | Dana Reyes |
|
|
27
|
+
|
|
28
|
+
Back downspout detached during the June storm; water is pooling against the
|
|
29
|
+
foundation on the north side. Needs a bracket and probably a new elbow section.
|
|
30
|
+
Hardware store trip required — measure the downspout diameter first (looks like 3x4).
|
|
31
|
+
|
|
32
|
+
## Observations
|
|
33
|
+
- [status] open
|
|
34
|
+
- [priority] high
|
|
35
|
+
- [due] July 20, 2026
|
|
36
|
+
- [context] home-maintenance
|
|
37
|
+
|
|
38
|
+
## Relations
|
|
39
|
+
- part_of [[House Projects]]
|
|
40
|
+
- relates_to [[June Storm Damage]]
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Write the body generously.** Search retrieves chunks from note bodies, so richer prose makes notes *more* discoverable, not less. Prose carries meaning; observations carry precision. A note that is only bullet-point observations reads like a database row and loses the context that makes it useful a year later.
|
|
44
|
+
|
|
45
|
+
## Observations
|
|
46
|
+
|
|
47
|
+
Categorized, atomic facts: `- [category] content #optional-tag`
|
|
48
|
+
|
|
49
|
+
- Categories are **free-form** — `[decision]`, `[status]`, `[symptom]`, `[cost]`, `[lesson]` — whatever fits. Consistency within a note type is what matters (and is what schemas enforce).
|
|
50
|
+
- **One fact per line.** `- [decision] Use JWT with 15-minute expiry` not a paragraph of three decisions.
|
|
51
|
+
- **Categories are queryable** — searching by category finds every note carrying that kind of fact.
|
|
52
|
+
- Tags add cross-cutting findability: `- [risk] no SLA on the API #vendor #reliability`.
|
|
53
|
+
|
|
54
|
+
In this system, observations do double duty: they're facts, and they're the **fields** schemas validate — for scalar, enum, and array fields. A Task schema requiring `status` means every task note carries `- [status] ...` as an observation. (Entity-reference fields validate against relations instead — see below.)
|
|
55
|
+
|
|
56
|
+
## Relations
|
|
57
|
+
|
|
58
|
+
Typed, directional links: `- relation_type [[Target Note Title]]`
|
|
59
|
+
|
|
60
|
+
- relation_type is a descriptive snake_case verb: `part_of`, `depends_on`, `assigned_to`, `replaces`, `paid_by`. Invent freely.
|
|
61
|
+
- Wiki-links in body prose also create edges (untyped `references`). Use the Relations section for the structural links, prose links for narrative mentions.
|
|
62
|
+
- Targets may not exist yet — forward links resolve on creation.
|
|
63
|
+
- `build_context` on a `memory://` URL walks these edges to assemble connected context — this is what makes the graph worth building.
|
|
64
|
+
|
|
65
|
+
## Picoschema syntax
|
|
66
|
+
|
|
67
|
+
Schemas are notes (conventionally in a `Schemas/` folder) whose frontmatter metadata defines fields:
|
|
68
|
+
|
|
69
|
+
```yaml
|
|
70
|
+
schema:
|
|
71
|
+
status(enum): "[open, in-progress, blocked, done], current state"
|
|
72
|
+
due?: string, due date if any (Month D, YYYY)
|
|
73
|
+
priority?(enum): "[low, medium, high], urgency"
|
|
74
|
+
project?: Project, parent project note
|
|
75
|
+
steps?(array): string, ordered sub-steps
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
- Types: `string`, `integer`, `number`, `boolean`
|
|
79
|
+
- `?` suffix = optional field
|
|
80
|
+
- `(enum)` = fixed value list — use for anything status-like
|
|
81
|
+
- `(array)` = list field
|
|
82
|
+
- A capitalized type name (`Person`, `Project`) makes it a **relation field**, validated against the note's Relations section (the relation type must equal the field name: `- project [[Target Note]]`) — it creates a graph edge, not an observation
|
|
83
|
+
- Every field gets a comma-then-description: it's documentation the next assistant reads
|
|
84
|
+
|
|
85
|
+
## Creating a schema
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
write_note(
|
|
89
|
+
title="Task",
|
|
90
|
+
directory="Schemas",
|
|
91
|
+
note_type="schema",
|
|
92
|
+
metadata={
|
|
93
|
+
"entity": "Task",
|
|
94
|
+
"version": 1,
|
|
95
|
+
"schema": {
|
|
96
|
+
"status(enum)": "[open, in-progress, blocked, done], current state",
|
|
97
|
+
"due?": "string, due date (Month D, YYYY)",
|
|
98
|
+
"priority?(enum)": "[low, medium, high], urgency",
|
|
99
|
+
"project?": "Project, parent project"
|
|
100
|
+
},
|
|
101
|
+
"settings": {"validation": "warn"}
|
|
102
|
+
},
|
|
103
|
+
content="""# Task
|
|
104
|
+
|
|
105
|
+
Schema for task notes. Tasks live in Tasks/, titled with a short imperative
|
|
106
|
+
description. Status moves open → in-progress → done; blocked is a holding state.
|
|
107
|
+
|
|
108
|
+
## Observations
|
|
109
|
+
- [convention] Every task carries status as an observation
|
|
110
|
+
- [convention] Done tasks get a close-date appended before archiving"""
|
|
111
|
+
)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Key points:
|
|
115
|
+
|
|
116
|
+
- **`validation: warn`, not `strict`** — the modes are `warn` and `strict`. Warn logs findings without blocking anything; `strict` escalates the same findings to errors. Start every schema on warn — a new user fighting rejected writes will abandon the system. Move a schema to `strict` only if something automated consumes the notes and malformed ones break it.
|
|
117
|
+
- **Content notes declare their type** via `note_type` matching the schema's entity (e.g. `note_type="task"`).
|
|
118
|
+
- **Scalar, enum, and array fields must appear as observations** in the note body (`- [status] open`) to satisfy validation — frontmatter-only values don't count. Putting them in both places gives you metadata search *and* schema validation. **Entity-reference fields are different**: they're satisfied by a relation, not an observation — a line in `## Relations` whose type matches the field name (`project?: Project` is satisfied by `- project [[Kitchen Renovation]]`).
|
|
119
|
+
- **Version in metadata** — bump on breaking changes (new required field, removed field, type change). Additive optional fields don't need a bump.
|
|
120
|
+
|
|
121
|
+
## Validation & drift
|
|
122
|
+
|
|
123
|
+
- `schema_validate(note_type="task")` — check all notes of a type, or a single note via `identifier=`. It reports **missing required fields** and **invalid enum values** — that's the whole contract. Fields the schema doesn't declare are only counted as informational "unmatched" metadata (schemas are a subset, not a straitjacket), and scalar values are not type-checked, so a clean validate doesn't guarantee more than required-field presence and enum conformance. Use `schema_diff` to surface undeclared fields.
|
|
124
|
+
- `schema_diff(note_type="task")` — finds drift: fields notes use that the schema doesn't define (candidates to add as optional), and schema fields nobody uses (candidates to drop).
|
|
125
|
+
- `schema_infer(note_type="task")` — when notes of a type already exist without a schema, infer one from their actual structure instead of designing from scratch.
|
|
126
|
+
|
|
127
|
+
Evolution loop: `schema_diff` → edit the schema note (append changelog row, bump version if breaking) → `schema_validate` → fix outliers. Suggest the user run this monthly-ish once the system has real volume.
|
|
128
|
+
|
|
129
|
+
## Design guidelines
|
|
130
|
+
|
|
131
|
+
- **Every note type gets a schema; every note gets observations.** These two are not scale-dependent. A domain the user wants "loose" gets a one-field schema and a one-observation minimum, not an exemption — unstructured notes can't be queried, validated, or trusted by a future assistant, and retrofitting structure later is far more expensive than carrying one line now.
|
|
132
|
+
- **2–4 required fields, everything else optional.** Required fields are a tax on every note; each one must earn its place. Status almost always earns it. `notes_from_that_one_meeting` does not.
|
|
133
|
+
- **Enums for anything status-like.** Free-text statuses drift (`done`, `Done`, `finished`, `complete ✓`) and become unqueryable. Define the vocabulary in the schema and list it in the domain instruction note.
|
|
134
|
+
- **Schema per entity type, not per folder.** A `Person` schema serves contacts, family, and coworkers even if they live in different folders.
|
|
135
|
+
- **Don't over-constrain.** A schema describes common structure; it is not a straitjacket. If the user fights a field during the first week, make it optional or delete it.
|
|
136
|
+
- **Schemas are documentation.** Even at `warn` with validation never run, the schema note tells the next assistant exactly what a conforming note looks like. Write field descriptions accordingly.
|
|
@@ -47,14 +47,14 @@ Use `(enum)` with a list of allowed values:
|
|
|
47
47
|
|
|
48
48
|
```yaml
|
|
49
49
|
schema:
|
|
50
|
-
status(enum): [active, blocked, done, abandoned]
|
|
50
|
+
status(enum, current state): [active, blocked, done, abandoned]
|
|
51
51
|
```
|
|
52
52
|
|
|
53
53
|
Optional enum:
|
|
54
54
|
|
|
55
55
|
```yaml
|
|
56
56
|
schema:
|
|
57
|
-
priority?(enum): [low, medium, high, critical]
|
|
57
|
+
priority?(enum, task priority): [low, medium, high, critical]
|
|
58
58
|
```
|
|
59
59
|
|
|
60
60
|
### Arrays
|
|
@@ -83,9 +83,11 @@ Relations create edges in the knowledge graph, linking notes together.
|
|
|
83
83
|
|
|
84
84
|
```yaml
|
|
85
85
|
settings:
|
|
86
|
-
validation: warn # warn (log issues) or
|
|
86
|
+
validation: warn # warn (log issues) or strict (errors)
|
|
87
87
|
```
|
|
88
88
|
|
|
89
|
+
Use `strict` as the canonical enforcing mode. `error` is accepted only as a compatibility alias.
|
|
90
|
+
|
|
89
91
|
### Complete Example
|
|
90
92
|
|
|
91
93
|
```yaml
|
|
@@ -100,7 +102,7 @@ schema:
|
|
|
100
102
|
attendees?(array): Person, who attended
|
|
101
103
|
decisions?(array): string, decisions made
|
|
102
104
|
action_items?(array): string, follow-up tasks
|
|
103
|
-
status?(enum): [scheduled, completed, cancelled]
|
|
105
|
+
status?(enum, meeting state): [scheduled, completed, cancelled]
|
|
104
106
|
settings:
|
|
105
107
|
validation: warn
|
|
106
108
|
---
|
|
@@ -183,7 +185,7 @@ Validation reports:
|
|
|
183
185
|
### Handling Validation Results
|
|
184
186
|
|
|
185
187
|
- **`warn` mode**: Review warnings periodically. Fix notes that are clearly wrong; add optional fields to the schema for legitimate new patterns.
|
|
186
|
-
- **`
|
|
188
|
+
- **`strict` mode**: Use where conformance matters (e.g., automated pipelines consuming notes).
|
|
187
189
|
|
|
188
190
|
## Detecting Drift
|
|
189
191
|
|
|
@@ -26,7 +26,7 @@ entity: Task
|
|
|
26
26
|
version: 1
|
|
27
27
|
schema:
|
|
28
28
|
description: string, what needs to be done
|
|
29
|
-
status?(enum): [active, blocked, done, abandoned]
|
|
29
|
+
status?(enum, current state): [active, blocked, done, abandoned]
|
|
30
30
|
assigned_to?: string, who is working on this
|
|
31
31
|
steps?(array): string, ordered steps to complete
|
|
32
32
|
current_step?: integer, which step number we're on (1-indexed)
|
|
@@ -11,6 +11,7 @@ function normalizeProject(project: ProjectListResult) {
|
|
|
11
11
|
is_private: project.is_private === true,
|
|
12
12
|
is_default: project.is_default === true || project.isDefault === true,
|
|
13
13
|
workspace_name: project.workspace_name ?? null,
|
|
14
|
+
workspace_slug: project.workspace_slug ?? null,
|
|
14
15
|
workspace_type: project.workspace_type ?? null,
|
|
15
16
|
workspace_tenant_id: project.workspace_tenant_id ?? null,
|
|
16
17
|
}
|
|
@@ -28,7 +29,7 @@ export function registerProjectListTool(
|
|
|
28
29
|
parameters: Type.Object({
|
|
29
30
|
workspace: Type.Optional(
|
|
30
31
|
Type.String({
|
|
31
|
-
description: "Filter by workspace name or tenant_id",
|
|
32
|
+
description: "Filter by workspace name, slug, or tenant_id",
|
|
32
33
|
}),
|
|
33
34
|
),
|
|
34
35
|
}),
|