mirror of
https://github.com/koala73/worldmonitor.git
synced 2026-04-25 17:14:57 +02:00
Add three fields to Forecast proto (simulation_adjustment, sim_path_confidence, demoted_by_simulation) and implement a thin colored underbar below each forecast title that encodes simulation evidence without adding columns or text clutter. Visual design (Option D): - 2px colored bar, width = sim path confidence for positive adj, 100% for negative/demoted (structural, not confidence-dependent) - Green ≥0.70 conf, amber <0.70, orange negative, red demoted - Opacity 0.45 at rest; 0.9 + text label on hover - Plain language hover labels: "AI signal · +8%", "AI caution · −12%", "AI flag: dropped · −15%" — no "sim" jargon visible to users - Demoted rows dim to opacity 0.5 Passes through simulation fields in buildPublishedForecastPayload. No chip renders until the ExpandedPath → Forecast plumbing lands (follow-up PR); all rendering code is ready and typecheck clean. 🤖 Generated with Claude Sonnet 4.6 via Claude Code + Compound Engineering v2.49.0 Co-authored-by: Claude Sonnet 4.6 (200K context) <noreply@anthropic.com>
488 lines
18 KiB
YAML
488 lines
18 KiB
YAML
openapi: 3.1.0
|
||
info:
|
||
title: ForecastService API
|
||
version: 1.0.0
|
||
paths:
|
||
/api/forecast/v1/get-forecasts:
|
||
get:
|
||
tags:
|
||
- ForecastService
|
||
summary: GetForecasts
|
||
operationId: GetForecasts
|
||
parameters:
|
||
- name: domain
|
||
in: query
|
||
required: false
|
||
schema:
|
||
type: string
|
||
- name: region
|
||
in: query
|
||
required: false
|
||
schema:
|
||
type: string
|
||
responses:
|
||
"200":
|
||
description: Successful response
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/GetForecastsResponse'
|
||
"400":
|
||
description: Validation error
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/ValidationError'
|
||
default:
|
||
description: Error response
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/Error'
|
||
/api/forecast/v1/get-simulation-package:
|
||
get:
|
||
tags:
|
||
- ForecastService
|
||
summary: GetSimulationPackage
|
||
operationId: GetSimulationPackage
|
||
parameters:
|
||
- name: runId
|
||
in: query
|
||
description: Currently ignored; always returns the latest package. Reserved for Phase 3 per-run lookup.
|
||
required: false
|
||
schema:
|
||
type: string
|
||
responses:
|
||
"200":
|
||
description: Successful response
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/GetSimulationPackageResponse'
|
||
"400":
|
||
description: Validation error
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/ValidationError'
|
||
default:
|
||
description: Error response
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/Error'
|
||
/api/forecast/v1/get-simulation-outcome:
|
||
get:
|
||
tags:
|
||
- ForecastService
|
||
summary: GetSimulationOutcome
|
||
operationId: GetSimulationOutcome
|
||
parameters:
|
||
- name: runId
|
||
in: query
|
||
description: |-
|
||
IMPORTANT: Currently a no-op. Always returns the latest available outcome regardless of runId.
|
||
Per-run lookup is reserved for Phase 3. Check the response 'note' field when runId is supplied
|
||
and you need to detect a mismatch between requested and returned run.
|
||
required: false
|
||
schema:
|
||
type: string
|
||
responses:
|
||
"200":
|
||
description: Successful response
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/GetSimulationOutcomeResponse'
|
||
"400":
|
||
description: Validation error
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/ValidationError'
|
||
default:
|
||
description: Error response
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/Error'
|
||
components:
|
||
schemas:
|
||
Error:
|
||
type: object
|
||
properties:
|
||
message:
|
||
type: string
|
||
description: Error message (e.g., 'user not found', 'database connection failed')
|
||
description: Error is returned when a handler encounters an error. It contains a simple error message that the developer can customize.
|
||
FieldViolation:
|
||
type: object
|
||
properties:
|
||
field:
|
||
type: string
|
||
description: The field path that failed validation (e.g., 'user.email' for nested fields). For header validation, this will be the header name (e.g., 'X-API-Key')
|
||
description:
|
||
type: string
|
||
description: Human-readable description of the validation violation (e.g., 'must be a valid email address', 'required field missing')
|
||
required:
|
||
- field
|
||
- description
|
||
description: FieldViolation describes a single validation error for a specific field.
|
||
ValidationError:
|
||
type: object
|
||
properties:
|
||
violations:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/FieldViolation'
|
||
description: List of validation violations
|
||
required:
|
||
- violations
|
||
description: ValidationError is returned when request validation fails. It contains a list of field violations describing what went wrong.
|
||
GetForecastsRequest:
|
||
type: object
|
||
properties:
|
||
domain:
|
||
type: string
|
||
region:
|
||
type: string
|
||
GetForecastsResponse:
|
||
type: object
|
||
properties:
|
||
forecasts:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/Forecast'
|
||
generatedAt:
|
||
type: integer
|
||
format: int64
|
||
description: 'Warning: Values > 2^53 may lose precision in JavaScript'
|
||
Forecast:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: string
|
||
domain:
|
||
type: string
|
||
region:
|
||
type: string
|
||
title:
|
||
type: string
|
||
scenario:
|
||
type: string
|
||
feedSummary:
|
||
type: string
|
||
probability:
|
||
type: number
|
||
format: double
|
||
confidence:
|
||
type: number
|
||
format: double
|
||
timeHorizon:
|
||
type: string
|
||
signals:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/ForecastSignal'
|
||
cascades:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/CascadeEffect'
|
||
trend:
|
||
type: string
|
||
priorProbability:
|
||
type: number
|
||
format: double
|
||
calibration:
|
||
$ref: '#/components/schemas/CalibrationInfo'
|
||
createdAt:
|
||
type: integer
|
||
format: int64
|
||
description: 'Warning: Values > 2^53 may lose precision in JavaScript'
|
||
updatedAt:
|
||
type: integer
|
||
format: int64
|
||
description: 'Warning: Values > 2^53 may lose precision in JavaScript'
|
||
perspectives:
|
||
$ref: '#/components/schemas/Perspectives'
|
||
projections:
|
||
$ref: '#/components/schemas/Projections'
|
||
caseFile:
|
||
$ref: '#/components/schemas/ForecastCase'
|
||
simulationAdjustment:
|
||
type: number
|
||
format: double
|
||
description: |-
|
||
Simulation-scoring fields — populated when the deep forecast simulation pipeline
|
||
has run for this forecast's state and produced a non-zero adjustment.
|
||
simulation_adjustment: raw score delta (+0.08–+0.12 positive, -0.12/-0.15 negative).
|
||
sim_path_confidence: clamped [0,1] confidence of the matched sim top-path; 0 = not set.
|
||
demoted_by_simulation: true when a negative adjustment crossed the 0.50 acceptance threshold.
|
||
simPathConfidence:
|
||
type: number
|
||
format: double
|
||
demotedBySimulation:
|
||
type: boolean
|
||
ForecastSignal:
|
||
type: object
|
||
properties:
|
||
type:
|
||
type: string
|
||
value:
|
||
type: string
|
||
weight:
|
||
type: number
|
||
format: double
|
||
CascadeEffect:
|
||
type: object
|
||
properties:
|
||
domain:
|
||
type: string
|
||
effect:
|
||
type: string
|
||
probability:
|
||
type: number
|
||
format: double
|
||
CalibrationInfo:
|
||
type: object
|
||
properties:
|
||
marketTitle:
|
||
type: string
|
||
marketPrice:
|
||
type: number
|
||
format: double
|
||
drift:
|
||
type: number
|
||
format: double
|
||
source:
|
||
type: string
|
||
Perspectives:
|
||
type: object
|
||
properties:
|
||
strategic:
|
||
type: string
|
||
regional:
|
||
type: string
|
||
contrarian:
|
||
type: string
|
||
Projections:
|
||
type: object
|
||
properties:
|
||
h24:
|
||
type: number
|
||
format: double
|
||
d7:
|
||
type: number
|
||
format: double
|
||
d30:
|
||
type: number
|
||
format: double
|
||
ForecastCase:
|
||
type: object
|
||
properties:
|
||
supportingEvidence:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/ForecastCaseEvidence'
|
||
counterEvidence:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/ForecastCaseEvidence'
|
||
triggers:
|
||
type: array
|
||
items:
|
||
type: string
|
||
actorLenses:
|
||
type: array
|
||
items:
|
||
type: string
|
||
baseCase:
|
||
type: string
|
||
escalatoryCase:
|
||
type: string
|
||
contrarianCase:
|
||
type: string
|
||
changeSummary:
|
||
type: string
|
||
changeItems:
|
||
type: array
|
||
items:
|
||
type: string
|
||
actors:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/ForecastActor'
|
||
worldState:
|
||
$ref: '#/components/schemas/ForecastWorldState'
|
||
branches:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/ForecastBranch'
|
||
ForecastCaseEvidence:
|
||
type: object
|
||
properties:
|
||
type:
|
||
type: string
|
||
summary:
|
||
type: string
|
||
weight:
|
||
type: number
|
||
format: double
|
||
ForecastActor:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: string
|
||
name:
|
||
type: string
|
||
category:
|
||
type: string
|
||
role:
|
||
type: string
|
||
objectives:
|
||
type: array
|
||
items:
|
||
type: string
|
||
constraints:
|
||
type: array
|
||
items:
|
||
type: string
|
||
likelyActions:
|
||
type: array
|
||
items:
|
||
type: string
|
||
influenceScore:
|
||
type: number
|
||
format: double
|
||
ForecastWorldState:
|
||
type: object
|
||
properties:
|
||
summary:
|
||
type: string
|
||
activePressures:
|
||
type: array
|
||
items:
|
||
type: string
|
||
stabilizers:
|
||
type: array
|
||
items:
|
||
type: string
|
||
keyUnknowns:
|
||
type: array
|
||
items:
|
||
type: string
|
||
ForecastBranch:
|
||
type: object
|
||
properties:
|
||
kind:
|
||
type: string
|
||
title:
|
||
type: string
|
||
summary:
|
||
type: string
|
||
outcome:
|
||
type: string
|
||
projectedProbability:
|
||
type: number
|
||
format: double
|
||
rounds:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/ForecastBranchRound'
|
||
ForecastBranchRound:
|
||
type: object
|
||
properties:
|
||
round:
|
||
type: integer
|
||
format: int32
|
||
focus:
|
||
type: string
|
||
developments:
|
||
type: array
|
||
items:
|
||
type: string
|
||
actorMoves:
|
||
type: array
|
||
items:
|
||
type: string
|
||
probabilityShift:
|
||
type: number
|
||
format: double
|
||
GetSimulationPackageRequest:
|
||
type: object
|
||
properties:
|
||
runId:
|
||
type: string
|
||
description: Currently ignored; always returns the latest package. Reserved for Phase 3 per-run lookup.
|
||
GetSimulationPackageResponse:
|
||
type: object
|
||
properties:
|
||
found:
|
||
type: boolean
|
||
runId:
|
||
type: string
|
||
pkgKey:
|
||
type: string
|
||
schemaVersion:
|
||
type: string
|
||
theaterCount:
|
||
type: integer
|
||
format: int32
|
||
generatedAt:
|
||
type: integer
|
||
format: int64
|
||
description: 'Unix timestamp in milliseconds (from Date.now()). Warning: Values > 2^53 may lose precision in JavaScript.. Warning: Values > 2^53 may lose precision in JavaScript'
|
||
note:
|
||
type: string
|
||
description: |-
|
||
Populated when req.runId was supplied but does not match the returned package's runId.
|
||
Indicates that per-run filtering is not yet active and the latest package was returned instead.
|
||
error:
|
||
type: string
|
||
description: |-
|
||
Populated when the Redis lookup failed. Distinguish from healthy not-found (found=false, error="").
|
||
Value: "redis_unavailable" on Redis errors.
|
||
GetSimulationOutcomeRequest:
|
||
type: object
|
||
properties:
|
||
runId:
|
||
type: string
|
||
description: |-
|
||
IMPORTANT: Currently a no-op. Always returns the latest available outcome regardless of runId.
|
||
Per-run lookup is reserved for Phase 3. Check the response 'note' field when runId is supplied
|
||
and you need to detect a mismatch between requested and returned run.
|
||
GetSimulationOutcomeResponse:
|
||
type: object
|
||
properties:
|
||
found:
|
||
type: boolean
|
||
runId:
|
||
type: string
|
||
outcomeKey:
|
||
type: string
|
||
schemaVersion:
|
||
type: string
|
||
theaterCount:
|
||
type: integer
|
||
format: int32
|
||
generatedAt:
|
||
type: integer
|
||
format: int64
|
||
description: 'Unix timestamp in milliseconds (from Date.now()). Warning: Values > 2^53 may lose precision in JavaScript.. Warning: Values > 2^53 may lose precision in JavaScript'
|
||
note:
|
||
type: string
|
||
description: |-
|
||
Populated when req.runId was supplied but does not match the returned outcome's runId.
|
||
Indicates that per-run filtering is not yet active and the latest outcome was returned instead.
|
||
error:
|
||
type: string
|
||
description: |-
|
||
Populated when the Redis lookup failed. Distinguish from healthy not-found (found=false, error="").
|
||
Value: "redis_unavailable" on Redis errors.
|
||
theaterSummariesJson:
|
||
type: string
|
||
description: |-
|
||
JSON-encoded array of theater summaries for the UI (populated when found=true).
|
||
Shape: Array<{ theaterId, theaterLabel, stateKind, topPaths: [{label, summary, confidence, keyActors}], dominantReactions, stabilizers, invalidators }>
|
||
Parse with JSON.parse() on the client. Empty string when found=false.
|