2626from __future__ import annotations
2727
2828import copy
29+ import hashlib
2930import json
3031import re
3132from dataclasses import dataclass
3940from .init_stages import (
4041 _SLUG , ROUTES_NAME , _fence , _one_line , _page_frontmatter , _slug , _Stage , _title_of , review_route_line ,
4142)
42- from .init_support import InitError , InitRecord , generate
43+ from .init_support import InitError , InitRecord , classify_verdict , generate , judge
44+ from .models import ModelUnavailable
4345
4446MAX_CARD_FILES = 200
4547MAX_CARD_BYTES = 40_000
8890"""
8991
9092
91- def _implementation_payload (payload : dict , index , pin : str ) -> dict :
93+ def _implementation_payload (payload : dict , index , pin : str , * , max_bytes : int = MAX_CARD_BYTES ) -> dict :
9294 """Share the card cap fairly; unread or cropped code never becomes whole-file evidence."""
9395 if index .identity .get ("pin" ) != pin :
9496 raise ValueError ("module implementation index differs from the fixed source SHA" )
@@ -109,7 +111,7 @@ def _implementation_payload(payload: dict, index, pin: str) -> dict:
109111 def size ():
110112 return len (_fence (out ).encode ("utf-8" ))
111113
112- if size () > MAX_CARD_BYTES :
114+ if size () > max_bytes :
113115 raise ValueError ("module metadata exceeds the implementation card byte cap; no evidence dispatched" )
114116
115117 def ranges (lines , count ):
@@ -126,7 +128,7 @@ def ranges(lines, count):
126128
127129 for offset , (item , lines ) in enumerate (readable ):
128130 before = size ()
129- allocation = (MAX_CARD_BYTES - before ) // (len (readable ) - offset )
131+ allocation = (max_bytes - before ) // (len (readable ) - offset )
130132 limit = before + allocation
131133 item .update (status = "complete" , ranges = ranges (lines , len (lines )))
132134 if size () <= limit :
@@ -360,7 +362,7 @@ class _Modules(_Stage):
360362 STAGE = "modules"
361363
362364 def _input_options (self ) -> dict :
363- return {"module_prompt_version" : 2 } if self .rt .unlimited_subscription else {}
365+ return {"module_prompt_version" : 3 } if self .rt .unlimited_subscription else {}
364366
365367 def _build (self , tree : Path ) -> InitRecord :
366368 routes_path = f"{ self .repo_dir } /{ ROUTES_NAME } "
@@ -602,13 +604,50 @@ def items(name: str, allowed: set[str], text_key: str) -> list[tuple[str, str]]:
602604 group = _slug (PurePosixPath (key .rstrip ("/" )).name or self .lifecycle .repo )
603605 headings = {k : _one_line ((data .get ("headings" ) or {}).get (k )).replace ("*" , "" ) or v
604606 for k , v in _DEFAULT_HEADINGS .items ()}
605- return _Card (
607+ card = _Card (
606608 module = key , prefixes = [], title = _one_line (data ["title" ]),
607609 purpose = self ._d5_prose (str (data .get ("purpose" ) or "" )),
608610 entry_points = items ("entry_points" , in_module , "what" ), key_files = items ("key_files" , in_module , "what" ),
609611 docs = items ("docs" , doc_paths , "why" ),
610612 signals = [s for s in (_one_line (x , 60 ) for x in data .get ("signals" ) or []) if s ][:MAX_ITEMS ],
611613 group = group , group_title = _one_line (data .get ("group_title" )) or group , headings = headings )
614+ if self .rt .unlimited_subscription and not self ._review_card (card , listed ):
615+ return None
616+ return card
617+
618+ def _review_card (self , card : _Card , listed : list [dict ]) -> bool :
619+ """One independent source review; a rejected card cannot halt other modules."""
620+ from ..knowledge_service .l1 import Block
621+
622+ referenced = {path for path , _ in card .entry_points + card .key_files }
623+ page = f"{ self .root } /{ card .group } /module-review.md"
624+ text = self ._card_text (card )
625+ text_sha = hashlib .sha256 (text .encode ()).hexdigest ()
626+ key = f"module:{ card .module } "
627+ payload = {"repository" : self .lifecycle .full_name , "module" : card .module ,
628+ "files" : [entry for entry in listed if entry ["path" ] in referenced ],
629+ "doc_files" : [path for path , _ in card .docs ],
630+ "source_scope" : "Only shown implementation ranges support behavior. Directory names do not "
631+ "establish runtime roles; default side effects require shown configuration and branches. "
632+ "Printed/manual instructions do not establish execution; unshown steps remain unknown." }
633+ # Reserve the actual card text and the existing judge envelope/indentation.
634+ allowance = MAX_CARD_BYTES - len (_fence ({"card" : text }).encode ()) - 2048
635+ try :
636+ evidence = _implementation_payload (payload , self .source_index , self .record .pin , max_bytes = allowance )
637+ verdict = judge (self .rt , self .budget , self .lifecycle .init ,
638+ Block ("prose" , page , "" , "prose" , text_sha ), base = {}, head = {page : text },
639+ evidence = [evidence ])
640+ label , dimensions , reasons , model = (classify_verdict (verdict ), verdict .dimensions ,
641+ verdict .reasons , verdict .model )
642+ except (ValueError , BudgetExhausted , ModelUnavailable ) as exc :
643+ label , dimensions , reasons , model = "unjudged" , {}, {"source_review" : str (exc )}, ""
644+ self .record .verdicts [key ] = {"kind" : "module_prose" , "page" : page , "verdict" : label , "dimensions" : dimensions ,
645+ "reasons" : reasons , "model" : model , "text_sha" : text_sha }
646+ if label != "pass" :
647+ self .record .unfinished .append (f"module { card .module } : source review { label } ; card remains unknown" )
648+ self .record .dropped .append ({"rule_id" : key , "module" : card .module ,
649+ "why" : "source review: " + json .dumps (reasons )})
650+ return label == "pass"
612651
613652 def _card_text (self , card : _Card ) -> str :
614653 """The card page. Sections are bold labels, not headings: the
0 commit comments