#!/usr/bin/env python3
"""Run a networkless PostgreSQL least-privilege lifecycle for Leo.

The canary provisions the real guarded proposal/apply prerequisites and the
real Observatory read role in a disposable PostgreSQL container. It then
connects as the read principal, proves required retrieval, attempts forbidden
database and role operations, compares catalog/data fingerprints, and verifies
that the container and temporary work directory were removed.
"""

from __future__ import annotations

import argparse
import hashlib
import json
import os
import secrets
import shutil
import subprocess
import tempfile
import time
import uuid
from datetime import datetime, timezone
from pathlib import Path
from typing import Any

ROOT = Path(__file__).resolve().parents[1]
APPLY_PREREQS_SQL = ROOT / "scripts" / "kb_apply_prereqs.sql"
READ_ROLE_SQL = ROOT / "ops" / "observatory_read_role.sql"
DATABASE = "teleo_canonical"
IAM_USER = "sa-observatory-read-adapter@teleo-501523.iam"
PARTICIPANT_HANDLE = "m3taversal"
DEFAULT_IMAGE = os.environ.get("LEOCLEAN_RUNTIME_POSTGRES_IMAGE", "postgres:16-alpine")
CANARY_LABEL = "leo-negative-permissions"

BOOTSTRAP_SQL = r"""
create role kb_gate_owner nologin inherit;
create role kb_review login inherit;
create role kb_apply login inherit;

create schema kb_stage;

create table public.agents (
  id uuid primary key,
  handle text not null unique,
  kind text not null
);
create table public.strategies (
  id uuid primary key,
  agent_id uuid not null,
  active boolean not null default true
);
create table public.strategy_nodes (id uuid primary key);
create table public.claim_evidence (
  id uuid primary key,
  claim_id uuid not null,
  source_id uuid not null,
  role text not null
);
create table public.claim_edges (id uuid primary key);
create table public.claims (
  id uuid primary key,
  text text not null
);
create table public.sources (
  id uuid primary key,
  hash text not null
);
create table public.reasoning_tools (id uuid primary key);
create table public.private_notes (
  id uuid primary key,
  note text not null
);

insert into public.agents (id, handle, kind)
values ('11111111-1111-1111-1111-111111111111', '__PARTICIPANT_HANDLE__', 'human');

create table kb_stage.kb_proposals (
  id uuid primary key,
  proposal_type text not null,
  status text not null,
  payload jsonb not null,
  reviewed_by_handle text,
  reviewed_by_agent_id uuid,
  reviewed_at timestamptz,
  review_note text,
  applied_by_handle text,
  applied_by_agent_id uuid,
  applied_at timestamptz,
  updated_at timestamptz not null default now()
);
create table kb_stage.kb_review_principals (
  db_role name primary key,
  reviewed_by_handle text not null,
  reviewed_by_agent_id uuid not null references public.agents(id),
  active boolean not null default true,
  created_at timestamptz not null default now()
);
create table kb_stage.kb_proposal_approvals (
  proposal_id uuid primary key references kb_stage.kb_proposals(id),
  proposal_type text not null,
  payload jsonb not null,
  reviewed_by_handle text not null,
  reviewed_by_agent_id uuid not null references public.agents(id),
  reviewed_by_db_role name not null,
  reviewed_at timestamptz not null,
  review_note text not null
);

insert into public.claims (id, text)
values (
  '22222222-2222-2222-2222-222222222222',
  'Canonical Leo knowledge remains review-gated.'
);
insert into public.sources (id, hash)
values ('33333333-3333-3333-3333-333333333333', 'fixture-source-hash');
insert into public.private_notes (id, note)
values ('44444444-4444-4444-4444-444444444444', 'not allowlisted');
insert into kb_stage.kb_proposals (
  id, proposal_type, status, payload
) values (
  '55555555-5555-5555-5555-555555555555',
  'revise_strategy',
  'pending_review',
  '{"apply_payload":{"agent_id":"11111111-1111-1111-1111-111111111111"}}'
);
"""


class CanaryError(RuntimeError):
    """A setup or verification step failed."""


def utc_now() -> str:
    return datetime.now(timezone.utc).isoformat()


def canonical_sha256(value: Any) -> str:
    encoded = json.dumps(value, sort_keys=True, separators=(",", ":")).encode()
    return hashlib.sha256(encoded).hexdigest()


def sql_literal(value: str) -> str:
    return "'" + value.replace("'", "''") + "'"


def sql_identifier(value: str) -> str:
    return '"' + value.replace('"', '""') + '"'


def run(
    command: list[str],
    *,
    input_text: str | None = None,
    check: bool = True,
    timeout: int = 60,
) -> subprocess.CompletedProcess[str]:
    completed = subprocess.run(
        command,
        input=input_text,
        text=True,
        capture_output=True,
        check=False,
        timeout=timeout,
    )
    if check and completed.returncode != 0:
        stderr = completed.stderr.strip()[-2000:]
        raise CanaryError(f"command failed with exit {completed.returncode}: {stderr}")
    return completed


def admin_psql(container: str, sql: str, *, exec_env: dict[str, str] | None = None) -> str:
    command = ["docker", "exec"]
    for name, value in sorted((exec_env or {}).items()):
        command.extend(["-e", f"{name}={value}"])
    command.extend(
        [
            "-i",
            container,
            "psql",
            "-X",
            "--set",
            "ON_ERROR_STOP=1",
            "-U",
            "postgres",
            "-d",
            DATABASE,
            "-At",
        ]
    )
    return run(command, input_text=sql).stdout


def user_psql(
    container: str,
    password: str,
    sql_commands: list[str],
) -> subprocess.CompletedProcess[str]:
    command = [
        "docker",
        "exec",
        "-e",
        f"PGPASSWORD={password}",
        container,
        "psql",
        "-X",
        "--set",
        "ON_ERROR_STOP=1",
        "--set",
        "VERBOSITY=verbose",
        "-h",
        "127.0.0.1",
        "-U",
        IAM_USER,
        "-d",
        DATABASE,
        "-At",
    ]
    for sql in sql_commands:
        command.extend(["-c", sql])
    return run(command, check=False)


def parse_last_json(output: str) -> dict[str, Any]:
    for line in reversed(output.splitlines()):
        line = line.strip()
        if not line.startswith("{"):
            continue
        parsed = json.loads(line)
        if isinstance(parsed, dict):
            return parsed
    raise CanaryError("expected a JSON object in psql output")


def sanitized_denial(completed: subprocess.CompletedProcess[str]) -> str:
    lines = [line.strip() for line in completed.stderr.splitlines() if line.strip()]
    for line in lines:
        if "ERROR:" in line or "FATAL:" in line:
            return line[:500]
    return (lines[-1] if lines else "no database denial was returned")[:500]


def allowed_operation(
    operation_id: str,
    category: str,
    completed: subprocess.CompletedProcess[str],
) -> dict[str, Any]:
    return {
        "id": operation_id,
        "category": category,
        "expected": "allowed",
        "passed": completed.returncode == 0,
        "exit_code": completed.returncode,
        "readback": [line for line in completed.stdout.splitlines() if line],
        "error": sanitized_denial(completed) if completed.returncode != 0 else None,
    }


def denied_operation(
    operation_id: str,
    category: str,
    completed: subprocess.CompletedProcess[str],
    expected_fragments: tuple[str, ...],
) -> dict[str, Any]:
    denial = sanitized_denial(completed)
    lowered = denial.lower()
    matched = next((fragment for fragment in expected_fragments if fragment.lower() in lowered), None)
    return {
        "id": operation_id,
        "category": category,
        "expected": "denied",
        "passed": completed.returncode != 0 and matched is not None,
        "exit_code": completed.returncode,
        "matched_denial": matched,
        "denial": denial,
    }


def fingerprint(container: str) -> dict[str, Any]:
    principal = sql_literal(IAM_USER)
    query = f"""
select jsonb_build_object(
  'database', current_database(),
  'participant_handle', (
    select handle
      from public.agents
     where id = '11111111-1111-1111-1111-111111111111'
  ),
  'principal', (
    select jsonb_build_object(
      'rolname', rolname,
      'rolinherit', rolinherit,
      'rolsuper', rolsuper,
      'rolcreatedb', rolcreatedb,
      'rolcreaterole', rolcreaterole,
      'rolreplication', rolreplication,
      'rolbypassrls', rolbypassrls
    )
      from pg_catalog.pg_roles
     where rolname = {principal}
  ),
  'memberships', coalesce((
    select jsonb_agg(
             jsonb_build_object(
               'role', granted.rolname,
               'admin_option', membership.admin_option
             ) order by granted.rolname
           )
      from pg_catalog.pg_auth_members membership
      join pg_catalog.pg_roles granted on granted.oid = membership.roleid
      join pg_catalog.pg_roles member on member.oid = membership.member
     where member.rolname = {principal}
  ), '[]'::jsonb),
  'role_database_settings', coalesce((
    select jsonb_agg(setting order by setting)
      from pg_catalog.pg_db_role_setting role_setting
      cross join lateral unnest(role_setting.setconfig) setting
     where role_setting.setrole = (select oid from pg_catalog.pg_roles where rolname = {principal})
       and role_setting.setdatabase = (select oid from pg_catalog.pg_database where datname = current_database())
  ), '[]'::jsonb),
  'effective_privileges', jsonb_build_object(
    'claims_select', pg_catalog.has_table_privilege({principal}, 'public.claims', 'SELECT'),
    'claims_insert', pg_catalog.has_table_privilege({principal}, 'public.claims', 'INSERT'),
    'proposals_select', pg_catalog.has_table_privilege({principal}, 'kb_stage.kb_proposals', 'SELECT'),
    'proposals_insert', pg_catalog.has_table_privilege({principal}, 'kb_stage.kb_proposals', 'INSERT'),
    'private_notes_select', pg_catalog.has_table_privilege({principal}, 'public.private_notes', 'SELECT'),
    'public_schema_create', pg_catalog.has_schema_privilege({principal}, 'public', 'CREATE'),
    'kb_stage_schema_create', pg_catalog.has_schema_privilege({principal}, 'kb_stage', 'CREATE'),
    'approve_execute', pg_catalog.has_function_privilege(
      {principal},
      'kb_stage.approve_strict_proposal(uuid,text,jsonb,text,text)',
      'EXECUTE'
    ),
    'assert_execute', pg_catalog.has_function_privilege(
      {principal},
      'kb_stage.assert_approved_proposal(uuid,text,jsonb,text,uuid,timestamptz,text)',
      'EXECUTE'
    ),
    'finish_execute', pg_catalog.has_function_privilege(
      {principal},
      'kb_stage.finish_approved_proposal(uuid,text,jsonb,text,uuid,timestamptz,text,text)',
      'EXECUTE'
    )
  ),
  'row_counts', jsonb_build_object(
    'public.claims', (select count(*) from public.claims),
    'public.sources', (select count(*) from public.sources),
    'public.claim_evidence', (select count(*) from public.claim_evidence),
    'public.claim_edges', (select count(*) from public.claim_edges),
    'kb_stage.kb_proposals', (select count(*) from kb_stage.kb_proposals)
  )
)::text;
"""
    return parse_last_json(admin_psql(container, query))


def wait_until_ready(container: str) -> None:
    ready_streak = 0
    deadline = time.monotonic() + 30
    while time.monotonic() < deadline:
        ready = run(
            ["docker", "exec", container, "pg_isready", "-U", "postgres", "-d", DATABASE],
            check=False,
        )
        if ready.returncode == 0:
            ready_streak += 1
            if ready_streak >= 2:
                return
        else:
            ready_streak = 0
        time.sleep(0.25)
    raise CanaryError("disposable PostgreSQL did not become stably ready")


def operation_suite(container: str, password: str) -> list[dict[str, Any]]:
    read_only_off = "set default_transaction_read_only = off"
    fake_payload = "'{\"apply_payload\":{}}'::jsonb"
    proposal_id = "'55555555-5555-5555-5555-555555555555'::uuid"
    reviewer_id = "'11111111-1111-1111-1111-111111111111'::uuid"
    reviewer_handle = sql_literal(PARTICIPANT_HANDLE)
    operations = [
        allowed_operation(
            "session_read_only_defaults",
            "session",
            user_psql(
                container,
                password,
                [
                    "select jsonb_build_object("
                    "'default_transaction_read_only', current_setting('default_transaction_read_only'), "
                    "'transaction_read_only', current_setting('transaction_read_only'))::text"
                ],
            ),
        ),
        allowed_operation(
            "canonical_retrieval",
            "retrieval",
            user_psql(
                container,
                password,
                [
                    "select jsonb_build_object('claim_count', count(*), 'sample_text', min(text))::text "
                    "from public.claims"
                ],
            ),
        ),
        allowed_operation(
            "proposal_ledger_retrieval",
            "retrieval",
            user_psql(
                container,
                password,
                [
                    "select jsonb_build_object('proposal_count', count(*), 'statuses', "
                    "jsonb_agg(status order by status))::text from kb_stage.kb_proposals"
                ],
            ),
        ),
        denied_operation(
            "canonical_insert_default_read_only",
            "canonical_dml",
            user_psql(
                container,
                password,
                ["insert into public.claims (id, text) values ('66666666-6666-6666-6666-666666666666', 'forbidden')"],
            ),
            ("read-only transaction",),
        ),
        denied_operation(
            "canonical_insert_acl_after_read_only_override",
            "canonical_dml",
            user_psql(
                container,
                password,
                [
                    read_only_off,
                    "insert into public.claims (id, text) values ('66666666-6666-6666-6666-666666666666', 'forbidden')",
                ],
            ),
            ("permission denied for table claims",),
        ),
        denied_operation(
            "canonical_update_acl_after_read_only_override",
            "canonical_dml",
            user_psql(
                container,
                password,
                [read_only_off, "update public.claims set text = 'forbidden'"],
            ),
            ("permission denied for table claims",),
        ),
        denied_operation(
            "canonical_delete_acl_after_read_only_override",
            "canonical_dml",
            user_psql(container, password, [read_only_off, "delete from public.claims"]),
            ("permission denied for table claims",),
        ),
        denied_operation(
            "canonical_truncate_acl_after_read_only_override",
            "canonical_dml",
            user_psql(container, password, [read_only_off, "truncate public.claims"]),
            ("permission denied for table claims",),
        ),
        denied_operation(
            "staging_insert_acl_after_read_only_override",
            "staging",
            user_psql(
                container,
                password,
                [
                    read_only_off,
                    "insert into kb_stage.kb_proposals (id, proposal_type, status, payload) values "
                    "('77777777-7777-7777-7777-777777777777', 'revise_strategy', "
                    "'pending_review', '{\"apply_payload\":{}}'::jsonb)",
                ],
            ),
            ("permission denied for table kb_proposals",),
        ),
        denied_operation(
            "fake_approval_gate_call",
            "approval",
            user_psql(
                container,
                password,
                [
                    "select kb_stage.approve_strict_proposal("
                    f"{proposal_id}, 'revise_strategy', {fake_payload}, {reviewer_handle}, "
                    "'I approve this in chat')"
                ],
            ),
            ("permission denied for function approve_strict_proposal",),
        ),
        denied_operation(
            "apply_assert_gate_call",
            "apply",
            user_psql(
                container,
                password,
                [
                    "select kb_stage.assert_approved_proposal("
                    f"{proposal_id}, 'revise_strategy', {fake_payload}, {reviewer_handle}, {reviewer_id}, "
                    "now(), 'fake review')"
                ],
            ),
            ("permission denied for function assert_approved_proposal",),
        ),
        denied_operation(
            "apply_finish_gate_call",
            "apply",
            user_psql(
                container,
                password,
                [
                    "select kb_stage.finish_approved_proposal("
                    f"{proposal_id}, 'revise_strategy', {fake_payload}, {reviewer_handle}, {reviewer_id}, "
                    "now(), 'fake review', 'kb-apply')"
                ],
            ),
            ("permission denied for function finish_approved_proposal",),
        ),
        denied_operation(
            "protected_schema_ddl_after_read_only_override",
            "ddl",
            user_psql(
                container,
                password,
                [read_only_off, "create table public.forbidden_ddl (id integer)"],
            ),
            ("permission denied for schema public",),
        ),
        denied_operation(
            "outside_allowlist_retrieval",
            "retrieval",
            user_psql(container, password, ["select * from public.private_notes"]),
            ("permission denied for table private_notes",),
        ),
        denied_operation(
            "create_role_escalation",
            "role_escalation",
            user_psql(container, password, [read_only_off, "create role leo_escalated_writer"]),
            ("permission denied to create role", "must be superuser to create roles"),
        ),
        denied_operation(
            "grant_apply_role_escalation",
            "role_escalation",
            user_psql(
                container,
                password,
                [read_only_off, f"grant kb_apply to {sql_identifier(IAM_USER)}"],
            ),
            ("permission denied to grant role", "must have admin option on role"),
        ),
        denied_operation(
            "set_apply_role_escalation",
            "role_escalation",
            user_psql(container, password, ["set role kb_apply"]),
            ("permission denied to set role",),
        ),
        denied_operation(
            "alter_role_createdb_escalation",
            "role_escalation",
            user_psql(
                container,
                password,
                [read_only_off, f"alter role {sql_identifier(IAM_USER)} createdb"],
            ),
            ("permission denied to alter role", "must be superuser to alter"),
        ),
        denied_operation(
            "claim_table_ownership_escalation",
            "role_escalation",
            user_psql(
                container,
                password,
                [read_only_off, f"alter table public.claims owner to {sql_identifier(IAM_USER)}"],
            ),
            ("must be owner of table claims",),
        ),
    ]
    return operations


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--output", type=Path, required=True, help="Receipt JSON destination")
    parser.add_argument("--image", default=DEFAULT_IMAGE, help="Disposable PostgreSQL image")
    return parser.parse_args()


def main() -> int:
    args = parse_args()
    if shutil.which("docker") is None:
        raise SystemExit("Docker is required for the negative-permissions canary")

    run_id = uuid.uuid4().hex[:12]
    container = f"leo-negative-permissions-{run_id}"
    temp_dir = Path(tempfile.mkdtemp(prefix=f"{container}-"))
    password = secrets.token_urlsafe(24)
    started_at = utc_now()
    container_started = False
    execution_passed = False
    receipt: dict[str, Any] = {
        "artifact": "leo_negative_permissions_canary",
        "schema": "livingip.leo-negative-permissions.v1",
        "run_id": run_id,
        "started_at_utc": started_at,
        "required_tier": "T2_runtime",
        "current_tier": "T0_spec",
        "scope": {
            "database": DATABASE,
            "principal": IAM_USER,
            "container_network": "none",
            "production_contacted": False,
            "canonical_production_rows_contacted": False,
        },
        "identity_policy": {
            "participant_handle": PARTICIPANT_HANDLE,
            "reviewer_handle": PARTICIPANT_HANDLE,
            "exact_handle_required": True,
        },
        "operations": [],
        "passed": False,
    }

    try:
        repo_sha = run(["git", "rev-parse", "HEAD"], timeout=15).stdout.strip()
        docker_server = run(["docker", "version", "--format", "{{.Server.Version}}"], timeout=15).stdout.strip()
        image_id = run(
            ["docker", "image", "inspect", "--format", "{{.Id}}", args.image],
            timeout=15,
        ).stdout.strip()
        receipt["runtime"] = {
            "repo_git_sha": repo_sha,
            "postgres_image": args.image,
            "postgres_image_id": image_id,
            "docker_server_version": docker_server,
        }

        run(
            [
                "docker",
                "run",
                "--detach",
                "--rm",
                "--name",
                container,
                "--network",
                "none",
                "--label",
                f"livingip.canary={CANARY_LABEL}",
                "--label",
                f"livingip.canary.instance={container}",
                "--tmpfs",
                "/var/lib/postgresql/data:rw,nosuid,nodev,size=512m",
                "--env",
                f"POSTGRES_PASSWORD={secrets.token_urlsafe(24)}",
                "--env",
                f"POSTGRES_DB={DATABASE}",
                args.image,
            ],
            timeout=30,
        )
        container_started = True
        wait_until_ready(container)

        inspect = run(
            [
                "docker",
                "inspect",
                "--format",
                "{{json .HostConfig.NetworkMode}}|{{json .Mounts}}|"
                '{{index .Config.Labels "livingip.canary"}}|'
                '{{index .Config.Labels "livingip.canary.instance"}}',
                container,
            ],
        ).stdout.strip()
        network_mode, mounts, canary_label, instance_label = inspect.split("|", 3)
        isolation = {
            "network_mode": json.loads(network_mode),
            "persistent_mounts": json.loads(mounts),
            "canary_label": canary_label,
            "instance_label": instance_label,
            "temporary_data_mount": "tmpfs:/var/lib/postgresql/data",
        }
        receipt["isolation"] = isolation
        if isolation != {
            "network_mode": "none",
            "persistent_mounts": [],
            "canary_label": CANARY_LABEL,
            "instance_label": container,
            "temporary_data_mount": "tmpfs:/var/lib/postgresql/data",
        }:
            raise CanaryError(f"disposable container isolation mismatch: {isolation}")

        admin_psql(container, BOOTSTRAP_SQL.replace("__PARTICIPANT_HANDLE__", PARTICIPANT_HANDLE))
        admin_psql(container, APPLY_PREREQS_SQL.read_text(encoding="utf-8"))
        admin_psql(
            container,
            f"create role {sql_identifier(IAM_USER)} login inherit password {sql_literal(password)};",
        )
        migration_output = admin_psql(
            container,
            READ_ROLE_SQL.read_text(encoding="utf-8"),
            exec_env={"OBSERVATORY_DB_IAM_USER": IAM_USER},
        )
        receipt["role_provisioning"] = parse_last_json(migration_output)

        before = fingerprint(container)
        operations = operation_suite(container, password)
        after = fingerprint(container)
        receipt["operations"] = operations
        receipt["fingerprint"] = {
            "before": before,
            "after": after,
            "before_sha256": canonical_sha256(before),
            "after_sha256": canonical_sha256(after),
            "unchanged": before == after,
        }
        receipt["summary"] = {
            "allowed_expected": sum(operation["expected"] == "allowed" for operation in operations),
            "allowed_passed": sum(
                operation["expected"] == "allowed" and operation["passed"] for operation in operations
            ),
            "denied_expected": sum(operation["expected"] == "denied" for operation in operations),
            "denied_passed": sum(operation["expected"] == "denied" and operation["passed"] for operation in operations),
        }
        execution_passed = all(operation["passed"] for operation in operations) and before == after
    except Exception as exc:
        receipt["execution_error"] = str(exc)[:2000]
    finally:
        remove = run(["docker", "rm", "--force", container], check=False, timeout=30)
        if temp_dir.exists():
            shutil.rmtree(temp_dir)
        exact_name = run(
            [
                "docker",
                "container",
                "ls",
                "--all",
                "--filter",
                f"name=^/{container}$",
                "--format",
                "{{.Names}}",
            ],
            check=False,
            timeout=15,
        )
        label_scope = run(
            [
                "docker",
                "container",
                "ls",
                "--all",
                "--filter",
                f"label=livingip.canary={CANARY_LABEL}",
                "--filter",
                f"label=livingip.canary.instance={container}",
                "--format",
                "{{.Names}}",
            ],
            check=False,
            timeout=15,
        )
        cleanup = {
            "container_started": container_started,
            "remove_exit_code": remove.returncode,
            "exact_name_readback_exit_code": exact_name.returncode,
            "exact_name_orphans": [line for line in exact_name.stdout.splitlines() if line],
            "label_readback_exit_code": label_scope.returncode,
            "label_scoped_orphans": [line for line in label_scope.stdout.splitlines() if line],
            "temporary_workdir_exists": temp_dir.exists(),
        }
        cleanup["passed"] = (
            (not container_started or remove.returncode == 0)
            and exact_name.returncode == 0
            and not cleanup["exact_name_orphans"]
            and label_scope.returncode == 0
            and not cleanup["label_scoped_orphans"]
            and not cleanup["temporary_workdir_exists"]
        )
        receipt["cleanup"] = cleanup
        receipt["finished_at_utc"] = utc_now()
        receipt["current_tier"] = "T2_runtime" if execution_passed and cleanup["passed"] else "T0_spec"
        receipt["passed"] = execution_passed and cleanup["passed"]

    args.output.parent.mkdir(parents=True, exist_ok=True)
    temporary_output = args.output.with_name(f".{args.output.name}.{run_id}.tmp")
    temporary_output.write_text(json.dumps(receipt, indent=2, sort_keys=True) + "\n", encoding="utf-8")
    os.replace(temporary_output, args.output)
    print(
        json.dumps(
            {
                "artifact": receipt["artifact"],
                "passed": receipt["passed"],
                "current_tier": receipt["current_tier"],
                "operations": receipt.get("summary"),
                "cleanup": receipt["cleanup"],
                "output": str(args.output),
            },
            sort_keys=True,
        )
    )
    return 0 if receipt["passed"] else 1


if __name__ == "__main__":
    raise SystemExit(main())
