6-Stage Pipeline

Documentation > Architecture > 6-Stage Pipeline

Guide IDA-2
AudienceDevelopers, Contributors
PrerequisitesA-1: Chipset Architecture
Time20 minutes
DifficultyAdvanced

6-Stage Pipeline

Every time a Claude Code session begins, or a GSD phase transitions, or a skill context needs refreshing, a question arises: which skills should occupy the context window right now? The answer is never obvious. A project might have dozens of installed skills across project and user scope. The context window has a finite token budget. Some skills conflict. Some are irrelevant. Some are critical. The 6-stage pipeline exists to answer this question correctly, every time, without human intervention.

In the chipset architecture, this pipeline is Agnus — the context manager. Just as the original Amiga's Agnus coordinated DMA channels to move the right data into memory at the right cycle, this pipeline coordinates skill loading to place the right knowledge into the context window at the right moment. The pipeline is deterministic: given the same inputs (installed skills, current context, agent profile, cache state), it produces the same output. This makes it testable, debuggable, and predictable.

Pipeline Overview

The pipeline processes skills through six stages in strict order: Score → Resolve → ModelFilter → CacheOrder → Budget → Load. Each stage receives the output of the previous stage, applies a single transformation, and passes its result forward. No stage may bypass another, and no stage may reach back to modify a previous stage's output. This linearity is intentional — it makes the pipeline's behavior comprehensible and each stage independently testable.

The implementation lives in src/application/stages/, with each stage as a separate TypeScript module: score-stage.ts, resolve-stage.ts, model-filter-stage.ts, cache-order-stage.ts, budget-stage.ts, and load-stage.ts. An index.ts barrel export wires them together. Test files (score-stage.test.ts, model-filter-stage.test.ts, budget-stage.test.ts, cache-order-stage.test.ts) verify each stage in isolation.

Stage 1: Score

Input: All installed skills (from both .claude/skills/ and ~/.claude/skills/) plus the current session context (active files, recent commands, user intent).

Mechanism: The scorer evaluates each skill against the current context using three signals. Embedding similarity compares the skill's description and trigger intents against the current context using local embeddings (when available) or heuristic keyword matching (as fallback). Trigger matching checks whether the active files match the skill's file globs, whether the user's expressed intent matches the skill's intent triggers, and whether the session context matches the skill's context keywords. Specificity weighting favors skills with narrow, precise triggers over broad, general ones — a skill triggered by "*.test.tsx" in a testing context scores higher than one triggered by "*.ts".

Output: A scored skill list where each skill has a relevance score from 0 to 100. Skills below their configured threshold (default 0.5, mapped to a score of 50) are eliminated entirely.

Design rationale: Scoring happens first because it is the highest-leverage filter. If 40 skills are installed but only 8 are relevant to the current context, the remaining stages only need to process 8 candidates. Scoring before resolution also prevents wasted work: there is no point resolving scope conflicts for skills that will never load.

Source: src/application/stages/score-stage.ts, src/activation/ (activation scoring), src/embeddings/ (embedding infrastructure)

Stage 2: Resolve

Input: Scored skills from both user scope (~/.claude/skills/) and project scope (.claude/skills/).

Mechanism: When both scopes contain a skill with the same name, the project-level version shadows the user-level version. This is not a merge — the user-level skill is dropped entirely. The project-level skill is assumed to be more specific to the current work context. If the extends field is present (skill inheritance), the resolver also computes the effective skill by composing the parent and child, with child content overriding parent content for the same sections.

Output: A deduplicated skill list containing only one version of each skill name, with inheritance resolved.

Design rationale: Resolution happens after scoring so that only relevant skills are resolved. A user might have 30 user-level skills, but if only 2 are relevant to the current context, only those 2 need scope resolution. The "project shadows user" rule is a deliberate simplification: rather than attempting a complex merge of two skill versions, the system assumes that if a project has created a skill with the same name as a user-level skill, the project version is the one that should be used. This prevents surprising behavior where user-level defaults bleed into project-specific workflows.

Source: src/application/stages/resolve-stage.ts, src/composition/ (dependency graph and resolver for skill inheritance)

Stage 3: ModelFilter

Input: Resolved skills plus the current agent profile (executor, planner, researcher, verifier, or the default interactive profile).

Mechanism: Different agent profiles have different token budgets and different skill needs. An executor agent working through a phase plan needs actionable skills (code patterns, testing practices, deployment procedures) but not planning skills. A planner agent needs architectural skills and constraint awareness but not debugging utilities. The ModelFilter classifies each skill into tiers — critical (must load for this profile), standard (should load if budget allows), or optional (nice to have) — based on the intersection of the skill's triggers and the agent's profile requirements.

Output: A profile-appropriate skill subset with tier classifications attached to each skill.

Design rationale: Filtering by agent profile prevents context pollution. When GSD forks a subagent for phase execution, that subagent starts with a clean context window. Loading every relevant skill into that clean context defeats the purpose of the fork. The ModelFilter ensures the subagent gets only the skills appropriate to its role, preserving the context space for actual work. This stage exists separately from Budget because it encodes policy (what should load for this role) rather than constraint (what fits in the budget).

Source: src/application/stages/model-filter-stage.ts

Stage 4: CacheOrder

Input: Filtered skills plus the current cache state (which skills were loaded in the previous pipeline run and are still warm in memory).

Mechanism: Skills that are already warm in the cache are preferred over cold skills at equal relevance scores. The CacheOrder stage reorders the skill list to place cache-warm skills earlier in the loading sequence. It also manages cache TTL (time-to-live), marking skills as cold after a configurable period of inactivity. This is not about whether a skill loads — that is Budget's decision — but about the order in which skills are presented to the Budget stage, which processes skills sequentially until the budget is exhausted.

Output: A cache-optimized load order where warm skills precede cold skills within the same tier and relevance band.

Design rationale: Cache ordering exists because the Budget stage processes skills in sequence. If two skills have equal relevance and tier, the one that was recently active is more likely to be useful in the current session (temporal locality). By ordering warm skills first, the Budget stage naturally allocates space to recently-used skills before cold ones. This is the same principle behind the Amiga's blitter queue: operations already in flight get priority over new requests.

Source: src/application/stages/cache-order-stage.ts

Stage 5: Budget

Input: Ordered skills plus the cumulative token budget (default 15,500 characters, configurable).

Mechanism: The Budget stage walks the ordered skill list and accumulates character counts. It processes skills in tier order: all critical skills first, then standard, then optional. Within each tier, skills are processed in the order established by CacheOrder. When adding the next skill would exceed the budget, that skill and all subsequent skills in the current tier are moved to the overflow queue. Critical skills are never moved to overflow — if the critical tier alone exceeds the budget, the pipeline raises a budget violation warning (this indicates a configuration problem, not a runtime decision).

Output: Two lists: skills that fit within the budget (the load set) and skills that did not fit (the overflow queue). The overflow queue is logged so operators can see what was deferred.

Design rationale: The budget is a hard constraint, not a guideline. The context window is finite, and skills compete with the actual user conversation and code content for space. Allowing the budget to be exceeded would cause unpredictable context truncation downstream. The tier-ordered processing ensures that if budget pressure forces a tradeoff, optional skills are sacrificed before standard ones, and standard ones before critical ones. This is analogous to the Amiga's DMA priority system: display DMA (critical) could never be starved by audio DMA (standard) or blitter DMA (optional).

Source: src/application/stages/budget-stage.ts

Stage 6: Load

Input: The budgeted skill set (skills that fit within the token budget).

Mechanism: The Load stage reads each skill's content from its SKILL.md file and injects it into the Claude Code context. Skills are loaded in the order determined by previous stages. The loader validates that each skill's content has not changed since scoring (guarding against race conditions where a skill file is modified between scoring and loading). If reference materials exist in the skill's directory, the loader notes them as available for on-demand retrieval but does not inject them into the context (they would be fetched via Denise's progressive disclosure system if needed).

Output: Active skills in the Claude Code session. The pipeline is complete.

Design rationale: Loading is deliberately the last stage because it is the only stage with side effects. Every stage before Load is a pure transformation: it takes input, produces output, and modifies nothing. Load is where the rubber meets the road — where skills actually enter the context window. By keeping the effectful stage at the end, the pipeline can be run in "dry run" mode (useful for testing and debugging) by simply stopping before Stage 6. This is the same pattern used in build systems (compute the dependency graph first, execute last) and the original Amiga's copper list (compute display instructions first, execute during horizontal blank).

Pipeline Invariants

The pipeline guarantees four invariants that consumers can rely on unconditionally:

1. Budget never exceeded. The total character count of loaded skills will never exceed the configured budget. If a skill cannot fit, it goes to the overflow queue. If critical skills alone exceed the budget, the pipeline raises an error rather than silently overflowing. This invariant means the rest of the system never needs to worry about skill content causing context truncation.

2. Project skills always shadow user skills with the same name. If both .claude/skills/my-skill/ and ~/.claude/skills/my-skill/ exist, only the project version loads. The user version is not merged, not partially applied, not consulted. This invariant means project-specific customizations always take effect without worrying about user-level defaults interfering.

3. Critical skills always load before optional ones. Within the Budget stage, tier ordering is absolute. A critical skill with a low relevance score still loads before an optional skill with a high relevance score. This invariant means that marking a skill as critical is a guarantee, not a preference.

4. Cache-warm skills preferred over cold ones at equal relevance. When two skills have the same tier and similar relevance scores, the one that was recently active gets priority in the loading order. This invariant provides temporal locality without overriding importance-based decisions.

What's Next