Skip to content

Frontend: surface failed/empty graph builds + Rebuild, harden polling - #51

Open
shivswami wants to merge 1 commit into
nikmcfly:mainfrom
shivswami:pr2-frontend-rebuild-resilience
Open

Frontend: surface failed/empty graph builds + Rebuild, harden polling#51
shivswami wants to merge 1 commit into
nikmcfly:mainfrom
shivswami:pr2-frontend-rebuild-resilience

Conversation

@shivswami

Copy link
Copy Markdown

What

Makes graph-build failures visible and recoverable, and stops polling from hanging forever.

Why

  • When NER returned 0 entities, the UI showed "Completed" with empty stats and no way forward.
  • Polling ran indefinitely if the backend restarted mid-build.
  • A page refresh lost the uploaded requirement.

Changes

  • Step1GraphBuild.vue / Process.vue — show a Failed badge + "Rebuild Graph" button when a build yields 0 entities; block advancing to environment setup.
  • MainView.vue — backend connection indicator (15s health poll), 30-min polling timeout, stop after repeated consecutive errors; rebuild handler.
  • pendingUpload.js — persist the requirement to sessionStorage so a refresh no longer discards it (File objects can't be restored — user is prompted to re-upload).

Includes a bugfix

Process.vue rebuild used buildGraph(projectId, { force }), but buildGraph(data) takes a single object — so force was dropped and a bare string was sent as the body, causing rebuild to always fail with "Please provide project_id". Now uses the correct signature.

Backend dependency

Uses the existing force rebuild (POST /api/graph/build with force: true) — already in main.

Tested

  • Production build passes (vite build, 675 modules).
  • [Runtime rebuild / timeout behavior confirmed locally.]

- Step1GraphBuild / Process: show Failed badge + Rebuild button when a
  build yields 0 entities; block advancing to environment setup
- MainView: backend connection indicator (15s poll), 30-min polling
  timeout, stop after repeated consecutive errors
- MainView/Process: rebuild via buildGraph({ project_id, force: true })
- pendingUpload: persist requirement to sessionStorage so a refresh no
  longer discards it (File objects aren't restorable — user re-uploads)

Includes fix: Process.vue rebuild used buildGraph(projectId, {force}) which
dropped force and sent a bare string as the body (always failed).
Pxls21 added a commit to Pxls21/MiroFish-Offline that referenced this pull request Jul 11, 2026
…erfactual endpoint

FOUR increments (two staged commits squashed for transfer):
1. report_agent._ensure_prompt_budget -- deterministic chars/3 token guard
   on all 4 accumulated-context LLM calls; drops middle messages oldest-
   first, never system/instruction/final-turn; LLM_CONTEXT_WINDOW env
   (default 16384); fail-loud event report.context_truncated; raises with
   exact sizes when structurally impossible (gate-7 400 root cause).
2. llm_client.chat(disable_thinking=) opt-in extra_body flag (PR nikmcfly#34 only
   covered response_format) + defensive preamble strip at report-chat
   returns with report.chat_preamble_stripped event.
3. Twitter interview 400 fix: instance-level override of oasis
   perform_interview which double-prepends role=system (camel context
   creator already returns it) -- dedup at our call site, Reddit untouched;
   root-caused by reading installed camel-oasis 0.2.5 / camel-ai 0.2.78.
4. Counterfactual branch endpoint hand-ported from nativ3ai c492630:
   POST /api/simulation/<id>/counterfactual + manager clone/inject +
   scheduled-event firing in all 3 child round loops, instrumented
   (simulation.counterfactual_create span, sim.child.scheduled_event_fired
   instant span), comments Englishized. Backend only; Vue panels not
   ported (conflict with our PR nikmcfly#51 frontend picks -- separate decision).
nocode-ananas added a commit to nocode-ananas/polylop that referenced this pull request Jul 24, 2026
nocode-ananas added a commit to nocode-ananas/polylop that referenced this pull request Jul 24, 2026
requestWithRetry() wrapped eight POST endpoints with a 3x retry and
exponential backoff. A slow but successful call (a long Mistral run behind
/simulation/start or /graph/build) hits the client timeout, gets retried,
and starts a second and third run server-side - the user sees one hung
click and gets three simulations.

Retry removed from all POST call sites (graph: ontology/generate, build;
simulation: create, prepare, start, interview/batch; report: generate,
chat). The helper stays exported for idempotent GET/polling use and now
says so.

Audit 2026-06-13, wave 2. Not covered by the PR nikmcfly#50/nikmcfly#51 backport - those
touched retry inside the LLM client, not the browser API layer.
nocode-ananas added a commit to nocode-ananas/polylop that referenced this pull request Jul 24, 2026
…ges)

- frontend/src/views/Process.vue (2059 lines): the "Process" route imports
  MainView.vue, nothing imports this file. Dead since the MainView split,
  but still got patched by mistake in the PR nikmcfly#51 backport.
- frontend/src/views/Home.css: third copy of the home styles, imported by
  nobody.
- frontend/src/App.vue: its <style> block held a verbatim second copy of
  public/home-styles.css (80 rules, identical declarations, only the
  section comments differed) - a silent drift trap, since the App.vue copy
  is injected later and would win. Removed; the seven global rules (reset,
  #app, scrollbar, button) stay. Rendering verified unchanged against a
  before/after screenshot of the home page.
- static/image/: six upstream images plus Screenshot/ (5.7 MB) that nothing
  references. .dockerignore does not exclude static/, so they were shipped
  in every image. The two files used by README.md stay.

Audit 2026-06-13, wave 2.
nocode-ananas added a commit to nocode-ananas/polylop that referenced this pull request Jul 24, 2026
Fixes the simulation lifecycle bugs from the 2026-06-13 audit (section B).
No upstream PR covers these — nikmcfly nikmcfly#30/nikmcfly#41/nikmcfly#50/nikmcfly#51 are already backported
and address other areas.

Every finding was reproduced against the unpatched code before fixing, using
backend/tests/test_simulation_lifecycle.py: 11/31 checks pass on the baseline,
31/31 with this patch.

Stop had no effect on reconnected simulations
  stop_simulation only ever looked at cls._processes, which is empty after a
  backend restart. The status flipped to 'stopped' while the process kept
  running — and kept calling the LLM API, costing real money. Added a PID-based
  termination path (SIGTERM, then SIGKILL) for simulations we have no Popen
  handle for.

Manually stopped runs were reported as failed/completed
  The monitor thread woke up to the SIGTERM exit code and overwrote the stop
  with 'failed'; the orphan monitor could report 'completed'. An explicit stop
  is now recorded and wins over the exit-code verdict. stop_simulation also
  waits for the monitor instead of racing it.

Double start was possible
  Nothing serialized check-and-spawn, so concurrent /start requests (the axios
  interceptor retries POSTs up to 3x) each spawned a process into the same
  directory. Added a per-simulation lock, and a liveness check so a start is
  refused while the real process is alive even if the status file claims
  otherwise.

state.json and run_state.json drifted apart
  Nothing wrote the terminal status into state.json when a simulation ended on
  its own, so finished runs kept showing as running (live: sim_cb2ebb78e07a,
  run_state 'completed' vs state.json 'running'). The runner now mirrors its
  terminal status into state.json through one shared, atomic writer. /stop
  records STOPPED instead of PAUSED — the process is terminated, not suspended,
  no view renders 'paused', and PAUSED overwrote the runner's own status.

PID reuse could adopt an unrelated process
  os.kill(pid, 0) only proves some process holds that PID. The start command
  line is now recorded and verified before a process is adopted or signalled;
  an unverifiable PID closes the run instead of hanging it on 'running'
  forever. psutil is not in the runtime image, so this is stdlib only
  (/proc, with a ps fallback for macOS).

Also in this change
  - run_state.json is written atomically; concurrent readers used to see
    truncated JSON (40008 corrupted reads in a 2s test on the old code)
  - a 'running' state with no PID no longer hangs forever on reconnect
  - failed runs get a completed_at timestamp like every other terminal state
  - the orphan reconnect is skipped in the Flask reloader's parent process:
    with DEBUG on, create_app runs twice, and two monitor threads in two
    processes were writing the same run_state.json

Not touched on purpose: FLASK_HOST/DEBUG/NEO4J_PASSWORD hardening, declined
2026-07-15. The reloader guard above makes the lifecycle robust against debug
mode instead of turning it off.
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