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
9 changes: 9 additions & 0 deletions planfile/core/store_tickets.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@ def _ticket_from_data(self, t_data: dict[str, Any]) -> Ticket | None:
# legacy YAML compat: British 'cancelled' → American 'canceled'
if t_data.get('status') == 'cancelled':
t_data['status'] = 'canceled'
# Early queue writers used ``completed`` for both the ticket and
# execution terminal. Project that historical alias as the
# canonical ``done`` state without rewriting the authoritative
# YAML or its audit history merely because it was read.
if t_data.get('status') == 'completed':
t_data['status'] = 'done'
execution = t_data.get('execution')
if isinstance(execution, dict) and execution.get('state') == 'completed':
t_data['execution'] = {**execution, 'state': 'done'}
if 'integration' in t_data and isinstance(t_data['integration'], str):
t_data['labels'] = [t_data.pop('integration')]
if hasattr(self, '_project_ticket_evidence'):
Expand Down
25 changes: 25 additions & 0 deletions tests/test_ticket_execution.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from __future__ import annotations

import pytest
import yaml

from planfile import (
Planfile,
Expand All @@ -15,6 +16,30 @@
from planfile.core.store import ImmutableTerminalReopenError


def test_legacy_completed_terminal_is_projected_as_done_without_rewriting_yaml(tmp_path):
pf = Planfile(str(tmp_path))
ticket = pf.create_ticket(
name="Historical completed terminal",
execution=TicketExecution(state="running"),
)
sprint_path = tmp_path / ".planfile" / "sprints" / "current.yaml"
stored = yaml.safe_load(sprint_path.read_text(encoding="utf-8"))
raw_ticket = stored["sprint"]["tickets"][ticket.id]
raw_ticket["status"] = "completed"
raw_ticket["execution"]["state"] = "completed"
sprint_path.write_text(yaml.safe_dump(stored, sort_keys=False), encoding="utf-8")

loaded = pf.get_ticket(ticket.id)

assert loaded is not None
assert loaded.status == "done"
assert loaded.execution is not None
assert loaded.execution.state == "done"
authoritative = yaml.safe_load(sprint_path.read_text(encoding="utf-8"))
assert authoritative["sprint"]["tickets"][ticket.id]["status"] == "completed"
assert authoritative["sprint"]["tickets"][ticket.id]["execution"]["state"] == "completed"


def test_ticket_round_trip_with_execution_fields(tmp_path):
pf = Planfile(str(tmp_path))
ticket = pf.create_ticket(
Expand Down
Loading