Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions project/ticket-191/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Ticket 191: Reduce cyclomatic complexity: get_agent_availability (CC=15)

- **ID**: ticket-191
- **Owner**: unresolved:human
- **Status**: IN_PROGRESS
- **Workflow state**: EDIT
- **Created**: 2026-09-20

## Goal and scope

SESSION_EXECUTION_AUTHORIZATION: the planfile queue owner handed STARTER-609
to this session with explicit completion instructions (checks, commit,
`planfile ticket done STARTER-609`).

`project/analysis.toon.yaml` reports `get_agent_availability` defined in
`src/koru/agent_availability.py:137` with cyclomatic complexity 15 (limit 15).
Extract the environment-override lookup, the registry-entry parsing and the
temporary-block expiry reset into focused module-level helpers with unchanged
behaviour, bringing the function under the limit 15.

## Acceptance criteria

- [x] AC-01: `get_agent_availability` cyclomatic complexity is below 15
(code2llm re-run on this checkout, output outside the repo tree).
- [x] AC-02: `tests/test_agent_availability.py` passes unmodified.
- [x] AC-03: `ruff check` passes on `src/koru/agent_availability.py`.
- [x] AC-04: `./project/governance-check.sh --base origin/main` passes.

## Validation evidence

Recorded 2026-09-20 in `.worktrees/ticket-191--agent-availability-complexity`
(branch `ticket/191-agent-availability-complexity`, base `origin/main` = `f171fe09`):

- AC-01: `code2llm <worktree> -f all -o /tmp/opencode/code2llm-ticket191
--no-chunk --exclude *.md --exclude plugins` — the CC report no longer lists
`get_agent_availability` (module row `agent_availability CC=7`). Independent
AST recount: `get_agent_availability` CC 15 → 5; every extracted helper ≤ 7
(`_availability_from_registry_entry` 7, `_with_expired_block_reset` 4,
`_environment_override` 3). Behaviour differential over 18 scenarios
(empty id, env precedence both-ways, registry miss/hit, invalid entries,
expiry boundary at equality, corrupt registry) is byte-identical to the
accepted base.
- AC-02: `PYTHONPATH=src python -m pytest tests/test_agent_availability.py -q`
— 7 passed, unmodified.
- AC-03: `ruff check src/koru/agent_availability.py` — All checks passed.
`ruff format --check` findings on pre-existing untouched spans are unchanged
from the accepted base (verified via `git stash` comparison); the refactor
keeps the module's existing formatting idiom verbatim.
- AC-04: `bash project/governance-check.sh --base origin/main` —
`GOV-PASS: passed (0 errors, 0 warnings)`.

## Tracking boundary

This directory contains the minimal reviewed intent. Optional participant prose
and raw command logs are not required delivery output.
97 changes: 97 additions & 0 deletions project/ticket-191/intent.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
{
"schema": "new-project.intent/v3",
"ticket": "ticket-191",
"summary": "Reduce cyclomatic complexity: get_agent_availability (CC=15)",
"workstream": "application",
"classification": {
"kind": "SERVICE",
"priority": "P2",
"origin": "health"
},
"allowedPaths": [
"project/ticket-191/**",
"TODO.md",
"project/TICKETS.md",
"src/koru/agent_availability.py",
"tests/test_agent_availability.py"
],
"forbiddenPaths": [
"project/ticket-*/user-*.md"
],
"stacks": [],
"dependsOn": [],
"conflictsWith": [],
"integrationTicket": null,
"delivery": {
"acceptedBaseSha": "f171fe09a90cd59315de70a8b02b87db1158ff89",
"targetBranch": "main",
"outcome": "get_agent_availability drops from cyclomatic complexity 15 to below the code2llm limit 15 by extracting behavior-preserving module-level helpers (_environment_override, _availability_from_registry_entry, _with_expired_block_reset). Env-override precedence (KORU_AGENT_AVAILABLE before KORU_AGENT_UNAVAILABLE), registry parsing, invalid-entry fallback, and temporary-block expiry semantics are byte-for-byte equivalent; tests/test_agent_availability.py passes unmodified.",
"nonGoals": [
"Changing any observable status, reason, source, observed_at or retry_after value",
"Regenerating project/analysis.toon.yaml or project/planfile-tickets.yaml (integration-owned artifacts)",
"Renaming or removing any public name",
"Reformatting pre-existing spans of src/koru/agent_availability.py outside the refactored function"
],
"complexity": "S",
"estimatedMinutes": 30,
"budgets": {
"maxImplementationFiles": 1,
"maxAffectedComponents": 1,
"maxPublicInterfaceChanges": 0,
"maxRuntimeDependencies": 0
},
"architecture": {
"status": "accepted",
"decision": "Decompose get_agent_availability into three single-responsibility private module functions mirroring the module's existing function-oriented style: environment override resolution, registry-entry parsing with the invalid-entry fallback, and expired-block reset. The public function keeps only normalization and dispatch; each helper returns an explicit AgentAvailability so behavior is equivalent.",
"components": [
{
"name": "agent-availability",
"paths": [
"src/koru/agent_availability.py",
"tests/test_agent_availability.py"
]
}
],
"responsibilityChanges": false,
"interfaceChanges": [],
"dataChanges": [],
"ui": {
"impact": "none",
"states": [],
"evidence": []
},
"rollback": "Revert the commit; the original single get_agent_availability body is restored verbatim."
},
"runtimeDependencies": [],
"validation": [
{
"criterion": "AC-01",
"commands": [
"code2llm <worktree> -f all -o /tmp/opencode/code2llm-ticket191 --no-chunk --exclude *.md --exclude plugins (complexity re-measured on get_agent_availability)"
],
"evidence": "Re-run code2llm reports CC < 15 for get_agent_availability."
},
{
"criterion": "AC-02",
"commands": [
"PYTHONPATH=src python -m pytest tests/test_agent_availability.py -q"
],
"evidence": "Existing agent-availability suite passes unmodified."
},
{
"criterion": "AC-03",
"commands": [
"ruff check src/koru/agent_availability.py"
],
"evidence": "Lint passes on the refactored module; ruff format status of pre-existing untouched spans is unchanged from the accepted base."
},
{
"criterion": "AC-04",
"commands": [
"bash project/governance-check.sh --base origin/main"
],
"evidence": "Governance gate passes with zero errors."
}
]
}
}
71 changes: 46 additions & 25 deletions src/koru/agent_availability.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,31 +134,27 @@ def _registry_write_lock() -> Iterator[None]:
fcntl.flock(lock_file.fileno(), fcntl.LOCK_UN)


def get_agent_availability(agent_id: str, *, now: float | None = None) -> AgentAvailability:
"""Return effective availability, including environment overrides and expiry."""
normalized = normalize_agent_id(agent_id)
if not normalized:
return AgentAvailability(agent_id="", reason="empty_agent_id")
if normalized in _env_ids("KORU_AGENT_AVAILABLE"):
return AgentAvailability(
agent_id=normalized,
status="available",
reason="environment override",
source="env:KORU_AGENT_AVAILABLE",
)
if normalized in _env_ids("KORU_AGENT_UNAVAILABLE"):
return AgentAvailability(
agent_id=normalized,
status="unavailable",
reason="environment override",
source="env:KORU_AGENT_UNAVAILABLE",
)
def _environment_override(normalized: str) -> AgentAvailability | None:
for env_name, status in (
("KORU_AGENT_AVAILABLE", "available"),
("KORU_AGENT_UNAVAILABLE", "unavailable"),
):
if normalized in _env_ids(env_name):
return AgentAvailability(
agent_id=normalized,
status=status,
reason="environment override",
source=f"env:{env_name}",
)
return None

raw = _read_registry().get(normalized)
if raw is None:
return AgentAvailability(agent_id=normalized)

def _availability_from_registry_entry(
normalized: str,
raw: Mapping[str, Any],
) -> AgentAvailability:
try:
availability = AgentAvailability(
return AgentAvailability(
agent_id=normalized,
status=str(raw.get("status") or "unknown"),
reason=str(raw.get("reason") or ""),
Expand All @@ -172,18 +168,43 @@ def get_agent_availability(agent_id: str, *, now: float | None = None) -> AgentA
)
except (TypeError, ValueError):
return AgentAvailability(agent_id=normalized, reason="invalid_registry_entry")
current = time.time() if now is None else now


def _with_expired_block_reset(
availability: AgentAvailability,
*,
current: float,
) -> AgentAvailability:
if availability.blocked and availability.retry_after is not None:
if availability.retry_after <= current:
return AgentAvailability(
agent_id=normalized,
agent_id=availability.agent_id,
reason="temporary block expired",
source=availability.source,
observed_at=availability.observed_at,
)
return availability


def get_agent_availability(agent_id: str, *, now: float | None = None) -> AgentAvailability:
"""Return effective availability, including environment overrides and expiry."""
normalized = normalize_agent_id(agent_id)
if not normalized:
return AgentAvailability(agent_id="", reason="empty_agent_id")
override = _environment_override(normalized)
if override is not None:
return override

raw = _read_registry().get(normalized)
if raw is None:
return AgentAvailability(agent_id=normalized)
availability = _availability_from_registry_entry(normalized, raw)
return _with_expired_block_reset(
availability,
current=time.time() if now is None else now,
)


def set_agent_availability(
agent_id: str,
*,
Expand Down
Loading