Cortex skill

Operate Cortex, the LifeOS memory system — the typed Knowledge Archive (People, Companies, Ideas, Research with typed related: links) plus recall of prior work sessions, ISAs, and conversations.

by danielmiessler·MIT license·★ 19,269 Stars on the repo·GitHub ↗

Use now

Files of Cortex

danielmiessler/main1 file shown
SKILL.md
Show the full text533 lines

Cortex Skill

What It Does

Operate Cortex, the LifeOS memory system, from one skill. Two halves: the Knowledge Archive — a curated, typed graph of notes across six entity domains (People, Companies, Ideas, Research, Blogs, Books), every note shipping typed related: cross-links so the archive is a connected graph, not a pile of files — and recall — finding prior work (sessions, ISAs, conversations) by topic or date phrase. Operations cover search, add, harvest, develop, ingest, contradiction-finding, graph traversal, compressed retrieval, conversation mining, the weekly distill pass, and session recall.

The Problem

Notes you save in isolation are notes you never find again. A flat folder of facts has no way to tell you that two notes contradict each other, that a new source updates an old claim, or that an idea connects to a person and a company you wrote up months ago. Knowledge dies when it can't be retrieved or related. This archive forces every note into a typed schema with mandatory cross-links and ripples updates through related notes on ingest, so the connections are built in at write time instead of being reconstructed by hand later.

How It Works

Manage the LifeOS Knowledge Archive at ~/.claude/LIFEOS/MEMORY/KNOWLEDGE/. Each operation routes through a subcommand below; notes follow the archive schema and ship with typed cross-links.

Archive schema: ~/.claude/LIFEOS/MEMORY/KNOWLEDGE/_schema.md

Hard rule — never write KNOWLEDGE/ directly. All writes route through this skill (add, harvest, ingest, develop) — never cp, mv, Write, or Edit straight into the directory, even during migration or bulk import. The skill enforces typed frontmatter, mandatory cross-links, and domain classification; raw filesystem writes skip those guarantees and corrupt the graph silently (broken links don't fail loudly — they produce phantom entries retrieval misses). Migration tooling MUST call this skill per chunk, never shell out to cp. The one sanctioned exception is the Algorithm's LEARN phase, which writes to KNOWLEDGE/ directly because it holds the best context and applies the same schemas (see Gotchas).

Workflow Routing

Workflows are inline command sections in this file (no Workflows/ dir); each row routes to the matching H2 section below.

Trigger Workflow Action
/knowledge (no args) status Health dashboard
/knowledge <query> search Search for notes matching query
/knowledge search <query> search Explicit search
/knowledge add <type> add Create a new note (People, Companies, or Ideas)
/knowledge harvest harvest Run KnowledgeHarvester on all sources
/knowledge develop develop Surface seedlings and enrich them
/knowledge ingest <url-or-file> ingest Read source, create note, ripple updates to related notes
/knowledge contradictions contradictions Find and review conflicting claims across notes
/knowledge graph graph Knowledge graph stats and navigation
/knowledge graph <slug> graph Traverse graph from a note
/knowledge retrieve <query> retrieve Compressed context retrieval
/knowledge mine mine Mine recent conversations for memory candidates
/knowledge distill distill Weekly harvest of the archive into a routed, cited digest
/cortex recall <query> (also /cs) recall Find prior work — sessions, ISAs, conversations — by topic or date phrase

/cortex <args> and legacy /knowledge <args> route identically — the subcommand decides.

If $ARGUMENTS doesn't match a subcommand, treat it as a search query.


status (default, no args)

Run the harvester status command and display results:

bun ~/.claude/LIFEOS/TOOLS/KnowledgeHarvester.ts status

Also show:

  • Quick summary of domains with note counts
  • Any orphan wikilinks
  • Any stale seedlings
  • Time since last harvest

Present in NATIVE mode.


search <query>

Search the Knowledge Archive for notes matching $ARGUMENTS.

Step 1 — Lexical search:

rg -i "$ARGUMENTS" ~/.claude/LIFEOS/MEMORY/KNOWLEDGE/ --type md -l

Step 2 — Frontmatter search (tags and titles):

rg -i "title:.*$ARGUMENTS|tags:.*$ARGUMENTS" ~/.claude/LIFEOS/MEMORY/KNOWLEDGE/ --type md -l

Step 3 — Wikilink search:

rg "\[\[.*$ARGUMENTS.*\]\]" ~/.claude/LIFEOS/MEMORY/KNOWLEDGE/ --type md -l

Deduplicate results across all three. For each match, read the first 5 lines of frontmatter to show title, domain, status, tags.

Present results as a table:

| Note | Domain | Status | Tags | Relevance |

If no results found, say so and suggest checking the full MEMORY/ system or running a harvest.


add <type>

Create a new note manually in the specified entity type.

  1. Validate type is one of: People, Companies, Ideas, Research
  2. Ask for a title (or use remaining args after type)
  3. Generate kebab-case filename from title
  4. MANDATORY: Find 2-3 related notes first. Before writing the new note, grep existing Knowledge for related entities by topic/tags/name. This becomes the related: frontmatter array. No Knowledge note ships without typed links. See Canonical Linking Requirement below.
  5. Create the note with proper frontmatter from _schema.md — the validator (LIFEOS/TOOLS/KnowledgeSchema.ts ENVELOPE) requires all EIGHT of: id (mint via mintId(slug, created) — kb_ + 12 hex chars), type, title, tags (min 1), quality (0-10), created, updated, convention: kb-v3 — plus type-specific body sections. A note built from the old six-field list can never validate (public issue #1678, @christauff). Set created: and updated: to today's date from date +%Y-%m-%d — archive-entry dates, never a source's publication date (public PR #1604, @asdf8675309).
  6. Write the file to KNOWLEDGE/<Type>/<kebab-case-title>.md — slug max 60 chars
  7. Verify every slug in related: exists in the archive before saving
  8. Regenerate the type's MOC:
bun ~/.claude/LIFEOS/TOOLS/KnowledgeHarvester.ts index

Topic is a tag, not a type. A security insight is an Idea with a security tag. A security company is a Company with a security tag. The entity type determines the schema; the tag determines the topic.

Canonical Linking Requirement (MANDATORY)

Every new Knowledge note must ship with typed cross-links. This is not optional. The normative contract is _schema.md (regenerated by the schema tools): every note carries typed related: links naming the target note and the relationship type, so the archive is a graph, not a pile.

Every write must include:

  1. related: frontmatter array — 2-4 typed entries linking to other Knowledge entries (any domain: People, Companies, Ideas, Research)
  2. Body wikilinks — 1-3 [[slug]] references woven into the prose where natural (Implications, Evidence, or Context sections)

9 relationship types (pick the most accurate, prefer specific over generic):

Type Meaning
related Generic association (default only if no better fit)
supports Provides evidence for the linked note
contradicts Conflicts with the linked note
extends Builds upon the linked note
part-of Component of a larger whole
instance-of Example of a pattern
caused-by Result of the linked note
preceded-by Came before temporally
derived-from Distilled from the linked source note (e.g. blog → idea)

Frontmatter format:

related:
  - slug: other-note-slug
    type: extends
  - slug: another-note-slug
    type: supports

How to find related notes before writing:

# By topic/keyword
rg -l "TOPIC" ~/.claude/LIFEOS/MEMORY/KNOWLEDGE/ --type md

# By tag overlap
rg "^tags:.*TAG" ~/.claude/LIFEOS/MEMORY/KNOWLEDGE/ --type md -l

# For People/Companies — grep by name
rg -l "Person Name" ~/.claude/LIFEOS/MEMORY/KNOWLEDGE/

Enforcement:

  • Writes that skip related: are incomplete and must be fixed before the skill/workflow returns success
  • The ingest workflow runs this as part of the ripple pass
  • The Algorithm LEARN phase includes this in its knowledge capture step
  • All agents writing Knowledge entries must follow this rule — it is part of the schema, not an optional enhancement

harvest

Run the KnowledgeHarvester to pull new knowledge from all LifeOS sources:

bun ~/.claude/LIFEOS/TOOLS/KnowledgeHarvester.ts harvest

Display results. If nothing was harvested, explain that sources are already up to date.

Optionally accept --source filter: /knowledge harvest work or /knowledge harvest memory.


develop

The weekly gardening workflow. Surface seedling notes that are ready for enrichment.

Step 1 — Find seedlings:

rg "^status: seedling" ~/.claude/LIFEOS/MEMORY/KNOWLEDGE/ --type md -l

Step 2 — For each seedling:

  • Read the note
  • Read related notes (follow wikilinks, search for same tags)
  • Check if newer WORK/ ISAs or auto-memory entries have relevant context
  • Enrich the note: add context, suggest wikilinks, flesh out content

Step 3 — Present the diff to the user for approval.

Step 4 — If approved:

  • Write the updated note
  • Promote status from seedling to budding (or evergreen if comprehensive)
  • Update updated: to today's date from date +%Y-%m-%d; leave created: untouched
  • Regenerate affected MOCs

If no seedlings exist, report archive is clean.


ingest <url-or-file>

Ingest a source into the Knowledge Archive. This is the key Karpathy-inspired upgrade: reading a source doesn't just create one note — it ripples updates through existing related notes.

If no argument provided: Show usage: /knowledge ingest <url-or-file-path>

Step 1 — Fetch the source
  • URL: Use WebFetch to retrieve and read the content. If WebFetch fails, try curl -sL via Bash.
  • File path: Use Read tool to read the local file.

Summarize the source in 2-3 sentences. Identify key entities, claims, and insights.

Step 2 — Classify and create primary note

Determine entity type (People, Companies, Ideas, or Research) using the classification rules in _schema.md. Most ingested sources become Ideas.

Create the primary note using the schema for that type:

  • Generate kebab-case slug from title (max 60 chars)
  • Write to KNOWLEDGE/<Type>/<slug>.md with proper frontmatter
  • Set created: and updated: to today's date from date +%Y-%m-%d — both record when the note entered the archive, not when the source was published. A stated publication date belongs in source_date:; never let it reach created:, and never guess a date the source does not state (public PR #1604, @asdf8675309)
  • Include source_url: or source_path: in frontmatter
  • MANDATORY: Include related: array with 2-4 typed links — the ripple pass (Step 3) identifies these, and they must be baked into the frontmatter of the primary note at creation time, not added after
Step 3 — Ripple pass (the key innovation)

Search for existing notes that relate to this new content:

# Search by extracted tags
rg -i "TAG1|TAG2|TAG3" ~/.claude/LIFEOS/MEMORY/KNOWLEDGE/ --type md -l --glob '!_*'

# Search by key entities/concepts mentioned
rg -i "ENTITY1|ENTITY2" ~/.claude/LIFEOS/MEMORY/KNOWLEDGE/ --type md -l --glob '!_*'

For each related note found (up to 10):

  1. Read the note
  2. Determine if the new source adds information, context, or contradicts existing claims
  3. If yes, propose the specific update (add wikilink, add evidence, note contradiction)

Present the ripple plan to the user:

📥 INGEST RIPPLE PLAN:
  PRIMARY: Ideas/new-note-slug — "Title" (created)
  PRIMARY related: frontmatter links (MANDATORY):
    → Ideas/existing-note-1 — type: extends
    → Ideas/existing-note-2 — type: supports
    → People/person-slug — type: related
  RIPPLE (reverse-direction updates to existing notes):
    → Ideas/existing-note-1 — add body [[new-note-slug]] wikilink + add to its related: array (type: extends)
    → Ideas/existing-note-2 — update Evidence section with new data point + add to related:
    → Ideas/existing-note-3 — ⚠️ CONTRADICTION: new source says X, note says Y — type: contradicts
  NO CHANGE: Ideas/tangentially-related — mentioned same tag but no substantive connection
Step 4 — Execute ripple updates

After the user approves (or you determine updates are low-risk cross-references):

  • Primary note: ensure related: frontmatter array has 2-4 typed entries — this is mandatory, not optional
  • Related notes: add reverse-direction related: entries to their frontmatter with appropriate types
  • Body wikilinks: add [[wikilinks]] in existing prose where natural (not forced)
  • Update updated: to today's date from date +%Y-%m-%d on modified notes; leave their created: untouched
  • For contradictions: add a > ⚠️ **Contradiction:** [note] claims X — see [[new-note]] for counter-evidence callout, AND add type: contradicts in related: arrays
Step 5 — Log and index

Append to KNOWLEDGE/_log.md:

## [YYYY-MM-DD] ingest | Title
- Source: <url or path>
- Primary: <Type>/<slug>
- Ripple: N notes updated, N contradictions flagged

Regenerate MOCs:

bun ~/.claude/LIFEOS/TOOLS/KnowledgeHarvester.ts index

Present in NATIVE mode.


contradictions

Find and review conflicting claims across Knowledge notes.

Step 1 — Get contradiction candidates

Run the KnowledgeHarvester contradiction finder:

bun ~/.claude/LIFEOS/TOOLS/KnowledgeHarvester.ts contradictions

This outputs pairs of notes with high tag overlap (2+ shared tags), ranked by overlap count.

Step 2 — Semantic review

For each pair (up to 10 highest-overlap pairs):

  1. Read both notes
  2. Extract key claims from each (Thesis, Evidence, Key Facts sections)
  3. Check for:
    • Direct contradictions — Note A says X, Note B says not-X
    • Temporal supersession — Note A's claim is outdated by Note B's newer evidence
    • Scope conflicts — Both claim authority on the same topic but reach different conclusions
Step 3 — Report

Present findings:

🔍 CONTRADICTION SCAN:
  Pairs checked: N
  Contradictions found: N
  Superseded claims: N

  ⚠️ CONTRADICTION:
    [[note-a]] claims: "X"
    [[note-b]] claims: "Y"
    Resolution: [suggest which is correct, or flag for the user]

  📅 SUPERSEDED:
    [[older-note]] (2026-01-15): "X was true"
    [[newer-note]] (2026-03-20): "X is no longer true because Y"
    Action: Update older note with correction
Step 4 — Fix (with approval)

If the user approves resolutions:

  • Update contradicted notes with correction callouts
  • Update superseded notes with > 📅 **Updated:** See [[newer-note]] for current information
  • Update updated: dates
  • Regenerate MOCs

Present in NATIVE mode.


graph [slug]

Navigate the Knowledge Archive as a graph.

No argument — stats overview:

bun ~/.claude/LIFEOS/TOOLS/KnowledgeGraph.ts stats

Show node count, edge count, top clusters, most connected hubs, and isolated nodes.

With slug — traverse from a note:

bun ~/.claude/LIFEOS/TOOLS/KnowledgeGraph.ts traverse <slug> --hops 2

Show all notes connected within 2 hops via tags, wikilinks, and typed relationships. Useful for exploring how knowledge connects across domains.

Related notes only:

bun ~/.claude/LIFEOS/TOOLS/KnowledgeGraph.ts related <slug>

Present in NATIVE mode.


retrieve <query>

Compressed context retrieval over the Knowledge Archive using BM25-lite scoring.

bun ~/.claude/LIFEOS/TOOLS/MemoryRetriever.ts "<query>" --top 5

Returns the top matching notes with compressed summaries, ranked by title match, tag overlap, and content frequency. Useful for loading relevant knowledge context without reading full files.

For raw excerpts without LLM compression:

bun ~/.claude/LIFEOS/TOOLS/MemoryRetriever.ts "<query>" --raw

Present in NATIVE mode.


mine

Mine recent conversations for memory candidates (decisions, preferences, milestones, problems).

bun ~/.claude/LIFEOS/TOOLS/SessionHarvester.ts --mine --recent 10

Candidates are written to KNOWLEDGE/_harvest-queue/ for review — never directly to KNOWLEDGE/. Use /knowledge harvest to process the queue.

For dry run (preview only):

bun ~/.claude/LIFEOS/TOOLS/SessionHarvester.ts --mine --recent 10 --dry-run

Present in NATIVE mode.


distill

Weekly harvest of the archive into routed outputs. Distill is a router, not a destination: every item lands in the system of record that already owns it, and the digest is an index pointing at those destinations. It never creates or edits KNOWLEDGE notes (mutations belong to develop/contradictions/ingest), and it never re-surfaces an item a previous run already routed.

Done looks like: a dated digest at ~/.claude/LIFEOS/MEMORY/DIGESTS/YYYY-MM-DD-distill.md with ≤10 items across three lanes, every item citing its source notes and linking its routed destination; ≤5 content-idea issues filed; ≤5 upgrades filed; all surfaced items marked in state. Overflow is named with a dropped-count, never silently truncated.

Step 1 — Gather (deterministic)
bun ~/.claude/LIFEOS/TOOLS/KnowledgeDistill.ts gather --days 7

Returns JSON: in-window notes (created/updated, minus previously surfaced), hot tag clusters (window count vs archive baseline), seedling and contradiction stats.

Step 2 — Synthesize (the model leg)

Cluster the candidates into digest items. Quality bar per item: ≥2 source notes, a why-now line (recency, cluster growth, or TELOS relevance), and a one-sentence pitch in plain language. Three lanes:

  • Content candidates — ideas that could become a post, newsletter section, or video. Bias toward the principal's TELOS content goals.
  • System improvements — anything that smells like a LifeOS upgrade.
  • Archive health — contradiction pairs and seedling counts, report-only; deeper action routes to /knowledge contradictions or /knowledge develop.
Step 3 — Route to systems of record

Content lane (≤5). The destination repo and label come from LIFEOS/USER/CUSTOMIZATIONS/SKILLS/Cortex/DistillConfig.json (contentRepo, contentLabel) — never hardcoded here:

gh issue create --repo <contentRepo> --label <contentLabel> \
  --title "<pitch>" --body "<why-now + source note paths + suggested format>"

No config file → skip the issue lane and list content candidates in the digest only.

System lane (≤5; dedupe is claim-hash based, duplicates exit 0 silently):

bun ~/.claude/LIFEOS/TOOLS/Upgrades.ts add --claim "<one sentence>" --source autonomous \
  --recommendation "<proposed encoding>" --target <hook|doctrine|rule|skill|settings|context> \
  --evidence "<source note path>"
Step 4 — Digest + mark

Write the digest file (lanes as sections; every item: pitch, sources, destination link or "empty — <reason>"), then:

bun ~/.claude/LIFEOS/TOOLS/KnowledgeDistill.ts mark --digest <digest-path>

which records surfaced slugs and item hashes in MEMORY/STATE/distill.json.

Headless / scheduled

bun ~/.claude/LIFEOS/TOOLS/KnowledgeDistill.ts run --headless [--dry-run] performs all four steps unattended (synthesis via Inference.ts, never a nested claude session). The weekly launchd job com.lifeos.distill (Sun 09:00) runs exactly this. --dry-run prints the full routing plan and writes nothing.

Present in NATIVE mode.


recall <query>

Find prior LifeOS work — sessions, ISAs, conversations — by topic, partial words, or date phrases like "yesterday" or "last week". A deterministic Bun CLI searches five sources in parallel (work registry, session names, work dir names, ISA bodies, conversation jsonl), scores by token-overlap × recency, applies date filters, and returns ranked results with snippets.

bun run ~/.claude/skills/Cortex/Tools/ContextSearch.ts "$ARGUMENTS" --pretty --limit 10

Flag patterns: --limit N · --since YYYY-MM-DD · --json | jq '.results[0]'. Date phrases parse inline: "yesterday markdown" becomes a single-day since/until window plus token search for markdown.

Usage modes:

  1. Standalone (/cs <topic> or an explicit recall ask) — present the pretty block, then: "Context loaded on [topic]. Most recent: [X]. What would you like to do?"
  2. Paired with a request — run the search silently first, use top results to anchor the answer; Read a result's path when deeper detail helps. Don't dump the search block unless asked.

Recall gotchas (ported from the retired ContextSearch skill):

  • Token-overlap scoring, not substring matching — queries hit sessions sharing ≥1 non-stopword token, so half-remembered phrasing works.
  • today/yesterday/last week/N days ago/YYYY-MM-DD parse out as bounded date filters; remaining tokens still score content.
  • JSONL date = first user-message timestamp, not file mtime (mtime drifts on re-read); session-names.json entries fall back to mtime.
  • JSONL search is restricted to this install's own conversation directory by design.
  • No persistent index — every query rescans (~1–2s total); an opt-in cached index is the v2 if scale demands.

Gotchas

  • 4 entity types now. People (human beings), Companies (organizations), Ideas (insights/theses/analyses), Research (multi-source investigations with methodology). If it doesn't fit one of these, it's not knowledge — it belongs in WORK/ or LEARNING/.
  • Topic = tag, not domain. A security insight is an Idea with a security tag. Never create topic-based folders.
  • The lookup test. "Would the user look this up by name?" — if not, it's not knowledge.
  • Schema enforcement. Each entity type has required fields defined in _schema.md. Always read the schema before writing.
  • Algorithm LEARN phase writes directly. The LEARN phase has the best context — it writes to KNOWLEDGE/ with proper schemas. Harvester reflections are disabled.
  • Never delete notes without asking. Pruning is automatic (90-day seedling expiry via harvester). Manual deletion requires the user's approval.
  • Wikilinks use strict kebab-case. [[prompt-injection]] not [[Prompt Injection]].
  • All harvested notes start as seedlings. Only /knowledge develop promotes them.
  • Temporal validity is optional. Notes can have valid_from/valid_until frontmatter fields to track when facts were true. The contradiction detector uses these to skip non-overlapping time windows.

Examples

Example 1: Search the archive

User: "what do we know about prompt injection?"
→ Routes to search — 3-pass (lexical + frontmatter + wikilink) over MEMORY/KNOWLEDGE/
→ Returns table of matching notes with domain, status, tags

Example 2: Ingest a source

User: "/knowledge ingest https://example.com/article"
→ Fetches the source, classifies entity type, creates primary note with typed related: links
→ Ripple pass proposes updates to existing related notes; user approves; MOCs regenerated

Example 3: Status check

User: "knowledge status"
→ Runs KnowledgeHarvester.ts status
→ Shows domain note counts, orphan wikilinks, stale seedlings, time since last harvest
1---
2name: Cortex
3version: 2.1.2
4description: "Operate Cortex, the LifeOS memory system — the typed Knowledge Archive (People, Companies, Ideas, Research with typed related: links) plus recall of prior work sessions, ISAs, and conversations. Search, add, harvest, develop, ingest, distill, graph-navigate, recall. USE WHEN cortex, knowledge, knowledge base, search knowledge, what do we know about, archive, harvest, knowledge status, develop note, add to knowledge, ingest, contradictions, knowledge graph, retrieve, mine conversations, distill, weekly digest, cortex digest, context search, prior work, recall, remember, previous sessions, context recovery, what did we do, find session, search history, resume, pick up where we left off, cold start, yesterday's work, last week, the one about. NOT FOR published-content semantic search across blog/newsletter/X/LinkedIn, or one-shot URL/YouTube ingestion via the Arbol harvester pipeline."
5argument-hint: [search|add|harvest|develop|ingest|distill|recall|contradictions|graph|retrieve|mine|<query>]
6context: fork
7background: false
8---
9 
10# Cortex Skill
11 
12## What It Does
13 
14Operate Cortex, the LifeOS memory system, from one skill. Two halves: the **Knowledge Archive** — a curated, typed graph of notes across six entity domains (People, Companies, Ideas, Research, Blogs, Books), every note shipping typed `related:` cross-links so the archive is a connected graph, not a pile of files — and **recall** — finding prior work (sessions, ISAs, conversations) by topic or date phrase. Operations cover search, add, harvest, develop, ingest, contradiction-finding, graph traversal, compressed retrieval, conversation mining, the weekly distill pass, and session recall.
15 
16## The Problem
17 
18Notes you save in isolation are notes you never find again. A flat folder of facts has no way to tell you that two notes contradict each other, that a new source updates an old claim, or that an idea connects to a person and a company you wrote up months ago. Knowledge dies when it can't be retrieved or related. This archive forces every note into a typed schema with mandatory cross-links and ripples updates through related notes on ingest, so the connections are built in at write time instead of being reconstructed by hand later.
19 
20## How It Works
21 
22Manage the LifeOS Knowledge Archive at `~/.claude/LIFEOS/MEMORY/KNOWLEDGE/`. Each operation routes through a subcommand below; notes follow the archive schema and ship with typed cross-links.
23 
24**Archive schema:** `~/.claude/LIFEOS/MEMORY/KNOWLEDGE/_schema.md`
25 
26**Hard rule — never write KNOWLEDGE/ directly.** All writes route through this skill (`add`, `harvest`, `ingest`, `develop`) — never `cp`, `mv`, `Write`, or `Edit` straight into the directory, even during migration or bulk import. The skill enforces typed frontmatter, mandatory cross-links, and domain classification; raw filesystem writes skip those guarantees and corrupt the graph silently (broken links don't fail loudly — they produce phantom entries retrieval misses). Migration tooling MUST call this skill per chunk, never shell out to `cp`. The one sanctioned exception is the Algorithm's LEARN phase, which writes to `KNOWLEDGE/` directly because it holds the best context and applies the same schemas (see Gotchas).
27 
28## Workflow Routing
29 
30Workflows are inline command sections in this file (no Workflows/ dir); each row routes to the matching H2 section below.
31 
32| Trigger | Workflow | Action |
33|---------|----------|--------|
34| `/knowledge` (no args) | **status** | Health dashboard |
35| `/knowledge <query>` | **search** | Search for notes matching query |
36| `/knowledge search <query>` | **search** | Explicit search |
37| `/knowledge add <type>` | **add** | Create a new note (People, Companies, or Ideas) |
38| `/knowledge harvest` | **harvest** | Run KnowledgeHarvester on all sources |
39| `/knowledge develop` | **develop** | Surface seedlings and enrich them |
40| `/knowledge ingest <url-or-file>` | **ingest** | Read source, create note, ripple updates to related notes |
41| `/knowledge contradictions` | **contradictions** | Find and review conflicting claims across notes |
42| `/knowledge graph` | **graph** | Knowledge graph stats and navigation |
43| `/knowledge graph <slug>` | **graph** | Traverse graph from a note |
44| `/knowledge retrieve <query>` | **retrieve** | Compressed context retrieval |
45| `/knowledge mine` | **mine** | Mine recent conversations for memory candidates |
46| `/knowledge distill` | **distill** | Weekly harvest of the archive into a routed, cited digest |
47| `/cortex recall <query>` (also `/cs`) | **recall** | Find prior work — sessions, ISAs, conversations — by topic or date phrase |
48 
49`/cortex <args>` and legacy `/knowledge <args>` route identically — the subcommand decides.
50 
51If `$ARGUMENTS` doesn't match a subcommand, treat it as a search query.
52 
53---
54 
55## status (default, no args)
56 
57Run the harvester status command and display results:
58 
59```bash
60bun ~/.claude/LIFEOS/TOOLS/KnowledgeHarvester.ts status
61```
62 
63Also show:
64- Quick summary of domains with note counts
65- Any orphan wikilinks
66- Any stale seedlings
67- Time since last harvest
68 
69Present in NATIVE mode.
70 
71---
72 
73## search <query>
74 
75Search the Knowledge Archive for notes matching `$ARGUMENTS`.
76 
77**Step 1 — Lexical search:**
78```bash
79rg -i "$ARGUMENTS" ~/.claude/LIFEOS/MEMORY/KNOWLEDGE/ --type md -l
80```
81 
82**Step 2 — Frontmatter search (tags and titles):**
83```bash
84rg -i "title:.*$ARGUMENTS|tags:.*$ARGUMENTS" ~/.claude/LIFEOS/MEMORY/KNOWLEDGE/ --type md -l
85```
86 
87**Step 3 — Wikilink search:**
88```bash
89rg "\[\[.*$ARGUMENTS.*\]\]" ~/.claude/LIFEOS/MEMORY/KNOWLEDGE/ --type md -l
90```
91 
92Deduplicate results across all three. For each match, read the first 5 lines of frontmatter to show title, domain, status, tags.
93 
94Present results as a table:
95```
96| Note | Domain | Status | Tags | Relevance |
97```
98 
99If no results found, say so and suggest checking the full MEMORY/ system or running a harvest.
100 
101---
102 
103## add <type>
104 
105Create a new note manually in the specified entity type.
106 
1071. Validate type is one of: People, Companies, Ideas, Research
1082. Ask for a title (or use remaining args after type)
1093. Generate kebab-case filename from title
1104. **MANDATORY: Find 2-3 related notes first.** Before writing the new note, grep existing Knowledge for related entities by topic/tags/name. This becomes the `related:` frontmatter array. No Knowledge note ships without typed links. See Canonical Linking Requirement below.
1115. Create the note with proper frontmatter from `_schema.md` — the validator (`LIFEOS/TOOLS/KnowledgeSchema.ts` ENVELOPE) requires all EIGHT of: `id` (mint via `mintId(slug, created)` — `kb_` + 12 hex chars), `type`, `title`, `tags` (min 1), `quality` (0-10), `created`, `updated`, `convention: kb-v3` — plus type-specific body sections. A note built from the old six-field list can never validate (public issue #1678, @christauff). Set `created:` and `updated:` to today's date from `date +%Y-%m-%d` — archive-entry dates, never a source's publication date (public PR #1604, @asdf8675309).
1126. Write the file to `KNOWLEDGE/<Type>/<kebab-case-title>.md` — slug max 60 chars
1137. Verify every slug in `related:` exists in the archive before saving
1148. Regenerate the type's MOC:
115```bash
116bun ~/.claude/LIFEOS/TOOLS/KnowledgeHarvester.ts index
117```
118 
119**Topic is a tag, not a type.** A security insight is an Idea with a `security` tag. A security company is a Company with a `security` tag. The entity type determines the schema; the tag determines the topic.
120 
121## Canonical Linking Requirement (MANDATORY)
122 
123**Every new Knowledge note must ship with typed cross-links.** This is not optional. The normative contract is `_schema.md` (regenerated by the schema tools): every note carries typed `related:` links naming the target note and the relationship type, so the archive is a graph, not a pile.
124 
125**Every write must include:**
126 
1271. **`related:` frontmatter array** — 2-4 typed entries linking to other Knowledge entries (any domain: People, Companies, Ideas, Research)
1282. **Body wikilinks** — 1-3 `[[slug]]` references woven into the prose where natural (Implications, Evidence, or Context sections)
129 
130**9 relationship types** (pick the most accurate, prefer specific over generic):
131 
132| Type | Meaning |
133|------|---------|
134| `related` | Generic association (default only if no better fit) |
135| `supports` | Provides evidence for the linked note |
136| `contradicts` | Conflicts with the linked note |
137| `extends` | Builds upon the linked note |
138| `part-of` | Component of a larger whole |
139| `instance-of` | Example of a pattern |
140| `caused-by` | Result of the linked note |
141| `preceded-by` | Came before temporally |
142| `derived-from` | Distilled from the linked source note (e.g. blog → idea) |
143 
144**Frontmatter format:**
145```yaml
146related:
147 - slug: other-note-slug
148 type: extends
149 - slug: another-note-slug
150 type: supports
151```
152 
153**How to find related notes before writing:**
154```bash
155# By topic/keyword
156rg -l "TOPIC" ~/.claude/LIFEOS/MEMORY/KNOWLEDGE/ --type md
157 
158# By tag overlap
159rg "^tags:.*TAG" ~/.claude/LIFEOS/MEMORY/KNOWLEDGE/ --type md -l
160 
161# For People/Companies — grep by name
162rg -l "Person Name" ~/.claude/LIFEOS/MEMORY/KNOWLEDGE/
163```
164 
165**Enforcement:**
166- Writes that skip `related:` are incomplete and must be fixed before the skill/workflow returns success
167- The `ingest` workflow runs this as part of the ripple pass
168- The Algorithm LEARN phase includes this in its knowledge capture step
169- All agents writing Knowledge entries must follow this rule — it is part of the schema, not an optional enhancement
170 
171---
172 
173## harvest
174 
175Run the KnowledgeHarvester to pull new knowledge from all LifeOS sources:
176 
177```bash
178bun ~/.claude/LIFEOS/TOOLS/KnowledgeHarvester.ts harvest
179```
180 
181Display results. If nothing was harvested, explain that sources are already up to date.
182 
183Optionally accept `--source` filter: `/knowledge harvest work` or `/knowledge harvest memory`.
184 
185---
186 
187## develop
188 
189The weekly gardening workflow. Surface seedling notes that are ready for enrichment.
190 
191**Step 1 — Find seedlings:**
192```bash
193rg "^status: seedling" ~/.claude/LIFEOS/MEMORY/KNOWLEDGE/ --type md -l
194```
195 
196**Step 2 — For each seedling:**
197- Read the note
198- Read related notes (follow wikilinks, search for same tags)
199- Check if newer WORK/ ISAs or auto-memory entries have relevant context
200- Enrich the note: add context, suggest wikilinks, flesh out content
201 
202**Step 3 — Present the diff to the user for approval.**
203 
204**Step 4 — If approved:**
205- Write the updated note
206- Promote status from `seedling` to `budding` (or `evergreen` if comprehensive)
207- Update `updated:` to today's date from `date +%Y-%m-%d`; leave `created:` untouched
208- Regenerate affected MOCs
209 
210If no seedlings exist, report archive is clean.
211 
212---
213 
214## ingest <url-or-file>
215 
216Ingest a source into the Knowledge Archive. This is the key Karpathy-inspired upgrade: reading a source doesn't just create one note — it **ripples updates through existing related notes**.
217 
218**If no argument provided:** Show usage: `/knowledge ingest <url-or-file-path>`
219 
220### Step 1 — Fetch the source
221 
222- **URL:** Use WebFetch to retrieve and read the content. If WebFetch fails, try `curl -sL` via Bash.
223- **File path:** Use Read tool to read the local file.
224 
225Summarize the source in 2-3 sentences. Identify key entities, claims, and insights.
226 
227### Step 2 — Classify and create primary note
228 
229Determine entity type (People, Companies, Ideas, or Research) using the classification rules in `_schema.md`. Most ingested sources become Ideas.
230 
231Create the primary note using the schema for that type:
232- Generate kebab-case slug from title (max 60 chars)
233- Write to `KNOWLEDGE/<Type>/<slug>.md` with proper frontmatter
234- Set `created:` and `updated:` to today's date from `date +%Y-%m-%d` — both record when the note entered the archive, **not** when the source was published. A stated publication date belongs in `source_date:`; never let it reach `created:`, and never guess a date the source does not state (public PR #1604, @asdf8675309)
235- Include `source_url:` or `source_path:` in frontmatter
236- **MANDATORY: Include `related:` array with 2-4 typed links** — the ripple pass (Step 3) identifies these, and they must be baked into the frontmatter of the primary note at creation time, not added after
237 
238### Step 3 — Ripple pass (the key innovation)
239 
240Search for existing notes that relate to this new content:
241 
242```bash
243# Search by extracted tags
244rg -i "TAG1|TAG2|TAG3" ~/.claude/LIFEOS/MEMORY/KNOWLEDGE/ --type md -l --glob '!_*'
245 
246# Search by key entities/concepts mentioned
247rg -i "ENTITY1|ENTITY2" ~/.claude/LIFEOS/MEMORY/KNOWLEDGE/ --type md -l --glob '!_*'
248```
249 
250For each related note found (up to 10):
2511. Read the note
2522. Determine if the new source adds information, context, or contradicts existing claims
2533. If yes, propose the specific update (add wikilink, add evidence, note contradiction)
254 
255**Present the ripple plan to the user:**
256```
257📥 INGEST RIPPLE PLAN:
258 PRIMARY: Ideas/new-note-slug — "Title" (created)
259 PRIMARY related: frontmatter links (MANDATORY):
260 → Ideas/existing-note-1 — type: extends
261 → Ideas/existing-note-2 — type: supports
262 → People/person-slug — type: related
263 RIPPLE (reverse-direction updates to existing notes):
264 → Ideas/existing-note-1 — add body [[new-note-slug]] wikilink + add to its related: array (type: extends)
265 → Ideas/existing-note-2 — update Evidence section with new data point + add to related:
266 → Ideas/existing-note-3 — ⚠️ CONTRADICTION: new source says X, note says Y — type: contradicts
267 NO CHANGE: Ideas/tangentially-related — mentioned same tag but no substantive connection
268```
269 
270### Step 4 — Execute ripple updates
271 
272After the user approves (or you determine updates are low-risk cross-references):
273- **Primary note**: ensure `related:` frontmatter array has 2-4 typed entries — this is mandatory, not optional
274- **Related notes**: add reverse-direction `related:` entries to their frontmatter with appropriate types
275- **Body wikilinks**: add `[[wikilinks]]` in existing prose where natural (not forced)
276- Update `updated:` to today's date from `date +%Y-%m-%d` on modified notes; leave their `created:` untouched
277- For contradictions: add a `> ⚠️ **Contradiction:** [note] claims X — see [[new-note]] for counter-evidence` callout, AND add `type: contradicts` in related: arrays
278 
279### Step 5 — Log and index
280 
281Append to `KNOWLEDGE/_log.md`:
282```
283## [YYYY-MM-DD] ingest | Title
284- Source: <url or path>
285- Primary: <Type>/<slug>
286- Ripple: N notes updated, N contradictions flagged
287```
288 
289Regenerate MOCs:
290```bash
291bun ~/.claude/LIFEOS/TOOLS/KnowledgeHarvester.ts index
292```
293 
294Present in NATIVE mode.
295 
296---
297 
298## contradictions
299 
300Find and review conflicting claims across Knowledge notes.
301 
302### Step 1 — Get contradiction candidates
303 
304Run the KnowledgeHarvester contradiction finder:
305```bash
306bun ~/.claude/LIFEOS/TOOLS/KnowledgeHarvester.ts contradictions
307```
308 
309This outputs pairs of notes with high tag overlap (2+ shared tags), ranked by overlap count.
310 
311### Step 2 — Semantic review
312 
313For each pair (up to 10 highest-overlap pairs):
3141. Read both notes
3152. Extract key claims from each (Thesis, Evidence, Key Facts sections)
3163. Check for:
317 - **Direct contradictions** — Note A says X, Note B says not-X
318 - **Temporal supersession** — Note A's claim is outdated by Note B's newer evidence
319 - **Scope conflicts** — Both claim authority on the same topic but reach different conclusions
320 
321### Step 3 — Report
322 
323Present findings:
324```
325🔍 CONTRADICTION SCAN:
326 Pairs checked: N
327 Contradictions found: N
328 Superseded claims: N
329 
330 ⚠️ CONTRADICTION:
331 [[note-a]] claims: "X"
332 [[note-b]] claims: "Y"
333 Resolution: [suggest which is correct, or flag for the user]
334 
335 📅 SUPERSEDED:
336 [[older-note]] (2026-01-15): "X was true"
337 [[newer-note]] (2026-03-20): "X is no longer true because Y"
338 Action: Update older note with correction
339```
340 
341### Step 4 — Fix (with approval)
342 
343If the user approves resolutions:
344- Update contradicted notes with correction callouts
345- Update superseded notes with `> 📅 **Updated:** See [[newer-note]] for current information`
346- Update `updated:` dates
347- Regenerate MOCs
348 
349Present in NATIVE mode.
350 
351---
352 
353## graph [slug]
354 
355Navigate the Knowledge Archive as a graph.
356 
357**No argument — stats overview:**
358```bash
359bun ~/.claude/LIFEOS/TOOLS/KnowledgeGraph.ts stats
360```
361 
362Show node count, edge count, top clusters, most connected hubs, and isolated nodes.
363 
364**With slug — traverse from a note:**
365```bash
366bun ~/.claude/LIFEOS/TOOLS/KnowledgeGraph.ts traverse <slug> --hops 2
367```
368 
369Show all notes connected within 2 hops via tags, wikilinks, and typed relationships. Useful for exploring how knowledge connects across domains.
370 
371**Related notes only:**
372```bash
373bun ~/.claude/LIFEOS/TOOLS/KnowledgeGraph.ts related <slug>
374```
375 
376Present in NATIVE mode.
377 
378---
379 
380## retrieve <query>
381 
382Compressed context retrieval over the Knowledge Archive using BM25-lite scoring.
383 
384```bash
385bun ~/.claude/LIFEOS/TOOLS/MemoryRetriever.ts "<query>" --top 5
386```
387 
388Returns the top matching notes with compressed summaries, ranked by title match, tag overlap, and content frequency. Useful for loading relevant knowledge context without reading full files.
389 
390For raw excerpts without LLM compression:
391```bash
392bun ~/.claude/LIFEOS/TOOLS/MemoryRetriever.ts "<query>" --raw
393```
394 
395Present in NATIVE mode.
396 
397---
398 
399## mine
400 
401Mine recent conversations for memory candidates (decisions, preferences, milestones, problems).
402 
403```bash
404bun ~/.claude/LIFEOS/TOOLS/SessionHarvester.ts --mine --recent 10
405```
406 
407Candidates are written to `KNOWLEDGE/_harvest-queue/` for review — never directly to KNOWLEDGE/. Use `/knowledge harvest` to process the queue.
408 
409For dry run (preview only):
410```bash
411bun ~/.claude/LIFEOS/TOOLS/SessionHarvester.ts --mine --recent 10 --dry-run
412```
413 
414Present in NATIVE mode.
415 
416---
417 
418## distill
419 
420Weekly harvest of the archive into routed outputs. **Distill is a router, not a destination**: every item lands in the system of record that already owns it, and the digest is an index pointing at those destinations. It never creates or edits KNOWLEDGE notes (mutations belong to `develop`/`contradictions`/`ingest`), and it never re-surfaces an item a previous run already routed.
421 
422**Done looks like:** a dated digest at `~/.claude/LIFEOS/MEMORY/DIGESTS/YYYY-MM-DD-distill.md` with ≤10 items across three lanes, every item citing its source notes and linking its routed destination; ≤5 content-idea issues filed; ≤5 upgrades filed; all surfaced items marked in state. Overflow is named with a dropped-count, never silently truncated.
423 
424### Step 1 — Gather (deterministic)
425 
426```bash
427bun ~/.claude/LIFEOS/TOOLS/KnowledgeDistill.ts gather --days 7
428```
429 
430Returns JSON: in-window notes (created/updated, minus previously surfaced), hot tag clusters (window count vs archive baseline), seedling and contradiction stats.
431 
432### Step 2 — Synthesize (the model leg)
433 
434Cluster the candidates into digest items. Quality bar per item: ≥2 source notes, a why-now line (recency, cluster growth, or TELOS relevance), and a one-sentence pitch in plain language. Three lanes:
435 
436- **Content candidates** — ideas that could become a post, newsletter section, or video. Bias toward the principal's TELOS content goals.
437- **System improvements** — anything that smells like a LifeOS upgrade.
438- **Archive health** — contradiction pairs and seedling counts, report-only; deeper action routes to `/knowledge contradictions` or `/knowledge develop`.
439 
440### Step 3 — Route to systems of record
441 
442Content lane (≤5). The destination repo and label come from `LIFEOS/USER/CUSTOMIZATIONS/SKILLS/Cortex/DistillConfig.json` (`contentRepo`, `contentLabel`) — never hardcoded here:
443```bash
444gh issue create --repo <contentRepo> --label <contentLabel> \
445 --title "<pitch>" --body "<why-now + source note paths + suggested format>"
446```
447No config file → skip the issue lane and list content candidates in the digest only.
448 
449System lane (≤5; dedupe is claim-hash based, duplicates exit 0 silently):
450```bash
451bun ~/.claude/LIFEOS/TOOLS/Upgrades.ts add --claim "<one sentence>" --source autonomous \
452 --recommendation "<proposed encoding>" --target <hook|doctrine|rule|skill|settings|context> \
453 --evidence "<source note path>"
454```
455 
456### Step 4 — Digest + mark
457 
458Write the digest file (lanes as sections; every item: pitch, sources, destination link or "empty — <reason>"), then:
459 
460```bash
461bun ~/.claude/LIFEOS/TOOLS/KnowledgeDistill.ts mark --digest <digest-path>
462```
463 
464which records surfaced slugs and item hashes in `MEMORY/STATE/distill.json`.
465 
466### Headless / scheduled
467 
468`bun ~/.claude/LIFEOS/TOOLS/KnowledgeDistill.ts run --headless [--dry-run]` performs all four steps unattended (synthesis via `Inference.ts`, never a nested `claude` session). The weekly launchd job `com.lifeos.distill` (Sun 09:00) runs exactly this. `--dry-run` prints the full routing plan and writes nothing.
469 
470Present in NATIVE mode.
471 
472---
473 
474## recall <query>
475 
476Find prior LifeOS work — sessions, ISAs, conversations — by topic, partial words, or date phrases like "yesterday" or "last week". A deterministic Bun CLI searches five sources in parallel (work registry, session names, work dir names, ISA bodies, conversation jsonl), scores by token-overlap × recency, applies date filters, and returns ranked results with snippets.
477 
478```bash
479bun run ~/.claude/skills/Cortex/Tools/ContextSearch.ts "$ARGUMENTS" --pretty --limit 10
480```
481 
482Flag patterns: `--limit N` · `--since YYYY-MM-DD` · `--json | jq '.results[0]'`. Date phrases parse inline: `"yesterday markdown"` becomes a single-day since/until window plus token search for `markdown`.
483 
484**Usage modes:**
485 
4861. **Standalone** (`/cs <topic>` or an explicit recall ask) — present the pretty block, then: "Context loaded on [topic]. Most recent: [X]. What would you like to do?"
4872. **Paired with a request** — run the search silently first, use top results to anchor the answer; Read a result's `path` when deeper detail helps. Don't dump the search block unless asked.
488 
489**Recall gotchas (ported from the retired ContextSearch skill):**
490 
491- Token-overlap scoring, not substring matching — queries hit sessions sharing ≥1 non-stopword token, so half-remembered phrasing works.
492- `today`/`yesterday`/`last week`/`N days ago`/`YYYY-MM-DD` parse out as bounded date filters; remaining tokens still score content.
493- JSONL date = first user-message timestamp, not file mtime (mtime drifts on re-read); session-names.json entries fall back to mtime.
494- JSONL search is restricted to this install's own conversation directory by design.
495- No persistent index — every query rescans (~1–2s total); an opt-in cached index is the v2 if scale demands.
496 
497---
498 
499## Gotchas
500 
501- **4 entity types now.** People (human beings), Companies (organizations), Ideas (insights/theses/analyses), Research (multi-source investigations with methodology). If it doesn't fit one of these, it's not knowledge — it belongs in WORK/ or LEARNING/.
502- **Topic = tag, not domain.** A security insight is an Idea with a `security` tag. Never create topic-based folders.
503- **The lookup test.** "Would the user look this up by name?" — if not, it's not knowledge.
504- **Schema enforcement.** Each entity type has required fields defined in `_schema.md`. Always read the schema before writing.
505- **Algorithm LEARN phase writes directly.** The LEARN phase has the best context — it writes to KNOWLEDGE/ with proper schemas. Harvester reflections are disabled.
506- **Never delete notes without asking.** Pruning is automatic (90-day seedling expiry via harvester). Manual deletion requires the user's approval.
507- **Wikilinks use strict kebab-case.** `[[prompt-injection]]` not `[[Prompt Injection]]`.
508- **All harvested notes start as seedlings.** Only `/knowledge develop` promotes them.
509- **Temporal validity is optional.** Notes can have `valid_from`/`valid_until` frontmatter fields to track when facts were true. The contradiction detector uses these to skip non-overlapping time windows.
510 
511## Examples
512 
513**Example 1: Search the archive**
514```
515User: "what do we know about prompt injection?"
516→ Routes to search — 3-pass (lexical + frontmatter + wikilink) over MEMORY/KNOWLEDGE/
517→ Returns table of matching notes with domain, status, tags
518```
519 
520**Example 2: Ingest a source**
521```
522User: "/knowledge ingest https://example.com/article"
523→ Fetches the source, classifies entity type, creates primary note with typed related: links
524→ Ripple pass proposes updates to existing related notes; user approves; MOCs regenerated
525```
526 
527**Example 3: Status check**
528```
529User: "knowledge status"
530→ Runs KnowledgeHarvester.ts status
531→ Shows domain note counts, orphan wikilinks, stale seedlings, time since last harvest
532```
533 

Discussion

Alternatives