input

package
v1.9.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 5, 2026 License: MIT Imports: 7 Imported by: 0

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

Constants

This section is empty.

Variables

This section is empty.

Functions

func WithPointInPolygonCache added in v1.7.0

func WithPointInPolygonCache(ctx context.Context) context.Context

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

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL