6161
6262LANDS_TAG = {"landed" : "Landed" , "ready" : "Ready" , "open" : "Your call" }
6363FRAME_PROSE = (("claim_check" , "Claim check" ),)
64- FRAME_CALLS = (("orient_call" , "Your answer on the summary" ), ("plan_call" , "Your answer on the order" ))
64+ # (session key, the question the reviewer answered before the walk)
65+ FRAME_CALLS = (
66+ ("orient_call" , "Is the summary of this change right?" ),
67+ ("plan_call" , "In what order should the items be read?" ),
68+ )
6569
6670# Only for --standalone. The viewport tag is load-bearing: report.css has a
6771# 620px breakpoint that never fires without it.
387391 // ledger both say so; this says it where the click happened.
388392 function owedNote() {
389393 const staged = document.querySelector('#stage .beat.s-flag');
390- return staged ? `beat ${staged.dataset.n} stays owed` : '';
394+ return staged ? `item ${staged.dataset.n} stays owed` : '';
391395 }
392396
393397 function resumePending() {
@@ -598,6 +602,30 @@ def diff_html(lines):
598602 )
599603
600604
605+ def decision_text (beat , mode , replacement = False , untrusted_pr = False ):
606+ """A flag's (question, recommendation). A session older than the field gets the
607+ choice its buttons offer, and no guessed recommendation."""
608+ decision = beat .get ("decision" )
609+ if isinstance (decision , dict ):
610+ return (
611+ decision .get ("question" , "Decision not recorded." ),
612+ decision .get ("recommendation" , "Recommended option not recorded." ),
613+ )
614+ if beat .get ("resolution_kind" ) != "decision" and (
615+ replacement or (mode == "branch" and untrusted_pr )
616+ ):
617+ question = "Replace this blocked session before choosing delivery, or drop this finding?"
618+ elif beat .get ("resolution_kind" ) == "decision" :
619+ question = "Choose the policy option in FIX or drop this question?"
620+ else :
621+ question = {
622+ "branch" : "Implement the proposed fix or drop this finding?" ,
623+ "review" : "Include in review or drop this finding?" ,
624+ "report" : "Include in report or drop this finding?" ,
625+ }[mode ]
626+ return question , "Recommended option not recorded."
627+
628+
601629def outcome_html (beat , mode ):
602630 """One plain sentence on an accepted beat: what the reviewer chose and what it did.
603631 Accepting posts or records a finding; only branch mode changes the code."""
@@ -804,26 +832,7 @@ def beat_html(
804832
805833 body = []
806834 if beat .get ("state" ) == "flag" :
807- decision = beat .get ("decision" )
808- if isinstance (decision , dict ):
809- question = decision .get ("question" , "Decision not recorded." )
810- recommendation = decision .get ("recommendation" , "Recommended option not recorded." )
811- else :
812- if beat .get ("resolution_kind" ) != "decision" and (
813- replacement or (mode == "branch" and untrusted_pr )
814- ):
815- question = "Replace this blocked session before choosing delivery, or drop this finding?"
816- else :
817- question = (
818- "Choose the policy option in FIX or drop this question?"
819- if beat .get ("resolution_kind" ) == "decision"
820- else {
821- "branch" : "Implement the proposed fix or drop this finding?" ,
822- "review" : "Include in review or drop this finding?" ,
823- "report" : "Include in report or drop this finding?" ,
824- }[mode ]
825- )
826- recommendation = "Recommended option not recorded."
835+ question , recommendation = decision_text (beat , mode , replacement , untrusted_pr )
827836 body .append (
828837 '<div class="decision">'
829838 f'<p><strong>Decision:</strong> { md (question )} </p>'
@@ -950,7 +959,8 @@ def beat_html(
950959 )
951960
952961 return (
953- f'<details class="beat s-{ suffix } " data-n="{ attr (n )} "{ " open" if expanded else "" } >'
962+ f'<details class="beat s-{ suffix } " id="item-{ attr (n )} " data-n="{ attr (n )} "'
963+ f'{ " open" if expanded else "" } >'
954964 f"<summary>"
955965 f'<span class="b-num">{ md (n )} </span>'
956966 f'<span class="b-tier">{ md (beat .get ("tier" , "" ))} </span>'
@@ -1097,7 +1107,7 @@ def stage_html(session, beats, current, card):
10971107 )
10981108
10991109
1100- def assessment (session , beats , problems_by_n , validation_problems = ()):
1110+ def assessment (session , beats , problems_by_n , validation_problems = (), walking = False ):
11011111 text = session .get ("recommendation" )
11021112 if not isinstance (text , str ) or not text .strip ():
11031113 text = "Recommendation not recorded. Do not infer merge readiness from the counts."
@@ -1114,7 +1124,11 @@ def assessment(session, beats, problems_by_n, validation_problems=()):
11141124 limits .append (f"{ unverified } item{ 's' if unverified != 1 else '' } unverified." )
11151125 if problems_by_n or validation_problems :
11161126 limits .append ("The report has validation problems; resolve them before relying on it." )
1117- label = "Provisional recommendation" if limits else "Recommendation"
1127+ owed = sum (beat .get ("state" ) == "flag" for beat in beats )
1128+ if limits :
1129+ label = "Provisional recommendation"
1130+ else :
1131+ label = "Recommendation" if owed or walking else "Final recommendation"
11181132 if execution_mode (session ) == "no_exec" :
11191133 limits .append ("Static inspection only; runtime behavior was not verified." )
11201134 mode = session_mode (session )
@@ -1124,17 +1138,46 @@ def assessment(session, beats, problems_by_n, validation_problems=()):
11241138 f"{ included } finding{ 's' if included != 1 else '' } included in the { mode } . "
11251139 "Including a finding does not fix the issue it describes."
11261140 )
1127- owed = sum (beat .get ("state" ) == "flag" for beat in beats )
11281141 return label , text , limits , owed
11291142
11301143
1131- def assessment_html (session , beats , problems_by_n , validation_problems = ()):
1132- label , text , limits , owed = assessment (session , beats , problems_by_n , validation_problems )
1144+ NEXT_STEP = {
1145+ "review" : "Nothing reaches GitHub until you post the whole review at the end." ,
1146+ "report" : "Including a finding records it in this report and posts nothing." ,
1147+ "branch" : "Implement applies the stated fix and commits it." ,
1148+ }
1149+
1150+
1151+ def assessment_html (
1152+ session , beats , problems_by_n , validation_problems = (),
1153+ replacement = False , untrusted_pr = False , walking = False ,
1154+ ):
1155+ label , text , limits , owed = assessment (
1156+ session , beats , problems_by_n , validation_problems , walking ,
1157+ )
11331158 change = session .get ("reconstruction" )
1159+ mode = session_mode (session )
1160+ asks = "" .join (
1161+ f'<li><a href="#item-{ attr (beat .get ("n" , "?" ))} ">Item { md (beat .get ("n" , "?" ))} </a>: '
1162+ f'{ md (decision_text (beat , mode , replacement , untrusted_pr )[0 ])} </li>'
1163+ for beat in beats if beat .get ("state" ) == "flag"
1164+ )
1165+ if asks :
1166+ blocked = replacement or (mode == "branch" and untrusted_pr )
1167+ step = (
1168+ "Implementing is blocked for this session; a finding can only be dropped or noted."
1169+ if blocked else NEXT_STEP [mode ]
1170+ )
1171+ waiting = f'<ul class="asks">{ asks } </ul><p class="next-step">{ step } </p>'
1172+ elif label == "Final recommendation" :
1173+ waiting = '<p class="next-step">The review is complete. Nothing is waiting on you.</p>'
1174+ else :
1175+ waiting = ""
11341176 return (
11351177 '<section class="assessment" aria-label="Review recommendation">'
11361178 f'<h2>{ label } </h2><p class="recommendation">{ md (text )} </p>'
11371179 f'<p class="decisions-owed">{ owed } decision{ "s" if owed != 1 else "" } owed</p>'
1180+ + waiting
11381181 + "" .join (f'<p class="qualification">{ md (limit )} </p>' for limit in limits )
11391182 + (f'<h3>What this change is</h3><p class="change">{ md (change )} </p>' if change else "" )
11401183 + '</section>'
@@ -1162,7 +1205,10 @@ def body_html(
11621205 key = lambda b : b ["n" ],
11631206 default = None ,
11641207 )
1165- parts = [assessment_html (session , beats , problems_by_n , validation_problems )]
1208+ parts = [assessment_html (
1209+ session , beats , problems_by_n , validation_problems , replacement , untrusted_pr ,
1210+ walking = live and phase != "done" ,
1211+ )]
11661212 if live :
11671213 parts .append (bar_html (session , beats , current , phase ))
11681214 if phase != "done" :
@@ -1257,10 +1303,11 @@ def frame_html(session, live):
12571303 calls = [(label , session .get (key )) for key , label in FRAME_CALLS if session .get (key )]
12581304 if not prose and not calls :
12591305 return ""
1260- summary = '<span class="sep">·</span>' .join (
1261- f'<span class="frame-k">{ label } </span><q>{ md (text )} </q>' for label , text in calls
1262- ) or '<span class="frame-k">How this review was framed</span>'
1263- body = "" .join (f"<dt>{ label } </dt><dd>{ md (text )} </dd>" for label , text in prose )
1306+ summary = '<span class="frame-k">How this review was framed</span>'
1307+ body = "" .join (
1308+ f'<dt class="frame-q">{ label } </dt><dd>You answered <q>{ md (text )} </q></dd>'
1309+ for label , text in calls
1310+ ) + "" .join (f"<dt>{ label } </dt><dd>{ md (text )} </dd>" for label , text in prose )
12641311 if body :
12651312 body = f"<dl>{ body } </dl>"
12661313 return (
@@ -1516,7 +1563,9 @@ def main():
15161563 )
15171564 out .write_text (SHELL + page if args .standalone else page , encoding = "utf-8" )
15181565
1519- label , recommendation , limits , owed = assessment (session , beats , problems_by_n , all_problems )
1566+ label , recommendation , limits , owed = assessment (
1567+ session , beats , problems_by_n , all_problems , walking = args .live ,
1568+ )
15201569 print (f"{ label } : { recommendation } " , file = sys .stderr )
15211570 print (f"{ owed } decision{ 's' if owed != 1 else '' } owed" , file = sys .stderr )
15221571 for limit in limits :
0 commit comments