Refresh Day 95 entry with live Forgejo milestone data for v3.2.0–v3.7.0.
All milestone totals expanded due to continued agent-driven issue creation:
M3 67% (223/331), M4 64% (101/159), M5 70% (126/179),
M6 63% (174/277), M7 50% (127/253), M8 46% (333/722).
Update gantt chart completion percentages, risk register, track forecast,
and task inventory to reflect current state.
structlog's cache_logger_on_first_use=True causes module-level loggers
to permanently cache their processor chain on first use. Tests using
capture_logs() reconfigure processors, but cached loggers never pick up
the new configuration — resulting in empty capture lists.
Fixed by adding custom capture context managers that:
1. Temporarily disable logger caching
2. Replace the module-level logger with a fresh uncached instance
3. Restore original logger and config on exit
Fixes 11 LSP server stub scenarios and 2 retry policy wiring scenarios.
Apply remaining fixes not covered by the 4278ba91 commit:
1. src/cleveragents/cli/main.py:
info and diagnostics commands now call configure_structlog(WARNING)
before build_info_data()/build_diagnostics_data() when non-rich format
is requested. This prevents debug-level structlog messages from
corrupting --format json/yaml output in integration tests.
2. robot/helper_config_cli.py:
Call configure_structlog(WARNING) before importing cleveragents CLI
commands so plugin_manager debug messages don't pollute CliRunner
captured output (fixes Config List JSON Format test).
3. features/steps/aimodelscredentials_steps.py:
ModelProviderOption config checks now use getattr fallback so they
work both when context.model_config is set (via explicit 'I examine
the ModelProviderOption model_config' step) and when context.model_instance
is set (via 'I create a ModelProviderOption with only priority set to N').
4. features/steps/plan_namespaced_name_tdd_steps.py:
@when steps now set context.error and context.lsp_error in addition to
context.exception so the existing @then steps from service_steps.py
and lsp_registry_steps.py match and validate correctly.
No quality gates suppressed. All changes are to test and source files.
ISSUES CLOSED: #2597
- Rewrite TUI session export/import step definitions to use constructor-
based dependency injection (container_factory) instead of
unittest.mock.patch context managers that fail across fork() boundaries
in the parallel test runner.
- Add container_factory parameter to TuiCommandRouter dataclass so tests
can inject a mock container that survives multiprocessing.fork().
- Add use_step_matcher('re') to a2a_jsonrpc_wire_format_steps.py so
regex-based step patterns are matched correctly (fixes 30 errored
scenarios with 56 undefined steps).
- Add plural 'rows' variant to database_handler_crud_steps.py row-count
step matcher (fixes 1 errored scenario).
ISSUES CLOSED: #2597
Fix all failing CI quality gates (lint, unit_tests, format) without
suppressing any quality enforcement.
Root causes and fixes:
1. Format: features/steps/plan_namespaced_name_tdd_steps.py had trailing
whitespace; fixed by running ruff format.
2. Unit tests - A2A JSON-RPC 2.0 migration (commit 9c6d6915) renamed
A2aRequest fields (operation→method, request_id→id, a2a_version→jsonrpc)
and A2aResponse fields (status+data→result, request_id→id) but did not
update all step files and feature files:
- a2a_jsonrpc_wire_format_steps.py: added use_step_matcher('re') and
reset to 'parse' at end to prevent parallel test interference
- a2a_facade_wiring_steps.py: updated operation= to method=, .status/.data
to .result
- a2a_facade_steps.py: updated request_id→id, a2a_version→jsonrpc,
A2aResponse(request_id=..., status=...) to new API
- m6_facade_steps.py: updated all old API usage
- devcontainer_cleanup_steps.py: updated A2aRequest(operation=...)
- plan_prompt_command_steps.py: updated A2aRequest(operation=...)
- wf03_plan_prompt_confidence_steps.py: updated A2aRequest(operation=...)
- consolidated_misc.feature: updated old A2aRequest/A2aResponse scenarios
3. Unit tests - Session CLI output changed (commit 0d5d9cf0 and others):
- 'Session Created' → 'Session created' (lowercase)
- 'Session Details' → 'Session Summary'
- 'Sessions (N total)' → 'Sessions'
- session list JSON: top-level 'total' → nested 'summary.total'
- Fixed in: session_cli.feature, session_cli_coverage_boost.feature,
session_cli_uncovered_branches.feature, session_list_error.feature,
tdd_session_create_persist_steps.py
4. Unit tests - Plan list output changed (commit 1a07a891):
- 'V3 Lifecycle Plans' → 'Plans'
- 'Lifecycle Plans' → 'Plans'
- Name column removed (restored in source)
- Invariants column removed (restored in source)
- Project truncation removed (restored in source)
- Fixed in: plan_cli_cancel_revert_coverage.feature,
plan_lifecycle_cli_coverage.feature, plan_cli_coverage_boost_steps.py,
plan.py (source code restored)
5. Unit tests - Plan apply command now requires ULID (commit 300a5d6d):
- plan_cli_coverage_r3.feature: updated 'PLAN-001' to valid ULID
- plan_cli_coverage_r3_steps.py: added --yes flag, added new step for
no-eligible-plans path
6. Unit tests - Various source code bugs:
- ThoughtBlock: converted from @dataclass to Pydantic BaseModel
(architecture test requires all dataclasses to use Pydantic)
- session.py: added DatabaseError handling to export, import, tell commands
- database.py: fixed rollback_to() to reuse checkpoint connection for writes
- database.py: added _get_checkpoint_conn() helper
- check-tls-cert.py: fixed SSLCertVerificationError.reason AttributeError
7. Unit tests - Test step bugs:
- error_recovery_coverage_boost_steps.py: fixed invalid ULID _PLAN_ID
- session_service_coverage_steps.py: fixed 'sha256:' prefix bug in checksum
- database_models_new_coverage_steps.py: added 'name' field to session mock
- async_audit_recording_steps.py: fixed Settings(audit_async=False) via env var
- coverage_threshold_config_steps.py: added --coverage-min pattern support
- m5_acms_smoke_steps.py: updated usage hint text
- actor_cli_yaml_steps.py: updated 'Removed actor' → 'Actor removed'
- aimodelscredentials_steps.py: set context.imported_class in import step
- domain_base_model.feature: added missing 'When I examine model_config' step
- tui_first_run_steps.py: fixed module reload to restore cleveragents.tui.*
modules after test (prevented patch interference in subsequent tests)
- tui_first_run_steps.py: added set_search('') step for empty string
- resource_handler_base_coverage_r3_steps.py: use _MinimalHandler instead
of DatabaseResourceHandler for NotImplementedError tests
- resource_handler_crud.feature: updated to test new DatabaseHandler behavior
- resource_handler_sandbox.feature: updated to test new DatabaseHandler behavior
- tdd_json_decode_crash_persistence.feature: fixed @tdd_bug → @tdd_issue tags
8. Parallel test interference:
- All step files using use_step_matcher('re') now reset to 'parse' at end
to prevent global matcher state leaking to subsequent step files
Add missing `finally: if self._auto_commit: session.close()` blocks to all four public session-creating methods in AutomationProfileRepository: get_by_name(), list_all(), upsert(), and delete(). Closes#987
Add the missing --namespace/-n option to lifecycle_list_plans() in
plan.py, mirroring the existing implementation in list_actions() in
action.py. The service layer already supported namespace filtering;
only the CLI layer was missing the option.
Changes:
- Add namespace parameter to lifecycle_list_plans() with --namespace/-n
option flags and 'Filter plans by namespace' help text
- Pass namespace through to service.list_plans(namespace=namespace, ...)
- Update TUI Filters panel to display 'Namespace: <value>' when provided
- Add usage examples to command docstring
- Add 4 Behave unit test scenarios covering --namespace/-n option
- Add 2 Robot Framework integration tests verifying namespace filtering
ISSUES CLOSED: #2165
MCPToolAdapter.invoke() was reading error messages from result.get('error',
'unknown error'), but the MCP 1.4.0 protocol returns errors in the content
field as a list of content items with type and text keys. This caused every
error from a real MCP 1.4.0-compliant server to be silently replaced with
the string 'unknown error'.
Changes:
- src/cleveragents/mcp/adapter.py: extract error_text from content[0].text
with safe guards (isinstance check, length check) and fallback to
'unknown error' when content is absent or empty
- features/mocks/mock_mcp_transport.py: return MCP 1.4.0-compliant error
responses using content list format instead of the non-standard error key
- features/tdd_mcp_error_content_key.feature: Behave scenario verifying
correct error extraction from MCP 1.4.0 content arrays (written as TDD
issue-capture, @tdd_expected_fail removed after fix applied)
- features/steps/tdd_mcp_error_content_key_steps.py: step definitions for
the new scenario including _MCP14ErrorTransport mock subclass
All 51 MCP adapter scenarios pass. Typecheck: 0 errors. Lint: clean.
ISSUES CLOSED: #2158
Add missing `finally: if self._auto_commit: session.close()` blocks to
all four public session-creating methods in `AutomationProfileRepository`:
`get_by_name()`, `list_all()`, `upsert()`, and `delete()`. Without
these blocks, sessions were never closed when the repository operated in
auto_commit mode, causing a slow session leak that could exhaust the
connection pool over time.
The fix mirrors the pattern already used by `SessionRepository`, which
correctly closes its session in a `finally` block for every public method.
Removed the `@tdd_expected_fail` tag from the TDD test
(`tdd_automation_profile_session_leak.feature`) so the six scenarios
now run as normal regression tests (four original plus two new scenarios
for `get_by_name` and `list_all`).
ISSUES CLOSED: #987
This TDD test captures the buggy behavior where `NamespacedName.validate_namespace()`
and `validate_name()` accept names starting with digits, violating the spec requirement
that namespace and name components must start with a letter (consistent with
`_BARE_NAME_RE` in project.py).
Three failing scenarios demonstrate the bug:
1. parse() accepting namespace starting with digit ("123abc/my-action")
2. constructor accepting name starting with digit (local/"123-action")
3. constructor accepting namespace starting with digit ("999org"/valid-name)
Tagged with @tdd_expected_fail per Bug Fix Workflow.
Implements #2145
Blocks #2147
Implement inline permission question widget for quick allow/reject
decisions within the conversation stream with file diff context.
ISSUES CLOSED: #997
Add a new Behave TDD regression scenario that persists corrupt automation-profile JSON and verifies repository reads do not leak raw JSONDecodeError. The scenario is tagged with @tdd_bug, @tdd_bug_989, and @tdd_expected_fail so it currently inverts the intentional assertion failure and will require tag removal when bug #989 is fixed.\n\nAlso stabilize an unrelated integration fixture in robot/resource_dag.robot by sharing a single SQLAlchemy session in inline scripts; this removes nondeterministic visibility of flushed resource-type rows and keeps required nox integration quality gates deterministic for this branch.\n\nISSUES CLOSED: #1094
Introduce DomainBaseModel in src/cleveragents/domain/models/base.py that
defines the single shared model_config (str_strip_whitespace, validate_assignment,
arbitrary_types_allowed=False, populate_by_name, use_enum_values) previously
duplicated verbatim across five domain model files.
Update 14 classes across five files to inherit from DomainBaseModel instead
of pydantic.BaseModel directly, removing all inline model_config duplication:
- aimodelscredentials/ai_models_credentials.py (ModelProviderOption)
- aimodelserrors/ai_models_errors.py (ModelError, FallbackResult)
- aimodelsproviders/ai_models_providers.py (ModelProviderExtraAuthVars,
ModelProviderConfigSchema)
- auth/auth.py (AuthHeader, TrialPlansExceededError, TrialMessagesExceededError,
BillingError, ApiError, ClientAccount, ClientAuth)
- planconfig/plan_config.py (PlanConfig, ConfigSetting)
Pure structural refactor — no behavioral changes. Models with different
configurations (acms, aimodels_custom, etc.) are left untouched.
Add BDD feature (22 scenarios) and Robot integration tests (8 test cases)
verifying inheritance, config correctness, and no-duplication invariants.
ISSUES CLOSED: #1941
Rewrites the A2aRequest and A2aResponse Pydantic models to use the field
names mandated by the JSON-RPC 2.0 specification, fixing a fundamental
protocol compliance issue that prevented external A2A-compliant clients
from communicating with the server.
Changes:
- A2aRequest: a2a_version→jsonrpc (fixed '2.0'), request_id→id,
operation→method; auth field removed (not in JSON-RPC 2.0)
- A2aResponse: a2a_version→jsonrpc, request_id→id, status+data→result
(success path), timing_ms removed; added _result_xor_error validator
enforcing mutual exclusion of result and error fields
- A2aLocalFacade.dispatch(): updated to use request.method, request.id,
result=data, error=A2aErrorDetail(...)
- A2aHttpTransport.send(): updated to use request.method
- CLI call sites (session.py, plan.py): updated A2aRequest(method=...)
and response.result / response.error field access
- All existing A2A Behave step files updated to new field names
- New 35-scenario Behave feature (a2a_jsonrpc_wire_format.feature)
covering serialisation, deserialisation, validation, and facade dispatch
- New 7-test Robot Framework suite (a2a_jsonrpc_wire_format.robot)
for end-to-end wire format verification
ISSUES CLOSED: #1501
Extended `_print_actor()` to render the full spec-required output for
`agents actor add`: the Actor Added panel now includes a Type field, and
three additional panels (Config, Capabilities, Tools) plus a success
status line are rendered when `show_add_panels=True`.
- Add `Type:` field to the Actor Added panel (from `config_blob["type"]`)
- Implement Config panel: Path, Hash, Options count, Nodes count, Edges count
- Implement Capabilities panel: bulleted list (rendered only when non-empty)
- Implement Tools panel: Rich table with Tool, Read-Only, Safe columns
(rendered only when non-empty; string tool entries default to yes/yes)
- Add `✓ OK Actor added` success status line after all panels
- Pass `config_path` and `show_add_panels=True` from `add()` command
- Add Behave BDD scenarios covering each new panel and the success line
- Add Robot Framework integration test verifying the full rich output
ISSUES CLOSED: #1499
Add ULID format validation to all v3 plan commands to prevent users from
accidentally mixing legacy ('agents tell') and v3 ('agents plan use')
workflows. The two systems use separate storage backends and cannot be
mixed; this change detects the mismatch early and provides actionable
error messages.
Changes:
- Add _validate_plan_ulid() with proper Crockford Base32 regex
(^[0-9A-HJKMNP-TV-Z]{26}$, re.IGNORECASE) that correctly rejects
invalid characters (I, L, O, U), hyphens, and wrong-length strings
- Add _PLAN_ULID_RE compiled regex and _ULID_VALIDATION_ERROR_MSG constant
with actionable guidance explaining the legacy/v3 incompatibility
- Apply ULID validation to all v3 commands: execute_plan,
_lifecycle_apply_with_id, lifecycle_apply_plan, plan_status,
plan_errors, cancel_plan (only on user-provided IDs, not auto-discovered)
- Update _LEGACY_DEPRECATION_MSG and tell/build command warnings to
explicitly state that the two workflows are INCOMPATIBLE and cannot be mixed
- Add comprehensive BDD tests in features/plan_ulid_validation.feature
with step definitions using Typer CLI runner (not subprocess)
- Update CONTRIBUTING.md with 'Workflow Choice: Legacy vs. v3 Plan
Lifecycle' section documenting the incompatibility and migration path
ISSUES CLOSED: #1560
The TLS handshake failure on git.dev.cleveragents.com was caused by the
hostname being absent from the certificate's Subject Alternative Names
(SANs), or by SNI virtual-host misconfiguration on the server side.
This commit delivers the repository-side remediation:
- scripts/check-tls-cert.py: New TLS certificate health-check script.
Connects to a hostname, verifies the certificate's SANs include the
target hostname, checks expiry, and reports errors/warnings. Accepts
an injectable SSLContext for unit testing without real network access.
Supports wildcard SAN matching and configurable expiry warning threshold.
- docs/development/ops-runbook.md: New ops runbook documenting the full
certificate renewal procedure (Let's Encrypt/certbot and manual CA),
SNI misconfiguration diagnosis steps, expiry monitoring with cron, and
recommended alert thresholds (30/14/7/0 days).
- features/tls_certificate_check.feature: 14 Behave scenarios tagged
@tdd_issue @tdd_issue_1543 covering: missing SAN detection, valid SAN
acceptance, expired certificate detection, expiry warning threshold,
TLS handshake errors, connection timeouts, connection refused, wildcard
SAN matching, and _hostname_matches_san unit tests.
- features/steps/tls_certificate_check_steps.py: Step definitions for
the above feature, using unittest.mock to inject SSL contexts and
socket connections so no real network calls are made.
- mkdocs.yml: Added Ops Runbook to the Development section navigation.
The actual server-side certificate renewal (adding git.dev.cleveragents.com
as a SAN and reloading the web server) must be performed by the server
administrator following the procedure in docs/development/ops-runbook.md.
Closes#1543
ISSUES CLOSED: #1543
Add deterministic BDD feature and step definitions to resolve the
flaky-test detection alert for test_example_flaky_test (issue #1542).
Root cause: the async-job heartbeat step previously relied on a fixed
time.sleep(0.01) that was insufficient on fast CI runners. When two
consecutive datetime.now(UTC) calls returned the same microsecond value
the 'heartbeat updated' assertion failed intermittently.
The busy-wait guard (already present in async_execution_steps.py) is
the correct fix. This commit adds a dedicated feature that:
- Validates the heartbeat timestamp strictly advances after the
busy-wait (the primary test_example_flaky_test scenario)
- Covers rejection of heartbeat recording for queued and completed jobs
- Adds a bounded heartbeat step that asserts the busy-wait terminates
within a wall-clock budget, preventing infinite hangs on broken clocks
ISSUES CLOSED: #1542
- Add _EVENT_TYPE_TO_METHOD class-level mapping (ClassVar[dict[str, str]]) to
convert A2A event types to JSON-RPC 2.0 method names:
TaskStatusUpdateEvent → task/statusUpdate
TaskArtifactUpdateEvent → task/artifactUpdate
- Refactor SseEventFormatter.format() to produce JSON-RPC 2.0 notification
envelope: {"jsonrpc": "2.0", "method": "...", "params": {...}}
- Move event data fields into params object; include taskId (from plan_id)
in params when plan_id is present, per spec §Streaming Architecture
- Remove non-spec fields (event_id, event_type, timestamp, plan_id) from
the data payload; these remain in SSE envelope headers (event: and id:)
- Update BDD feature to verify JSON-RPC 2.0 structure for both event types,
events with/without plan_id, custom data in params, and exclusion of
non-spec fields
- Fix pre-existing type errors in step definitions: replace try/except
ImportError pattern with direct imports and use behave.runner.Context
for proper static typing (0 pyright errors)
ISSUES CLOSED: #1502
Implements issue #1520. The version command now displays the actual git commit SHA to help identify which version of the code is running.
Co-authored-by: Jeffrey Phillips Freeman <the@jeffreyfreeman.me>
Co-committed-by: Jeffrey Phillips Freeman <the@jeffreyfreeman.me>
Renames `plan lifecycle-list` to `plan list` and `plan lifecycle-apply` to `plan apply` to align with the specification's canonical command names. Removes legacy V2 plan commands that occupied those names.
- Renamed CLI command registrations from lifecycle-list/lifecycle-apply to list/apply
- Removed legacy V2 apply and list commands (~200 lines)
- Updated apply shortcut in main.py to delegate to v3 lifecycle
- Added defensive null check for plan existence in apply command
- Updated 63+ test, doc, and benchmark files for consistency
Closes#881
Co-authored-by: Jeffrey Phillips Freeman <the@jeffreyfreeman.me>
Co-committed-by: Jeffrey Phillips Freeman <the@jeffreyfreeman.me>
Add lifecycle management and sandbox support for MCP tools:
- McpClient: lazy start, configurable idle timeout with auto-stop, health monitoring with automatic restart
- McpRegistry: namespace-isolated tracking of multiple MCP servers
- SandboxPathRewriter: bi-directional file path rewriting between host and sandbox workspaces
- MCPCapabilityMetadata: structured exposure of full MCP server capabilities
- 26 BDD scenarios covering lifecycle, sandbox rewriting, and edge cases
ISSUES CLOSED: #938
Add kubeconform manifest validation to the existing helm CI job.
The helm job already had helm lint and helm template steps (added in
#1085). This commit completes the optional acceptance criterion from
#1089 by adding kubeconform v0.7.0 to validate rendered manifests
against the Kubernetes 1.29.0 schema in strict mode.
Closes#1089
Add Markdown export format for sessions alongside the existing JSON export.
Implement TUI slash command routing for /session:export and /session:import.
- Add Session.as_export_markdown() domain method producing a human-readable
Markdown transcript (lossy, not importable — for sharing/documentation)
- Extend CLI 'agents session export' with --format flag (json|md)
- Extend TuiCommandRouter._session_command() to handle 'export' and 'import'
subcommands, delegating to the session service via the DI container
- Add 16 Behave scenarios covering domain model, CLI, and TUI command paths
Closes#1004
Add Markdown export format for sessions alongside the existing JSON export.
Implement TUI slash command routing for /session:export and /session:import.
- Add Session.as_export_markdown() domain method producing a human-readable
Markdown transcript (lossy, not importable — for sharing/documentation)
- Extend CLI 'agents session export' with --format flag (json|md)
- Extend TuiCommandRouter._session_command() to handle 'export' and 'import'
subcommands, delegating to the session service via the DI container
- Add 16 Behave scenarios covering domain model, CLI, and TUI command paths
Closes#1004
Implements the first-run experience for the TUI as specified in the
specification's 'First-Run Experience' section (§ TUI Architecture).
On first launch (no personas configured), a centered ActorSelectionOverlay
widget is displayed guiding the user to select an actor. The overlay
supports keyboard navigation (j/k), fuzzy search (/), and confirmation
(enter). Selecting an actor creates a default persona and dismisses the
overlay.
Changes:
- Add src/cleveragents/tui/first_run.py: is_first_run() detection helper
and create_default_persona_for_actor() setup helper
- Add src/cleveragents/tui/widgets/actor_selection_overlay.py:
ActorSelectionOverlay widget with show/hide/navigate/search/confirm API
and render_actor_selection() pure rendering function
- Update src/cleveragents/tui/app.py: integrate first-run check in
on_mount(), yield ActorSelectionOverlay in compose(), add
_complete_first_run() method
- Update src/cleveragents/tui/widgets/__init__.py: export
ActorSelectionOverlay
- Add features/tui_first_run.feature + features/steps/tui_first_run_steps.py:
comprehensive Behave BDD tests covering all public API paths
ISSUES CLOSED: #1007
Implements the four missing protocol methods on DevcontainerHandler required by Epic #825 (ResourceHandler Protocol Completion):
- delete(): uses 'devcontainer exec rm -rf' to delete files/dirs inside the container; returns DeleteResult(success=False) for missing/stopped containers rather than raising.
- list_children(): uses 'devcontainer exec ls -1' to enumerate workspace entries; returns an empty list on container failure.
- diff(): compares content hashes between the devcontainer workspace and another filesystem location; uses EMPTY_CONTENT_HASH sentinel to treat both-absent as no-change.
- create_sandbox(): delegates to BaseResourceHandler.create_sandbox with lazy activation (same DETECTED/STOPPED/FAILED guard as resolve()).
All methods raise ValueError for resources with no location.
Adds 18 Behave BDD scenarios covering success paths, failure/stopped-container paths, empty-path edge cases, and ValueError guards.
ISSUES CLOSED: #1242
Co-authored-by: Jeffrey Phillips Freeman <the@jeffreyfreeman.me>
Co-committed-by: Jeffrey Phillips Freeman <the@jeffreyfreeman.me>
Enriches the PLAN_APPLIED domain event with SEC7 audit-logging statistics per docs/specification.md §Audit Logging (issue #716).
Changes:
- PlanLifecycleService.complete_apply() now accepts optional keyword arguments: files_changed, lines_added, lines_removed, resources_modified, apply_duration_seconds (all default to 0). These are included in the PLAN_APPLIED event details dict alongside the existing action_name, phase, and project_names fields.
- PlanApplyService.apply_with_validation_gate() computes changeset statistics from SpecChangeSet.summary() and measures apply duration, then passes all statistics to complete_apply().
- New BDD feature features/plan_applied_event_enrichment.feature with 13 scenarios.
Closes#716
Co-authored-by: Jeffrey Phillips Freeman <the@jeffreyfreeman.me>
Co-committed-by: Jeffrey Phillips Freeman <the@jeffreyfreeman.me>