CtrlK
BlogDocsLog inGet started
Tessl Logo

jbaruch/speaker-toolkit

Six-skill presentation system: ingest talks into a rhetoric vault, run interactive clarification, generate a speaker profile, create presentations that match your documented patterns, produce the deck illustrations + thumbnail visual layer, and publish talk pages to a Jekyll shownotes site. Includes a 111-entry Presentation Patterns taxonomy (81 observable: 62 patterns + 19 antipatterns; 30 unobservable: 21 patterns + 9 antipatterns) for scoring, brainstorming, and go-live preparation.

75

Quality

94%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Low

Low-risk findings worth noting

Overview
Quality
Evals
Security
Files

migrate-tracking-database.pyskills/vault-ingress/scripts/

#!/usr/bin/env python3
"""Migrate one tracking database with a hash precondition and exact backup."""

from __future__ import annotations

import argparse
import hashlib
import json
from pathlib import Path
import sys
from typing import NoReturn

from persisted_pattern_observations import (
    apply_swapped_field_repairs,
    assess_persisted_pattern_observations,
)
from return_validation import ReturnValidationError, load_catalog
from tracking_database import (
    TrackingDatabaseError,
    migrate_tracking_database,
)
from tracking_database_io import (
    BackupRequest,
    TrackingDatabaseIOError,
    commit_tracking_database,
    decode_json_object,
    render_json_object,
    snapshot_tracking_database,
)


REPORT_SCHEMA_VERSION = 1


class TrackingDatabaseMigrationError(ValueError):
    """Migration input or filesystem state failed a precondition."""


class JsonArgumentParser(argparse.ArgumentParser):
    """Route command-line mistakes through the JSON error contract."""

    def error(self, message: str) -> NoReturn:
        raise TrackingDatabaseMigrationError(f"invalid arguments: {message}")


def _backup_path(path: Path, digest: str) -> Path:
    return path.parent / ".backups" / f"{path.name}.owner-migration-{digest}.bak"


def _validate_expected_digest(value: str) -> None:
    if len(value) != 64 or any(
        character not in "0123456789abcdef" for character in value
    ):
        raise TrackingDatabaseMigrationError(
            "--expected-sha256 must be a 64-character lowercase hexadecimal digest"
        )


def execute(
    path: Path,
    *,
    apply: bool,
    expected_sha256: str | None,
) -> dict[str, object]:
    database_path = path.expanduser().absolute()
    if expected_sha256 is not None:
        _validate_expected_digest(expected_sha256)
    if apply and expected_sha256 is None:
        raise TrackingDatabaseMigrationError(
            "--apply requires --expected-sha256 from a dry-run report"
        )

    try:
        snapshot = snapshot_tracking_database(database_path)
        database = decode_json_object(snapshot)
    except TrackingDatabaseIOError as exc:
        raise TrackingDatabaseMigrationError(str(exc)) from exc
    database_path = snapshot.path
    if expected_sha256 is not None and expected_sha256 != snapshot.sha256:
        raise TrackingDatabaseMigrationError(
            "input sha256 precondition failed: "
            f"expected {expected_sha256}, found {snapshot.sha256}"
        )

    try:
        migration = migrate_tracking_database(database)
        # Run on the migrated candidate, before it is rendered: the whole defect
        # is that migration stamps a talk current without reading the nested
        # detections, so the gate has to sit between the stamp and the write.
        observation_counts = gate_persisted_observations(migration.database)
        changed = migration.changed or any(observation_counts.values())
        rendered = render_json_object(migration.database) if changed else snapshot.raw
    except (TrackingDatabaseError, TrackingDatabaseIOError) as exc:
        raise TrackingDatabaseMigrationError(str(exc)) from exc
    except ReturnValidationError as exc:
        raise TrackingDatabaseMigrationError(
            f"cannot gate persisted observations: {exc}"
        ) from exc

    predicted_backup = _backup_path(database_path, snapshot.sha256) if changed else None
    output_sha256 = hashlib.sha256(rendered).hexdigest()
    database_written = False
    durability_state = "dry_run"
    warnings: list[str] = []
    reported_backup = str(predicted_backup) if predicted_backup is not None else None

    if apply:
        backup_request = (
            BackupRequest(
                path=predicted_backup,
                input_sha256=snapshot.sha256,
            )
            if predicted_backup is not None
            else None
        )
        try:
            result = commit_tracking_database(
                snapshot,
                rendered,
                backup=backup_request,
            )
        except TrackingDatabaseIOError as exc:
            raise TrackingDatabaseMigrationError(str(exc)) from exc
        output_sha256 = result.output_sha256
        database_written = result.installed
        durability_state = result.durability_state
        warnings = list(result.warnings)
        reported_backup = result.backup

    return {
        "schema_version": REPORT_SCHEMA_VERSION,
        "ok": True,
        "mode": "apply" if apply else "dry-run",
        "database": str(database_path),
        "input_sha256": snapshot.sha256,
        "from_schema_version": migration.from_schema_version,
        "to_schema_version": migration.to_schema_version,
        "changed": changed,
        "database_written": database_written,
        "backup": reported_backup,
        "output_sha256": output_sha256,
        "record_counts": dict(migration.record_counts),
        "persisted_observations": dict(observation_counts),
        "durability_state": durability_state,
        "warnings": warnings,
    }


# A talk in one of these states claims its analysis is complete, which is the
# claim a corrupt observation block contradicts. Anything earlier has nothing to
# stamp.
COMPLETED_STATUSES = frozenset({"processed", "processed_partial"})
REPAIRED_REASON = "persisted_observation_repaired"
REQUEUE_REASON = "persisted_observation_invalid"


def gate_persisted_observations(database: dict) -> dict[str, int]:
    """Repair what is losslessly repairable; requeue the rest. Never stamp both.

    #147 migration stamps a talk as current record schema without ever reading
    the nested detection objects, so a block with `evidence` and `dimensions`
    swapped, an unknown pattern id, or a missing dimensions array became
    "current" on the strength of its container's shape.

    Two outcomes, and no third. An exact inverse-schema swap is undone in place,
    because both original values live in the repair record and putting them back
    is reversible. Everything else keeps its original bytes and goes back on the
    queue: a defect this function cannot undo without inventing a value is a
    defect an owner has to look at, and rewriting it here would destroy the
    evidence of what went wrong.
    """
    counts = {"repaired": 0, "requeued": 0}
    talks = database.get("talks")
    if not isinstance(talks, list):
        return counts
    catalog = load_catalog()
    for index, talk in enumerate(talks):
        if not isinstance(talk, dict):
            continue
        if talk.get("status") not in COMPLETED_STATUSES:
            continue
        if talk.get("pattern_observations") is None:
            # Absence is incompleteness, not corruption — the same boundary
            # preflight draws. Requeueing every talk that predates pattern
            # scoring would flood a queue that is working.
            continue
        assessment = assess_persisted_pattern_observations(talk, catalog)
        if assessment.usable:
            continue
        if assessment.repairs:
            # Re-assess rather than assume. A talk can carry a repairable swap
            # AND an unrelated defect, and the repair fixes only the swap — so
            # the repair counts only when the block it produces is one this gate
            # would have let through on its own.
            repaired = apply_swapped_field_repairs(talk, assessment.repairs)
            if assess_persisted_pattern_observations(repaired, catalog).usable:
                talks[index] = repaired
                counts["repaired"] += 1
                continue
        talk["status"] = "needs-reprocessing"
        talk["reprocess_reason"] = REQUEUE_REASON
        counts["requeued"] += 1
    return counts


def main(argv: list[str] | None = None) -> int:
    parser = JsonArgumentParser(description=__doc__)
    parser.add_argument("database", type=Path)
    parser.add_argument("--apply", action="store_true")
    parser.add_argument("--expected-sha256")
    try:
        args = parser.parse_args(argv)
        report = execute(
            args.database,
            apply=args.apply,
            expected_sha256=args.expected_sha256,
        )
    except TrackingDatabaseMigrationError as exc:
        print(
            json.dumps(
                {
                    "schema_version": REPORT_SCHEMA_VERSION,
                    "ok": False,
                    "error": str(exc),
                }
            )
        )
        print(f"tracking-database migration failed: {exc}", file=sys.stderr)
        return 2
    print(json.dumps(report, indent=2, sort_keys=True))
    return 0


if __name__ == "__main__":
    sys.exit(main())

skills

vault-ingress

SKILL.md

README.md

tile.json