koala73 / koala73/worldmonitor
feat(forecast): adopt the 202 async-job contract on trigger-simulation (conditional 202/200)
- Dominant language
- TypeScript
- Stars
- 86.6k
- Forks
- 13.1k
- Avg merge
- 8h 4m
- Merged PRs (30d)
- 825
Description
Follow-up to #4810, which restored the 202 Accepted + Location async-job pattern on `scenario/run-scenario` (orank Access `async-job-pattern` now satisfied by that op).
`forecast/trigger-simulation` mirrors the same enqueue pattern (server-derived runId, Railway worker, poll `get-simulation-outcome`) but was deliberately left at 200-always in #4810 because it has non-enqueue success outcomes that make an unconditional 202 dishonest:
- `queued=true` → a fresh enqueue: **202 Accepted** + `Location: /api/forecast/v1/get-simulation-outcome?runId=` would be correct (use `setSuccessStatusOverride` from `server/_shared/response-headers.ts`, POST-200-only semantics already handled by the gateway).
- `reason='no_package'` → nothing enqueued (no package pointer): 202 would be wrong; keep 200.
- `reason='already-handled'` → idempotency hit: debatable (the work IS accepted/complete); pick one and document it.
Work needed beyond the handler one-liner:
1. Conditional status = the OpenAPI must document BOTH a 202 (queued) and a 200 (not-queued) success with the same `TriggerSimulationResponse` schema — extend `scripts/openapi-inject-async-jobs.mjs` with a dual-success mode, and give each response a **curated per-outcome example** (`queued:true` on the 202, `queued:false, reason:'no_package'` on the 200). The examples injector stamps ONE example on every 2xx (`successResponses` matches all `2\d\d`), so it would currently show `queued:true` on the 200 — a doc lie; needs an override hook.
2. `trigger_simulation.proto` response NOTE says 'All paths that return this message represent HTTP 200' — rewrite + `make generate` regen.
3. `tests/openapi-idempotency-contract.test.mjs` now asserts **exactly one** 2xx success per POST (generalized in #4810) — the dual-success op needs that assertion relaxed for ops documented as async-enqueue.
4. Extend `tests/openapi-async-jobs-contract.test.mjs` + the runtime tests for the conditional branch.
No orank points depend on this (run-scenario already satisfies the check) — this is REST-purity/honesty work.
Contributor guide
Research direction
Start with the trigger-simulation handler and compare its enqueue behavior with scenario/run-scenario from #4810. Then read scripts/openapi-inject-async-jobs.mjs, trigger_simulation.proto, and the named OpenAPI and runtime contract tests; run make generate and those tests. Done means conditional 202/200 behavior, per-outcome examples, regenerated notes, and updated contract coverage, including the already-handled outcome decision.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- openapi, typescript
- Domain
- api, testing
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100