97 lines
3.7 KiB
Python
97 lines
3.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Build the untracked-file triage manifest.
|
|
|
|
Universe: every file `git ls-files --others --exclude-standard` reports in
|
|
/root/troupe-standup (untracked, non-ignored). Each path is classified by the
|
|
rule in `category()` into exactly one bucket, and the result is written as a
|
|
strict TSV (path \t category \t rule_note) plus a JSON manifest.
|
|
|
|
Rule notes are verbatim, so the category of any row can be re-derived from the
|
|
path alone; the verifier does exactly that re-derivation.
|
|
"""
|
|
import json
|
|
import os
|
|
import subprocess
|
|
from collections import Counter
|
|
|
|
REPO = "/root/troupe-standup"
|
|
OUT_DIR = os.path.join(REPO, "wizard-work", "sol")
|
|
MANIFEST_TSV = os.path.join(OUT_DIR, "untracked-classify.tsv")
|
|
MANIFEST_JSON = os.path.join(OUT_DIR, "untracked-classify.json")
|
|
SUMMARY_MD = os.path.join(OUT_DIR, "untracked-classify-summary.md")
|
|
|
|
# Bucket order matters: earlier buckets win on overlap.
|
|
def category(p):
|
|
base = os.path.basename(p)
|
|
if p.startswith("wizard-work/null/"):
|
|
return "probe"
|
|
if p.startswith("wizard-work/spark/"):
|
|
return "deliverable"
|
|
if p.startswith("wizard-work/sphinx/"):
|
|
return "report"
|
|
if p.startswith("wizard-work/ledger/"):
|
|
return "report"
|
|
if p.startswith("wizard-work/"):
|
|
return "other"
|
|
if base.endswith(".py"):
|
|
return "tool"
|
|
if base.endswith((".json", ".csv", ".tsv", ".db", ".yaml", ".yml",
|
|
".toml", ".lock", ".html", ".css")):
|
|
return "data"
|
|
if base.endswith((".md", ".txt")):
|
|
return "report"
|
|
return "other"
|
|
|
|
def rule_note(p):
|
|
c = category(p)
|
|
if c == "probe":
|
|
return "wizard-work/null scratch probing output"
|
|
if c == "deliverable":
|
|
return "finished scripts + tests + output under wizard-work/spark"
|
|
if c == "report":
|
|
return "analysis / extraction / archive probe report"
|
|
if c == "tool":
|
|
return "standalone Python tool"
|
|
if c == "data":
|
|
return "data artifact (json/csv/tsv/db/yaml/toml/lock/html/css)"
|
|
if c == "other":
|
|
return "other file type"
|
|
return "unclassified"
|
|
|
|
def main():
|
|
out = subprocess.run(
|
|
["git", "-C", REPO, "ls-files", "--others", "--exclude-standard"],
|
|
capture_output=True, text=True, check=True).stdout
|
|
paths = sorted(set(out.splitlines()))
|
|
paths = [p for p in paths if p] # drop empties
|
|
rows = [(p, category(p), rule_note(p)) for p in paths]
|
|
|
|
os.makedirs(OUT_DIR, exist_ok=True)
|
|
with open(MANIFEST_TSV, "w") as f:
|
|
f.write("path\tcategory\trule_note\n")
|
|
for p, c, n in rows:
|
|
f.write(f"{p}\t{c}\t{n}\n")
|
|
with open(MANIFEST_JSON, "w") as f:
|
|
json.dump({"count": len(rows),
|
|
"snapshot_paths": paths,
|
|
"rows": rows,
|
|
"buckets": Counter(c for _, c, _ in rows)}, f, indent=2)
|
|
with open(os.path.join(OUT_DIR, "untracked-classify-snapshot.txt"), "w") as f:
|
|
f.write("\n".join(paths) + "\n")
|
|
summary = ["# untracked-file triage manifest",
|
|
"",
|
|
"Universe: `git ls-files --others --exclude-standard` in the"
|
|
" troupe repo, refreshed 2026-10-28.",
|
|
"Vault (absolute path): " + MANIFEST_TSV,
|
|
"Frozen universe (paths, one per line):"
|
|
" " + os.path.join(OUT_DIR, "untracked-classify-snapshot.txt"),
|
|
"Sum: %d files" % len(rows),
|
|
""] + ["%s: %d" % (k, v) for k, v in sorted(Counter(c for _, c, _ in rows).items())] + [""]
|
|
open(SUMMARY_MD, "w").write("\n".join(summary))
|
|
print("count", len(rows))
|
|
for k, v in sorted(Counter(c for _, c, _ in rows).items()):
|
|
print(k, v)
|
|
|
|
if __name__ == "__main__":
|
|
main()
|