Documentation
¶
Overview ¶
Package input defines the primary/driving ports of the application. Driving adapters (HTTP, MCP) depend on these interfaces, not on the concrete application services, so the application core stays replaceable behind its left-hand edge.
Index ¶
- func WithPointInPolygonCache(ctx context.Context) context.Context
- type ActiveSpan
- type CapturedEvent
- type CapturedSpan
- type CapturedTrace
- type Gazetteer
- type HealthChecker
- type HealthDetails
- type PointInPolygonCache
- type QueryService
- type SourceRegistry
- type SourceState
- type SpanStat
- type SpanSummary
- type Stats
- type SyncResult
- type Syncer
- type TelemetryQuery
- type TraceFilter
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func WithPointInPolygonCache ¶ added in v1.7.0
WithPointInPolygonCache returns a context carrying a fresh cache. Call it once per request, at the adapter boundary; the scope must not outlive the request, or a later request could be answered from a stale entry.
Types ¶
type ActiveSpan ¶
type ActiveSpan struct {
TraceID string `json:"trace_id"`
SpanID string `json:"span_id"`
ParentSpanID string `json:"parent_span_id,omitempty"`
Name string `json:"name"`
Kind string `json:"kind"`
Start time.Time `json:"start"`
AgeMS float64 `json:"age_ms"`
Attributes map[string]any `json:"attributes,omitempty"`
}
ActiveSpan is a lightweight snapshot of an in-flight span. Returned by ListActive so the MCP server can answer "what's currently running?" — the question you need to ask when something hangs.
type CapturedEvent ¶
type CapturedEvent struct {
Name string `json:"name"`
Time time.Time `json:"time"`
Attributes map[string]any `json:"attributes,omitempty"`
}
CapturedEvent is a serializable copy of a span event.
type CapturedSpan ¶
type CapturedSpan struct {
TraceID string `json:"trace_id"`
SpanID string `json:"span_id"`
ParentSpanID string `json:"parent_span_id,omitempty"`
Name string `json:"name"`
Kind string `json:"kind"`
Start time.Time `json:"start"`
End time.Time `json:"end"`
DurationMS float64 `json:"duration_ms"`
StatusCode string `json:"status_code"`
StatusMsg string `json:"status_message,omitempty"`
Attributes map[string]any `json:"attributes,omitempty"`
Events []CapturedEvent `json:"events,omitempty"`
}
CapturedSpan is a serializable copy of a span. Only data useful for the MCP server is retained — no SDK objects.
type CapturedTrace ¶
type CapturedTrace struct {
TraceID string `json:"trace_id"`
RootName string `json:"root_name"`
Service string `json:"service"`
Start time.Time `json:"start"`
End time.Time `json:"end"`
DurationMS float64 `json:"duration_ms"`
StatusCode string `json:"status_code"`
SpanCount int `json:"span_count"`
Spans []CapturedSpan `json:"spans"`
}
CapturedTrace is a complete trace tree as captured by the ring buffer.
type Gazetteer ¶
type Gazetteer interface {
// Locate reverse-geocodes a coordinate to its administrative hierarchy
// (levels 2–8), each level labeled with its semantic meaning.
Locate(ctx context.Context, p domain.Coordinate) (*domain.Locality, error)
// Bearing returns the most salient nearby place as a bearing fix
// ("4 km E Würzburg"), selected per the BearingPolicy.
Bearing(ctx context.Context, p domain.Coordinate, pol domain.BearingPolicy) (*domain.Fix, error)
// Islands returns the named island(s) whose polygon contains the point, or
// nil when the point is on no island or the optional islands layer is not
// configured — adapters render a null islands block in that case.
Islands(ctx context.Context, p domain.Coordinate) ([]domain.Island, error)
// Mountains returns the smallest containing mountain range and single-mountain
// territory (independently, per landform), or nil when the point is on neither
// or the optional mountains layer is not configured — adapters render a null
// mountains block in that case.
Mountains(ctx context.Context, p domain.Coordinate) (*domain.MountainResult, error)
// Elevation returns the height above sea level at the point, or (nil, nil)
// when the optional elevation feature is not wired — adapters render a null
// elevation block in that case.
Elevation(ctx context.Context, p domain.Coordinate) (*domain.Elevation, error)
// Exposure returns the terrain slope + aspect at the point, derived from the
// elevation DEM. It is (nil, nil) when the elevation feature is not wired or
// the point (or a neighbor) has no DEM coverage — adapters render a null
// exposure block in that case.
Exposure(ctx context.Context, p domain.Coordinate) (*domain.Exposure, error)
// Capabilities reports which optional blocks this deployment can answer at
// all, so a consumer can tell a null block that means "not part of this
// dataset" from one that means "no result here". Every method above returns
// (nil, nil) for both, which made a package that quietly lost a layer
// indistinguishable from correct behavior.
Capabilities() domain.GazetteerCapabilities
}
Gazetteer is the primary port for reverse geocoding and bearing ("Peilung"). It is a capability distinct from the generic point-query QueryService: it reads a dedicated places/admin GeoPackage, not the generic source pool, so the generic engine stays schema-agnostic.
type HealthChecker ¶
type HealthChecker interface {
// IsHealthy returns true if the service is healthy.
IsHealthy(ctx context.Context) bool
// IsReady returns true if the service is ready to accept requests.
IsReady(ctx context.Context) bool
// GetHealthDetails returns detailed health information.
GetHealthDetails(ctx context.Context) HealthDetails
}
HealthChecker defines the primary port for health checks.
type HealthDetails ¶
type HealthDetails struct {
Healthy bool // Overall health status
Ready bool // Ready to accept requests
SourcesLoaded int // Number of loaded sources
SourcesReady int // Number of ready sources
Components map[string]string // Component statuses
Sources []SourceState // Per-source status (lets a client see which source is still indexing)
}
HealthDetails contains detailed health information.
type PointInPolygonCache ¶ added in v1.7.0
type PointInPolygonCache struct {
// contains filtered or unexported fields
}
PointInPolygonCache memoizes point-in-polygon results for the duration of one request.
It exists because the gazetteer's sections are independent by design — each answers its own block and owns its own queries — and two of them need the same answer. Locate and Bearing both ask which admin polygons contain the query point, so every request ran that query twice. Measured on the committed request set, those two calls were the single largest cost in the response: 2 calls per request, ~2550 ms in total, more than the batched lineage walk.
The cache lives here rather than inside the service because the adapter has to be able to open the scope (one per HTTP/MCP request) and adapters cannot import the application layer. Absent a scope, nothing is cached and every section queries as before, so wiring it is optional and a service used without an adapter behaves identically.
func PointInPolygonCacheFrom ¶ added in v1.7.0
func PointInPolygonCacheFrom(ctx context.Context) *PointInPolygonCache
PointInPolygonCacheFrom returns the cache carried by ctx, or nil when none was opened. A nil receiver is safe on every method, so callers need no branch.
func (*PointInPolygonCache) Get ¶ added in v1.7.0
func (c *PointInPolygonCache) Get(layer string, at domain.Coordinate) ([]domain.Feature, bool)
Get returns a memoized result. ok is false on a nil cache or a miss.
func (*PointInPolygonCache) Put ¶ added in v1.7.0
func (c *PointInPolygonCache) Put(layer string, at domain.Coordinate, features []domain.Feature)
Put memoizes a result. A nil cache discards it.
The slice is stored as given, not copied: the gazetteer's sections only read the features they get back. That is a deliberate trade — a copy per call would undo part of what the cache saves — and it means a caller must not mutate a slice it has handed over.
type QueryService ¶
type QueryService interface {
// QueryPoint performs a point query across all registered sources.
QueryPoint(ctx context.Context, req domain.QueryRequest) (*domain.QueryResponse, error)
// QueryPointInSource performs a point query in a specific source.
QueryPointInSource(ctx context.Context, sourceID string, req domain.QueryRequest) (*domain.QueryResult, error)
// QueryBatch resolves many coordinates in one pass, returning one response per
// input coordinate in order. Point-in-polygon is done set-based (one query per
// source/layer for all points). sources (optional) restricts to those source
// ids; properties (optional) filters returned feature properties.
QueryBatch(ctx context.Context, coords []domain.Coordinate, sources []string, properties []string) ([]*domain.QueryResponse, error)
}
QueryService defines the primary port for spatial queries across sources.
type SourceRegistry ¶
type SourceRegistry interface {
// ListSources returns all registered sources.
ListSources(ctx context.Context) ([]domain.Source, error)
// GetSource returns a specific source by ID.
GetSource(ctx context.Context, id string) (*domain.Source, error)
// GetSourceStatus returns the status of a source.
GetSourceStatus(ctx context.Context, id string) (domain.SourceStatus, error)
}
SourceRegistry defines the primary port for source management.
type SourceState ¶
type SourceState struct {
ID string `json:"id"`
Status string `json:"status"` // loading | indexing | ready | error | unloading
Ready bool `json:"ready"`
}
SourceState is the per-source status exposed via /health, so a client can tell a specific source apart (e.g. a large one still "indexing") without the whole instance being marked not-ready.
type SpanStat ¶ added in v1.7.0
type SpanStat struct {
Name string `json:"name"`
// Group is the value of the group-by attribute (e.g. the "spatial.layer" a
// query hit). Empty when not grouping, or when a span lacks the attribute.
Group string `json:"group,omitempty"`
Spans int `json:"spans"`
// Traces counts the traces that contain this span — NOT the traces in the
// window. A span on a conditional code path appears in fewer.
Traces int `json:"traces"`
// PerTrace is Spans/Traces: calls per trace *that reaches this span*, not per
// request in the window. That is deliberate — it keeps the amplification
// undiluted by requests that skip the path, which is what an N+1 detector
// needs. Read it together with Traces: "234.9 calls in each of 11 traces" is
// a different statement from "103.4 calls averaged over all 25", and both are
// derivable, but only the first localizes the defect.
PerTrace float64 `json:"per_trace"`
TotalMS float64 `json:"total_ms"`
MeanMS float64 `json:"mean_ms"`
P50MS float64 `json:"p50_ms"`
P95MS float64 `json:"p95_ms"`
MaxMS float64 `json:"max_ms"`
Errors int `json:"errors,omitempty"`
}
SpanStat aggregates every occurrence of one span name (optionally split by an attribute) across a set of traces.
PerTrace is the field that earns this type its keep: a span that runs 512 times in a single request is an N+1 query pattern, and no percentile reveals that — only the ratio does. Totals alone would show "703 ms in ResolveChain" and leave the reader guessing whether that is one slow query or hundreds of fast ones.
type SpanSummary ¶ added in v1.7.0
type SpanSummary struct {
Traces int `json:"traces"`
RootP50MS float64 `json:"root_p50_ms"`
RootP95MS float64 `json:"root_p95_ms"`
RootMaxMS float64 `json:"root_max_ms"`
GroupBy string `json:"group_by,omitempty"`
Spans []SpanStat `json:"spans"`
}
SpanSummary is the aggregate view of a set of traces: what ran, how often, and where the time went. It answers the perf question ("which span dominates?") and the debugging question ("what is called more often than it should be?").
func SummarizeSpans ¶ added in v1.7.0
func SummarizeSpans(traces []*CapturedTrace, groupBy string) SpanSummary
SummarizeSpans aggregates spans across traces, newest-first order irrelevant.
groupBy names a span attribute to split by (e.g. "spatial.layer"); pass "" to aggregate by span name alone. Spans missing the attribute fall into an unlabeled group rather than being dropped, so the totals always add up.
type Stats ¶
type Stats struct {
Capacity int `json:"capacity"` // per-pool capacity
TracesActive int `json:"traces_active"` // traces with at least one open span
SpansActive int `json:"spans_active"` // open spans (across all traces)
TracesStored int `json:"traces_stored"` // successful, retained
ErrorTracesStored int `json:"error_traces_stored"`
Evicted uint64 `json:"evicted_total"`
OldestEnd time.Time `json:"oldest_end,omitempty"`
NewestEnd time.Time `json:"newest_end,omitempty"`
}
Stats summarizes ring-buffer contents for /health, /stats, and MCP overview.
type SyncResult ¶
type SyncResult struct {
SourcesAdded int `json:"sources_added"`
SourcesRemoved int `json:"sources_removed"`
SourcesTotal int `json:"sources_total"`
SyncedAt time.Time `json:"synced_at"`
NextScheduledAt time.Time `json:"next_scheduled_at,omitempty"`
}
SyncResult contains the outcome of a synchronization run. It is a driving-port DTO (like HealthDetails) returned to adapters that expose sync.
type Syncer ¶
type Syncer interface {
// TriggerSync runs a synchronization with remote storage on demand,
// returning what changed. May return domain.ErrRateLimited.
TriggerSync(ctx context.Context) (SyncResult, error)
}
Syncer defines the primary port for triggering storage synchronization.
type TelemetryQuery ¶
type TelemetryQuery interface {
// GetTrace returns a completed trace by id, or nil if not retained.
GetTrace(id string) *CapturedTrace
// ListTraces returns completed traces matching the filter, newest first.
ListTraces(TraceFilter) []*CapturedTrace
// ListActive returns in-flight spans — the answer to "what's running now?".
ListActive() []*ActiveSpan
// Stats summarizes buffer contents.
Stats() Stats
// OTelErrorCount is the process-wide count of OTel internal errors.
OTelErrorCount() uint64
}
TelemetryQuery is the primary port a driving adapter (the MCP server) uses to read captured trace data — "what ran, what's running, what failed". It is the seam that keeps the MCP adapter decoupled from the concrete telemetry adapter: MCP depends on this interface, the telemetry ring buffer implements it, and the composition root wires them together.
The DTOs below are the contract (serialized to MCP/JSON); they are defined here, not in the telemetry adapter, so neither side imports the other. The telemetry adapter aliases these types so its internal code is unchanged.
type TraceFilter ¶
type TraceFilter struct {
// MinDuration retains only traces longer than this. Zero means no filter.
MinDuration time.Duration
// Status retains only traces with this status. Valid values follow the
// OTel codes.Code stringification: "Ok", "Error", "Unset" (mixed case).
// Empty means no filter.
Status string
// NameContains retains only traces whose root span name contains this
// substring. Empty means no filter.
NameContains string
// Since retains only traces that ended at or after this time. Zero means
// no filter.
Since time.Time
// Limit caps the number of results. Zero means no cap.
Limit int
}
TraceFilter narrows down ListTraces results.