Skip to content

fix(generation): repair invalid model output - #313

Merged
graphite-app[bot] merged 1 commit into
mainfrom
gen/gen-repair-loop
Sep 1, 2026
Merged

fix(generation): repair invalid model output#313
graphite-app[bot] merged 1 commit into
mainfrom
gen/gen-repair-loop

Conversation

@anandpant

@anandpant anandpant commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

Scope

  • Add a configurable semantic repair budget with maxRepairAttempts: 2 by default, allowing one more semantic repair when the first repair introduces a different violation class.
  • Repair invalid JSON/IR with the prior output plus all validator diagnostics; regenerate truncated output fresh.
  • Promote flowchart.start_has_incoming and flowchart.self_loop, including their validator hints, at the top of repair requests. Start-node failures reroute to the first process after start; self-loops reroute to an earlier distinct process or decision node.
  • Preserve the original malformed candidate and its text/raw/error/usage when a repair call fails or returns invalid output; enrich it with both attempts' diagnostics.
  • Catch typed repair-call failures while allowing interruption to propagate, and always record sketchi_generation_repairs{outcome="failed"} for failed repair calls.
  • Classify truncation from only the latest model response so a truncated regeneration that is still invalid proceeds to semantic repair.
  • Preserve every existing node and label except the minimal edit required by reported diagnostics, and restate the original diagram type, scenario, parsed minimum node count, required node labels, and required branch labels on every repair call.
  • Keep transport retries and timeouts around each model call, normalize empty optional edge labels, and extract the first complete JSON object when a model appends trailing output.
  • Bypass Cloudflare AI Gateway cache in fresh mode with cf-aig-skip-cache: true.

Expected latency tradeoff

  • Each provider attempt has a 30-second timeout and up to two transient retries with 250 ms and 500 ms backoff, so one exhausted model call can take up to 90.75 seconds.
  • A malformed initial response can trigger two complete repair calls, making the configured generation-policy ceiling 272.25 seconds before outer request overhead. Requests above 120 seconds are therefore expected in the worst repair/retry cases; the added latency is the tradeoff for recovering when a repair introduces a new violation such as dropping below a requested minimum node count.
  • The deployed probe permits 302.25 seconds: the full 272.25-second policy budget plus a 30-second margin.

Proof

  • pnpm nx test diagram-generation --skip-nx-cache - 37/37 passed at 237076a.
  • pnpm nx run-many -t typecheck,test,build --skip-nx-cache - passed at 237076a.
  • pnpm nx build-storybook diagram-ui --skip-nx-cache - passed at 237076a.
  • Added typed repair-failure preservation, invalid-repair original-candidate preservation, interruption propagation, failed repair metric, latest-response truncation classification, priority start-node and self-loop correction, preserve-content/hard-requirement repair instructions, repair-introduced minimum violation recovery, cache bypass, requested-cycle, minimum-count, diagnostic-composition, and repair-success fixtures.

This is PR 5 of 6 in the generation-reliability stack.

anandpant commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator Author

How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • merge - adds this PR to the back of the merge queue
  • fast - for urgent changes, fast-track this PR to the front of the merge queue

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

@anandpant
anandpant force-pushed the gen/gen-repair-loop branch 2 times, most recently from 0f395b2 to 388d06d Compare September 1, 2026 03:41
@anandpant
anandpant force-pushed the gen/gen-mindmap-nested branch from 47cc19f to c35165e Compare September 1, 2026 18:38
@anandpant
anandpant force-pushed the gen/gen-repair-loop branch 3 times, most recently from d811c8b to cc8206f Compare September 1, 2026 18:52
@anandpant
anandpant force-pushed the gen/gen-mindmap-nested branch from c35165e to 779498f Compare September 1, 2026 18:52
@anandpant
anandpant force-pushed the gen/gen-mindmap-nested branch from 779498f to 308fe6e Compare September 1, 2026 19:27
@anandpant
anandpant force-pushed the gen/gen-mindmap-nested branch from 308fe6e to 0cac637 Compare September 1, 2026 20:15
@anandpant anandpant changed the title fix(generation): repair invalid model output once fix(generation): repair invalid model output Sep 1, 2026
@graphite-app

graphite-app Bot commented Sep 1, 2026

Copy link
Copy Markdown

Merge activity

## Scope

- Add a configurable semantic repair budget with `maxRepairAttempts: 2` by default, allowing one more semantic repair when the first repair introduces a different violation class.
- Repair invalid JSON/IR with the prior output plus all validator diagnostics; regenerate truncated output fresh.
- Promote `flowchart.start_has_incoming` and `flowchart.self_loop`, including their validator hints, at the top of repair requests. Start-node failures reroute to the first process after start; self-loops reroute to an earlier distinct process or decision node.
- Preserve the original malformed candidate and its text/raw/error/usage when a repair call fails or returns invalid output; enrich it with both attempts' diagnostics.
- Catch typed repair-call failures while allowing interruption to propagate, and always record `sketchi_generation_repairs{outcome="failed"}` for failed repair calls.
- Classify truncation from only the latest model response so a truncated regeneration that is still invalid proceeds to semantic repair.
- Preserve every existing node and label except the minimal edit required by reported diagnostics, and restate the original diagram type, scenario, parsed minimum node count, required node labels, and required branch labels on every repair call.
- Keep transport retries and timeouts around each model call, normalize empty optional edge labels, and extract the first complete JSON object when a model appends trailing output.
- Bypass Cloudflare AI Gateway cache in fresh mode with `cf-aig-skip-cache: true`.

## Expected latency tradeoff

- Each provider attempt has a 30-second timeout and up to two transient retries with 250 ms and 500 ms backoff, so one exhausted model call can take up to 90.75 seconds.
- A malformed initial response can trigger two complete repair calls, making the configured generation-policy ceiling 272.25 seconds before outer request overhead. Requests above 120 seconds are therefore expected in the worst repair/retry cases; the added latency is the tradeoff for recovering when a repair introduces a new violation such as dropping below a requested minimum node count.
- The deployed probe permits 302.25 seconds: the full 272.25-second policy budget plus a 30-second margin.

## Proof

- `pnpm nx test diagram-generation --skip-nx-cache` - 37/37 passed at `237076a`.
- `pnpm nx run-many -t typecheck,test,build --skip-nx-cache` - passed at `237076a`.
- `pnpm nx build-storybook diagram-ui --skip-nx-cache` - passed at `237076a`.
- Added typed repair-failure preservation, invalid-repair original-candidate preservation, interruption propagation, failed repair metric, latest-response truncation classification, priority start-node and self-loop correction, preserve-content/hard-requirement repair instructions, repair-introduced minimum violation recovery, cache bypass, requested-cycle, minimum-count, diagnostic-composition, and repair-success fixtures.

This is PR 5 of 6 in the generation-reliability stack.
@graphite-app
graphite-app Bot force-pushed the gen/gen-mindmap-nested branch from 0cac637 to f014afc Compare September 1, 2026 21:27
@graphite-app
graphite-app Bot force-pushed the gen/gen-repair-loop branch from 237076a to 9f64991 Compare September 1, 2026 21:28
Base automatically changed from gen/gen-mindmap-nested to main September 1, 2026 21:31
@graphite-app
graphite-app Bot merged commit 9f64991 into main Sep 1, 2026
4 checks passed
@graphite-app
graphite-app Bot deleted the gen/gen-repair-loop branch September 1, 2026 21:31
@graphite-app
graphite-app Bot deployed to production September 1, 2026 21:31 Active
@graphite-app
graphite-app Bot deployed to production September 1, 2026 21:31 Active
@graphite-app
graphite-app Bot deployed to production September 1, 2026 21:31 Active
@graphite-app
graphite-app Bot deployed to production September 1, 2026 21:31 Active
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Sketchi Icons Preview

Status: deleted

Preview Worker cleanup has completed.

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Sketchi Web Preview

Status: deleted

Preview Worker cleanup has completed.

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Sketchi Playground / Studio Preview

Status: deleted

  • Surface: public product preview
  • Project: playground
  • Worker identity: sketchi-studio
  • Route policy: playground.sketchi.app product surface; authenticated Studio remains unexposed
  • Preview Worker: sketchi-studio-pr-313
  • Commit: 9f64991c008e
  • Workflow run: https://github.com/shpitdev/sketchi/actions/runs/33561588752

Preview Worker cleanup has completed.

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Sketchi Eval Harness Preview

Status: deleted

  • Surface: internal preview; not linked from public navigation
  • Project: eval-harness
  • Worker identity: sketchi-playground
  • Route policy: internal eval harness; no public product domain
  • Preview Worker: sketchi-playground-pr-313
  • Commit: 9f64991c008e
  • Workflow run: https://github.com/shpitdev/sketchi/actions/runs/33561588752

Preview Worker cleanup has completed.

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Sketchi Excalidraw Workspace Preview

Status: deleted

  • Surface: internal preview; not linked from public navigation
  • Project: excalidraw
  • Worker identity: sketchi-excalidraw
  • Route policy: internal canvas workspace; no public product domain
  • Preview Worker: sketchi-excalidraw-pr-313
  • Commit: 9f64991c008e
  • Workflow run: https://github.com/shpitdev/sketchi/actions/runs/33561588752

Preview Worker cleanup has completed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant