Closes #4600 — TUI shell safety: add CRITICAL danger level, update pattern table Closes #4523 — SandboxStrategyProtocol name, write() return type DiffEntry, registration config keys Closes #4382 — validation attach --key value format, SemanticEmbeddingStrategy v1 note, SpecStrategyAdapter doc Closes #4554 — skill YAML: remove skill: wrapper, fix agent_skills_dirs -> agent_skill_folders Closes #3675 — ACMS reference doc: remove resolved v1 limitations, update strategy interface examples Fixes invariant precedence pseudocode: plan > action > project > global (was missing action tier)
12 KiB
ACMS v1 Context Assembly Pipeline
This document covers the Adaptive Context Management System (ACMS) v1 context assembly pipeline, which integrates UKO (Unified Knowledge Ontology) and CRP (Context Request Protocol) components with pluggable context strategies.
Overview
The ACMS pipeline assembles context fragments into a budget-constrained payload for actor consumption. It supports multiple context strategies that determine how fragments are ranked and selected to fit within the token budget. The pipeline implements the spec's 10-component architecture across three phases: Strategy Orchestration, Fragment Fusion, and Context Finalization.
| Component | Description |
|---|---|
ContextFragment |
A single piece of context with UKO node, relevance, provenance, and tier |
FragmentProvenance |
Provenance trace linking a fragment back to its originating resource |
ContextBudget |
Token budget with max and reserved tokens |
ContextPayload |
Assembled payload with fragments, token count, budget usage, context hash, and provenance map |
ACMSPipeline |
Pipeline that applies a context strategy to assemble payloads |
Context Fragments
Each ContextFragment represents a single piece of context:
| Field | Type | Description |
|---|---|---|
fragment_id |
str |
ULID identifier (auto-generated) |
uko_node |
str |
UKO URI of the source node (required) |
content |
str |
Rendered text content (max 1,000,000 chars) |
detail_depth |
int |
Resolved depth: 0 (MODULE_LISTING) through 9 (FULL_SOURCE), default 0 |
token_count |
int |
Actual token count of content (required, >= 0) |
relevance_score |
float |
Score from 0.0 to 1.0 (default 0.5) |
provenance |
FragmentProvenance |
Provenance trace (required) |
tier |
str |
Priority tier: "hot", "warm", "cold" (default "warm") |
metadata |
dict[str, str] |
Arbitrary key-value metadata (max 64 entries) |
created_at |
datetime |
UTC timestamp (auto-generated) |
FragmentProvenance
| Field | Type | Description |
|---|---|---|
resource_uri |
str |
URI of the originating resource (required) |
location |
str |
Location within the resource (default "") |
resource_type |
str |
Type of originating resource (default "unknown") |
Tiers
Fragments are classified into three tiers:
- hot -- Critical context that should always be included first
- warm -- Standard context included when budget allows (default)
- cold -- Low-priority context included only if space remains
Note: In v1, tiers are sort-priority labels used for ranking during assembly. They do not represent storage tiers with retention policies, promotion, or eviction semantics. Full hot/warm/cold storage-tier semantics will be implemented separately in
ContextTierService.
Budget Management
The ContextBudget defines the token budget for assembly:
max_tokens-- Maximum total tokens (default 4096, must be >= 1)reserved_tokens-- Tokens reserved for system prompt (default 512, must be >= 0)available_tokens-- Computed asmax_tokens - reserved_tokens
Validation: reserved_tokens must be strictly less than max_tokens.
The pipeline never exceeds the available token budget. Fragments that would push the total over the limit are skipped, with early termination once the budget is fully consumed.
Context Payload
The assembled ContextPayload includes:
| Field | Type | Description |
|---|---|---|
payload_id |
str |
ULID identifier (auto-generated) |
plan_id |
str |
Plan this payload was assembled for |
fragments |
tuple[ContextFragment, ...] |
Selected fragments |
total_tokens |
int |
Sum of fragment token_count values |
budget |
ContextBudget |
Budget used for assembly |
budget_used |
float |
Fraction of budget consumed (0.0-1.0) |
strategies_used |
tuple[str, ...] |
Strategy names that contributed (immutable) |
context_hash |
str |
SHA-256 hash of assembled content |
preamble |
str | None |
Optional structure summary |
provenance_map |
dict[str, Any] |
Fragment ID -> provenance mapping |
assembled_at |
datetime |
UTC timestamp |
Properties:
is_within_budget--Trueif total tokens do not exceed available budgetremaining_tokens-- Tokens still available in the budget
Context Strategies
Strategies implement the ContextStrategy protocol (defined in cleveragents.domain.models.acms.strategy):
| Method | Description |
|---|---|
name (property) |
Strategy identifier |
capabilities (property) |
StrategyCapabilities dataclass |
can_handle(request: ContextRequest, backends: BackendSet) |
Confidence (0.0-1.0) for handling a request |
assemble(request: ContextRequest, backends: BackendSet, budget: int, plan_context: PlanContext) |
Execute the strategy; must respect the budget |
explain() |
Human-readable explanation |
StrategyCapabilities fields: uses_text, uses_vector, uses_graph, uses_temporal, uko_levels, resource_types, quality_score.
Relevance (default)
Sorts fragments by relevance_score descending. Highest-relevance
fragments are selected first until the budget is exhausted.
Recency
Sorts fragments by created_at descending (datetime comparison, not
string comparison). Most recent fragments are selected first.
Tiered
Groups fragments by tier priority (hot > warm > cold). Within
each tier, fragments are sorted by relevance_score descending.
10-Component Pipeline Architecture
The pipeline runs three phases:
All 10 components have explicit Protocol definitions and injectable
Default implementations. Inject custom components via the
ACMSPipeline constructor.
Phase 1 — Strategy Orchestration: StrategySelector,
BudgetAllocator, StrategyExecutor (v1 defaults: single-strategy
selection, full-budget allocation, synchronous execution)
Phase 2 — Fragment Fusion: FragmentDeduplicator,
DetailDepthResolver, FragmentScorer, BudgetPacker,
FragmentOrderer (v1 defaults: pass-through / no-op)
Phase 3 — Context Finalization: PreambleGenerator,
SkeletonCompressor (v1 defaults: no-op preamble, identity compression)
v1 Known Limitations
The following are known deviations from the full specification, accepted for the v1 implementation with a path to spec conformance in future milestones:
| Area | v1 Behaviour | Spec Target | Planned |
|---|---|---|---|
| Pipeline components | All 10 Protocol + Default classes defined; defaults are pass-through stubs | Production implementations (parallel execution, dedup, scoring, compression) | Future milestone |
| Tiers | Sort-priority labels for ranking (hot > warm > cold) |
Storage tiers with retention policies, promotion/demotion | ContextTierService in future milestone |
StrategySelector.select() |
(strategies, request: dict) |
(strategies, request: ContextRequest, backends: BackendSet) — spec §42666 |
M6 strategy registry aligns signatures |
BudgetAllocator.allocate() |
(candidates, total_budget: int) |
(candidates, total_budget, request: ContextRequest) — spec §42682 |
Future milestone |
StrategyExecutor.execute() |
(allocations, fragments, budget) |
(allocations, request: ContextRequest, backends: BackendSet, plan_context: PlanContext) — spec §42698 |
M6 strategy registry aligns signatures |
BudgetPacker.pack() |
(fragments: Sequence[ContextFragment], budget: ContextBudget) |
(scored_fragments: list[ScoredFragment], budget: int, detail_level_maps) — spec §42763 |
Future milestone |
SkeletonCompressor.compress() |
(fragments: tuple, skeleton_budget: int) -> tuple |
(parent_context: AssembledContext, child_focus: list[str], skeleton_budget: int) -> AssembledContext — spec §42811 |
Future milestone |
DetailDepthResolver.resolve() |
Missing budget parameter |
resolve(fragments, budget) |
Future milestone |
FragmentScorer.score() |
Missing plan_context param; returns Sequence[ContextFragment] |
Returns list[ScoredFragment] with plan_context |
Future milestone |
PreambleGenerator.generate() |
Missing strategies_used, budget_used, max_tokens params |
Full parameter set per spec §42795 | Future milestone |
provenance_map keying |
Keyed by fragment_id (ULID) |
Spec keys by uko_node (UKO URI) |
Under review — fragment_id avoids collisions when multiple fragments share a uko_node |
| CRP base model mutability | FragmentProvenance, ContextFragment, ContextBudget, AssembledContext are frozen=True |
CRP models may be mutable | Core types extend CRP bases via Pydantic v2 inheritance; freezing CRP bases ensures consistent immutability across the hierarchy. No CRP consumer mutates these instances. |
Extension Points
Register custom context strategies at runtime using the spec-aligned ContextStrategy protocol from cleveragents.domain.models.acms.strategy. Custom strategies are registered via SpecStrategyAdapter:
from cleveragents.domain.models.acms.strategy import (
ContextStrategy,
StrategyCapabilities,
ContextRequest,
BackendSet,
PlanContext,
)
from cleveragents.domain.models.core.context_fragment import ContextFragment
from cleveragents.application.services.acms_service import ACMSPipeline, SpecStrategyAdapter
class MyCustomStrategy:
@property
def name(self) -> str:
return "custom"
@property
def capabilities(self) -> StrategyCapabilities:
return StrategyCapabilities(uses_text=True, quality_score=0.5)
def can_handle(self, request: ContextRequest, backends: BackendSet) -> float:
return 0.5
def assemble(
self,
request: ContextRequest,
backends: BackendSet,
budget: int,
plan_context: PlanContext,
) -> list[ContextFragment]:
# Custom retrieval logic using backends
return []
def explain(self) -> str:
return "Custom strategy description."
pipeline = ACMSPipeline()
adapter = SpecStrategyAdapter(MyCustomStrategy())
pipeline.register_strategy("custom", adapter)
Example Usage
from cleveragents.domain.models.core.context_fragment import (
ContextBudget,
ContextFragment,
ContextPayload,
FragmentProvenance,
)
from cleveragents.application.services.acms_service import ACMSPipeline
# Create fragments
fragments = [
ContextFragment(
uko_node="project://myapp/src/main.py",
content="def main(): ...",
relevance_score=0.9,
token_count=100,
tier="hot",
provenance=FragmentProvenance(resource_uri="project://myapp/src/main.py"),
),
ContextFragment(
uko_node="project://myapp/decisions/001",
content="Use async IO",
relevance_score=0.7,
token_count=50,
tier="warm",
provenance=FragmentProvenance(resource_uri="project://myapp/decisions/001"),
),
ContextFragment(
uko_node="project://myapp/README.md",
content="README.md content",
relevance_score=0.3,
token_count=200,
tier="cold",
provenance=FragmentProvenance(resource_uri="project://myapp/README.md"),
),
]
# Define budget
budget = ContextBudget(max_tokens=2048, reserved_tokens=256)
# Assemble with default relevance strategy
pipeline = ACMSPipeline()
payload = pipeline.assemble(plan_id="plan-1", fragments=fragments, budget=budget)
print(f"Fragments selected: {len(payload.fragments)}")
print(f"Total tokens: {payload.total_tokens}")
print(f"Budget used: {payload.budget_used:.1%}")
print(f"Within budget: {payload.is_within_budget}")
print(f"Remaining: {payload.remaining_tokens}")
print(f"Context hash: {payload.context_hash[:16]}...")
print(f"Strategies: {payload.strategies_used}")