@@ -164,6 +164,16 @@ def _git_tree_id(root: Path, spec: str) -> str | None:
164164 return result .stdout .strip () or None
165165
166166
167+ def _git_is_ancestor (root : Path , ancestor : str , descendant : str ) -> bool :
168+ result = subprocess .run (
169+ ["git" , "-C" , str (root ), "merge-base" , "--is-ancestor" , ancestor , descendant ],
170+ capture_output = True ,
171+ text = True ,
172+ check = False ,
173+ )
174+ return result .returncode == 0
175+
176+
167177def _handoff_recorded_identities (handoff : dict [str , object ]) -> list [str ]:
168178 identities : list [str ] = []
169179 review = handoff .get ("acceptance_review" ) if isinstance (handoff .get ("acceptance_review" ), dict ) else {}
@@ -206,8 +216,9 @@ def _verified_handoff_tree(root: Path, handoff: dict[str, object]) -> str | None
206216def _material_repository_root (
207217 args : argparse .Namespace ,
208218 validated : list [tuple [dict [str , object ], dict [str , object ]]],
219+ commands : list [str ],
209220) -> Path :
210- roots : set [ Path ] = set ()
221+ entries : list [ tuple [ Path , str ]] = []
211222 for handoff , brief in validated :
212223 if not _handoff_has_material_changes (handoff , brief ):
213224 continue
@@ -216,13 +227,44 @@ def _material_repository_root(
216227 if not isinstance (repository , dict ):
217228 continue
218229 recorded = str (repository .get ("root" ) or "" ).strip ()
219- if recorded :
220- roots .add (Path (recorded ).expanduser ().resolve ())
221- if len (roots ) > 1 :
230+ metadata = repository .get ("metadata" ) if isinstance (repository .get ("metadata" ), dict ) else {}
231+ identity = str (metadata .get ("actual_commit" ) or "" ).strip ()
232+ if recorded and identity :
233+ entries .append ((Path (recorded ).expanduser ().resolve (), identity ))
234+ if not entries :
235+ return project_root (args )
236+ acceptance_entries : list [tuple [Path , str ]] = []
237+ for handoff , _brief in validated :
238+ if not any (_handoff_command_result (handoff , command ) == "passed" for command in commands ):
239+ continue
240+ repositories = handoff .get ("repository" ) if isinstance (handoff .get ("repository" ), list ) else []
241+ for repository in repositories :
242+ if not isinstance (repository , dict ):
243+ continue
244+ recorded = str (repository .get ("root" ) or "" ).strip ()
245+ metadata = repository .get ("metadata" ) if isinstance (repository .get ("metadata" ), dict ) else {}
246+ identity = str (metadata .get ("actual_commit" ) or "" ).strip ()
247+ if recorded and identity :
248+ acceptance_entries .append ((Path (recorded ).expanduser ().resolve (), identity ))
249+ fresh_acceptance_roots = {
250+ root
251+ for root , identity in acceptance_entries
252+ if _git_tree_id (root , "HEAD" ) == _git_tree_id (root , identity )
253+ }
254+ if len (fresh_acceptance_roots ) == 1 :
255+ return next (iter (fresh_acceptance_roots ))
256+ terminal : list [tuple [Path , str ]] = []
257+ identities = {identity for _root , identity in entries }
258+ for root , identity in entries :
259+ if _git_tree_id (root , identity ) and all (_git_is_ancestor (root , other , identity ) for other in identities ):
260+ terminal .append ((root , identity ))
261+ terminal_identities = {identity for _root , identity in terminal }
262+ if len (terminal_identities ) == 1 :
263+ identity = next (iter (terminal_identities ))
264+ return next (root for root , candidate in terminal if candidate == identity )
265+ if len ({root for root , _identity in entries }) > 1 :
222266 raise SystemExit ("acceptance-blocked: final plan repository is ambiguous" )
223- if roots :
224- return next (iter (roots ))
225- return project_root (args )
267+ return entries [0 ][0 ]
226268
227269
228270def _acceptance_result_detail (results : set [str ]) -> str :
@@ -313,7 +355,7 @@ def _assert_archive_plan_acceptance(
313355 commands = _declared_integration_commands (body )
314356 if not commands :
315357 return
316- git_root = _material_repository_root (args , validated )
358+ git_root = _material_repository_root (args , validated , commands )
317359 terminal_tree = _git_tree_id (git_root , "HEAD" )
318360 material = [pair for pair in validated if _handoff_has_material_changes (* pair )]
319361 for command in commands :
0 commit comments