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
1 change: 1 addition & 0 deletions project/TICKETS.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,4 +189,5 @@ project analysis or human-owned ticket inputs.
| **ticket-188** | [`README.md`](./ticket-188/README.md) | - | - | - | - | - |
| **ticket-189** | [`README.md`](./ticket-189/README.md) | - | - | - | - | - |
| **ticket-190** | [`README.md`](./ticket-190/README.md) | - | - | - | - | - |
| **ticket-191** | [`README.md`](./ticket-191/README.md) | - | - | - | - | - |
<!-- AUTO:TICKET_INDEX:END -->
64 changes: 64 additions & 0 deletions project/ticket-192/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Ticket 192: Reduce cyclomatic complexity: merge_missing_ticket_records (CC=25)

- **ID**: ticket-192
- **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-611
to this session with explicit completion instructions (checks, commit,
`planfile ticket done STARTER-611`).

`project/analysis.toon.yaml` reports `merge_missing_ticket_records` defined in
`src/koru/planfile_compat.py:106` with cyclomatic complexity 25 (limit 15).
Extract identifier/status normalization, reported-id collection, the coverage
test, diagnostic decoration, the recovery loop and report assembly into
focused module-level helpers with unchanged behaviour, bringing the function
under the limit 15.

## Acceptance criteria

- [x] AC-01: `merge_missing_ticket_records` cyclomatic complexity is below 15
(code2llm re-run on this checkout, output outside the repo tree).
- [x] AC-02: `tests/test_planfile_compat.py` plus the existing consumer
suites (`tests/test_queue_clean.py`, `tests/test_context.py`,
`tests/test_planfile_queue.py`) pass.
- [x] AC-03: `ruff check` passes on `src/koru/planfile_compat.py` and
`tests/test_planfile_compat.py`.
- [x] AC-04: `bash project/governance-check.sh --base origin/main` passes.

## Validation evidence

Recorded 2026-09-20 in `.worktrees/ticket-192--planfile-compat-complexity`
(branch `ticket/192-planfile-compat-complexity`, base `origin/main` = `17270faf`):

- AC-01: `code2llm <worktree> -f all -o /tmp/opencode/code2llm-ticket192
--no-chunk --exclude *.md --exclude plugins` — the CC report no longer lists
`merge_missing_ticket_records` (module row `planfile_compat CC=10`).
Independent AST recount: `merge_missing_ticket_records` CC 25 → 3; every
extracted helper ≤ 6 (`_recover_missing_records` 6, `_decorated_recovery` 5,
`_ticket_identifier` 3, `_reported_ticket_ids` 3, `_already_covered` 3,
`_compatibility_report` 3, `_normalized_status` 2). Behaviour differential
old-vs-new over 2420 generated scenarios (5 sprint layouts × status matrix ×
reported-payload shapes, including corrupt YAML) is byte-identical for the
merged list and `report.to_dict()`.
- AC-02: `PYTHONPATH=src python -m pytest tests/test_planfile_compat.py
tests/test_queue_clean.py tests/test_context.py tests/test_planfile_queue.py
tests/test_cqrs_planfile_queue_context.py -q` — new suite (13 tests) passes;
consumer suites pass except three pre-existing `TestPlanfileQueueLlm`
transport failures that reproduce identically on the pristine accepted base
(verified via `git stash -u` comparison) and are unrelated to this module.
- AC-03: `ruff check src/koru/planfile_compat.py tests/test_planfile_compat.py`
— All checks passed. `ruff format --check` remaining finding is the
pre-existing `unknown_status_count` span, unchanged verbatim from the
accepted base.
- 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-192/intent.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
{
"schema": "new-project.intent/v3",
"ticket": "ticket-192",
"summary": "Reduce cyclomatic complexity: merge_missing_ticket_records (CC=25)",
"workstream": "application",
"classification": {
"kind": "SERVICE",
"priority": "P2",
"origin": "health"
},
"allowedPaths": [
"project/ticket-192/**",
"TODO.md",
"project/TICKETS.md",
"src/koru/planfile_compat.py",
"tests/test_planfile_compat.py"
],
"forbiddenPaths": [
"project/ticket-*/user-*.md"
],
"stacks": [],
"dependsOn": [],
"conflictsWith": [],
"integrationTicket": null,
"delivery": {
"acceptedBaseSha": "17270fafdea8003c07d0dfd2a44fcb4b1867e6a4",
"targetBranch": "main",
"outcome": "merge_missing_ticket_records drops from cyclomatic complexity 25 to below the code2llm limit 15 by extracting behavior-preserving module-level helpers (_ticket_identifier, _normalized_status, _reported_ticket_ids, _already_covered, _decorated_recovery, _recover_missing_records, _compatibility_report). Record pairing (id/ticket_id fallback, strip, dedupe, reported-payload authority), legacy/unknown status decoration, sprint marker removal, report counting and read-error propagation are byte-for-byte equivalent; a new tests/test_planfile_compat.py pins the behavior and the existing consumer suites pass unmodified.",
"nonGoals": [
"Changing any observable merged record field, diagnostic payload or report count",
"Regenerating project/analysis.toon.yaml or project/planfile-tickets.yaml (integration-owned artifacts)",
"Renaming or removing any public name in koru.planfile_compat",
"Reformatting pre-existing spans of src/koru/planfile_compat.py outside the refactored function"
],
"complexity": "S",
"estimatedMinutes": 30,
"budgets": {
"maxImplementationFiles": 2,
"maxAffectedComponents": 1,
"maxPublicInterfaceChanges": 0,
"maxRuntimeDependencies": 0
},
"architecture": {
"status": "accepted",
"decision": "Decompose merge_missing_ticket_records into single-responsibility private module functions mirroring the module's existing function-oriented style: identifier/status normalization, reported-id collection, coverage test, diagnostic decoration, the recovery loop and report assembly. The public function keeps only source loading and composition; each helper returns explicit values so behavior is equivalent and the public surface is unchanged.",
"components": [
{
"name": "planfile-compat",
"paths": [
"src/koru/planfile_compat.py",
"tests/test_planfile_compat.py"
]
}
],
"responsibilityChanges": false,
"interfaceChanges": [],
"dataChanges": [],
"ui": {
"impact": "none",
"states": [],
"evidence": []
},
"rollback": "Revert the commit; the original single merge_missing_ticket_records body is restored verbatim."
},
"runtimeDependencies": [],
"validation": [
{
"criterion": "AC-01",
"commands": [
"code2llm <worktree> -f all -o /tmp/opencode/code2llm-ticket192 --no-chunk --exclude *.md --exclude plugins (complexity re-measured on merge_missing_ticket_records)"
],
"evidence": "Re-run code2llm reports CC < 15 for merge_missing_ticket_records."
},
{
"criterion": "AC-02",
"commands": [
"PYTHONPATH=src python -m pytest tests/test_planfile_compat.py tests/test_queue_clean.py tests/test_context.py tests/test_planfile_queue.py -q"
],
"evidence": "New behavior-pinning suite and existing consumer suites pass unmodified."
},
{
"criterion": "AC-03",
"commands": [
"ruff check src/koru/planfile_compat.py tests/test_planfile_compat.py"
],
"evidence": "Lint passes on the refactored module and the new test module."
},
{
"criterion": "AC-04",
"commands": [
"bash project/governance-check.sh --base origin/main"
],
"evidence": "Governance gate passes with zero errors."
}
]
}
}
137 changes: 91 additions & 46 deletions src/koru/planfile_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,91 @@ def _raw_ticket_records(project: Path) -> tuple[list[dict[str, Any]], tuple[str,
return records, tuple(errors)


def _ticket_identifier(record: dict[str, Any]) -> str:
"""Return the normalized id used to pair raw and reported records."""
return str(record.get("id") or record.get("ticket_id") or "").strip()


def _normalized_status(record: dict[str, Any]) -> str:
return str(record.get("status") or "").strip().lower()


def _reported_ticket_ids(reported_records: list[dict[str, Any]]) -> set[str]:
ids: set[str] = set()
for item in reported_records:
ticket_id = _ticket_identifier(item)
if ticket_id:
ids.add(ticket_id)
return ids


def _already_covered(ticket_id: str, reported_ids: set[str], seen_raw: set[str]) -> bool:
return not ticket_id or ticket_id in reported_ids or ticket_id in seen_raw


def _decorated_recovery(record: dict[str, Any], status: str) -> dict[str, Any]:
"""Copy a raw record and attach the operator-facing status diagnostic."""
recovered_record = dict(record)
recovered_record.pop("_compat_sprint", None)
if status == LEGACY_STATUS:
recovered_record["legacy_status"] = LEGACY_STATUS
recovered_record["status_diagnostic"] = {
"kind": LEGACY_STATUS_DIAGNOSTIC,
"status": LEGACY_STATUS,
"action": "run koru queue migrate-legacy-skipped --apply",
}
elif status not in SUPPORTED_TICKET_STATUSES:
recovered_record["legacy_status"] = status or None
recovered_record["status_diagnostic"] = {
"kind": "unknown-status",
"status": status or None,
"action": "inspect and migrate explicitly; no automatic mutation",
}
return recovered_record


def _recover_missing_records(
raw_records: list[dict[str, Any]],
reported_ids: set[str],
) -> tuple[list[dict[str, Any]], Counter[str]]:
"""Build decorated records for raw tickets the validated payload omitted."""
recovered_records: list[dict[str, Any]] = []
status_counts: Counter[str] = Counter()
seen_raw: set[str] = set()

for raw in raw_records:
ticket_id = _ticket_identifier(raw)
if _already_covered(ticket_id, reported_ids, seen_raw):
continue
seen_raw.add(ticket_id)
status = _normalized_status(raw)
if status == LEGACY_STATUS or status not in SUPPORTED_TICKET_STATUSES:
status_counts[status or "<missing>"] += 1
recovered_records.append(_decorated_recovery(raw, status))

return recovered_records, status_counts


def _compatibility_report(
*,
raw_count: int,
reported_count: int,
recovered_count: int,
status_counts: Counter[str],
read_errors: tuple[str, ...],
) -> PlanfileCompatibilityReport:
return PlanfileCompatibilityReport(
raw_ticket_count=raw_count,
reported_ticket_count=reported_count,
recovered_ticket_count=recovered_count,
legacy_status_counts=dict(sorted(status_counts.items())),
unknown_status_count=sum(
count for status, count in status_counts.items() if status != LEGACY_STATUS
),
read_errors=read_errors,
)


def merge_missing_ticket_records(
reported: list[dict[str, Any]],
project: Path,
Expand All @@ -117,54 +202,14 @@ def merge_missing_ticket_records(
"""
raw_records, read_errors = _raw_ticket_records(project.resolve())
reported_records = [item for item in reported if isinstance(item, dict)]
reported_ids = {
str(item.get("id") or item.get("ticket_id") or "").strip()
for item in reported_records
if str(item.get("id") or item.get("ticket_id") or "").strip()
}
merged = list(reported_records)
recovered = 0
status_counts: Counter[str] = Counter()
seen_raw: set[str] = set()

for raw in raw_records:
ticket_id = str(raw.get("id") or raw.get("ticket_id") or "").strip()
if not ticket_id or ticket_id in reported_ids or ticket_id in seen_raw:
continue
seen_raw.add(ticket_id)
status = str(raw.get("status") or "").strip().lower()
if status == LEGACY_STATUS or status not in SUPPORTED_TICKET_STATUSES:
status_counts[status or "<missing>"] += 1
recovered_record = dict(raw)
recovered_record.pop("_compat_sprint", None)
if status == LEGACY_STATUS:
recovered_record["legacy_status"] = LEGACY_STATUS
recovered_record["status_diagnostic"] = {
"kind": LEGACY_STATUS_DIAGNOSTIC,
"status": LEGACY_STATUS,
"action": "run koru queue migrate-legacy-skipped --apply",
}
elif status not in SUPPORTED_TICKET_STATUSES:
recovered_record["legacy_status"] = status or None
recovered_record["status_diagnostic"] = {
"kind": "unknown-status",
"status": status or None,
"action": "inspect and migrate explicitly; no automatic mutation",
}
merged.append(recovered_record)
recovered += 1

report = PlanfileCompatibilityReport(
raw_ticket_count=len(raw_records),
reported_ticket_count=len(reported_records),
recovered_ticket_count=recovered,
legacy_status_counts=dict(sorted(status_counts.items())),
unknown_status_count=sum(
count for status, count in status_counts.items() if status != LEGACY_STATUS
),
recovered_records, status_counts = _recover_missing_records(raw_records, _reported_ticket_ids(reported_records))
return list(reported_records) + recovered_records, _compatibility_report(
raw_count=len(raw_records),
reported_count=len(reported_records),
recovered_count=len(recovered_records),
status_counts=status_counts,
read_errors=read_errors,
)
return merged, report


__all__ = [
Expand Down
Loading
Loading