← Files Portable ResumeARCHIVED FILE
skills/.portable-resume/runtime/portable_resume/select.py
5.84 KB · Oct 3, 2026 · 06:34 UTC
"""Deterministic latest/ID/path/text selection with bounded ambiguity output."""
from __future__ import annotations
import os
from dataclasses import dataclass
from datetime import datetime
from typing import Iterable
from .bounds import DEFAULT_BOUNDS
from .diagnostics import DiagnosticError
from .model import Candidate, SessionSummary
from .paths import canonicalize_cwd, require_regular_no_symlinks, same_cwd
@dataclass(frozen=True, slots=True)
class SelectionResult:
selected: SessionSummary | None
candidates: tuple[Candidate, ...] = ()
def _timestamp_micros(value: str | None) -> int:
if value is None:
return 0
try:
return int(datetime.fromisoformat(value.replace("Z", "+00:00")).timestamp() * 1_000_000)
except (ValueError, OverflowError):
return 0
def summary_sort_key(summary: SessionSummary) -> tuple[bool, int, str, str, str]:
return (
summary.updated_at is None,
-_timestamp_micros(summary.updated_at),
summary.source,
summary.session_id,
summary.provider or "",
)
def candidate_sort_key(candidate: Candidate) -> tuple[bool, int, str, str]:
return (
candidate.updated_at is None,
-_timestamp_micros(candidate.updated_at),
candidate.source,
candidate.session_id,
)
def bounded_candidates(values: Iterable[SessionSummary]) -> tuple[Candidate, ...]:
candidates = sorted((value.candidate() for value in values), key=candidate_sort_key)
return tuple(candidates[: DEFAULT_BOUNDS.listed_sessions])
def summary_matches(summary: SessionSummary, needle_casefold: str) -> bool:
"""Casefolded substring match over list/show selection fields."""
fields = (
summary.session_id,
summary.title or "",
summary.cwd or "",
summary.branch or "",
)
return any(needle_casefold in field.casefold() for field in fields)
def select_session(
summaries: Iterable[SessionSummary],
*,
ref: str | None,
cwd: str | None,
approved_roots: Iterable[str] = (),
workspace_mode: str = "exact",
) -> SelectionResult:
"""Select one eligible summary or raise a stable no-match/ambiguous diagnostic."""
values = list(summaries)
if len(values) > DEFAULT_BOUNDS.scanned_records:
raise DiagnosticError.limit_exceeded()
# Sessions without a durable cwd stay eligible: the store cannot prove a
# workspace mismatch (OpenHands event files have no cwd field).
if workspace_mode in {"worktree", "repository"} and cwd is not None:
from .workspace import filter_by_workspace, resolve_workspace
identity = resolve_workspace(cwd, mode=workspace_mode)
eligible = [row for row, _reason in filter_by_workspace(values, identity)]
else:
eligible = [
value
for value in values
if cwd is None or value.cwd is None or same_cwd(value.cwd, cwd)
]
normalized_ref = "latest" if ref is None or not ref.strip() else ref.strip()
if normalized_ref == "latest":
ordered = sorted(eligible, key=summary_sort_key)
if not ordered:
raise DiagnosticError("E_NO_MATCH")
return SelectionResult(ordered[0])
# Prefer canonical UUID equality (uppercase paste still matches).
import uuid as _uuid
ref_uuid: str | None = None
try:
ref_uuid = str(_uuid.UUID(normalized_ref))
except ValueError:
ref_uuid = None
exact_id = [
value
for value in values
if value.session_id == normalized_ref
or (ref_uuid is not None and value.session_id == ref_uuid)
]
# Dedupe if both branches matched the same row.
if exact_id:
seen: set[str] = set()
unique: list[SessionSummary] = []
for value in exact_id:
key = f"{value.source}:{value.session_id}:{value.source_path or ''}"
if key in seen:
continue
seen.add(key)
unique.append(value)
exact_id = unique
if len(exact_id) == 1:
return SelectionResult(exact_id[0])
if len(exact_id) > 1:
candidates = bounded_candidates(exact_id)
raise AmbiguousSelection(candidates)
if os.path.isabs(normalized_ref):
roots = tuple(approved_roots)
if not roots:
raise DiagnosticError.unsafe_path()
canonical_match: str | None = None
for root in roots:
try:
safe, _ = require_regular_no_symlinks(normalized_ref, root)
except DiagnosticError:
continue
canonical_match = canonicalize_cwd(safe)
break
if canonical_match is None:
raise DiagnosticError.unsafe_path()
exact_path = [
value
for value in values
if value.source_path is not None and canonicalize_cwd(value.source_path) == canonical_match
]
if len(exact_path) == 1:
return SelectionResult(exact_path[0])
if len(exact_path) > 1:
raise AmbiguousSelection(bounded_candidates(exact_path))
raise DiagnosticError("E_NO_MATCH")
if len(normalized_ref) > DEFAULT_BOUNDS.ref_chars:
raise DiagnosticError.limit_exceeded()
needle = normalized_ref.casefold()
matches: list[SessionSummary] = []
for value in eligible:
if summary_matches(value, needle):
matches.append(value)
if not matches:
raise DiagnosticError("E_NO_MATCH")
if len(matches) > 1:
raise AmbiguousSelection(bounded_candidates(matches))
return SelectionResult(matches[0])
class AmbiguousSelection(DiagnosticError):
"""Ambiguity preserves only safe, closed candidate summaries for stdout."""
def __init__(self, candidates: tuple[Candidate, ...]):
super().__init__("E_AMBIGUOUS")
self.candidates = tuple(sorted(candidates, key=candidate_sort_key))[: DEFAULT_BOUNDS.listed_sessions]
SHA-256: 1aaef57f6110916b46aa6e5da52e0a5f25eb842699dcf7109571aa60459cf898