Documentation source
Frameworks
Captured methods (STORM, Design Thinking, Crazy 8s, …) authored once as pure data, then compiled down into a human experience, an agent skill, or an autonomous knowledge loop — no new runtime.
# Frameworks
A **Framework** is a captured, reusable method — a technique or methodology like STORM, Design Thinking, Crazy 8s, SCAMPER, or Jobs-to-be-Done. You author the method *once* as pure data, and the platform *projects* it into whichever actionable surface fits the moment: a human walks through it as a guided experience, an agent runs it as a skill, or it ticks autonomously as a research loop.
## Overview
The module lives in `features/frameworks/`. Its whole reason for existing is **capture once → project into the right actionable surface**. A method like STORM is the same body of knowledge whether a person works through it step by step, an agent executes it on demand, or a cron-driven loop keeps re-running it as new sources appear. Re-encoding that method three times — once per surface — is exactly the kind of duplication this codebase fights.
A framework does **not** add a new runtime. It is a content schema (`FrameworkDefinition`) and a set of pure compilers that **lower** the captured method down to the runtimes the platform already owns:
```
frameworkToExercise() → human EXPERIENCE (features/exercises runner)
frameworkToSkill() → agent SKILL (features/skills runSkill + export)
frameworkToKnowledgeLoop() → autonomous LOOP (features/loops knowledge loop)
```
This mirrors the pattern `KnowledgeLoopDefinition` already follows when it compiles to a `GoalLoopTemplate`: a captured shape lowers into an existing primitive instead of standing up a parallel system. A framework adds zero tables, zero executors, and zero new session types — it is data plus translation.
Because the `FrameworkDefinition` *is* the content schema of the `framework` entity type (DB-driven per Critical Rule 2), a captured method is just a `framework` entity row that anyone — a person in the admin UI, an agent via `createEntity` — can author. The compilers read that row.
## Key Concepts
**FrameworkDefinition** — the canonical capture shape, defined in `features/frameworks/lib/types.ts`. A `framework` entity's `content` JSONB parses to this; the compilers consume it. Core fields:
- `slug` — kebab-case identifier (`^[a-z][a-z0-9-]{0,63}$`).
- `name`, `version`, `summary` — display + provenance basics.
- `category` — one of the `FRAMEWORK_CATEGORIES` (`research`, `ideation`, `design-thinking`, `strategy`, `decision`, `prioritization`, `facilitation`, `analysis`, `discovery`, `reflection`, `planning`, `other`). Discriminates the catalog and launch surfaces.
- `origin` — provenance (`source`, `author`, `url`, `year`) so a captured method is citable. E.g. `Stanford OVAL — STORM (NAACL 2024)`.
- `whenToUse` — trigger cues for *when to reach for the method*. These feed skill eval `triggerCases` and the experience launch copy.
- `outcome` — what running the method yields. Becomes the loop goal and the artifact framing.
- `participants` — `solo` | `facilitated` | `team` | `agent` | `mixed`. Informs whether the experience projection makes sense at all.
- `estimatedMinutes`, `entityTypeSlugs`, `tags`, `methodConfig` (method-specific knobs like STORM's perspective/round counts).
- `steps` — the ordered method phases (see below). **The shared spine across all three projections.**
- `artifact` — the document the method produces, grouped into `sections` of step outputs.
- `activation` — per-projection opt-in (see *The three projections*).
**FrameworkStep** — one ordered phase of the method. Every step declares **who performs it** and, for human steps, **how it is captured**:
- `id`, `title`, `prompt` (the human-facing instruction/question), optional `guidance` (coaching hint).
- `actor` — `FRAMEWORK_ACTORS`: `human` | `agent` | `both`. **This is the projection switch.** Human/`both` steps become exercise inputs; agent/`both` steps become skill instructions and loop synthesis work.
- `inputType` — `FRAMEWORK_INPUT_TYPES`: `text` | `textarea` | `choice` | `score` | `none`. Maps 1:1 onto `ExerciseInputType` plus `none`. A `none` step is informational/agent-only — it has no human input and is dropped from the exercise projection.
- `options` / `min` / `max` / `step` — input config for `choice` and `score`.
- `artifactKey` — where this step's output lands in the artifact (defaults to `id`).
- `toolHints` — tools an agent would reach for here; these feed the skill manifest's `requiredTools` and the loop synthesis brief.
- `produces` — the entity type slug an agent step is expected to create.
- `required` — gates the exercise step.
**The three projections** — a framework opts in to the surfaces that fit it via `activation`. Each block is optional; absence means "don't project to that surface":
- **`activation.experience`** — the human experience (exercise runner). `exerciseKind` (`generating` is the sensible default), `launchReason`, `startLabel`.
- **`activation.skill`** — the agent skill (`runSkill` + export to Claude/GPT/MCP). `slug`, extra `requiredTools`/`requiredToolGroups`, `delegations` to specialist agents (STORM's experts + moderator), `exposeViaMcp`.
- **`activation.loop`** — the autonomous/recurring research loop. Only *iterative* methods opt in. Carries the knowledge-loop-specific fields the generic framework can't derive (`domain`, `recordEntityTypeSlug`, `cadence`, `safetyClass`, …) plus a pass-through `spec` (sources, surfaces, eval, review gate, research iteration) validated by `validateKnowledgeLoop` after the compile.
**FrameworkOrigin / FrameworkArtifact / FrameworkActivation** — the supporting sub-schemas, all `.strict()` Zod v4 shapes co-located in `types.ts`.
## How It Works
### The compiler pattern (no parallel system)
The module is **a content schema plus three pure functions**. Nothing executes inside `features/frameworks/` — the compilers transform a `FrameworkDefinition` into the *input shape* of an existing runtime, and that runtime does the work.
```
┌─ frameworkToExercise() → ExerciseFrameworkDefinition → exercise runner
FrameworkDefinition ─────┼─ frameworkToSkill() → FrameworkSkill (SkillManifestV1) → runSkill / export
(steps[] = spine) └─ frameworkToKnowledgeLoop() → KnowledgeLoopDefinition → goal-loop runner
```
The **`steps[]` array is the shared spine**. Each compiler reads the same steps and filters/lowers them by `actor`:
- **To an experience:** `frameworkToExercise()` keeps only human-capturable steps (`actor !== "agent"` and `inputType !== "none"`), maps each to an `ExerciseStepDefinition`, and projects the artifact sections onto the surviving steps. Agent-only steps (STORM's multi-agent research phases) are dropped — they belong to the skill/loop. If nothing human-capturable remains, the function returns `null` so the caller treats the experience as genuinely absent rather than shipping an empty runner.
- **To a skill:** `frameworkToSkill()` renders every step (in order) into a deterministic markdown playbook — stable prefix so Anthropic prompt-caching stays warm. Per-step `toolHints` and `activation.skill.delegations` become the `SkillManifestV1` capability + governance declarations; `whenToUse` becomes eval `triggerCases`; the framework binds to its own `framework` entity and its `entityTypeSlugs`.
- **To a loop:** `frameworkToKnowledgeLoop()` derives what it *can* (goal from `outcome`, synthesis instructions from the agent steps, the framework's own skill as the operating method, example use cases from `whenToUse`) and **merges the author `spec` on top** for the parts that need author intent (sources, record type, eval rubric, research-iteration budget). It then hands the merged input to `validateKnowledgeLoop` so callers see every error and warning, and returns the parsed definition (or `null` on a malformed merge) alongside the raw input and validation report.
This is the same lowering discipline the codebase uses everywhere: a higher-level declarative shape compiles down to a primitive (`BlockSpec`/`ViewSpec`/`FormSpec` → render path; `KnowledgeLoopDefinition` → `GoalLoopTemplate`). Frameworks add nothing to execute — they add a way to *say a method once*.
### Validation keeps the projections honest
`validateFramework(input)` parses against the schema and then checks cross-field invariants that keep the three projections coherent:
- **Step ids must be unique** — projection keys collide otherwise.
- **`choice` inputs need `options`.**
- **Artifact sections must reference real steps.**
- **Experience projection needs a human step** — if `activation.experience` is enabled but no step is human-capturable, the exercise would be empty (a `warn` telling you to disable the projection or add a human step).
- **Loop projection needs a spec** — if `activation.loop.enabled` is true but `activation.loop.spec` is absent, `frameworkToKnowledgeLoop` can't synthesize sources/eval (a `warn`).
`error` issues block; `warn` issues are advisory. `defineFramework(input)` is the parse-or-throw convenience used by in-repo catalog literals.
### The catalog
In-repo reference methods live as typed literals in `features/frameworks/lib/catalog/` (e.g. `storm.ts`, `design-thinking.ts`, `ideation.ts`, `decision.ts`), each authored with `defineFramework(...)`. `storm.ts` captures STORM as two frameworks — `storm-research` (the flagship automated roundtable + cited report + self-feeding research loop) and `co-storm-roundtable` (a lighter human-in-the-loop steering variant) — both projecting to all the surfaces their `activation` opts into.
## API Reference
### Compilers
| Function | Location | Signature → returns |
| --- | --- | --- |
| `frameworkToExercise(framework)` | `features/frameworks/lib/framework-to-exercise.ts` | `(framework: FrameworkDefinition) => ExerciseFrameworkDefinition \| null` — `null` when no human-capturable step survives |
| `frameworkToSkill(framework)` | `features/frameworks/lib/framework-to-skill.ts` | `(framework: FrameworkDefinition) => FrameworkSkill \| null` — `null` when `activation.skill.enabled === false` |
| `frameworkToKnowledgeLoop(framework)` | `features/frameworks/lib/framework-to-knowledge-loop.ts` | `(framework: FrameworkDefinition) => FrameworkKnowledgeLoop \| null` — `null` when the loop projection is opted out |
`FrameworkSkill` is a superset of the fields `amble_upsert_skill` / `CreateSkillInput` consume, so the result can be upserted to a tenant **or** seeded into a `BundleManifest` unchanged:
```ts
interface FrameworkSkill {
slug: string;
name: string;
description: string;
category: string;
instructions: string; // the rendered markdown playbook
metadata: { manifest: SkillManifestV1; frameworkSlug: string };
}
```
`FrameworkKnowledgeLoop` always returns the merged `input` (so a caller can inspect a failing merge) alongside the parsed `definition` and the `validation` report:
```ts
interface FrameworkKnowledgeLoop {
definition: KnowledgeLoopDefinition | null; // null when the merge is schema-invalid
input: KnowledgeLoopDefinitionInput;
validation: KnowledgeLoopValidation;
}
```
### Schema & validation
| Function / Export | Location | Purpose |
| --- | --- | --- |
| `FrameworkDefinitionSchema` | `features/frameworks/lib/types.ts` | Zod v4 schema for the `framework` entity content / capture shape |
| `validateFramework(input)` | Same | Parse + cross-field invariant check → `FrameworkValidation` (`error` blocks, `warn` advises) |
| `defineFramework(input)` | Same | Parse-or-throw convenience for in-repo catalog literals |
| `FrameworkStepSchema`, `FrameworkArtifactSchema`, `FrameworkActivationSchema`, `FrameworkOriginSchema` | Same | The sub-schemas |
### Types & constants
| Type / Constant | Location | Purpose |
| --- | --- | --- |
| `FrameworkDefinition` / `FrameworkDefinitionInput` | `features/frameworks/lib/types.ts` | Output / input inference of the schema |
| `FrameworkStep` | Same | One ordered method phase |
| `FrameworkActivation`, `FrameworkArtifact`, `FrameworkOrigin` | Same | Activation / artifact / provenance shapes |
| `FrameworkValidation`, `FrameworkIssue` | Same | Validation result + issue (`severity`, `code`, `message`) |
| `FRAMEWORK_CATEGORIES`, `FRAMEWORK_ACTORS`, `FRAMEWORK_INPUT_TYPES`, `FRAMEWORK_PARTICIPANTS` | Same | The enumerated vocabularies |
| `FrameworkCategory`, `FrameworkActor`, `FrameworkInputType`, `FrameworkParticipant` | Same | Their inferred union types |
The barrel `features/frameworks/lib/index.ts` re-exports the three compilers, their result types, and everything from `types.ts`.
## For Agents
Frameworks reach agents through the **skill projection** — a captured method becomes a `runSkill`-able, MCP-exportable skill with no extra wiring.
### Running a framework as a skill — `run-storm-research`
The flagship example is STORM. `storm-research`'s `activation.skill` declares `slug: "run-storm-research"`, the `research` tool group, the tools the method touches (`exaResearch`, `webSearch`, `createEntity`, `createRelation`, `submitResponse`), `exposeViaMcp: true`, and three delegations (`research-expert` ×N parallel, `research-moderator`, `research-writer`). `frameworkToSkill()` lowers that into a `SkillManifestV1` and an ordered markdown playbook so an agent can:
1. See `run-storm-research` in its skill index (the framework's `summary` + `whenToUse` trigger cues drive the index entry).
2. `loadSkill("run-storm-research")` to pull the full STORM procedure — discover perspectives → warm-start the roundtable → run the discourse loop → surface unknown unknowns → write the cited report.
3. Execute through the normal chat → session → agent path, delegating to the perspective experts and moderator as the manifest declares, writing `perspective` / `source` / `research-question` / `research-report` records as each step's `produces` directs.
Because the same framework also activates a **loop**, `run-storm-research` doubles as the loop's operating method (`frameworkToKnowledgeLoop` seeds `synthesis.skills` with the framework's skill slug), so the cited-research sweep can run on a weekly cadence and re-enter its own gap research-questions on each pass.
### Capturing a new method
To add a method, author a `FrameworkDefinition` — that's the entire authoring surface. Two paths:
- **In-repo reference method:** add a `defineFramework({ … })` literal under `features/frameworks/lib/catalog/`. Co-locate a `.test.ts` (every catalog file already pairs one) asserting `validateFramework(def).ok` and the projection shapes you care about.
- **Tenant-authored method:** create a `framework` entity whose `content` matches `FrameworkDefinitionSchema`. The same compilers run over the row.
When capturing, declare each step's `actor` honestly — that single field decides which surfaces the step shows up in:
- A step a **person** does → `actor: "human"` with the right `inputType`. It becomes an exercise input.
- A step an **agent** does → `actor: "agent"`, `inputType: "none"`, with `toolHints` and `produces`. It becomes skill instructions and loop synthesis work, and is dropped from the exercise.
- A step **either** can do → `actor: "both"`. It appears in *both* projections.
Then opt the method into the surfaces that fit via `activation` (a pure capture method might enable only `experience`; an autonomous research method enables all three) and run `validateFramework` — heed the `warn`s, which tell you when a projection would be empty or under-specified.
## Design Decisions
**A framework is data, not a runtime.** The strongest temptation here was a "framework engine" that runs methods. That would be a fourth executor competing with exercises, skills, and loops. Instead the module is a schema plus three pure compilers — it *says* a method once and lowers it into runtimes that already exist. This is the no-parallel-systems rule applied literally: capture once, project into the primitives we own.
**`actor` on the step is the projection switch.** Rather than authoring three parallel step lists (one per surface), there is *one* ordered `steps[]` and each step declares who performs it. The compilers filter by `actor` — human steps survive into the exercise, agent steps into the skill/loop, `both` into all. One spine, three lowerings; the method's order and intent are stated exactly once.
**Compilers return `null` (or a validation report), never a half-built surface.** `frameworkToExercise` returns `null` when no human step survives, so a research-only method doesn't ship an empty runner. `frameworkToSkill`/`frameworkToKnowledgeLoop` return `null` when their projection is opted out. `frameworkToKnowledgeLoop` additionally returns the merged input + `validateKnowledgeLoop` report so a malformed merge is *inspectable*, not a thrown surprise.
**The loop projection requires an author spec.** A generic framework can't invent a sound knowledge loop — its sources, record type, eval rubric, and research-iteration budget are domain decisions. So `activation.loop.spec` is passed through verbatim and validated by the loop's own validator after the compile. The framework supplies only what it can legitimately derive (goal, synthesis from agent steps, its own skill); the author supplies the rest. Enabling the loop without a spec is a `warn`, not a silent empty loop.
**Skill instructions are rendered deterministically.** `frameworkToSkill` emits the playbook in stable order (title → summary → origin → outcome → when-to-use → parameters → procedure → output) so the prompt prefix stays byte-stable across runs and Anthropic prompt-caching stays warm.
**The capture shape *is* an entity content schema.** `FrameworkDefinition` doubles as the `framework` entity type's `content` JSONB schema (Critical Rule 2 — entity types are DB-driven). A captured method is a row anyone or any agent can author with `createEntity`, not a hardcoded TypeScript registry.
## Related Modules
- **Exercises** (`features/exercises/`) — the human-experience runtime `frameworkToExercise()` targets. See [Exercises](/docs/features/exercises).
- **Skills** (`features/skills/`) — the agent-skill runtime `frameworkToSkill()` targets; the emitted `SkillManifestV1` rides the same readiness, export, and `runSkill` paths as any other skill. See [Skills](/docs/features/skills).
- **Loops** (`features/loops/`) — `frameworkToKnowledgeLoop()` reuses `KnowledgeLoopDefinition` + `validateKnowledgeLoop`, which compile to the goal-loop runner. See [Loops](/docs/features/loops) and [Knowledge Loops](/docs/features/knowledge-loops).
- **Entities** (`features/entities/`) — the `framework` entity type whose content schema is `FrameworkDefinition`; agent + producer steps create entity records via the entity tools. See [Entity System](/docs/features/entity-system).
- **Agents** (`features/agents/`) — specialist agents (`research-expert`, `research-moderator`, `research-writer`) that a framework's skill delegates to.