docs(spec): clarify layer boundary DI exception, ULID scope, and fill TUI/ACMS gaps [AUTO-ARCH-1]
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 1m19s
CI / benchmark-regression (pull_request) Failing after 30s
CI / typecheck (pull_request) Successful in 1m32s
CI / quality (pull_request) Successful in 1m31s
CI / build (pull_request) Successful in 30s
CI / security (pull_request) Successful in 1m46s
CI / helm (pull_request) Successful in 50s
CI / push-validation (pull_request) Successful in 22s
CI / e2e_tests (pull_request) Successful in 3m46s
CI / integration_tests (pull_request) Successful in 4m44s
CI / unit_tests (pull_request) Successful in 4m46s
CI / docker (pull_request) Failing after 1s
CI / coverage (pull_request) Successful in 17m20s
CI / status-check (pull_request) Failing after 3s
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 1m19s
CI / benchmark-regression (pull_request) Failing after 30s
CI / typecheck (pull_request) Successful in 1m32s
CI / quality (pull_request) Successful in 1m31s
CI / build (pull_request) Successful in 30s
CI / security (pull_request) Successful in 1m46s
CI / helm (pull_request) Successful in 50s
CI / push-validation (pull_request) Successful in 22s
CI / e2e_tests (pull_request) Successful in 3m46s
CI / integration_tests (pull_request) Successful in 4m44s
CI / unit_tests (pull_request) Successful in 4m46s
CI / docker (pull_request) Failing after 1s
CI / coverage (pull_request) Successful in 17m20s
CI / status-check (pull_request) Failing after 3s
This commit is contained in:
@@ -47086,6 +47086,48 @@ This section defines the ordered milestone plan for CleverAgents v3.x, mapping a
|
||||
| 11 | Skeleton compression for child plan context inheritance | §Skeleton Compressor | Child plans receive compressed parent context via `skeleton_ratio` budget |
|
||||
| 12 | Test coverage ≥ 97% | §Quality Gates | `nox -s coverage_report` passes |
|
||||
|
||||
#### Pipeline Protocol Contracts
|
||||
|
||||
Each of the 10 Context Assembly Pipeline stages has a defined input/output contract. Implementations of pluggable components MUST honour these contracts:
|
||||
|
||||
| Stage | Input | Output | Error Behavior |
|
||||
|-------|-------|--------|----------------|
|
||||
| **StrategySelector** | `ContextRequest` (CRP directives, plan context, budget) | `list[StrategyInvocation]` — strategies to run with confidence weights | Returns empty list on failure; pipeline continues with no strategies (produces empty context) |
|
||||
| **BudgetAllocator** | `list[StrategyInvocation]`, total token budget | `dict[str, int]` — per-strategy token budgets | Raises `BudgetAllocationError` if total budget < `min-useful-budget`; pipeline aborts |
|
||||
| **StrategyExecutor** | `list[StrategyInvocation]` with budgets | `list[ContextFragment]` — raw retrieved fragments | Per-strategy circuit breaker; failed strategies produce empty fragment list; other strategies continue |
|
||||
| **FragmentDeduplicator** | `list[ContextFragment]` | `list[ContextFragment]` — deduplicated fragments | Returns input unchanged on internal error; logs warning |
|
||||
| **DetailDepthResolver** | `list[ContextFragment]` with potential depth conflicts | `list[ContextFragment]` — one entry per UKO node at resolved depth | Returns input unchanged on conflict; logs warning |
|
||||
| **FragmentScorer** | `list[ContextFragment]`, `PlanContext` | `list[ScoredFragment]` — fragments with composite relevance scores | Returns fragments with score=0.0 on scoring failure; they will be deprioritized by BudgetPacker |
|
||||
| **BudgetPacker** | `list[ScoredFragment]`, token budget | `list[ScoredFragment]` — fragments that fit within budget | Raises `BudgetPackingError` if no fragment fits even at depth 0; pipeline produces empty context |
|
||||
| **FragmentOrderer** | `list[ScoredFragment]` | `list[ScoredFragment]` — coherently ordered fragments | Returns input in original order on failure; logs warning |
|
||||
| **PreambleGenerator** | `list[ScoredFragment]`, `PlanContext` | `str` — provenance preamble (max 200 tokens) | Returns empty string on failure; context assembled without preamble |
|
||||
| **SkeletonCompressor** | Parent plan `list[ScoredFragment]`, skeleton budget ratio | `list[ScoredFragment]` — compressed skeleton fragments | Returns empty list on failure; child plan receives no inherited context |
|
||||
|
||||
**Storage Tier Definitions:**
|
||||
|
||||
| Tier | Contents | Capacity | Eviction Policy |
|
||||
|------|----------|----------|-----------------|
|
||||
| **Hot** | Current working set loaded into LLM context window | `context.hot.max-tokens` (default: 16,000 tokens) | Overflow evicts lowest-scored fragments to warm tier |
|
||||
| **Warm** | Recent decisions and fragments available for retrieval | `context.warm.max-decisions` (default: 100 decisions); retained for `context.tiers.warm.retention-hours` (default: 24h) | Age-based eviction to cold tier |
|
||||
| **Cold** | Historical decisions and fragments for audit and correction | `context.cold.max-decisions` (default: 500 decisions); retained for `context.tiers.cold.retention-days` (default: 90 days) | Permanent archive; manual deletion only |
|
||||
|
||||
**Budget Enforcement Protocol:**
|
||||
|
||||
1. Files exceeding `context.file.max-size` (default: 1 MB) are summarized or excluded before fragment creation.
|
||||
2. Total file size across all fragments must not exceed `context.file.max-total-size` (default: 50 MB); fragments are excluded in reverse score order until the limit is met.
|
||||
3. The token budget is a hard ceiling enforced by `BudgetPacker`; no fragment may cause the assembled context to exceed `model_context_window - response_reserve - tool_definitions - skeleton_allocation`.
|
||||
4. Fragments below `context.query.min-relevance` (default: 0.3) are discarded by `FragmentScorer` before packing.
|
||||
|
||||
**Context Assembly Output Format:**
|
||||
|
||||
The pipeline produces an `AssembledContext` object delivered to the actor:
|
||||
- `preamble: str` — provenance summary (source strategies, fragment counts, budget utilization)
|
||||
- `fragments: list[OrderedFragment]` — ordered context fragments, each with `uko_uri`, `content`, `depth`, `token_count`, `source_strategy`
|
||||
- `budget_used: int` — total tokens consumed
|
||||
- `budget_total: int` — total budget available
|
||||
- `strategies_invoked: list[str]` — names of strategies that contributed fragments
|
||||
- `skeleton_fragments: list[OrderedFragment]` — compressed parent context (empty for root plans)
|
||||
|
||||
#### Key Architectural Constraints
|
||||
|
||||
- **Pipeline composability**: All 10 Context Assembly Pipeline slots are overridable at plan > project > global scope.
|
||||
@@ -47221,6 +47263,29 @@ This section defines the ordered milestone plan for CleverAgents v3.x, mapping a
|
||||
| 18 | `agents tui web` launches Textual Web mode | §TUI — Web Mode | Web mode accessible via browser at configured port |
|
||||
| 19 | Test coverage ≥ 97% | §Quality Gates | `nox -s coverage_report` passes |
|
||||
|
||||
#### Key Component Interfaces
|
||||
|
||||
The following components define the public interfaces for the TUI implementation. Each must be implemented as specified for the deliverables to be verifiable:
|
||||
|
||||
| Component | Module | Responsibility | Public Interface |
|
||||
|-----------|--------|----------------|-----------------|
|
||||
| `CleverAgentsApp` | `tui/app.py` | Root Textual `App`; manages screens, global state, A2A client | `run()`, `push_screen(screen)`, `switch_session(session_id)`, `notify(message, severity)` |
|
||||
| `MainScreen` | `tui/screens/main.py` | Primary chat interface with sidebar and prompt | `cycle_sidebar_state()`, `submit_prompt(text)`, `stream_message(block)`, `set_persona(persona)` |
|
||||
| `TuiMaterializer` | `tui/materializer.py` | `MaterializationStrategy` implementation mapping `ElementHandle` events to Textual widgets | `materialize(session: OutputSession) -> None`; implements all `ElementHandle` types from ADR-021 |
|
||||
| `PersonaRegistry` | `tui/persona/registry.py` | Loads, validates, and provides access to persona YAML files | `load_all() -> list[Persona]`, `get(name: str) -> Persona`, `save(persona: Persona) -> None`, `delete(name: str) -> None` |
|
||||
| `SessionTracker` | `tui/session/tracker.py` | Tracks active TUI sessions and their A2A bindings | `create_session(persona: Persona) -> TuiSession`, `get_active() -> TuiSession`, `switch(session_id: str) -> None`, `close(session_id: str) -> None` |
|
||||
| `ReferencePickerOverlay` | `tui/widgets/reference_picker.py` | Fuzzy-search overlay for `@` reference resolution | `search(query: str) -> list[ReferenceResult]`; resolves to CRP directives via A2A |
|
||||
| `SlashCommandOverlay` | `tui/widgets/slash_command.py` | Tab-completable command overlay for `/` prefix | `filter(prefix: str) -> list[Command]`, `execute(command: str, args: list[str]) -> None` |
|
||||
| `PersonaBar` | `tui/widgets/persona_bar.py` | Always-visible status bar below prompt | `update(persona: Persona, preset: str, cost: float) -> None` |
|
||||
|
||||
**Verifiable Checks for Component Interfaces:**
|
||||
|
||||
- `TuiMaterializer` must pass the same `OutputSession` test fixtures used for `RichMaterializer` — all 9 `ElementHandle` types produce Textual widgets without error.
|
||||
- `PersonaRegistry.load_all()` must return an empty list (not raise) when `~/.config/cleveragents/personas/` does not exist.
|
||||
- `SessionTracker.create_session()` must persist the session to `~/.local/state/cleveragents/tui.db` before returning.
|
||||
- `ReferencePickerOverlay.search()` must return results within 200ms for indexes with up to 10,000 resources.
|
||||
- `MainScreen.cycle_sidebar_state()` must cycle `hidden → visible → fullscreen → hidden` and update layout without layout thrashing.
|
||||
|
||||
#### Key Architectural Constraints
|
||||
|
||||
- **Textual version**: Textual ≥ 1.0 required; no compatibility with pre-1.0 API.
|
||||
@@ -47313,9 +47378,15 @@ These architectural invariants must be maintained across all milestones:
|
||||
|
||||
1. **Spec-first**: No feature is implemented without spec coverage. If implementation discovers a better approach, the spec is updated first via PR.
|
||||
2. **Layer boundaries**: Presentation → Application → Domain → Infrastructure. No reverse dependencies.
|
||||
|
||||
> **DI Container Exception**: The dependency injection container (`application/container.py`) is the sole permitted location where the application layer may reference infrastructure layer concrete types. This is the wiring point. All other application services MUST depend only on protocol abstractions defined in `application/protocols/` or `domain/`. This is an architectural invariant, not a guideline.
|
||||
|
||||
3. **Type safety**: Full Pyright strict compliance. No `# type: ignore` suppressions.
|
||||
4. **Fail-fast**: All argument validation at entry points. No silent failures.
|
||||
5. **ULID identifiers**: Plans, decisions, resources, correction attempts, and validation attachments use ULIDs. Projects, actions, skills, and tools use namespaced names.
|
||||
|
||||
> **ULID Scope**: ULID identifiers are required for all domain entity identifiers: Plan IDs, Decision IDs, Resource IDs, Correction Attempt IDs, and Validation IDs. Internal implementation identifiers (e.g., LangGraph thread IDs, temporary cache keys) are NOT required to use ULID format. The distinction: if the ID is stored in the database as a domain entity attribute, it must be a ULID; if it is an ephemeral internal implementation detail, it may use any suitable format.
|
||||
|
||||
6. **Namespace format**: `[[server:]namespace/]name`. `local/` reserved for local-only items.
|
||||
7. **A2A exclusivity**: All client-server communication uses A2A. No REST API.
|
||||
8. **BDD tests**: All unit-level tests expressed as Behave/Gherkin scenarios. No xUnit-style tests.
|
||||
|
||||
Reference in New Issue
Block a user