fix(sim): report failed, canceled and incomplete runs instead of a false pass - #58
Merged
Merged
Conversation
scott-lowe-vapi
force-pushed
the
fix/sim-false-green
branch
from
October 1, 2026 23:11
8a438bc to
06a1492
Compare
Contributor
Author
This was referenced Oct 1, 2026
scott-lowe-vapi
marked this pull request as ready for review
October 1, 2026 23:52
vtkovapi
approved these changes
Oct 3, 2026
Contributor
Author
Merge activity
|
scott-lowe-vapi
changed the base branch from
ci/test-workflow
to
graphite-base/58
October 3, 2026 05:59
…lse pass `npm run sim` read a `results` field the simulation-run API doesn't return and counted `status === "pass"` (items are `passed`/`failed`), so every watched run summarised as 0/0 and exited 0, including failing runs. - src/sim-result.ts: strict, pure verdict. Passed only when the run ended, every expected item exists and passed, and each had a required evaluation that was actually scored; otherwise failed or incomplete with a reason. - src/sim.ts: read run items (paginated or bare array, deduped by id), wait for late item results, print the run link from the create response and the failing judges, cancel the run on --timeout (default 20 min) or Ctrl-C. - src/vapi-client.ts: config-free client; run creation is never retried on a 5xx, since the run may already be queued. - src/sim-cmd.ts: exported simCommandRun; exit 0 passed, 1 failed, 2 usage, 3 incomplete; --no-watch prints the link and exits 0. - Docs: run/item shapes and the verdict in docs/learnings/simulations.md, sim rows in README and AGENTS.md, improvements.md #33. Refs TEST-141 Co-Authored-By: Claude Opus 5.5 <[email protected]>
scott-lowe-vapi
force-pushed
the
fix/sim-false-green
branch
from
October 3, 2026 06:00
06a1492 to
663612e
Compare
scott-lowe-vapi
added a commit
that referenced
this pull request
Oct 3, 2026
…g-free modules (#59) ## Value **V.A.L.U.E. tier:** project — PR 3 of 10 for inline simulation PR checks ([TEST-141](https://linear.app/vapi/issue/TEST-141/gitops-run-simulation-suites-against-pr-changes-inline-as-ci-checks)); this PR is a behaviour-preserving refactor. - **Problem:** the PR check (PRs 4–7) has to read an org's resource files and resolve org API keys. All the code that does that sits behind `config.ts`, which parses `argv`, binds one org and can `process.exit` at import time. Copying it into the check would let push and the check drift apart, so the check could test a different payload from the one push sends. - **Who it affects:** gitops users, whose PR checks must test exactly what push would deploy; and maintainers, who get one copy of the loader instead of two (three for the `.md` system-prompt injection). - **What changes:** code moves into two config-free modules. `resources.ts`, `config.ts` and `promote-cmd.ts` delegate to them and keep their exports, logs and error messages. - **`src/resource-parse.ts`:** - from `resources.ts`: `FOLDER_MAP`, `VALID_EXTENSIONS`, frontmatter/file parsing, the directory scan and the loader; - from `config.ts`: `.vapi-ignore` reading and matching; - new: `orgResourcesRead(rootDir, org)`, which returns every resource of an org keyed `type:id`; - the three copies of the `.md` body → system message injection become `markdownResourceParse`. - **`src/org-connection.ts`:** `envValue`, `tokensParse(envName)`, `connectionLoad` and `childRun`, moved from `promote-cmd.ts` and parameterised on the root dir, the token variable and the base URL. - **`api.ts`:** takes `VapiApiError`, `parseApiMessage`, `shouldRetry` and the backoff constants from `vapi-client.ts`, and re-exports `VapiApiError`. Both clients now share one error class (so `instanceof` checks see one class) and one retry rule. - **`promotion.ts`:** - imports `FOLDER_MAP` / `VALID_EXTENSIONS` instead of keeping its own copies; - exports `SLUG_RE`, `promotionBindingsParse`, `promotionBindingsResolve`, `promotionBindingsApply` and `PromotionBindingsResolved`, for the check config (PR 4) and the payload builder (PR 5). - Also dropped: the unused `FOLDER_TO_TYPE` map in `resources.ts`. ## Evidence of value **The output is byte-identical before and after.** The same fixture org was run at both refs against a local stub API. Nothing reached a real org. The fixture: - an `.md` assistant whose frontmatter has a system message the body must replace; - a nested assistant; - an ignored assistant; - a backup copy and an unsupported file; - a `.ts` tool and a YAML tool; - a squad, a structured output and all four simulation types; - a two-org `promotion.yml`. | Command | `fix/sim-false-green` (06a1492) | This branch (0518af1) | |---|---|---| | `push ev-dev --dry-run` (new-file gate refusal) | identical | identical | | `push ev-dev --dry-run --allow-new-files` (full plan, every would-POST) | identical | identical | | `loadResources` JSON for every type (full parsed data, incl. the `.md` system message) | identical | identical | | `validate ev-dev` | identical | identical | | `promote --pipeline release --from ev-dev --to ev-prod` (plan) | identical | identical | | **Combined stdout + stderr + stub request log** | sha256 `3f3b609f…1d3b`, 14,927 bytes | sha256 `3f3b609f…1d3b`, 14,927 bytes | - Dry-run placeholder IDs (`dry-run-post-<Date.now()>`) and the stub's port are normalised before hashing. - `--allow-new-files` is passed only because every fixture file is new by construction. **Tests:** `npm test` goes from 377 to 398 passing (21 new), and `npm run build` (src + tests) is clean. ## Testing plan - **`tests/resource-parse.test.ts`** (temp-dir fixtures) covers: - `.md` body replacing a frontmatter system message, and an empty body; - missing frontmatter; - `.md` parsing being the same for `parseResourceDataFromFile` and the loader; - sorted order across `.yml`/`.yaml`/`.ts` and nested dirs; - hidden and `.bkp` files skipped; - duplicate IDs refused, with the exact YAML-not-an-object message; - missing directory; - `.vapi-ignore` comments, blanks and `!`; - `*` vs `**` vs `?` matching; - `orgResourcesRead` reading one org and applying its ignore file, and the caller's override. - **`tests/org-connection.test.ts`** covers: - plain and quoted `.env` values; - token-map parsing, with the variable named in every error; - token precedence (map, then `.env.<org>`) and base URL precedence (configured, then `.env.<org>`); - the missing-token error; - `childRun` passing the org and key to a real child, and dropping an inherited `VAPI_BASE_URL`; - a failing child. - The existing push dry-run, `.vapi-ignore` push, promotion, audit and cleanup-safety suites pass unchanged. - **Not tested:** - a push against a real org (the comparison uses a stub API that returns empty lists, so update/PATCH paths for existing resources aren't exercised; the code they call is unchanged); - `promote --apply` (the moved `connectionLoad`/`childRun` are covered by unit tests, but no live child `pull`/`apply` was run); - Node 20 locally (left to CI). Stacked on #58. Review with `git diff --color-moved=dimmed-zebra fix/sim-false-green...` to see that most of the diff is moved lines. Refs TEST-141 🤖 Generated with [Claude Code](https://claude.com/claude-code)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Value
V.A.L.U.E. tier: project — PR 2 of 10 for inline simulation PR checks (TEST-141). Stacked on #57.
npm run simreported every run as passed. It read aresultsfield the simulation-run API doesn't return and countedstatus === "pass"(items arepassed/failed), so it always summarised 0/0 and exited 0, failing runs included.npm run sim, locally or in CI. The PR check (later in this stack) reuses this verdict, so it has to be right.src/sim-result.ts).--timeoutand Ctrl-C both cancel the run.Evidence of value
Same stub API, one passed and one failed item:
main'ssim.ts{"pass":0,"fail":0}→ exits 0 (false green)failed — 1 of 1 simulations failed→ exits 1Live, against a test org (chat transport):
✗ … open-24h (expected = true, got false)— runpassed — 1 of 1 simulations passed— runThe temporary resources were deleted afterwards.
Testing plan
tests/sim-result.test.tsis a verdict table covering:results);itemCounts, and a run that hasn't ended.tests/sim-run.test.tsrunsrunSimulationagainst a local HTTP stub:--no-watch;npm run buildandnpm testpass (377 tests).runningwhen it's canceled (cancel was only exercised against the stub), and voice transport (the live runs used chat). Default behaviour change: unknown CLI arguments are now an error (exit 2) instead of being silently ignored.Refs TEST-141
🤖 Generated with Claude Code