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
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Related Pages
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:
| Type | Filename Pattern | Description |
|---|---|---|
concept | concept-<slug>.md | Knowledge concepts and definitions |
guide | guide-<slug>.md | Procedural how-to documentation |
decision | decision-YYYY-MM-DD-<slug>.md | Decision records with date prefix |
journal | journal-YYYY-MM-DD-HHMM-<slug>.md | Time-stamped reflection entries |
move | move-<slug>/SKILL.md | Directory-based skill/move definitions |
map | map.md | Fixed filename for wiki maps |
profile | profile-<slug>.md | Agent profiles |
synthesis | synthesis-<slug>.md | Compiled 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
Recall (Search)
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:
- Tokenizes page content using a Porter stemmer
- Filters stop words (the, and, of, a, an, in, to, is, etc.)
- Builds inverted index mapping tokens to page IDs
- 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:
- Discovers input pages by topic recall or explicit agent memory
- Generates frontmatter with metadata, sources, and tags
- Creates marker-bounded content regions for idempotent re-rendering
- 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:
| Field | Description |
|---|---|
id | Unique synthesis identifier |
title | Synthesis title with topic/agent context |
type | Always "synthesis" |
wiki | Target wiki namespace |
status | Draft status |
sources | Array of wikilinks to contributing pages |
last_compiled | ISO date of last synthesis refresh |
Sources: src/core/synthesize.ts:1-85
Wikilink System
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:
| Function | Purpose |
|---|---|
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
| Tool | Description |
|---|---|
vault.recall | Full-text search across vault |
vault.inbox-list | List captured thoughts |
vault.page-read | Read specific page by ID |
vault.channel-tail | Pull recent channel entries |
Write Tools
| Tool | Description |
|---|---|
vault.inbox | Capture fleeting thoughts |
vault.new | Create typed page from template |
vault.new-wiki | Scaffold new wiki space |
vault.synthesize | Compile synthesis from matching pages |
vault.set-active | Set ambient active wiki |
vault.agent-journal | Append agent reflection |
vault.reindex | Regenerate index files |
Coordination Tools
| Tool | Description |
|---|---|
vault.channel-post | Post to coordination channel |
vault.task-* | Task lifecycle (create, list, claim, update) |
vault.claims-* | Claims management (submit, list, retract, reject) |
Platform Tools
| Tool | Description |
|---|---|
vault.profile-register | Register profile with Stadium platform |
vault.sync-skills | Deploy agent moveset as local skills |
vault.bootstrap-repo | Wire consuming repo with MCP config |
Wait Primitives
| Tool | Description |
|---|---|
vault.wait-for | Block until matching event |
vault.wait-for-any | Wake on first match (race) |
vault.wait-for-all | Wake when all filters matched |
vault.wait-for-many | Bounded 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:
- Explicit
wiki:argument on tool call --default-wiki=<name>server flag.active-wikifile at vault root- Error if none found
Server Configuration
stoa --vault=/path/to/vault \
--default-wiki=alpha \
--default-family=my-family
Related Documentation
- Installation Guide — Full setup and configuration walkthrough
- Manual Smoke Test — Verify your installation
- Wait-For Primitives — Push primitives documentation
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
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Related Pages
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.
| Type | Filename Pattern | Description |
|---|---|---|
concept | concept-<slug>.md | Knowledge concepts and definitions |
decision | decision-YYYY-MM-DD-<slug>.md | Decision records with date prefix |
guide | guide-<slug>.md | Procedural guides |
source | source-<slug>.md | External citations and references |
idea | idea-<slug>.md | Ideas and brainstorms |
question | question-<slug>.md | Questions for investigation |
profile | profile-<slug>.md | Agent or entity profiles |
synthesis | synthesis-<slug>.md | Compiled synthesis pages |
journal | journal-YYYY-MM-DD-HHMM-<slug>.md | Agent journal entries |
move | move-<slug>/SKILL.md | Skills/moves (directory layout) |
map | map.md | Wiki 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
Wikilink Format
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
Token-Based Search
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:
- Lowercase normalization
- Strip non-alphanumeric characters
- Split on whitespace
- Filter stop words and single characters
- 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:
| File | Purpose |
|---|---|
pages.json | Full page metadata with frontmatter |
tokens.json | Search tokens mapped to page IDs |
links.json | Inter-page references (wikilinks) |
wikis.json | Wiki registry and metadata |
profiles.json | Profile-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.synthesizecommand 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:
familyArg— Explicit parameter passed to the functiondefaultFamily— Configured default (via--default-familyCLI arg).active-familyfile at vault rootnull— Falls through to single-wiki resolution
Sources: src/core/family.ts:15-40
Wiki Resolution Order
Wiki parameter resolution follows this order:
- Explicit
wiki:argument on tool call --default-wiki=<name>flag on server invocation.active-wikifile at vault root- 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
| Field | Type | Description |
|---|---|---|
id | string | Unique page identifier |
title | string | Display title |
type | string | Page type |
wiki | string | Wiki name |
status | string | Lifecycle status |
created | string | ISO 8601 creation date |
updated | string | ISO 8601 modification date |
summary | string | Brief description |
tags | string[] | Categorization tags |
layer | string | knowledge or execution |
sources | string[] | Wikilinks to source pages |
Profile-Specific Fields
| Field | Type | Description |
|---|---|---|
pokemon | string | Species name |
evolution_stage | string | basic, stage1, stage2 |
pokemon_type | string | Pokémon type (if applicable) |
dev_specialty | string | Developer specialty |
rarity | string | common, baby, legendary, mythical |
platform_profile_id | string | Stadium platform ID |
platform_stats | object | Persisted 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 rollupsync-skills— Moveset synchronizationbootstrap-repo— Agent documentation patches
Sources: src/core/marker-render.ts:1-50
Tool Categories
Read Primitives
| Tool | Purpose |
|---|---|
vault.recall | Full-text search with filters |
vault.page-read | Read single page by ID |
vault.channel-tail | Pull coordination channel entries |
vault.synthesis-staleness | Check synthesis freshness |
Wait (Push Primitives)
| Tool | Purpose |
|---|---|
vault.wait-for | Block until matching event |
vault.wait-for-any | Wake on first match across N filters |
vault.wait-for-all | Wake when all N filters matched |
vault.wait-for-many | Bounded batch over window |
Write — Content
| Tool | Purpose |
|---|---|
vault.inbox | Capture fleeting thoughts |
vault.new | Create typed page from template |
vault.new-wiki | Scaffold new wiki structure |
vault.set-active | Set ambient active wiki |
vault.synthesize | Compile synthesis page |
vault.agent-journal | Append agent reflection |
Write — System
| Tool | Purpose |
|---|---|
vault.reindex | Regenerate _index/ files |
Coordination
| Tool | Purpose |
|---|---|
vault.channel-post | Post to coordination channel |
vault.task-claim | Atomically claim pending task |
vault.task-create | Create new task |
vault.task-list | List tasks |
vault.task-update | Update task state |
vault.bootstrap-repo | Wire consuming repo with MCP config |
vault.sync-skills | Deploy agent moveset as local skills |
Sources: README.md:30-80
Configuration Files
Server Configuration
| Config Method | Description |
|---|---|
--vault=<path> | Vault root directory |
--default-wiki=<name> | Default wiki for operations |
--default-family=<name> | Default family for multi-wiki ops |
STOA_VAULT_PATH | Environment variable alternative |
Active Context Files
| File | Purpose |
|---|---|
.active-wiki | Current active wiki name |
.active-family | Current 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
| Term | Definition |
|---|---|
| Vault | Root directory containing all wikis and metadata |
| Wiki | Organizational unit containing typed pages |
| Family | Grouping of related wikis |
| Page | Single markdown document with frontmatter |
| Type | Page classification determining storage and behavior |
| Wikilink | Cross-reference syntax [[wikis/...]] |
| Synthesis | Compiled page aggregating multiple sources |
| Layer | Knowledge vs execution classification |
| Marker | HTML comment bounding managed file regions |
| Index | Aggregated metadata for efficient querying |
| Debt | Gap between source pages and their synthesis |
Sources: src/core/index.ts:1-50
Architecture and Design
Related topics: Introduction to Stoa, Event Bus System
Continue reading this section for the full explanation and source context.
Related Pages
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
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Related Pages
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:
| Layer | Purpose |
|---|---|
| Watcher | Filesystem observer that monitors vault paths |
| State Cache | In-memory Map storing prior state for diffing |
| Matchers | Rule-based filters that determine when to emit events |
| Bus | Event dispatcher connecting watchers to consumers |
| Wait-For Tools | Blocking 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 --> CSources: 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
| Method | Signature | Purpose | |
|---|---|---|---|
get | `<T>(source, wiki, id) => T \ | undefined` | Retrieve cached state |
set | <T>(source, wiki, id, state) => void | Store state snapshot | |
has | (source, wiki, id) => boolean | Check if state exists | |
size | () => number | Return 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
| Condition | Action |
|---|---|
| Status changed | Add task_status_change: { from, to } |
| Owner changed | Add task_owner_change: { from, to } |
| Both unchanged | Emit false (no event) |
| Both changed | Emit 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:
compare(prev, cur)— Returns{ emit: boolean, enrichment?: Partial<VaultEvent> }nextState(parsed)— Extracts state fields from current file parseinit(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
| Tool | Behavior |
|---|---|
vault.wait-for | Block until one matching event lands; cursor-based catch-up |
vault.wait-for-any | Wake on first match across N filters (race semantics) |
vault.wait-for-all | Wake when all N filters have matched at least once |
vault.wait-for-many | Bounded 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 eventIntegration 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:
| Source | Priority | Environment Variable |
|---|---|---|
CLI --vault | 1 (highest) | — |
STOA_VAULT_PATH | 2 | STOA_VAULT_PATH |
| Default | 3 (lowest) | — |
Active wiki/family files (.active-wiki, .active-family) influence which entities are monitored.
Error Handling
| Scenario | Behavior |
|---|---|
| Unknown wiki | Events filtered out; no error |
| File read failure | Error propagates to watcher |
| State not in cache | Treat as new entity; use init |
| Matcher throws | Error propagates; event not emitted |
Sources: src/transport/stdio.ts:27-28
Agent Profiles and Evolution
Related topics: Core Concepts and Vocabulary, Runtime Adapters
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Related Pages
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:#e8f5e9Profile 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:
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Must match ^profile- pattern |
title | string | Yes | Display name for the profile |
type | enum | Yes | Always "profile" |
wiki | string | Yes | Parent wiki namespace |
status | enum | No | Page status (draft/active/accepted/superseded/archived) |
pokemon | string | No | Species name (v1.5 convention) |
species_name | string | No | Legacy species name (fallback) |
evolution_stage | string | No | Stage identifier (defaults to "basic") |
pokemon_type | string | No | Type classification |
dev_specialty | string | No | Developer specialty marker |
platform_profile_id | string | No | Stadium platform ID (set post-registration) |
platform_stats | object | No | Stats 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 resultSources: 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
| Parameter | Source | Description |
|---|---|---|
wiki | Computed | Defaults to "_agents" |
title | Computed | Format: <species> agent |
frontmatterExtras.pokemon | selected_species | Species name |
frontmatterExtras.evolution_stage | evolution_stage | Defaults to "basic" |
frontmatterExtras.pokemon_type | pokemon_type | Optional type |
frontmatterExtras.dev_specialty | dev_specialty | Optional 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
| Roll | Probability | Effect |
|---|---|---|
| Rarity | Varies by species | Determines rarity tier |
| Shiny | 1/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 --> BProfile Identification
Supported ID Patterns
The system supports multiple profile identification patterns:
| Pattern | Description | Example |
|---|---|---|
| Standard | profile-<slug>.md | profile-fire-type.md |
| Agent-specific | profile-<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/registerfor profile creation - Error Handling: Propagates
StadiumApiErrorwitherror_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
| Setting | Default | Source |
|---|---|---|
| Default Wiki | _agents | routes-write.ts:78 |
| Evolution Stage | "basic" | routes-write.ts:90 |
| Shiny Probability | 1/64 | routes-write.ts:98-112 |
| Platform Endpoint | /profiles/register | profile-register.ts:23 |
Related Tools
| Tool | Purpose |
|---|---|
vault.profile-register | Register profile with Stadium platform |
vault.evolve-profile | Trigger evolution on eligible profiles |
vault.refresh-profile-memory | Update 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
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Related Pages
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_updatedtimestamps
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:
| Field | Type | Description |
|---|---|---|
id | string | Unique task identifier (kebab-case per HARD_KNOWLEDGE_TYPES) |
wiki | string | The wiki namespace containing this task |
status | string | Current task status (e.g., pending, claimed, merged) |
claimed_by | string? | Agent ID that claimed this task |
claimed_at | string? | ISO timestamp when the task was claimed |
updated | string | ISO 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:
| Status | Description |
|---|---|
draft | Initial state; task created but not ready for work |
active | Task is open and in progress |
claimed | Task has been assigned to an agent |
merged | Task work has been merged/completed |
archived | Task 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:
- The system loads the current task state
- If
claimed_byis already set, the race-loser receivesAlreadyClaimedError - If unclaimed, the task is atomically updated with
claimed_byandclaimed_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
mergedstatus 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.,active→merged)task_owner_change— whenclaimed_bychanges
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
| Tool | Description |
|---|---|
vault.task-create | Create a new task in a specified wiki |
vault.task-list | List tasks with optional filters |
vault.task-update | Update task fields (status, claimed_by, etc.) |
vault.task-claim | Atomically 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:
- Writes journal file at
wikis/_agents/journal/<journal_id>.md - Idempotent rewrite — same
now+ same input produces samejournal_id - Conditional task transition via
computeTaskTransition task_updated: falsewhen 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:
| Key | Default | Description |
|---|---|---|
task.min_cluster_size | 3 | Minimum cluster size for synthesis debt detection |
Note: Task-specific configuration loading is pending (resolution-lifecycle spec implementation).
Related Concepts
- 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
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Related Pages
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: <name><br/>wiki: <wiki>| C[Vault FS]
D[Other Agent] -->|vault.channel-tail| E[Channel Summary]
C -->|queryPages idx| EChannel Naming Convention
Channels must follow a strict naming format validated at lint time:
| Format | Regex | Example |
|---|---|---|
| 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:
| Field | Type | Description |
|---|---|---|
name | string | Channel identifier |
wiki | string | Wiki where the channel exists |
lastEntry | object | Most recent entry with id, author, ts, excerpt |
count24h | number | Entries 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:
type === "journal"— Only journal pages participate in channelswiki === 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:
- Initial confidence level —
high,medium, orlow - Time since last validation — Measured in days
- 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:
| Parameter | Default | Purpose |
|---|---|---|
half_life_days | Configurable | Days for confidence to halve |
effective_floor | Configurable | Minimum 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.summaryif available, falling back toclaim.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 retractionretracted_by: Author performing retractionretraction_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:
| Field | Value |
|---|---|
type | synthesis |
status | draft |
last_compiled | ISO 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:
| Check | Severity | Message |
|---|---|---|
| Invalid kebab-case format | warning | channel "<name>" must be lowercase kebab-case |
Related Tools
| Tool | Purpose |
|---|---|
vault.channel-post | Post a message to a coordination channel |
vault.channel-tail | Pull recent entries from a channel |
vault.claim | Submit, validate, or retract claims |
vault.synthesize | Compile synthesis pages incorporating claims |
Sources: src/core/lint.ts:47-54
Runtime Adapters
Related topics: Sync Agents and Skills, Agent Profiles and Evolution
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Related Pages
Related topics: Sync Agents and Skills, Agent Profiles and Evolution
The repository context provided does not contain the source files required for this topic:
src/core/runtime-adapters/types.tssrc/core/runtime-adapters/registry.tssrc/core/runtime-adapters/claude-code.tssrc/core/subagent-protocol.tssrc/core/subagent-intent.ts
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:
| Layer | Purpose | Key Files |
|---|---|---|
| CLI/API | User-facing entry points | src/tools/sync-skills.ts, src/cli/commands/sync-agents.ts |
| Core Logic | Business rules and deployment orchestration | src/core/skills.ts, src/core/skills-platform.ts |
| Persistence | Deployment registry management | src/core/deployments.ts |
graph TD
A["vault.sync-skills<br/>(MCP Tool)"] --> B["Read Profile<br/>wikis/<wiki>/profiles/<profileId>.md"]
B --> C["Extract moves[]<br/>from profile frontmatter"]
C --> D{"For each move"}
D -->|Canonical path| E["wikis/_agents/moves/<moveId>/SKILL.md"]
D -->|Deployed path| F["<deployment.skills_dir>/<moveId>/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:
| Mode | Behavior | Fallback |
|---|---|---|
symlink | Creates symbolic links from deployment directory to canonical vault files | Auto-falls back to copy on EPERM/EACCES/EEXIST |
copy | Recursively copies the move directory to the deployment path | None |
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:
| Condition | Report | Action |
|---|---|---|
| Canonical missing | Throws | Vault integrity bug |
| Deployed missing | { kind: "missing", actual_hash: undefined } | Deploy needed |
| Hashes differ | { kind: "hash-mismatch", actual_hash } | Re-deploy needed |
| Hashes match | No 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
- Each move is a directory containing a single
SKILL.mdfile - Move IDs are used as directory names
- The directory layout enables recursive deployment operations Sources: src/core/skills-platform.ts:18-23
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
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Related Pages
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:
| Category | Purpose | Typical Consumer |
|---|---|---|
| Read — pull primitives | Query existing vault content | Agents, humans |
| Write — content | Create or modify vault pages | Agents |
| Wait — push primitives | Block until vault events occur | Event-driven workflows |
| Coordination | Cross-instance communication and task management | Multi-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:
| Parameter | Type | Description | ||
|---|---|---|---|---|
topic | string | Search query text | ||
wiki | string (optional) | Limit to specific wiki | ||
layer | `"all" \ | "knowledge" \ | "execution"` | Filter by page type category |
type | string (optional) | Filter by exact page type | ||
status | string (optional) | Filter by page status | ||
channel | string (optional) | Filter by channel name | ||
limit | number | Maximum 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:
| Code | Severity | Description |
|---|---|---|
SYNTHESIS_DEBT | warning | Hard-knowledge clusters without synthesis coverage |
CLAIM_WITH_NO_SCOPE | warn | Claims missing all scope fields |
CROSS_WIKI_LINK_BROKEN | error | Wikilinks 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:
| Scope | ID Pattern | Wiki | Purpose |
|---|---|---|---|
topic | synthesis-<slug> | configurable (default: alpha) | General topic synthesis |
memory | synthesis-<agent>-memory | _agents | Per-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:
| Type | Filename Pattern | Notes |
|---|---|---|
concept | concept-<slug>.md | Knowledge atom |
spec | spec-<slug>.md | Technical specification |
decision | decision-YYYY-MM-DD-<slug>.md | Date-prefixed |
journal | journal-YYYY-MM-DD-HHMM-<slug>.md | Date+time required |
move | move-<slug>/SKILL.md | Directory layout |
map | map.md | Fixed canonical filename |
synthesis | synthesis-<slug>.md | Hub page |
claim | claim-<slug>.md | Assertion with evidence |
idea | idea-<slug>.md | Unvalidated concept |
question | question-<slug>.md | Open inquiry |
source | source-<slug>.md | External citation |
guide | guide-<slug>.md | Procedural 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.
| Tool | Semantics |
|---|---|
vault.wait-for | Block until one matching event lands |
vault.wait-for-any | Wake on first match across N filters (race) |
vault.wait-for-all | Wake when all N filters have matched at least once |
vault.wait-for-many | Bounded 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:
| Field | Type | Description |
|---|---|---|
id | string | Page ID |
channel | string | Channel name |
wiki | string | Wiki scope |
author | string | Author identifier |
ts | ISO date | Timestamp |
excerpt | string | First 240 chars of body |
pageId | string | Reference 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:
| Condition | Behavior |
|---|---|
| Canonical missing | Throw (vault-integrity bug) |
| Deployed missing | Emit { kind: "missing", actual_hash: undefined } |
| Hash mismatch | Emit { kind: "hash-mismatch", actual_hash } |
| Hashes match | Omit 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.
| Metric | Calculation |
|---|---|
lag_days | (now - compiledMs) / MS_PER_DAY |
| Filter | min_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| IPage Type Layering
The system distinguishes between two semantic layers:
| Layer | Types | Role |
|---|---|---|
| Knowledge | concept, spec, decision, synthesis | Durable thinking content |
| Execution | guide, source, idea, question | Procedural 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
Wikilink Resolution
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.
| Behavior | Scope |
|---|---|
| Top-level fenced blocks | Stripped |
| Indented fenced blocks | Preserved |
| Inline code spans | Preserved |
| Malformed links | Silently skipped |
Sources: src/core/wikilinks.ts:1-50
Configuration
Tools read configuration from vault-mcp.json in the vault root:
| Key | Description |
|---|---|
vaultPath | Absolute path to vault directory |
defaultWiki | Fallback wiki when unspecified |
defaultFamily | Fallback 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.
Users may get misleading failures or incomplete behavior unless configuration is checked carefully.
The project should not be treated as fully validated until this signal is reviewed.
Users cannot judge support quality until recent activity, releases, and issue response are checked.
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.
Count of project-level external discussion links exposed on this manual page.
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.
- Configuration risk needs validation - GitHub / issue
Source: Project Pack community evidence and pitfall evidence