Skip to content

Task-context models

akms.task_context.models

Repository-agnostic models for deterministic task knowledge routes.

These models intentionally live outside :mod:akms.schema.models: task route indexes are retrieval inputs, not part of the frozen AKMS v2 node frontmatter or propagation schemas.

RouteRecord

Bases: BaseModel

One required-node route and its audit information.

canonical_sort_key

canonical_sort_key() -> tuple[str, str, str]

Return the stable ordering key used within one route.

Source code in packages/akms/src/akms/task_context/models.py
def canonical_sort_key(self) -> tuple[str, str, str]:
    """Return the stable ordering key used within one route."""

    provenance = json.dumps(
        self.provenance,
        allow_nan=False,
        ensure_ascii=False,
        separators=(",", ":"),
        sort_keys=True,
    )
    return self.node_id, self.reason, provenance

RouteValidationIssue

Bases: BaseModel

A machine-readable route validation failure.

RouteIndexValidationError

RouteIndexValidationError(
    issues: list[RouteValidationIssue]
    | tuple[RouteValidationIssue, ...],
)

Bases: ValueError

Raised with all route-index validation issues found in one pass.

Source code in packages/akms/src/akms/task_context/models.py
def __init__(
    self, issues: list[RouteValidationIssue] | tuple[RouteValidationIssue, ...]
):
    if not issues:
        raise ValueError("RouteIndexValidationError requires at least one issue")
    self.issues = tuple(issues)
    summary = "; ".join(
        f"{'.'.join(str(part) for part in issue.location)}: {issue.message}"
        for issue in self.issues
    )
    super().__init__(summary)

errors

errors() -> list[dict[str, Any]]

Return Pydantic-style serializable issue dictionaries.

Source code in packages/akms/src/akms/task_context/models.py
def errors(self) -> list[dict[str, Any]]:
    """Return Pydantic-style serializable issue dictionaries."""

    return [
        issue.model_dump(mode="json", exclude_none=True) for issue in self.issues
    ]

TaskRouteIndex

Bases: BaseModel

Canonical path/symbol routes to required AKMS nodes.

canonical_data

canonical_data() -> dict[str, Any]

Return the canonical JSON-compatible representation.

Source code in packages/akms/src/akms/task_context/models.py
def canonical_data(self) -> dict[str, Any]:
    """Return the canonical JSON-compatible representation."""

    return self.model_dump(mode="json")

canonical_json

canonical_json() -> str

Serialize deterministically for hashing, caching, and diffs.

Source code in packages/akms/src/akms/task_context/models.py
def canonical_json(self) -> str:
    """Serialize deterministically for hashing, caching, and diffs."""

    return json.dumps(
        self.canonical_data(),
        allow_nan=False,
        ensure_ascii=False,
        separators=(",", ":"),
        sort_keys=True,
    )

normalize_repository_path

normalize_repository_path(path: str) -> str

Return a canonical repository-relative POSIX path.

Both POSIX and Windows separators are accepted. Absolute paths, drive prefixes, parent traversal, NUL bytes, and paths that normalize to the repository root are rejected.

Source code in packages/akms/src/akms/task_context/models.py
def normalize_repository_path(path: str) -> str:
    """Return a canonical repository-relative POSIX path.

    Both POSIX and Windows separators are accepted. Absolute paths, drive
    prefixes, parent traversal, NUL bytes, and paths that normalize to the
    repository root are rejected.
    """

    if not isinstance(path, str):
        raise ValueError("Route path must be a string")

    candidate = path.strip()
    if not candidate:
        raise ValueError("Route path must not be empty")
    if "\x00" in candidate:
        raise ValueError("Route path must not contain NUL bytes")

    candidate = candidate.replace("\\", "/")
    if candidate.startswith("/") or _WINDOWS_DRIVE.match(candidate):
        raise ValueError("Route path must be repository-relative")

    parts: list[str] = []
    for part in candidate.split("/"):
        if part in {"", "."}:
            continue
        if part == "..":
            raise ValueError("Route path must not traverse outside the repository")
        parts.append(part)

    if not parts:
        raise ValueError("Route path must identify a repository entry")
    return "/".join(parts)