Kairo-Jinkendo/backend/tests/test_agent_slots_unit.py
Lars a5154cbd3b
Some checks failed
Deploy Development / deploy (push) Successful in 47s
Test Suite / pytest-backend (push) Failing after 4m15s
Test Suite / k6 /api/health Baseline (push) Has been skipped
Test Suite / playwright-smoke (push) Has been skipped
Test Suite / lint-backend (push) Successful in 2s
Test Suite / compose-smoke (push) Has been skipped
feat(steering): Kernel v0.4 Agent-Slots, Tech Debt und Review-Attention
K-Ext-4/P8 deklarative Agent-Slots; P7 tech_debt Read Model und Vokabular; Review-Attention in den Kernel verlagert.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-27 11:54:48 +02:00

139 lines
4.2 KiB
Python

"""Unit tests for agent slot providers."""
from __future__ import annotations
from datetime import datetime, timedelta, timezone
from types import SimpleNamespace
from steering.agent_slots.guardrail_review import resolve_guardrail_review_slots
from steering.agent_slots.recurring_due import resolve_recurring_due_slots
from steering.agent_slots.review_due import resolve_review_due_slots
from steering.agent_slots.registry import compute_agent_slots
from steering.kernel.models import HorizonMarker
def _ctx(**kwargs):
defaults = {
"initiative_id": "init-1",
"horizon": HorizonMarker(kind="none"),
"operating_context": {
"data_slices": ["reviews", "recurring", "roadmap"],
"steering_elements": ["gate_fulfillment", "recurring_rhythm"],
"agent_slot_config": {},
},
"_reviews": [],
"_recurring_elements": [],
"_actions": [],
}
defaults.update(kwargs)
class FakeCtx:
initiative_id = defaults["initiative_id"]
horizon = defaults["horizon"]
operating_context = defaults["operating_context"]
@property
def steering_elements(self):
return frozenset(self.operating_context.get("steering_elements") or [])
@property
def data_slices(self):
return list(self.operating_context.get("data_slices") or [])
@property
def agent_slot_config(self):
return dict(self.operating_context.get("agent_slot_config") or {})
@property
def reviews(self):
return defaults["_reviews"]
@property
def recurring_elements(self):
return defaults["_recurring_elements"]
@property
def actions(self):
return defaults["_actions"]
return FakeCtx()
def test_review_due_slot_for_overdue_planned_review():
past = (datetime.now(timezone.utc) - timedelta(days=1)).isoformat()
ctx = _ctx(
_reviews=[
{
"id": "rev-1",
"title": "Architektur-Review",
"status": "planned",
"due_at": past,
}
]
)
slots = resolve_review_due_slots(ctx)
assert len(slots) == 1
assert slots[0]["slot_key"] == "review.due"
assert slots[0]["scope_id"] == "rev-1"
assert slots[0]["actor_role_hint"] == "reviewer"
def test_recurring_due_slot():
past = (datetime.now(timezone.utc) - timedelta(hours=2)).isoformat()
ctx = _ctx(
_recurring_elements=[
{
"id": "rec-1",
"title": "Sprint-Retro",
"status": "active",
"next_due_at": past,
}
]
)
slots = resolve_recurring_due_slots(ctx)
assert len(slots) == 1
assert slots[0]["slot_key"] == "recurring.due"
def test_guardrail_slot_when_gate_has_planning_debt():
ctx = _ctx(
horizon=HorizonMarker(
kind="gate",
gate_roadmap_item_id="gate-1",
gate_title="G3 Architektur",
),
_reviews=[],
)
read_models = {
"planning_debt": [{"roadmap_item_id": "gate-1", "title": "G3"}],
}
slots = resolve_guardrail_review_slots(ctx, read_models)
assert len(slots) == 1
assert slots[0]["slot_key"] == "review.guardrail"
assert slots[0]["guardrail_pack"] == "default_v0"
def test_compute_agent_slots_respects_enabled_slots_filter():
past = (datetime.now(timezone.utc) - timedelta(days=1)).isoformat()
ctx = _ctx(
operating_context={
"data_slices": ["reviews", "recurring", "roadmap"],
"steering_elements": ["gate_fulfillment", "recurring_rhythm"],
"agent_slot_config": {"enabled_slots": ["review.due"]},
},
_reviews=[
{"id": "rev-1", "title": "Review", "status": "planned", "due_at": past}
],
_recurring_elements=[
{
"id": "rec-1",
"title": "Retro",
"status": "active",
"next_due_at": past,
}
],
)
slots = compute_agent_slots(ctx, read_models={}, proposals={})
assert len(slots) == 1
assert slots[0]["slot_key"] == "review.due"