@@ -111,7 +111,10 @@ def cmd_score(args: argparse.Namespace) -> int:
111111 if write_cache :
112112 ledger .replace_all (entries , SCORES_PATH )
113113
114- _print_histogram (hist , scored , hard_flags , wrote_cache = write_cache )
114+ if getattr (args , "format" , "text" ) == "md" :
115+ _print_markdown (hist , scored , hard_flags )
116+ else :
117+ _print_histogram (hist , scored , hard_flags , wrote_cache = write_cache )
115118 return 0
116119
117120
@@ -146,6 +149,43 @@ def _print_histogram(hist, scored, hard_flags, wrote_cache) -> None:
146149 print ("\n cache: wrote full Tier 0 scores to data/_verify/state/scores.jsonl" )
147150
148151
152+ def _print_markdown (hist , scored , hard_flags ) -> None :
153+ """GitHub-flavored markdown table — readable in PR comments (mirrors the
154+ TechEngineBot validation-stats style)."""
155+ if scored == 0 :
156+ print ("_No records scored._" )
157+ return
158+ totals = Counter ()
159+ rows = []
160+ for cat in CATEGORIES :
161+ if cat not in hist :
162+ continue
163+ c = hist [cat ]
164+ tot = sum (c .values ())
165+ totals .update (c )
166+ gpct = 100 * c ["green" ] / tot if tot else 0.0
167+ rows .append (
168+ f"| { cat } | { tot } | { c ['green' ]} | { c ['yellow' ]} | { c ['red' ]} | { gpct :.1f} % |"
169+ )
170+ gtot = sum (totals .values ()) or 1
171+ print (f"**{ scored } record(s) scored.**\n " )
172+ print ("| Category | Total | 🟢 Green | 🟡 Yellow | 🔴 Red | Green % |" )
173+ print ("| --- | ---: | ---: | ---: | ---: | ---: |" )
174+ for r in rows :
175+ print (r )
176+ print (
177+ f"| **All** | **{ sum (totals .values ())} ** | **{ totals ['green' ]} ** | "
178+ f"**{ totals ['yellow' ]} ** | **{ totals ['red' ]} ** | "
179+ f"**{ 100 * totals ['green' ]/ gtot :.1f} %** |"
180+ )
181+ if hard_flags :
182+ print ("\n **Hard violations** (forced red):\n " )
183+ print ("| Count | Check |" )
184+ print ("| ---: | --- |" )
185+ for name , n in hard_flags .most_common (10 ):
186+ print (f"| { n } | `{ name } ` |" )
187+
188+
149189def cmd_report (args : argparse .Namespace ) -> int :
150190 if not SCORES_PATH .exists ():
151191 print ("no scores cache — run `python -m app.verify score` first" )
@@ -371,6 +411,8 @@ def build_parser() -> argparse.ArgumentParser:
371411 sc .add_argument ("--unverified-only" , action = "store_true" , help = "skip verified:true records" )
372412 sc .add_argument ("--changed" , action = "store_true" , help = "only records changed vs origin/main" )
373413 sc .add_argument ("--no-cache" , action = "store_true" , help = "do not write the scores cache" )
414+ sc .add_argument ("--format" , choices = ["text" , "md" ], default = "text" ,
415+ help = "output format: text histogram (default) or markdown table" )
374416 sc .set_defaults (func = cmd_score )
375417
376418 rp = sub .add_parser ("report" , help = "summarize latest ledger state" )
0 commit comments