Doramagic Project Pack · Human Manual

stoa

Stoa operates as an MCP server that manages a hierarchical vault of wiki pages. The vault structure organizes content into wikis, each containing typed pages (concepts, guides, decisions, ...

Introduction to Stoa

Related topics: Core Concepts and Vocabulary, Architecture and Design

Section Related Pages

Continue reading this section for the full explanation and source context.

Section Core Components

Continue reading this section for the full explanation and source context.

Section Page Types

Continue reading this section for the full explanation and source context.

Section Vault Structure

Continue reading this section for the full explanation and source context.

Related topics: Core Concepts and Vocabulary, Architecture and Design

Introduction to Stoa

Stoa is a vault-based knowledge management system powered by MCP (Model Context Protocol). It provides a structured wiki infrastructure for organizing, synthesizing, and retrieving information across multiple knowledge domains. The system combines a file-based vault architecture with intelligent tooling for content discovery, cross-referencing, and collaborative agent workflows.

Overview

Stoa operates as an MCP server that manages a hierarchical vault of wiki pages. The vault structure organizes content into wikis, each containing typed pages (concepts, guides, decisions, journals, etc.) with frontmatter metadata and markdown body content.

The system provides:

  • File-based storage: All content stored as markdown files with YAML frontmatter
  • MCP tooling: Standardized tools for read, write, search, and coordination operations
  • Multi-wiki support: Independent wiki spaces with cross-wiki linking via wikilinks
  • Agent profiles: Named agent identities with associated skills and capabilities
  • Synthesis engine: Automated compilation of related pages into synthesized summaries

Architecture

Core Components

graph TD
    subgraph "Transport Layer"
        STDIO[Stdio Transport]
        HTTP[HTTP Transport]
    end
    
    subgraph "Core Engine"
        IDX[Index Engine]
        SYN[Synthesis Engine]
        WL[Wikilink Parser]
        FM[Frontmatter Handler]
    end
    
    subgraph "Tools"
        RECALL[Recall - Search]
        INBOX[Inbox - Capture]
        NEW[New - Create]
        SYNTH[Synthesize]
        CLAIM[Claims]
        TASKS[Tasks]
    end
    
    subgraph "Platform Integration"
        POKEAPI[PokeAPI Client]
        STADIUM[Stadium Platform]
        SKILLS[Skills Platform]
    end
    
    STDIO --> SERVER[MCP Server]
    HTTP --> SERVER
    SERVER --> TOOLS[Tools Registry]
    TOOLS --> CORE[Core Engine]
    CORE --> IDX
    CORE --> SYN
    CORE --> WL
    CORE --> FM
    TOOLS --> PLATFORM[Platform APIs]

Page Types

The system supports multiple page types with distinct conventions:

TypeFilename PatternDescription
conceptconcept-<slug>.mdKnowledge concepts and definitions
guideguide-<slug>.mdProcedural how-to documentation
decisiondecision-YYYY-MM-DD-<slug>.mdDecision records with date prefix
journaljournal-YYYY-MM-DD-HHMM-<slug>.mdTime-stamped reflection entries
movemove-<slug>/SKILL.mdDirectory-based skill/move definitions
mapmap.mdFixed filename for wiki maps
profileprofile-<slug>.mdAgent profiles
synthesissynthesis-<slug>.mdCompiled summaries of related pages

Sources: src/core/ids.ts:25-35

Vault Structure

graph TD
    VAULT[Vault Root]
    WIKIS[wikis/]
    META[_meta/]
    INDEX[_index/]
    AGENTS[wikis/_agents/]
    
    VAULT --> WIKIS
    VAULT --> META
    VAULT --> INDEX
    VAULT --> AGENTS
    
    WIKIS --> WIKI1[alpha/]
    WIKIS --> WIKI2[beta/]
    
    WIKI1 --> TYPES1[concepts/ guides/ decisions/]
    WIKI2 --> TYPES2[concepts/ guides/ synthesis/]
    
    AGENTS --> PROFILES[profiles/]
    AGENTS --> MOVES[moves/]
    AGENTS --> JOURNALS[journal/]

Key Features

The vault.recall tool provides full-text search across the vault using an inverted token index. Search results are filtered by topic, wiki, layer (knowledge vs execution), and other metadata criteria.

The indexing process:

  1. Tokenizes page content using a Porter stemmer
  2. Filters stop words (the, and, of, a, an, in, to, is, etc.)
  3. Builds inverted index mapping tokens to page IDs
  4. Supports frontmatter fields in search scope
// Tokenization logic from index.ts
const UPSERT_STOP_WORDS = new Set([
  "the","and","of","a","an","in","to","is","for","on","with","as","at","by","or","be",
  "this","that","it","from","are","was","were","not","but","if"
]);

function upsertTokenize(text: string): string[] {
  return text.toLowerCase()
    .replace(/[^a-z0-9\s]/g, " ")
    .split(/\s+/)
    .filter(t => t.length > 1 && !UPSERT_STOP_WORDS.has(t))
    .map(t => upsertStemmer.stem(t));
}

Sources: src/core/index.ts:45-52

Synthesis Engine

The synthesis feature compiles related pages into structured summaries. The engine:

  1. Discovers input pages by topic recall or explicit agent memory
  2. Generates frontmatter with metadata, sources, and tags
  3. Creates marker-bounded content regions for idempotent re-rendering
  4. Supports per-agent memory synthesis scoped to wikis/_agents
graph LR
    INPUT[Input Pages] --> RECALL[Recall Query]
    RECALL --> FILTER[Filter by Topic/Wiki/Agent]
    FILTER --> BUILD[Build Synthesis Frontmatter]
    BUILD --> RENDER[Render Marker-Bounded Content]
    RENDER --> OUTPUT[Synthesis Page]

The synthesis frontmatter includes:

FieldDescription
idUnique synthesis identifier
titleSynthesis title with topic/agent context
typeAlways "synthesis"
wikiTarget wiki namespace
statusDraft status
sourcesArray of wikilinks to contributing pages
last_compiledISO date of last synthesis refresh

Sources: src/core/synthesize.ts:1-85

Stoa uses vault-root absolute wikilinks for cross-page references:

[[wikis/<wiki>/<type>/<id>(|alias)]]

The wikilink parser:

  • Extracts links from page bodies and frontmatter related: arrays
  • Skips links inside fenced code blocks
  • Silently skips malformed links
  • Preserves inline code spans
export interface WikilinkRef {
  raw: string;
  wiki: string;
  type: string;
  id: string;
  alias?: string;
  source: "body" | "frontmatter";
}

Sources: src/core/wikilinks.ts:1-30

Profile System

Agent profiles define identities within the system. Each profile:

  • Has a unique profile-<slug> ID
  • Stores species/pokemon data via frontmatter
  • Links to skills/moves in wikis/_agents/moves/
  • Can be registered with the Stadium platform
graph TD
    PROFILE[Profile Page] --> FRONTMATTER[Frontmatter]
    PROFILE --> BODY[Body Content]
    
    FRONTMATTER --> SPECIES[pokemon: species_name]
    FRONTMATTER --> TYPE[pokemon_type: type]
    FRONTMATTER --> SPECIALTY[dev_specialty: specialty]
    FRONTMATTER --> STAGE[evolution_stage: basic|stage1|stage2]
    
    PROFILE --> REGISTER[Profile Register Tool]
    REGISTER --> STADIUM[Stadium Platform API]

Sources: src/core/profiles.ts:1-30

PokeAPI Integration

The system integrates with the PokeAPI to enrich agent profiles with game-related data:

FunctionPurpose
fetchSpecies()Get legendary/mythical/baby flags and evolution data
fetchPokemonSpecies()Full species data with evolution chain
filterByEvolutionStage()Filter candidates by basic/stage1/stage2

Data is cached locally to minimize API calls.

Sources: src/core/pokeapi.ts:1-80

Page Operations

graph TD
    READ[readPage] --> PARSE[Parse Frontmatter + Body]
    READ --> VALIDATE[Validate Page Type]
    
    WRITE[writePage] --> PATH[Resolve Path for Type]
    WRITE --> MERGE[Merge Frontmatter]
    WRITE --> SERIALIZE[Serialize to Markdown]
    
    UPSERT[upsertPage] --> CONDITIONAL[Read then Write]
    UPSERT --> REINDEX[Update Token Index]

The WritePageInput interface:

export interface WritePageInput {
  id: string;
  type: NoteType;
  wiki: string;
  frontmatter: Record<string, any>;
  body: string;
  expectedUpdated?: string;  // Optimistic locking
}

Sources: src/core/pages.ts:40-70

Tools Reference

Read Tools

ToolDescription
vault.recallFull-text search across vault
vault.inbox-listList captured thoughts
vault.page-readRead specific page by ID
vault.channel-tailPull recent channel entries

Write Tools

ToolDescription
vault.inboxCapture fleeting thoughts
vault.newCreate typed page from template
vault.new-wikiScaffold new wiki space
vault.synthesizeCompile synthesis from matching pages
vault.set-activeSet ambient active wiki
vault.agent-journalAppend agent reflection
vault.reindexRegenerate index files

Coordination Tools

ToolDescription
vault.channel-postPost to coordination channel
vault.task-*Task lifecycle (create, list, claim, update)
vault.claims-*Claims management (submit, list, retract, reject)

Platform Tools

ToolDescription
vault.profile-registerRegister profile with Stadium platform
vault.sync-skillsDeploy agent moveset as local skills
vault.bootstrap-repoWire consuming repo with MCP config

Wait Primitives

ToolDescription
vault.wait-forBlock until matching event
vault.wait-for-anyWake on first match (race)
vault.wait-for-allWake when all filters matched
vault.wait-for-manyBounded batch over window

Sources: README.md:1-60

CLI Usage

# Set vault path environment variable
export STOA_VAULT_PATH=/path/to/vault

# Recall search
stoa recall "topic query"

# Capture a thought
stoa inbox "thought to capture"

# List wikis
stoa list-wikis

# With explicit vault path
stoa --vault=/path/to/vault recall "search terms"

Configuration

Wiki Parameter Resolution

The wiki: parameter resolves in this order:

  1. Explicit wiki: argument on tool call
  2. --default-wiki=<name> server flag
  3. .active-wiki file at vault root
  4. Error if none found

Server Configuration

stoa --vault=/path/to/vault \
     --default-wiki=alpha \
     --default-family=my-family

License

FSL-1 (Functional Source License)

Sources: src/core/ids.ts:25-35

Core Concepts and Vocabulary

Related topics: Introduction to Stoa, Agent Profiles and Evolution

Section Related Pages

Continue reading this section for the full explanation and source context.

Section Vault Root Organization

Continue reading this section for the full explanation and source context.

Section Wiki Hierarchy

Continue reading this section for the full explanation and source context.

Section Page Types

Continue reading this section for the full explanation and source context.

Related topics: Introduction to Stoa, Agent Profiles and Evolution

Core Concepts and Vocabulary

This document provides a comprehensive reference for the fundamental concepts, data structures, and terminology used throughout the stoa vault management system. Understanding these core concepts is essential for working effectively with the vault infrastructure, creating and managing pages, and integrating with the MCP (Model Context Protocol) server.

Vault Structure

The stoa system operates on a directory-based vault architecture where all content is organized within a hierarchical folder structure. The vault serves as the single source of truth for all wiki pages, profiles, agents, and metadata.

Vault Root Organization

<vault-path>/
├── wikis/
│   ├── <wiki-name>/
│   │   ├── index.md
│   │   ├── map.md
│   │   ├── <type>-<slug>.md
│   │   ├── profiles/
│   │   ├── synthesis/
│   │   └── ...
│   └── _agents/
│       ├── synthesis/
│       └── journal/
├── _index/
│   ├── pages.json
│   ├── tokens.json
│   ├── links.json
│   ├── wikis.json
│   └── profiles.json
├── .active-wiki
└── .active-family

Sources: src/core/index.ts:1-50

Wiki Hierarchy

Wikis are the primary organizational unit within the vault. Each wiki contains typed pages organized by content type. The system supports multiple wikis per vault, with each wiki potentially belonging to a family for grouped operations.

graph TD
    A[Vault Root] --> B[wikis/]
    B --> C[<wiki-name>]
    B --> D[_agents]
    B --> E[_meta]
    C --> F[concepts/]
    C --> G[decisions/]
    C --> H[guides/]
    C --> I[profiles/]
    C --> J[synthesis/]
    C --> K[inbox/]

Sources: src/core/family.ts:1-30

Page System

Page Types

Pages in stoa are distinguished by their type field in frontmatter. The type determines the file naming convention, storage location, and how the page is processed by various tools.

TypeFilename PatternDescription
conceptconcept-<slug>.mdKnowledge concepts and definitions
decisiondecision-YYYY-MM-DD-<slug>.mdDecision records with date prefix
guideguide-<slug>.mdProcedural guides
sourcesource-<slug>.mdExternal citations and references
ideaidea-<slug>.mdIdeas and brainstorms
questionquestion-<slug>.mdQuestions for investigation
profileprofile-<slug>.mdAgent or entity profiles
synthesissynthesis-<slug>.mdCompiled synthesis pages
journaljournal-YYYY-MM-DD-HHMM-<slug>.mdAgent journal entries
movemove-<slug>/SKILL.mdSkills/moves (directory layout)
mapmap.mdWiki map page (fixed filename)

Sources: src/core/ids.ts:1-50

Page Data Model

Every page in the vault has a consistent structure combining YAML frontmatter with markdown body content.

interface IndexedPage {
  id: string;           // Unique page identifier
  path: string;         // Relative path from vault root
  wiki: string;         // Wiki name containing this page
  type: NoteType;       // Page type discriminator
  title?: string;       // Human-readable title
  tags?: string[];      // Categorization tags
  status?: string;      // Lifecycle status (draft, published, etc.)
  created?: string;     // ISO 8601 creation date
  updated?: string;     // ISO 8601 last modified date
  summary?: string;     // Brief description
  layer?: string;       // knowledge | execution classification
  sources?: string[];   // Referenced wikilinks
}

Sources: src/core/pages.ts:1-40

The system uses a structured wikilink syntax for cross-referencing pages within the vault.

[[wikis/<wiki>/<type>/<id>(|alias)]]

Components:

  • wikis/ — Literal prefix indicating a vault-root absolute link
  • <wiki> — Target wiki name (e.g., alpha, _agents)
  • <type> — Page type folder (e.g., concept, decision, profile)
  • <id> — Page identifier
  • |alias — Optional display alias

Examples:

[[wikis/alpha/concept/trust-gradient-axes]]
[[wikis/_agents/profile/pikachu-agent|My Agent]]

Sources: src/core/wikilinks.ts:1-60

Indexing System

The index system uses a custom tokenization pipeline for full-text search across all vault pages.

function upsertTokenize(text: string): string[] {
  return text.toLowerCase()
    .replace(/[^a-z0-9\s]/g, " ")
    .split(/\s+/)
    .filter(t => t.length > 1 && !UPSERT_STOP_WORDS.has(t))
    .map(t => upsertStemmer.stem(t));  // Porter stemming
}

Tokenization Pipeline:

  1. Lowercase normalization
  2. Strip non-alphanumeric characters
  3. Split on whitespace
  4. Filter stop words and single characters
  5. Apply Porter stemmer

Stop Words: the, and, of, a, an, in, to, is, for, on, with, as, at, by, or, be, this, that, it, from, are, was, were, not, but, if

Sources: src/core/index.ts:40-55

Index Files

The _index/ directory contains aggregated metadata for efficient querying:

FilePurpose
pages.jsonFull page metadata with frontmatter
tokens.jsonSearch tokens mapped to page IDs
links.jsonInter-page references (wikilinks)
wikis.jsonWiki registry and metadata
profiles.jsonProfile-specific metadata

Sources: src/core/index.ts:55-70

Layer Classification

Pages are classified into layers for filtering and organization:

const KNOWLEDGE_TYPES = ["concept", "decision", "spec", "source"];
const EXECUTION_TYPES = ["guide", "idea", "question"];
  • Knowledge Layer — Factual, distillable content (concepts, decisions, specs, sources)
  • Execution Layer — Procedural and exploratory content (guides, ideas, questions)

Sources: src/core/index.ts:20-30

Synthesis System

Synthesis Pages

Synthesis pages compile multiple related pages into a cohesive document. They serve as "second-order" content that distills information from source pages.

interface SynthesisPage extends IndexedPage {
  type: "synthesis";
  topic: string;           // Synthesis subject
  sources: string[];       // Wikilinks to contributing pages
  last_compiled: string;   // ISO date of last regeneration
  scope?: "memory";        // Agent memory synthesis flag
  by_agent?: string;       // Agent identifier for memory syntheses
}

Sources: src/core/synthesize.ts:1-50

Synthesis Generation

graph TD
    A[Synthesize Request] --> B{scope?}
    B -->|memory| C[Collect Agent Pages]
    B -->|topic| D[Recall Matching Pages]
    C --> E[Filter by Author]
    D --> E
    E --> F[Build Frontmatter]
    F --> G[Generate Synthesis File]
    G --> H[Upsert to Index]

Sources: src/core/synthesize.ts:50-120

Synthesis Debt Detection

The lint system identifies clusters of related hard-knowledge pages that lack corresponding synthesis pages.

Debt Rule:

  • Clusters pages by shared tags
  • Flags clusters with ≥3 pages that have no synthesis
  • Suggests vault.synthesize command with derived title

Hard-Knowledge Types: concept, decision, spec

Sources: src/core/lint-checks/synthesis-debt.ts:1-50

Synthesis Staleness

The system tracks how long synthesis pages have gone without recompilation:

interface SynthesisStaleness {
  id: string;
  lag_days: number | null;  // null if never compiled
  last_compiled: string | null;
  topic: string;
}

Sources: src/core/syntheses.ts:1-50

Wiki Resolution

Family Resolution Order

When determining the active family context, the system follows this precedence:

  1. familyArg — Explicit parameter passed to the function
  2. defaultFamily — Configured default (via --default-family CLI arg)
  3. .active-family file at vault root
  4. null — Falls through to single-wiki resolution

Sources: src/core/family.ts:15-40

Wiki Resolution Order

Wiki parameter resolution follows this order:

  1. Explicit wiki: argument on tool call
  2. --default-wiki=<name> flag on server invocation
  3. .active-wiki file at vault root
  4. Error if no wiki can be determined

Sources: README.md:1-30

Family-Wiki Validation

When both familyArg and wikiArg are provided with knownWikis, the system validates consistency:

if (knownWikis[wikiArg].family !== familyArg) {
  throw new FamilyMismatchError(...);
}

Sources: src/core/family.ts:25-35

Event Bus and State Cache

StateCache

The StateCache provides an in-memory cache for tracking page states across event sources.

class StateCache {
  private states = new Map<string, unknown>();
  
  private key(source: string, wiki: string, id: string): string {
    return `${source}:${wiki}:${id}`;
  }
  
  get<T>(source: string, wiki: string, id: string): T | undefined;
  set<T>(source: string, wiki: string, id: string, state: T): void;
  has(source: string, wiki: string, id: string): boolean;
  size(): number;
}

Key Format: source:wiki:id

Sources: src/core/eventbus/state-cache.ts:1-35

Cache Initialization

The system pre-warms the cache by walking the wikis/ directory before accepting live events, ensuring the first change event has valid prior state for diffing.

function walkInitablePaths(vaultPath: string): string[] {
  // Recursively find all .md files in wikis/
  // Returns paths where 'init' is defined for state tracking
}

Sources: src/transport/stdio.ts:40-70

Frontmatter Schema

Standard Frontmatter Fields

FieldTypeDescription
idstringUnique page identifier
titlestringDisplay title
typestringPage type
wikistringWiki name
statusstringLifecycle status
createdstringISO 8601 creation date
updatedstringISO 8601 modification date
summarystringBrief description
tagsstring[]Categorization tags
layerstringknowledge or execution
sourcesstring[]Wikilinks to source pages

Profile-Specific Fields

FieldTypeDescription
pokemonstringSpecies name
evolution_stagestringbasic, stage1, stage2
pokemon_typestringPokémon type (if applicable)
dev_specialtystringDeveloper specialty
raritystringcommon, baby, legendary, mythical
platform_profile_idstringStadium platform ID
platform_statsobjectPersisted platform statistics

Sources: src/transport/ui/routes-write.ts:1-40

Marker Rendering

The marker system enables tools to maintain managed regions within human-editable files.

Marker Contract

  • Start Marker: <!-- <name>:start (rendered: <date>, half-life: <N>d) -->
  • End Marker: <!-- <name>:end -->
  • Idempotent: Same inputs produce byte-identical output
  • Independent: Different marker names don't interfere
interface MarkerOptions {
  renderedDate?: string;   // ISO date YYYY-MM-DD
  halfLifeDays?: number;   // Half-life in days
}

Applications:

  • synthesize — Claims rollup
  • sync-skills — Moveset synchronization
  • bootstrap-repo — Agent documentation patches

Sources: src/core/marker-render.ts:1-50

Tool Categories

Read Primitives

ToolPurpose
vault.recallFull-text search with filters
vault.page-readRead single page by ID
vault.channel-tailPull coordination channel entries
vault.synthesis-stalenessCheck synthesis freshness

Wait (Push Primitives)

ToolPurpose
vault.wait-forBlock until matching event
vault.wait-for-anyWake on first match across N filters
vault.wait-for-allWake when all N filters matched
vault.wait-for-manyBounded batch over window

Write — Content

ToolPurpose
vault.inboxCapture fleeting thoughts
vault.newCreate typed page from template
vault.new-wikiScaffold new wiki structure
vault.set-activeSet ambient active wiki
vault.synthesizeCompile synthesis page
vault.agent-journalAppend agent reflection

Write — System

ToolPurpose
vault.reindexRegenerate _index/ files

Coordination

ToolPurpose
vault.channel-postPost to coordination channel
vault.task-claimAtomically claim pending task
vault.task-createCreate new task
vault.task-listList tasks
vault.task-updateUpdate task state
vault.bootstrap-repoWire consuming repo with MCP config
vault.sync-skillsDeploy agent moveset as local skills

Sources: README.md:30-80

Configuration Files

Server Configuration

Config MethodDescription
--vault=<path>Vault root directory
--default-wiki=<name>Default wiki for operations
--default-family=<name>Default family for multi-wiki ops
STOA_VAULT_PATHEnvironment variable alternative

Active Context Files

FilePurpose
.active-wikiCurrent active wiki name
.active-familyCurrent active family name

Common Patterns

Idempotent Page Creation

Pages use upsert semantics — creating a page with an existing ID updates rather than duplicates:

function writePage(vaultPath: string, input: WritePageInput): WritePageResult {
  const path = pathForPage(vaultPath, input.id, input.type, input.wiki);
  // Uses expectedUpdated for optimistic concurrency control
}

Sources: src/core/pages.ts:40-80

Three-Step Alias Resolution

Profile and agent references resolve through multiple resolution steps:

function normalizeProfileId(vaultPath: string, raw: string): string {
  const r1 = resolveCurrent(vaultPath, raw);
  if (r1 !== raw) return r1;
  const candidate = raw.startsWith("profile-") ? raw : `profile-${raw}`;
  return resolveCurrent(vaultPath, candidate);
}

Sources: src/tools/sync-agents.ts:1-40

Lint Rule Pattern

New lint rules follow a consistent structure:

export const LINT_RULE = {
  ruleId: "rule-name",
  severity: "warn" | "error",
  message: "Human-readable description",
};

Sources: src/core/lint-checks/claim-with-no-scope.ts:1-30

Terminology Summary

TermDefinition
VaultRoot directory containing all wikis and metadata
WikiOrganizational unit containing typed pages
FamilyGrouping of related wikis
PageSingle markdown document with frontmatter
TypePage classification determining storage and behavior
WikilinkCross-reference syntax [[wikis/...]]
SynthesisCompiled page aggregating multiple sources
LayerKnowledge vs execution classification
MarkerHTML comment bounding managed file regions
IndexAggregated metadata for efficient querying
DebtGap between source pages and their synthesis

Sources: src/core/index.ts:1-50

Architecture and Design

Related topics: Introduction to Stoa, Event Bus System

Section Related Pages

Continue reading this section for the full explanation and source context.

Related topics: Introduction to Stoa, Event Bus System

Architecture and Design

Overview

Stoa is an MCP (Model Context Protocol) server that provides structured access to a flat-file knowledge vault. It bridges AI agents with a wiki-based knowledge management system, enabling semantic search, cross-wiki linking, task management, and skill deployment. The architecture follows a layered design with clear separation between transport mechanisms, core domain logic, and tool interfaces.

Purpose and Scope:

  • Expose vault operations via MCP protocol (STDIO or HTTP transports)
  • Maintain a search index for full-text and structured queries
  • Enforce wiki semantics and validation rules through lint checks
  • Support multi-wiki, multi-family deployments
  • Enable skill/move deployment workflows

Sources: src/core/index.ts:1-50

Source: https://github.com/BrettNye/stoa / Human Manual

Event Bus System

Related topics: Architecture and Design, Channels and Claims

Section Related Pages

Continue reading this section for the full explanation and source context.

Section Key Methods

Continue reading this section for the full explanation and source context.

Section Key Generation

Continue reading this section for the full explanation and source context.

Section Initialization

Continue reading this section for the full explanation and source context.

Related topics: Architecture and Design, Channels and Claims

Event Bus System

The Event Bus System is a core infrastructure component in Stoa that provides reactive, push-based primitives for monitoring vault changes. Rather than polling for updates, the system leverages the local filesystem watch event bus to notify consumers when specific conditions are met.

Overview

The Event Bus System enables agents and tools to:

  • React to vault mutations — page creation, updates, deletions trigger events
  • Track state transitions — monitor changes to structured data like task status
  • Block until conditions — wait-for primitives that return only when matching events occur
  • Warm state on startup — preload current state before processing live events

Sources: src/transport/stdio.ts:27-28

Architecture

The system consists of several interconnected layers:

LayerPurpose
WatcherFilesystem observer that monitors vault paths
State CacheIn-memory Map storing prior state for diffing
MatchersRule-based filters that determine when to emit events
BusEvent dispatcher connecting watchers to consumers
Wait-For ToolsBlocking API surface for callers
graph TD
    A[Vault Filesystem] -->|FS Events| B[Watcher]
    B --> C[State Cache]
    C -->|Prior State| D[Matchers]
    D -->|Emit Decision| E[Event Bus]
    E -->|VaultEvent| F[Wait-For API]
    E -->|VaultEvent| G[Other Consumers]
    
    H[Startup] -->|walkInitablePaths| I[Warm Cache]
    I --> C

Sources: src/core/eventbus/state-cache.ts:1-15

State Cache

The StateCache class provides an in-memory snapshot of vault entity states, enabling diff-based change detection.

Key Methods

MethodSignaturePurpose
get`<T>(source, wiki, id) => T \undefined`Retrieve cached state
set<T>(source, wiki, id, state) => voidStore state snapshot
has(source, wiki, id) => booleanCheck if state exists
size() => numberReturn cache entry count

Key Generation

States are stored using a composite key format:

${source}:${wiki}:${id}

This allows isolated state tracking per source (e.g., file-watcher, mcp-tool) across wikis and page IDs.

Sources: src/core/eventbus/state-cache.ts:3-22

Initialization

Before accepting live events, the system warms the cache by walking existing files:

function walkInitablePaths(vaultPath: string): string[] {
  const root = join(vaultPath, "wikis");
  if (!existsSync(root)) return [];
  // ...
}

This ensures the first change event has valid prior state for diffing.

Sources: src/transport/stdio.ts:30-46

Matchers

Matchers are pure functions that determine whether a filesystem change should emit a VaultEvent. Each matcher handles a specific entity type.

Task Matcher

The task matcher tracks frontmatter changes on task pages and emits enrichment data for status/owner transitions.

#### State Picking

function pickTaskState(frontmatter): Partial<VaultEvent> {
  return {
    status: frontmatter.status,
    owner: frontmatter.owner
  };
}

#### Change Detection Logic

ConditionAction
Status changedAdd task_status_change: { from, to }
Owner changedAdd task_owner_change: { from, to }
Both unchangedEmit false (no event)
Both changedEmit true with both enrichments
nextState(parsed) { return pickTaskState(parsed.frontmatter); },
init(_path, parsed) { return pickTaskState(parsed.frontmatter); },

Sources: src/core/eventbus/matchers/task.ts:1-26

Matcher Contract

Matchers implement three functions:

  1. compare(prev, cur) — Returns { emit: boolean, enrichment?: Partial<VaultEvent> }
  2. nextState(parsed) — Extracts state fields from current file parse
  3. init(path, parsed) — Provides initial state for a newly discovered entity

VaultEvent Structure

Events emitted by the bus carry structured change data:

interface VaultEvent {
  type: string;           // Event type identifier
  wiki: string;           // Wiki containing the entity
  id: string;             // Entity identifier
  source: string;         // Origin of the event
  timestamp?: string;     // ISO timestamp
  task_status_change?: { from: string; to: string };
  task_owner_change?: { from: string; to: string };
}

Wait-For Primitives

The Event Bus exposes blocking wait operations for callers that need to pause until conditions are met.

Available Primitives

ToolBehavior
vault.wait-forBlock until one matching event lands; cursor-based catch-up
vault.wait-for-anyWake on first match across N filters (race semantics)
vault.wait-for-allWake when all N filters have matched at least once
vault.wait-for-manyBounded batch over a window

These primitives connect to the filesystem watch event bus, providing push semantics instead of polling.

Sources: README.md:18-25

Event Flow

sequenceDiagram
    participant FS as Filesystem
    participant Watcher
    participant Cache as State Cache
    participant Matcher
    participant Bus
    participant Consumer

    Note over FS,Consumer: Startup Phase
    Consumer->>Watcher: walkInitablePaths()
    Watcher->>FS: Read existing files
    Watcher->>Cache: set() prior states
    Cache-->>Consumer: Cache warmed

    Note over FS,Consumer: Live Event Phase
    FS->>Watcher: File change detected
    Watcher->>Cache: get() prior state
    Cache-->>Watcher: Return previous state
    Watcher->>Matcher: compare(prev, cur)
    Matcher-->>Watcher: { emit, enrichment }
    Watcher->>Bus: Emit VaultEvent
    Bus->>Consumer: Deliver event

Integration Points

With Index System

The event bus integrates with the vault index for querying wikis and pages:

export function queryWikis(idx: VaultIndex): IndexedWiki[] {
  return idx.wikis;
}

Sources: src/core/index.ts:1-10

With Lint System

Lint checks consume event data for validation:

lintAliasDrift(vaultPath, input.wiki, diagnostics);
lintAgentsWiki(vaultPath, diagnostics);

Sources: src/core/lint.ts:1-30

Configuration

The system relies on vault path resolution:

SourcePriorityEnvironment Variable
CLI --vault1 (highest)
STOA_VAULT_PATH2STOA_VAULT_PATH
Default3 (lowest)

Active wiki/family files (.active-wiki, .active-family) influence which entities are monitored.

Error Handling

ScenarioBehavior
Unknown wikiEvents filtered out; no error
File read failureError propagates to watcher
State not in cacheTreat as new entity; use init
Matcher throwsError propagates; event not emitted

Sources: src/transport/stdio.ts:27-28

Agent Profiles and Evolution

Related topics: Core Concepts and Vocabulary, Runtime Adapters

Section Related Pages

Continue reading this section for the full explanation and source context.

Section Profile File Location

Continue reading this section for the full explanation and source context.

Section Frontmatter Schema

Continue reading this section for the full explanation and source context.

Section Registration Tool Interface

Continue reading this section for the full explanation and source context.

Related topics: Core Concepts and Vocabulary, Runtime Adapters

Agent Profiles and Evolution

Overview

Agent Profiles and Evolution is a gamified system within the Stoa vault that models agent identities as collectible profiles with species characteristics, evolution stages, and platform-tracked statistics. The system combines the vault's knowledge management with external platform integration via the Stadium API to create persistent, trackable agent personas.

Sources: src/tools/profile-register.ts:1-28

Architecture

graph TD
    subgraph "Vault Layer"
        P[Profile Page]
        FM[Frontmatter]
        W[Wikis Directory]
    end
    
    subgraph "Profile Creation"
        RT[routes-write.ts]
        NT[newTool.handler]
        RR[Rarity Roll]
        SR[Shiny Roll]
    end
    
    subgraph "Platform Integration"
        SC[StadiumClient]
        PA[/profiles/register API]
        PR[Platform Response]
    end
    
    subgraph "Evolution System"
        EC[evolution-claims.ts]
        EV[Evolution Logic]
    end
    
    P --> FM
    FM --> RT
    W --> P
    RT --> NT
    NT --> RR
    RR --> SR
    SR --> SC
    SC --> PA
    PA --> PR
    PR --> EC
    EC --> EV
    
    style P fill:#e1f5fe
    style SC fill:#fff3e0
    style EV fill:#e8f5e9

Profile Structure

Profile File Location

Profiles are stored as markdown files in the vault with a standardized path structure:

wikis/<wiki>/profiles/<profile_id>.md

Sources: src/tools/profile-register.ts:17-18

Frontmatter Schema

Profile pages use a specialized frontmatter schema with the following key fields:

FieldTypeRequiredDescription
idstringYesMust match ^profile- pattern
titlestringYesDisplay name for the profile
typeenumYesAlways "profile"
wikistringYesParent wiki namespace
statusenumNoPage status (draft/active/accepted/superseded/archived)
pokemonstringNoSpecies name (v1.5 convention)
species_namestringNoLegacy species name (fallback)
evolution_stagestringNoStage identifier (defaults to "basic")
pokemon_typestringNoType classification
dev_specialtystringNoDeveloper specialty marker
platform_profile_idstringNoStadium platform ID (set post-registration)
platform_statsobjectNoStats returned from platform

Sources: src/core/frontmatter.ts:23-45, src/transport/ui/routes-write.ts:88-92

Profile Registration Flow

The registration process synchronizes vault profiles with the external Stadium platform:

sequenceDiagram
    participant Agent
    participant MCP as MCP Server
    participant Vault as Vault FS
    participant Stadium as Stadium API
    
    Agent->>MCP: profile-register(profile_id)
    MCP->>Vault: Read wikis/<wiki>/profiles/<id>.md
    Vault->>MCP: Raw file content
    MCP->>MCP: parseFrontmatter()
    Note over MCP: Extract pokemon/species_name
    MCP->>Stadium: POST /profiles/register
    Stadium->>MCP: { profile_id, stats }
    MCP->>Vault: upsertPage() with platform fields
    MCP->>Agent: Registration result

Sources: src/tools/profile-register.ts:13-27

Registration Tool Interface

const Input = z.object({
  profile_id: z.string().regex(/^profile-/),
  wiki: z.string().optional()
});

Sources: src/tools/profile-register.ts:20-24

Profile Creation (UI Routes)

The routes-write.ts module handles interactive profile creation with gamification elements:

Sources: src/transport/ui/routes-write.ts:78-130

Creation Parameters

ParameterSourceDescription
wikiComputedDefaults to "_agents"
titleComputedFormat: <species> agent
frontmatterExtras.pokemonselected_speciesSpecies name
frontmatterExtras.evolution_stageevolution_stageDefaults to "basic"
frontmatterExtras.pokemon_typepokemon_typeOptional type
frontmatterExtras.dev_specialtydev_specialtyOptional specialty

Sources: src/transport/ui/routes-write.ts:80-93

Gamification: Rarity and Shiny Rolls

During profile creation, two gamification rolls occur before the file is written:

let rarity: "common" | "baby" | "legendary" | "mythical" = "common";
let isShiny = false;

Sources: src/transport/ui/routes-write.ts:96-97

RollProbabilityEffect
RarityVaries by speciesDetermines rarity tier
Shiny1/64 (1.5625%)Applies shiny modifier to profile

The shiny roll fetches species data and persists the result into the profile frontmatter.

Sources: src/transport/ui/routes-write.ts:98-112

Evolution System

The evolution system is implemented across multiple modules that track and process evolution-related claims:

Evolution Claims

The evolution-claims.ts module handles the logic for processing evolution-related claims within the claims store. This system validates evolution conditions and updates profile states accordingly.

Sources: src/core/evolution-claims.ts

Evolution Workflow

graph LR
    A[Profile Created] --> B{Claims Accumulated}
    B -->|Sufficient| C[Evolution Trigger]
    B -->|Insufficient| D[Wait State]
    C --> E[Evolution Validation]
    E -->|Valid| F[Profile Updated]
    E -->|Invalid| G[Reject Evolution]
    F --> H[Platform Sync]
    D --> B

Profile Identification

Supported ID Patterns

The system supports multiple profile identification patterns:

PatternDescriptionExample
Standardprofile-<slug>.mdprofile-fire-type.md
Agent-specificprofile-<agent-name>profile-claude

Sources: src/core/pages.ts:45-60

Path Resolution

The pathForPage function resolves profile paths:

// Profile pages use the profiles subdirectory
const path = join(vaultPath, "wikis", wiki, "profiles", `${id}.md`);

Sources: src/core/pages.ts:47-49

Stadium Platform Integration

Client Architecture

The StadiumClient handles all communication with the Stadium platform:

  • Base URL: Configured via resolveStadiumConfig()
  • Endpoint: /profiles/register for profile creation
  • Error Handling: Propagates StadiumApiError with error_code

Sources: src/tools/profile-register.ts:23

Platform Data Flow

┌─────────────────┐     ┌──────────────────┐     ┌─────────────────┐
│   Vault File    │────▶│  profile-register │────▶│ Stadium API     │
│   (pokemon: X)  │     │  Tool            │     │ POST /register  │
└─────────────────┘     └──────────────────┘     └─────────────────┘
                              │                        │
                              ▼                        ▼
                        ┌──────────────────┐     ┌─────────────────┐
                        │ upsertPage()     │◀────│ profile_id      │
                        │ (persist result) │     │ stats           │
                        └──────────────────┘     └─────────────────┘

Error Propagation

Server errors from the Stadium platform are propagated as StadiumApiError. Common error codes include:

  • pokeapi_unknown_species: Species not found in platform
  • Network failures: Caught and returned with descriptive messages

Sources: src/transport/ui/routes-write.ts:100-105

Configuration and Defaults

SettingDefaultSource
Default Wiki_agentsroutes-write.ts:78
Evolution Stage"basic"routes-write.ts:90
Shiny Probability1/64routes-write.ts:98-112
Platform Endpoint/profiles/registerprofile-register.ts:23
ToolPurpose
vault.profile-registerRegister profile with Stadium platform
vault.evolve-profileTrigger evolution on eligible profiles
vault.refresh-profile-memoryUpdate agent memory in profile

Sources: src/tools/profile-register.ts:31-35

Future Considerations

The current implementation (v1.5 convention) stores species information in the pokemon frontmatter field. The system maintains backward compatibility with the legacy species_name field for existing profiles. Future versions may expand the evolution system to include:

  • Multi-stage evolution chains
  • Evolution condition validation via claims
  • Platform-synced evolution history

Source: https://github.com/BrettNye/stoa / Human Manual

Task Workflow System

Related topics: Channels and Claims, Core Concepts and Vocabulary

Section Related Pages

Continue reading this section for the full explanation and source context.

Section Task Interface

Continue reading this section for the full explanation and source context.

Section Task Status Values

Continue reading this section for the full explanation and source context.

Section Phase 1: Fast Path — Index Lookup

Continue reading this section for the full explanation and source context.

Related topics: Channels and Claims, Core Concepts and Vocabulary

Task Workflow System

The Task Workflow System in Stoa provides a structured mechanism for creating, tracking, claiming, and resolving tasks across the vault's wiki-based knowledge management environment. It integrates with the broader event bus system for real-time state change notifications and supports atomic task claiming to prevent race conditions in multi-agent scenarios.

Overview

The Task Workflow System serves as the coordination backbone for agent-driven work items within Stoa. It enables:

  • Task lifecycle management — creation, status tracking, and resolution
  • Atomic claiming — race-safe task assignment to agents
  • Two-phase task lookup — fast index-based retrieval with disk-scan fallback
  • Event-driven state propagation — real-time task status change notifications via the event bus
  • Optimistic Concurrency Control (OCC) — safe concurrent updates using expected_updated timestamps

Sources: README.md:1-20

Core Data Model

Task Interface

interface Task {
  id: string;
  title: string;
  type: string;
  wiki: string;
  status: string;
  claimed_by?: string;
  claimed_at?: string;
  updated: string;
  body: string;
}

The Task interface represents a work item stored as markdown frontmatter within wikis/<wiki>/tasks/ directories. Key fields include:

FieldTypeDescription
idstringUnique task identifier (kebab-case per HARD_KNOWLEDGE_TYPES)
wikistringThe wiki namespace containing this task
statusstringCurrent task status (e.g., pending, claimed, merged)
claimed_bystring?Agent ID that claimed this task
claimed_atstring?ISO timestamp when the task was claimed
updatedstringISO date of last modification (used for OCC)

Sources: src/core/tasks.ts:1-30

Task Status Values

The system uses a defined set of task statuses aligned with the PageStatus enum:

StatusDescription
draftInitial state; task created but not ready for work
activeTask is open and in progress
claimedTask has been assigned to an agent
mergedTask work has been merged/completed
archivedTask is no longer active

Sources: src/core/frontmatter.ts:1-20

Task Lookup Architecture

Stox implements a two-phase task lookup strategy to balance performance with data freshness.

graph TD
    A[Find Task by ID] --> B{Fast Path:<br/>Index Lookup}
    B -->|Found| C[Return Task]
    B -->|Not Found| D{Slow Path:<br/>Disk Scan}
    D -->|Found| C
    D -->|Not Found| E[Return null]

Phase 1: Fast Path — Index Lookup

The system first queries _index/pages.json for the task:

const idx = loadIndex(vaultPath);
const hit = idx.pages.find(p => p.id === taskId && p.type === "task");
if (hit) {
  return {
    wiki: hit.wiki,
    updated: hit.updated,
    status: String((hit as any).status ?? "")
  };
}

This path executes in O(n) on the index array and succeeds for all tasks that have been indexed since their last modification. Sources: src/tools/merge-record.ts:1-40

Phase 2: Slow Path — Disk Scan Fallback

When the index lacks the task (e.g., tasks authored directly on disk between reindexes), the system falls back to a disk scan:

return findTaskOnDisk(vaultPath, taskId);

The findTaskOnDisk function delegates to the generalized findOnDisk utility in core/disk-fallback.ts, restricting results to type: "task". This handles the case surfaced in Phase-3 Wave 5 T5-3 where tasks created via direct file authoring were immediately followed by a vault.merge-record call that silently missed the transition. Sources: src/core/tasks.ts:1-30

Task Claiming

The atomic task claiming mechanism prevents race conditions when multiple agents attempt to claim the same task simultaneously.

Claim Operation

export class AlreadyClaimedError extends Error {
  code = "AlreadyClaimed" as const;
}

When an agent attempts to claim a task:

  1. The system loads the current task state
  2. If claimed_by is already set, the race-loser receives AlreadyClaimedError
  3. If unclaimed, the task is atomically updated with claimed_by and claimed_at

The vault.task-claim tool implements this atomicity, ensuring only one agent successfully claims a task. Sources: src/tools/task-claim.ts

Task Transition Rules

The merge-record.ts module defines conditional task transition logic:

function computeTaskTransition(status: string, task_id: string): TaskTransition | null {
  // Returns a transition only when status === "merged" AND task_id is non-empty
  // For all halt/fail statuses it returns null and the task is NOT touched
}

This rule ensures that:

  • Only merged status triggers a task transition
  • The journal is written regardless of whether the task update succeeds
  • Failure/halt journals are safe to re-run without side effects

Sources: src/tools/merge-record.ts:1-60

Event Bus Integration

The task system integrates with Stoa's event bus for real-time state change notifications.

Task State Matcher

export const taskStateMatcher: VaultStateMatcher = {
  key: "task",
  extractKey(parsed) { return String(parsed.frontmatter.id); },
  diff(prev, cur) {
    if (cur.status === prev.status && cur.owner === prev.owner) {
      return { emit: false };
    }
    const enrichment: Partial<VaultEvent> = {};
    if (cur.status !== prev.status) {
      enrichment.task_status_change = { from: prev.status, to: cur.status };
    }
    if (cur.owner !== prev.owner) {
      enrichment.task_owner_change = { from: prev.owner, to: cur.owner };
    }
    return { emit: true, enrichment };
  },
  nextState(parsed) { return pickTaskState(parsed.frontmatter); },
  init(_path, parsed) { return pickTaskState(parsed.frontmatter); },
};

The matcher extracts task state from frontmatter and emits events for:

  • task_status_change — when status transitions (e.g., activemerged)
  • task_owner_change — when claimed_by changes

Sources: src/core/eventbus/matchers/task.ts:1-30

State Extraction

function pickTaskState(fm: Record<string, any>): TaskState {
  return {
    status: String(fm.status ?? "draft"),
    owner: fm.claimed_by ? String(fm.claimed_by) : undefined,
  };
}

Task Operations

Available Task Tools

ToolDescription
vault.task-createCreate a new task in a specified wiki
vault.task-listList tasks with optional filters
vault.task-updateUpdate task fields (status, claimed_by, etc.)
vault.task-claimAtomically claim a pending task

Sources: README.md:1-20

Optimistic Concurrency Control

Task updates use OCC to prevent lost updates:

interface ReleaseInput {
  task_id: string;
  expected_updated: string;  // Timestamp from last known state
  wiki: string;
  reason?: string;
}

The system compares the provided expected_updated against the current updated value. If they match, the update proceeds; otherwise, it fails to prevent concurrent modification.

Journal Integration

When merge-record.ts processes a merged pull request:

  1. Writes journal file at wikis/_agents/journal/<journal_id>.md
  2. Idempotent rewrite — same now + same input produces same journal_id
  3. Conditional task transition via computeTaskTransition
  4. task_updated: false when task_id not found, but journal still written

Sources: src/tools/merge-record.ts:1-30

Configuration

Task behavior is configurable via _meta/lint-config.yaml:

KeyDefaultDescription
task.min_cluster_size3Minimum cluster size for synthesis debt detection

Note: Task-specific configuration loading is pending (resolution-lifecycle spec implementation).

  • Synthesis Debt — Tasks may be created to address synthesis clusters
  • Claim Clustering — Related claim grouping logic
  • Task Readiness — Determines when tasks are ready for claiming
  • Event Bus — Real-time task event propagation

Sources: README.md:1-20

Channels and Claims

Related topics: Task Workflow System, Event Bus System

Section Related Pages

Continue reading this section for the full explanation and source context.

Section Purpose

Continue reading this section for the full explanation and source context.

Section Architecture

Continue reading this section for the full explanation and source context.

Section Channel Naming Convention

Continue reading this section for the full explanation and source context.

Related topics: Task Workflow System, Event Bus System

Channels and Claims

Overview

Channels and Claims are two interconnected systems within the Stoa vault that enable coordination and knowledge assertion respectively. Channels provide a pub/sub mechanism for cross-instance communication, while Claims represent structured knowledge assertions with built-in confidence scoring and temporal decay.

Channels

Purpose

Channels enable coordination and communication across multiple vault instances or agents. They function as a lightweight messaging layer backed by journal pages stored directly in the vault filesystem.

Architecture

Channels are implemented as journal pages with a channel field in their frontmatter. The system leverages the existing page indexing infrastructure rather than maintaining a separate message broker.

graph TD
    A[Agent/Instance] -->|vault.channel-post| B[Journal Page]
    B -->|type: journal<br/>channel: &lt;name&gt;<br/>wiki: &lt;wiki&gt;| C[Vault FS]
    D[Other Agent] -->|vault.channel-tail| E[Channel Summary]
    C -->|queryPages idx| E

Channel Naming Convention

Channels must follow a strict naming format validated at lint time:

FormatRegexExample
Lowercase kebab-case^[a-z0-9]+(-[a-z0-9]+)*$pr-reviews, task-alerts

Validation is enforced in src/core/lint.ts:

if (p.channel && !/^[a-z0-9]+(-[a-z0-9]+)*$/.test(p.channel)) {
  diagnostics.push({
    severity: "warning", code: "BAD_CHANNEL_FORMAT",
    page_id: p.id, wiki: p.wiki,
    message: `channel "${p.channel}" must be lowercase kebab-case`
  });
}

Sources: src/core/lint.ts:47-54

Channel Summaries

The channel-tail tool aggregates journal entries into summaries containing:

FieldTypeDescription
namestringChannel identifier
wikistringWiki where the channel exists
lastEntryobjectMost recent entry with id, author, ts, excerpt
count24hnumberEntries within the last 24 hours

Each summary caches author information and body excerpts (first 240 characters) from the actual page files to avoid repeated filesystem reads during aggregation.

Sources: src/core/channel.ts:21-35

Resolving Channel Scope

Channel queries are scoped by wiki. When listing or tailing channels, the system filters pages by:

  1. type === "journal" — Only journal pages participate in channels
  2. wiki === opts.wiki — Wiki isolation is enforced
const pages = queryPages(idx, { type: "journal", wiki: opts.wiki });

Sources: src/core/channel.ts:18

Claims

Purpose

Claims represent structured knowledge assertions within the vault. They carry explicit confidence levels that decay over time, creating a system where knowledge freshness is tracked and surfaced.

Data Model

Claims are stored as pages with the following frontmatter structure:

id: <slug>
type: claim
title: <human-readable title>
authored_by: <agent-or-human>
confidence: high | medium | low
status: pending | accepted | retracted | rejected
evidence:
  - [[wikis/<wiki>/<type>/<id>]]
last_validated: YYYY-MM-DD
created: YYYY-MM-DD
updated: YYYY-MM-DD

Confidence Decay

Claims use a temporal decay model to represent knowledge staleness. The effective confidence decreases based on:

  1. Initial confidence levelhigh, medium, or low
  2. Time since last validation — Measured in days
  3. Configured half-life — Number of days for confidence to halve

The effective confidence formula is documented in claim-render.ts:

const eff = effectiveConfidence(
  {
    confidence: claim.confidence,
    last_validated: claim.last_validated,
    status: claim.status,
  },
  today,
  {
    half_life_days: config.half_life_days,
    effective_floor: config.effective_floor,
  }
);

Sources: src/core/claim-render.ts:45-57

Effective Confidence Computation

The decay system applies a floor to prevent confidence from reaching zero:

ParameterDefaultPurpose
half_life_daysConfigurableDays for confidence to halve
effective_floorConfigurableMinimum confidence value

Rendering Claims

Claims are rendered as bullet points in synthesis pages:

- **`<key>`** — <body>. *(confidence <eff> as of <render-date>, validated <last_validated>, evidence: [[<first>]])*

The system:

  • Rounds effective confidence to 2 decimal places
  • Uses claim.summary if available, falling back to claim.body
  • Includes only the first evidence entry
  • Collapses internal whitespace to maintain single-line bullets
// Collapse all internal whitespace runs (incl. embedded newlines) to a
// single space. `.trim()` alone leaves embedded `\n` intact, which would
// break the single-line bullet and corrupt the vault-claims:start..end

Sources: src/core/claim-render.ts:64-70

Claim Lifecycle

stateDiagram-v2
  [*] --> pending: Creation
  pending --> accepted: Validation by curator
  pending --> rejected: Conflicting higher-confidence claim
  accepted --> retracted: Author retracts
  accepted --> rejected: Override with strictly higher confidence
  retracted --> [*]
  rejected --> [*]

Claim Operations

#### Retraction

Authors may retract their own claims, recording:

  • retracted_at: Timestamp of retraction
  • retracted_by: Author performing retraction
  • retraction_reason: Human-readable explanation

Only the original author may retract a claim:

if (target.authored_by !== as) {
  throw new Error(
    `only the original author may retract claim ${claimId}: authored_by=${target.authored_by ?? "<unset>"}, as=${as}`,
  );
}

Sources: src/tools/claim.ts:85-90

#### Rejection

Claims are rejected when a higher-confidence claim on the same assertion already exists:

return {
  claim_id: ex.id,
  action: "rejected",
  rejection: {
    reason: `existing claim ${ex.id} has effective confidence ${existingEffective.toFixed(3)}; submission's ${yourConfidence} is not strictly higher`,
    existing_id: ex.id,
    existing_effective_confidence: existingEffective,
    your_confidence: yourConfidence,
  },
};

Sources: src/tools/claim.ts:99-108

Claim Sorting in Syntheses

When rendering synthesis pages, claims are sorted by a scoring function that accounts for effective confidence and profile affinity:

const score = (c: ParsedClaim) => {
  const eff = effectiveConfidence(/* ... */);
  const boost = (c.profile ?? []).includes(deployingProfileId) ? 0.1 : 0;
  return eff + boost;
};
return [...claims].sort((a, b) => score(b) - score(a));

Sources: src/core/claim-render.ts:26-34

Synthesis Integration

Claims are incorporated into synthesis pages via the sources frontmatter field:

sources: inputIds.map(i => `[[wikis/${wiki}/${typeFolderForId(i)}/${i}]]`)

Sources: src/core/synthesize.ts:62

The synthesis metadata includes:

FieldValue
typesynthesis
statusdraft
last_compiledISO date of generation

Lint Rules

Claim Scope Validation

Claims without scope (profile, move, scope_wiki, or tags) receive a warning:

if (!allEmpty) return [];
return [{
  ruleId: "claim-with-no-scope",
  severity: "warn",
  line: 1,
  message: "Claim has no scope (profile/move/scope_wiki/tags all empty); no read path will surface it.",
}];

Sources: src/core/lint-checks/claim-with-no-scope.ts:17-24

Channel Format Validation

Channel identifiers are validated during lint runs to ensure consistency:

CheckSeverityMessage
Invalid kebab-case formatwarningchannel "<name>" must be lowercase kebab-case
ToolPurpose
vault.channel-postPost a message to a coordination channel
vault.channel-tailPull recent entries from a channel
vault.claimSubmit, validate, or retract claims
vault.synthesizeCompile synthesis pages incorporating claims

Sources: src/core/lint.ts:47-54

Runtime Adapters

Related topics: Sync Agents and Skills, Agent Profiles and Evolution

Section Related Pages

Continue reading this section for the full explanation and source context.

Section Deployment Target

Continue reading this section for the full explanation and source context.

Section Deploy Mode

Continue reading this section for the full explanation and source context.

Section Drift Detection

Continue reading this section for the full explanation and source context.

Related topics: Sync Agents and Skills, Agent Profiles and Evolution

The repository context provided does not contain the source files required for this topic:

These files are listed in the `

Sync Agents and Skills

The sync agents and skills system is a core deployment mechanism that bridges the vault's canonical knowledge store with agent runtime environments. It provides two synchronized capabilities: agent profile deployment and skills (moveset) deployment, enabling consistent agent behavior across different execution contexts.

Architecture Overview

The system operates across three layers:

LayerPurposeKey Files
CLI/APIUser-facing entry pointssrc/tools/sync-skills.ts, src/cli/commands/sync-agents.ts
Core LogicBusiness rules and deployment orchestrationsrc/core/skills.ts, src/core/skills-platform.ts
PersistenceDeployment registry managementsrc/core/deployments.ts
graph TD
    A["vault.sync-skills<br/>(MCP Tool)"] --> B["Read Profile<br/>wikis/&lt;wiki&gt;/profiles/&lt;profileId&gt;.md"]
    B --> C["Extract moves[]<br/>from profile frontmatter"]
    C --> D{"For each move"}
    D -->|Canonical path| E["wikis/_agents/moves/&lt;moveId&gt;/SKILL.md"]
    D -->|Deployed path| F["&lt;deployment.skills_dir&gt;/&lt;moveId&gt;/SKILL.md"]
    E --> G["probeSymlinkSupport()"]
    G --> H{"Host supports symlinks?"}
    H -->|Yes| I["fs.symlinkSync()"]
    H -->|No| J["recursiveCopy()"]
    I --> K["mode: 'symlink'"]
    J --> L["mode: 'copy'"]
    K --> M["Update _index/deployments.json"]
    L --> M
    M --> N["Return DeploymentEntry[]"]

Sources: src/tools/sync-skills.ts

Core Concepts

Deployment Target

The system supports multiple agent runtimes:

type DeployTarget = "claude-code" | "openclaw" | "codex";

Each target represents a distinct agent runtime that consumes skills from the local filesystem. Sources: src/core/deployments.ts:14

Deploy Mode

Two deployment modes control how skills are materialized:

ModeBehaviorFallback
symlinkCreates symbolic links from deployment directory to canonical vault filesAuto-falls back to copy on EPERM/EACCES/EEXIST
copyRecursively copies the move directory to the deployment pathNone

The system automatically probes symlink support at <targetDir>/.symlink-probe-<random> before attempting symlink deployment. Sources: src/core/skills-platform.ts:63-75

Drift Detection

The detectDriftAt() function compares canonical and deployed skill files using content hashing:

export function detectDriftAt(
  deployment: DriftDeployment,
  vaultPath: string
): DriftReport[]

Drift report rules:

ConditionReportAction
Canonical missingThrowsVault integrity bug
Deployed missing{ kind: "missing", actual_hash: undefined }Deploy needed
Hashes differ{ kind: "hash-mismatch", actual_hash }Re-deploy needed
Hashes matchNo entry (omit)No action

Sources: src/core/skills-platform.ts:27-40

Data Models

DeploymentEntry

export interface DeploymentEntry {
  repo_path: string;
  target: "claude-code" | "openclaw" | "codex";
  mode: DeployMode;
  actual_mode?: DeployMode;
  synced_at: string;

  // v1.7 additions
  runtime?: RuntimeName;
  source_revision?: string;
  subagent_def_path?: string;
}

The actual_mode field records whether symlinking was actually used (vs requested), enabling truthful reporting. Sources: src/core/deployments.ts:19-31

DeploymentRegistry

export type DeploymentRegistry = Record<string, DeploymentEntry[]>;

The registry is keyed by profileId, with each entry representing one deployment of that profile. Stored at _index/deployments.json at the vault root. Sources: src/core/deployments.ts:33-34

Skills Structure

Move Directory Layout

Skills (called "moves") follow a directory convention:

wikis/_agents/moves/
├── move-flamethrower/
│   └── SKILL.md
├── move-thunderbolt/
│   └── SKILL.md
└── move-protect/
    └── SKILL.md

Profile Structure

Profiles reference moves via the moves frontmatter field:

Sources: src/tools/sync-skills.ts

Tool Categories Reference

Related topics: Introduction to Stoa, Channels and Claims

Section Related Pages

Continue reading this section for the full explanation and source context.

Section Recall

Continue reading this section for the full explanation and source context.

Section Read

Continue reading this section for the full explanation and source context.

Section Lint

Continue reading this section for the full explanation and source context.

Related topics: Introduction to Stoa, Channels and Claims

Tool Categories Reference

Overview

The Stoa vault system exposes a collection of MCP (Model Context Protocol) tools organized into logical categories that handle knowledge retrieval, content creation, channel coordination, and linting operations. These tools operate on a vault directory structure where wikis contain typed pages with structured frontmatter and markdown bodies.

The tool system is designed around a wiki-centric data model where pages are typed (concept, spec, decision, synthesis, etc.), tagged, and optionally scoped to wikis or families. Tools provide both pull-based operations (read, recall) and push-based primitives (wait-for, channel-post), enabling both synchronous queries and event-driven workflows.

Sources: README.md:1-50

Tool Category Taxonomy

The available tools are organized into four primary categories:

CategoryPurposeTypical Consumer
Read — pull primitivesQuery existing vault contentAgents, humans
Write — contentCreate or modify vault pagesAgents
Wait — push primitivesBlock until vault events occurEvent-driven workflows
CoordinationCross-instance communication and task managementMulti-agent systems

Sources: README.md:1-50

Read Primitives

Read tools retrieve information from the vault index or direct file access without modifying state.

Recall

The vault.recall tool searches the vault index for pages matching query parameters. It supports filtering by wiki, type, status, channel, and layer (knowledge vs execution types). Results include relevance-ranked hits with IDs and metadata.

Query Parameters:

ParameterTypeDescription
topicstringSearch query text
wikistring (optional)Limit to specific wiki
layer`"all" \"knowledge" \"execution"`Filter by page type category
typestring (optional)Filter by exact page type
statusstring (optional)Filter by page status
channelstring (optional)Filter by channel name
limitnumberMaximum results (default 25)

The recall mechanism uses tokenized full-text search with stop-word removal and stemming via the Porter stemmer. Results are ranked by relevance.

Sources: src/core/index.ts:1-50

Read

The vault.read tool fetches a specific page by ID or path, returning parsed frontmatter and body content. Unlike recall which searches, read is a direct lookup operation.

Lint

The vault.lint tool runs diagnostic checks against the vault corpus. Lint checks validate structural properties and frontmatter invariants across the index.

Available Lint Checks:

CodeSeverityDescription
SYNTHESIS_DEBTwarningHard-knowledge clusters without synthesis coverage
CLAIM_WITH_NO_SCOPEwarnClaims missing all scope fields
CROSS_WIKI_LINK_BROKENerrorWikilinks pointing to non-existent pages

The lint system distinguishes between Group A rules (fast, frontmatter-only) and Group B rules (claim-file parsing required). Synthesis debt is a structural-property rule that scales with vault growth since it requires no content reads.

Sources: src/core/lint-checks/synthesis-debt.ts:1-60 Sources: src/core/lint-checks/claim-with-no-scope.ts:1-30

Write Primitives

Write tools create or modify vault content. These operations are idempotent where possible and preserve manual edits.

Synthesize

The vault.synthesize tool compiles or refreshes a synthesis page from a set of input pages. Syntheses are mid-tier hub pages that distill clusters of related concepts, specs, or decisions into recallable views.

Synthesis Modes:

ScopeID PatternWikiPurpose
topicsynthesis-<slug>configurable (default: alpha)General topic synthesis
memorysynthesis-<agent>-memory_agentsPer-agent memory compilation

Frontmatter Template:

id: synthesis-<slug>
title: "<topic> — synthesis"
type: synthesis
wiki: alpha
status: draft
created: <today>
updated: <today>
summary: "Synthesis of <N> pages on <topic>"
tags: []
last_compiled: <today>
sources:
  - [[wikis/alpha/concept/page-id-1]]
  - [[wikis/alpha/spec/page-id-2]]

The synthesis tool preserves manual edits in protected zones bounded by <!-- manual_notes:start ... --> and <!-- manual_notes:end --> markers. Re-compiles extract and re-apply content from these zones without losing human-authored prose.

Sources: src/core/synthesize.ts:1-100 Sources: src/core/frontmatter.ts:1-80

Inbox

The vault.inbox tool captures fleeting thoughts to the active wiki's inbox/ directory. This provides a low-friction capture mechanism for ideas that don't yet warrant a full typed page.

New

The vault.new tool creates a typed page from a template. Supported types include:

TypeFilename PatternNotes
conceptconcept-<slug>.mdKnowledge atom
specspec-<slug>.mdTechnical specification
decisiondecision-YYYY-MM-DD-<slug>.mdDate-prefixed
journaljournal-YYYY-MM-DD-HHMM-<slug>.mdDate+time required
movemove-<slug>/SKILL.mdDirectory layout
mapmap.mdFixed canonical filename
synthesissynthesis-<slug>.mdHub page
claimclaim-<slug>.mdAssertion with evidence
ideaidea-<slug>.mdUnvalidated concept
questionquestion-<slug>.mdOpen inquiry
sourcesource-<slug>.mdExternal citation
guideguide-<slug>.mdProcedural documentation

Sources: src/core/ids.ts:1-50

Agent Journal

The vault.agent-journal tool appends a first-person agent reflection at end-of-task. Journals are typed pages with channel: agent-log that capture agent decision rationale and state transitions.

Wait (Push Primitives)

Wait tools implement blocking semantics for event-driven workflows, replacing polling with cursor-based catch-up.

ToolSemantics
vault.wait-forBlock until one matching event lands
vault.wait-for-anyWake on first match across N filters (race)
vault.wait-for-allWake when all N filters have matched at least once
vault.wait-for-manyBounded batch over a window

These primitives use the stdio transport for MCP communication, maintaining a state cache for diffing prior and current vault events.

Sources: src/transport/stdio.ts:1-80 Sources: src/core/eventbus/state-cache.ts:1-40

Coordination Tools

Channel Post

The vault.channel-post tool posts to a coordination channel, enabling cross-instance communication. Channels are scoped to wikis and identified by kebab-case names.

Channel Entry Structure:

FieldTypeDescription
idstringPage ID
channelstringChannel name
wikistringWiki scope
authorstringAuthor identifier
tsISO dateTimestamp
excerptstringFirst 240 chars of body
pageIdstringReference to full page

The channel system aggregates journal entries and provides 24-hour activity counts for monitoring.

Sources: src/core/channel.ts:1-80

Task Claim

The vault.task-claim tool atomically claims a pending task. Race losers receive an AlreadyClaimedError, ensuring exactly-once task assignment in multi-agent scenarios.

Bootstrap Repo

The vault.bootstrap-repo tool wires a consuming repository with .mcp.json and a CLAUDE.md fragment, establishing the MCP configuration and agent instructions.

Sync Skills

The vault.sync-skills tool deploys agent profiles to a target skills directory, maintaining drift detection between canonical vault sources and deployed skill files.

Drift Detection:

ConditionBehavior
Canonical missingThrow (vault-integrity bug)
Deployed missingEmit { kind: "missing", actual_hash: undefined }
Hash mismatchEmit { kind: "hash-mismatch", actual_hash }
Hashes matchOmit from report

Sources: src/core/skills-platform.ts:1-80

Reindex

The vault.reindex tool regenerates _index/ files and per-wiki index.md, rebuilding the search index after bulk operations or corruption recovery.

Synthesis Staleness Tracking

The system tracks when synthesis pages were last compiled to identify debt. Staleness is computed as the difference between current time and last_compiled frontmatter field.

MetricCalculation
lag_days(now - compiledMs) / MS_PER_DAY
Filtermin_lag_days threshold (default: none)

This enables curators to identify syntheses that haven't been refreshed despite upstream changes to their source pages.

Sources: src/core/syntheses.ts:1-80

Tool Data Flow

graph TD
    subgraph "Read Primitives"
        R[Recall] -->|index query| I[VaultIndex]
        RD[Read] -->|direct file| F[Markdown files]
    end
    
    subgraph "Write Primitives"
        S[Synthesize] -->|read prior| F
        S -->|write| N[New file]
        IN[Inbox] -->|append| I
        AJ[Agent Journal] -->|append| C[Channel]
    end
    
    subgraph "Event System"
        W[Wait-for tools] -->|subscribe| E[Event Bus]
        E -->|state diff| SC[State Cache]
    end
    
    subgraph "Coordination"
        CP[Channel Post] -->|broadcast| C
        TC[Task Claim] -->|atomic| T[Task Registry]
    end
    
    subgraph "Validation"
        L[Lint] -->|check| CH[Claim Handlers]
        L -->|check| SD[Synthesis Debt]
        L -->|check| CW[Claim Scope]
    end
    
    I -.->|rebuild| R
    F -.->|parse| I

Page Type Layering

The system distinguishes between two semantic layers:

LayerTypesRole
Knowledgeconcept, spec, decision, synthesisDurable thinking content
Executionguide, source, idea, questionProcedural and citation

Recall and synthesis debt checks filter by layer, enabling scope-aware queries that don't mix execution metadata into knowledge graph traversals.

Sources: src/core/index.ts:1-50 Sources: src/core/lint-checks/synthesis-debt.ts:1-60

Family Resolution

Tools can operate at wiki or family scope. Family resolution follows a priority cascade:

graph LR
    A[CLI --family arg] --> B{Match?}
    B -->|yes| Z[Return family]
    B -->|no| C[Check .active-family file]
    C --> D{Exists?}
    D -->|yes| Z
    D -->|no| E[Use defaultFamily config]
    E --> F{Configured?}
    F -->|yes| Z
    F -->|no| G[Return null - single wiki]

When a wiki is explicitly specified alongside a family argument, the system validates consistency and throws FamilyMismatchError on mismatch.

Sources: src/core/family.ts:1-80

Tools consume and produce wikilinks in the vault-root absolute form: [[wikis/<wiki>/<type>/<id>(|alias)]]. The wikilink extractor is code-fence-aware and handles both body content and frontmatter related: arrays.

BehaviorScope
Top-level fenced blocksStripped
Indented fenced blocksPreserved
Inline code spansPreserved
Malformed linksSilently skipped

Sources: src/core/wikilinks.ts:1-50

Configuration

Tools read configuration from vault-mcp.json in the vault root:

KeyDescription
vaultPathAbsolute path to vault directory
defaultWikiFallback wiki when unspecified
defaultFamilyFallback family scope

The stdio server logs its configuration on startup: vault-mcp stdio server ready (vault=<path>, default-wiki=<wiki>, default-family=<family>).

Sources: src/transport/stdio.ts:1-80

Sources: README.md:1-50

Doramagic Pitfall Log

Source-linked risks stay visible on the manual page so the preview does not read like a recommendation.

medium Configuration risk needs validation

Users may get misleading failures or incomplete behavior unless configuration is checked carefully.

medium README/documentation is current enough for a first validation pass.

The project should not be treated as fully validated until this signal is reviewed.

medium Maintainer activity is unknown

Users cannot judge support quality until recent activity, releases, and issue response are checked.

medium no_demo

The project may affect permissions, credentials, data exposure, or host boundaries.

Doramagic Pitfall Log

Doramagic extracted 7 source-linked risk signals. Review them before installing or handing real data to the project.

1. Configuration risk: Configuration risk needs validation

  • Severity: medium
  • Finding: Configuration risk is backed by a source signal: Configuration risk needs validation. Treat it as a review item until the current version is checked.
  • User impact: Users may get misleading failures or incomplete behavior unless configuration is checked carefully.
  • Recommended check: Open the linked source, confirm whether it still applies to the current version, and keep the first run isolated.
  • Evidence: capability.host_targets | github_repo:1229401738 | https://github.com/BrettNye/stoa | host_targets=mcp_host, claude, claude_code

2. Capability assumption: README/documentation is current enough for a first validation pass.

  • Severity: medium
  • Finding: README/documentation is current enough for a first validation pass.
  • User impact: The project should not be treated as fully validated until this signal is reviewed.
  • Recommended check: Open the linked source, confirm whether it still applies to the current version, and keep the first run isolated.
  • Evidence: capability.assumptions | github_repo:1229401738 | https://github.com/BrettNye/stoa | README/documentation is current enough for a first validation pass.

3. Maintenance risk: Maintainer activity is unknown

  • Severity: medium
  • Finding: Maintenance risk is backed by a source signal: Maintainer activity is unknown. Treat it as a review item until the current version is checked.
  • User impact: Users cannot judge support quality until recent activity, releases, and issue response are checked.
  • Recommended check: Open the linked source, confirm whether it still applies to the current version, and keep the first run isolated.
  • Evidence: evidence.maintainer_signals | github_repo:1229401738 | https://github.com/BrettNye/stoa | last_activity_observed missing

4. Security or permission risk: no_demo

  • Severity: medium
  • Finding: no_demo
  • User impact: The project may affect permissions, credentials, data exposure, or host boundaries.
  • Recommended check: Open the linked source, confirm whether it still applies to the current version, and keep the first run isolated.
  • Evidence: downstream_validation.risk_items | github_repo:1229401738 | https://github.com/BrettNye/stoa | no_demo; severity=medium

5. Security or permission risk: no_demo

  • Severity: medium
  • Finding: no_demo
  • User impact: The project may affect permissions, credentials, data exposure, or host boundaries.
  • Recommended check: Open the linked source, confirm whether it still applies to the current version, and keep the first run isolated.
  • Evidence: risks.scoring_risks | github_repo:1229401738 | https://github.com/BrettNye/stoa | no_demo; severity=medium

6. Maintenance risk: issue_or_pr_quality=unknown

  • Severity: low
  • Finding: issue_or_pr_quality=unknown。
  • User impact: Users cannot judge support quality until recent activity, releases, and issue response are checked.
  • Recommended check: Open the linked source, confirm whether it still applies to the current version, and keep the first run isolated.
  • Evidence: evidence.maintainer_signals | github_repo:1229401738 | https://github.com/BrettNye/stoa | issue_or_pr_quality=unknown

7. Maintenance risk: release_recency=unknown

  • Severity: low
  • Finding: release_recency=unknown。
  • User impact: Users cannot judge support quality until recent activity, releases, and issue response are checked.
  • Recommended check: Open the linked source, confirm whether it still applies to the current version, and keep the first run isolated.
  • Evidence: evidence.maintainer_signals | github_repo:1229401738 | https://github.com/BrettNye/stoa | release_recency=unknown

Source: Doramagic discovery, validation, and Project Pack records

Community Discussion Evidence

These external discussion links are review inputs, not standalone proof that the project is production-ready.

Sources 1

Count of project-level external discussion links exposed on this manual page.

Use Review before install

Open the linked issues or discussions before treating the pack as ready for your environment.

Community Discussion Evidence

Doramagic exposes project-level community discussion separately from official documentation. Review these links before using stoa with real data or production workflows.

Source: Project Pack community evidence and pitfall evidence