@ggui-ai/ui-gen 0.5.0-rc.0 → 0.5.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/dist/adapters/generation-dispatch.d.ts +1 -1
  2. package/dist/adapters/generation-dispatch.js +239 -46
  3. package/dist/adapters/generation-dispatch.js.map +1 -1
  4. package/dist/advanced/index.d.ts +2 -2
  5. package/dist/advanced/index.js +239 -46
  6. package/dist/advanced/index.js.map +1 -1
  7. package/dist/blueprint-validator.js +25 -8
  8. package/dist/blueprint-validator.js.map +1 -1
  9. package/dist/boilerplate.js.map +1 -1
  10. package/dist/check/index.d.ts +1 -1
  11. package/dist/check/index.js.map +1 -1
  12. package/dist/{create-ui-generator-R1jZv_Vk.d.ts → create-ui-generator-CQiyjb5D.d.ts} +1 -1
  13. package/dist/{dispatch-33NUiVIT.d.ts → dispatch-CzRfy1J7.d.ts} +1 -1
  14. package/dist/evaluation/axis-checks/index.d.ts +3 -3
  15. package/dist/evaluation/axis-checks/index.js +18 -0
  16. package/dist/evaluation/axis-checks/index.js.map +1 -1
  17. package/dist/evaluation/axis-checks/registry.d.ts +1 -1
  18. package/dist/evaluation/axis-checks/registry.js +18 -0
  19. package/dist/evaluation/axis-checks/registry.js.map +1 -1
  20. package/dist/evaluation/index.d.ts +2 -2
  21. package/dist/evaluation/index.js +18 -0
  22. package/dist/evaluation/index.js.map +1 -1
  23. package/dist/harness/check/runtime-render/index.d.ts +1 -1
  24. package/dist/harness/check/runtime-render/index.js +14 -7
  25. package/dist/harness/check/runtime-render/index.js.map +1 -1
  26. package/dist/harness/index.d.ts +1 -1
  27. package/dist/harness/index.js +6 -4
  28. package/dist/harness/index.js.map +1 -1
  29. package/dist/harness/result-types.d.ts +1 -1
  30. package/dist/harness/runtime.d.ts +1 -1
  31. package/dist/harness/runtime.js +11 -6
  32. package/dist/harness/runtime.js.map +1 -1
  33. package/dist/harness/types-public.d.ts +15 -3
  34. package/dist/harness/types-public.js +6 -4
  35. package/dist/harness/types-public.js.map +1 -1
  36. package/dist/harness/validator-trace-sink.d.ts +1 -1
  37. package/dist/index.d.ts +2 -2
  38. package/dist/index.js +239 -46
  39. package/dist/index.js.map +1 -1
  40. package/dist/patch.d.ts +1 -1
  41. package/dist/{types-public-Dl1rJ-yU.d.ts → types-public-D9swfbNi.d.ts} +31 -1
  42. package/dist/workflows.d.ts +1 -1
  43. package/package.json +12 -12
  44. package/src/blueprint-validator.ts +20 -7
  45. package/src/coding-agent/tools.ts +103 -1
  46. package/src/evaluation/axis-checks/extras.ts +34 -0
  47. package/src/evaluation/types-public.ts +32 -0
  48. package/src/harness/check/runtime-render/adapter.ts +22 -17
  49. package/src/harness/check/runtime-render/host-boundary.ts +13 -2
  50. package/src/harness/check/runtime-render/render-check.ts +4 -1
  51. package/src/harness/coding/dupe-break.ts +0 -0
  52. package/src/harness/coding/run-coding-turn.ts +78 -1
  53. package/src/harness/coding/run-eval-round.ts +78 -31
  54. package/src/harness/pitfalls.ts +11 -6
  55. package/src/harness/types-public.ts +15 -2
  56. package/src/run-check.ts +9 -4
@@ -1,7 +1,7 @@
1
1
  import { EvaluationConfig } from '../evaluation/types.js';
2
2
  import { ModelRoles, GenerationResult } from '../harness/result-types.js';
3
3
  import { ProviderName, ToolDefinition, AdapterResult } from './types.js';
4
- import { Q as QualityConfig } from '../types-public-Dl1rJ-yU.js';
4
+ import { Q as QualityConfig } from '../types-public-D9swfbNi.js';
5
5
  import { DataContract, JsonObject, GadgetDescriptor } from '@ggui-ai/protocol';
6
6
  import { R as RenderingContext } from '../prompts-BVhtH3M1.js';
7
7
  import 'zod';
@@ -4100,10 +4100,23 @@ var init_tag_balance = __esm({
4100
4100
  // src/harness/coding/dupe-break.ts
4101
4101
  var dupe_break_exports = {};
4102
4102
  __export(dupe_break_exports, {
4103
+ SAME_EXCHANGE_BREAK_AT: () => SAME_EXCHANGE_BREAK_AT,
4104
+ SAME_EXCHANGE_NUDGE: () => SAME_EXCHANGE_NUDGE,
4103
4105
  computePatchFingerprint: () => computePatchFingerprint,
4104
4106
  createDupeBreakState: () => createDupeBreakState,
4105
- errorClassBucket: () => errorClassBucket
4107
+ errorClassBucket: () => errorClassBucket,
4108
+ recordToolExchange: () => recordToolExchange
4106
4109
  });
4110
+ function recordToolExchange(state, name, input, resultText) {
4111
+ const key = createHash("sha256").update(name).update("\0").update(JSON.stringify(input) ?? "undefined").update("\0").update(resultText).digest("hex");
4112
+ if (state.lastExchangeKey === key) {
4113
+ state.consecutiveIdenticalExchanges += 1;
4114
+ } else {
4115
+ state.lastExchangeKey = key;
4116
+ state.consecutiveIdenticalExchanges = 1;
4117
+ }
4118
+ return state.consecutiveIdenticalExchanges;
4119
+ }
4107
4120
  function createDupeBreakState() {
4108
4121
  return {
4109
4122
  lastFailedPatchFingerprint: null,
@@ -4118,7 +4131,9 @@ function createDupeBreakState() {
4118
4131
  diagnosticBrokeLoopCount: 0,
4119
4132
  recentFailedPatches: [],
4120
4133
  consecutiveBrokenApplies: 0,
4121
- consecutiveNoToolCalls: 0
4134
+ consecutiveNoToolCalls: 0,
4135
+ lastExchangeKey: null,
4136
+ consecutiveIdenticalExchanges: 0
4122
4137
  };
4123
4138
  }
4124
4139
  function errorClassBucket(errText) {
@@ -4143,8 +4158,11 @@ function computePatchFingerprint(changes, errText) {
4143
4158
  h.update(errBucket);
4144
4159
  return h.digest("hex");
4145
4160
  }
4161
+ var SAME_EXCHANGE_BREAK_AT, SAME_EXCHANGE_NUDGE;
4146
4162
  var init_dupe_break = __esm({
4147
4163
  "src/harness/coding/dupe-break.ts"() {
4164
+ SAME_EXCHANGE_BREAK_AT = 3;
4165
+ SAME_EXCHANGE_NUDGE = "\n\n[harness] This is the SAME tool call with the SAME result as your previous turn \u2014 repeating it cannot produce new information. Use what you already have and emit an apply_changes (or scoped) call now; one more identical repeat terminates the run.";
4148
4166
  }
4149
4167
  });
4150
4168
 
@@ -10490,15 +10508,17 @@ async function runCheck(input) {
10490
10508
  (i) => i.result === "fail" && (i.tier === 0 || i.tier === void 0)
10491
10509
  ).length;
10492
10510
  if (cheapTier0Failures === 0) {
10493
- firedIds.push(check.runtimeRender.id);
10494
- const runtimeIssues = await check.runtimeRender.run({
10511
+ const outcome = await check.runtimeRender.run({
10495
10512
  sourceCode,
10496
10513
  compiledCode,
10497
10514
  contract,
10498
10515
  fixtureProps: input.fixtureProps
10499
10516
  });
10500
- issues.push(...runtimeIssues);
10501
- runtimeRenderIssueCount = runtimeIssues.length;
10517
+ if (outcome.status === "ran") {
10518
+ firedIds.push(check.runtimeRender.id);
10519
+ issues.push(...outcome.issues);
10520
+ runtimeRenderIssueCount = outcome.issues.length;
10521
+ }
10502
10522
  }
10503
10523
  }
10504
10524
  let llmIssueCount = 0;
@@ -13003,13 +13023,18 @@ var PITFALLS = [
13003
13023
  rule: "All design-system primitives are pre-imported by the boilerplate \u2014 do NOT add imports unless absolutely needed.",
13004
13024
  why: "Duplicate imports cause TS errors; the boilerplate already resolves the full primitive surface.",
13005
13025
  foundIn: "pre-#61 baseline"
13006
- },
13007
- {
13008
- id: "stepper-display-only",
13009
- rule: "`Stepper` is display-only: `steps` is a top-level `const` array and `current` is YOUR `useState` index \u2014 Stepper never stores or advances the step itself.",
13010
- why: "Data-vs-behavior: navigation state lives in generated code. The steps-as-state failure mode (useState-wrapped step arrays, expecting Stepper to self-advance) crashes with 'function is not iterable' or dead Next buttons.",
13011
- foundIn: "#306 composite-roster wave (multi-step fragment failure classes)"
13012
13026
  }
13027
+ // ─── stepper-display-only: retired to the CHECK leg (Exp 47 P2) ─────────
13028
+ //
13029
+ // The 6th always-on pitfall ("Stepper is display-only…") was added with
13030
+ // the #306 roster wave, but D8 measured Stepper adoption at 17/18 from
13031
+ // fragment + JSDoc alone — the always-on rule was demonstrably not
13032
+ // load-bearing, and exp66's factorial showed pitfall COUNT (5→6+) is
13033
+ // the dilution-sensitive lever (~+9s blended). Its guidance now ships
13034
+ // as the fix string of the `layout.multi_step.stepper_adopted` axis
13035
+ // check (evaluation/axis-checks/extras.ts) — delivered only to the
13036
+ // multi-step cells that actually violate it.
13037
+ //
13013
13038
  // ─── Provisional pitfalls considered + retired by exp66 n=6 factorial ───
13014
13039
  //
13015
13040
  // The 3 "new" pitfalls below were added in exp61 after OpenAI kanban
@@ -14082,6 +14107,23 @@ var multiStepHasState = {
14082
14107
  ];
14083
14108
  }
14084
14109
  };
14110
+ var stepperAdopted = {
14111
+ id: "layout.multi_step.stepper_adopted",
14112
+ axis: "layout",
14113
+ values: ["multi-step"],
14114
+ run(input) {
14115
+ if (input.compiledCode === null) return [];
14116
+ if (/<Stepper[\s/>]/.test(input.sourceCode)) return [];
14117
+ return [
14118
+ mkIssue2(
14119
+ "layout.multi_step.stepper_adopted",
14120
+ "Classified as layout=multi-step but the design system's Stepper composite is never rendered.",
14121
+ "Render <Stepper steps={STEPS} current={step} /> above the step body \u2014 STEPS is a top-level const array of step labels and `step` is your own useState index. Stepper is display-only: it never stores or advances the step itself.",
14122
+ "fail"
14123
+ )
14124
+ ];
14125
+ }
14126
+ };
14085
14127
  var mixedStreamsHaveHandlers = {
14086
14128
  id: "realtime.mixed.handlers_per_event",
14087
14129
  axis: "realtime",
@@ -14107,6 +14149,7 @@ var EXTRA_CHECKS = [
14107
14149
  swipeTriggerWired,
14108
14150
  composeCrossEntity,
14109
14151
  multiStepHasState,
14152
+ stepperAdopted,
14110
14153
  mixedStreamsHaveHandlers
14111
14154
  ];
14112
14155
 
@@ -14566,9 +14609,9 @@ function toMinimalElement(v) {
14566
14609
  "[runtime-render] host value lacks the element surface (tagName/getAttribute/querySelectorAll)"
14567
14610
  );
14568
14611
  }
14569
- function buildClickUser(userEventModule) {
14612
+ function buildClickUser(userEventModule, doc) {
14570
14613
  const setup = getMethod(userEventModule, "setup");
14571
- const instance = setup ? setup() : userEventModule;
14614
+ const instance = setup ? setup({ document: doc }) : userEventModule;
14572
14615
  const click = getMethod(instance, "click");
14573
14616
  if (!click) {
14574
14617
  throw new Error(
@@ -14767,7 +14810,7 @@ async function runRenderCheck(input) {
14767
14810
  return finalize(issues, t0, 0, 0);
14768
14811
  }
14769
14812
  const container = toMinimalElement(renderResult.container);
14770
- const user = buildClickUser(userEvent);
14813
+ const user = buildClickUser(userEvent, hostGlobals().document);
14771
14814
  let actionsChecked = 0;
14772
14815
  let streamsChecked = 0;
14773
14816
  try {
@@ -15457,8 +15500,12 @@ var DEFAULT_RUNTIME_RENDER_CHECK = {
15457
15500
  id: "runtime-render",
15458
15501
  run: async (input) => {
15459
15502
  const { sourceCode, compiledCode, contract, fixtureProps } = input;
15460
- if (compiledCode === null) return [];
15461
- if (!contract) return [];
15503
+ if (compiledCode === null) {
15504
+ return { status: "not-applicable", issues: [], reason: "no compiled code" };
15505
+ }
15506
+ if (!contract) {
15507
+ return { status: "not-applicable", issues: [], reason: "no contract surface" };
15508
+ }
15462
15509
  const mockup = prepareMockupProps({ contract, fixtureProps });
15463
15510
  let result;
15464
15511
  try {
@@ -15472,9 +15519,12 @@ var DEFAULT_RUNTIME_RENDER_CHECK = {
15472
15519
  console.warn(
15473
15520
  `[runtime-render] probe skipped \u2014 infra failure: ${message}`
15474
15521
  );
15475
- return [];
15522
+ return { status: "infra-skipped", issues: [], reason: message };
15476
15523
  }
15477
- return result.issues.map(toEvalIssue).filter((x) => x !== null);
15524
+ return {
15525
+ status: "ran",
15526
+ issues: result.issues.map(toEvalIssue).filter((x) => x !== null)
15527
+ };
15478
15528
  }
15479
15529
  };
15480
15530
  var CLASS_SPECIFIC_PATTERNS = [
@@ -15881,6 +15931,16 @@ async function executeTool(workspace, tool, input, commitMeta, contract, applyPa
15881
15931
  error: true
15882
15932
  };
15883
15933
  }
15934
+ const gutterLine = normalizedChanges.flatMap((c) => c.code).find((l) => /^\s*\d+(?::[0-9a-f]{2})?│/.test(l));
15935
+ if (gutterLine !== void 0) {
15936
+ console.log(
15937
+ `[coding-agent] apply_changes: GUTTER_TRANSCRIBED | ${JSON.stringify(gutterLine.slice(0, 60))}`
15938
+ );
15939
+ return {
15940
+ result: `PATCH_INVALID: your \`code\` contains the line-number gutter from the Current File view (e.g. ${JSON.stringify(gutterLine.trim().slice(0, 40))}). The \`N\u2502\` prefix is display decoration \u2014 \`code\` must be raw file content only. Re-emit the patch without the gutter.`,
15941
+ error: true
15942
+ };
15943
+ }
15884
15944
  const { defaultApplyPatch: defaultApplyPatch2 } = await Promise.resolve().then(() => (init_patch(), patch_exports));
15885
15945
  const patcher = applyPatch2 ?? defaultApplyPatch2;
15886
15946
  const patchResult = await patcher({
@@ -15956,6 +16016,16 @@ async function executeTool(workspace, tool, input, commitMeta, contract, applyPa
15956
16016
 
15957
16017
  Patch tag-balance imbalance: ${tagImbalanceSummary}
15958
16018
  (Net opens vs closes inside your edit ranges. If a tag has +N, you opened N more than you closed within the patch \u2014 verify each <Tag> has a matching </Tag> in your changes, or that the surrounding scaffold provides the closer.)` : "";
16019
+ const contextAddendum = buildRangeContextAddendum(
16020
+ errLine,
16021
+ changes,
16022
+ currentFile.split("\n")
16023
+ );
16024
+ if (contextAddendum.length > 0) {
16025
+ console.log(
16026
+ `[coding-agent] apply_changes: RANGE_CONTEXT_DIAG | line=${errLine} | ${JSON.stringify(contextAddendum.slice(0, 80))}`
16027
+ );
16028
+ }
15959
16029
  return {
15960
16030
  result: `${preflightPrefix}PATCH_APPLIED_BROKEN: patch applied but file has a syntax error. Workspace updated; no git commit yet.
15961
16031
  Changed ranges: ${ranges}
@@ -15964,7 +16034,7 @@ esbuild error: ${errText}${typeof errLine === "number" ? ` (line ${errLine}${typ
15964
16034
  ${sliceLabel}:
15965
16035
  ${slice}
15966
16036
 
15967
- The file now reflects your latest patch. Submit a follow-up apply_changes targeting the error location above to converge toward a compilable file.${imbalanceHint}`,
16037
+ The file now reflects your latest patch. Submit a follow-up apply_changes targeting the error location above to converge toward a compilable file.${contextAddendum}${imbalanceHint}`,
15968
16038
  error: false
15969
16039
  };
15970
16040
  }
@@ -16059,9 +16129,42 @@ ${content}` };
16059
16129
  return { result: `Unknown tool: ${tool}`, error: true };
16060
16130
  }
16061
16131
  }
16132
+ function buildRangeContextAddendum(errLine, changes, originalLines) {
16133
+ if (typeof errLine !== "number") return "";
16134
+ const hit = changes.find((c) => c.startLine === errLine);
16135
+ if (!hit) return "";
16136
+ const firstPayloadLine = hit.code.find((l) => l.trim().length > 0) ?? "";
16137
+ const declMatch = /^\s*(const|let|var|function|type|interface)\b/.exec(
16138
+ firstPayloadLine
16139
+ );
16140
+ if (declMatch) {
16141
+ for (let i = hit.startLine - 2; i >= 0; i--) {
16142
+ const line = originalLines[i];
16143
+ if (line === void 0 || line.trim().length === 0) continue;
16144
+ if (/\breturn\s*\($/.test(line.trim())) {
16145
+ return `
16146
+
16147
+ Range/context mismatch: line ${i + 1} is \`return (\` \u2014 your range starts at ${hit.startLine}, INSIDE the returned JSX expression, but your payload begins with a declaration (\`${declMatch[1]}\`). Declarations cannot live inside JSX: start the range at or before line ${i + 1} and re-emit \`return (\` after them.`;
16148
+ }
16149
+ break;
16150
+ }
16151
+ }
16152
+ if (hit.code.some((l) => l.includes("export default function"))) {
16153
+ const existing = originalLines.findIndex(
16154
+ (l) => l.includes("export default function")
16155
+ );
16156
+ if (existing >= 0 && existing + 1 < hit.startLine) {
16157
+ return `
16158
+
16159
+ Range/context mismatch: your payload emits a second \`export default function\` but the range starts at line ${hit.startLine}, INSIDE the existing component that opens at line ${existing + 1}. Patch within the existing function body \u2014 do not re-emit the component definition.`;
16160
+ }
16161
+ }
16162
+ return "";
16163
+ }
16062
16164
 
16063
16165
  // src/harness/coding/run-coding-turn.ts
16064
16166
  init_hashline();
16167
+ init_dupe_break();
16065
16168
  function extractFailSignature(resultText) {
16066
16169
  const esbuildMatch = resultText.match(/esbuild error: ([^\n]+)/);
16067
16170
  if (esbuildMatch) return esbuildMatch[1];
@@ -16334,9 +16437,35 @@ ${closingInstruction}`;
16334
16437
  }
16335
16438
  }
16336
16439
  console.log(`[simple] turn ${turnsUsed}: get_available_icons`);
16337
- const iconResult = `Available Lucide icon names (use with <Icon name="...">):
16440
+ let iconResult = `Available Lucide icon names (use with <Icon name="...">):
16338
16441
  ${iconNamesCache}`;
16442
+ let iconRepeats = 0;
16443
+ if (ctx.dupeBreak) {
16444
+ iconRepeats = recordToolExchange(ctx.dupeBreak, call.name, call.input, iconResult);
16445
+ if (iconRepeats === 2) iconResult += SAME_EXCHANGE_NUDGE;
16446
+ }
16339
16447
  await codingAgent.sendToolResult([{ callId: call.id, name: call.name, result: iconResult }]);
16448
+ if (iconRepeats >= SAME_EXCHANGE_BREAK_AT) {
16449
+ console.log(
16450
+ `[coding-agent] SAME_EXCHANGE_BREAK | tool=${call.name} | repeats=${iconRepeats} \u2014 terminating run (ggui#404)`
16451
+ );
16452
+ return {
16453
+ control: "break",
16454
+ compiledCode: "",
16455
+ selfCheckPassed: false,
16456
+ outcome: void 0,
16457
+ phase: void 0,
16458
+ tokens,
16459
+ ...cacheTokens,
16460
+ llmMs,
16461
+ toolMs: 0,
16462
+ lastResultText: iconResult,
16463
+ lastDiffFailed: false,
16464
+ isEvalFeedback: false,
16465
+ iconNamesCache,
16466
+ preWarmedContext
16467
+ };
16468
+ }
16340
16469
  return {
16341
16470
  control: "continue",
16342
16471
  compiledCode: "",
@@ -16488,12 +16617,45 @@ Repeated error: ${errText}
16488
16617
  } else if (ctx.dupeBreak && call.name === "rewrite") {
16489
16618
  if (!result.error) ctx.dupeBreak.consecutiveBrokenApplies = 0;
16490
16619
  }
16620
+ let sameExchangeRepeats = 0;
16621
+ if (ctx.dupeBreak) {
16622
+ sameExchangeRepeats = recordToolExchange(
16623
+ ctx.dupeBreak,
16624
+ call.name,
16625
+ call.input,
16626
+ result.result
16627
+ );
16628
+ if (sameExchangeRepeats === 2) {
16629
+ augmentedResultText += SAME_EXCHANGE_NUDGE;
16630
+ }
16631
+ }
16491
16632
  await codingAgent.sendToolResult([{
16492
16633
  callId: call.id,
16493
16634
  name: call.name,
16494
16635
  result: augmentedResultText,
16495
16636
  isError: !!result.error
16496
16637
  }]);
16638
+ if (sameExchangeRepeats >= SAME_EXCHANGE_BREAK_AT) {
16639
+ console.log(
16640
+ `[coding-agent] SAME_EXCHANGE_BREAK | tool=${call.name} | repeats=${sameExchangeRepeats} \u2014 terminating run (ggui#404)`
16641
+ );
16642
+ return {
16643
+ control: "break",
16644
+ compiledCode: "",
16645
+ selfCheckPassed: false,
16646
+ outcome: void 0,
16647
+ phase: void 0,
16648
+ tokens,
16649
+ ...cacheTokens,
16650
+ llmMs,
16651
+ toolMs,
16652
+ lastResultText: augmentedResultText,
16653
+ lastDiffFailed: false,
16654
+ isEvalFeedback: false,
16655
+ iconNamesCache,
16656
+ preWarmedContext
16657
+ };
16658
+ }
16497
16659
  const turnDiffFailed = !!result.error && result.result.includes("DIFF");
16498
16660
  const isDiffFail = turnDiffFailed && !isPatchInvalid;
16499
16661
  const outcome = result.done ? "PASS" : isPatchInvalid ? "PATCH_INVALID" : isDiffFail ? "DIFF_FAIL" : "SELF_CHECK_FAIL";
@@ -16575,26 +16737,46 @@ async function runProbeAtExit(input) {
16575
16737
  const { harness, sourceCode, compiledCode, contract, fixtureProps } = input;
16576
16738
  const probe = harness.check.runtimeRender;
16577
16739
  if (!probe || !compiledCode) {
16578
- return { fired: false, recoverableFail: false, probeIssues: [] };
16740
+ return {
16741
+ fired: false,
16742
+ recoverableFail: false,
16743
+ probeIssues: [],
16744
+ meta: {
16745
+ status: "not-applicable",
16746
+ reason: probe ? "no compiled code" : "no runtimeRender check configured"
16747
+ }
16748
+ };
16579
16749
  }
16580
- let probeIssues;
16750
+ let outcome;
16581
16751
  try {
16582
- probeIssues = await probe.run({
16752
+ outcome = await probe.run({
16583
16753
  sourceCode,
16584
16754
  compiledCode,
16585
16755
  contract,
16586
16756
  fixtureProps
16587
16757
  });
16588
16758
  } catch (e) {
16589
- console.warn(
16590
- `[simple] in-loop probe at exit: infra failure \u2014 ${e instanceof Error ? e.message : String(e)}`
16591
- );
16592
- return { fired: false, recoverableFail: false, probeIssues: [] };
16759
+ const message = e instanceof Error ? e.message : String(e);
16760
+ console.warn(`[simple] in-loop probe at exit: infra failure \u2014 ${message}`);
16761
+ return {
16762
+ fired: false,
16763
+ recoverableFail: false,
16764
+ probeIssues: [],
16765
+ meta: { status: "infra-skipped", reason: message }
16766
+ };
16593
16767
  }
16768
+ const meta = {
16769
+ status: outcome.status,
16770
+ ...outcome.reason !== void 0 ? { reason: outcome.reason } : {}
16771
+ };
16772
+ if (outcome.status !== "ran") {
16773
+ return { fired: false, recoverableFail: false, probeIssues: [], meta };
16774
+ }
16775
+ const probeIssues = outcome.issues;
16594
16776
  const recoverableFail = probeIssues.some(
16595
16777
  (i) => i.result === "fail" && typeof i.subcategory === "string" && i.subcategory.startsWith("runtime:render-no-throw") && typeof i.description === "string" && isRecoverableRenderCrash(i.description)
16596
16778
  );
16597
- return { fired: true, recoverableFail, probeIssues };
16779
+ return { fired: true, recoverableFail, probeIssues, meta };
16598
16780
  }
16599
16781
  function formatRuntimeProbeFeedback(issue) {
16600
16782
  const subcat = issue.subcategory ? `/${issue.subcategory}` : "";
@@ -16656,7 +16838,7 @@ async function runEvalRound(ctx, input) {
16656
16838
  );
16657
16839
  const lines = runtimeFails.slice(0, MAX_FEEDBACK_ISSUES).map(formatRuntimeProbeFeedback);
16658
16840
  const allIssues2 = [...modeIssues, ...exitProbe.probeIssues];
16659
- evalResult = { issues: allIssues2, pass: ["axis.low-risk"] };
16841
+ evalResult = { issues: allIssues2, pass: ["axis.low-risk"], runtimeProbe: exitProbe.meta };
16660
16842
  console.log(
16661
16843
  `[simple] eval round ${evalRoundsUsed}: low-risk bypass clean BUT runtime probe fail (recoverable) \u2014 granting +1 turn`
16662
16844
  );
@@ -16677,10 +16859,11 @@ async function runEvalRound(ctx, input) {
16677
16859
  }
16678
16860
  evalResult = {
16679
16861
  issues: [...modeIssues, ...exitProbe.probeIssues],
16680
- pass: ["axis.low-risk"]
16862
+ pass: ["axis.low-risk"],
16863
+ runtimeProbe: exitProbe.meta
16681
16864
  };
16682
16865
  console.log(
16683
- `[simple] eval round ${evalRoundsUsed}: low-risk bypass (axis-checks clean, tier-1/2 skipped${exitProbe.fired ? ", probe ran" : ""}) \u2014 PASS`
16866
+ `[simple] eval round ${evalRoundsUsed}: low-risk bypass (axis-checks clean, tier-1/2 skipped${exitProbe.fired ? ", probe ran" : `, probe ${exitProbe.meta.status}`}) \u2014 PASS`
16684
16867
  );
16685
16868
  return {
16686
16869
  control: "break",
@@ -16787,7 +16970,7 @@ async function runEvalRound(ctx, input) {
16787
16970
  );
16788
16971
  const lines = runtimeFails.slice(0, MAX_FEEDBACK_ISSUES).map(formatRuntimeProbeFeedback);
16789
16972
  const enrichedIssues = [...allIssues, ...exitProbe.probeIssues];
16790
- evalResult = { issues: enrichedIssues, pass: allPass };
16973
+ evalResult = { issues: enrichedIssues, pass: allPass, runtimeProbe: exitProbe.meta };
16791
16974
  const enrichedFingerprints = /* @__PURE__ */ new Set([
16792
16975
  ...currFailFingerprints,
16793
16976
  ...runtimeFails.map(fingerprintFail)
@@ -16810,12 +16993,11 @@ async function runEvalRound(ctx, input) {
16810
16993
  lastDiffFailed: false
16811
16994
  };
16812
16995
  }
16813
- if (exitProbe.fired && exitProbe.probeIssues.length > 0) {
16814
- evalResult = {
16815
- issues: [...allIssues, ...exitProbe.probeIssues],
16816
- pass: allPass
16817
- };
16818
- }
16996
+ evalResult = {
16997
+ issues: [...allIssues, ...exitProbe.probeIssues],
16998
+ pass: allPass,
16999
+ runtimeProbe: exitProbe.meta
17000
+ };
16819
17001
  console.log(`[simple] eval round ${evalRoundsUsed}: no blocking issues \u2014 PASS`);
16820
17002
  return {
16821
17003
  control: "break",
@@ -16837,6 +17019,18 @@ async function runEvalRound(ctx, input) {
16837
17019
  console.log(
16838
17020
  `[simple] eval round ${evalRoundsUsed}: stuck \u2014 all ${currFailFingerprints.size} fails recur from prior round, exiting eval-fix loop`
16839
17021
  );
17022
+ const stuckExitProbe = await runProbeAtExit({
17023
+ harness,
17024
+ sourceCode: currentSource,
17025
+ compiledCode,
17026
+ contract,
17027
+ fixtureProps
17028
+ });
17029
+ evalResult = {
17030
+ issues: [...evalResult.issues, ...stuckExitProbe.probeIssues],
17031
+ pass: evalResult.pass,
17032
+ runtimeProbe: stuckExitProbe.meta
17033
+ };
16840
17034
  return {
16841
17035
  control: "break",
16842
17036
  evalDone: false,
@@ -16864,12 +17058,11 @@ async function runEvalRound(ctx, input) {
16864
17058
  const hasRuntimeFail = exitProbe.fired && exitProbe.recoverableFail;
16865
17059
  const allowRuntimeExtension = hasRuntimeFail && evalRoundsUsed < maxEvalRounds + RUNTIME_EXTENSION_BONUS;
16866
17060
  if (!allowRuntimeExtension) {
16867
- if (exitProbe.fired && exitProbe.probeIssues.length > 0) {
16868
- evalResult = {
16869
- issues: [...evalResult.issues, ...exitProbe.probeIssues],
16870
- pass: evalResult.pass
16871
- };
16872
- }
17061
+ evalResult = {
17062
+ issues: [...evalResult.issues, ...exitProbe.probeIssues],
17063
+ pass: evalResult.pass,
17064
+ runtimeProbe: exitProbe.meta
17065
+ };
16873
17066
  console.log(`[simple] eval round ${evalRoundsUsed}: max eval rounds reached \u2014 stopping`);
16874
17067
  return {
16875
17068
  control: "break",
@@ -16891,7 +17084,7 @@ async function runEvalRound(ctx, input) {
16891
17084
  );
16892
17085
  const probeLines = runtimeFails.slice(0, MAX_FEEDBACK_ISSUES).map(formatRuntimeProbeFeedback);
16893
17086
  const enrichedIssues = [...evalResult.issues, ...exitProbe.probeIssues];
16894
- evalResult = { issues: enrichedIssues, pass: evalResult.pass };
17087
+ evalResult = { issues: enrichedIssues, pass: evalResult.pass, runtimeProbe: exitProbe.meta };
16895
17088
  console.log(
16896
17089
  `[simple] eval round ${evalRoundsUsed}: runtime-probe fail active at cap \u2014 granting +1 retry (${maxEvalRounds + RUNTIME_EXTENSION_BONUS - evalRoundsUsed} of ${RUNTIME_EXTENSION_BONUS} bonus rounds remain)`
16897
17090
  );