feat(opencode): the OpenCode harness, checked against its goldens

The tool source generated from the schema, the home with its view, the
shared and per-process environments, the permission rules, the commands,
the warm-up under the supervisor, the event stream and the
classification, its patterns as hand-written scanners. The goldens record
the answer for each, a warm-up against a fake opencode included.

A timed-out supervised command now gets SIGTERM, so the supervisor takes
its tree down (SIGKILL 10 s later); any other child gets SIGKILL. :Json
gains DumpCompact.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Jorijn van der Graaf 2026-10-31 08:32:51 +01:00
commit 5253fe3aff
Signed by: jorijnvdgraaf
GPG key ID: 2937E59CDCC1BCFB
10 changed files with 1946 additions and 7 deletions

View file

@ -324,7 +324,9 @@ namespace Crafter::Review::Process {
if (deadline) {
const auto left = std::chrono::duration_cast<std::chrono::milliseconds>(*deadline - std::chrono::steady_clock::now()).count();
if (left <= 0) {
Signal(child.pid, SIGKILL);
// A supervisor takes the whole tree down on SIGTERM; any
// other child gets SIGKILL.
Signal(child.pid, argv.size() > 1 && argv[1] == SuperviseVerb ? SIGTERM : SIGKILL);
captured.timedOut = true;
break;
}
@ -360,7 +362,12 @@ namespace Crafter::Review::Process {
for (const std::int32_t fd : { child.input, child.output, child.error }) {
if (fd >= 0) close(fd);
}
captured.returnCode = Wait(child.pid).value_or(-1);
std::optional<std::int32_t> code = Wait(child.pid, captured.timedOut ? std::optional(std::chrono::milliseconds(10000)) : std::nullopt);
if (!code) {
Signal(child.pid, SIGKILL);
code = Wait(child.pid);
}
captured.returnCode = code.value_or(-1);
return captured;
}