Skip to content

fix(api): correct baseline-snapshot sample + tech-events omitted-paging default (#4599 adversarial follow-ups A+B)#4666

Merged
koala73 merged 3 commits into
mainfrom
fix/openapi-baseline-sample-and-techevents-default
Jul 3, 2026
Merged

fix(api): correct baseline-snapshot sample + tech-events omitted-paging default (#4599 adversarial follow-ups A+B)#4666
koala73 merged 3 commits into
mainfrom
fix/openapi-baseline-sample-and-techevents-default

Conversation

@koala73

@koala73 koala73 commented Jul 3, 2026

Copy link
Copy Markdown
Owner

What & why

An adversarial re-audit of the merged OpenAPI docs (#4657, umbrella #4599) — 5 lenses (samples, auth, completeness, contract-validity, doc-truth) each verified against the real gateway/handlers — surfaced two defects the guards missed. This PR fixes both (the version-independent pair; the plugin-gated items C/D follow in a second PR).

A — RecordBaselineSnapshot sample rejected by the handler

The request sample used type: "all", but VALID_BASELINE_TYPES = [military_flights, vessels, protests, news, ais_gaps, satellite_fires]record-baseline-snapshot.ts:43 silently continues on a non-member, so a copy-paste yields updated: 0 while the 200 example claims updated: 1. Root cause: the examples injector's endsWith('type') heuristic emitted the literal 'all' with no curated override (the sibling GetTemporalBaseline query enum correctly shows military_flights).

Fix: drift-anchored BASELINE_TYPE_EXAMPLE_ID (= infrastructureTemporalBaselineTypes[0]) + an overrideStringExample case scoped to baseline ops. Re-ran only the examples injector — version-independent, 3-artifact diff.

B — tech-events "defaults to 50/90 when omitted" was false

The REST decoder maps an omitted query param to 0 (Number(params.get("limit") ?? "0")), and clampInt treats a finite 0 as a value (→ min 1), not the fallback — so a no-params call returned 1 event within 1 day, not the documented 50/90. Siblings guard correctly (seismology || 500, BLS > 0 ? : 60); tech-events didn't.

Fix: extract a pure, tested resolveTechEventsPaging() that guards with || default, and wire both handler call sites (fetchTechEvents + filterEvents) to it — de-duplicating the two identical resolutions. The primary documented claim is now true.

Note: the handler fix means an explicit 0 now also -> 50 (0 and omitted are indistinguishable on the wire). The proto field comment was updated to "defaults to 50/90 when omitted or 0" and the ResearchService specs regenerated with the pinned sebuf v0.11.1 plugin (2nd commit), so this PR is fully self-consistent.

Tests (Bug Fix Protocol — reproduced first)

  • resolveTechEventsPaging omitted/0 → 50/90 (added the failing test, confirmed it returned {1,1}, then fixed).
  • record-baseline-snapshot sample typeVALID_BASELINE_TYPES (drift-anchored guard).
  • 384 contract tests pass; tsc clean; examples injector --check idempotent; biome clean.

Follow-up: #4658 tracks the handler-behavior notes.

…ng default (adversarial follow-ups #4599)

Two defects surfaced by an adversarial re-audit of the merged OpenAPI docs (#4657):

A — RecordBaselineSnapshot request sample used `type: "all"`, which the handler
rejects (VALID_BASELINE_TYPES has no 'all' → record-baseline-snapshot.ts:43
silently `continue`s), so a copy-paste yields `updated: 0` while the 200 example
claims `updated: 1`. The examples injector's `endsWith('type')` heuristic emitted
the literal 'all' with no curated override. Add a drift-anchored
BASELINE_TYPE_EXAMPLE_ID (= infrastructureTemporalBaselineTypes[0], 'military_flights',
matching the sibling GetTemporalBaseline enum example) + an overrideStringExample
case scoped to baseline ops. Re-ran only the examples injector (version-independent).

B — list-tech-events documented `limit`/`days` "defaults to 50/90 when omitted",
but the REST decoder maps an omitted query param to 0, and clampInt treats a finite
0 as a value (→ min 1), not the fallback — so a no-params call returned 1 event
within 1 day, not 50/90. Extract a pure, tested resolveTechEventsPaging() that
guards with `|| default` (mirrors seismology `|| 500` and BLS `>0 ? : 60`); wire
both handler call sites (fetchTechEvents + filterEvents) to it. The primary
documented claim is now true. (The residual "explicit 0 clamps to 1" wording is a
proto-comment change and rides the follow-up regen PR with #4599 items C/D.)

Guard tests: baseline sample type ∈ VALID_BASELINE_TYPES; resolveTechEventsPaging
omitted/0 → 50/90 (reproduced failing first, then fixed). 384 contract tests pass;
tsc clean; examples injector --check idempotent.

Claude-Session: https://claude.ai/code/session_01EsjtheWqt8GxEZfd3DyTMu
@vercel

vercel Bot commented Jul 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
worldmonitor Ready Ready Preview, Comment Jul 3, 2026 3:52pm

Request Review

@mintlify

mintlify Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
WorldMonitor 🟢 Ready View Preview Jul 3, 2026, 2:55 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@greptile-apps

greptile-apps Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes two doc-to-handler drift defects caught by an adversarial re-audit of the OpenAPI specs. Both fixes are narrowly scoped, version-independent, and accompanied by drift-anchored tests.

  • Fix A (baseline snapshot sample): The RecordBaselineSnapshot request example used type: "all", which is not in VALID_BASELINE_TYPES; the handler silently skipped it, contradicting the updated: 1 response example. The injector now emits military_flights (pinned via a drift-anchored IIFE that reads from openapi-filter-param-contracts.json), with a contract test guarding against future regressions.
  • Fix B (tech-events paging defaults): An omitted limit/days query param decoded to 0 and bypassed the documented 50/90 defaults, returning 1 item in 1 day instead. The new resolveTechEventsPaging() helper guards with || default before clampInt, de-duplicates the two identical resolution sites in fetchTechEvents and filterEvents, and mirrors the seismology/BLS sibling pattern.

Confidence Score: 5/5

Safe to merge — both changes are narrowly scoped bug fixes with targeted tests and no observable side-effects on other endpoints.

The handler fix correctly mirrors the established sibling pattern (|| default) and is exercised by five unit tests covering the critical 0/undefined case. The OpenAPI sample correction is regenerated output guarded by a new drift-anchored contract test. The only rough edge is a test description that says 'below the minimum' while its input is at the minimum, which is a labelling issue with no impact on correctness.

tests/tech-events-paging.test.mts — the 'below minimum' test case exercises the boundary value (1), not a sub-minimum value; worth a one-line fix before the follow-up PR adds more paging tests.

Important Files Changed

Filename Overview
server/worldmonitor/research/v1/_tech-events-paging.ts New pure helper that resolves paging defaults for list-tech-events; correctly guards 0/undefined via `
server/worldmonitor/research/v1/list-tech-events.ts Replaces two identical inline clampInt calls in fetchTechEvents and filterEvents with resolveTechEventsPaging; de-duplicates paging resolution and fixes the omitted-param default bug.
scripts/openapi-inject-examples.mjs Adds drift-anchored BASELINE_TYPE_EXAMPLE_ID and a scoped override in overrideStringExample to emit a real VALID_BASELINE_TYPES member for baseline operations' type fields.
tests/tech-events-paging.test.mts New test suite for resolveTechEventsPaging; the 0/undefined and upper-clamp cases are correct, but the 'below minimum' test case uses limit=1 (the minimum) instead of a negative value, making the description misleading.
tests/openapi-examples-contract.test.mjs Adds a drift-anchored contract test that verifies all record-baseline-snapshot sample types are members of VALID_BASELINE_TYPES; well-structured guard against injector regressions.
docs/api/InfrastructureService.openapi.json Single-line change: record-baseline-snapshot request example type corrected from 'all' (invalid, silently skipped by handler) to 'military_flights' (valid VALID_BASELINE_TYPES member).
docs/api/InfrastructureService.openapi.yaml Mirrors the JSON fix — same type correction in the YAML spec for record-baseline-snapshot.
docs/api/worldmonitor.openapi.yaml Same type correction applied to the combined worldmonitor unified spec.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[REST decoder\nparams.get limit ?? 0] -->|decoded value| B{resolveTechEventsPaging}
    B -->|req.limit = 0 or undefined| C["req.limit || 50 → 50\nclampInt(50, 50, 1, 200) → 50"]
    B -->|req.limit = 25| D["req.limit || 50 → 25\nclampInt(25, 50, 1, 200) → 25"]
    B -->|req.limit = -5| E["req.limit || 50 → -5\nclampInt(-5, 50, 1, 200) → 1"]
    C --> F[fetchTechEvents / filterEvents]
    D --> F
    E --> F

    G[examples injector\noverrideStringExample] -->|key='type', path includes 'baseline'| H[BASELINE_TYPE_EXAMPLE_ID\n= military_flights]
    H --> I[record-baseline-snapshot\nrequest sample]
    I -->|handler reads type| J{VALID_BASELINE_TYPES\nincludes?}
    J -->|yes| K[updated += 1]
    J -->|no — old: 'all'| L[silently continue\nupdated = 0]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[REST decoder\nparams.get limit ?? 0] -->|decoded value| B{resolveTechEventsPaging}
    B -->|req.limit = 0 or undefined| C["req.limit || 50 → 50\nclampInt(50, 50, 1, 200) → 50"]
    B -->|req.limit = 25| D["req.limit || 50 → 25\nclampInt(25, 50, 1, 200) → 25"]
    B -->|req.limit = -5| E["req.limit || 50 → -5\nclampInt(-5, 50, 1, 200) → 1"]
    C --> F[fetchTechEvents / filterEvents]
    D --> F
    E --> F

    G[examples injector\noverrideStringExample] -->|key='type', path includes 'baseline'| H[BASELINE_TYPE_EXAMPLE_ID\n= military_flights]
    H --> I[record-baseline-snapshot\nrequest sample]
    I -->|handler reads type| J{VALID_BASELINE_TYPES\nincludes?}
    J -->|yes| K[updated += 1]
    J -->|no — old: 'all'| L[silently continue\nupdated = 0]
Loading

Reviews (1): Last reviewed commit: "fix(api): correct baseline-snapshot samp..." | Re-trigger Greptile

Comment thread tests/tech-events-paging.test.mts Outdated
Comment on lines +27 to +29
it('clamps explicit values below the minimum to 1', () => {
assert.deepEqual(resolveTechEventsPaging({ limit: 1, days: 1 }), { limit: 1, days: 1 });
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The test description says "clamps explicit values below the minimum to 1", but the input values { limit: 1, days: 1 } are AT the minimum, not below it. The output { limit: 1, days: 1 } confirms this — no clamping actually occurs. As-is the test verifies that the lower bound is inclusive (fine), but the name implies a negative/sub-minimum value (-5 etc.) would be tested. This could mislead a future reader who changes clampInt's lower boundary and wonders why this test still passes.

Suggested change
it('clamps explicit values below the minimum to 1', () => {
assert.deepEqual(resolveTechEventsPaging({ limit: 1, days: 1 }), { limit: 1, days: 1 });
});
it('floor-clamps the minimum-boundary values (limit=1, days=1 are the minimums)', () => {
assert.deepEqual(resolveTechEventsPaging({ limit: 1, days: 1 }), { limit: 1, days: 1 });
});
it('clamps explicit values below the minimum to 1', () => {
assert.deepEqual(resolveTechEventsPaging({ limit: -5, days: -3 }), { limit: 1, days: 1 });
});

… fixed default (review #B)

Follow-on to the resolveTechEventsPaging fix in this PR: with the handler now
treating omitted/0 as the 50/90 default, the proto field comment's residual
"0 is not unlimited (it clamps up to 1)" clause was stale (0 now → 50, since an
omitted query param and an explicit 0 are the same value on the wire). Reword to
"defaults to 50/90 when omitted or 0" and regenerate the ResearchService specs +
bundle with the pinned sebuf v0.11.1 plugin (scoped diff: 3 artifacts, param
descriptions only). make generate idempotent; contract + description-guard tests pass.
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