#!/usr/bin/env python3 from __future__ import annotations import json from pathlib import Path ROOT = Path(__file__).resolve().parents[1] LABELS = [ "intake", "agent-job", "read-only", "test-generation", "verifier", "consolidation", "archive-candidate", "needs-human", "high-leverage", "token-burn-safe", ] JOB_LABELS = { "intake-audit-komandozentral": ["intake", "agent-job", "read-only", "high-leverage", "token-burn-safe"], "intake-generate-tests-werbinich": ["intake", "agent-job", "read-only", "test-generation", "high-leverage", "token-burn-safe"], "intake-consolidate-agent-control-tower-repos": ["intake", "agent-job", "read-only", "consolidation", "needs-human", "high-leverage", "token-burn-safe"], "intake-archive-dead-prototype-xyz": ["intake", "agent-job", "read-only", "archive-candidate", "needs-human", "token-burn-safe"], "intake-build-verifier-hermes-profile-health": ["intake", "agent-job", "read-only", "verifier", "high-leverage", "token-burn-safe"], } DESCRIPTIONS = { "intake": "Generated or managed by ProjectIntakeTower intake workflow.", "agent-job": "Has a bounded worker/job contract with artifact duty.", "read-only": "Source projects must not be modified by this task.", "test-generation": "Produces tests or test patches, not applied automatically.", "verifier": "Builds, runs, or improves verification artifacts.", "consolidation": "Compares overlapping repos and proposes a consolidation path.", "archive-candidate": "Candidate for archive/no-action review; never delete automatically.", "needs-human": "Requires owner decision before execution or source mutation.", "high-leverage": "Expected to reduce many open loops or unblock multiple projects.", "token-burn-safe": "Bounded to local artifacts and concise evidence to avoid runaway context/token use.", } def write_labels_md() -> None: lines = ["# GitHub Labels", "", "Canonical labels for ProjectIntakeTower intake drafts/jobs:", ""] for label in LABELS: lines.append(f"- `{label}` — {DESCRIPTIONS[label]}") lines += [ "", "## Legacy/generated aliases still accepted", "", "- `intake:discovered`", "- `intake:todo`", "- `intake:verification-failed`", "- `intake:quick-win`", "- `intake:needs-human`", "- `intake:archive-candidate`", "- `intake:merge-candidate`", "- `agent:handoff-ready`", "- `priority:p0`", "- `priority:p1`", "- `priority:p2`", "- `priority:p3`", "", ] (ROOT / "github" / "labels.md").write_text("\n".join(lines), encoding="utf-8") def update_jobs() -> int: count = 0 for jid, labels in JOB_LABELS.items(): path = ROOT / "jobs" / "pending" / f"{jid}.json" if not path.exists(): continue data = json.loads(path.read_text(encoding="utf-8")) data["labels"] = labels data.setdefault("inputs", {})["labels"] = labels path.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8") count += 1 return count def update_drafts() -> int: count = 0 for jid, labels in JOB_LABELS.items(): path = ROOT / "github" / "issue_drafts" / f"{jid}.md" if not path.exists(): continue text = path.read_text(encoding="utf-8") label_block = "Labels: " + ", ".join(labels) + "\n\n" lines = text.splitlines() # Insert after H1 title; replace old Labels line if present near top. if len(lines) > 1 and lines[1].startswith("Labels:"): lines[1] = label_block.strip() new_text = "\n".join(lines) + "\n" elif "\nLabels: " in text[:300]: new_text = text else: new_text = lines[0] + "\n\n" + label_block + "\n".join(lines[1:]).lstrip("\n") + "\n" path.write_text(new_text, encoding="utf-8") count += 1 return count def update_triage() -> None: path = ROOT / "triage" / "INTAKE_COMMANDS.md" text = path.read_text(encoding="utf-8") if path.exists() else "# Intake Commands\n" section = "\n## Label set\n\n" + "\n".join(f"- `{x}`" for x in LABELS) + "\n" if "## Label set" in text: text = text.split("## Label set")[0].rstrip() + section else: text = text.rstrip() + section path.write_text(text, encoding="utf-8") def main() -> int: write_labels_md() jobs = update_jobs() drafts = update_drafts() update_triage() print(json.dumps({"status": "succeeded", "labels": LABELS, "jobs_updated": jobs, "drafts_updated": drafts}, indent=2)) return 0 if __name__ == "__main__": raise SystemExit(main())