55import json
66import re
77import time
8+ from collections import Counter
89from dataclasses import dataclass , replace
910from pathlib import Path
1011
2425from .models import ModelUnavailable
2526
2627
28+ def validate_container (data : dict ) -> None :
29+ if not isinstance (data , dict ) or not isinstance (data .get ("sections" ), list ) or len (data ["sections" ]) > len (FACETS ):
30+ raise ValueError ("sections must list at most seven depth facets" )
31+
32+
2733@dataclass
2834class _KnowledgeDepth (_Knowledge ):
2935 STAGE = "knowledge-deepen"
@@ -43,7 +49,7 @@ def _init_identity(self) -> str:
4349 ":subscription-generator" if self .rt .subscription_generator else "" )
4450
4551 def _input_options (self ) -> dict :
46- return {** super ()._input_options (), "depth_version" : 1 }
52+ return {** super ()._input_options (), "depth_version" : 2 }
4753
4854 def _base_for_run (self , latest : str ) -> str :
4955 previous = InitRecord .load (self .rt .state_dir , self .lifecycle .repo , self .STAGE )
@@ -100,6 +106,9 @@ def _build(self, tree: Path) -> InitRecord:
100106 _ , problems = verified_blocks (self .head [page ], feature , tree , self .record .pin )
101107 if problems :
102108 return self ._blocked (problems + ["refresh stale/edited depth explicitly before extending it" ])
109+ initial = audit_depth (self .head , tree , policy , self .record .pin )
110+ if initial ["problems" ]:
111+ return self ._blocked (initial ["problems" ])
103112 context = DepthContext (tree , inventory (tree , policy ))
104113 # A completed first pass takes priority over repairs, so one difficult
105114 # feature cannot consume the budget before the others get visited.
@@ -211,26 +220,35 @@ def _attempt(self, tree, context, feature, entry):
211220 entry ["status" ] = "extracting"
212221 self .record .save (self .rt .state_dir )
213222 entry ["draft" ] = generate (self .rt , self .budget , self .lifecycle .init , system = SYSTEM_DEPTH ,
214- prompt = prompt (payload ), validate = validate_draft ).data
223+ prompt = prompt (payload ), validate = validate_container ).data
215224 entry ["context_sha256" ] = digest (prompt (payload ))
216225 entry ["status" ] = "extracted"
217226 self .record .save (self .rt .state_dir )
218227 elif entry .get ("context_sha256" ) != digest (prompt (payload )):
219228 raise ValueError ("checkpoint draft context changed" )
220229 data = entry ["draft" ]
221- validate_draft (data )
230+ validate_container (data )
222231 offered = files + docs
223232 blocks = dict (old_blocks )
233+ duplicates = Counter (s .get ("facet" ) for s in data ["sections" ] if isinstance (s , dict ) and isinstance (s .get ("facet" ), str ))
234+ skipped = []
224235 for section in data ["sections" ]:
225- if section ["facet" ] in old_blocks or section ["facet" ] not in payload ["facets" ]:
226- raise ValueError ("draft attempts to rewrite an existing depth facet" )
227- for evidence in section ["evidence" ]:
228- if not any (item ["path" ] == evidence ["path" ] and item ["start" ] <= evidence ["start" ]
229- <= evidence ["end" ] <= item ["end" ] for item in offered ):
230- raise ValueError ("depth evidence crosses an unshown source gap" )
231- blocks [section ["facet" ]] = render_block (feature , section , tree , self .lifecycle .full_name , self .record .pin )
236+ try :
237+ validate_draft ({"sections" : [section ]})
238+ if duplicates [section ["facet" ]] > 1 or section ["facet" ] not in payload ["facets" ]:
239+ raise ValueError ("duplicate or unrequested depth facet" )
240+ for evidence in section ["evidence" ]:
241+ if not any (item ["path" ] == evidence ["path" ] and item ["start" ] <= evidence ["start" ]
242+ <= evidence ["end" ] <= item ["end" ] for item in offered ):
243+ raise ValueError ("depth evidence crosses an unshown source gap" )
244+ if section ["facet" ] == "flow" and any (s ["path" ] not in context .production for s in section ["trace" ]):
245+ raise ValueError ("flow must trace production implementation" )
246+ blocks [section ["facet" ]] = render_block (feature , section , tree , self .lifecycle .full_name , self .record .pin )
247+ except (ValueError , TypeError , KeyError , SyntaxError ) as exc :
248+ skipped .append (str (section .get ("facet" , "invalid" )) + ": " + str (exc ) if isinstance (section , dict ) else str (exc ))
249+ entry ["skipped_facets" ] = skipped
232250 if blocks == old_blocks :
233- raise ModelUnavailable ("no supported new depth facets" )
251+ raise ModelUnavailable ("no supported new depth facets; " + "; " . join ( skipped ) )
234252 front = _page_frontmatter (_one_line (feature .title ) + ":实现深读" , kind = "architecture" ,
235253 today = self .today , tags = self .tags )
236254 related = f"[功能概览]({ Path (feature .page ).name } ) · [owner 入口](_index.md)\n \n "
@@ -260,7 +278,7 @@ def _attempt(self, tree, context, feature, entry):
260278 self .record .verdicts [key ] = entry ["checked" ]
261279 entry ["shown_source_files" ] = sorted ({f ["path" ] for f in files })
262280 entry ["status" ] = label
263- entry ["reason" ] = json .dumps (reasons , ensure_ascii = False )
281+ entry ["reason" ] = json .dumps ({ "review" : reasons , "unsupported_facets" : skipped } , ensure_ascii = False )
264282 entry .pop ("draft" , None )
265283 entry .pop ("checked" , None )
266284 if label != "pass" :
0 commit comments