47 lines
1.8 KiB
Python
47 lines
1.8 KiB
Python
#!/usr/bin/env python3
|
|
"""library_drop_classify.py
|
|
|
|
Re-derive the library-drop triage roster from drops/*/MANIFEST.md. Deterministic;
|
|
used by _check_library_drop_roster.sh to diff against the committed
|
|
library-drop-roster.json. Exits 0 only if the committed roster matches the
|
|
source manifests.
|
|
"""
|
|
import json
|
|
import os
|
|
import re
|
|
import sys
|
|
|
|
BASE = '/root/stackchain-library-updates/drops'
|
|
|
|
|
|
def classify(txt):
|
|
# Ordered, low-level rules first.
|
|
if 'HTTP 402' in txt or 'Insufficient Balance' in txt:
|
|
return 'blocked', 'HTTP 402 / Insufficient Balance'
|
|
if 'prepared and delivered to Telegram review' in txt and 'not posted' in txt.lower():
|
|
return 'prepared_not_posted', 'prepared_and_delivered + not_posted'
|
|
if ('**PUBLISHED**' in txt) or ('**PUBLISHED.**' in txt) or ('PUBLISHED by Alexander' in txt) \
|
|
or re.search(r'PUBLISHED\.\*\*\s+Posted', txt):
|
|
return 'published', 'Status: PUBLISHED / Published by Alexander'
|
|
if ('No public change' in txt) or ('No episode' in txt) or ('NOT PUBLISHED' in txt) \
|
|
or ('Nothing on the site' in txt) or ('not published' in txt.lower()) \
|
|
or ('unpublished' in txt.lower()) or ('public change' in txt.lower() and 'none' in txt.lower()):
|
|
return 'no_episode', 'no-public-change / not-unpublished'
|
|
return 'unclassified', 'unclassified'
|
|
|
|
|
|
def main():
|
|
dirs = sorted([d for d in os.listdir(BASE)
|
|
if d.startswith('2026-') and os.path.isdir(os.path.join(BASE, d))])
|
|
rows = []
|
|
for d in dirs:
|
|
txt = open(os.path.join(BASE, d, 'MANIFEST.md'), encoding='utf-8',
|
|
errors='replace').read()
|
|
bucket, matched = classify(txt)
|
|
rows.append({'dir': d, 'bucket': bucket, 'matched': matched})
|
|
json.dump(rows, sys.stdout, indent=1)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|