Phase 1 PR1: RegionalSnapshot proto + RPC handler#2951
Conversation
Defines the canonical RegionalSnapshot wire format for Phase 1 of the
Regional Intelligence Model. Mirrors the TypeScript contract in
shared/regions.types.d.ts that Phase 0 landed with.
New proto file: proto/worldmonitor/intelligence/v1/get_regional_snapshot.proto
Messages:
- RegionalSnapshot (13 top-level fields matching the spec)
- SnapshotMeta (11 fields including snapshot_id, narrative_provider,
narrative_model, trigger_reason, snapshot_confidence, missing_inputs,
stale_inputs, valid_until, versions)
- RegimeState (label + transition history)
- BalanceVector (7 axes: 4 pressures + 3 buffers + net_balance + decomposed
drivers)
- BalanceDriver (axis, magnitude, evidence_ids, orientation)
- ActorState (leverage_score, role, domains, delta, evidence_ids)
- LeverageEdge (actor-to-actor directed influence)
- ScenarioSet + ScenarioLane (per-horizon distribution normalizing to 1.0)
- TransmissionPath (typed fields: severity, confidence, latency_hours,
magnitude range, asset class, template provenance)
- TriggerLadder + Trigger + TriggerThreshold (structured operator/value/
window/baseline)
- MobilityState + AirspaceStatus + FlightCorridorStress + AirportNodeStatus
- EvidenceItem (typed origin for the trust trail)
- RegionalNarrative + NarrativeSection (LLM-synthesized text with
evidence_ids on every section)
RPC: GetRegionalSnapshot(GetRegionalSnapshotRequest) -> GetRegionalSnapshotResponse
- GET /api/intelligence/v1/get-regional-snapshot
- region_id validated as lowercase kebab via buf.validate regex
- No other parameters; the handler reads canonical state
Generated code committed alongside:
- src/generated/client/worldmonitor/intelligence/v1/service_client.ts
- src/generated/server/worldmonitor/intelligence/v1/service_server.ts
- docs/api/IntelligenceService.openapi.{json,yaml}
The generated TypeScript types use camelCase per standard buf codegen, while
Phase 0 persists snapshots in Redis using the snake_case shape from
shared/regions.types.d.ts. The handler lands in a follow-up commit with a
localized snake_case -> camelCase adapter so Phase 0 code stays frozen.
Spec: docs/internal/pro-regional-intelligence-upgrade.md
Reads canonical persisted RegionalSnapshot for a region via the two-hop
lookup pattern established by the Phase 0 persist layer:
1. GET intelligence:snapshot:v1:{region}:latest -> snapshot_id
2. GET intelligence:snapshot-by-id:v1:{snapshot_id} -> full snapshot JSON
Returns empty response (snapshot omitted) when:
- No latest pointer exists (seed has never run or unknown region)
- Latest pointer references a pruned or TTL-expired snapshot
- Snapshot JSON is malformed
The handler does NOT recompute on miss. One writer (the seed bundle),
canonical reads. Matches the architecture commitment in the spec.
Includes a full snake_case -> camelCase adapter so the persisted Phase 0
shape (shared/regions.types.d.ts) maps cleanly onto the camelCase proto
wire format generated by buf. The adapter is the single bridge between
the two shapes; Phase 0 code stays frozen. Adapter handles every nested
message: SnapshotMeta, RegimeState, BalanceVector (+pressures/buffers
drivers), ActorState, LeverageEdge, ScenarioSet (+lanes +transmissions),
TransmissionPath, TriggerLadder (+triggers +thresholds), MobilityState
(+airspace +flight corridors +airports), EvidenceItem, RegionalNarrative
(+5 sections +watch items).
Wiring:
- Registered on intelligenceHandler in handler.ts
- Added to PREMIUM_RPC_PATHS (src/shared/premium-paths.ts) so the
gateway enforces Pro subscription or API key
- Added to RPC_CACHE_TIER with 'slow' tier (300s browser, 1800s edge)
matching similar premium intelligence RPCs
Not in this PR:
- LLM narrative generator (follow-up PR2, wires into snapshot writer)
- RegionalIntelligenceBoard panel UI (follow-up PR3)
- ENDPOINT_ENTITLEMENTS tier-specific enforcement (PREMIUM_RPC_PATHS
alone is the Pro gate; only stock-analysis endpoints currently use
tier-specific enforcement)
…ructural checks 29 tests across 5 suites covering: adaptSnapshot (18 tests): real unit tests of the snake_case -> camelCase adapter with synthetic persisted snapshots. Covers every nested message (SnapshotMeta, RegimeState, BalanceVector with 7 axes + decomposed drivers, ActorState, LeverageEdge, ScenarioSet with nested lanes and transmissions, TriggerLadder with all 3 buckets + TriggerThreshold, MobilityState with airspace/flights/airports, EvidenceItem, RegionalNarrative with all 5 sections + watch_items). Also asserts empty-default behavior when nested fields are missing. Handler structural checks (8 tests): validates import of getCachedJson, canonical key prefixes, two-hop lookup ordering, empty-response fallbacks on missing pointer or malformed snapshot, and export signature matching the service interface. Registration (2 tests): confirms getRegionalSnapshot is imported and registered on the intelligenceHandler object. Security wiring (2 tests): confirms the endpoint is in PREMIUM_RPC_PATHS and RPC_CACHE_TIER with 'slow' tier. Proto definition (3 tests): confirms the RPC method declaration, region_id validation regex, RegionalSnapshot top-level field layout, and BalanceVector 7-axis declaration.
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Greptile SummaryThis PR ships the proto definition and RPC handler for Confidence Score: 5/5Safe to merge — all findings are P2 style suggestions with no functional or security impact. The two-hop Redis read, adapter logic, auth gating, and handler wiring are all correct. The only findings are: a dead RPC_CACHE_TIER entry (pre-existing pattern for premium paths) and a slightly loose proto regex that permits trailing hyphens. Neither affects runtime behavior. No files require special attention beyond the two P2 notes on gateway.ts and get_regional_snapshot.proto. Important Files Changed
Sequence DiagramsequenceDiagram
participant Client
participant Gateway as Gateway (auth + cache)
participant Handler as getRegionalSnapshot
participant Redis as Redis (Upstash)
Client->>Gateway: GET /api/intelligence/v1/get-regional-snapshot?region_id=mena
Gateway->>Gateway: Auth check (PREMIUM_RPC_PATHS)
Gateway->>Gateway: Proto validation (buf.validate regex)
Gateway->>Handler: invoke(req)
Handler->>Redis: GET intelligence:snapshot:v1:mena:latest
Redis-->>Handler: snapshot_id (string) or null
alt no snapshot_id
Handler-->>Gateway: {}
else snapshot_id found
Handler->>Redis: GET intelligence:snapshot-by-id:v1:{snapshot_id}
Redis-->>Handler: PersistedSnapshot (snake_case JSON) or null
alt null / malformed
Handler-->>Gateway: {}
else valid
Handler->>Handler: adaptSnapshot() snake_case→camelCase
Handler-->>Gateway: { snapshot: RegionalSnapshot }
end
end
Gateway->>Gateway: Apply slow-browser cache headers (max-age=300, no CDN)
Gateway-->>Client: 200 OK
Reviews (1): Last reviewed commit: "test(intelligence): unit tests for get-r..." | Re-trigger Greptile |
| '/api/intelligence/v1/get-country-energy-profile': 'slow', | ||
| '/api/intelligence/v1/compute-energy-shock': 'fast', | ||
| '/api/intelligence/v1/get-country-port-activity': 'slow', | ||
| '/api/intelligence/v1/get-regional-snapshot': 'slow', |
There was a problem hiding this comment.
RPC_CACHE_TIER entry is dead code for premium paths
Because PREMIUM_RPC_PATHS.has(pathname) is checked first in the cache-tier decision (line 434), the gateway unconditionally uses 'slow-browser' for every premium endpoint — the RPC_CACHE_TIER[pathname] branch is never reached. The effective Cache-Control for this endpoint is max-age=300, stale-while-revalidate=60, stale-if-error=1800 (no public, no s-maxage, no CDN caching), not the 'slow' tier claimed in the PR description ("300s browser, 1800s edge").
All other entries in PREMIUM_RPC_PATHS have the same dead entry in RPC_CACHE_TIER, so this is an established (if confusing) codebase pattern rather than a new mistake. However, the test in tests/get-regional-snapshot.test.mts (line 438–440) validates the presence of the dead entry, which could mislead future maintainers into believing edge caching is active for this endpoint.
Consider removing the entry or adding a comment noting that premium paths ignore RPC_CACHE_TIER.
| (buf.validate.field).required = true, | ||
| (buf.validate.field).string.min_len = 1, | ||
| (buf.validate.field).string.max_len = 32, | ||
| (buf.validate.field).string.pattern = "^[a-z][a-z0-9-]*$", |
There was a problem hiding this comment.
Regex permits trailing hyphens in
region_id
The pattern ^[a-z][a-z0-9-]*$ allows values like mena- or east-asia- — a hyphen in any position after the first character is valid, including at the end. This isn't a functional bug (Redis simply returns null for unknown keys), but it's looser than the kebab-case convention used by the canonical region IDs (e.g. mena, east-asia).
A pattern that prevents consecutive or trailing hyphens would match the intent more precisely:
| (buf.validate.field).string.pattern = "^[a-z][a-z0-9-]*$", | |
| (buf.validate.field).string.pattern = "^[a-z][a-z0-9]*(-[a-z0-9]+)*$", |
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Two P2 findings from Greptile on the RegionalSnapshot proto+RPC PR. 1) region_id regex permitted trailing and consecutive hyphens Old: ^[a-z][a-z0-9-]*$ — accepted "mena-", "east-asia-", "foo--bar" New: ^[a-z][a-z0-9]*(-[a-z0-9]+)*$ — strict kebab-case, every hyphen must be followed by at least one alphanumeric character. Regenerated openapi JSON/YAML via `make generate`. Test assertion updated to match. 2) RPC_CACHE_TIER entry looked like dead code for premium paths Greptile flagged that `isPremium` short-circuits the tier lookup to 'slow-browser' before RPC_CACHE_TIER is consulted, so the entry is never read at runtime. Kept the entry because `tests/route-cache-tier.test.mjs` enforces a parity contract requiring every generated GET route to have an explicit tier. Added a NOTE comment in gateway.ts explaining the policy, and updated the security-wiring test with a rationale comment so future maintainers know the entry is intentional documentation, not a stale wire.
Summary
Phase 1 PR1 of the Regional Intelligence Model. Server-only; ships the proto definition and the RPC handler that reads the canonical snapshots persisted by Phase 0. No UI changes. No LLM calls. PR2 (narrative generator) and PR3 (Intelligence Board panel) follow independently.
What landed
Proto
proto/worldmonitor/intelligence/v1/get_regional_snapshot.protowith the full RegionalSnapshot wire format: 13 top-level fields, 17 nested message types, mirrorsshared/regions.types.d.tsthat Phase 0 already ships. Every balance axis, actor, scenario lane, transmission path, trigger threshold, mobility substate, evidence item, and narrative section has a typed proto message.GetRegionalSnapshot(GetRegionalSnapshotRequest) returns (GetRegionalSnapshotResponse)added to IntelligenceService.region_idvalidated viabuf.validateregex as lowercase kebab.make generateoutput committed: generated client/server TypeScript types, OpenAPI JSON/YAML.Handler
server/worldmonitor/intelligence/v1/get-regional-snapshot.tsimplements the two-hop read::latestpointer -> snapshot_id ->snapshot-by-idpayload. Returns empty response on miss or malformed JSON. Does NOT recompute on miss - one writer (the seed bundle), canonical reads, matching the architecture spec.adaptSnapshot) bridging Phase 0's persisted shape (snake_case fromshared/regions.types.d.ts) to the proto's camelCase wire format. Adapter is the single bridge; Phase 0 code stays frozen.intelligence/v1/handler.ts.Security
/api/intelligence/v1/get-regional-snapshottoPREMIUM_RPC_PATHS— gateway enforces Pro subscription or API key.RPC_CACHE_TIERwith'slow'tier (300s browser, 1800s edge) matching similar premium intelligence RPCs.Tests
tests/get-regional-snapshot.test.mts— 29 tests across 5 suites:adaptSnapshot()covering every nested message, with synthetic inputs and empty-default fallbackshandler.tsVerification
npm run test:data: 4293 / 4293 passnpm run typecheck:all: cleanbiome lint: 1 pre-existing warning onserver/gateway.ts:242complexity (unrelated tech debt)What's deliberately NOT in this PR
scripts/seed-regional-snapshots.mjsto callcallLlmReasoningonce per region per 6h cycle, populatesnarrative_provider+narrative_modelin SnapshotMeta.Post-Deploy Monitoring & Validation
get-regional-snapshot— expect low traffic (premium users only, not bootstrapped)/api/intelligence/v1/get-regional-snapshotlatency. Should be <100ms (two Redis GETs){}initially (Phase 0 seed may not have persisted anything yet for this specific deployment), then populated snapshot after the next seed cron cycle (~6h).GET intelligence:snapshot:v1:mena:latestshould return a snapshot_id after the first Phase 0 cron fires post-merge.seed-bundle-derived-signalslogs.PR sequence
Spec references
docs/internal/pro-regional-intelligence-upgrade.md— architecturedocs/internal/pro-regional-intelligence-appendix-engineering.md— file/key inventorydocs/internal/pro-regional-intelligence-appendix-scoring.md— formulas