The TDD regression files for bug #968 were incorrectly deleted instead of
updated to assert the spec-compliant fixed behaviour from issue #6325.
Per CONTRIBUTING.md the regression guard must be kept and updated — not
removed — once the bug is fixed.
Restore and update all three files to assert the new expected behaviour:
- plan explain rejects a non-decision-id argument with rc=1
- error output contains "not found"
- decision data fields are absent from output
Also pair the orphaned robot/helper_tdd_plan_explain_plan_id.py with a
restored robot test file and update it to verify rc=1 rejection behaviour.
ISSUES CLOSED: #6325
- Shorten error message in explain_decision_cmd to fit 88-char limit (E501)
- Add `from None` to raise typer.Exit(1) inside except block (B904)
- Fix step definition mock to raise DecisionNotFoundError instead of
returning None, so the exception handler fires and output contains
"not found" as the scenario asserts
ISSUES CLOSED: #6325
Align the automation-profile add command's machine-readable output with the specification by emitting the flat thresholds/flags schema and created timestamp, and add coverage that locks the expected structure.
ISSUES CLOSED: #6345
- Add create_sandbox() override to _ContainerBaseHandler raising
NotImplementedError, matching CloudResourceHandler pattern (issue #836)
- Add project_access() override to _ContainerBaseHandler raising
NotImplementedError, consistent with all other stub methods
- Rename @then("the import should succeed without errors") step to
@then("the container handler module import should succeed without errors")
to resolve AmbiguousStep collision with tdd_a2a_sdk_dependency_steps.py
- Update container_handler.feature to use renamed step
- Apply ruff format to container.py
ISSUES CLOSED: #2907
Implements the missing cleveragents.resource.handlers.container module
that is referenced by all seven container infrastructure resource types
registered in _resource_registry_container.py.
Previously, any attempt to use container-runtime, container-image,
container-mount, container-exec-env, container-port, container-volume,
or container-network resources raised HandlerResolutionError with
ModuleNotFoundError at runtime.
Changes:
- Add src/cleveragents/resource/handlers/container.py with five handler
classes: ContainerRuntimeHandler, ContainerImageHandler,
ContainerChildHandler (shared by mount/exec-env/port), ContainerVolumeHandler,
ContainerNetworkHandler
- All handlers extend _ContainerBaseHandler which extends BaseResourceHandler
and satisfies the ResourceHandler protocol
- resolve() raises NotImplementedError (container sandbox provisioning
is pending, mirrors CloudResourceHandler pattern)
- content_hash() returns identity hash based on resource type + location
- All CRUD and lifecycle stubs raise NotImplementedError
- Update handlers/__init__.py to export the five new handler classes
- Add features/container_handler.feature with 72 BDD scenarios covering
module importability, protocol conformance, handler resolution, type
labels, CRUD stubs, lifecycle stubs, and registry integration
- Add features/steps/container_handler_steps.py with step definitions
All nox sessions pass: lint, typecheck, unit_tests (72/72 scenarios).
ISSUES CLOSED: #2907
Cover the list branches in _format_rich() and _format_color() that were
left untested, causing the coverage gate to drop below the 97% threshold.
Add two new @when steps (list+rich, list+color), two @then steps for list
panel assertions, and two new feature scenarios exercising those paths.
ISSUES CLOSED: #2921
The format_output() function in src/cleveragents/cli/formatting.py had two
routing bugs that caused incorrect output for the 'rich' and 'color' formats:
1. The 'rich' format had no explicit dispatch branch and silently fell through
to the final JSON fallback, returning raw JSON instead of styled terminal
output. Since 'rich' is the default CLI format (per ADR-021), this meant
all commands using format_output() (version, info, diagnostics) produced
JSON by default.
2. The 'color' format was incorrectly routed to _format_plain() instead of a
color-aware renderer, producing plain text with no ANSI color codes.
Fix:
- Added _format_rich() helper that delegates to RichMaterializer via
OutputSession, producing ANSI-styled terminal output consistent with
format_output_session().
- Added _format_color() helper that delegates to ColorMaterializer via
OutputSession, producing ANSI-colored terminal output.
- Added explicit OutputFormat.RICH dispatch in format_output() routing.
- Fixed OutputFormat.COLOR dispatch to use _format_color() instead of
_format_plain().
Tests:
- Updated existing BDD scenario that was validating the buggy behavior
(expected JSON for rich format) to now assert correct styled output.
- Added new BDD scenarios: 'rich format produces styled terminal output not
JSON' and 'color format produces ANSI-colored output not plain text'.
- Added Robot Framework integration tests in cli_formats.robot and
helper_cli_formats.py verifying end-to-end styled output for both formats.
All nox sessions pass: lint, typecheck, unit_tests, security_scan.
ISSUES CLOSED: #2921
Remove the unsafe `# type: ignore` suppression on line 111 of
`legacy_migrator.py` and replace it with an explicit
`assert existing_plan.id is not None` statement. This provides
proper type narrowing to the type checker while preserving the
logical correctness guaranteed by the preceding `if existing_plan:`
guard.
Also adds a new BDD scenario in `legacy_migrator_coverage.feature`
that explicitly exercises the code path where an existing plan with
a non-None id is found during migration, verifying the assert-based
type narrowing works correctly end-to-end.
ISSUES CLOSED: #3051
- Updated SkeletonCompressorService.compress() to accept
(fragments: tuple[ContextFragment, ...], skeleton_budget: int)
-> tuple[ContextFragment, ...], matching the SkeletonCompressor protocol
- Removed skeleton_ratio and CompressionResult from the public API
- Added @runtime_checkable to SkeletonCompressor protocol in acms_service.py
- Added structural subtype assertion at module level to prevent future
protocol drift
- Rewrote all Behave feature scenarios and step definitions to use
skeleton_budget
- Updated benchmarks and robot helpers to use absolute token budgets
- Removed CompressionResult export from services __init__.py and
vulture_whitelist.py
- The depth_breadth_projection.py call site already correctly computed
and passed skeleton_budget
ISSUES CLOSED: #2925
Implement skill: wrapper key unwrapping in SkillConfigSchema.from_yaml()
to support the spec-required YAML format with cleveragents: metadata header.
- Strip cleveragents: metadata block from raw YAML before validation
- Unwrap skill: wrapper key if present, with descriptive errors for invalid values
- Maintain backward compatibility with flat YAML format (no wrapper)
- Add Behave scenarios tagged @tdd_issue and @tdd_issue_1472 covering:
* Spec-compliant YAML with skill: wrapper key
* Spec-compliant YAML with cleveragents: header
* skill: with None, string, and list values (error cases)
* Backward compatibility with flat YAML
* cleveragents: header without skill: wrapper
Closes#1472
Changed DiffDisplayMode enum values from side_by_side/context to split/auto
as required by specification §29570, §30139, §30391.
- Renamed SIDE_BY_SIDE="side_by_side" → SPLIT="split" in models.py
- Renamed CONTEXT="context" → AUTO="auto" in models.py
- Renamed side_by_side_diff() → split_diff() in models.py
- Renamed context_diff() → auto_diff() in models.py
- Updated _DIFF_MODE_CYCLE in screen.py to use SPLIT and AUTO
- Updated all BDD feature scenarios and step definitions
- Fixed broken Behave step parameter renames (restored standard 'context' param)
Closes#1449
---
Automated by CleverAgents Bot
Supervisor: Implementation | Agent: task-implementor
Updated step definitions to match the corrected enum values (SPLIT and AUTO instead of SIDE_BY_SIDE and CONTEXT) and updated method names (split_diff instead of side_by_side_diff).
ISSUES CLOSED: #1449
AuditService.record() was generating its own timestamp internally,
discarding the original DomainEvent.timestamp. This means audit entries
recorded when an event was audited, not when the domain event actually
occurred, breaking forensic accuracy per §Audit Logging (SEC7).
Changes:
- Add `timestamp: datetime | None = None` keyword parameter to
AuditService.record(). When provided, uses it as created_at;
falls back to datetime.now(tz=UTC) for backward compatibility.
Applied to both the async queue path and the synchronous DB path.
- AuditEventSubscriber._handle_event() now passes timestamp=event.timestamp
so the original event creation time is preserved in audit entries.
- Add 3 Behave BDD scenarios covering: full pipeline timestamp
preservation, direct record() with explicit timestamp, and backward
compatibility (record() without timestamp auto-generates created_at).
- Add preserve_event_timestamp Robot integration test and helper subcommand.
- Add static source check in security_audit.robot verifying the
timestamp parameter signature exists.
ISSUES CLOSED: #719
Three BDD scenarios and two Robot Framework integration tests verifying that
_get_service() in automation_profile.py resolves AutomationProfileService
through the DI container rather than manually calling create_engine or
sessionmaker (bug #990).
Bug #990 was fixed by PR #1181 before this TDD test PR merged; the
@tdd_expected_fail tag is therefore absent and these tests serve as
permanent regression guards confirming the fix remains in place.
ISSUES CLOSED: #1031
The new entity-sync feature introduced A2aRequest with a `method` field
(JSON-RPC 2.0 wire format), but several test/helper files used the wrong
field name `operation` when constructing requests or logging, and checked
non-existent `.status`/`.data` attributes on A2aResponse instead of
`.result`/`.error`.
Fixes:
- asgi_app.py: log `a2a_request.method`, not `.operation` (typecheck error)
- server_lifecycle_steps.py: send `method` key in JSON-RPC payload; look
up status in `result` dict, not top-level response body
- server_lifecycle.feature: expect "healthy" (what the handler returns),
not "ok"
- entity_sync_steps.py: construct A2aRequest(method=...) not (operation=...);
assert on .result/.error instead of .status/.data
- robot/helper_entity_sync.py: same method= and result/error fixes across
sync_pull, sync_push, sync_status, sync_facade_no_service helpers
- robot/helper_server_lifecycle.py: same method= and result path fixes
ISSUES CLOSED: #1125
Wrap `await request.json()` in its own try-except for
`json.JSONDecodeError` so malformed/non-JSON request bodies yield
HTTP 400 with JSON-RPC error code -32700 (Parse error) instead of
propagating unhandled to FastAPI's ServerErrorMiddleware and
returning HTTP 500.
Update the BDD step `step_post_a2a_malformed` to send actual raw
non-JSON bytes (`content=b"not-valid-json"`) so the scenario
exercises the JSON parse failure path rather than the existing
A2aRequest validation path (-32600).
ISSUES CLOSED: #863
Add Behave BDD scenarios covering:
- ServerLifecycle.start() with mocked uvicorn (full lifecycle)
- run_server() convenience function with Settings defaults and overrides
- Signal handler installation and non-main-thread edge case
- TeamCollaborationService validation edge cases (empty args)
- SessionRegistry validation edge cases and get_active_sessions
- VersionConflictError negative version validation
- Untracked resource version stamp creation path
These tests cover previously uncovered validation branches and the
server lifecycle start/shutdown paths to maintain >=97% coverage.
Implemented multi-user connection handling with user identity tracking
(TeamMember with owner/admin/member/viewer roles), role-based access
control (TeamPermission with read/write/admin/manage_members), concurrent
session support (SessionRegistry with thread-safe locking and per-user/
per-project queries), and optimistic-locking conflict resolution
(VersionStamp with last-writer-wins, reject, and merge strategies).
Added TeamCollaborationService as the central orchestrator for all
collaboration operations: team membership management, permission
enforcement, session lifecycle, and version-stamp conflict detection/
resolution. The service cleans up user sessions when members are removed.
Domain models follow existing patterns: Pydantic BaseModel with
ConfigDict, StrEnum enums, ULID identifiers, and an error hierarchy
rooted in TeamCollaborationError.
Includes 48 Behave BDD scenarios (178 steps) covering all models,
service operations, and edge cases, plus 15 Robot Framework integration
tests for end-to-end workflow validation.
ISSUES CLOSED: #863
Implement FastAPI-based ASGI application served by uvicorn for
the CleverAgents server mode. Add health check endpoint, A2A
JSON-RPC routing, configurable host:port binding, and graceful
shutdown handling. Server launches via `agents server start`.
- Add FastAPI ASGI app factory at infrastructure/server/asgi_app.py
with /.well-known/agent.json (Agent Card), /health (liveness),
and /a2a (A2A JSON-RPC 2.0 dispatch via A2aLocalFacade)
- Add ServerLifecycle class at infrastructure/server/server_lifecycle.py
wrapping uvicorn.Server with SIGTERM/SIGINT graceful shutdown
- Add `agents server start` CLI command with --host, --port, --log-level
options, resolving defaults from Settings
- Add fastapi>=0.115.0 dependency to pyproject.toml (uvicorn already present)
- Add Behave BDD tests (features/server_lifecycle.feature, 20 scenarios)
- Add Robot Framework integration tests (robot/server_lifecycle.robot)
- Update CHANGELOG.md and vulture_whitelist.py
ISSUES CLOSED: #862
Remove duplicate @then step from uow_coverage_boost_steps.py that
conflicted with actor_config_steps.py:168. Both patterns matched
'a ValueError should be raised containing "{...}"' but used different
capture-variable names, triggering AmbiguousStep at import time.
Update the four When steps to store the caught exception in
context.last_error so the existing actor_config_steps assertion handles
the Then clause.
ISSUES CLOSED: #878
Add PostgreSQL support as the server-mode storage backend alongside
existing SQLite for local mode. Verify all ORM models are dialect-
agnostic, configure connection pooling for multi-user access, add
Docker Compose for local PG development, and wire database URL
selection based on deployment mode.
Changes:
- Add psycopg2-binary dependency to pyproject.toml
- Add server_mode, db_pool_size, db_max_overflow, db_pool_recycle
settings to Settings with environment variable support
- Add resolve_database_url() and is_postgresql() to Settings for
mode-aware database URL resolution
- Configure UnitOfWork engine creation with pool_size, max_overflow,
pool_recycle, and pool_pre_ping for PostgreSQL connections
- Update MigrationRunner to handle both SQLite and PostgreSQL backends
- Add compare_type=True to Alembic env.py for dialect-aware migrations
- Add docker-compose.yml with PostgreSQL 16-alpine for local development
- Add Behave BDD feature (14 scenarios) covering settings, pool config,
engine creation, ORM dialect compatibility, and migration runner
- Add Robot Framework integration tests (12 test cases) for the
abstraction layer with requires_postgresql tag for live PG tests
Fixes:
- Restore require_confirmation parameter to UnitOfWork.__init__
- Add argument validation to UnitOfWork.__init__ (fail-fast)
- Add explicit PostgreSQL isolation_level (READ COMMITTED)
- Add dispose() method and context manager support to UnitOfWork
- Fix MigrationRunner.get_current_revision() to dispose engine
- Fix Settings.is_postgresql() to handle ValueError gracefully
ISSUES CLOSED: #878
Add PostgreSQL support as the server-mode storage backend alongside
existing SQLite for local mode. Verify all ORM models are dialect-
agnostic, configure connection pooling for multi-user access, add
Docker Compose for local PG development, and wire database URL
selection based on deployment mode.
Changes:
- Add psycopg2-binary dependency to pyproject.toml
- Add server_mode, db_pool_size, db_max_overflow, db_pool_recycle
settings to Settings with environment variable support
- Add resolve_database_url() and is_postgresql() to Settings for
mode-aware database URL resolution
- Configure UnitOfWork engine creation with pool_size, max_overflow,
pool_recycle, and pool_pre_ping for PostgreSQL connections
- Update MigrationRunner to handle both SQLite and PostgreSQL backends
- Add compare_type=True to Alembic env.py for dialect-aware migrations
- Add docker-compose.yml with PostgreSQL 16-alpine for local development
- Add Behave BDD feature (14 scenarios) covering settings, pool config,
engine creation, ORM dialect compatibility, and migration runner
- Add Robot Framework integration tests (12 test cases) for the
abstraction layer with requires_postgresql tag for live PG tests
ISSUES CLOSED: #878
Lint (ruff):
- schema.py: import `cast`, replace non-breaking hyphens, inline SIM103
return, wrap long is_nested_v3 line
- config.py: replace `if k in d: del d[k]` with `pop`, collapse nested
ifs (SIM102)
- format both reformatted-only files (ruff format)
Typecheck (pyright):
- config.py: guard `actor_type.lower()` calls with isinstance checks so
the None branch is type-safe
Unit tests (behave):
- Remove duplicate `@then('v3actor the command ...')` and
`@then('the actor should be registered with type ...')` registrations
that crashed step-registry with AmbiguousStep — shared versions live
in actor_add_v3_schema_validation_steps.py
- Add missing Actor import; drop unused ActorConfiguration / is_v3_yaml
imports and unused provider/model locals
- Populate `context.registered_actor_type` from the upsert call args so
the shared assertion step works for combined-format scenarios
- Add a `@when("I run the actor add command without a name argument")`
step that exercises the CLI's "Actor name is required" validation
path (the previously-active step always supplied a name, masking the
scenario's intent)
ISSUES CLOSED: #11189
When users register actors via `agents actor add --config` using the
spec-compliant combined config.actor YAML format — either as a compact
string (config:\n actor: "<provider>/<model>") or as a nested dict
(config:\n actor:\n type: llm\n provider: aws) — the CLI crashed
with click.BadParameter: "Invalid value: 'provider' is required" because
the parser did not detect or flatten the nested structure.
Added:
- _detect_nested_config_actor() in schema.py to recognise both
compact-string and nested-dict forms of config-actor format.
- _flatten_config_actor() in schema.py to merge config["actor"]
fields into the dict top-level (removing the wrapper).
- Flattening logic in ActorConfiguration.from_blob() to handle both
string ("<provider>/<model>") and nested-dict forms of config.actor.
- CLI flattening step at the start of `agents actor add` command.
- New BDD scenarios for combined-format registration.
- Unit tests for detection, flattening, schema-v3 detection, and
full from_blob() flow.
ISSUES CLOSED: #11189
The PR added resolve_actor_options() to StrategyActor, LLMStrategizeActor,
and LLMExecuteActor, but the three mock lifecycle SimpleNamespace objects
used by tests did not include this method, causing AttributeError at
runtime:
- features/mocks/mock_strategy_llm.py: make_mock_lifecycle() used by
robot/helper_strategy_actor.py (strategy_actor.robot llm-json test)
and features/steps/strategy_actor_llm_steps.py
- features/steps/llm_actors_coverage_steps.py: _make_mock_lifecycle()
used by llm_actors_coverage.feature scenarios
- robot/helper_m5_e2e_context.py: inline lifecycle SimpleNamespace
used by m5_e2e_verification.robot Execute Phase LLM Uses ACMS Context
All three now include resolve_actor_options=MagicMock(return_value=None)
matching the None-return contract the actors use when no custom backend
is configured.
ISSUES CLOSED: #11256
Extract shared build_llm_kwargs_from_options() utility in actor/config.py
that converts actor-level YAML options (openai_api_base, openai_api_key,
temperature, max_tokens, etc.) into kwargs for ProviderRegistry.create_llm().
Add options field to ActorConfigSchema so it survives Pydantic validation
instead of being silently dropped by extra='ignore' default.
Add resolve_actor_options() to PlanLifecycleService and both LifecycleService
and PlanLifecycleProtocol protocols so every call site can retrieve the
actor's config_blob.options dict.
Fix the four call sites that previously called create_llm() with zero extra
kwargs, preventing local/custom LLM backends from working:
- StrategyActor._execute_with_llm() in strategy_actor.py
- LLMStrategizeActor.execute() in llm_actors.py
- LLMExecuteActor.execute() in llm_actors.py
- SessionWorkflow._resolve_llm() in session_workflow.py
Refactor reactive path (stream_router.py, tool_caller.py) to use the
shared build_llm_kwargs_from_options() utility instead of inline copies.
Wire actor_options_resolver through CLI session builder and A2A facade.
ISSUES CLOSED: #11256
Add feature file and Behave steps covering all reachable code paths in
merge_conflict.py: MODIFY_MODIFY/DELETE/ADD_ADD conflict types, auto-
resolution for MODIFY_DELETE/DELETE_MODIFY/convergent ADD_ADD, DELETE_DELETE
absence, detect_diff_text formatting, describe_all status types, and direct
_analyse_field call for the unreachable ADD_ADD branch.
Restores coverage above the 96.5% threshold after the merge_conflict module
was added with zero test coverage.
ISSUES CLOSED: #11000
The _info_raises side_effect previously raised RuntimeError on every
logger.info call, which caused the pre-Popen "lsp.transport.starting"
call (transport.py:109) to raise before subprocess.Popen was ever
reached. This meant the cleanup guard at lines 160-186 was never
exercised, making the scenario a permanent no-op TDD stub rather than
a genuine regression guard.
Fix: make _info_raises conditional on the first positional argument
being "lsp.transport.started" (the post-Popen message). The pre-Popen
"lsp.transport.starting" call now passes through normally, Popen
succeeds (mocked), and the RuntimeError is raised inside the guarded
try/except block, triggering terminate() + wait() cleanup.
Remove @tdd_expected_fail from the scenario since the cleanup code at
transport.py:160-186 is in place and the scenario now passes.
Closes#7044
The `else: proc.stderr = stderr` clause was dropped when the helper
was extracted into `_ltcov_helpers.py`. `stdin` and `stdout` both
have the corresponding else branches; this restores parity so that
callers passing a non-"auto" stderr value have it honoured.
- features/steps/lsp_transport_coverage_steps.py: change relative import
`from ._ltcov_helpers import build_lsp_frame, ...` to absolute
`from _ltcov_helpers import make_mock_process ...`, removing unused
`build_lsp_frame`
- features/steps/lsp_transport_post_spawn_cleanup_steps.py: remove unused
`subprocess` and `MagicMock` imports; fix relative import to absolute;
collapse short assert messages to single lines; remove trailing blank line
Behave's exec_file loader does not set __name__ in globals, causing relative
imports to raise KeyError at step-module load time. Absolute imports work
because Behave adds features/steps/ to sys.path.
ISSUES CLOSED: #11237
Closes#7044
The StdioTransport.start() method had an unprotected logger.info() call
after successful Popen(). If that call raised, the subprocess would
leak as an orphaned process. Wrap all post-spawn initialization in a
try/except guard: on any exception after spawn, terminate and wait for
the process (with kill fallback), reset state to None, then re-raise
so callers still get proper error semantics.
The existing stop() method cleanup pattern (terminate → wait → kill) is
mirrored here for consistency across the transport lifecycle.
Tests added:
- TDD scenario with @tdd_issue_7044 verifying subprocess cleanup on
post-Popen exception and state reset to None
- Explicit is_alive() scenarios covering both alive and not-alive states
Refactoring:
- Extracted _make_mock_process and _build_lsp_frame helpers into a
shared _ltcov_helpers module to keep step files under the 500-line
CONTRIBUTING.md limit.
ISSUES CLOSED: #7044
Input._watch_value sets self.virtual_size (Reactive layout=True) on every
keystroke, keeping Textual's WriterThread write queue permanently non-empty.
The queue never reaches qsize()==0 so flush() is never called and typed
characters are invisible until Enter drains the queue.
Two fixes applied:
1. _PromptTextInput subclasses textual.widgets.Input and overrides
virtual_size with Reactive(layout=False). Setting virtual_size in
_watch_value no longer triggers refresh(layout=True). Zero type:ignore
suppressions — uses proper Textual reactive types.
2. CSS #prompt and #prompt > Input changed from height:auto to fixed
heights (3 and 1). This prevents the auto_dimensions guard in
_watch_value from adding a second refresh(layout=True) per keystroke.
3 BDD regression scenarios added covering: virtual_size layout=False,
_PromptInputBase usage in _TextualPromptInput, and fixed CSS height.
ISSUES CLOSED: #11249
Port the options-block merging logic from SimpleLLMAgent._resolve_llm()
(stream_router.py, added in PR #11225 / commit b3851693) to the parallel
resolution path ToolCallingLLMCaller._resolve_llm() in tool_caller.py.
Without this fix, running an actor with --skill would ignore the options
block in the actor YAML, causing openai_api_base and openai_api_key to be
silently dropped. The provider registry then fell back to OPENAI_API_KEY
from the environment, routing requests to api.openai.com instead of the
configured local llama-swap/llama.cpp backend (HTTP 401).
Changes:
- tool_caller.ToolCallingLLMCaller._resolve_llm: read actor_config.options;
extract openai_api_key -> __api_key_sentinel; forward allowed keys
(openai_api_base, timeout, top_p, frequency_penalty, presence_penalty);
reject reserved keys (provider_type, model_id) and unknown keys with
logger.warning, matching SimpleLLMAgent behaviour exactly.
- features/actor_run_tool_calling.feature: add five BDD regression
scenarios (section V, tagged @tdd_issue @tdd_issue_11243) covering
api_base+key forwarding, allowed extra keys, reserved key rejection,
unknown key rejection, and top-level precedence over options.
- features/steps/actor_run_tool_calling_steps.py: add corresponding step
definitions for all five scenarios.
All nox quality gates pass (lint, typecheck, unit_tests 15822/0 fail,
integration_tests 1999/0 fail, coverage_report 97%).
ISSUES CLOSED: #11243
langchain-community >= 0.3 renamed connection_string to connection
in SQLChatMessageHistory.__init__(). Use the new 'connection' kwarg
first and fall back to 'connection_string' for older versions.
Also updates the test assertion in memory_service_coverage_steps.py
to accept either parameter name.
Fixes CI errors in:
- plan_service_coverage.feature:128,141
- consolidated_misc.feature:1531