From bd2081bea43040d3db40aa8e019deaff2658a116 Mon Sep 17 00:00:00 2001 From: Rod Vince Date: Thu, 8 Oct 2026 16:42:08 -0600 Subject: [PATCH] =?UTF-8?q?test:=20the=20guardian=20of=20repair=20hints=20?= =?UTF-8?q?provokes=20the=20phase=20=C2=ABcollaborators=C2=BB=20and=20foll?= =?UTF-8?q?ows=20its=20hint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DevTools 0.44.2 added a phase to its landing gate: «collaborators» — an operation whose run() the house cannot hand what it works through is refused when its code lands. This guardian reads the phases from the installed source and fails by name when it has no judge to provoke one, so main went red the moment Composer resolved 0.44.2: nine failures, all in this class. The runtime already writes the hint for that phase (#758): the cure is an edit of the plugin class, not a corrected file. What was missing is here, in the fixture: - the stand-in for the construction probe answers the way the probe does for such an operation; - a test class is not judged by it, as it is not by the other two judges that build or run; - the guardian follows the hint it gets — a plain edit of the plugin class, which the house now has — and the house runs that call for a seat and for the terminal. --- ...RepairHintNamesACallTheHouseAdmitsTest.php | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/tests/Agent/ARepairHintNamesACallTheHouseAdmitsTest.php b/tests/Agent/ARepairHintNamesACallTheHouseAdmitsTest.php index 82a38509..93c3e343 100644 --- a/tests/Agent/ARepairHintNamesACallTheHouseAdmitsTest.php +++ b/tests/Agent/ARepairHintNamesACallTheHouseAdmitsTest.php @@ -107,7 +107,7 @@ public function testTheCallAHintNamesIsOneTheHouseRuns(string $phase, string $su $rejection = $this->reject($house, $phase, $subject, $door); if ($rejection === null) { // Not every judge sees every subject: a test class is a judge, so nothing builds or runs it. - self::assertContains([$phase, $subject], [['container', 'test'], ['behavior', 'test']], "{$phase} did not reject a {$subject}"); + self::assertContains([$phase, $subject], [['container', 'test'], ['collaborators', 'test'], ['behavior', 'test']], "{$phase} did not reject a {$subject}"); return; } @@ -124,6 +124,11 @@ public function testTheCallAHintNamesIsOneTheHouseRuns(string $phase, string $su $followed = $house['door']->call('edit', ['plugin' => 'Blog', 'class' => $class, 'source' => ['session' => self::SESSION, 'seq' => $rejection['seq'], 'sha256' => $named[1]], 'edits' => $repair], $house['caller']); + } elseif (str_contains($hint, 'Make the edit this refusal writes out on the plugin class — a plain edit with find and replace, and no source')) { + // THE CURE IS NOT IN THE FILE (greenhouse evidence/1156): the hint names an edit of the PLUGIN class, a call + // of its own that lands first. That edit is the call the house has to run. + $followed = $house['door']->call('edit', ['plugin' => 'Blog', 'class' => 'Blog', + 'edits' => [['find' => self::BOOT, 'replace' => self::BOOT . ' the clock is registered here']]], $house['caller']); } elseif (preg_match('/complete corrected file with implement\b/', $hint) === 1) { $followed = $house['door']->call('implement', ['plugin' => 'Blog', 'class' => $class, 'content' => str_replace(self::defect($phase), self::REPAIRS[$phase], self::proposal($phase, $subject))], $house['caller']); @@ -162,10 +167,14 @@ public function testAHintForARecordedSourceIsWrittenOnlyWhenTheRecordedDoorAdmit 'edits' => [['find' => self::defect('container'), 'replace' => self::REPAIRS['container']]]], $terminal['caller']); } + /** The line of the plugin class a cure that is not in the file edits. */ + private const BOOT = '// boot:'; + private const REPAIRS = [ 'syntax' => 'public function index(): void {}', 'static-analysis' => '// conforms', 'container' => '// asks for nothing', + 'collaborators' => '// is handed what it works through', 'behavior' => '// does it', ]; @@ -240,6 +249,8 @@ private function house(array $scopes): array mkdir(\dirname($root . '/' . $path), 0o700, true); file_put_contents($root . '/' . $path, "temporary[] = $judges; mkdir($judges, 0o700); @@ -265,9 +276,14 @@ private function house(array $scopes): array [, , $root, $class] = $argv; $file = $root . '/src/Plugins/Blog/Controllers/' . substr((string) strrchr($class, '\\'), 1) . '.php'; $red = is_file($file) && str_contains((string) file_get_contents($file), '// RED:container'); + // What the probe says of an operation whose run() the house cannot hand what it works through (DevTools + // 0.44.2, greenhouse evidence/1154): the class is built, and the entry that lists it resolves nothing. + $unhanded = is_file($file) && str_contains((string) file_get_contents($file), '// RED:collaborators'); echo json_encode(['booted' => true, 'routes' => is_file($file) ? ['GET /blog'] : [], 'built' => !$red, 'error' => 'ContainerResolutionException: Cannot resolve parameter $container', - 'unresolvable' => [['parameter' => '$container', 'type' => 'Milpa\\Interfaces\\Di\\DIContainerInterface']]]), "\n"; + 'unresolvable' => [['parameter' => '$container', 'type' => 'Milpa\\Interfaces\\Di\\DIContainerInterface']]] + + ($unhanded ? ['operation' => ['name' => 'blog:posts', 'handed' => false, 'unhanded' => [ + ['type' => 'App\\Services\\Clock', 'error' => 'Service "App\\Services\\Clock" is not registered in the container.']]]] : [])), "\n"; PHP); chmod($judges . '/probe', 0o700); $runner = $judges . '/trial-run.php';