Files
cleveragents-core/robot/helper_tdd_context_tier_runtime.py
T
brent.edwards 202c9bfe75
CI / lint (push) Successful in 13s
CI / e2e_tests (push) Has started running
CI / quality (push) Successful in 23s
CI / benchmark-publish (push) Has started running
CI / typecheck (push) Successful in 43s
CI / benchmark-regression (push) Has been skipped
CI / build (push) Successful in 22s
CI / security (push) Successful in 59s
CI / integration_tests (push) Successful in 2m49s
CI / unit_tests (push) Successful in 4m1s
CI / docker (push) Successful in 1m26s
CI / coverage (push) Successful in 9m14s
test(acms): TDD failing tests for context tier runtime logic (bug #821) (#1058)
## Summary

TDD expected-fail tests proving bug #821 exists: `ContextTierService` has data models for hot/warm/cold tiers but **no runtime logic** for automatic promotion, demotion, or eviction.

- **Promotion on access**: Accessing a cold-tier fragment repeatedly via `get()` does NOT auto-promote it — `get()` updates `access_count`/`last_accessed` but never calls `promote()`
- **Demotion on staleness**: No staleness enforcement method exists — tried `enforce_staleness()`, `apply_tier_policy()`, `tick()`, etc. — none are implemented
- **Eviction on budget overflow**: `store()` does NOT enforce `TierBudget.max_tokens_hot` — the hot tier grows without bound

### Files Added
| File | Purpose |
|------|---------|
| `features/tdd_context_tier_runtime.feature` | 3 Behave scenarios tagged `@tdd_expected_fail @tdd_bug @tdd_bug_821 @mock_only` |
| `features/steps/tdd_context_tier_runtime_steps.py` | Type-annotated step definitions exercising real `ContextTierService` |
| `robot/tdd_context_tier_runtime.robot` | 3 Robot Framework integration tests tagged `tdd_expected_fail` |
| `robot/helper_tdd_context_tier_runtime.py` | Helper script for Robot tests with 3 subcommands |

### Verification
- `nox -s lint` — passed
- `nox -s typecheck` — passed (0 errors)
- `nox -s unit_tests -- features/tdd_context_tier_runtime.feature` — **3 scenarios passed** (all assertions fail as expected, `@tdd_expected_fail` inverts to CI pass)

ISSUES CLOSED: #840

Reviewed-on: #1058
Co-authored-by: Brent Edwards <brent.edwards@cleverthis.com>
Co-committed-by: Brent Edwards <brent.edwards@cleverthis.com>
2026-03-19 22:24:41 +00:00

233 lines
7.6 KiB
Python

"""Helper script for tdd_context_tier_runtime.robot smoke tests.
Each subcommand exercises ``ContextTierService`` to reproduce bug #821.
The helper reports the **real** outcome: it exits 0 and prints the sentinel
when the expected behaviour is observed (bug fixed), and exits 1 when the
bug is still present. The ``tdd_expected_fail_listener`` on the Robot side
handles pass/fail inversion while the bug remains open.
"""
from __future__ import annotations
import sys
from collections.abc import Callable
from datetime import UTC, datetime, timedelta
from pathlib import Path
from typing import NoReturn
# Ensure local source tree is importable.
_ROOT: Path = Path(__file__).resolve().parents[1]
_SRC: str = str(_ROOT / "src")
_ROBOT: str = str(_ROOT / "robot")
for _p in (_SRC, _ROBOT):
if _p not in sys.path:
sys.path.insert(0, _p)
from cleveragents.application.services.context_tiers import ( # noqa: E402
ContextTierService,
)
from cleveragents.domain.models.acms.tiers import ( # noqa: E402
ContextTier,
TierBudget,
TieredFragment,
)
def _fail(msg: str) -> NoReturn:
"""Print error message to stderr and exit with code 1."""
print(msg, file=sys.stderr)
sys.exit(1)
def _make_fragment(
fragment_id: str,
tier: ContextTier,
token_count: int = 50,
content: str = "test content",
last_accessed: datetime | None = None,
) -> TieredFragment:
"""Create a ``TieredFragment`` with the given properties."""
kwargs: dict[str, object] = {
"fragment_id": fragment_id,
"content": content,
"tier": tier,
"token_count": token_count,
"project_name": "test-project",
}
if last_accessed is not None:
kwargs["last_accessed"] = last_accessed
return TieredFragment(**kwargs) # type: ignore[arg-type]
# ---------------------------------------------------------------------------
# Subcommands
# ---------------------------------------------------------------------------
def _promote_on_access() -> None:
"""Verify that accessing a cold-tier fragment repeatedly auto-promotes it.
Bug #821: ``get()`` increments ``access_count`` and updates
``last_accessed`` but never calls ``promote()`` or any equivalent
runtime logic. The fragment stays in the cold tier forever.
"""
service: ContextTierService = ContextTierService(settings=None)
frag: TieredFragment = _make_fragment(
fragment_id="frag-promote-001",
tier=ContextTier.COLD,
token_count=50,
)
service.store(frag)
# Access the fragment 5 times
for _ in range(5):
result: TieredFragment | None = service.get("frag-promote-001")
if result is None:
_fail("Fragment disappeared during repeated access")
# Check if the fragment was promoted
final: TieredFragment | None = service.get("frag-promote-001")
if final is None:
_fail("Fragment not found after repeated access")
if final.tier == ContextTier.COLD:
_fail(
f"Fragment is still in COLD tier after 5 accesses "
f"(access_count={final.access_count}). "
f"ContextTierService.get() does not auto-promote fragments "
f"based on access patterns (bug #821)."
)
print("tdd-promote-on-access-ok")
def _demote_on_staleness() -> None:
"""Verify that a stale hot-tier fragment is auto-demoted.
Bug #821: ContextTierService has no staleness enforcement method.
No ``enforce_staleness()``, ``apply_tier_policy()``, ``tick()``,
or similar runtime method exists.
"""
service: ContextTierService = ContextTierService(settings=None)
stale_time: datetime = datetime.now(tz=UTC) - timedelta(hours=24)
frag: TieredFragment = _make_fragment(
fragment_id="frag-stale-001",
tier=ContextTier.HOT,
token_count=100,
last_accessed=stale_time,
)
service.store(frag)
# Attempt to invoke staleness enforcement
invoked: bool = False
for method_name in (
"enforce_staleness",
"apply_tier_policy",
"tick",
"enforce_demotion_policy",
"run_lifecycle",
"apply_lifecycle",
):
method: object = getattr(service, method_name, None)
if callable(method):
method()
invoked = True
break
if not invoked:
_fail(
"No staleness enforcement method found on ContextTierService. "
"Tried: enforce_staleness(), apply_tier_policy(), tick(), "
"enforce_demotion_policy(), run_lifecycle(), apply_lifecycle(). "
"ContextTierService has no runtime logic for automatic "
"demotion of stale fragments (bug #821)."
)
# Check if the fragment was demoted
result: TieredFragment | None = service.get("frag-stale-001")
if result is None:
_fail("Stale fragment not found after staleness enforcement")
if result.tier == ContextTier.HOT:
_fail(
f"Stale fragment is still in HOT tier "
f"(last_accessed={result.last_accessed}). "
f"Staleness enforcement did not demote it (bug #821)."
)
print("tdd-demote-on-staleness-ok")
def _evict_on_overflow() -> None:
"""Verify that hot tier budget overflow triggers auto-eviction.
Bug #821: ``store()`` does not check ``TierBudget.max_tokens_hot``
and does not call ``evict_lru()`` when the budget is exceeded.
"""
service: ContextTierService = ContextTierService(settings=None)
service._budget = TierBudget(
max_tokens_hot=100,
max_decisions_warm=500,
max_decisions_cold=5000,
)
# Fill hot tier to exactly 100 tokens (2 x 50-token fragments)
for i in range(2):
frag: TieredFragment = _make_fragment(
fragment_id=f"frag-budget-{i:03d}",
tier=ContextTier.HOT,
token_count=50,
content="x" * 50,
last_accessed=datetime.now(tz=UTC) - timedelta(minutes=2 - i),
)
service.store(frag)
oldest_id: str = "frag-budget-000"
# Store one more fragment that exceeds the budget
overflow: TieredFragment = _make_fragment(
fragment_id="frag-overflow-001",
tier=ContextTier.HOT,
token_count=50,
content="overflow",
)
service.store(overflow)
# Check hot tier total tokens
hot_total: int = sum(f.token_count for f in service._hot.values())
if hot_total > 100:
_fail(
f"Hot tier has {hot_total} tokens but budget is 100 tokens. "
f"store() does not enforce TierBudget.max_tokens_hot or "
f"trigger automatic LRU eviction (bug #821)."
)
# Check that the oldest fragment was evicted
oldest: TieredFragment | None = service.get(oldest_id)
if oldest is not None and oldest.tier == ContextTier.HOT:
_fail(
f"Oldest fragment '{oldest_id}' is still in hot tier after "
f"budget overflow. store() does not auto-evict (bug #821)."
)
print("tdd-evict-on-overflow-ok")
# ---------------------------------------------------------------------------
# Dispatcher
# ---------------------------------------------------------------------------
_COMMANDS: dict[str, Callable[[], None]] = {
"promote-on-access": _promote_on_access,
"demote-on-staleness": _demote_on_staleness,
"evict-on-overflow": _evict_on_overflow,
}
if __name__ == "__main__":
if len(sys.argv) < 2 or sys.argv[1] not in _COMMANDS:
print(
f"Usage: {sys.argv[0]} <{'|'.join(_COMMANDS)}>",
file=sys.stderr,
)
sys.exit(1)
cmd: Callable[[], None] = _COMMANDS[sys.argv[1]]
cmd()