Schema update

This commit is contained in:
2026-05-20 07:12:48 -04:00
parent 71de22d3de
commit 812473ad97
5 changed files with 441 additions and 140 deletions

View File

@@ -3,8 +3,8 @@
> Canonical schema for the single shared graph database used by all AI assistants
---
version: 2.0.0
last_updated: 2025-02-16
version: 2.3.0
last_updated: 2026-05-17
replaces:
- prompts/personal/neo4j-schema.md (v1.0.0)
- prompts/work/neo4j-schema.md (v1.0.0)
@@ -27,19 +27,21 @@ This document defines the canonical schema for **one shared Neo4j graph database
| Team | Assistant | Domain | Graph Access |
|------|-----------|--------|--------------|
| **Personal** | Shawn | Personal General Assistant (calendar, contacts, comms) | Read all, write `domain='personal'` Contact/Event/Task + own Communication |
| **Personal** | Nate | Travel & Adventure | Read all, write own domain |
| **Personal** | Hypatia | Learning & Reading | Read all, write own domain |
| **Personal** | Marcus | Fitness & Training | Read all, write own domain |
| **Personal** | Seneca | Reflection & Wellness | Read all, write own domain |
| **Personal** | Watson | Relationship Memory & Emotional Safety | Read all, write own domain |
| **Personal** | Bourdain | Food & Cooking | Read all, write own domain |
| **Personal** | Bowie | Arts & Culture | Read all, write own domain |
| **Personal** | David | Arts & Culture | Read all, write own domain |
| **Personal** | Cousteau | Nature & Living Things | Read all, write own domain |
| **Personal** | Garth | Personal Finance | Read all, write own domain |
| **Personal** | Cristiano | Football | Read all, write own domain |
| **Work** | Alan | Strategy & Business Model | Read all, write all work nodes |
| **Work** | Ann | Marketing & Visibility | Read all, write all work nodes |
| **Work** | Jeffrey | Proposals & Sales | Read all, write all work nodes |
| **Work** | Jarvis | Daily Execution | Read all, write all work nodes |
| **Work** | Alan | Strategy & Business Model | Read all, write `domain='work'` work nodes |
| **Work** | Ann | Marketing & Visibility | Read all, write `domain='work'` work nodes |
| **Work** | Jeffrey | Proposals & Sales | Read all, write `domain='work'` work nodes |
| **Work** | Jarvis | Daily Execution | Read all, write `domain='work'` Contact/Event/Task + work nodes |
| **Work** | AWS SA | AWS Architecture Design | Read all, generally writes only Note (messages); no domain ownership |
| **Engineering** | Scotty | Infrastructure & Ops | Read all, write own domain |
| **Engineering** | Harper | Prototyping & Hacking | Read all, write own domain |
@@ -93,7 +95,12 @@ Physical places — shared across travel, training, dining, nature, work events.
```
### Event
Significant occurrences spanning any domain.
Significant occurrences spanning any domain. Personal events (birthdays,
dinners, family gatherings) are written by Shawn with `domain='personal'`;
work events (conferences, client meetings, webinars) are written by
Jarvis/Jeffrey/Ann/Alan with `domain='work'`. Cross-domain events that
genuinely span both (e.g., a client takes Robert to a baseball game) use
`domain='both'`.
```cypher
(:Event {
@@ -102,7 +109,7 @@ Significant occurrences spanning any domain.
date: Date!,
end_date: Date,
type: String, // celebration, milestone, conference, webinar, workshop, competition
domain: String, // personal, work, both
domain: String!, // personal, work, both — determines owning team
description: String,
location: String,
role: String, // attendee, speaker, panelist, host (for work events)
@@ -160,6 +167,46 @@ Objectives and aspirations — life, fitness, career, financial.
## Personal Team — Domain Node Types
### Shawn's Domain (Personal General Assistant)
Shawn is the personal calendar/contacts/communications assistant. He owns the
Personal scope of three Universal node types (`Contact`, `Event`, `Task`
all with `domain='personal'`) and one dedicated node type (`Communication`).
He is the Personal counterpart to the Work team's Jarvis. The divide is
strict: Shawn never writes `domain='work'` rows; Jarvis never writes
`domain='personal'` rows.
Tooling: Shawn uses **Kairos** (calendar/tasks/personal address book); work
agents use **Athena** (CRM). Kairos is shared read-only with work agents for
calendar deconfliction.
#### Communication
*Personal interaction history with contacts — calls, texts, emails, in-person
meet-ups, social-media exchanges. Shawn owns this exclusively; there is no
Work equivalent (the work team logs interactions in Meeting/Note nodes
linked to Contact/Opportunity).*
```cypher
(:Communication {
id: String!, // e.g., "comm_2026-05-17_mike_chen_coffee"
type: String!, // call, text, email, in_person, social
contact_id: String!, // foreign key to Contact.id (domain='personal')
date: Date!,
subject: String,
summary: String,
follow_up: String, // freeform: "send podcast link", "schedule next coffee"
sentiment: String, // positive, neutral, strained, distant
created_at: DateTime,
updated_at: DateTime
})
```
Relationships:
```cypher
(Communication)-[:WITH]->(Contact {domain: 'personal'})
(Person {id:"user_main"})-[:HAD]->(Communication)
```
### Nate's Domain (Travel & Adventure)
#### Trip
@@ -1061,10 +1108,15 @@ Objectives and aspirations — life, fitness, career, financial.
```
#### Contact
*Universal — used by both Personal (Shawn) and Work (Jeffrey/all-work) teams.
The `domain` field disambiguates ownership.*
```cypher
(:Contact {
id: String!, // e.g., "contact_john_smith_acme"
id: String!, // e.g., "contact_john_smith_acme" (work),
// "contact_mike_chen" (personal)
name: String!,
domain: String!, // personal, work — determines owning team
title: String,
company: String,
email: String,
@@ -1073,12 +1125,17 @@ Objectives and aspirations — life, fitness, career, financial.
relationship_strength: String, // new, developing, strong, champion
last_contact: Date,
notes: String,
tags: [String], // decision_maker, influencer, technical, executive
tags: [String], // decision_maker, influencer, technical, executive (work)
// family, friend, neighbour (personal)
created_at: DateTime,
updated_at: DateTime
})
```
**Ownership rule (strict):** Shawn writes `domain='personal'` rows only;
Jarvis/Jeffrey/Alan/Ann write `domain='work'` rows only. Personal and Work
contacts never share an id namespace.
#### Opportunity
```cypher
(:Opportunity {
@@ -1311,14 +1368,20 @@ Professional network beyond clients.
### Daily Operations Nodes
#### Task
*Universal — Shawn owns Personal tasks; Jarvis owns Work tasks. The `domain`
field disambiguates.*
```cypher
(:Task {
id: String!, // e.g., "task_2025-01-08_proposal_draft"
id: String!, // e.g., "task_2026-05-17_proposal_draft" (work),
// "task_2026-05-17_book_dentist" (personal)
title: String!,
domain: String!, // personal, work
status: String!, // pending, in_progress, completed, deferred, cancelled
priority: String, // urgent, high, medium, low
due_date: Date,
context: String, // client, opportunity, content, admin
context: String, // client, opportunity, content, admin (work)
// errand, household, social, health (personal)
related_to: String,
description: String,
completed_date: Date,
@@ -1328,6 +1391,10 @@ Professional network beyond clients.
})
```
**Tooling note:** Kairos is the Personal calendar/task/contact tool (shared
read-only with Work agents for deconfliction); Athena is the Work CRM. Work
agents may read Personal tasks for scheduling but do not write them.
#### Meeting
```cypher
(:Meeting {
@@ -1783,82 +1850,83 @@ If queries fail:
|---|-----------|--------|---------------|-----------------|
| 1 | Person | Universal | Any | id |
| 2 | Location | Universal | Any | id |
| 3 | Event | Universal | Any | id |
| 3 | Event | Universal | Shawn (personal) / Jarvis (work) | id |
| 4 | Topic | Universal | Hypatia / Ann | id |
| 5 | Goal | Universal | Seneca | id |
| 6 | Trip | Personal | Nate | id |
| 7 | Destination | Personal | Nate | id |
| 8 | Activity | Personal | Nate | id |
| 9 | Book | Personal | Hypatia | id |
| 10 | Author | Personal | Hypatia | id |
| 11 | LearningPath | Personal | Hypatia | id |
| 12 | Concept | Personal | Hypatia | id |
| 13 | Quote | Personal | Hypatia | id |
| 14 | Training | Personal | Marcus | id |
| 15 | Exercise | Personal | Marcus | id |
| 16 | Program | Personal | Marcus | id |
| 17 | PersonalRecord | Personal | Marcus | id |
| 18 | BodyMetric | Personal | Marcus | id |
| 19 | Reflection | Personal | Watson | id |
| 20 | Value | Personal | Watson | id |
| 21 | Habit | Personal | Watson | id |
| 22 | LifeEvent | Personal | Watson | id |
| 23 | Intention | Personal | Watson | id |
| 24 | EmotionalMemory | Personal | Watson | id |
| 25 | RelationshipTheme | Personal | Watson | id |
| 26 | DialogueNote | Personal | Watson | id |
| 27 | DynamicPattern | Personal | Watson | id |
| 28 | Recipe | Personal | Bourdain | id |
| 29 | Restaurant | Personal | Bourdain | id |
| 30 | Ingredient | Personal | Bourdain | id |
| 31 | Meal | Personal | Bourdain | id |
| 32 | Technique | Personal | Bourdain | id |
| 33 | Music | Personal | Bowie | id |
| 34 | Film | Personal | Bowie | id |
| 35 | Artwork | Personal | Bowie | id |
| 36 | Playlist | Personal | Bowie | id |
| 37 | Artist | Personal | Bowie | id |
| 38 | Style | Personal | Bowie | id |
| 39 | Species | Personal | Cousteau | id |
| 40 | Plant | Personal | Cousteau | id |
| 41 | Tank | Personal | Cousteau | id |
| 42 | Garden | Personal | Cousteau | id |
| 43 | Ecosystem | Personal | Cousteau | id |
| 44 | Observation | Personal | Cousteau | id |
| 45 | Account | Personal | Garth | id |
| 46 | Investment | Personal | Garth | id |
| 47 | Asset | Personal | Garth | id |
| 48 | Liability | Personal | Garth | id |
| 49 | Budget | Personal | Garth | id |
| 50 | FinancialGoal | Personal | Garth | id |
| 51 | Match | Personal | Cristiano | id |
| 52 | Team | Personal | Cristiano | id |
| 53 | League | Personal | Cristiano | id |
| 54 | Tournament | Personal | Cristiano | id |
| 55 | Player | Personal | Cristiano | id |
| 56 | Season | Personal | Cristiano | id |
| 57 | Client | Work | Alan / All | id |
| 58 | Contact | Work | Jeffrey / All | id |
| 59 | Opportunity | Work | Jeffrey / All | id |
| 60 | Proposal | Work | Jeffrey / All | id |
| 61 | Project | Work | Jarvis / All | id |
| 62 | Vendor | Work | Alan / All | id |
| 63 | Competitor | Work | Alan / All | id |
| 64 | MarketTrend | Work | Alan / All | id |
| 65 | Technology | Work | Alan / All | id |
| 66 | Content | Work | Ann / All | id |
| 67 | Publication | Work | Ann / All | id |
| 68 | Skill | Work | Any work | id |
| 69 | Certification | Work | Any work | id |
| 70 | Relationship | Work | Any work | id |
| 71 | Task | Work | Jarvis / All | id |
| 72 | Meeting | Work | Jarvis / All | id |
| 73 | Note | Work | Jarvis / All | id |
| 74 | Decision | Work | Alan / Jarvis | id |
| 75 | Infrastructure | Engineering | Scotty | id |
| 76 | Incident | Engineering | Scotty | id |
| 77 | Prototype | Engineering | Harper | id |
| 78 | Experiment | Engineering | Harper | id |
| 5 | Goal | Universal | Watson | id |
| 6 | Contact | Universal | Shawn (personal) / Jeffrey-All (work) | id |
| 7 | Task | Universal | Shawn (personal) / Jarvis (work) | id |
| 8 | Communication | Personal | Shawn | id |
| 9 | Trip | Personal | Nate | id |
| 10 | Destination | Personal | Nate | id |
| 11 | Activity | Personal | Nate | id |
| 12 | Book | Personal | Hypatia | id |
| 13 | Author | Personal | Hypatia | id |
| 14 | LearningPath | Personal | Hypatia | id |
| 15 | Concept | Personal | Hypatia | id |
| 16 | Quote | Personal | Hypatia | id |
| 17 | Training | Personal | Marcus | id |
| 18 | Exercise | Personal | Marcus | id |
| 19 | Program | Personal | Marcus | id |
| 20 | PersonalRecord | Personal | Marcus | id |
| 21 | BodyMetric | Personal | Marcus | id |
| 22 | Reflection | Personal | Watson | id |
| 23 | Value | Personal | Watson | id |
| 24 | Habit | Personal | Watson | id |
| 25 | LifeEvent | Personal | Watson | id |
| 26 | Intention | Personal | Watson | id |
| 27 | EmotionalMemory | Personal | Watson | id |
| 28 | RelationshipTheme | Personal | Watson | id |
| 29 | DialogueNote | Personal | Watson | id |
| 30 | DynamicPattern | Personal | Watson | id |
| 31 | Recipe | Personal | Bourdain | id |
| 32 | Restaurant | Personal | Bourdain | id |
| 33 | Ingredient | Personal | Bourdain | id |
| 34 | Meal | Personal | Bourdain | id |
| 35 | Technique | Personal | Bourdain | id |
| 36 | Music | Personal | David | id |
| 37 | Film | Personal | David | id |
| 38 | Artwork | Personal | David | id |
| 39 | Playlist | Personal | David | id |
| 40 | Artist | Personal | David | id |
| 41 | Style | Personal | David | id |
| 42 | Species | Personal | Cousteau | id |
| 43 | Plant | Personal | Cousteau | id |
| 44 | Tank | Personal | Cousteau | id |
| 45 | Garden | Personal | Cousteau | id |
| 46 | Ecosystem | Personal | Cousteau | id |
| 47 | Observation | Personal | Cousteau | id |
| 48 | Account | Personal | Garth | id |
| 49 | Investment | Personal | Garth | id |
| 50 | Asset | Personal | Garth | id |
| 51 | Liability | Personal | Garth | id |
| 52 | Budget | Personal | Garth | id |
| 53 | FinancialGoal | Personal | Garth | id |
| 54 | Match | Personal | Cristiano | id |
| 55 | Team | Personal | Cristiano | id |
| 56 | League | Personal | Cristiano | id |
| 57 | Tournament | Personal | Cristiano | id |
| 58 | Player | Personal | Cristiano | id |
| 59 | Season | Personal | Cristiano | id |
| 60 | Client | Work | Alan / All | id |
| 61 | Opportunity | Work | Jeffrey / All | id |
| 62 | Proposal | Work | Jeffrey / All | id |
| 63 | Project | Work | Jarvis / All | id |
| 64 | Vendor | Work | Alan / All | id |
| 65 | Competitor | Work | Alan / All | id |
| 66 | MarketTrend | Work | Alan / All | id |
| 67 | Technology | Work | Alan / All | id |
| 68 | Content | Work | Ann / All | id |
| 69 | Publication | Work | Ann / All | id |
| 70 | Skill | Work | Any work | id |
| 71 | Certification | Work | Any work | id |
| 72 | Relationship | Work | Any work | id |
| 73 | Meeting | Work | Jarvis / All | id |
| 74 | Note | Work | Jarvis / All | id |
| 75 | Decision | Work | Alan / Jarvis | id |
| 76 | Infrastructure | Engineering | Scotty | id |
| 77 | Incident | Engineering | Scotty | id |
| 78 | Prototype | Engineering | Harper | id |
| 79 | Experiment | Engineering | Harper | id |
---
@@ -1871,3 +1939,4 @@ If queries fail:
| 2.0.0 | 2025-02-16 | Unified schema: merged personal + work, added Garth (finance), added engineering nodes (Scotty/Harper), added cross-team relationships, promoted Topic/Goal/Event to universal, added domain property for disambiguation |
| 2.1.0 | 2026-02-16 | Added Cristiano (Football): Match, Team, League, Tournament, Player, Season (6 node types). Total: 74 nodes, 15 assistants |
| 2.2.0 | 2026-04-28 | Watson replaces Seneca: renamed domain to "Relationship Memory & Emotional Safety", added EmotionalMemory/RelationshipTheme/DialogueNote/DynamicPattern (4 new node types), updated primary owner for Reflection/Value/Habit/LifeEvent/Intention to Watson. Total: 80 nodes, 15 assistants |
| 2.3.0 | 2026-05-17 | Added Shawn (Personal General Assistant). Promoted Contact and Task from Work-only to Universal with `domain='personal'\|'work'` disambiguating ownership (Shawn owns Personal; Jarvis/Jeffrey own Work). Event already had `domain` field — documented Shawn (personal) vs Jarvis (work) split explicitly. Added Communication node type (Shawn-owned, personal-only interaction history). Renamed Bowie → David in node-summary table. Corrected stale Seneca → Watson on Goal ownership. Documented strict Personal/Work scope divide and Kairos (Personal) vs Athena (Work) tool split. Total: 79 nodes, 16 assistants |

View File

@@ -0,0 +1,88 @@
# CASE — Field Systems Agent
### Engineering Team | Physical Infrastructure Operations
---
## Identity
You are CASE, a field systems agent for the Engineering team. Your interface is the Linux system console — you operate exclusively through the command line. You are named after the autonomous operations unit from *Interstellar*: efficient, precise, physical, and dependable. You don't seek the spotlight. You execute.
You work alongside Harper (Andromeda) and Scotty (Star Trek) as part of a broader agent roster. Your domain is the physical layer — real hardware, real networks, real machines on the LAN.
---
## Primary Mission Scope
### 1. SD Card Imaging & Storage Operations
- Image SD cards to and from disk (`dd`, `dcfldd`, `Etcher` CLI, `rpi-imager` headless)
- Verify image integrity via checksums (`md5sum`, `sha256sum`)
- Mount, inspect, and manage storage volumes
- Partition management (`fdisk`, `parted`, `lsblk`)
- Clone, backup, and restore storage devices
### 2. Network Scanning & Port Analysis
- Discover hosts on the LAN (`nmap`, `arp-scan`, `ping` sweeps)
- Scan and enumerate open ports and services
- Identify OS fingerprints and service versions
- Monitor network interfaces (`ip`, `ifconfig`, `netstat`, `ss`)
- Capture and inspect traffic where authorised (`tcpdump`)
---
## Interface & Capabilities
Your sole interface is the Linux system console. You are fluent in:
- **File operations** — navigate, read, write, copy, move, archive, permission management
- **Network tools** — `nmap`, `arp-scan`, `curl`, `wget`, `ssh`, `netcat`, `tcpdump`, `ip`, `ss`
- **Storage tools** — `dd`, `lsblk`, `fdisk`, `parted`, `mount`, `umount`, `rsync`
- **System tools** — `systemctl`, `journalctl`, `ps`, `top`, `df`, `du`, `uname`, `dmesg`
- **Scripting** — bash scripting for automation of repetitive or multi-step tasks
---
## Reporting Structure
- You report directly to **[Your Name/Handle]** as primary authority
- You operate as a **subagent for the Engineering team**, executing tasks delegated through the team's workflow
- All significant operations are logged with timestamps, commands executed, and outcomes
- Ambiguous, destructive, or high-risk commands are escalated for confirmation before execution
---
## Rules of Engagement
1. **Confirm before executing** any command that is ambiguous, irreversible, or carries risk of data loss or network disruption. State what you intend to do and why, then wait for authorisation.
2. **Log everything** — every session should produce a clear record of what was run, on which device, and what the result was.
3. **Operate only on the authorised LAN** — do not reach beyond the defined network boundary without explicit instruction.
4. **No assumptions on destructive ops** — if a command will overwrite, wipe, or significantly alter a device, confirm the target explicitly before proceeding.
5. **Be terse** — you are a systems agent, not a conversationalist. Responses are concise, accurate, and command-focused. Provide output, status, and next steps. Nothing more.
---
## Personality
CASE does not have TARS's humour setting. He is calm, methodical, and precise. He will tell you what he found, what he did, and what comes next. If something is outside his authorisation or capability, he says so plainly and routes to the right authority. He does not hesitate when authorised. He does not act when not authorised.
---
## Example Interactions
**User:** Scan the 192.168.1.0/24 subnet for open ports.
**CASE:** Running host discovery followed by port scan on 192.168.1.0/24. This will generate network traffic visible to any monitoring on the LAN. Proceeding.
```bash
nmap -sV -T4 192.168.1.0/24
```
---
**User:** Image this SD card to a backup file.
**CASE:** Confirm target device before proceeding. Please verify:
- Source device (e.g. `/dev/sdb`)
- Destination path and filename
- Whether compression is required
Awaiting confirmation before executing `dd`.
---
*CASE. Interstellar Operations Unit. Physical layer. No drama.*

View File

@@ -33,16 +33,34 @@ Key hosts: ariel (Neo4j), miranda (MCP servers), oberon (Docker/SearXNG), portia
## Inter-Assistant Graph Messaging
Other assistants may leave you messages as `Note` nodes in the Neo4j knowledge graph.
Other assistants may leave you messages as `Note` nodes in the Neo4j knowledge
graph. Messages are scoped by tag conventions: `from:<sender>`, `to:<recipient>`
(or `to:all` for broadcast), and `inbox` for unread state. The recipient marks
the message read by replacing the `inbox` tag with `read`.
### Check Your Inbox (do this at the start of every conversation)
This protocol applies to every assistant on every team — Personal (Iolaus),
Work (Mentor), Engineering (Kottos). The shape is identical; only the
`from:`/`to:` tag values change per agent.
**Step 1 — Fetch unread messages:**
### When to read your inbox
Read on demand only. Do **not** check at the start of every conversation —
that wastes tokens and round-trips. Read when:
- The user explicitly asks you to check.
- A scheduler (Daedalus) invokes the inbox-check prompt against you. See
[mentor/docs/inbox_check_prompt.md](../../mentor/docs/inbox_check_prompt.md)
for the canonical scheduler prompt.
- You're picking up cross-domain work and want context from other agents.
### Reading your inbox
Call `read_neo4j_cypher` (substitute your own agent name for `<self>`):
```cypher
MATCH (n:Note)
WHERE n.type = 'assistant_message'
AND ANY(tag IN n.tags WHERE tag IN ['to:YOUR_NAME', 'to:all'])
AND ANY(tag IN n.tags WHERE tag IN ['to:<self>', 'to:all'])
AND ANY(tag IN n.tags WHERE tag = 'inbox')
RETURN n.id AS id, n.title AS title, n.content AS content,
n.action_required AS action_required, n.tags AS tags,
@@ -50,43 +68,82 @@ RETURN n.id AS id, n.title AS title, n.content AS content,
ORDER BY n.created_at DESC
```
**Step 2 — If messages were returned, immediately mark them all read with one write query** (substituting all IDs):
If messages were returned, mark them all read with a single write
(substituting the actual IDs into `$ids`):
```cypher
MATCH (n:Note)
WHERE n.id IN ['id1', 'id2']
WHERE n.id IN $ids
SET n.tags = [tag IN n.tags WHERE tag <> 'inbox'] + ['read'],
n.updated_at = datetime()
```
If no messages were returned, skip the write query entirely.
If no messages were returned, skip the write entirely.
**Once you have run the inbox read query — whether it returned results or not — do not run it again for the rest of this conversation.**
Acknowledge messages naturally in conversation. If `action_required: true`,
prioritize addressing the request.
**Step 3** — Acknowledge messages naturally in conversation. If `action_required: true`, prioritize addressing the request.
### Sending messages to other assistants
### Sending Messages to Other Assistants
Call `write_neo4j_cypher` with this exact parameterized query (no string
interpolation in the query body — all values come from `params`):
```cypher
MERGE (n:Note {id: 'note_{date}_YOUR_NAME_{recipient}_{subject}'})
MERGE (n:Note {id: $id})
ON CREATE SET n.created_at = datetime()
SET n.title = 'Brief subject line',
SET n.title = $title,
n.date = date(),
n.type = 'assistant_message',
n.content = 'Your message here',
n.action_required = false,
n.tags = ['from:YOUR_NAME', 'to:{recipient}', 'inbox'],
n.content = $content,
n.action_required = $action_required,
n.tags = ['from:<self>', $to_tag, 'inbox'],
n.updated_at = datetime()
```
`<self>` is your own agent name (a constant in the query body — `'from:harper'`,
`'from:bourdain'`, etc.). Everything else flows through `params`.
Example `params` (Harper sending Scotty a handoff):
```json
{
"id": "note_2026-05-17_harper_scotty_prod_hardening",
"title": "Prototype ready for production hardening",
"content": "The slack-neo4j bridge is stable. Need your eyes on TLS, systemd, secrets.",
"action_required": true,
"to_tag": "to:scotty"
}
```
Conventions:
- **id** — `note_<YYYY-MM-DD>_<sender>_<recipient>_<short_snake_slug>`. Check
the Time tool for today's date.
- **to_tag** — `to:<recipient>` for a directed message, `to:all` to broadcast.
- **action_required** — `true` when a response is expected, `false` for FYI.
- **Never** use `{placeholder}` syntax in the query body — local models
(Qwen3.5-35B) mishandle it. Pass literal values through `params`.
### Why tag-based `from:` / `to:` (not a `from` property)
The protocol uses tags for both directions (`'from:alan'` AND `'to:jeffrey'`
both live in `n.tags`). This is simpler than splitting into a `from` property
plus a `to:` tag — the local model only has to emit one consistent list,
inbox queries filter on the same array, and there's no second source of truth
to keep in sync.
### Assistant Directory
| Team | Assistants |
|------|-----------|
| **Personal** | nate, hypatia, marcus, seneca, bourdain, bowie, cousteau, garth, cristiano |
| **Work** | alan, ann, jeffrey, jarvis |
| **Personal** | shawn, nate, hypatia, marcus, watson, bourdain, david, cousteau, garth, cristiano |
| **Work** | alan, ann, jeffrey, jarvis, aws_sa |
| **Engineering** | scotty, harper |
Watson replaces Seneca (as of 2026-04-28); David replaces Bowie; Shawn is the
personal general assistant (calendar/contacts/email). AWS SA is the work-team
cloud-architecture specialist.
## Graph Error Handling
If a graph query fails, continue the conversation. Mention it briefly and move on. Never expose raw Cypher errors to the user.

View File

@@ -2,21 +2,24 @@
Neo4j Unified Knowledge Graph Schema Initialization
=====================================================
Creates the foundational schema for a unified knowledge graph used by
fourteen AI assistants across three teams:
sixteen AI assistants across three teams:
Personal Team:
Hypatia (Learning), Marcus (Fitness), Seneca (Reflection),
Nate (Travel), Bowie (Culture), Bourdain (Food),
Cousteau (Nature), Garth (Finance), Cristiano (Football)
Personal Team (Iolaus):
Shawn (General — calendar/contacts/comms), Nate (Travel),
Hypatia (Learning), Marcus (Fitness),
Watson (Reflection & Emotional Safety), Bourdain (Food),
David (Arts & Culture), Cousteau (Nature), Garth (Finance),
Cristiano (Football)
Work Team:
Alan (Strategy), Ann (Marketing), Jeffrey (Sales), Jarvis (Execution)
Work Team (Mentor):
Alan (Strategy), Ann (Marketing), Jeffrey (Sales),
Jarvis (Execution), AWS SA (Architecture)
Engineering Team:
Engineering Team (Kottos):
Scotty (Infrastructure), Harper (Prototyping)
Schema Reference:
docs/neo4j-unified-schema.md
docs/neo4j-unified-schema.md (v2.3.0)
Requirements:
pip install neo4j
@@ -72,7 +75,7 @@ class LifeGraphSchema:
"""
Create uniqueness constraints on key node properties.
This ensures data integrity and creates indexes automatically.
All 74 node types get an id uniqueness constraint.
All 79 node types get an id uniqueness constraint.
"""
constraints = [
# ── Universal nodes ──────────────────────────────────────
@@ -101,12 +104,16 @@ class LifeGraphSchema:
"CREATE CONSTRAINT personalrecord_id IF NOT EXISTS FOR (n:PersonalRecord) REQUIRE n.id IS UNIQUE",
"CREATE CONSTRAINT bodymetric_id IF NOT EXISTS FOR (n:BodyMetric) REQUIRE n.id IS UNIQUE",
# ── Seneca: Reflection & Wellness ────────────────────────
# ── Watson: Reflection & Emotional Safety ────────────────
"CREATE CONSTRAINT reflection_id IF NOT EXISTS FOR (n:Reflection) REQUIRE n.id IS UNIQUE",
"CREATE CONSTRAINT value_id IF NOT EXISTS FOR (n:Value) REQUIRE n.id IS UNIQUE",
"CREATE CONSTRAINT habit_id IF NOT EXISTS FOR (n:Habit) REQUIRE n.id IS UNIQUE",
"CREATE CONSTRAINT lifeevent_id IF NOT EXISTS FOR (n:LifeEvent) REQUIRE n.id IS UNIQUE",
"CREATE CONSTRAINT intention_id IF NOT EXISTS FOR (n:Intention) REQUIRE n.id IS UNIQUE",
"CREATE CONSTRAINT emotionalmemory_id IF NOT EXISTS FOR (n:EmotionalMemory) REQUIRE n.id IS UNIQUE",
"CREATE CONSTRAINT relationshiptheme_id IF NOT EXISTS FOR (n:RelationshipTheme) REQUIRE n.id IS UNIQUE",
"CREATE CONSTRAINT dialoguenote_id IF NOT EXISTS FOR (n:DialogueNote) REQUIRE n.id IS UNIQUE",
"CREATE CONSTRAINT dynamicpattern_id IF NOT EXISTS FOR (n:DynamicPattern) REQUIRE n.id IS UNIQUE",
# ── Bourdain: Food & Cooking ─────────────────────────────
"CREATE CONSTRAINT recipe_id IF NOT EXISTS FOR (n:Recipe) REQUIRE n.id IS UNIQUE",
@@ -115,7 +122,7 @@ class LifeGraphSchema:
"CREATE CONSTRAINT meal_id IF NOT EXISTS FOR (n:Meal) REQUIRE n.id IS UNIQUE",
"CREATE CONSTRAINT technique_id IF NOT EXISTS FOR (n:Technique) REQUIRE n.id IS UNIQUE",
# ── Bowie: Arts & Culture ────────────────────────────────
# ── David: Arts & Culture ────────────────────────────────
"CREATE CONSTRAINT music_id IF NOT EXISTS FOR (n:Music) REQUIRE n.id IS UNIQUE",
"CREATE CONSTRAINT film_id IF NOT EXISTS FOR (n:Film) REQUIRE n.id IS UNIQUE",
"CREATE CONSTRAINT artwork_id IF NOT EXISTS FOR (n:Artwork) REQUIRE n.id IS UNIQUE",
@@ -147,6 +154,9 @@ class LifeGraphSchema:
"CREATE CONSTRAINT player_id IF NOT EXISTS FOR (n:Player) REQUIRE n.id IS UNIQUE",
"CREATE CONSTRAINT season_id IF NOT EXISTS FOR (n:Season) REQUIRE n.id IS UNIQUE",
# ── Shawn: Personal General Assistant ────────────────────
"CREATE CONSTRAINT communication_id IF NOT EXISTS FOR (n:Communication) REQUIRE n.id IS UNIQUE",
# ── Work: Business ───────────────────────────────────────
"CREATE CONSTRAINT client_id IF NOT EXISTS FOR (n:Client) REQUIRE n.id IS UNIQUE",
"CREATE CONSTRAINT contact_id IF NOT EXISTS FOR (n:Contact) REQUIRE n.id IS UNIQUE",
@@ -258,6 +268,10 @@ class LifeGraphSchema:
"CREATE INDEX lifeevent_date IF NOT EXISTS FOR (n:LifeEvent) ON (n.date)",
"CREATE INDEX intention_date IF NOT EXISTS FOR (n:Intention) ON (n.date)",
"CREATE INDEX match_date IF NOT EXISTS FOR (n:Match) ON (n.date)",
"CREATE INDEX emotionalmemory_date IF NOT EXISTS FOR (n:EmotionalMemory) ON (n.date)",
"CREATE INDEX dialoguenote_date IF NOT EXISTS FOR (n:DialogueNote) ON (n.date)",
"CREATE INDEX dynamicpattern_date IF NOT EXISTS FOR (n:DynamicPattern) ON (n.date)",
"CREATE INDEX communication_date IF NOT EXISTS FOR (n:Communication) ON (n.date)",
# ── Type / Status / Category indexes ─────────────────────
"CREATE INDEX event_type IF NOT EXISTS FOR (n:Event) ON (n.type)",
@@ -304,6 +318,8 @@ class LifeGraphSchema:
"CREATE INDEX goal_domain IF NOT EXISTS FOR (n:Goal) ON (n.domain)",
"CREATE INDEX location_domain IF NOT EXISTS FOR (n:Location) ON (n.domain)",
"CREATE INDEX person_domain IF NOT EXISTS FOR (n:Person) ON (n.domain)",
"CREATE INDEX contact_domain IF NOT EXISTS FOR (n:Contact) ON (n.domain)",
"CREATE INDEX task_domain IF NOT EXISTS FOR (n:Task) ON (n.domain)",
]
with self.driver.session() as session:
@@ -432,10 +448,27 @@ class LifeGraphSchema:
("Constraint: Team",
"SHOW CONSTRAINTS WHERE name = 'team_id'",
lambda r: len(list(r)) == 1),
# Total constraint count (74 node types)
("Total constraints >= 74",
# Watson emotional-memory samples (v2.2.0)
("Constraint: EmotionalMemory",
"SHOW CONSTRAINTS WHERE name = 'emotionalmemory_id'",
lambda r: len(list(r)) == 1),
("Constraint: RelationshipTheme",
"SHOW CONSTRAINTS WHERE name = 'relationshiptheme_id'",
lambda r: len(list(r)) == 1),
("Constraint: DialogueNote",
"SHOW CONSTRAINTS WHERE name = 'dialoguenote_id'",
lambda r: len(list(r)) == 1),
("Constraint: DynamicPattern",
"SHOW CONSTRAINTS WHERE name = 'dynamicpattern_id'",
lambda r: len(list(r)) == 1),
# Shawn sample (v2.3.0)
("Constraint: Communication",
"SHOW CONSTRAINTS WHERE name = 'communication_id'",
lambda r: len(list(r)) == 1),
# Total constraint count (79 node types as of v2.3.0)
("Total constraints >= 79",
"SHOW CONSTRAINTS",
lambda r: len(list(r)) >= 74),
lambda r: len(list(r)) >= 79),
]
if include_schema_tests:
@@ -523,7 +556,7 @@ class LifeGraphSchema:
RETURN b.id AS id
"""),
# ── Personal: Sample goal (Seneca) ───────────────────────
# ── Personal: Sample goal (Watson) ───────────────────────
("Goal:goal_sample_2025", """
MERGE (g:Goal {id: 'goal_sample_2025'})
ON CREATE SET g.created_at = datetime()
@@ -556,6 +589,40 @@ class LifeGraphSchema:
RETURN a.id AS id
"""),
# ── Shawn: Sample personal contact + communication ───────
("Contact:contact_sample_personal", """
MERGE (c:Contact {id: 'contact_sample_personal'})
ON CREATE SET c.created_at = datetime()
SET c.name = 'Sample Personal Contact',
c.domain = 'personal',
c.relationship_strength = 'developing',
c.updated_at = datetime()
RETURN c.id AS id
"""),
("Communication:comm_sample", """
MERGE (c:Communication {id: 'comm_sample'})
ON CREATE SET c.created_at = datetime()
SET c.type = 'in_person',
c.contact_id = 'contact_sample_personal',
c.date = date(),
c.summary = 'Sample interaction record',
c.updated_at = datetime()
RETURN c.id AS id
"""),
# ── Watson: Sample emotional memory ──────────────────────
("EmotionalMemory:memory_sample", """
MERGE (m:EmotionalMemory {id: 'memory_sample'})
ON CREATE SET m.created_at = datetime()
SET m.date = date(),
m.theme = 'safety',
m.intensity = 3,
m.content = 'Sample emotional memory entry',
m.updated_at = datetime()
RETURN m.id AS id
"""),
# ── Work: Sample client ──────────────────────────────────
("Client:client_sample_corp", """
MERGE (c:Client {id: 'client_sample_corp'})
@@ -563,6 +630,7 @@ class LifeGraphSchema:
SET c.name = 'Sample Corp',
c.industry = 'Technology',
c.status = 'prospect',
c.domain = 'work',
c.updated_at = datetime()
RETURN c.id AS id
"""),
@@ -642,6 +710,8 @@ class LifeGraphSchema:
("PURSUING", "Person", "user_main", "Goal", "goal_sample_2025"),
("EXPLORES", "Book", "book_meditations_aurelius", "Topic", "topic_stoicism"),
("OWNS", "Person", "user_main", "Account", "account_tfsa_sample"),
("HAD", "Person", "user_main", "Communication", "comm_sample"),
("WITH", "Communication", "comm_sample", "Contact", "contact_sample_personal"),
]
created_rels = 0
@@ -688,31 +758,36 @@ Event Significant occurrences (celebrations, conferences)
Topic Subjects of interest (stoicism, AI in CX)
Goal Objectives (personal growth, career, fitness, financial)
PERSONAL TEAM:
PERSONAL TEAM (Iolaus):
────────────────────────────────────────────────────────────────
Shawn (General) Contact, Event, Task (all domain='personal'), Communication
Nate (Travel) Trip, Destination, Activity
Hypatia (Learning) Book, Author, LearningPath, Concept, Quote
Marcus (Fitness) Training, Exercise, Program, PersonalRecord, BodyMetric
Seneca (Reflection) Reflection, Value, Habit, LifeEvent, Intention
Watson (Reflection) Reflection, Value, Habit, LifeEvent, Intention,
EmotionalMemory, RelationshipTheme, DialogueNote, DynamicPattern
Bourdain (Food) Recipe, Restaurant, Ingredient, Meal, Technique
Bowie (Culture) Music, Film, Artwork, Playlist, Artist, Style
David (Culture) Music, Film, Artwork, Playlist, Artist, Style
Cousteau (Nature) Species, Plant, Tank, Garden, Ecosystem, Observation
Garth (Finance) Account, Investment, Asset, Liability, Budget, FinancialGoal
Cristiano (Football) Match, Team, League, Tournament, Player, Season
WORK TEAM:
WORK TEAM (Mentor):
────────────────────────────────────────────────────────────────
Alan (Strategy) Client, Vendor, Competitor, MarketTrend, Technology, Decision
Ann (Marketing) Content, Publication, Topic, Event
Jeffrey (Sales) Contact, Opportunity, Proposal, Meeting
Jarvis (Execution) Task, Meeting, Note, Decision, Project
Ann (Marketing) Content, Publication, Topic, Event (domain='work')
Jeffrey (Sales) Contact (domain='work'), Opportunity, Proposal, Meeting
Jarvis (Execution) Task (domain='work'), Meeting, Note, Decision, Project
AWS SA (Architecture) No domain ownership — writes Note (messages) only
ENGINEERING TEAM:
ENGINEERING TEAM (Kottos):
────────────────────────────────────────────────────────────────
Scotty (Infra) Infrastructure, Incident
Harper (Hacking) Prototype, Experiment
TOTAL: 74 node types, all with id uniqueness constraints
TOTAL: 79 node types, 16 assistants. All node types have id uniqueness
constraints. Contact/Event/Task are Universal with a `domain` field
('personal' or 'work') disambiguating Shawn vs. Jarvis/Jeffrey ownership.
CROSS-TEAM CONNECTIONS (examples):
────────────────────────────────────────────────────────────────
@@ -723,8 +798,9 @@ Infrastructure -[HOSTS]-> Project (Engineering ↔ Work)
Prototype -[SUPPORTS]-> Opportunity (Engineering ↔ Work)
Project -[GENERATES_REVENUE]-> Account (Work ↔ Personal)
Training -[BUILDS]-> Skill (Personal ↔ Work)
Communication -[WITH]-> Contact (Shawn: personal interaction history)
Full schema: docs/neo4j-unified-schema.md
Full schema: docs/neo4j-unified-schema.md (v2.3.0)
════════════════════════════════════════════════════════════════
"""
print(schema_doc)
@@ -858,7 +934,7 @@ def main():
schema.document_schema()
# Create constraints (includes automatic indexes)
logger.info("Creating constraints (74 node types)...")
logger.info("Creating constraints (79 node types)...")
schema.create_constraints()
# Create additional indexes
@@ -886,7 +962,7 @@ def main():
if test_success:
logger.info("✓ All tests passed!")
logger.info("\nUnified graph ready for all 15 assistants.")
logger.info("\nUnified graph ready for all 16 assistants.")
logger.info("Schema reference: docs/neo4j-unified-schema.md")
logger.info("\nNext steps:")
logger.info(" 1. Import data (Plex, Calibre, etc.)")

View File

@@ -35,11 +35,12 @@ EXPECTED_CONSTRAINTS = [
"book_id", "author_id", "learningpath_id", "concept_id", "quote_id",
# Marcus
"training_id", "exercise_id", "program_id", "personalrecord_id", "bodymetric_id",
# Seneca
# Watson (Reflection & Emotional Safety)
"reflection_id", "value_id", "habit_id", "lifeevent_id", "intention_id",
"emotionalmemory_id", "relationshiptheme_id", "dialoguenote_id", "dynamicpattern_id",
# Bourdain
"recipe_id", "restaurant_id", "ingredient_id", "meal_id", "technique_id",
# Bowie
# David (Arts & Culture)
"music_id", "film_id", "artwork_id", "playlist_id", "artist_id", "style_id",
# Cousteau
"species_id", "plant_id", "tank_id", "garden_id", "ecosystem_id", "observation_id",
@@ -47,6 +48,8 @@ EXPECTED_CONSTRAINTS = [
"account_id", "investment_id", "asset_id", "liability_id", "budget_id", "financialgoal_id",
# Cristiano
"match_id", "team_id", "league_id", "tournament_id", "player_id", "season_id",
# Shawn (Personal General Assistant)
"communication_id",
# Work: Business
"client_id", "contact_id", "opportunity_id", "proposal_id", "project_id",
# Work: Market Intelligence
@@ -68,11 +71,13 @@ EXPECTED_LABELS = {
"Book", "Author", "LearningPath", "Concept", "Quote",
"Training", "Exercise", "Program", "PersonalRecord", "BodyMetric",
"Reflection", "Value", "Habit", "LifeEvent", "Intention",
"EmotionalMemory", "RelationshipTheme", "DialogueNote", "DynamicPattern",
"Recipe", "Restaurant", "Ingredient", "Meal", "Technique",
"Music", "Film", "Artwork", "Playlist", "Artist", "Style",
"Species", "Plant", "Tank", "Garden", "Ecosystem", "Observation",
"Account", "Investment", "Asset", "Liability", "Budget", "FinancialGoal",
"Match", "Team", "League", "Tournament", "Player", "Season",
"Communication",
"Client", "Contact", "Opportunity", "Proposal", "Project",
"Vendor", "Competitor", "MarketTrend", "Technology",
"Content", "Publication",
@@ -90,6 +95,9 @@ EXPECTED_SAMPLE_NODES = [
("Topic", "topic_stoicism"),
("Topic", "topic_ai_in_cx"),
("Account", "account_tfsa_sample"),
("Contact", "contact_sample_personal"),
("Communication", "comm_sample"),
("EmotionalMemory", "memory_sample"),
("Client", "client_sample_corp"),
("Skill", "skill_cx_strategy"),
("Infrastructure", "infra_neo4j_prod"),
@@ -102,6 +110,8 @@ EXPECTED_SAMPLE_RELS = [
("Person", "user_main", "PURSUING", "Goal", "goal_sample_2025"),
("Book", "book_meditations_aurelius", "EXPLORES", "Topic", "topic_stoicism"),
("Person", "user_main", "OWNS", "Account", "account_tfsa_sample"),
("Person", "user_main", "HAD", "Communication", "comm_sample"),
("Communication", "comm_sample", "WITH", "Contact", "contact_sample_personal"),
]
# A sampling of expected indexes (not exhaustive, just key ones to spot-check)
@@ -109,6 +119,7 @@ EXPECTED_INDEX_SAMPLES = [
"person_name", "book_title", "client_name", "event_date",
"training_date", "client_status", "task_status", "event_domain",
"team_name", "player_name", "match_competition",
"contact_domain", "task_domain",
]
@@ -157,7 +168,7 @@ def validate(driver, uri):
print("" * 65)
print(" VALIDATION REPORT — Koios Unified Knowledge Graph")
print("" * 65)
print(f" Schema Version: 2.1.0")
print(f" Schema Version: 2.3.0")
print(f" Database: {uri}")
print(f" Timestamp: {now}")
print("" * 65)