docs: document v3.8.1 changes — skeleton context inheritance, actor YAML-first path, checkpoint triggers #3696
@@ -7,6 +7,63 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
||||
|
||||
---
|
||||
|
||||
## [3.8.1] — 2026-04-05
|
||||
|
||||
### Added
|
||||
|
||||
- **ACMS — Skeleton context inheritance for child plans**: `ACMSPipeline.assemble()`
|
||||
and `ContextAssemblyPipeline.assemble()` now accept `skeleton_ratio: float = 0.15`
|
||||
and `parent_fragments: tuple[ContextFragment, ...] | None = None` parameters.
|
||||
When `parent_fragments` is provided, the `SkeletonCompressor` is invoked in
|
||||
Phase 3 to compress parent context to the skeleton budget
|
||||
(`int(budget.available_tokens * skeleton_ratio)`). Compressed skeleton fragments
|
||||
are returned in the new `ContextPayload.skeleton_fragments` field, enabling child
|
||||
plans to inherit a compressed representation of the parent plan's accumulated
|
||||
context. Includes 4 Behave unit test scenarios and 1 Robot Framework integration
|
||||
test. (#3563)
|
||||
|
||||
### Fixed
|
||||
|
||||
- **CLI — `agents actor add` YAML-first persistence path**: The `actor add` command
|
||||
now routes through `ActorRegistry.add()` instead of the legacy
|
||||
`registry.upsert_actor()` path, ensuring the original YAML text,
|
||||
`schema_version`, and `compiled_metadata` are preserved in the database.
|
||||
A `_load_config_text()` helper returns both raw text and parsed dict; the
|
||||
service fallback path (no registry) is unchanged. (#3426)
|
||||
|
||||
- **CLI — `agents diagnostics` extended to all 9 providers**: The diagnostics
|
||||
command now checks all nine supported providers (OpenAI, Anthropic, Google,
|
||||
Azure OpenAI, OpenRouter, Gemini, Cohere, Groq, Together) for credential
|
||||
presence and reports which are configured. (#3422)
|
||||
|
||||
- **Executor — Automatic per-tool-write and event-based checkpoint triggers**:
|
||||
The execution engine now creates checkpoints automatically at four trigger
|
||||
points: `on_tool_write` (before each write-tool call), `on_tool_write_complete`
|
||||
(after each write-tool call), `on_subplan_spawn` (before first subplan attempt),
|
||||
and `on_error` (when Execute phase fails). Triggers are configurable via
|
||||
`core.checkpoints.auto_create_on`. `CheckpointService` is injected as an
|
||||
optional parameter into `ToolRunner`, `SubplanExecutionService`, and
|
||||
`PlanExecutor`. (#3439)
|
||||
|
||||
- **CLI — `--container-id` flag on `agents resource add container-instance`**:
|
||||
The `resource add` command for container-instance resources now accepts a
|
||||
`--container-id` flag to specify the Docker/Podman container ID explicitly,
|
||||
rather than requiring auto-discovery. (#2598)
|
||||
|
||||
- **Domain — `ToolLifecycle` execute hook**: The `ToolLifecycle` model now
|
||||
includes the `execute` hook to satisfy the spec's four-stage lifecycle
|
||||
(`activate → validate → execute → deactivate`). (#2820)
|
||||
|
||||
- **MCP — `MCPToolResult.data` type annotation**: The `data` field type
|
||||
annotation on `MCPToolResult` has been corrected to match the MCP 1.4.0
|
||||
content list format. (#2743)
|
||||
|
||||
- **CLI — `automation-profile list` output structure**: The `automation-profile
|
||||
list` command now renders the correct Rich table structure and output format
|
||||
matching the specification. (#2064)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.0] — 2026-04-05
|
||||
|
||||
### Added
|
||||
|
||||
@@ -68,6 +68,25 @@ actor = registry.get("openai/gpt-4o")
|
||||
|
||||
In-memory registry for actor configurations. Thread-safe.
|
||||
|
||||
### `ActorRegistry.add()` — YAML-first persistence path
|
||||
|
||||
The `agents actor add` CLI command routes through `ActorRegistry.add()` to
|
||||
ensure the original YAML text, `schema_version`, and `compiled_metadata` are
|
||||
preserved in the database.
|
||||
|
||||
```python
|
||||
registry.add(yaml_text="name: openai/gpt-4o\n...", update=False)
|
||||
```
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `yaml_text` | `str` | Raw YAML text of the actor configuration |
|
||||
| `update` | `bool` | When `True`, overwrite an existing actor with the same name |
|
||||
|
||||
> **Note:** The legacy `registry.upsert_actor()` path is still available for
|
||||
> programmatic use but is no longer called by the CLI. Use `registry.add()`
|
||||
> for all new code that needs to persist actor configurations.
|
||||
|
||||
---
|
||||
|
||||
## `compile_actor` / `CompiledActor`
|
||||
|
||||
@@ -217,6 +217,34 @@ Key UKO capabilities:
|
||||
- **Graph persistence** — `UKOGraphPersistence` serialises/restores state via
|
||||
JSON-file or in-memory backends across application restarts
|
||||
|
||||
### Skeleton Context Inheritance
|
||||
|
||||
When a parent plan spawns a child plan, the ACMS pipeline compresses the
|
||||
parent's accumulated context fragments into a **skeleton** for propagation.
|
||||
This prevents child plans from starting with an empty context while keeping
|
||||
the inherited payload within budget.
|
||||
|
||||
```
|
||||
Parent Plan Context
|
||||
│
|
||||
▼ SkeletonCompressor (skeleton_ratio=0.15)
|
||||
│
|
||||
▼ skeleton_fragments (top-relevance, budget-bounded)
|
||||
│
|
||||
▼ Child Plan ContextPayload.skeleton_fragments
|
||||
```
|
||||
|
||||
Key parameters:
|
||||
|
||||
| Parameter | Default | Description |
|
||||
|-----------|---------|-------------|
|
||||
| `skeleton_ratio` | `0.15` | Fraction of the child's token budget allocated to inherited skeleton context |
|
||||
| `parent_fragments` | `None` | Parent plan's accumulated `ContextFragment` tuple; `None` disables skeleton compression |
|
||||
|
||||
The `ContextPayload.skeleton_fragments` field carries the compressed result.
|
||||
See [`docs/reference/skeleton_compressor.md`](reference/skeleton_compressor.md)
|
||||
for the full compression algorithm and configuration reference.
|
||||
|
||||
---
|
||||
|
||||
## Invariant Reconciliation
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
# ACMS Skeleton Context Inheritance
|
||||
|
||||
## Overview
|
||||
|
||||
When a parent plan spawns a child plan, the child plan starts with no
|
||||
accumulated context. Without inherited context, the child's actor must
|
||||
re-discover information the parent already gathered, wasting tokens and
|
||||
execution time.
|
||||
|
||||
**Skeleton context inheritance** solves this by compressing the parent
|
||||
plan's accumulated context fragments into a budget-bounded **skeleton**
|
||||
that is passed to the child plan's context assembly pipeline.
|
||||
|
||||
This feature was introduced in v3.8.1 (issue #3563).
|
||||
|
||||
---
|
||||
|
||||
## How It Works
|
||||
|
||||
```
|
||||
Parent Plan
|
||||
├── ContextFragment (relevance=0.9, tokens=800)
|
||||
├── ContextFragment (relevance=0.7, tokens=600)
|
||||
└── ContextFragment (relevance=0.3, tokens=400)
|
||||
│
|
||||
▼ SkeletonCompressor
|
||||
│ skeleton_ratio=0.15
|
||||
│ skeleton_budget = available_tokens * 0.15
|
||||
│
|
||||
▼ Compressed skeleton (top-relevance, budget-bounded)
|
||||
│
|
||||
Child Plan ContextPayload
|
||||
├── fragments — child's own assembled context
|
||||
└── skeleton_fragments — inherited from parent (compressed)
|
||||
```
|
||||
|
||||
The `SkeletonCompressor` sorts parent fragments by relevance descending
|
||||
and greedily packs them into the skeleton budget. The compressed result
|
||||
is returned in `ContextPayload.skeleton_fragments`.
|
||||
|
||||
---
|
||||
|
||||
## API
|
||||
|
||||
### `ACMSPipeline.assemble()`
|
||||
|
||||
```python
|
||||
from cleveragents.application.services.acms_service import ACMSPipeline
|
||||
from cleveragents.domain.models.core.context_fragment import ContextFragment
|
||||
|
||||
pipeline = ACMSPipeline()
|
||||
payload = pipeline.assemble(
|
||||
plan_id="child-plan-id",
|
||||
fragments=child_fragments,
|
||||
budget=budget,
|
||||
strategy="relevance",
|
||||
# Skeleton inheritance parameters:
|
||||
skeleton_ratio=0.15, # fraction of budget for skeleton
|
||||
parent_fragments=parent_frags, # parent's accumulated fragments
|
||||
)
|
||||
|
||||
# Access inherited skeleton
|
||||
for frag in payload.skeleton_fragments:
|
||||
print(frag.uko_node, frag.token_count)
|
||||
```
|
||||
|
||||
**New parameters (v3.8.1+):**
|
||||
|
||||
| Parameter | Type | Default | Description |
|
||||
|-----------|------|---------|-------------|
|
||||
| `skeleton_ratio` | `float` | `0.15` | Fraction of `budget.available_tokens` allocated to the skeleton |
|
||||
| `parent_fragments` | `tuple[ContextFragment, ...] \| None` | `None` | Parent plan's accumulated fragments; `None` disables skeleton compression |
|
||||
|
||||
When `parent_fragments` is `None`, `ContextPayload.skeleton_fragments` is
|
||||
an empty tuple and no compression is performed.
|
||||
|
||||
### `ContextAssemblyPipeline.assemble()`
|
||||
|
||||
The same `skeleton_ratio` and `parent_fragments` parameters are available
|
||||
on `ContextAssemblyPipeline.assemble()` for consistency across the two
|
||||
pipeline entry points.
|
||||
|
||||
### `ContextPayload.skeleton_fragments`
|
||||
|
||||
```python
|
||||
@dataclass(frozen=True)
|
||||
class ContextPayload:
|
||||
...
|
||||
skeleton_fragments: tuple[ContextFragment, ...] = ()
|
||||
```
|
||||
|
||||
Immutable tuple of compressed parent context fragments. Empty when no
|
||||
parent context was provided.
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
### Default skeleton ratio
|
||||
|
||||
The default `skeleton_ratio` of `0.15` means the skeleton budget is 15%
|
||||
of the child plan's available token budget. For a 4096-token budget with
|
||||
512 reserved tokens, the skeleton budget is:
|
||||
|
||||
```
|
||||
skeleton_budget = (4096 - 512) * 0.15 = 537 tokens
|
||||
```
|
||||
|
||||
### Per-project override
|
||||
|
||||
Set a custom skeleton ratio for a project's context policy:
|
||||
|
||||
```bash
|
||||
agents project context set --skeleton-ratio 0.20
|
||||
```
|
||||
|
||||
This overrides the default for all child plans spawned within that project.
|
||||
|
||||
---
|
||||
|
||||
## Relationship to `SkeletonCompressorService`
|
||||
|
||||
The `SkeletonCompressorService` (registered in the DI container as
|
||||
`skeleton_compressor_service`) is the underlying service that performs
|
||||
the compression. The ACMS pipeline calls it internally during Phase 3
|
||||
(Context Finalization).
|
||||
|
||||
For direct use of the compressor outside the pipeline, see
|
||||
[`docs/reference/skeleton_compressor.md`](../reference/skeleton_compressor.md).
|
||||
|
||||
---
|
||||
|
||||
## Subplan Spawning Integration
|
||||
|
||||
The `SubplanService.spawn()` method passes the parent plan's accumulated
|
||||
context as `parent_fragments` when assembling the child plan's initial
|
||||
context. This is the primary production path for skeleton inheritance.
|
||||
|
||||
```python
|
||||
# Conceptual — internal to SubplanService
|
||||
child_payload = pipeline.assemble(
|
||||
plan_id=child_plan_id,
|
||||
fragments=child_fragments,
|
||||
budget=child_budget,
|
||||
parent_fragments=parent_plan.accumulated_fragments,
|
||||
skeleton_ratio=project_context_policy.skeleton_ratio,
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **`skeleton_ratio=0.0`** disables compression entirely — all parent
|
||||
fragments pass through unchanged. Use this only when you want the full
|
||||
parent context in the child (rare; usually exceeds budget).
|
||||
- **`skeleton_ratio=1.0`** keeps only the single highest-relevance fragment.
|
||||
Useful for very tight token budgets.
|
||||
- The skeleton budget is computed from `budget.available_tokens`, not
|
||||
`budget.max_tokens`. Reserved tokens are already excluded.
|
||||
- Skeleton fragments are **not** deduplicated against the child's own
|
||||
assembled fragments. If the same resource appears in both, it will be
|
||||
present twice. A deduplication pass is planned for a future milestone.
|
||||
Reference in New Issue
Block a user