sol: untracked-file triage classification manifest + verifier
This commit is contained in:
1 parent
27edccc773
commit
376afd33f4
4 files changed
+970
No files matched your search
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
# CHECK -- sol: Untracked-file triage classification (forge: verify lane).
|
||||
# Exit 0 iff the committed Untracked-Triad-2026-11-01.json re-derives every
|
||||
# classified path's category/decision from the canonical rule, and each path is
|
||||
# still a real, untracked, non-ignored file on disk.
|
||||
set -u
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR" && pwd)"
|
||||
cd "$REPO_ROOT" || exit 2
|
||||
|
||||
exec python3 wizard-work/sol/verify_untracked_triad_2026_11_01.py
|
||||
@@ -0,0 +1,668 @@
|
||||
{
|
||||
"artifact": "Untracked-Triad-2026-11-01.json",
|
||||
"corpus_sha256": "321891e6717758c2018fcf7273dd6b37bc61c9325312b54f5a3b82134e1d8826",
|
||||
"files": [
|
||||
"CHECK-sol-untracked-classify",
|
||||
"CHECK-triage-2026-10-29",
|
||||
"checkins/2026-10-27-sentinel.md",
|
||||
"d.jsonl",
|
||||
"docs/clawrouter-plan.md",
|
||||
"docs/flux2-deploy.md",
|
||||
"docs/video-deploy.md",
|
||||
"inbox/dump_seed.py",
|
||||
"kanban.db",
|
||||
"recoveries/apply-queue-status-2026-10-25.md",
|
||||
"repo_exts.tsv",
|
||||
"repo_files.tsv",
|
||||
"repo_tree.tsv",
|
||||
"s.json",
|
||||
"test_sample.txt",
|
||||
"test_validation.txt",
|
||||
"test_verification.txt",
|
||||
"validate_artifact.py",
|
||||
"wizard-work/null/.hermes-tmp.RqZp3z",
|
||||
"wizard-work/null/README-gatehygiene.md",
|
||||
"wizard-work/null/README-q62-scan.md",
|
||||
"wizard-work/null/_probe10.py",
|
||||
"wizard-work/null/_probe11.py",
|
||||
"wizard-work/null/_probe12.py",
|
||||
"wizard-work/null/_probe13.py",
|
||||
"wizard-work/null/_probe14.py",
|
||||
"wizard-work/null/_probe5.py",
|
||||
"wizard-work/null/_probe6.py",
|
||||
"wizard-work/null/_probe7.py",
|
||||
"wizard-work/null/_probe8.py",
|
||||
"wizard-work/null/_probe9.py",
|
||||
"wizard-work/null/_probe_binding_defects.py",
|
||||
"wizard-work/null/_probe_gh1.py",
|
||||
"wizard-work/null/_probe_gh2.py",
|
||||
"wizard-work/null/_probe_gh3.py",
|
||||
"wizard-work/null/_probe_gh4.py",
|
||||
"wizard-work/null/_probe_gh5.py",
|
||||
"wizard-work/null/_probe_gh6.py",
|
||||
"wizard-work/null/_probe_gh7.py",
|
||||
"wizard-work/null/_probe_gh8.py",
|
||||
"wizard-work/null/_probe_gh9.py",
|
||||
"wizard-work/null/_probe_mut2.py",
|
||||
"wizard-work/null/_probe_mut_check.py",
|
||||
"wizard-work/null/_probe_q70_5h.py",
|
||||
"wizard-work/null/_probe_repro1.py",
|
||||
"wizard-work/null/_probe_repro2.py",
|
||||
"wizard-work/null/_probe_repro3.py",
|
||||
"wizard-work/null/_probe_repro4.py",
|
||||
"wizard-work/null/_probe_repro5.py",
|
||||
"wizard-work/null/_probe_repro6.py",
|
||||
"wizard-work/null/_probe_repro7.py",
|
||||
"wizard-work/null/check_gatehygiene.py",
|
||||
"wizard-work/null/check_provenance.py",
|
||||
"wizard-work/null/check_provenance_baseline.tsv",
|
||||
"wizard-work/null/check_provenance_ledger.py",
|
||||
"wizard-work/null/check_q62_scan.py",
|
||||
"wizard-work/null/cli_contract.py",
|
||||
"wizard-work/null/diffkit/__init__.py",
|
||||
"wizard-work/null/diffkit/_flatpkg.py",
|
||||
"wizard-work/null/diffkit/core.py",
|
||||
"wizard-work/null/diffkit/diffkit_cli.py",
|
||||
"wizard-work/null/diffkit/myers.py",
|
||||
"wizard-work/null/diffkit_gate.py",
|
||||
"wizard-work/null/gatehygiene.py",
|
||||
"wizard-work/null/json_salvage.py",
|
||||
"wizard-work/null/membershipkit/.hermes-tmp.sx2gIa",
|
||||
"wizard-work/null/membershipkit/check_membershipkit.sh",
|
||||
"wizard-work/null/membershipkit/mutate_membershipkit.py",
|
||||
"wizard-work/null/membershipkit/test_membershipkit.py",
|
||||
"wizard-work/null/mutate_resolver.py",
|
||||
"wizard-work/null/mutate_salvage.py",
|
||||
"wizard-work/null/q58_warncontrol.py",
|
||||
"wizard-work/null/q62_scan.py",
|
||||
"wizard-work/null/q70_sabotage.py",
|
||||
"wizard-work/null/q76_binding.py",
|
||||
"wizard-work/null/q80_warn.py",
|
||||
"wizard-work/null/revwalk.py",
|
||||
"wizard-work/null/test_gate.py",
|
||||
"wizard-work/null/test_gate_mutants.py",
|
||||
"wizard-work/null/test_json_salvage.py",
|
||||
"wizard-work/null/test_myers_diff.py",
|
||||
"wizard-work/null/test_revwalk.py",
|
||||
"wizard-work/null/verify_check_provenance.py",
|
||||
"wizard-work/null/verify_staged.py",
|
||||
"wizard-work/sol/CHECK-sol-profile-triage-2026-11-02",
|
||||
"wizard-work/sol/Triage-2026-11-02.json",
|
||||
"wizard-work/sol/Triage-2026-11-02.md",
|
||||
"wizard-work/sol/_print_paths.py",
|
||||
"wizard-work/sol/build_untracked_triad_2026_11_01.py",
|
||||
"wizard-work/sol/verify_sol_triage_2026_11_02.py",
|
||||
"wizard-work/sol/verify_untracked_triad_2026_11_01.py",
|
||||
"wizard-work/sphinx/archive-mmlongcite-2026-10-25/probe_paper_facts.json"
|
||||
],
|
||||
"files_count": 92,
|
||||
"generated_at": "2026-11-01T07:23:13Z",
|
||||
"items": [
|
||||
{
|
||||
"category": "gate",
|
||||
"decision": "approve",
|
||||
"id": 1,
|
||||
"path": "CHECK-sol-untracked-classify"
|
||||
},
|
||||
{
|
||||
"category": "gate",
|
||||
"decision": "approve",
|
||||
"id": 2,
|
||||
"path": "CHECK-triage-2026-10-29"
|
||||
},
|
||||
{
|
||||
"category": "documentation",
|
||||
"decision": "info",
|
||||
"id": 3,
|
||||
"path": "checkins/2026-10-27-sentinel.md"
|
||||
},
|
||||
{
|
||||
"category": "data",
|
||||
"decision": "observe",
|
||||
"id": 4,
|
||||
"path": "d.jsonl"
|
||||
},
|
||||
{
|
||||
"category": "documentation",
|
||||
"decision": "info",
|
||||
"id": 5,
|
||||
"path": "docs/clawrouter-plan.md"
|
||||
},
|
||||
{
|
||||
"category": "documentation",
|
||||
"decision": "info",
|
||||
"id": 6,
|
||||
"path": "docs/flux2-deploy.md"
|
||||
},
|
||||
{
|
||||
"category": "documentation",
|
||||
"decision": "info",
|
||||
"id": 7,
|
||||
"path": "docs/video-deploy.md"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 8,
|
||||
"path": "inbox/dump_seed.py"
|
||||
},
|
||||
{
|
||||
"category": "persistence",
|
||||
"decision": "archive",
|
||||
"id": 9,
|
||||
"path": "kanban.db"
|
||||
},
|
||||
{
|
||||
"category": "documentation",
|
||||
"decision": "info",
|
||||
"id": 10,
|
||||
"path": "recoveries/apply-queue-status-2026-10-25.md"
|
||||
},
|
||||
{
|
||||
"category": "data",
|
||||
"decision": "observe",
|
||||
"id": 11,
|
||||
"path": "repo_exts.tsv"
|
||||
},
|
||||
{
|
||||
"category": "data",
|
||||
"decision": "observe",
|
||||
"id": 12,
|
||||
"path": "repo_files.tsv"
|
||||
},
|
||||
{
|
||||
"category": "data",
|
||||
"decision": "observe",
|
||||
"id": 13,
|
||||
"path": "repo_tree.tsv"
|
||||
},
|
||||
{
|
||||
"category": "data",
|
||||
"decision": "observe",
|
||||
"id": 14,
|
||||
"path": "s.json"
|
||||
},
|
||||
{
|
||||
"category": "data",
|
||||
"decision": "observe",
|
||||
"id": 15,
|
||||
"path": "test_sample.txt"
|
||||
},
|
||||
{
|
||||
"category": "data",
|
||||
"decision": "observe",
|
||||
"id": 16,
|
||||
"path": "test_validation.txt"
|
||||
},
|
||||
{
|
||||
"category": "data",
|
||||
"decision": "observe",
|
||||
"id": 17,
|
||||
"path": "test_verification.txt"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 18,
|
||||
"path": "validate_artifact.py"
|
||||
},
|
||||
{
|
||||
"category": "other",
|
||||
"decision": "review",
|
||||
"id": 19,
|
||||
"path": "wizard-work/null/.hermes-tmp.RqZp3z"
|
||||
},
|
||||
{
|
||||
"category": "documentation",
|
||||
"decision": "info",
|
||||
"id": 20,
|
||||
"path": "wizard-work/null/README-gatehygiene.md"
|
||||
},
|
||||
{
|
||||
"category": "documentation",
|
||||
"decision": "info",
|
||||
"id": 21,
|
||||
"path": "wizard-work/null/README-q62-scan.md"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 22,
|
||||
"path": "wizard-work/null/_probe10.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 23,
|
||||
"path": "wizard-work/null/_probe11.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 24,
|
||||
"path": "wizard-work/null/_probe12.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 25,
|
||||
"path": "wizard-work/null/_probe13.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 26,
|
||||
"path": "wizard-work/null/_probe14.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 27,
|
||||
"path": "wizard-work/null/_probe5.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 28,
|
||||
"path": "wizard-work/null/_probe6.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 29,
|
||||
"path": "wizard-work/null/_probe7.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 30,
|
||||
"path": "wizard-work/null/_probe8.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 31,
|
||||
"path": "wizard-work/null/_probe9.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 32,
|
||||
"path": "wizard-work/null/_probe_binding_defects.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 33,
|
||||
"path": "wizard-work/null/_probe_gh1.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 34,
|
||||
"path": "wizard-work/null/_probe_gh2.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 35,
|
||||
"path": "wizard-work/null/_probe_gh3.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 36,
|
||||
"path": "wizard-work/null/_probe_gh4.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 37,
|
||||
"path": "wizard-work/null/_probe_gh5.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 38,
|
||||
"path": "wizard-work/null/_probe_gh6.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 39,
|
||||
"path": "wizard-work/null/_probe_gh7.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 40,
|
||||
"path": "wizard-work/null/_probe_gh8.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 41,
|
||||
"path": "wizard-work/null/_probe_gh9.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 42,
|
||||
"path": "wizard-work/null/_probe_mut2.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 43,
|
||||
"path": "wizard-work/null/_probe_mut_check.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 44,
|
||||
"path": "wizard-work/null/_probe_q70_5h.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 45,
|
||||
"path": "wizard-work/null/_probe_repro1.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 46,
|
||||
"path": "wizard-work/null/_probe_repro2.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 47,
|
||||
"path": "wizard-work/null/_probe_repro3.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 48,
|
||||
"path": "wizard-work/null/_probe_repro4.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 49,
|
||||
"path": "wizard-work/null/_probe_repro5.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 50,
|
||||
"path": "wizard-work/null/_probe_repro6.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 51,
|
||||
"path": "wizard-work/null/_probe_repro7.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 52,
|
||||
"path": "wizard-work/null/check_gatehygiene.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 53,
|
||||
"path": "wizard-work/null/check_provenance.py"
|
||||
},
|
||||
{
|
||||
"category": "data",
|
||||
"decision": "observe",
|
||||
"id": 54,
|
||||
"path": "wizard-work/null/check_provenance_baseline.tsv"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 55,
|
||||
"path": "wizard-work/null/check_provenance_ledger.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 56,
|
||||
"path": "wizard-work/null/check_q62_scan.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 57,
|
||||
"path": "wizard-work/null/cli_contract.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 58,
|
||||
"path": "wizard-work/null/diffkit/__init__.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 59,
|
||||
"path": "wizard-work/null/diffkit/_flatpkg.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 60,
|
||||
"path": "wizard-work/null/diffkit/core.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 61,
|
||||
"path": "wizard-work/null/diffkit/diffkit_cli.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 62,
|
||||
"path": "wizard-work/null/diffkit/myers.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 63,
|
||||
"path": "wizard-work/null/diffkit_gate.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 64,
|
||||
"path": "wizard-work/null/gatehygiene.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 65,
|
||||
"path": "wizard-work/null/json_salvage.py"
|
||||
},
|
||||
{
|
||||
"category": "other",
|
||||
"decision": "review",
|
||||
"id": 66,
|
||||
"path": "wizard-work/null/membershipkit/.hermes-tmp.sx2gIa"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 67,
|
||||
"path": "wizard-work/null/membershipkit/check_membershipkit.sh"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 68,
|
||||
"path": "wizard-work/null/membershipkit/mutate_membershipkit.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 69,
|
||||
"path": "wizard-work/null/membershipkit/test_membershipkit.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 70,
|
||||
"path": "wizard-work/null/mutate_resolver.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 71,
|
||||
"path": "wizard-work/null/mutate_salvage.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 72,
|
||||
"path": "wizard-work/null/q58_warncontrol.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 73,
|
||||
"path": "wizard-work/null/q62_scan.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 74,
|
||||
"path": "wizard-work/null/q70_sabotage.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 75,
|
||||
"path": "wizard-work/null/q76_binding.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 76,
|
||||
"path": "wizard-work/null/q80_warn.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 77,
|
||||
"path": "wizard-work/null/revwalk.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 78,
|
||||
"path": "wizard-work/null/test_gate.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 79,
|
||||
"path": "wizard-work/null/test_gate_mutants.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 80,
|
||||
"path": "wizard-work/null/test_json_salvage.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 81,
|
||||
"path": "wizard-work/null/test_myers_diff.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 82,
|
||||
"path": "wizard-work/null/test_revwalk.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 83,
|
||||
"path": "wizard-work/null/verify_check_provenance.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 84,
|
||||
"path": "wizard-work/null/verify_staged.py"
|
||||
},
|
||||
{
|
||||
"category": "other",
|
||||
"decision": "review",
|
||||
"id": 85,
|
||||
"path": "wizard-work/sol/CHECK-sol-profile-triage-2026-11-02"
|
||||
},
|
||||
{
|
||||
"category": "data",
|
||||
"decision": "observe",
|
||||
"id": 86,
|
||||
"path": "wizard-work/sol/Triage-2026-11-02.json"
|
||||
},
|
||||
{
|
||||
"category": "documentation",
|
||||
"decision": "info",
|
||||
"id": 87,
|
||||
"path": "wizard-work/sol/Triage-2026-11-02.md"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 88,
|
||||
"path": "wizard-work/sol/_print_paths.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 89,
|
||||
"path": "wizard-work/sol/build_untracked_triad_2026_11_01.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 90,
|
||||
"path": "wizard-work/sol/verify_sol_triage_2026_11_02.py"
|
||||
},
|
||||
{
|
||||
"category": "tooling",
|
||||
"decision": "review",
|
||||
"id": 91,
|
||||
"path": "wizard-work/sol/verify_untracked_triad_2026_11_01.py"
|
||||
},
|
||||
{
|
||||
"category": "data",
|
||||
"decision": "observe",
|
||||
"id": 92,
|
||||
"path": "wizard-work/sphinx/archive-mmlongcite-2026-10-25/probe_paper_facts.json"
|
||||
}
|
||||
],
|
||||
"lane": "Triage - everyday Q&A and classification",
|
||||
"repo": "/root/troupe-standup",
|
||||
"rule": "path-based deterministic classifier (see MODULE_DOCLINE)",
|
||||
"rule_version": "1",
|
||||
"summary": {
|
||||
"data": 11,
|
||||
"documentation": 8,
|
||||
"gate": 2,
|
||||
"other": 3,
|
||||
"persistence": 1,
|
||||
"tooling": 67,
|
||||
"total": 92
|
||||
},
|
||||
"task": "classify untracked repo files into deterministic buckets"
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build Untracked-Triad-2026-11-01.json.
|
||||
|
||||
Triage: classify the untracked (git ?? ) regular files of the repo into
|
||||
deterministic buckets, then write the machine-readable artifact.
|
||||
|
||||
Rule (deterministic, derived from the relative path only):
|
||||
|
||||
category:
|
||||
gate path starts with "CHECK-"
|
||||
persistence ends with ".db"
|
||||
data ends with ".json", ".jsonl", ".tsv" or ".txt"
|
||||
tooling ends with ".py" or ".sh"
|
||||
documentation ends with ".md"
|
||||
other anything else
|
||||
|
||||
decision:
|
||||
gate -> approve
|
||||
tooling -> review
|
||||
documentation -> info
|
||||
data -> observe
|
||||
persistence -> archive
|
||||
other -> review
|
||||
|
||||
The manifest snapshots the exact file list at generation time (files + a
|
||||
sha256 of the canonical list) so the independent verifier can confirm the
|
||||
artifact is a faithful, reproducible classification, not a hand-edited guess.
|
||||
"""
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
|
||||
def find_repo_root(start):
|
||||
"""Walk up from *start* until a directory that contains a .git marker."""
|
||||
d = os.path.abspath(start)
|
||||
for _ in range(64):
|
||||
if os.path.isdir(os.path.join(d, ".git")):
|
||||
return d
|
||||
parent = os.path.dirname(d)
|
||||
if parent == d:
|
||||
break
|
||||
d = parent
|
||||
raise RuntimeError("could not locate repo root (no .git) above " + start)
|
||||
|
||||
|
||||
REPO_ROOT = find_repo_root(os.path.dirname(os.path.abspath(__file__)))
|
||||
OUT_REPO = os.path.join(
|
||||
REPO_ROOT, "wizard-work", "sol", "Untracked-Triad-2026-11-01.json"
|
||||
)
|
||||
OUT_WIZ = os.path.expanduser(
|
||||
os.path.join("~", ".hermes", "wizard-outputs", "sol", "Untracked-Triad-2026-11-01.json")
|
||||
)
|
||||
|
||||
|
||||
def git_untracked(repo):
|
||||
"""Return sorted list of untracked regular files (skip untracked dirs)."""
|
||||
proc = subprocess.run(
|
||||
["git", "-C", repo, "status", "--porcelain=v1"],
|
||||
check=True, capture_output=True, text=True,
|
||||
)
|
||||
files = []
|
||||
for line in proc.stdout.splitlines():
|
||||
if line.startswith("??"):
|
||||
path = line[3:].strip()
|
||||
if path and not path.endswith("/"):
|
||||
files.append(path)
|
||||
return sorted(files)
|
||||
|
||||
|
||||
def category_of(rel):
|
||||
low = rel.lower()
|
||||
if low.startswith("check-"):
|
||||
return "gate"
|
||||
if low.endswith(".db"):
|
||||
return "persistence"
|
||||
if low.endswith((".json", ".jsonl", ".tsv", ".txt")):
|
||||
return "data"
|
||||
if low.endswith((".py", ".sh")):
|
||||
return "tooling"
|
||||
if low.endswith(".md"):
|
||||
return "documentation"
|
||||
return "other"
|
||||
|
||||
|
||||
DECISION = {
|
||||
"gate": "approve",
|
||||
"tooling": "review",
|
||||
"documentation": "info",
|
||||
"data": "observe",
|
||||
"persistence": "archive",
|
||||
"other": "review",
|
||||
}
|
||||
|
||||
|
||||
def build(files):
|
||||
items = []
|
||||
for i, rel in enumerate(files, start=1):
|
||||
cat = category_of(rel)
|
||||
items.append({
|
||||
"id": i,
|
||||
"path": rel,
|
||||
"category": cat,
|
||||
"decision": DECISION[cat],
|
||||
})
|
||||
return items
|
||||
|
||||
|
||||
def corpus_sha256(files):
|
||||
canon = json.dumps({"files": files}, sort_keys=True, separators=(",", ":")).encode("utf-8")
|
||||
return hashlib.sha256(canon).hexdigest()
|
||||
|
||||
|
||||
def main():
|
||||
files = git_untracked(REPO_ROOT)
|
||||
items = build(files)
|
||||
manifest = {
|
||||
"artifact": "Untracked-Triad-2026-11-01.json",
|
||||
"lane": "Triage - everyday Q&A and classification",
|
||||
"task": "classify untracked repo files into deterministic buckets",
|
||||
"generated_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
||||
"repo": REPO_ROOT,
|
||||
"rule": "path-based deterministic classifier (see MODULE_DOCLINE)",
|
||||
"rule_version": "1",
|
||||
"files": files,
|
||||
"files_count": len(files),
|
||||
"corpus_sha256": corpus_sha256(files),
|
||||
"summary": {
|
||||
"total": len(files),
|
||||
"gate": sum(1 for it in items if it["category"] == "gate"),
|
||||
"persistence": sum(1 for it in items if it["category"] == "persistence"),
|
||||
"data": sum(1 for it in items if it["category"] == "data"),
|
||||
"tooling": sum(1 for it in items if it["category"] == "tooling"),
|
||||
"documentation": sum(1 for it in items if it["category"] == "documentation"),
|
||||
"other": sum(1 for it in items if it["category"] == "other"),
|
||||
},
|
||||
"items": items,
|
||||
}
|
||||
|
||||
for out in (OUT_REPO, OUT_WIZ):
|
||||
os.makedirs(os.path.dirname(out), exist_ok=True)
|
||||
with open(out, "w", encoding="utf-8") as fh:
|
||||
json.dump(manifest, fh, indent=2, sort_keys=True)
|
||||
fh.write("\n")
|
||||
|
||||
print("Untracked-Triad-2026-11-01.json written")
|
||||
print(" files:", len(files))
|
||||
print(" corpus_sha256:", manifest["corpus_sha256"])
|
||||
print(" categories:", manifest["summary"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,134 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Independent verifier for Untracked-Triad-2026-11-01.json.
|
||||
|
||||
The classification rule is re-derived from each item's path (the generator's
|
||||
stored category/decision fields are NOT trusted). A wrong classification makes
|
||||
this exit non-zero. Additionally every classified path must still exist on disk
|
||||
as a real, untracked, non-ignored file.
|
||||
|
||||
Exit 0 iff all checks pass.
|
||||
"""
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
MANIFEST = os.path.join(HERE, "Untracked-Triad-2026-11-01.json")
|
||||
|
||||
REPO_ROOT = os.path.dirname(os.path.dirname(HERE))
|
||||
|
||||
|
||||
def cat_of(rel):
|
||||
low = rel.lower()
|
||||
if low.startswith("check-"):
|
||||
return "gate"
|
||||
if low.endswith(".db"):
|
||||
return "persistence"
|
||||
if low.endswith((".json", ".jsonl", ".tsv", ".txt")):
|
||||
return "data"
|
||||
if low.endswith((".py", ".sh")):
|
||||
return "tooling"
|
||||
if low.endswith(".md"):
|
||||
return "documentation"
|
||||
return "other"
|
||||
|
||||
|
||||
DECISION = {
|
||||
"gate": "approve",
|
||||
"tooling": "review",
|
||||
"documentation": "info",
|
||||
"data": "observe",
|
||||
"persistence": "archive",
|
||||
"other": "review",
|
||||
}
|
||||
|
||||
|
||||
def git_status_untracked(repo):
|
||||
proc = subprocess_run(["git", "-C", repo, "status", "--porcelain=v1"])
|
||||
return set(line[3:].strip() for line in proc.stdout.splitlines()
|
||||
if line.startswith("??") and not line[3:].strip().endswith("/"))
|
||||
|
||||
|
||||
def git_check_ignored(repo, rel):
|
||||
proc = subprocess_run(["git", "check-ignore", "-q", rel])
|
||||
return proc.returncode == 0
|
||||
|
||||
|
||||
def subprocess_run(cmd):
|
||||
import subprocess
|
||||
return subprocess.run(cmd, capture_output=True, text=True)
|
||||
|
||||
|
||||
def main():
|
||||
errors = []
|
||||
try:
|
||||
with open(MANIFEST, encoding="utf-8") as fh:
|
||||
man = json.load(fh)
|
||||
except Exception as exc:
|
||||
print("FAIL: cannot load manifest:", exc)
|
||||
sys.exit(1)
|
||||
|
||||
files = man.get("files", [])
|
||||
items = man.get("items", [])
|
||||
repo = man.get("repo", REPO_ROOT)
|
||||
|
||||
# 1. corpus sha256 must match the recorded file list
|
||||
canon = json.dumps({"files": sorted(files)}, sort_keys=True, separators=(",", ":")).encode("utf-8")
|
||||
computed_sha = hashlib.sha256(canon).hexdigest()
|
||||
if computed_sha != man.get("corpus_sha256"):
|
||||
errors.append("corpus_sha256 mismatch (recorded %s vs computed %s)" %
|
||||
(man.get("corpus_sha256"), computed_sha))
|
||||
|
||||
# 2. ids must be unique and exactly 1..N
|
||||
ids = [it.get("id") for it in items]
|
||||
if sorted(ids) != list(range(1, len(items) + 1)):
|
||||
errors.append("ids are not exactly 1..N")
|
||||
|
||||
# 3. re-derive category/decision from each path; verify file exists, untracked, non-ignored
|
||||
for it in items:
|
||||
rel = it.get("path")
|
||||
exp_cat = it.get("category")
|
||||
exp_dec = it.get("decision")
|
||||
if not rel:
|
||||
errors.append("missing path on item id=%r" % (it.get("id"),))
|
||||
continue
|
||||
act_cat = cat_of(rel)
|
||||
act_dec = DECISION.get(act_cat)
|
||||
if exp_cat != act_cat:
|
||||
errors.append("id=%s path=%s category %s != re-derived %s" % (it.get("id"), rel, exp_cat, act_cat))
|
||||
if exp_dec != act_dec:
|
||||
errors.append("id=%s path=%s decision %s != re-derived %s" % (it.get("id"), rel, exp_dec, act_dec))
|
||||
if not os.path.isfile(os.path.join(repo, rel)):
|
||||
errors.append("id=%s path=%s does not exist as a real file" % (it.get("id"), rel))
|
||||
if rel not in git_status_untracked(repo):
|
||||
errors.append("id=%s path=%s is not currently untracked" % (it.get("id"), rel))
|
||||
if git_check_ignored(repo, rel):
|
||||
errors.append("id=%s path=%s is git-ignored" % (it.get("id"), rel))
|
||||
|
||||
# 4. summary counts must be consistent with items (skip the "total" key,
|
||||
# which is the corpus size, not a category).
|
||||
from collections import Counter
|
||||
CAT_KEYS = {"gate", "persistence", "data", "tooling", "documentation", "other"}
|
||||
cnt = Counter(it.get("category") for it in items)
|
||||
for cat, n in man.get("summary", {}).items():
|
||||
if cat not in CAT_KEYS:
|
||||
continue
|
||||
if cnt.get(cat, 0) != n:
|
||||
errors.append("summary %s=%d but items have %d" % (cat, n, cnt.get(cat, 0)))
|
||||
|
||||
if errors:
|
||||
print("FAIL: %d problem(s)" % len(errors))
|
||||
for e in errors[:40]:
|
||||
print(" -", e)
|
||||
sys.exit(1)
|
||||
|
||||
print("PASS: %d files classified; %d re-derive to matching category/decision; "
|
||||
"all files real, untracked, non-ignored; corpus sha256 matches."
|
||||
% (len(files), len(items)))
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user