fix(runner): the BYOK refusal steers — a dispatched job is never stranded

The BYOK refusal path (engineFor came up empty) only flipped the job
record to error and returned: no steer. But the teacher had already
dispatched — the ack promised a result as a new message — so the lesson
stalled at the dock card with the turn over and the teacher waiting
forever on a brief that would never arrive. Every other failed job
steers (the research/demo/visual/pdf catch paths all deliver the kind's
failure shape through finishJob); the refusal now follows the SAME
convention:

- record the honest error (envelope wording + missing tier) WITHOUT
  flipping status — finalizeJob owns the terminal flip after delivery;
- build the kind's failure result (Research failed:/FAILED:/PDF failed:
  shapes the isSubagentFailure heuristics read) and steer it through
  finishJob, so the wake pipeline resumes the lesson and the teacher
  reads the refusal and tells the learner.

Also cleaned the stale engineFor doc (the model-slots-era block still
said 'factory-pin chain ... absent → config pins' — two comments had
accumulated on the field; merged into one BYOK-accurate block).

Platform's refusal test pinned the old no-steer semantics; updated to
assert the new convention (steer delivered, record flipped by
finalizeJob after). The other piEngineFor callers (teacher-agent,
pi-run, visual-maker, pdf-reader) are deliberate honest-thrower
fallbacks (opts.engine ?? piEngineFor(role)) — missed-injection
detectors, kept as-is.
This commit is contained in:
amosblomqvist
2026-09-22 17:41:07 +03:00
parent eaa963f98b
commit 4d5b38b540
2 changed files with 54 additions and 18 deletions
+13 -1
View File
@@ -1373,9 +1373,21 @@ describe("BYOK job refusal (no factory floor)", () => {
await runner.runJob(job.id);
const done = jobStore.getJob(job.id)!;
// The refusal lands through the SAME convention as every other
// failed job (BYOK review fix, Teacher stream 2026-09-22): the kind's
// failure result steers the teacher — a dispatched job is never
// stranded waiting for a result that will never come — and
// finalizeJob flips the record to error after the delivery.
assert.equal(done.status, "error", "the job lands error, never a phantom run");
assert.match(done.error ?? "", /No model configured/);
assert.match(done.error ?? "", /missing: cheap/);
assert.equal(delivery.steers.length, 0, "no result steer — nothing ran");
assert.equal(delivery.steers.length, 1, "the refusal STEERED — the teacher is not stranded");
const text = delivery.steers[0]!.parts[0];
assert.ok(
text.type === "text" && text.text.includes("Research failed: No model configured"),
"the steer carries the kind's failure shape",
);
const result = done.result as ResearchResult;
assert.equal(result.sources.length, 0, "the failure result matches the research convention");
});
});