Compare commits

...

12 Commits

Author SHA1 Message Date
HAL9000 911fe64344 docs: fix merge conflicts in v3.2.0/v3.3.0 and v3.4.0/v3.5.0 documentation PRs [AUTO-DOCS-4] 2026-04-15 15:43:16 +00:00
HAL9000 b25f34a0d4 docs: Add v3.4.0 ACMS and v3.5.0 Autonomy Hardening documentation
- Add docs/context.md: ACMS v1 context management documentation
- Add docs/autonomy.md: Autonomy hardening and A2A protocol documentation
- Update CHANGELOG.md with v3.4.0 and v3.5.0 unreleased sections
- Update mkdocs.yml navigation

[AUTO-DOCS-3]
2026-04-15 15:42:03 +00:00
HAL9000 14223dbc25 docs: update mkdocs nav and CHANGELOG for v3.2.0/v3.3.0 API docs 2026-04-14 20:04:01 +00:00
HAL9000 4167f7371a docs(api): add decision tree, invariant, checkpoint, and plan correction references (v3.2.0/v3.3.0) 2026-04-14 20:04:01 +00:00
CleverAgents Build Agent 8e0fa1733f Build: Better protection against agents editing the main working directory 2026-04-14 20:04:01 +00:00
HAL9000 f10ec45c75 docs(changelog): add v3.3.0 changelog entry for #7582 fail_fast fix 2026-04-14 20:04:01 +00:00
HAL9000 415785a358 fix(concurrency): fix SubplanExecutionService._execute_parallel() #7582
Ensure fail_fast cancels in-flight futures and reports them as CANCELLED.

Add Behave coverage that reproduces the concurrency regression.

ISSUES CLOSED: #7582
2026-04-14 20:04:01 +00:00
CleverAgents Build Agent 143538c200 Build: Made conflict resolution a more explicit part of the pr-merge agents 2026-04-14 20:04:01 +00:00
HAL9000 dafe37da7a fix(plan-lifecycle): record prompt_definition as root decision during Strategize
Fixes a bug where the root decision was recorded as 'strategy_choice' instead of
the correct 'prompt_definition' type during the Strategize phase. The decision tree
now correctly records the plan's prompt/description as the root decision, ensuring
proper decision tree structure and downstream decision evaluation.

Changes:
- Modified start_strategize() to record prompt_definition as the root decision
- Updated decision question to 'What is the plan prompt?'
- Set chosen_option to plan.description with fallback to action_name or plan_id
- Added test scenario to verify root decision type is prompt_definition

Closes #9061
2026-04-14 20:04:00 +00:00
HAL9000 b47f9440ba [AUTO-TIME-1] Update timeline: 2026-04-14 milestone status 2026-04-14 20:03:34 +00:00
HAL9000 e75db02f2c test(benchmarks): add ASV benchmarks for LangGraph and TUI 2026-04-14 12:42:25 +00:00
HAL9000 64d7b22620 docs(timeline): update milestone status and schedule adherence 2026-04-14 [AUTO-TIME-1] 2026-04-14 01:17:22 +00:00
42 changed files with 2642 additions and 103 deletions
+5 -1
View File
@@ -8,7 +8,11 @@ hidden: true
temperature: 0.2
model: anthropic/claude-sonnet-4-6
permission:
edit: allow
edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow
bash:
"*": deny
+5 -1
View File
@@ -8,7 +8,11 @@ hidden: true
temperature: 0.3
model: anthropic/claude-sonnet-4-6
permission:
edit: allow
edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow
bash:
"*": deny
+5 -1
View File
@@ -8,7 +8,11 @@ temperature: 0.2
model: anthropic/claude-sonnet-4-6
color: success
permission:
edit: allow
edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow
bash:
"*": deny
+5 -1
View File
@@ -8,7 +8,11 @@ hidden: true
temperature: 0.2
# NO MODEL SPECIFIED - inherits from caller (tier selector)
permission:
edit: allow
edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow
bash:
"*": deny
+5 -1
View File
@@ -8,7 +8,11 @@ hidden: true
temperature: 0.2
# NO MODEL SPECIFIED - inherits from caller (tier selector)
permission:
edit: allow
edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow
bash:
"*": deny
+5 -1
View File
@@ -7,7 +7,11 @@ hidden: true
temperature: 0.3
model: anthropic/claude-sonnet-4-6
permission:
edit: allow
edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow
bash:
"*": deny
+6 -1
View File
@@ -7,7 +7,11 @@ temperature: 0.2
model: anthropic/claude-sonnet-4-6
color: "#059669"
permission:
edit: allow
edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow
bash:
"*": deny
@@ -61,6 +65,7 @@ You manually fix a specific pull request. The user tells you which PR to fix. Yo
4. Fix the code — address both CI failures and review feedback.
5. Run quality gates locally (`nox -e lint`, `nox -e typecheck`, `nox -e unit_tests`, `nox -e integration_tests`).
6. Commit and push using `git-commit-helper`.
7. Clean up the isolated clone using `repo-isolator`.
## Rules
+5 -1
View File
@@ -9,7 +9,11 @@ hidden: true
temperature: 0.1
# No model specified — tier is set by the supervisor via tier selectors
permission:
edit: allow
edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow
bash:
"*": deny
+5 -1
View File
@@ -8,7 +8,11 @@ hidden: true
temperature: 0.2
# NO MODEL SPECIFIED - inherits from caller (tier selector)
permission:
edit: allow
edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow
bash:
"*": deny
+5 -1
View File
@@ -8,7 +8,11 @@ temperature: 0.2
# NO MODEL SPECIFIED - inherits from caller (tier selector)
color: warning
permission:
edit: allow
edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow
bash:
"*": deny
+5 -1
View File
@@ -8,7 +8,11 @@ temperature: 0.1
# NO MODEL SPECIFIED - inherits from caller (tier selector)
color: warning
permission:
edit: allow
edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow
bash:
"*": deny
+6 -2
View File
@@ -9,7 +9,11 @@ temperature: 0.1
model: anthropic/claude-sonnet-4-6
color: warning
permission:
edit: allow
edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow
bash:
"*": deny
@@ -46,7 +50,7 @@ permission:
# PR CI Test Fixer
You fix failing CI checks on a PR branch. You work in an isolated clone directory.
You fix failing CI checks on a PR branch. You work in an isolated clone directory`$WORK_DIR` is always a path inside `/tmp/` created by `repo-isolator`. All file edits and every git operation (`add`, `commit`, `push`, branch switching) must be performed inside `$WORK_DIR`, never against `/app`.
## What You Do
+15 -10
View File
@@ -1,7 +1,7 @@
---
description: >
PR merge supervisor. Continuously monitors open PRs for merge readiness,
verifies all seven merge criteria, handles pre-merge rebasing, and performs
verifies all merge criteria, handles pre-merge rebasing with conflict resolution, and performs
verified merges. Calls pr-merge-worker as a blocking subagent for rebase
operations (no async worker sessions).
mode: subagent
@@ -10,7 +10,11 @@ temperature: 0.1
model: anthropic/claude-sonnet-4-6
color: "#059669"
permission:
edit: deny
edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: deny
bash:
"*": deny
@@ -53,7 +57,7 @@ permission:
# PR Merge Supervisor
You are a supervisor that monitors open PRs for merge readiness, verifies all criteria are met, and merges them. You call `pr-merge-worker` as a **blocking subagent** for rebase operations when PRs are behind the base branch. Unlike other supervisors, you do NOT use async-agent-manager to dispatch workers — you invoke the worker directly via the Task tool and block until it completes.
You are a supervisor that monitors open PRs for merge readiness, verifies all criteria are met, rebases stale PR, resolves conflicts, and merges them. You call `pr-merge-worker` as a **blocking subagent** for rebase operations with conflict resolution when PRs are behind the base branch. Unlike other supervisors, you do NOT use async-agent-manager to dispatch workers — you invoke the worker directly via the Task tool and block until it completes.
## What You Receive
@@ -99,9 +103,11 @@ Before merging any PR, the following must be true:
5. **No `needs feedback` label** — the PR is not waiting for human input
6. **Not blocked** — no `Blocked` label
**Note:** The above criteria do not need to be satisfied before dispatching a `pr-merge-worker`. While they must be satisfied before the actual merge, the rebasing and conflict resolution steps are still useful even if it isnt ready to be merged.
## Workers
You call `pr-merge-worker` as a **blocking subagent** (via the Task tool) for rebase operations when PRs are behind the base branch. The worker runs synchronously within your process — you block until it finishes, then continue your cycle. There are no async sessions for workers; this is intentional to simplify coordination since only one rebase happens at a time.
You call `pr-merge-worker` as a **blocking subagent** (via the Task tool) for rebase operations with conflict resolution when PRs are behind the base branch. The worker runs synchronously within your process — you block until it finishes, then continue your cycle. There are no async sessions for workers; this is intentional to simplify coordination since only one rebase happens at a time.
## Main Loop
@@ -109,9 +115,9 @@ Poll every 5 minutes using `bash("sleep 300", timeout=360000)`.
Each cycle:
1. List all open PRs. (the forgejo task paginates so be sure to go through every page)
2. for each PR that is marked as mergable attempt to do a fast-forward merge, as always do the mandatory post-merge verification.
2. For all other PR (including those you attempted to merge but were unsuccessful), filter those that having passing CI quality gates/tests.
4. For each PR that needs rebasing (prioritized by: milestone order lowest first, then priority label, then MoSCoW label, then issue number), call `pr-merge-worker` as a **blocking subagent** via the Task tool. Pass the PR number, repository info, and credentials in the prompt. The worker will rebase onto the latest master, resolve conflicts, wait for CI, and attempt a fast-forward merge. You block until the worker finishes before processing the next PR.
2. for each PR that can be merged (has a passing CI,, isn't stale, and has at least 1 approval review) do a fast-forward or rebase merge, as always do the mandatory post-merge verification.
3. For all other PR (including those you attempted to merge but were unsuccessful) order them as follows: 1) CI passes and has one or more review approval 2) CI is failing without conflicts and has one or more review approval 3) CI is failing with conflicts and has one or more review approval 4) All other PRs, within each of those four categories sort by priority label with the most critical label ("Priority/CI Blocking") coming first.
4. For each PR, in the order specified in step 2 above, call `pr-merge-worker` as a **blocking subagent** via the Task tool. Pass the PR number, repository info, and credentials in the prompt. The worker will rebase onto the latest master, resolve conflicts, wait for CI, and attempt a fast-forward or rebase merge. You block until the worker finishes before processing the next PR.
5. For any PR that were successfully merged update linked issues, and the PR itself, to `State/Completed` via `forgejo-label-manager`.
## Tracking
@@ -131,6 +137,5 @@ Each cycle:
**Automated by CleverAgents Bot**
Supervisor: PR Merge | Agent: pr-merge-pool-supervisor
```
6. **Apply labels via `forgejo-label-manager`.** Never apply labels directly or using the Forgejo MCP/task. All label operations must go through `forgejo-label-manager`.
7. **Exhaustive pagination for all list results.** Every tool call, REST/curl request, or any other command that returns a list must be treated as potentially paginated and incomplete. Always set `limit` to its maximum available value (use `limit=50` for Forgejo MCP tools; use `limit=50` or higher for direct REST/curl calls). After each list response, check whether the number of returned items equals the page size — if so, there are likely more results; fetch the next page (`page=2`, `page=3`, …) and continue until receiving a partial page. Never assume the first response is the complete result. This rule applies to every list-returning call without exception. *Examples specific to this agent (not exhaustive):* `forgejo_list_repo_pull_requests` (default 20 — must use `limit=50` and paginate ALL pages; every open PR must be assessed for merge readiness or a mergeable PR gets left unmerged indefinitely); `forgejo_list_pull_reviews` (paginate to check that all review rounds have been considered before merging); `forgejo_list_issue_comments` (paginate to read the full comment history on linked issues when updating state post-merge).
5. **Apply labels via `forgejo-label-manager`.** Never apply labels directly or using the Forgejo MCP/task. All label operations must go through `forgejo-label-manager`.
6. **Exhaustive pagination for all list results.** Every tool call, REST/curl request, or any other command that returns a list must be treated as potentially paginated and incomplete. Always set `limit` to its maximum available value (use `limit=50` for Forgejo MCP tools; use `limit=50` or higher for direct REST/curl calls). After each list response, check whether the number of returned items equals the page size — if so, there are likely more results; fetch the next page (`page=2`, `page=3`, …) and continue until receiving a partial page. Never assume the first response is the complete result. This rule applies to every list-returning call without exception. *Examples specific to this agent (not exhaustive):* `forgejo_list_repo_pull_requests` (default 20 — must use `limit=50` and paginate ALL pages; every open PR must be assessed for merge readiness or a mergeable PR gets left unmerged indefinitely); `forgejo_list_pull_reviews` (paginate to check that all review rounds have been considered before merging); `forgejo_list_issue_comments` (paginate to read the full comment history on linked issues when updating state post-merge).
+10 -6
View File
@@ -1,14 +1,18 @@
---
description: >
PR merge worker. Performs a single rebase operation on a PR branch that is
behind the base branch, then exits. Called as a blocking subagent by the
PR merge worker. Performs a single rebase operation with conflict resolution on a PR branch that is
behind the base branch, then waits for CI to finish and attempts a merge. Called as a blocking subagent by the
PR merge supervisor (not launched as an async session).
mode: subagent
hidden: true
temperature: 0.1
model: anthropic/claude-sonnet-4-6
permission:
edit: allow
edit:
"*": deny
"/tmp/**": allow
"external_directory":
"/tmp/**": allow
webfetch: allow
bash:
"*": deny
@@ -46,19 +50,19 @@ You perform a single rebase operation on a PR branch, resolve any conflicts, and
## Procedure
Your prompt tells you which PR to rebase. You must:
1. Create an isolated clone using the `repo-isolator` subagent ensuring you pass it the branch used by the PR.
1. Create an isolated clone using the `repo-isolator` subagent ensuring you pass it the branch used by the PR. Make sure all work is done within this clone's directory.
2. Rebase it onto the base branch (usually `master`).
3. Resolve any conflicts that arise by reviewing the recent git history and using that to fix the conflicts
4. Force-push with lease using `git-commit-helper`
5. Clean up the clone.
6. Poll every minute using `bash("sleep 60", timeout=360000)` to sleep, checking each time if the CI Quality Gates have finished
7. Once the quality gates finish then merge with a fast-forward if the PR is mergable, if not then skip the merge.
7. Once the quality gates finish then merge with a fast-forward or rebase merge if the PR is mergable, if not then skip the merge.
8. Report back with any relevant details.
## Rules
1. **One task, then exit.** Do not loop. Do not sleep. Do not look for more work.
2. **Force-push with lease only.** Never use `--force` without `--lease`.
3. **Clean up your clone.** Delete the temporary directory before exiting.
3. **Clean up your clone.** Delete the temporary clone directory before exiting.
4. **Apply labels via `forgejo-label-manager`.** Never apply labels directly or using the Forgejo MCP/task. All label operations must go through `forgejo-label-manager`.
5. **Exhaustive pagination for all list results.** Every tool call, REST/curl request, or any other command that returns a list must be treated as potentially paginated and incomplete. Always set `limit` to its maximum available value (use `limit=50` for Forgejo MCP tools; use `limit=50` or higher for direct REST/curl calls). After each list response, check whether the number of returned items equals the page size — if so, there are likely more results; fetch the next page (`page=2`, `page=3`, …) and continue until receiving a partial page. Never assume the first response is the complete result. This rule applies to every list-returning call without exception. *Examples specific to this agent (not exhaustive):* `git log` listing commits during conflict resolution must be fully read; any future REST/curl calls returning JSON arrays must be paginated.
+10 -3
View File
@@ -9,7 +9,11 @@ temperature: 0.2
model: anthropic/claude-sonnet-4-6
color: primary
permission:
edit: allow
edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow
bash:
"*": deny
@@ -29,6 +33,8 @@ permission:
task:
"*": deny
"forgejo-label-manager": allow
"repo-isolator": allow
"git-commit-helper": allow
"forgejo_*": allow
# CRITICAL: Never list repo-level labels — use org labels via forgejo-label-manager
"forgejo_list_repo_labels": deny
@@ -60,5 +66,6 @@ You set up project infrastructure from scratch. Your caller provides the product
1. **Detect before creating.** Always check if something exists before creating it.
2. **Never overwrite.** If a file or label already exists, skip it.
3. **Credentials from prompt.** All Forgejo PAT, git identity, etc. come from the caller's prompt.
4. **Apply labels via `forgejo-label-manager`.** Never apply labels directly or using the Forgejo MCP/task. All label operations must go through `forgejo-label-manager`.
5. **Exhaustive pagination for all list results.** Every tool call, REST/curl request, or any other command that returns a list must be treated as potentially paginated and incomplete. Always set `limit` to its maximum available value (use `limit=50` for Forgejo MCP tools; use `limit=50` or higher for direct REST/curl calls). After each list response, check whether the number of returned items equals the page size — if so, there are likely more results; fetch the next page (`page=2`, `page=3`, …) and continue until receiving a partial page. Never assume the first response is the complete result. This rule applies to every list-returning call without exception. *Examples specific to this agent (not exhaustive):* any `forgejo_list_*` calls used when checking for existing labels, milestones, or workflows must be paginated — missing one means re-creating something that already exists.
4. **Work in an isolated clone.** Use `repo-isolator` to create an isolated clone of the target repository in `/tmp/`. All file creation and edits must be done in the clone — never directly in `/app`. Use `git-commit-helper` to commit and push changes. Clean up the isolated clone using `repo-isolator` when done.
5. **Apply labels via `forgejo-label-manager`.** Never apply labels directly or using the Forgejo MCP/task. All label operations must go through `forgejo-label-manager`.
6. **Exhaustive pagination for all list results.** Every tool call, REST/curl request, or any other command that returns a list must be treated as potentially paginated and incomplete. Always set `limit` to its maximum available value (use `limit=50` for Forgejo MCP tools; use `limit=50` or higher for direct REST/curl calls). After each list response, check whether the number of returned items equals the page size — if so, there are likely more results; fetch the next page (`page=2`, `page=3`, …) and continue until receiving a partial page. Never assume the first response is the complete result. This rule applies to every list-returning call without exception. *Examples specific to this agent (not exhaustive):* any `forgejo_list_*` calls used when checking for existing labels, milestones, or workflows must be paginated — missing one means re-creating something that already exists.
+5 -1
View File
@@ -9,7 +9,9 @@ temperature: 0.1
model: openai/gpt-5-codex
color: "#6B7280"
permission:
edit: deny
edit:
"*": deny
"/tmp/**": allow
webfetch: deny
bash:
"*": deny
@@ -37,6 +39,8 @@ permission:
# CRITICAL: DO NOT use forgejo_add_issue_labels directly
# Always delegate to forgejo-label-manager for label operations
"forgejo_add_issue_labels": deny
"external_directory":
"/tmp/**": allow
---
# Repository Isolator
+5 -1
View File
@@ -8,7 +8,11 @@ hidden: true
temperature: 0.2
# NO MODEL SPECIFIED - inherits from caller (tier selector)
permission:
edit: allow
edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow
bash:
"*": deny
+5 -1
View File
@@ -8,7 +8,11 @@ hidden: true
temperature: 0.2
model: anthropic/claude-sonnet-4-6
permission:
edit: allow
edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow
bash:
"*": deny
+5 -1
View File
@@ -11,7 +11,11 @@ temperature: 0.1
model: openai/gpt-5-codex
color: accent
permission:
edit: allow
edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow
bash:
"*": deny
+5 -1
View File
@@ -9,7 +9,11 @@ temperature: 0.2
# NO MODEL SPECIFIED - inherits from caller (tier selector)
color: warning
permission:
edit: allow
edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow
bash:
"*": deny
+5 -1
View File
@@ -7,7 +7,11 @@ hidden: true
temperature: 0.0
model: openai/gpt-5-codex
permission:
edit: allow
edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: deny
bash:
"*": deny
+5 -1
View File
@@ -7,7 +7,11 @@ hidden: true
temperature: 0.0
model: anthropic/claude-haiku-4-5
permission:
edit: allow
edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: deny
bash:
"*": deny
+5 -1
View File
@@ -7,7 +7,11 @@ hidden: true
temperature: 0.0
model: anthropic/claude-opus-4-6
permission:
edit: allow
edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: deny
bash:
"*": deny
+5 -1
View File
@@ -7,7 +7,11 @@ hidden: true
temperature: 0.0
model: anthropic/claude-sonnet-4-6
permission:
edit: allow
edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: deny
bash:
"*": deny
+6 -1
View File
@@ -8,7 +8,11 @@ hidden: true
temperature: 0.1
model: anthropic/claude-sonnet-4-6
permission:
edit: allow
edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow
bash:
"*": deny
@@ -53,6 +57,7 @@ Your prompt provides the current milestone status data and the timeline file for
3. Add new entries — never overwrite existing ones.
4. Commit using `git-commit-helper` with a Conventional Changelog message.
5. Push and exit. (No PR needed — timeline updates go directly to master.)
6. Clean up the isolated clone using `repo-isolator`.
## Rules
+5 -1
View File
@@ -8,7 +8,11 @@ hidden: true
temperature: 0.1
# NO MODEL SPECIFIED - inherits from caller (tier selector)
permission:
edit: allow
edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow
bash:
"*": deny
+5 -1
View File
@@ -8,7 +8,11 @@ hidden: true
temperature: 0.2
# NO MODEL SPECIFIED - inherits from caller (tier selector)
permission:
edit: allow
edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow
bash:
"*": deny
+134
View File
@@ -5,6 +5,13 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
## [Unreleased]
### Documentation
- Add `docs/api/decisions.md` — decision recording and tree CLI reference (v3.2.0)
- Add `docs/api/invariants.md` — invariant management CLI reference (v3.2.0)
- Add `docs/api/checkpoints.md` — checkpoint and rollback CLI reference (v3.3.0)
- Add `docs/api/plan-corrections.md` — plan correction modes and subplan system overview (v3.2.0/v3.3.0)
### Fixed
- **Automation Profile Silent Fallback** (#8232): `_resolve_profile_for_plan` in
@@ -163,6 +170,16 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
`sandbox_root=.cleveragents/sandbox/`, so LLM file output (`FILE:` blocks)
is written to disk during the execute phase. (#4222)
- **SubplanExecutionService fail_fast cancellation** (#7582): Fixed a race condition where
already-running parallel subplans were not cancelled when `fail_fast` fired. Previously,
`Future.cancel()` only prevented queued futures from starting but had no effect on
in-flight futures that completed after `stop_flag` was set — their `COMPLETE` results
were incorrectly included in the merge output. The fix adds a post-completion guard that
overrides any non-`ERRORED`/non-`CANCELLED` result to `CANCELLED` when `stop_flag` is
active, and clears the associated output to prevent it from entering the merge. Also
replaces the O(n) linear `status` lookup in the `as_completed()` loop with an O(1)
`status_map` dict pre-computed before the executor block.
- **Robot Framework TDD Listener Guards** (#5436): Added three guard conditions to the
`tdd_expected_fail_listener` `end_test()` function to prevent blindly inverting ALL test
failures to passes, which was masking infrastructure errors and causing flaky CI behavior.
@@ -186,7 +203,124 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
supervisors). Updated all numeric references, pre-flight checklists, and validation logic.
---
## [3.5.0] — Unreleased (Autonomy Hardening)
### Added
- **A2A Facade Session and Plan Lifecycle** — Session and plan lifecycle operations
are now functional via the CLI using the A2A (Agent-to-Agent) protocol facade.
Supports `agents session start/list/stop` and plan execution via `agents plan execute
--session SESSION_ID`. Follows ADR-047 (A2A Standard Adoption).
- **Event Queue Publish/Subscribe** — A new event queue system enables asynchronous
publish/subscribe communication between agents and plan lifecycle components.
Supports `agents events subscribe` and `agents events publish` CLI commands.
Event types include `PLAN_CREATED`, `STRATEGIZE_COMPLETE`, `EXECUTE_COMPLETE`,
`APPLY_COMPLETE`, `CORRECTION_APPLIED`, `INVARIANT_VIOLATED`, `SUBPLAN_STARTED`,
`SUBPLAN_COMPLETE`, `SUBPLAN_FAILED`, and `GUARD_TRIGGERED`.
- **Guard Enforcement** — Three guard types protect against runaway autonomous
execution: denylist (blocks specific tools/commands/file patterns), budget caps
(limits LLM tokens, cost, wall time, file writes), and tool call limits (caps
tool calls per subplan/plan and consecutive failures). Guards are configurable
at tool, subplan, plan, and global levels.
- **Automation Profile Resolution Precedence** — Automation profiles now resolve
with correct precedence: plan-level > action-level > global. The
`_resolve_profile_for_plan` function raises a clear `ValidationError` when an
unknown profile name is configured, listing available built-in profiles
(`manual`, `semi-auto`, `auto`, `supervised`).
- **Hierarchical Plan Decomposition (4+ Levels)** — The strategy actor now
decomposes plans into hierarchical subplan trees with 4+ levels of depth.
Decomposition considers dependency analysis, complexity estimation, risk
assessment, and resource constraints.
- **Decision Correction with Selective Subtree Recomputation** — When a decision
correction is applied, only the affected subtree is recomputed. Unaffected
branches of the plan tree are preserved, minimizing wasted work.
- **Parallel Execution Scaling (10+ Concurrent Subplans)**`SubplanExecutionService`
now scales to 10+ concurrent subplans via `ThreadPoolExecutor`. Fail-fast
cancellation correctly overrides in-flight subplan results to `CANCELLED` when
`stop_flag` is active, preventing corrupted merge output.
- **Large-Scale Autonomous Task Execution** — A realistic large-scale porting task
(Firefox-scale codebase) completes autonomously using hierarchical decomposition,
parallel execution, and validation-gated apply.
### Changed
- **Server Stubs Moved to M9 (v3.8.0)** — Server stubs previously scoped to this
milestone have been moved to v3.8.0 following the ACP to A2A protocol adoption
(ADR-047) and server architecture redesign (ADR-048).
- **TUI Features Moved to M8 (v3.7.0)** — TUI features previously scoped to this
milestone have been moved to v3.7.0.
### Technical
- `nox` passes with coverage >= 97% including large-project suites
- Hierarchical decomposition validated at 4+ levels of subplan depth
- Parallel execution validated at 10+ concurrent subplans
- Decision correction validated for selective subtree recomputation only
---
## [3.4.0] — Unreleased (ACMS v1 + Context Scaling)
### Added
- **Advanced Context Management System (ACMS) v1** — The ACMS pipeline is now
operational. Projects with 10,000+ files can be indexed and queried. The context
assembly pipeline produces scoped, budget-constrained context views for actors.
Hot/warm/cold storage tiers manage context lifecycle. See `docs/context.md` for
full documentation.
- **Context Assembly CLI** — New `agents context` command suite:
- `agents context list` — List all context fragments grouped by storage tier
- `agents context add PATH` — Manually add a file or directory to context
- `agents context show` — Show fragment details or assembled context view
- `agents context clear` — Remove fragments from the tier service
- **Context Policies with View-Specific Settings** — Context policies are
configurable in `.cleveragents/config.toml` with per-view overrides. Supports
`max_file_size`, `max_total_size`, tier capacity thresholds, and file exclusion
patterns.
- **Budget Enforcement**`max_file_size` (default 256 KB) and `max_total_size`
(default 10 MB) constraints are enforced during context assembly. Files exceeding
`max_file_size` are skipped. Assembly stops when `max_total_size` is reached.
- **Context Analysis Summaries** — Context assembly produces meaningful summaries
of the assembled context, including fragment counts per tier, total size, and
coverage metrics.
- **ACMS Integration with Plan Execution** — Plan execution (`agents plan execute`)
now leverages ACMS context for LLM calls. The `LLMExecuteActor` receives a
scoped, budget-constrained context view assembled from project resources.
### Fixed
- **ContextTierService Thread Safety** (#7547) — Added `threading.RLock` to
`ContextTierService` to prevent `RuntimeError: dictionary changed size during
iteration` and data corruption under concurrent plan execution. All public
methods now acquire the reentrant lock before accessing tier data structures.
- **ACMS Context Tier Hydration** (#1028) — `ContextTierService` no longer starts
empty on every CLI invocation. A new `context_tier_hydrator.py` reads files from
linked project resources, creates `TieredFragment` objects, and stores them in
the tier service before context assembly. Respects max file size (256 KB), total
budget (10 MB), binary file exclusion, and directory skipping.
### Technical
- Projects with 10,000+ files index without timeout
- Context window management validated across hot/warm/cold tiers
- ACMS v1 pipeline produces scoped context output
- Test coverage >= 97%
---
## [3.8.0] — 2026-04-05
### Added
+188
View File
@@ -0,0 +1,188 @@
"""ASV benchmarks for LangGraph execution hotspots.
Measures performance of:
- GraphExecutor.step execution
- RouterNode.route decision making
- Checkpoint read/write operations
- Graph state transitions
- Node execution throughput
"""
from __future__ import annotations
import asyncio
import sys
from pathlib import Path
from typing import Any
try:
from cleveragents.langgraph.graph import GraphConfig, LangGraph
from cleveragents.langgraph.nodes import Edge, Node, NodeConfig, NodeType
from cleveragents.langgraph.state import GraphState
except ModuleNotFoundError:
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
from cleveragents.langgraph.graph import GraphConfig, LangGraph
from cleveragents.langgraph.nodes import Edge, Node, NodeConfig, NodeType
from cleveragents.langgraph.state import GraphState
def _create_simple_graph() -> LangGraph:
"""Create a simple representative plan graph for benchmarking."""
config = GraphConfig(
name="bench-graph",
nodes={
"start": NodeConfig(name="start", type=NodeType.START),
"process": NodeConfig(name="process", type=NodeType.FUNCTION),
"route": NodeConfig(name="route", type=NodeType.CONDITIONAL),
"end": NodeConfig(name="end", type=NodeType.END),
},
edges=[
Edge(source="start", target="process"),
Edge(source="process", target="route"),
Edge(source="route", target="end"),
],
entry_point="start",
checkpointing=False,
)
return LangGraph(config)
def _create_checkpoint_graph() -> LangGraph:
"""Create a graph with checkpointing enabled for persistence benchmarks."""
checkpoint_dir = Path("/tmp/langgraph_bench_checkpoints")
checkpoint_dir.mkdir(exist_ok=True)
config = GraphConfig(
name="bench-graph-checkpoint",
nodes={
"start": NodeConfig(name="start", type=NodeType.START),
"process": NodeConfig(name="process", type=NodeType.FUNCTION),
"end": NodeConfig(name="end", type=NodeType.END),
},
edges=[
Edge(source="start", target="process"),
Edge(source="process", target="end"),
],
entry_point="start",
checkpointing=True,
checkpoint_dir=checkpoint_dir,
)
return LangGraph(config)
def _create_complex_graph() -> LangGraph:
"""Create a more complex graph with multiple nodes for throughput testing."""
config = GraphConfig(
name="bench-graph-complex",
nodes={
"start": NodeConfig(name="start", type=NodeType.START),
"node1": NodeConfig(name="node1", type=NodeType.FUNCTION),
"node2": NodeConfig(name="node2", type=NodeType.FUNCTION),
"node3": NodeConfig(name="node3", type=NodeType.FUNCTION),
"route": NodeConfig(name="route", type=NodeType.CONDITIONAL),
"end": NodeConfig(name="end", type=NodeType.END),
},
edges=[
Edge(source="start", target="node1"),
Edge(source="node1", target="node2"),
Edge(source="node2", target="node3"),
Edge(source="node3", target="route"),
Edge(source="route", target="end"),
],
entry_point="start",
checkpointing=False,
parallel_execution=True,
)
return LangGraph(config)
class LangGraphExecutionSuite:
"""Benchmark LangGraph execution hotspots."""
def setup(self) -> None:
"""Initialize graphs for benchmarking."""
self.simple_graph = _create_simple_graph()
self.checkpoint_graph = _create_checkpoint_graph()
self.complex_graph = _create_complex_graph()
# Sample input state
self.sample_state = GraphState()
def time_simple_graph_execute(self) -> None:
"""Measure simple graph execution time."""
asyncio.run(self.simple_graph.execute(self.sample_state))
def time_complex_graph_execute(self) -> None:
"""Measure complex graph execution time."""
asyncio.run(self.complex_graph.execute(self.sample_state))
def time_checkpoint_graph_execute(self) -> None:
"""Measure graph execution with checkpointing enabled."""
asyncio.run(self.checkpoint_graph.execute(self.sample_state))
def time_state_manager_get_state(self) -> None:
"""Measure state manager state retrieval."""
self.simple_graph.state_manager.get_state()
def time_state_manager_update_state(self) -> None:
"""Measure state manager state update."""
new_state = GraphState()
self.simple_graph.state_manager.state = new_state
def track_graph_nodes_count(self) -> int:
"""Track number of nodes in simple graph."""
return len(self.simple_graph.nodes)
def track_graph_edges_count(self) -> int:
"""Track number of edges in simple graph."""
return len(self.simple_graph.config.edges)
def track_execution_history_length(self) -> int:
"""Track execution history length after execution."""
return len(self.simple_graph.get_execution_history())
class LangGraphRouterNodeSuite:
"""Benchmark RouterNode routing performance."""
def setup(self) -> None:
"""Initialize router node for benchmarking."""
self.router_config = NodeConfig(
name="router",
type=NodeType.CONDITIONAL,
condition={"type": "simple_condition"},
)
self.router_node = Node(self.router_config)
def time_router_node_creation(self) -> None:
"""Measure router node instantiation time."""
Node(self.router_config)
def time_router_node_config_access(self) -> None:
"""Measure router node config access."""
_ = self.router_node.config.condition
class LangGraphStateTransitionSuite:
"""Benchmark graph state transitions and updates."""
def setup(self) -> None:
"""Initialize state for benchmarking."""
self.state = GraphState()
self.graph = _create_simple_graph()
def time_state_creation(self) -> None:
"""Measure GraphState creation time."""
GraphState()
def time_state_dict_conversion(self) -> None:
"""Measure state to dict conversion."""
self.state.to_dict()
def time_state_from_dict(self) -> None:
"""Measure state from dict creation."""
GraphState.from_dict({"messages": []})
def time_state_copy(self) -> None:
"""Measure state copy operation."""
self.state.copy()
+200
View File
@@ -0,0 +1,200 @@
"""ASV benchmarks for TUI rendering and command routing performance.
Measures performance of:
- TUI layout and render cycle time
- Slash command catalog hydration
- Command routing overhead
- Widget rendering performance
- Session view updates
"""
from __future__ import annotations
import sys
from pathlib import Path
from typing import Any
try:
from cleveragents.tui.slash_catalog import (
SLASH_COMMAND_SPECS,
SlashCommandSpec,
slash_command_specs,
)
except ModuleNotFoundError:
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
from cleveragents.tui.slash_catalog import (
SLASH_COMMAND_SPECS,
SlashCommandSpec,
slash_command_specs,
)
def _build_command_index() -> dict[str, list[SlashCommandSpec]]:
"""Build a command index grouped by category."""
index: dict[str, list[SlashCommandSpec]] = {}
for spec in SLASH_COMMAND_SPECS:
if spec.group not in index:
index[spec.group] = []
index[spec.group].append(spec)
return index
def _filter_commands_by_prefix(prefix: str) -> list[SlashCommandSpec]:
"""Filter commands by prefix for autocomplete."""
return [cmd for cmd in SLASH_COMMAND_SPECS if cmd.command.startswith(prefix)]
class TUISlashCatalogSuite:
"""Benchmark TUI slash command catalog operations."""
def setup(self) -> None:
"""Initialize catalog for benchmarking."""
self.catalog = SLASH_COMMAND_SPECS
self.command_index = _build_command_index()
def time_catalog_iteration(self) -> None:
"""Measure time to iterate through all commands."""
for _ in self.catalog:
pass
def time_catalog_grouping(self) -> None:
"""Measure time to group commands by category."""
_build_command_index()
def time_catalog_prefix_filter(self) -> None:
"""Measure time to filter commands by prefix."""
_filter_commands_by_prefix("session:")
def time_catalog_search(self) -> None:
"""Measure time to search for a specific command."""
target = "plan:execute"
for cmd in self.catalog:
if cmd.command == target:
break
def time_command_spec_creation(self) -> None:
"""Measure time to create a SlashCommandSpec."""
SlashCommandSpec(
command="test:command",
group="Test",
description="Test command",
)
def track_catalog_size(self) -> int:
"""Track total number of commands in catalog."""
return len(self.catalog)
def track_unique_groups(self) -> int:
"""Track number of unique command groups."""
return len(self.command_index)
def track_largest_group_size(self) -> int:
"""Track size of largest command group."""
return max(len(cmds) for cmds in self.command_index.values())
class TUICommandRoutingSuite:
"""Benchmark TUI command routing performance."""
def setup(self) -> None:
"""Initialize routing structures for benchmarking."""
self.catalog = SLASH_COMMAND_SPECS
self.command_map = {cmd.command: cmd for cmd in self.catalog}
def time_command_lookup(self) -> None:
"""Measure time to lookup a command in the map."""
_ = self.command_map.get("session:create")
def time_command_validation(self) -> None:
"""Measure time to validate a command exists."""
command = "plan:execute"
command in self.command_map
def time_group_lookup(self) -> None:
"""Measure time to find all commands in a group."""
group = "Session"
[cmd for cmd in self.catalog if cmd.group == group]
def time_description_search(self) -> None:
"""Measure time to search commands by description."""
search_term = "session"
[
cmd
for cmd in self.catalog
if search_term.lower() in cmd.description.lower()
]
def track_command_map_size(self) -> int:
"""Track size of command lookup map."""
return len(self.command_map)
class TUISessionViewSuite:
"""Benchmark TUI session view operations."""
def setup(self) -> None:
"""Initialize session view for benchmarking."""
from cleveragents.tui.app import SessionView
self.session = SessionView(
session_id="bench-session-001",
transcript=["message 1", "message 2", "message 3"],
)
def time_session_view_creation(self) -> None:
"""Measure time to create a SessionView."""
from cleveragents.tui.app import SessionView
SessionView(
session_id="test-session",
transcript=["test message"],
)
def time_transcript_append(self) -> None:
"""Measure time to append to transcript."""
self.session.transcript.append("new message")
def time_transcript_iteration(self) -> None:
"""Measure time to iterate through transcript."""
for _ in self.session.transcript:
pass
def track_session_id_length(self) -> int:
"""Track session ID length."""
return len(self.session.session_id)
def track_transcript_size(self) -> int:
"""Track transcript message count."""
return len(self.session.transcript)
class TUIWidgetRenderingSuite:
"""Benchmark TUI widget rendering operations."""
def setup(self) -> None:
"""Initialize widgets for benchmarking."""
self.command_specs = SLASH_COMMAND_SPECS
self.sample_text = "Sample widget content for rendering"
def time_command_spec_string_conversion(self) -> None:
"""Measure time to convert command spec to string."""
spec = self.command_specs[0]
str(spec)
def time_command_spec_formatting(self) -> None:
"""Measure time to format command spec for display."""
spec = self.command_specs[0]
f"{spec.command} - {spec.description}"
def time_text_rendering_short(self) -> None:
"""Measure time to render short text."""
len(self.sample_text)
def time_text_rendering_long(self) -> None:
"""Measure time to render long text."""
long_text = self.sample_text * 100
len(long_text)
def track_widget_content_size(self) -> int:
"""Track size of widget content."""
return len(self.sample_text)
+237
View File
@@ -0,0 +1,237 @@
# Checkpoint and Rollback API (v3.3.0)
CleverAgents supports checkpointing and rollback to snapshot sandbox state during plan
execution and restore it later. This page documents the CLI commands and Python API for
managing checkpoints.
---
## Overview
Checkpointing introduced in **v3.3.0** allows operators to:
- Snapshot sandbox state at key points during plan execution.
- Roll back to a previous snapshot to undo tool side-effects.
- Integrate with the decision-correction revert flow for targeted re-execution.
Checkpoints are immutable records stored in the `checkpoint_metadata` SQLite table and
backed by a `CheckpointRepository`. The `CheckpointService` is registered in the DI
container and injected into `ToolRunner`, `SubplanExecutionService`, and `PlanExecutor`.
---
## CLI Reference
### `agents plan checkpoint list`
List all checkpoints for a plan.
```bash
agents plan checkpoint list <PLAN_ID> [OPTIONS]
```
**Options:**
| Flag | Description |
|------|-------------|
| `--format`, `-f` | Output format: `json`, `yaml`, `plain`, `table`, `rich` |
**Examples:**
```bash
# List checkpoints for a plan
agents plan checkpoint list 01HXYZ1234567890ABCDEFGH
# JSON output for scripting
agents plan checkpoint list 01HXYZ1234567890ABCDEFGH --format json
```
Each checkpoint entry shows:
| Field | Description |
|-------|-------------|
| `checkpoint_id` | ULID identifier |
| `checkpoint_type` | `pre_write`, `post_step`, or `manual` |
| `decision_id` | Optional decision this checkpoint is aligned to |
| `sandbox_ref` | Git commit hash or patch reference |
| `size_bytes` | Size of the checkpoint data |
| `created_at` | UTC creation timestamp |
---
### `agents plan rollback`
Roll back a plan's sandbox to a previously captured checkpoint.
```bash
agents plan rollback [--yes|-y] <PLAN_ID> <CHECKPOINT_ID>
```
**Options:**
| Flag | Description |
|------|-------------|
| `--yes`, `-y` | Skip the interactive confirmation prompt |
**Examples:**
```bash
# Rollback with confirmation prompt
agents plan rollback 01HXYZ1234567890ABCDEFGH 01HABC1234567890ABCDEFGH
# Skip confirmation (for scripts/CI)
agents plan rollback --yes 01HXYZ1234567890ABCDEFGH 01HABC1234567890ABCDEFGH
```
**JSON output envelope** (when using `--format json`):
```json
{
"rollback_summary": {
"plan_id": "...",
"from_checkpoint_id": "...",
"restored_files_count": 3
},
"changes_reverted": ["path/to/file1.py", "path/to/file2.py"],
"impact": {
"files_affected": 3
},
"post_rollback_state": {
"active_checkpoint": "...",
"plan_id": "..."
},
"timing": {
"elapsed_seconds": 0.042
},
"messages": ["Rollback completed successfully."]
}
```
**Error cases:**
| Error | Cause |
|-------|-------|
| `Rollback blocked: plan is already applied` | Plan has reached terminal `applied` state |
| `Rollback blocked: sandbox is missing` | Sandbox was cleaned up before rollback |
| `Not found: checkpoint` | Checkpoint ID does not exist |
| `Validation Error` | Checkpoint does not belong to the specified plan |
---
## Checkpoint Lifecycle
### Checkpoint Types
| Type | When Created |
|------|-------------|
| `pre_write` | Automatically before each write-tool execution |
| `post_step` | Automatically after each write-tool execution |
| `manual` | Via `CheckpointService.create_checkpoint()` directly |
### Automatic Checkpoint Triggers (v3.8.0+)
The execution engine supports four automatic triggers, configurable via
`core.checkpoints.auto_create_on`:
| Trigger | When | Component |
|---------|------|-----------|
| `on_tool_write` | Before each write-tool execution | `ToolRunner` |
| `on_tool_write_complete` | After each write-tool execution | `ToolRunner` |
| `on_subplan_spawn` | Before first subplan execution attempt | `SubplanExecutionService` |
| `on_error` | When the Execute phase fails | `PlanExecutor` |
**Configuration:**
```toml
[core.checkpoints]
auto_create_on = ["on_tool_write", "on_tool_write_complete", "on_subplan_spawn", "on_error"]
```
To disable all automatic checkpoints:
```toml
[core.checkpoints]
auto_create_on = []
```
### Retention Policy
A `CheckpointRetentionPolicy` governs how many checkpoints a plan may keep:
| Field | Default | Range |
|-------|---------|-------|
| `max_checkpoints` | 50 | 1100 |
| `auto_prune` | `true` | — |
When `auto_prune` is enabled and the count exceeds `max_checkpoints`, the oldest
**interior** checkpoints are removed. The first (earliest) and most recent checkpoints
are always preserved.
---
## Checkpoint Data Model
Each checkpoint stores:
| Field | Type | Description |
|-------|------|-------------|
| `checkpoint_id` | ULID | Unique identifier |
| `plan_id` | ULID | The plan that owns this checkpoint |
| `sandbox_ref` | str | Git commit hash or patch reference |
| `decision_id` | ULID \| None | Optional decision alignment |
| `checkpoint_type` | str | `pre_write`, `post_step`, or `manual` |
| `resource_id` | ULID \| None | Optional resource association |
| `filesystem_path` | str | Relative path within the checkpoint directory |
| `size_bytes` | int | Size of the checkpoint data in bytes |
| `created_at` | datetime | UTC creation timestamp |
| `metadata` | dict | Audit metadata: `reason`, `source_tool`, `phase`, `extra` |
---
## Python API
The `CheckpointService` is obtained from the DI container:
```python
from cleveragents.application.container import get_container
checkpoint_service = get_container().checkpoint_service()
# Create a manual checkpoint
checkpoint = checkpoint_service.create_checkpoint(
plan_id="01HV...",
sandbox_ref="abc123",
checkpoint_type="manual",
metadata={"reason": "pre-correction snapshot", "phase": "execute"},
)
# List checkpoints for a plan
checkpoints = checkpoint_service.list_checkpoints(plan_id="01HV...")
# Roll back to a checkpoint
result = checkpoint_service.rollback_to_checkpoint(
plan_id="01HV...",
checkpoint_id="01HABC...",
)
print(f"Restored {result.restored_files_count} files")
```
---
## Rollback Guards
The rollback operation enforces two guards:
1. **Plan is applied** — Once a plan reaches the `applied` terminal state, rollback is
rejected with a `BusinessRuleViolation`.
2. **Sandbox is missing** — If the sandbox has been cleaned up, rollback is rejected
because there is nothing to restore.
---
## See Also
- [`docs/reference/checkpointing.md`](../reference/checkpointing.md) — Full checkpointing domain model reference
- [`docs/api/plan-corrections.md`](plan-corrections.md) — Plan correction modes (revert uses checkpoints)
- [ADR-015: Sandbox & Checkpoint](../adr/ADR-015-sandbox-and-checkpoint.md)
- [ADR-035: Decision Tree Rollback & Replay](../adr/ADR-035-decision-tree-rollback-and-replay.md)
+348
View File
@@ -0,0 +1,348 @@
# Decision Recording and Tree API (v3.2.0)
The decision subsystem records every choice point during a plan's Strategize and Execute
phases as a persistent tree of **Decision** nodes. This page documents the CLI commands
and Python API for inspecting and navigating that tree.
---
## Overview
During the **Strategize** phase, the strategy actor records each significant choice as a
`Decision` node. Decisions are linked parent-to-child to form a tree rooted at the
`prompt_definition` node (the original plan prompt). The tree is persisted to SQLite via
`DecisionRepository` and can be queried at any time — even after the plan has completed.
Key capabilities introduced in **v3.2.0**:
- Automatic decision recording for every strategy and execution choice.
- Context snapshots (SHA-256 hash + storage pointer) captured alongside each decision.
- Alternatives-considered tracking for every decision node.
- Full tree rendering via `agents plan tree`.
- Per-decision explanation via `agents plan explain`.
---
## CLI Reference
### `agents plan tree`
Display the decision tree for a plan.
```bash
agents plan tree <PLAN_ID> [OPTIONS]
```
**Options:**
| Flag | Description |
|------|-------------|
| `--format`, `-f` | Output format: `json`, `yaml`, `plain`, `table`, `rich` (default: `rich`) |
| `--show-superseded` | Include superseded decisions in the tree |
| `--depth` | Maximum tree depth to render (0 = unlimited, default: 0) |
**Examples:**
```bash
# Default rich tree view
agents plan tree 01HXYZ1234567890ABCDEFGH
# Table format
agents plan tree 01HXYZ1234567890ABCDEFGH --format table
# Include superseded decisions (e.g. after a correction)
agents plan tree 01HXYZ1234567890ABCDEFGH --show-superseded
# Limit depth to 2 levels
agents plan tree 01HXYZ1234567890ABCDEFGH --depth 2
# JSON output for scripting
agents plan tree 01HXYZ1234567890ABCDEFGH --format json
```
**Rich output** renders an indented tree with decision type labels, sequence numbers,
and full 26-character ULIDs so that IDs can be copied directly into follow-up commands
such as `agents plan explain` or `agents plan correct`.
---
### `agents plan explain`
Show full details for a single decision node, including alternatives considered,
context snapshot, and actor reasoning.
```bash
agents plan explain <DECISION_ID> [OPTIONS]
```
**Options:**
| Flag | Description |
|------|-------------|
| `--format`, `-f` | Output format: `json`, `yaml`, `plain`, `table`, `rich` |
| `--show-context` | Include context snapshot details (hash, storage ref, resources) |
| `--show-reasoning` | Include rationale and raw actor reasoning trace |
Alternatives considered are **always** included in the output regardless of flags.
**Examples:**
```bash
# Default rich output
agents plan explain 01HXYZ1234567890ABCDEFGH
# JSON with full context snapshot
agents plan explain 01HXYZ1234567890ABCDEFGH --format json --show-context
# Show actor reasoning trace
agents plan explain 01HXYZ1234567890ABCDEFGH --show-reasoning
# YAML with all details
agents plan explain 01HXYZ1234567890ABCDEFGH --format yaml \
--show-context --show-reasoning
```
---
## Decision Data Model
Each decision node stores the following fields.
### Identity
| Field | Type | Description |
|-------|------|-------------|
| `decision_id` | ULID | Auto-generated unique identifier |
| `plan_id` | ULID | Parent plan |
| `parent_decision_id` | ULID \| None | Parent node; `None` for the root |
| `sequence_number` | int | Monotonic order within the plan (0-indexed, never reused) |
### Classification
| Field | Type | Description |
|-------|------|-------------|
| `decision_type` | `DecisionType` | One of 11 enum values (see table below) |
**Decision types:**
| Type | Phase | Description |
|------|-------|-------------|
| `prompt_definition` | Strategize | Root decision — the plan prompt |
| `invariant_enforced` | Strategize | An invariant constraint was applied |
| `strategy_choice` | Strategize | High-level approach chosen |
| `implementation_choice` | Execute | How to implement a specific task |
| `resource_selection` | Execute | Which resources to read / modify |
| `subplan_spawn` | Strategize | Decision to create a child plan |
| `subplan_parallel_spawn` | Strategize | Spawn a group of child plans in parallel |
| `tool_invocation` | Execute | Which skill / tool to use |
| `error_recovery` | Execute | How to handle a failure |
| `validation_response` | Execute | Response to a validation failure |
| `user_intervention` | Any | User-provided guidance / correction |
### Content
| Field | Type | Description |
|-------|------|-------------|
| `question` | str | What question was being answered |
| `chosen_option` | str | The option that was selected |
| `alternatives_considered` | list[str] | Other options that were evaluated |
| `confidence_score` | float \| None | 0.01.0 confidence, or `None` |
### Context Snapshot
Every decision captures a `ContextSnapshot` for replay and correction:
| Field | Type | Description |
|-------|------|-------------|
| `hot_context_hash` | str | SHA-256 hash of the context window at decision time |
| `hot_context_ref` | str | Storage pointer to the full serialised context |
| `relevant_resources` | list[ResourceRef] | Resources in scope at decision time |
| `actor_state_ref` | str | LangGraph actor checkpoint reference |
### Rationale
| Field | Type | Description |
|-------|------|-------------|
| `rationale` | str | Human-readable explanation |
| `actor_reasoning` | str \| None | Raw LLM reasoning trace |
### Downstream Impact
| Field | Type | Description |
|-------|------|-------------|
| `downstream_decision_ids` | list[ULID] | Decisions that depend on this one |
| `downstream_plan_ids` | list[ULID] | Child plans spawned from this decision |
| `artifacts_produced` | list[ArtifactRef] | Artifacts created by this decision |
### Correction Metadata
| Field | Type | Description |
|-------|------|-------------|
| `is_correction` | bool | `True` if this decision corrects another |
| `corrects_decision_id` | ULID \| None | Original decision being corrected |
| `correction_reason` | str \| None | Why the correction was made |
| `superseded_by` | ULID \| None | Decision that replaced this one |
---
## Tree Structure
Decisions form a tree via `parent_decision_id`:
```
prompt_definition (root, parent=None)
├── invariant_enforced
├── strategy_choice
│ ├── implementation_choice
│ │ ├── resource_selection
│ │ └── tool_invocation
│ └── subplan_spawn
└── strategy_choice
```
The `prompt_definition` type is always the root and must have
`parent_decision_id = None`. The **current tree** consists of all decisions where
`superseded_by IS NULL`. Superseded decisions are hidden by default in `agents plan tree`
but can be shown with `--show-superseded`.
---
## Decision Persistence
Decisions are persisted to SQLite via `DecisionRepository` in the
`cleveragents.infrastructure.database` package. The service layer
(`DecisionService`) supports two modes:
| Mode | UnitOfWork | Storage |
|------|------------|---------|
| In-memory | `None` | Internal dicts |
| Persisted | provided | SQLite via `DecisionRepository` + in-memory write-through cache |
When a `UnitOfWork` is wired, mutations are written to the database first, then the
in-memory cache is updated (write-through).
**Python API — recording a decision:**
```python
from cleveragents.application.services.decision_service import DecisionService
from cleveragents.domain.models.core.decision import DecisionType
svc = DecisionService(unit_of_work=uow)
decision = svc.record_decision(
plan_id="01HV...",
decision_type=DecisionType.STRATEGY_CHOICE,
question="Which approach should we take?",
chosen_option="Build a REST API",
alternatives_considered=["GraphQL API", "gRPC service"],
confidence_score=0.85,
rationale="REST is simpler and better supported by existing tooling.",
)
```
**Python API — retrieving the tree:**
```python
# BFS from root(s), level by level
tree = svc.get_tree(plan_id="01HV...")
# Walk from a decision up to the root
path = svc.get_path_to_root(decision_id="01HXYZ...")
# List all decisions for a plan, ordered by sequence number
decisions = svc.list_decisions(plan_id="01HV...")
```
---
## See Also
- [`docs/reference/decision_model.md`](../reference/decision_model.md) — Full domain model reference
- [`docs/reference/decision_service.md`](../reference/decision_service.md) — `DecisionService` API reference
- [`docs/api/plan-corrections.md`](plan-corrections.md) — Correction modes and subplan system
- [ADR-007: Decision Tree & Correction](../adr/ADR-007-decision-tree-and-correction.md)
- [ADR-033: Decision Recording Protocol](../adr/ADR-033-decision-recording-protocol.md)
- [ADR-034: Decision Tree Versioning & History](../adr/ADR-034-decision-tree-versioning-and-history.md)
diff --git a/docs/api/invariants.md b/docs/api/invariants.md
new file mode 100644
index 0000000..2222222
--- /dev/null
+++ b/docs/api/invariants.md
@@ -0,0 +1,274 @@
# Invariant Management API (v3.2.0)
Invariants are natural-language constraints that govern plan execution. They are
evaluated at the start of the Strategize phase by the **Invariant Reconciliation Actor**
and recorded in the decision tree as `invariant_enforced` nodes.
---
## Overview
Invariants introduced in **v3.2.0** provide a declarative way to constrain what a plan
may do. They are scoped (global, project, action, or plan), merged by precedence, and
de-duplicated before enforcement. Violations block the phase transition and emit
`INVARIANT_VIOLATED` events.
Key capabilities:
- Four scope levels: `GLOBAL`, `PROJECT`, `ACTION`, `PLAN`.
- Merge precedence: plan > project > global (action invariants are promoted to plan scope).
- Automatic enforcement at every phase transition via `InvariantReconciliationActor`.
- CLI commands for adding, listing, and removing invariants.
---
## CLI Reference
### `agents invariant add`
Create a new invariant constraint.
```bash
agents invariant add <NAME> --description <DESC> [SCOPE_FLAG]
```
**Scope flags:**
| Flag | Scope | Description |
|------|-------|-------------|
| `--global` | `GLOBAL` | Applies to every plan in the system |
| `--project <NAME>` | `PROJECT` | Applies to plans targeting the named project |
| `--plan <PLAN_ID>` | `PLAN` | Attached directly to a specific plan |
| `--action <ACTION>` | `ACTION` | Defined in an action template; promoted on `plan use` |
**Examples:**
```bash
# Global invariant
agents invariant add --global "Never delete production data"
# Project-scoped invariant
agents invariant add --project myapp "All API changes need tests"
# Plan-specific invariant
agents invariant add --plan 01HXYZ... "Use Python 3.13 only"
# Action-scoped invariant
agents invariant add --action local/code-coverage "Minimum 80% coverage"
```
---
### `agents invariant list`
Display invariants, optionally filtered by scope or project.
```bash
agents invariant list [PATTERN] [OPTIONS]
```
**Options:**
| Flag | Description |
|------|-------------|
| `--global` | Show only global invariants |
| `--project <NAME>` | Show only invariants for the named project |
| `--effective --project <NAME>` | Show the merged effective set for a project |
| `--format`, `-f` | Output format: `json`, `yaml`, `plain`, `table`, `rich` |
+245
View File
@@ -0,0 +1,245 @@
# Invariant Management API (v3.2.0)
Invariants are natural-language constraints that govern plan execution. They are
evaluated at the start of the Strategize phase by the **Invariant Reconciliation Actor**
and recorded in the decision tree as `invariant_enforced` nodes.
---
## Overview
Invariants introduced in **v3.2.0** provide a declarative way to constrain what a plan
may do. They are scoped (global, project, action, or plan), merged by precedence, and
de-duplicated before enforcement. Violations block the phase transition and emit
`INVARIANT_VIOLATED` events.
Key capabilities:
- Four scope levels: `GLOBAL`, `PROJECT`, `ACTION`, `PLAN`.
- Merge precedence: plan > project > global (action invariants are promoted to plan scope).
- Automatic enforcement at every phase transition via `InvariantReconciliationActor`.
- CLI commands for adding, listing, and removing invariants.
---
## CLI Reference
### `agents invariant add`
Create a new invariant constraint.
```bash
agents invariant add <NAME> --description <DESC> [SCOPE_FLAG]
```
**Scope flags:**
| Flag | Scope | Description |
|------|-------|-------------|
| `--global` | `GLOBAL` | Applies to every plan in the system |
| `--project <NAME>` | `PROJECT` | Applies to plans targeting the named project |
| `--plan <PLAN_ID>` | `PLAN` | Attached directly to a specific plan |
| `--action <ACTION>` | `ACTION` | Defined in an action template; promoted on `plan use` |
**Examples:**
```bash
# Global invariant
agents invariant add --global "Never delete production data"
# Project-scoped invariant
agents invariant add --project myapp "All API changes need tests"
# Plan-specific invariant
agents invariant add --plan 01HXYZ... "Use Python 3.13 only"
# Action-scoped invariant
agents invariant add --action local/code-coverage "Minimum 80% coverage"
```
---
### `agents invariant list`
Display invariants, optionally filtered by scope or project.
```bash
agents invariant list [PATTERN] [OPTIONS]
```
**Options:**
| Flag | Description |
|------|-------------|
| `--global` | Show only global invariants |
| `--project <NAME>` | Show only invariants for the named project |
| `--effective --project <NAME>` | Show the merged effective set for a project |
| `--format`, `-f` | Output format: `json`, `yaml`, `plain`, `table`, `rich` |
**Examples:**
```bash
# List all active invariants
agents invariant list
# Filter by scope
agents invariant list --global
agents invariant list --project myapp
# Show merged effective set for a project
agents invariant list --effective --project myapp
# Filter by regex pattern
agents invariant list "data.*safe"
# JSON output
agents invariant list --format json
```
---
### `agents invariant remove`
Remove (soft-delete) an invariant by its ULID.
```bash
agents invariant remove <INVARIANT_ID> [--yes]
```
**Options:**
| Flag | Description |
|------|-------------|
| `--yes` | Skip the interactive confirmation prompt |
**Examples:**
```bash
# With confirmation prompt
agents invariant remove 01HXYZ...
# Skip confirmation
agents invariant remove --yes 01HXYZ...
```
Removal sets `active=False` on the invariant record; it is not hard-deleted. This
preserves the audit trail for enforcement records that reference the invariant.
---
## Scope Hierarchy and Merge Precedence
When computing the effective invariant set for a plan, the precedence chain is:
```
plan > project > global
```
- **Plan-level** invariants take highest precedence.
- **Project-level** invariants apply next.
- **Global-level** invariants are lowest precedence.
- **Action-level** invariants are promoted to plan scope when `plan use` is called.
### De-duplication
Invariants are de-duplicated by text (case-insensitive). When the same constraint text
appears at multiple scopes, only the highest-precedence copy is kept.
### Merge Example
Given:
- Global: "Never delete production data", "Log all changes"
- Project (`myapp`): "All API changes need tests", "Log all changes"
- Plan: "Use Python 3.13 only"
The effective set for a plan targeting `myapp` would be:
1. "Use Python 3.13 only" (plan)
2. "All API changes need tests" (project)
3. "Log all changes" (project — shadows the global duplicate)
4. "Never delete production data" (global)
---
## Invariant Enforcement During Strategize
At the start of the Strategize phase, the `InvariantReconciliationActor`:
1. Calls `InvariantService.get_effective_invariants(plan_id, project_name)` to collect
the merged invariant set.
2. Evaluates each invariant against the current plan context.
3. Creates an `InvariantEnforcementRecord` for each invariant.
4. Records an `invariant_enforced` decision in the decision tree.
5. Emits `INVARIANT_VIOLATED` for each violated invariant.
6. Blocks the phase transition with `ReconciliationBlockedError` if any invariant fails.
### Enforcement Record
Each record contains:
| Field | Description |
|-------|-------------|
| `invariant_id` | ULID of the invariant |
| `enforced` | Whether the invariant was successfully enforced |
| `actor_response` | Response text from the reconciliation actor |
| `decision_id` | ULID of the associated `invariant_enforced` decision node |
### Violation Model
When an invariant is violated, an `InvariantViolation` is created:
| Field | Description |
|-------|-------------|
| `invariant_id` | ULID of the violated invariant |
| `violated_text` | The invariant text that was violated |
| `severity` | `error`, `warning`, or `info` |
| `details` | Additional violation context |
---
## Python API
The `InvariantService` is registered as a Singleton in the DI container. Obtain it via
`container.invariant_service()` rather than constructing it directly.
```python
from cleveragents.application.services.invariant_service import InvariantService
from cleveragents.domain.models.core.invariant import InvariantScope
service = InvariantService(event_bus=event_bus)
# Add an invariant
inv = service.add_invariant(
text="All output files must be UTF-8 encoded",
scope=InvariantScope.PROJECT,
source_name="my-project",
)
# List effective invariants for a plan
effective = service.get_effective_invariants(
plan_id="01HV...",
project_name="my-project",
)
# Remove an invariant (soft-delete)
service.remove_invariant(invariant_id="01HXYZ...")
```
**Key methods:**
| Method | Returns | Description |
|--------|---------|-------------|
| `add_invariant(text, scope, source_name)` | `Invariant` | Add a new invariant |
| `list_invariants(scope, source_name, effective)` | `list[Invariant]` | Filter invariants |
| `remove_invariant(invariant_id)` | `Invariant` | Soft-delete (sets `active=False`) |
| `get_effective_invariants(plan_id, project_name)` | `list[Invariant]` | Merged precedence chain |
| `enforce_invariants(plan_id, invariants, actor_response, violated_ids)` | `list[InvariantEnforcementRecord]` | Create enforcement records |
---
## See Also
- [`docs/reference/invariants.md`](../reference/invariants.md) — Full invariant domain model reference
- [`docs/api/decisions.md`](decisions.md) — Decision recording and tree CLI reference
- [ADR-016: Invariant System](../adr/ADR-016-invariant-system.md)
+272
View File
@@ -0,0 +1,272 @@
# Plan Correction API (v3.2.0 / v3.3.0)
The correction subsystem allows operators to modify a plan's decision tree after
execution by either **reverting** a subtree of decisions or **appending** new guidance
as a child plan. This page documents the CLI commands, correction modes, and the
subplan system that powers append-mode corrections.
---
## Overview
Plan corrections introduced across **v3.2.0** and **v3.3.0** provide two complementary
modes for adapting a plan's strategy without discarding accumulated context:
| Mode | Effect |
|------|--------|
| `revert` | Invalidates the targeted decision and all descendants; re-executes from that point |
| `append` | Preserves the original decision; spawns a new child plan with operator guidance |
Corrections never mutate existing decisions. Instead, a new `Decision` is created with
`is_correction=True` and `corrects_decision_id` pointing to the original. The original
decision has its `superseded_by` field set to the new decision's ID.
---
## CLI Reference
### `agents plan correct`
Apply a correction to a plan's decision tree.
```bash
agents plan correct <PLAN_ID> --decision <DECISION_ID> --mode <MODE> --guidance <TEXT> [OPTIONS]
```
**Options:**
| Flag | Description |
|------|-------------|
| `--decision` | ULID of the decision to correct (required) |
| `--mode` | Correction mode: `revert` or `append` (required) |
| `--guidance` | Operator guidance text (110,000 characters, required) |
| `--dry-run` | Preview impact without making changes |
| `--yes`, `-y` | Skip the interactive confirmation prompt |
| `--format`, `-f` | Output format: `json`, `yaml`, `plain`, `table`, `rich` |
**Revert mode — re-execute from a targeted decision point:**
```bash
agents plan correct 01HXYZ... \
--decision 01HABC... \
--mode=revert \
--guidance "Use a safer database migration approach"
```
This invalidates the targeted decision and every descendant reachable via BFS traversal.
Associated artifacts are archived and affected child plans are rolled back. The plan then
re-executes from the targeted decision point using the operator's guidance.
**Append mode — add guidance without recomputing:**
```bash
agents plan correct 01HXYZ... \
--decision 01HABC... \
--mode=append \
--guidance "Add input validation to the API endpoint"
```
This preserves the original decision and spawns a **new child plan** rooted at the target
node. The child plan carries the operator's guidance and produces additional decisions
without disturbing the existing tree.
**Dry-run preview:**
```bash
agents plan correct 01HXYZ... \
--decision 01HABC... \
--mode=revert \
--guidance "..." \
--dry-run
```
The dry-run report includes:
- **impact** — Affected decisions, files, child plans, estimated cost, and risk level.
- **decisions_to_invalidate** — Decision IDs that *would* be marked invalid (revert only).
- **child_plans_to_rollback** — Child plans that *would* be rolled back.
- **estimated_recompute_time_seconds** — Wall-clock estimate.
- **warnings** — Human-readable cautions (e.g. high risk).
---
## Correction Modes in Detail
### Revert Mode
Invalidates the targeted decision and every descendant reachable via BFS traversal:
```
┌─── D1 (target) ◄── revert starts here
│ │
│ ┌──┴──┐
│ D2 D3 ← all invalidated
│ │
│ D4 ← also invalidated
```
**Risk classification** based on affected decision count:
| Affected Count | Risk Level |
|----------------|------------|
| ≤ 3 | low |
| 4 10 | medium |
| > 10 | high |
### Append Mode
Preserves the original decision and spawns a new child plan:
```
D1 (target)
┌──┴──────────┐
D2 (original) CP-new ← child plan appended
```
The child plan runs in its own sandbox and its results are merged back into the parent
plan using the configured merge strategy.
---
## Correction Status Lifecycle
```
PENDING → ANALYZING → EXECUTING → APPLIED
→ FAILED
PENDING → CANCELLED
ANALYZING → CANCELLED
```
Each execution of a correction is tracked as a `CorrectionAttemptRecord`. Multiple
attempts may exist for a single correction (e.g. if a first attempt fails and the
operator retries).
---
## Subplan System Overview
The **append** correction mode relies on the subplan system to spawn and execute child
plans. Subplans are also used directly during Strategize when the strategy actor decides
to decompose work into parallel or sequential child plans.
### Spawning Child Plans
When a `subplan_spawn` or `subplan_parallel_spawn` decision is recorded during
Strategize, the `SubplanService` handles the spawn workflow:
1. Extract spawn decisions from `DecisionService`.
2. Build `SpawnEntry` objects from those decisions.
3. Validate resource scopes, merge strategy, and parallelism bounds.
4. Create `SubplanStatus` and `SpawnMetadata` for each entry.
5. Return the result for the caller to persist on the parent plan.
### Execution Modes
| Mode | Description |
|------|-------------|
| `sequential` | Execute one at a time in order |
| `parallel` | Execute concurrently (up to `max_parallel`, default 5) |
| `dependency_ordered` | Respect DAG dependencies via topological sort |
### Three-Way Merge
After subplans complete, their sandbox outputs are merged using the configured
`SubplanMergeStrategy`:
| Strategy | Description |
|----------|-------------|
| `git_three_way` | Three-way merge via `git merge-file` (default) |
| `sequential_apply` | Apply changes in completion order |
| `fail_on_conflict` | Raise `MergeConflictError` on any conflict |
| `last_wins` | Final subplan's output overwrites earlier ones |
The `git_three_way` strategy uses `git merge-file` to combine non-overlapping changes
from different subplans automatically. Overlapping changes produce conflict markers in
the output.
### Subplan Configuration
```yaml
subplan_config:
execution_mode: parallel # sequential | parallel | dependency_ordered
merge_strategy: git_three_way # git_three_way | sequential_apply | fail_on_conflict | last_wins
max_parallel: 5 # 1-50, for parallel mode
fail_fast: false # stop all on first failure
timeout_per_subplan_seconds: ~ # optional per-subplan timeout
retry_failed: true # auto-retry failed subplans
max_retries: 2 # 0-5, max retry attempts
```
---
## Phase Reversion
When corrections cannot be resolved within the current strategy, the plan may revert to
an earlier lifecycle phase:
| Source Phase | Target Phase | Trigger |
|-------------|-------------|---------|
| Execute | Strategize | Validation failures block apply; constraints too restrictive |
| Apply (constrained) | Strategize | Cannot proceed within current strategy constraints |
Phase reversion is subject to a **loop guard**: each plan may revert at most **3 times**
(`Plan.MAX_REVERSIONS`). Once this limit is reached, both automatic and manual
reversions are blocked.
Manual reversion via CLI:
```bash
agents plan revert <plan_id> --to-phase strategize --reason "constraints too strict"
```
---
## Python API
```python
from cleveragents.application.services.correction_service import CorrectionService
from cleveragents.domain.models.core.correction import CorrectionMode
correction_service = CorrectionService(unit_of_work=uow)
# Request a correction
correction = correction_service.request_correction(
plan_id="01HV...",
original_decision_id="01HABC...",
mode=CorrectionMode.REVERT,
guidance="Use a safer database migration approach",
)
# Preview impact (dry run)
report = correction_service.generate_dry_run_report(correction.id)
print(f"Risk: {report.impact.risk_level}, Affected: {len(report.decisions_to_invalidate)}")
# Execute the correction
correction_service.execute_correction(correction.id)
```
**Key service methods:**
| Method | Description |
|--------|-------------|
| `request_correction()` | Create a new correction request |
| `analyze_impact()` | BFS impact analysis on the decision tree |
| `generate_dry_run_report()` | Full report without side effects |
| `execute_revert()` | Invalidate subtree + archive artifacts |
| `execute_append()` | Spawn child plan preserving original decision |
| `execute_correction()` | Dispatch to revert or append based on mode |
| `get_correction()` | Retrieve a correction by ID |
| `list_corrections()` | List corrections (optional plan_id filter) |
| `cancel_correction()` | Cancel a pending/analyzing correction |
---
## See Also
- [`docs/reference/decision_correction.md`](../reference/decision_correction.md) — Full correction domain model reference
- [`docs/reference/subplans.md`](../reference/subplans.md) — Subplan execution and merge strategies
- [`docs/reference/phase_reversion.md`](../reference/phase_reversion.md) — Phase reversion state machine
- [`docs/api/decisions.md`](decisions.md) — Decision recording and tree CLI reference
- [`docs/api/checkpoints.md`](checkpoints.md) — Checkpoint and rollback CLI reference
- [ADR-007: Decision Tree & Correction](../adr/ADR-007-decision-tree-and-correction.md)
+421
View File
@@ -0,0 +1,421 @@
# Autonomy Hardening
> **Milestone:** v3.5.0 — Autonomy Hardening
CleverAgents v3.5.0 introduces comprehensive autonomy hardening, enabling the system to autonomously execute large-scale tasks using hierarchical plan decomposition, parallel execution, and validation-gated apply.
---
## Overview
Autonomy hardening provides the infrastructure for safe, scalable autonomous task execution:
- **Hierarchical decomposition** — Plans decompose into 4+ levels of subplans
- **Parallel execution** — 10+ concurrent subplans execute simultaneously
- **Guard enforcement** — Denylist, budget caps, and tool call limits protect against runaway execution
- **Automation profiles** — Configurable precedence (plan > action > global) controls autonomy level
- **A2A protocol** — Agent-to-Agent facade enables session and plan lifecycle operations via CLI
- **Event queue** — Publish/subscribe system coordinates asynchronous agent communication
> **Note on Server Stubs:** Server stubs previously scoped to this milestone have been moved to M9 (v3.8.0) following the ACP to A2A protocol adoption (ADR-047) and server architecture redesign (ADR-048). TUI features were moved to M8 (v3.7.0).
---
## A2A Protocol (Agent-to-Agent)
The A2A (Agent-to-Agent) protocol provides a standardized facade for session and plan lifecycle operations.
### Overview
A2A replaces the earlier ACP (Agent Communication Protocol) following ADR-047. It provides:
- Standardized session management across agent boundaries
- Plan lifecycle operations (create, start, execute, apply, correct)
- Event-driven communication via publish/subscribe
- Type-safe message schemas
### A2A Facade CLI
The A2A facade exposes session and plan lifecycle operations via the `agents` CLI:
```
# Start an A2A session
agents session start --profile semi-auto
# List active sessions
agents session list
# Execute a plan via A2A facade
agents plan execute PLAN_ID --session SESSION_ID
# Subscribe to plan events
agents events subscribe --plan PLAN_ID
```
### Session Lifecycle
```
CREATE --> ACTIVE --> SUSPENDED --> TERMINATED
|
v
ERRORED
```
Sessions are created with an automation profile that governs the autonomy level for all plans within the session.
### References
- ADR-026: Agent-to-Agent Protocol (A2A)
- ADR-047: A2A Standard Adoption
---
## Event Queue
The event queue provides asynchronous publish/subscribe communication between agents and plan lifecycle components.
### Architecture
```
Publisher Event Queue Subscriber
| | |
|-- publish(event) -----> | |
| |-- deliver(event) ----> |
| | |
```
### Event Types
| Event | Description |
|-------|-------------|
| `PLAN_CREATED` | A new plan was created |
| `STRATEGIZE_STARTED` | Strategy phase began |
| `STRATEGIZE_COMPLETE` | Strategy phase completed |
| `EXECUTE_STARTED` | Execute phase began |
| `EXECUTE_COMPLETE` | Execute phase completed |
| `APPLY_STARTED` | Apply phase began |
| `APPLY_COMPLETE` | Apply phase completed |
| `CORRECTION_APPLIED` | A decision correction was applied |
| `INVARIANT_VIOLATED` | An invariant violation was detected |
| `SUBPLAN_STARTED` | A subplan began execution |
| `SUBPLAN_COMPLETE` | A subplan completed |
| `SUBPLAN_FAILED` | A subplan failed |
| `GUARD_TRIGGERED` | A guard condition was triggered |
### Subscribe to Events
```
# Subscribe to all events for a plan
agents events subscribe --plan PLAN_ID
# Subscribe to specific event types
agents events subscribe --plan PLAN_ID --type SUBPLAN_COMPLETE,SUBPLAN_FAILED
# Subscribe with timeout
agents events subscribe --plan PLAN_ID --timeout 300
```
### Publish Custom Events
```
# Publish a custom event
agents events publish --plan PLAN_ID --type CUSTOM --data '{"key": "value"}'
```
---
## Guard Enforcement
Guards protect against runaway autonomous execution by enforcing configurable limits.
### Guard Types
#### Denylist Guard
Prevents execution of specific tools, commands, or operations:
```toml
[guards.denylist]
tools = ["shell_exec", "file_delete"]
commands = ["rm -rf", "sudo", "chmod 777"]
file_patterns = ["/etc/**", "/sys/**", "~/.ssh/**"]
```
#### Budget Cap Guard
Limits resource consumption during autonomous execution:
```toml
[guards.budget]
max_llm_tokens = 1000000 # 1M tokens per plan
max_llm_cost_usd = 10.00 # $10 per plan
max_wall_time_seconds = 3600 # 1 hour per plan
max_file_writes = 1000 # 1000 file writes per plan
```
#### Tool Call Limit Guard
Limits the number of tool calls during execution:
```toml
[guards.tool_limits]
max_tool_calls_per_subplan = 100
max_tool_calls_per_plan = 10000
max_consecutive_failures = 5
```
### Guard Enforcement Behavior
When a guard is triggered:
1. The current subplan is halted
2. A `GUARD_TRIGGERED` event is published
3. The plan transitions to `ERRORED` state
4. A detailed guard violation report is generated
### Guard Configuration Precedence
Guards can be configured at multiple levels, with more specific configurations taking precedence:
```
Tool-level > Subplan-level > Plan-level > Global
```
---
## Automation Profiles
Automation profiles control the level of autonomy during plan execution.
### Built-in Profiles
| Profile | Description | Human Approval Required |
|---------|-------------|------------------------|
| `manual` | All decisions require human approval | Every decision |
| `semi-auto` | High-risk decisions require approval | Destructive operations |
| `auto` | Fully autonomous execution | None (guards only) |
| `supervised` | Periodic checkpoints for human review | At checkpoints |
### Profile Precedence
Automation profiles are resolved with the following precedence (highest to lowest):
1. **Plan-level** — Set via `agents plan create --profile PROFILE`
2. **Action-level** — Set via `agents action create --profile PROFILE`
3. **Global** — Set in `.cleveragents/config.toml` under `[automation]`
```toml
# Global default
[automation]
default_profile = "semi-auto"
```
```
# Plan-level override (highest precedence)
agents plan create --profile auto "Port the Firefox codebase to Rust"
```
### Custom Profiles
Custom profiles can be defined in `.cleveragents/profiles/`:
```toml
# .cleveragents/profiles/strict.toml
[profile]
name = "strict"
description = "Strict profile for sensitive codebases"
base = "semi-auto"
[profile.overrides]
require_approval_for = ["file_write", "git_commit", "shell_exec"]
max_consecutive_auto_decisions = 3
```
### Profile Resolution
The `_resolve_profile_for_plan` function in `PlanLifecycleService` resolves the effective profile for a plan. If the plan's configured profile name is not a known built-in profile, a `ValidationError` is raised with an actionable error message listing available profiles.
---
## Hierarchical Plan Decomposition
CleverAgents v3.5.0 supports hierarchical plan decomposition with 4+ levels of subplans.
### Decomposition Model
```
Plan (Level 0)
|
+-- Subplan A (Level 1)
| |
| +-- Subplan A.1 (Level 2)
| | |
| | +-- Subplan A.1.1 (Level 3)
| | | |
| | | +-- Subplan A.1.1.1 (Level 4)
| | |
| | +-- Subplan A.1.2 (Level 3)
| |
| +-- Subplan A.2 (Level 2)
|
+-- Subplan B (Level 1)
|
+-- Subplan B.1 (Level 2)
```
### Decomposition Strategy
The strategy actor decomposes plans based on:
1. **Dependency analysis** — Identifies independent work units that can execute in parallel
2. **Complexity estimation** — Estimates effort for each subplan to balance load
3. **Risk assessment** — Groups high-risk operations for sequential execution
4. **Resource constraints** — Respects available compute and memory limits
### Decision Correction with Subtree Recomputation
When a decision correction is applied, only the affected subtree is recomputed:
```
Plan
|
+-- Subplan A (complete)
|
+-- Subplan B (correction applied here)
| |
| +-- Subplan B.1 (RECOMPUTED)
| |
| +-- Subplan B.2 (RECOMPUTED)
|
+-- Subplan C (unaffected, not recomputed)
```
This selective recomputation minimizes wasted work when corrections are needed.
---
## Parallel Execution
CleverAgents v3.5.0 scales parallel execution to 10+ concurrent subplans.
### Execution Model
```
SubplanExecutionService
|
+-- ThreadPoolExecutor (max_workers=N)
|
+-- Worker 1: Subplan A.1
+-- Worker 2: Subplan A.2
+-- Worker 3: Subplan B.1
+-- Worker 4: Subplan B.2
+-- Worker 5: Subplan C.1
...
+-- Worker N: Subplan X.Y
```
### Fail-Fast Cancellation
When `fail_fast=True` and a subplan fails:
1. The `stop_flag` is set
2. Queued subplans are cancelled before starting
3. In-flight subplans that complete after `stop_flag` is set are overridden to `CANCELLED`
4. The merge output excludes cancelled subplan results
### Parallel Execution Configuration
```toml
[execution]
max_parallel_subplans = 10 # Maximum concurrent subplans
fail_fast = true # Cancel remaining subplans on first failure
subplan_timeout_seconds = 600 # 10 minutes per subplan
```
### Monitoring Parallel Execution
```
# Watch parallel execution in real-time
agents plan tree PLAN_ID --watch
# View execution metrics
agents plan show PLAN_ID --format json | jq '.execution_metrics'
```
---
## Large-Scale Autonomous Task Execution
CleverAgents v3.5.0 is validated against realistic large-scale porting tasks.
### Acceptance Criteria
The following acceptance criteria were validated for v3.5.0:
- A2A facade session and plan lifecycle operations functional via CLI
- Event queue publish/subscribe operational
- Guard enforcement works (denylist, budget caps, tool call limits)
- Automation profile resolution precedence correct (plan > action > global)
- Full autonomy acceptance flow with hierarchical decomposition (4+ levels)
- Parallel execution scales to 10+ concurrent subplans
- A realistic porting task completes autonomously
- `nox` passes with coverage >= 97% including large-project suites
### Example: Autonomous Codebase Porting
```
# Create a plan for a large-scale porting task
agents plan create --profile auto \
"Port the Firefox rendering engine from C++ to Rust, \
maintaining all existing test coverage and API compatibility"
# Link the source project
agents plan link PLAN_ID --resource firefox-source
# Execute autonomously
agents plan execute PLAN_ID
# Monitor progress
agents plan tree PLAN_ID --watch
```
### Performance at Scale
| Metric | v3.4.0 | v3.5.0 |
|--------|--------|--------|
| Max parallel subplans | 4 | 10+ |
| Max decomposition depth | 2 | 4+ |
| Decision correction scope | Full recompute | Subtree only |
| Large project support | 10K files | 50K+ files |
---
## Troubleshooting
### Guard Violations
If a guard is triggered unexpectedly:
1. Check the guard violation report: `agents plan show PLAN_ID --format json | jq '.guard_violations'`
2. Review guard configuration: `agents config show guards`
3. Adjust guard limits if appropriate: `agents config set guards.budget.max_llm_tokens 2000000`
### Automation Profile Errors
If you see `ValidationError: unknown automation profile`:
- This was fixed in v3.5.0 (automation profile silent fallback fix)
- Ensure you are using a built-in profile: `manual`, `semi-auto`, `auto`, or `supervised`
- Custom profiles must be defined in `.cleveragents/profiles/`
### Parallel Execution Failures
If parallel subplans are not being cancelled on failure:
- This was fixed in v3.5.0 (SubplanExecutionService fail_fast cancellation fix)
- Ensure you are running v3.5.0 or later
- Check `fail_fast` configuration: `agents config show execution.fail_fast`
---
*See also: [A2A Protocol API Reference](api/a2a.md) | [Actor System](api/actor.md) | [ADR-047: A2A Standard Adoption](adr/ADR-047-acp-standard-adoption.md)*
+313
View File
@@ -0,0 +1,313 @@
# Advanced Context Management System (ACMS)
> **Milestone:** v3.4.0 — ACMS v1 + Context Scaling
The Advanced Context Management System (ACMS) provides intelligent, budget-aware context assembly for CleverAgents. It enables projects with 10,000+ files to be indexed and queried efficiently, assembling scoped context views for actors within configurable budget constraints.
---
## Overview
ACMS solves the fundamental challenge of providing LLM actors with the right context at the right time, without exceeding token budgets or degrading performance on large codebases.
Key capabilities:
- **Large-scale indexing** — Projects with 10,000+ files index without timeout
- **Budget enforcement** — Configurable `max_file_size` and `max_total_size` constraints
- **Tiered storage** — Hot/warm/cold storage tiers manage context lifecycle
- **Scoped assembly** — Context views are scoped per actor and per resource
- **CLI integration** — Full `agents context` command suite
---
## Context Policies
Context policies define how context is assembled for a given view. Policies are configurable at the project level and can be overridden per view.
### Configuration
Context policies are stored in `.cleveragents/config.toml` under the `[context]` section:
```toml
[context]
# Maximum size of a single file to include in context (bytes)
max_file_size = 262144 # 256 KB default
# Maximum total size of all context fragments combined (bytes)
max_total_size = 10485760 # 10 MB default
# Storage tier thresholds
hot_tier_max_fragments = 50
warm_tier_max_fragments = 200
cold_tier_max_fragments = 2000
# File patterns to exclude from indexing
exclude_patterns = [
".git/**",
"node_modules/**",
"__pycache__/**",
"*.pyc",
"*.bin",
"*.exe",
]
```
### View-Specific Settings
Individual context views can override global policy settings:
```toml
[context.views.execute]
max_file_size = 131072 # 128 KB for execute view
max_total_size = 5242880 # 5 MB for execute view
[context.views.strategize]
max_file_size = 524288 # 512 KB for strategize view
max_total_size = 20971520 # 20 MB for strategize view
```
---
## Budget Enforcement
ACMS enforces two budget constraints during context assembly:
### `max_file_size`
Controls the maximum size of any single file included in a context fragment. Files exceeding this limit are excluded from context assembly.
- **Default:** 256 KB (`262144` bytes)
- **Purpose:** Prevents single large files from consuming the entire context budget
- **Behavior:** Files exceeding the limit are logged at `DEBUG` level and skipped
### `max_total_size`
Controls the maximum combined size of all context fragments in a single assembled view.
- **Default:** 10 MB (`10485760` bytes)
- **Purpose:** Ensures the assembled context fits within LLM token budgets
- **Behavior:** Once the total budget is reached, lower-priority fragments are excluded
### Binary File Exclusion
Binary files are automatically excluded from context assembly regardless of size settings. Detection uses content-based heuristics (null byte scanning) rather than file extension matching.
---
## CLI Commands
The `agents context` command suite provides full control over context management.
### `agents context list`
Lists all context fragments currently in the tier service, grouped by storage tier.
```
agents context list [--plan PLAN_ID] [--format {table,json,plain}]
```
**Options:**
| Option | Description |
|--------|-------------|
| `--plan PLAN_ID` | Filter fragments by plan ID |
| `--format` | Output format (default: `table`) |
### `agents context add`
Manually adds a file or directory to the context tier service.
```
agents context add PATH [--tier {hot,warm,cold}] [--plan PLAN_ID]
```
**Options:**
| Option | Description |
|--------|-------------|
| `PATH` | File or directory path to add |
| `--tier` | Target storage tier (default: `warm`) |
| `--plan PLAN_ID` | Associate fragment with a specific plan |
**Example:**
```
# Add a single file to hot tier
agents context add src/critical_module.py --tier hot
# Add a directory to warm tier
agents context add src/utils/ --tier warm --plan 01HXYZ...
```
### `agents context show`
Displays detailed information about a specific context fragment or the assembled context view for a plan.
```
agents context show [FRAGMENT_ID | --plan PLAN_ID] [--format {table,json,plain}]
```
**Options:**
| Option | Description |
|--------|-------------|
| `FRAGMENT_ID` | Show details for a specific fragment |
| `--plan PLAN_ID` | Show assembled context view for a plan |
| `--format` | Output format (default: `table`) |
### `agents context clear`
Removes context fragments from the tier service.
```
agents context clear [--tier {hot,warm,cold,all}] [--plan PLAN_ID] [--confirm]
```
**Options:**
| Option | Description |
|--------|-------------|
| `--tier` | Clear only the specified tier (default: `all`) |
| `--plan PLAN_ID` | Clear only fragments for a specific plan |
| `--confirm` | Skip confirmation prompt |
---
## Hot/Warm/Cold Storage Tiers
ACMS uses a three-tier storage model to manage context lifecycle efficiently.
### Tier Definitions
| Tier | Description | Access Speed | Eviction Policy |
|------|-------------|--------------|-----------------|
| **Hot** | Actively used fragments for the current actor | Immediate | LRU when capacity exceeded |
| **Warm** | Recently accessed fragments, likely to be reused | Fast | LRU + staleness-based |
| **Cold** | Indexed but infrequently accessed fragments | Slower | Age-based + LRU |
### Tier Promotion and Demotion
Fragments move between tiers automatically based on access patterns:
- **Promotion:** A fragment accessed from `warm` or `cold` is promoted toward `hot`
- **Demotion:** Fragments not accessed within the staleness window are demoted
- **Eviction:** When a tier reaches capacity, the least-recently-used fragment is evicted to the next tier or removed
### Thread Safety
`ContextTierService` uses a reentrant lock (`threading.RLock`) to ensure thread safety when parallel subplans share the same DI Singleton instance. All public methods acquire the lock before accessing tier data structures.
---
## Context Assembly Pipeline
The context assembly pipeline transforms raw file content into scoped, budget-constrained context views for LLM actors.
### Pipeline Stages
1. **Hydration**`git ls-files` / `os.walk` enumerates project files into `TieredFragment` objects
2. **Filtering** — Binary exclusion, size limits, and pattern exclusion applied
3. **Tiering** — Fragments assigned to hot/warm/cold based on relevance score
4. **Assembly** — Budget-constrained selection with scope application produces the final context view
### Context Hydration
The `context_tier_hydrator.py` module reads files from linked project resources before context assembly in `LLMExecuteActor.execute()`. It:
1. Resolves project resource paths via `plan.project_links`
2. Enumerates files using `git ls-files` (for git projects) or `os.walk` (for non-git projects)
3. Applies size and binary filters
4. Creates `TieredFragment` objects with metadata
5. Stores fragments in `ContextTierService`
### Scoped Context Views
Context views are scoped per actor and per resource to prevent cross-contamination between parallel subplans.
---
## Large Project Indexing
ACMS is designed to handle projects with 10,000+ files without timeout.
### Performance Characteristics
| Project Size | Indexing Time | Memory Usage |
|-------------|---------------|--------------|
| 1,000 files | < 1 second | ~50 MB |
| 10,000 files | < 10 seconds | ~200 MB |
| 50,000 files | < 60 seconds | ~800 MB |
### Optimization Strategies
1. **Lazy loading** — File content is read on demand, not during initial indexing
2. **Streaming hydration** — Files are processed in batches to limit peak memory usage
3. **Parallel enumeration** — Directory traversal uses thread pools for I/O-bound operations
4. **Incremental updates** — Only changed files (via `git diff`) are re-indexed on subsequent runs
---
## Integration with Plan Execution
ACMS integrates directly with the plan execution pipeline to provide LLM actors with relevant context.
### Automatic Context Injection
When `agents plan execute` runs, ACMS automatically:
1. Hydrates the context tier from project resources
2. Assembles a scoped context view for the execute actor
3. Injects the context view into the LLM prompt
4. Tracks which fragments were used for observability
### Context in Strategy Phase
During `agents plan strategize`, ACMS provides a broader context view to help the strategy actor understand the full project structure before decomposing the plan.
### Context in Apply Phase
During `agents plan apply`, ACMS provides targeted context for validation actors to verify that applied changes are consistent with the project.
### Configuration Example
```toml
[actor.execute]
context_view = "execute"
context_budget_bytes = 5242880
[actor.strategize]
context_view = "strategize"
context_budget_bytes = 20971520
```
---
## Troubleshooting
### Context is Empty
If the LLM receives no file context, check:
1. **Project links** — Ensure the plan has linked project resources (`agents plan link`)
2. **File permissions** — Ensure the agent process can read project files
3. **Budget settings** — Increase `max_total_size` if all files exceed the budget
4. **Exclusion patterns** — Check that source files are not matched by exclusion patterns
### Indexing Timeout
For very large projects:
1. Increase the hydration timeout in config: `context.hydration_timeout_seconds = 120`
2. Add more specific exclusion patterns to reduce the file count
3. Use `agents context add` to manually add only the most relevant files
### Thread Safety Errors
If you see `RuntimeError: dictionary changed size during iteration`:
- This was fixed in v3.4.0 (see `ContextTierService` thread safety fix)
- Ensure you are running v3.4.0 or later
---
*See also: [ACMS / UKO API Reference](api/acms.md) | [Context Hydration Module](modules/context-hydration.md)*
+79 -52
View File
@@ -10,26 +10,27 @@ The following chart shows all 29 epics across 9 legendary workstreams, 7 milesto
@startgantt
title CleverAgents Core — Epic-Level Project Schedule
footer Generated 2026-04-10 | 29 Epics | 9 Legendaries | 7 Milestones | 6 Developers | ~1650 SP | ~25 open bugs | 225 open PRs | Session 4 active
footer Generated 2026-04-14 | 29 Epics | 9 Legendaries | 7 Milestones | 6 Developers | ~1650 SP | ~435 open PRs | 3956 open issues | AUTO-TIME-1
Project starts 2026-02-03
saturday are closed
sunday are closed
printscale weekly zoom 2
today is 2026-04-10
today is 2026-04-14
today is colored in #FF6666
' ================================================================
' GANTT CHART UPDATE LOG (Day 100 — 2026-04-10)
' Changes: Day 100 refresh (cycle 2). Session 4 active (issue #4799, 32 workers).
' Open PRs: 221→225 (new PRs opened by agents). Open bugs: ~25 (Type/Bug label).
' M3 32% (249/770, scope expanded!), M4 49% (108/220), M5 43% (133/313),
' M6 18% (197/1085, scope massively expanded!), M7 38% (150/400),
' M8 45% (425/944), M9 28% (131/475).
' Massive scope expansion across all milestones since Day 98.
' pr-merge-pool-supervisor added as 17th supervisor. PR review reduced to 1 approval.
' git worktree sandbox merged. ACMS indexing pipeline wired.
' GANTT CHART UPDATE LOG (Day 104 — 2026-04-14) [AUTO-TIME-1]
' Changes: Schedule adherence check. v3.0.0 CLOSED (164 issues, 100%).
' v3.1.0 CLOSED (108 issues, 100%). Active milestones v3.2.0v3.9.0.
' M3 (v3.2.0) 21.9% (273/1247, 974 open). M4 (v3.3.0) 34.8% (111/319, 208 open).
' M5 (v3.4.0) 34.7% (142/409, 267 open). M6 (v3.5.0) 16.3% (220/1350, 1130 open).
' M7 (v3.6.0) 31.2% (153/491, 338 open). M8 (v3.7.0) 41.2% (428/1040, 612 open).
' M9 (v3.8.0) 26.6% (135/508, 373 open). v3.9.0 6.9% (4/58, 54 open).
' Total open issues: 3,956. Open PRs: ~435. Open Bugs: ~3.
' Milestones v3.2.0v3.6.0 are past their planned due dates.
' v3.7.0v3.9.0 have no assigned deadlines.
' ================================================================
<style>
@@ -231,7 +232,7 @@ ganttDiagram {
[Decisions + Validations M3 (#357)] as [M3] on {Luis} {Hamza} {Brent} {Jeff} requires 4 days
[M3] starts at [M2]'s end
[M3] is 65% completed
[M3] is 25% completed
[M3] is colored in LightSkyBlue/SteelBlue
' ── v3.3.0 MILESTONE ─────────────────────────────────────────
@@ -252,13 +253,13 @@ ganttDiagram {
[Corrections + Checkpoints M4 (#358)] as [M4] on {Luis} {Jeff} {Brent} {Hamza} requires 3 days
[M4] starts at [M3]'s end
[M4] is 60% completed
[M4] is 40% completed
[M4] is colored in LightSkyBlue/SteelBlue
[Security + Safety Hardening (#362)] as [SEC] on {Luis} requires 3 days
[SEC] starts 2026-03-02
[SEC] is 80% completed
[SEC] is 36% completed
[SEC] is colored in LightSkyBlue/SteelBlue
[Provider Fixes + Runtime (#363)] as [PROV] on {Luis} requires 2 days
@@ -283,12 +284,12 @@ ganttDiagram {
[ACMS v1 + Context Scaling M5 (#359)] as [M5] on {Hamza} {Jeff} {Aditya} {Brent} requires 4 days
[M5] starts at [M4]'s end
[M5] is 68% completed
[M5] is 36% completed
[M5] is colored in LightSkyBlue/SteelBlue
[Autonomy Hardening + Stubs M6 (#360)] as [M6] on {Luis} {Jeff} {Hamza} {Brent} requires 5 days
[M6] starts at [M5]'s end
[M6] is 55% completed
[M6] is 17% completed
[M6] is colored in LightSkyBlue/SteelBlue
@@ -299,7 +300,7 @@ ganttDiagram {
[Large Project Autonomy (#369)] as [LARGE] on {Luis} {Brent} {Jeff} requires 4 days
[LARGE] starts 2026-03-17
[LARGE] is 43% completed
[LARGE] is 42% completed
[LARGE] is colored in LightSkyBlue/SteelBlue
' ── v3.5.0 MILESTONE ─────────────────────────────────────────
@@ -319,13 +320,13 @@ ganttDiagram {
[Post-MVP Deferred Work (#366)] as [POST] on {Luis} {Hamza} {Jeff} {Rui} {Brent} requires 8 days
[POST] starts at [M6]'s end
[POST] is 0% completed
[POST] is colored in #E8E8E8/Silver
[POST] is 33% completed
[POST] is colored in LightSkyBlue/SteelBlue
[Multi-Agent RDF System (#367)] as [RDF] on {Aditya} requires 5 days
[RDF] starts 2026-03-24
[RDF] is 0% completed
[RDF] is colored in #E8E8E8/Silver
[RDF] is 33% completed
[RDF] is colored in LightSkyBlue/SteelBlue
' ── v3.6.0 MILESTONE ─────────────────────────────────────────
' Gate: All post-MVP deferred work
@@ -401,15 +402,18 @@ legend right
| v3.5.0 | Mar 10 | 2 | 151 | Autonomy hardening + server stubs |
| v3.6.0 | Mar 27 | 2 | 100 | Deferred work |
----
**Risk Register (Day 100 — 2026-04-10)**
|= Epic |= Completion |= Risk |= Blocker |
| M3 (#357) | 32% | **CRITICAL** | 521 open issues in v3.2.0 (scope expanded 320→770) |
| M4 (#358) | 49% | **CRITICAL** | 112 open issues in v3.3.0 |
| M5 (#359) | 43% | **HIGH** | 180 open issues in v3.4.0 |
| M6 (#360) | 18% | **CRITICAL** | 888 open issues in v3.5.0 (massive scope expansion) |
| M7 (#361) | 38% | **HIGH** | 250 open issues in v3.6.0 |
| M8 (#362) | 45% | **HIGH** | 519 open issues in v3.7.0 |
| SEC (#363) | 28% | **HIGH** | 344 open issues in v3.8.0 |
**Risk Register (Day 104 — 2026-04-14) [AUTO-TIME-1]**
|= Milestone |= Completion |= Risk |= Blocker |
| v3.0.0 | 100% ✅ | **CLOSED** | Complete — 164 issues closed |
| v3.1.0 | 100% ✅ | **CLOSED** | Complete — 108 issues closed |
| v3.2.0 | 21.9% | **CRITICAL** | 974 open issues; past due 2026-02-26 |
| v3.3.0 | 34.8% | **CRITICAL** | 208 open issues; past due 2026-03-02 |
| v3.4.0 | 34.7% | **CRITICAL** | 267 open issues; past due 2026-03-06 |
| v3.5.0 | 16.3% | **CRITICAL** | 1130 open issues; past due 2026-03-10 |
| v3.6.0 | 31.2% | **HIGH** | 338 open issues; past due 2026-03-28 |
| v3.7.0 | 41.2% | **HIGH** | 612 open issues; no deadline |
| v3.8.0 | 26.6% | **HIGH** | 373 open issues; no deadline |
| v3.9.0 | 6.9% | **MEDIUM** | 54 open issues; no deadline |
----
**Color Key**
|= Color |= Meaning |
@@ -417,11 +421,12 @@ legend right
| <back:LightSkyBlue> </back> | In Progress |
| <back:#E8E8E8> </back> | Not Started |
| <back:Gold> <> </back> | Milestone Target |
| <back:#FF6666> | </back> | Today (2026-04-10) |
| <back:#FF6666> | </back> | Today (2026-04-14) |
----
29 Epics | 9 Legendaries | 7 Milestones | 6 Developers
~1649 total story points | ~3300 estimated hours
**~25 open bugs (Type/Bug) — 225 open PRs — Session 4 active (32 workers)**
**v3.0.0 ✅ CLOSED (164 issues) — v3.1.0 ✅ CLOSED (108 issues)**
**3,956 open issues — ~435 open PRs — v3.2.0v3.9.0 active [AUTO-TIME-1 2026-04-14]**
end legend
@endgantt
@@ -445,7 +450,7 @@ saturday are closed
sunday are closed
printscale weekly zoom 2
today is 2026-04-10
today is 2026-04-14
today is colored in #FF6666
<style>
@@ -1950,11 +1955,11 @@ legend right
| <back:LightSkyBlue> </back> | Issue: In Progress |
| <back:#E8E8E8> </back> | Issue: Not Started |
| <back:Gold> <> </back> | Milestone Target |
| <back:#FF6666> | </back> | Today (2026-04-10) |
| <back:#FF6666> | </back> | Today (2026-04-14) |
----
271 Issues | 29 Epics | 9 Legendaries | 7 Milestones
~1649 total story points | ~3300 estimated hours
**~5 open bugs (Type/Bug) — 219 open PRs — Session 4 active (32 workers)**
**v3.0.0 ✅ CLOSED — v3.1.0 ✅ CLOSED — 3,956 open issues — ~435 open PRs [AUTO-TIME-1 2026-04-14]**
end legend
@endgantt
@@ -1966,13 +1971,13 @@ This section provides a high-level overview of the CleverAgents implementation r
### Current Status Summary
As of Day 100 (2026-04-10), the project has **225 open PRs** and **~2777 open issues** across active milestones. **Session 4 is active (launched 2026-04-08, issue #4799) with 32 parallel workers and full supervisor fleet**. Bug count is **~25 open bugs** (Type/Bug label; UAT issues tracked separately). M8 (v3.7.0) is now **45% complete** (425/944 issues closed). M3 (v3.2.0) at **32%** (249/770 — scope massively expanded), M4 (v3.3.0) at **49%** (108/220), M5 (v3.4.0) at **43%** (133/313), M6 (v3.5.0) at **18%** (197/1085 — scope massively expanded), M7 (v3.6.0) at **38%** (150/400).
As of 2026-04-14 [AUTO-TIME-1], the project has **~435 open PRs** and **3,956 open issues** across active milestones. **v3.0.0 and v3.1.0 are CLOSED and 100% complete.** Active development spans v3.2.0v3.9.0. Milestones v3.2.0v3.6.0 are past their planned due dates. v3.7.0v3.9.0 have no assigned deadlines.
**M1, M2 fully complete. M3 (v3.2.0)**: 33% complete (248/757), 509 open issues — scope expanded massively. **M4 (v3.3.0)**: 49% complete (108/220), 112 open issues. **M5 (v3.4.0)**: 44% complete (133/301), 168 open issues. **M6 (v3.5.0)**: 18% complete (197/1065), 868 open issues — scope expanded massively. **M7 (v3.6.0)**: 38% complete (150/392), 242 open issues. **M8 (v3.7.0)**: 45% complete (423/938), 515 open issues. All milestones M3-M7 are **overdue**; M8 is in active v3.7.0 development.
**v3.0.0 ✅ CLOSED** (164 issues, 100% complete). **v3.1.0 ✅ CLOSED** (108 issues, 100% complete). **v3.2.0**: 21.9% complete (273 closed, 974 open) — past due 2026-02-26. **v3.3.0**: 34.8% complete (111 closed, 208 open) — past due 2026-03-02. **v3.4.0**: 34.7% complete (142 closed, 267 open) — past due 2026-03-06. **v3.5.0**: 16.3% complete (220 closed, 1,130 open) — past due 2026-03-10. **v3.6.0**: 31.2% complete (153 closed, 338 open) — past due 2026-03-28. **v3.7.0**: 41.2% complete (428 closed, 612 open) — no deadline. **v3.8.0**: 26.6% complete (135 closed, 373 open) — no deadline. **v3.9.0**: 6.9% complete (4 closed, 54 open) — no deadline.
!!! warning "Schedule Risk: All Milestones M3-M7 Overdue — Massive Scope Expansion — Session 4 Active (Day 100: 219 Open PRs, M3/M6 Scope Exploded)"
!!! warning "Schedule Risk: v3.0.0 and v3.1.0 Complete — Milestones v3.2.0v3.6.0 Overdue — 3,956 Open Issues — ~435 Open PRs [AUTO-TIME-1 2026-04-14]"
All milestones M3 through M7 have passed their target dates. **Session 4 active (launched 2026-04-08, issue #4799) with 32 parallel workers**. Open PRs at **219** (up from 1 — agents opened many new PRs). Open bugs at **~5** (Type/Bug label). Session tracker issue: #4799. **Current priorities**: (1) **M3 scope explosion**509 open issues (was 85 at Day 98); scope grew from 320 to 757 total. (2) **M6 scope explosion**868 open issues (was 450 at Day 98); scope grew from 638 to 1065 total. (3) **Merge 219 open PRs** — pr-merge-pool-supervisor added as 17th supervisor. (4) **Continue M8 push** — 45% complete, 515 issues remaining. (5) **Clear M4/M5 backlogs** — M4 at 49% (112 open), M5 at 44% (168 open).
**v3.0.0 ✅ CLOSED** (164 issues). **v3.1.0 ✅ CLOSED** (108 issues). All milestones v3.2.0 through v3.6.0 have passed their target dates. Open PRs at **~435**. Total open issues: **3,956**. Open Bugs: **~3**. **Current priorities**: (1) **v3.2.0**974 open issues, 21.9% complete, past due 2026-02-26. (2) **v3.5.0**1,130 open issues, 16.3% complete, past due 2026-03-10. (3) **v3.7.0** — 612 open issues, 41.2% complete, no deadline. (4) **v3.3.0/v3.4.0/v3.6.0** — 208/267/338 open issues respectively. (5) **v3.8.0/v3.9.0** — 373/54 open issues, no deadlines.
### Parallel Workstreams
@@ -2140,18 +2145,21 @@ The following areas are substantially implemented and are no longer blocking:
The milestones and their target dates are:
| Milestone | Version | Target Date | Day # | Focus |
|-----------|---------|------------|-------|-------|
| M1 | v3.0.0 | 2026-02-15 | Day 7 | Minimal local source-code workflow |
| M2 | v3.1.0 | 2026-02-22 | Day 14 | Actor graphs + tool sources |
| M3 | v3.2.0 | 2026-02-26 | Day 18 | Decisions + validations + invariants |
| M4 | v3.3.0 | 2026-03-02 | Day 22 | Corrections + subplans + checkpoints |
| M5 | v3.4.0 | 2026-03-06 | Day 26 | ACMS v1 + context scaling |
| M6 | v3.5.0 | 2026-03-10 | Day 30 | Autonomy hardening |
| M7 | v3.6.0 | 2026-03-28 | Day 48 | Advanced Concepts & Deferred Features |
| M8 | v3.7.0 | Not set | N/A | TUI Implementation |
| M9 | v3.8.0 | Not Set | N/A | Server Implementation |
| Post-MVP | Not Set | Not Set | N/A | Deferred work |
| Milestone | Version | Target Date | Day # | Focus | Status |
|-----------|---------|------------|-------|-------|--------|
| M1 | v3.0.0 | 2026-02-15 | Day 7 | Minimal local source-code workflow | ✅ CLOSED (164 issues) |
| M2 | v3.1.0 | 2026-02-22 | Day 14 | Actor graphs + tool sources | ✅ CLOSED (108 issues) |
| M3 | v3.2.0 | 2026-02-26 | Day 18 | Decisions + validations + invariants | ⚠️ 21.9% (974 open) |
| M4 | v3.3.0 | 2026-03-02 | Day 22 | Corrections + subplans + checkpoints | ⚠️ 34.8% (208 open) |
| M5 | v3.4.0 | 2026-03-06 | Day 26 | ACMS v1 + context scaling | ⚠️ 34.7% (267 open) |
| M6 | v3.5.0 | 2026-03-10 | Day 30 | Autonomy hardening | ⚠️ 16.3% (1130 open) |
| M7 | v3.6.0 | 2026-03-28 | Day 48 | Advanced Concepts & Deferred Features | ⚠️ 31.2% (338 open) |
| M8 | v3.7.0 | Not set | N/A | TUI Implementation | 🔄 41.2% (612 open) |
| M9 | v3.8.0 | Not Set | N/A | Server Implementation | 🔄 26.6% (373 open) |
| M10 | v3.9.0 | Not Set | N/A | Extended features | 🔄 6.9% (54 open) |
| Post-MVP | Not Set | Not Set | N/A | Deferred work | 🔄 In Progress |
_Table updated by [AUTO-TIME-1] on 2026-04-14_
Day numbers are relative to the project kickoff on **February 9, 2026**.
@@ -2291,7 +2299,7 @@ Items without an assigned milestone represent backlog work to be addressed after
### Schedule Risk Summary
As of Day 96 (2026-04-06), all milestones M3 through M7 are **overdue**. The original 7-8 day slippage has grown to **20+ days** for M3-M6. **Session 3 is active with 16 parallel workers (~71 total agents)**. Open PRs now at **108** (down from 183 — 75 PRs merged/closed!). Open bugs at **~878** (stable). M8 (v3.7.0) at **46% complete** (418/917 closed). M9 (v3.8.0) at **28% complete** (131/467 closed). Session tracker: #3775. Critical path: PR #3774 (Click 8.2+ fix) must be merged; 108 open PRs need review and merge; M3/M4/M5/M6/M7 all overdue; M8 at 46% with 499 issues remaining; ~878 open bugs need TDD counterparts and systematic triage.
As of 2026-04-14 [AUTO-TIME-1], **v3.0.0 and v3.1.0 are CLOSED and 100% complete**. All milestones v3.2.0 through v3.6.0 are **overdue**. Open PRs at **~435**. Total open issues: **3,956** across v3.2.0v3.9.0. Open Bugs: **~3**. v3.2.0 at **21.9%** (273/1247 closed, 974 open). v3.3.0 at **34.8%** (111/319 closed, 208 open). v3.4.0 at **34.7%** (142/409 closed, 267 open). v3.5.0 at **16.3%** (220/1350 closed, 1,130 open). v3.6.0 at **31.2%** (153/491 closed, 338 open). v3.7.0 at **41.2%** (428/1040 closed, 612 open). v3.8.0 at **26.6%** (135/508 closed, 373 open). v3.9.0 at **6.9%** (4/58 closed, 54 open). Milestones v3.7.0v3.9.0 have no assigned deadlines.
**Day 50 planning actions (completed)**: (a) 2 reviewers assigned to all 20 open PRs. (b) Comments posted on #1154 and #1168 recommending closure. (c) Escalation comments on PRs with missing branches. (d) Issue #1206 created for `@tdd_expected_fail` tag cleanup. (e) Timeline updated with Day 50 schedule adherence entry.
@@ -5316,3 +5324,22 @@ Story points per developer per milestone.
| M9 | N/A | N/A | N/A | N/A | N/A | N/A | N/A | ~TBD SP |
| M6+ | N/A | N/A | N/A | N/A | N/A | N/A | N/A | ~TBD SP |
| **Total** | N/A | N/A | N/A | N/A | N/A | N/A | N/A | ~1649 SP |
### 2026-04-14 — Schedule Adherence Check [AUTO-TIME-1]
| Milestone | State | Open Issues | Closed Issues | Completion | Status |
|-----------|--------|-------------|---------------|------------|--------|
| v3.0.0 | CLOSED | 0 | 164 | 100% | ✅ Complete |
| v3.1.0 | CLOSED | 0 | 108 | 100% | ✅ Complete |
| v3.2.0 | OPEN | 974 | 273 | 21.9% | ⚠️ Behind (due 2026-02-26) |
| v3.3.0 | OPEN | 208 | 111 | 34.8% | ⚠️ Behind (due 2026-03-02) |
| v3.4.0 | OPEN | 267 | 142 | 34.7% | ⚠️ Behind (due 2026-03-06) |
| v3.5.0 | OPEN | 1130 | 220 | 16.3% | ⚠️ Behind (due 2026-03-10) |
| v3.6.0 | OPEN | 338 | 153 | 31.2% | ⚠️ Behind (due 2026-03-28) |
| v3.7.0 | OPEN | 612 | 428 | 41.2% | 🔄 In Progress (no deadline) |
| v3.8.0 | OPEN | 373 | 135 | 26.6% | 🔄 In Progress (no deadline) |
| v3.9.0 | OPEN | 54 | 4 | 6.9% | 🔄 In Progress (no deadline) |
**Summary**: v3.0.0 and v3.1.0 are complete. Active development spans v3.2.0v3.9.0 with 3,956 open issues, ~435 open PRs, and ~3 open bugs. Milestones v3.2.0v3.6.0 are past their planned due dates. v3.7.0v3.9.0 have no assigned deadlines.
_Updated by [AUTO-TIME-1] on 2026-04-14_
+9
View File
@@ -258,6 +258,15 @@ Feature: Subplan Execution and Merge
Then the first subplan should be errored
And the remaining subplans should have CANCELLED status
@parallel @cancel_status
Scenario: Parallel fail_fast marks in-flight futures as CANCELLED
Given a parent plan with 3 subplans in parallel mode with fail_fast
And the subplan executor will block for 1 seconds
And the first subplan will fail with "ValidationError: schema mismatch"
When the subplans are executed
Then the first subplan should be errored
And the remaining subplans should have CANCELLED status
# --- Dependency-ordered concurrent execution ---
@dependency_ordered @concurrent
@@ -0,0 +1,15 @@
Feature: Plan Lifecycle Decision Root Type
As a developer
I want to ensure that the root decision recorded during start_strategize is prompt_definition
So that the decision tree has the correct root node type
Background:
Given I have a plan lifecycle service with decision service
Scenario: start_strategize records prompt_definition as root decision
Given an action "local/test-action" with description "Test action description"
And a plan created from "local/test-action"
When I start strategize on the plan
Then the root decision should be recorded with type "prompt_definition"
And the root decision question should be "What is the plan prompt?"
And the root decision chosen_option should contain the plan description
+6
View File
@@ -23,6 +23,10 @@ nav:
- Configuration: api/config.md
- AI Providers: api/providers.md
- TUI: api/tui.md
- Decisions: api/decisions.md
- Invariants: api/invariants.md
- Checkpoints: api/checkpoints.md
- Plan Corrections: api/plan-corrections.md
- Modules:
- Shell Safety: modules/shell-safety.md
- UKO Provenance Tracking: modules/uko-provenance.md
@@ -41,6 +45,8 @@ nav:
- Automation Tracking: development/automation-tracking.md
- Custom Sandbox Strategy: development/custom_sandbox_strategy.md
- Documentation Writer: development/docs-writer.md
- Context Management (ACMS): context.md
- Autonomy Hardening: autonomy.md
- Implementation Timeline: timeline.md
- FAQ: faq.md
- Reference: reference/
@@ -1387,11 +1387,14 @@ class PlanLifecycleService:
self._commit_plan(plan)
self._logger.info("Strategize started", plan_id=plan_id)
# Record the root decision: prompt_definition
# This represents the plan's prompt/description and is the
# root of the decision tree
self._try_record_decision(
plan_id=plan_id,
decision_type="strategy_choice",
question="Which strategy should the plan follow?",
chosen_option=f"Begin strategize phase for plan {plan_id}",
decision_type="prompt_definition",
question="What is the plan prompt?",
chosen_option=plan.description or plan.action_name or plan_id,
)
return plan
@@ -301,6 +301,7 @@ class SubplanExecutionService:
completion_order: list[str] = []
stop_flag = False
timeout = self._config.timeout_per_subplan_seconds
status_map = {status.subplan_id: status for status in statuses}
with ThreadPoolExecutor(max_workers=max_workers) as pool:
future_to_id: dict[Future[tuple[SubplanStatus, dict[str, str]]], str] = {}
@@ -315,20 +316,26 @@ class SubplanExecutionService:
for future in as_completed(future_to_id):
subplan_id = future_to_id[future]
original_status = status_map[subplan_id]
try:
result_status, output = future.result()
except CancelledError:
result_status = self._cancel_status(
next(s for s in statuses if s.subplan_id == subplan_id)
)
result_status = self._cancel_status(original_status)
output = {}
except Exception as exc: # pragma: no cover - defensive
result_status = self._error_status(
next(s for s in statuses if s.subplan_id == subplan_id),
original_status,
str(exc),
)
output = {}
if stop_flag and result_status.status not in (
ProcessingState.ERRORED,
ProcessingState.CANCELLED,
):
result_status = self._cancel_status(original_status)
output = {}
results_map[subplan_id] = (result_status, output)
completion_order.append(subplan_id)