Files

296 lines
11 KiB
Python
Executable File

#!/usr/bin/env python3
import argparse
import csv
from pathlib import Path
FRAME_APP_ID = "4165890"
FRAME_PACKAGE_IDS = {"1629484", "1629486"}
FRAME_KOMODO_BASELINE = "2026-05-27T16:53:46"
FRAME_STEAMDB_BASELINE_UPDATE = "5 May 2026 - 18:50:54 UTC"
MAX_LINE_LEN = 220
def read_text(path: Path):
if not path.exists():
return ""
return path.read_text(encoding="utf-8", errors="ignore")
def read_lines(path: Path):
return [line.rstrip("\n") for line in read_text(path).splitlines() if line.strip()]
def read_tsv(path: Path):
if not path.exists():
return []
with path.open("r", encoding="utf-8", errors="ignore", newline="") as handle:
return list(csv.DictReader(handle, delimiter="\t"))
def read_komodo_product_modified(path: Path):
rows = []
for line in read_lines(path):
parts = line.split("\t")
if parts[:2] == ["product", "modified"]:
continue
if len(parts) >= 2:
rows.append({"product": parts[0], "modified": parts[1]})
return rows
def concise(value: str):
compact = " ".join((value or "").split())
if len(compact) > MAX_LINE_LEN:
return compact[: MAX_LINE_LEN - 3] + "..."
return compact
def bullet_table_row(row, columns):
values = []
for column in columns:
value = concise(row.get(column, ""))
if value:
values.append(f"{column}={value}")
return "; ".join(values)
def frame_rows(rows, id_key="id"):
return [
row
for row in rows
if row.get("product") == "Steam Frame"
or row.get(id_key) == FRAME_APP_ID
or row.get(id_key) in FRAME_PACKAGE_IDS
or row.get("package_id") in FRAME_PACKAGE_IDS
]
def machine_rows(rows, id_key="id"):
return [row for row in rows if row.get("product") == "Steam Machine"]
def valve_frame_ready(rows):
for row in frame_rows(rows):
row_type = row.get("type")
row_id = row.get("id")
status = row.get("status")
details = row.get("details", "")
if row_type == "package" and row_id in FRAME_PACKAGE_IDS and status == "public":
return True
if row_type == "app" and row_id == FRAME_APP_ID:
has_packages = "packages=" in details and "packages=;" not in details
has_package_groups = "package_groups=0" not in details and "package_groups=" in details
has_price = "price=" in details and not details.endswith("price=")
if has_packages or has_package_groups or has_price:
return True
return False
def steamkit_frame_moved(rows):
for row in frame_rows(rows):
if row.get("changed_since_previous") == "yes":
return True
if row.get("type") == "package" and row.get("id") in FRAME_PACKAGE_IDS and row.get("status") == "available":
return True
return False
def steamdb_frame_moved(rows):
for row in frame_rows(rows, id_key="package_id"):
if row.get("status") and row.get("status") != "private_exists_only":
return True
if row.get("last_record_update") and row.get("last_record_update") != FRAME_STEAMDB_BASELINE_UPDATE:
return True
return False
def komodo_frame_moved(rows):
for row in rows:
if row.get("product") == "frame" and row.get("modified") and row.get("modified") != FRAME_KOMODO_BASELINE:
return True
return False
def key_lines(path: Path, terms, limit):
selected = []
for line in read_lines(path):
lowered = line.lower()
if any(term in lowered for term in terms):
selected.append(concise(line))
if len(selected) >= limit:
break
return selected
def customs_vr_lines(path: Path, limit):
selected = []
for line in read_lines(path):
lowered = line.lower()
if line.startswith("|") and any(term in lowered for term in ("virtual reality", "vr", "headset", "xr")):
selected.append(concise(line))
if len(selected) >= limit:
break
return selected
def latest_compare_file(reports: Path):
compare_files = sorted(reports.glob("compare-vs-*.md"))
if not compare_files:
return None
return compare_files[-1]
def build(run_dir: Path):
reports = run_dir / "reports"
valve_rows = read_tsv(reports / "valve-reservation-packages.tsv")
steamkit_rows = read_tsv(reports / "steamkit-pics-packages.tsv")
steamdb_rows = read_tsv(reports / "steamdb-reservation-packages.tsv")
komodo_rows = read_komodo_product_modified(reports / "komodo-product-modified.tsv")
valve_ready = valve_frame_ready(valve_rows)
steamkit_moved = steamkit_frame_moved(steamkit_rows)
steamdb_moved = steamdb_frame_moved(steamdb_rows)
komodo_moved = komodo_frame_moved(komodo_rows)
if valve_ready:
verdict = "Steam Frame public readiness detected."
reason = "Valve store/package data now exposes Frame package, package group, price, or purchase/reservation readiness."
elif steamkit_moved or steamdb_moved:
verdict = "Steam Frame backend movement detected, but public store readiness is not confirmed."
reason = "Frame app/package metadata moved in SteamKit/PICS or SteamDB, while Valve public endpoints still need confirmation."
elif komodo_moved:
verdict = "Steam Frame site staging detected, but store/package readiness is not confirmed."
reason = "Komodo Frame records moved without matching Valve package readiness."
else:
verdict = "No public Steam Frame readiness change detected."
reason = "Frame app/package, public Valve endpoint, SteamDB, and Komodo checks do not show a launch-readiness move."
lines = [
f"# Steam Frame Focus: {run_dir.name}",
"",
"## Verdict",
"",
f"- {verdict}",
f"- {reason}",
"",
"## Frame Critical Signals",
"",
f"- Frame app: `{FRAME_APP_ID}`",
"- Frame packages: `1629484`, `1629486`",
"",
"### Valve Store / Package API",
]
frame_valve_rows = frame_rows(valve_rows)
if frame_valve_rows:
for row in frame_valve_rows:
lines.append(
f"- `{bullet_table_row(row, ('type', 'product', 'id', 'status', 'name', 'release_or_price', 'details'))}`"
)
else:
lines.append("- No Frame rows found in `valve-reservation-packages.tsv`.")
lines.extend(["", "### SteamKit / PICS"])
frame_steamkit_rows = frame_rows(steamkit_rows)
if frame_steamkit_rows:
for row in frame_steamkit_rows:
lines.append(
f"- `{bullet_table_row(row, ('type', 'product', 'id', 'status', 'changenumber', 'previous_changenumber', 'changed_since_previous', 'name', 'details'))}`"
)
else:
lines.append("- No Frame rows found in `steamkit-pics-packages.tsv`.")
lines.extend(["", "### SteamDB"])
frame_steamdb_rows = frame_rows(steamdb_rows, id_key="package_id")
if frame_steamdb_rows:
for row in frame_steamdb_rows:
lines.append(
f"- `{bullet_table_row(row, ('product', 'package_id', 'last_record_update', 'last_changenumber', 'possible_apps', 'status'))}`"
)
else:
lines.append("- No Frame rows found in `steamdb-reservation-packages.tsv`.")
lines.extend(["", "### Komodo"])
frame_komodo = [row for row in komodo_rows if row.get("product") == "frame"]
if frame_komodo:
for row in frame_komodo:
lines.append(f"- `{bullet_table_row(row, ('product', 'modified'))}`")
else:
lines.append("- No Frame product timestamp row found.")
for source in ("komodo-frame-sections.tsv", "komodo-frame-media.tsv"):
path = reports / source
count = len(read_lines(path))
lines.append(f"- `{source}` rows: `{count}`")
tracking_terms = ("frame", "deckard", "roy", "xr", "vr", "wireless adapter", "steam link")
tracking_lines = key_lines(reports / "steamtracking-frame-signals-key-lines.txt", tracking_terms, 10)
lines.extend(["", "### SteamTracking / GameTracking"])
if tracking_lines:
lines.extend([f"- `{line}`" for line in tracking_lines])
else:
lines.append("- No Frame-focused SteamTracking key lines found.")
vr_terms = ("steam frame", "deckard", "roy", "wireless adapter", "steam link vr", "arm64", "linuxarm64")
steamvr_lines = key_lines(reports / "steamvr-depots-key-lines.txt", vr_terms, 6)
steamos_lines = key_lines(reports / "steamos-mirror-key-lines.txt", vr_terms, 6)
lines.extend(["", "### SteamVR / SteamOS"])
if steamvr_lines or steamos_lines:
lines.extend([f"- SteamVR: `{line}`" for line in steamvr_lines])
lines.extend([f"- SteamOS: `{line}`" for line in steamos_lines])
else:
lines.append("- No Frame-relevant SteamVR or SteamOS key lines found.")
customs_lines = customs_vr_lines(reports / "customs-shipments.md", 6)
lines.extend(["", "### Customs / Shipments"])
if customs_lines:
lines.extend([f"- `{line}`" for line in customs_lines])
elif (reports / "customs-shipments-errors.txt").exists():
lines.append("- Shipment check had errors; inspect `customs-shipments-errors.txt`.")
else:
lines.append("- No VR/XR/headset shipment rows surfaced in the shipment report.")
lines.extend(["", "## Background Comparator Signals", ""])
machine_valve_rows = machine_rows(valve_rows)
machine_steamkit_rows = [
row for row in machine_rows(steamkit_rows) if row.get("changed_since_previous") == "yes"
]
if machine_valve_rows:
lines.append("- Steam Machine is now a launched comparator, not the active target.")
for row in machine_valve_rows[:6]:
lines.append(
f"- Machine Valve: `{bullet_table_row(row, ('type', 'id', 'status', 'name', 'release_or_price', 'details'))}`"
)
if machine_steamkit_rows:
for row in machine_steamkit_rows[:6]:
lines.append(
f"- Machine SteamKit moved: `{bullet_table_row(row, ('type', 'id', 'changenumber', 'previous_changenumber', 'changed_since_previous', 'details'))}`"
)
if not machine_valve_rows and not machine_steamkit_rows:
lines.append("- No Machine comparator rows were available in the saved reports.")
compare_file = latest_compare_file(reports)
lines.extend(["", "## Next Checks", ""])
lines.append("- Recheck SteamKit/PICS and Valve `appdetails` / `packagedetails` first.")
lines.append("- Treat SteamTracking or Komodo Frame movement as staging context until package/API visibility changes.")
lines.append("- Keep monitoring VR-labelled shipment rows as corroboration, not launch confirmation.")
if compare_file:
lines.append(f"- Cross-check material deltas in `{compare_file}`.")
lines.append("")
return "\n".join(lines)
def main():
parser = argparse.ArgumentParser(description="Write a Steam Frame focused report from saved watch artifacts.")
parser.add_argument("--run-dir", required=True)
parser.add_argument("--output", help="Optional output path")
args = parser.parse_args()
run_dir = Path(args.run_dir)
output = Path(args.output) if args.output else run_dir / "reports" / "frame-focus.md"
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(build(run_dir), encoding="utf-8")
if __name__ == "__main__":
main()