Documentation
¶
Overview ¶
Package research defines the payload types for the ADR-045 graph search rule chain: Intent, SearchResult, and RouteDecision.
These are the wire shapes the chain's coordinated components and the continuation rule pass through the payload registry. Phase 1 PR 1 lands the registry surface so PRs 2-6 can land components and the rule chain without churn on the schema contract.
See docs/adr/045-graph-search-rule-chain.md for the architecture and docs/operations/22-adr045-phase1-plan.md for the PR sequence.
Index ¶
- Constants
- func IsValidDecomposeScope(s string) bool
- func IsValidRouteAction(s string) bool
- func IsValidSeedRefType(s string) bool
- func RegisterPayloads(reg *payloadregistry.Registry) error
- type Candidate
- type ClassifierOutput
- type DecompTrace
- type DecomposeArgs
- type Evidence
- type Intent
- type RetightenArgs
- type RouteDecision
- func (p *RouteDecision) MarshalJSON() ([]byte, error)
- func (p *RouteDecision) ParseDecomposeArgs() (DecomposeArgs, error)
- func (p *RouteDecision) ParseRetightenArgs() (RetightenArgs, error)
- func (p *RouteDecision) ParseWalkSeedsArgs() (WalkSeedsArgs, error)
- func (p *RouteDecision) Schema() message.Type
- func (p *RouteDecision) UnmarshalJSON(data []byte) error
- func (p *RouteDecision) Validate() error
- type SearchResult
- type SeedRef
- type WalkSeedsArgs
Constants ¶
const ( // Domain is the payload registry domain for the ADR-045 graph // search rule chain. Domain = "research" // SchemaVersion is the current schema version for research payloads. SchemaVersion = "v1" )
Domain and version constants for the research message namespace. All three payload types (ResearchIntent, SearchResult, RouteDecision) share the domain and version; only the category differs.
const ( // CategoryIntent is the caller's research request. Topic + free-form // hints + budget. Entity IDs and predicates are outputs of the chain, // not inputs (see ADR-045's classifier-first amendment). CategoryIntent = "intent" // CategoryResult is the chain's terminal output. Synthesis text plus // evidence refs the parent can quote back, plus decomp trace for // trajectory review. CategoryResult = "result" // CategoryRouteDecision is route_search's structured emit: one of // four actions plus action-specific args plus rationale. CategoryRouteDecision = "route_decision" // CategoryClassifierOutput is the nl_classify component's emit: // classifier hints + initial candidate set. R1 fires on the // matching trigger key; route_search (PR 3) consumes the payload // to pick a routing action. CategoryClassifierOutput = "classifier_output" )
Category constants for research payload types.
const ( // ActionSynthesizeDirectly short-circuits to synthesize_answer when // classifier output is already enough to answer the topic. Many // parent topics will land here; the chain doesn't pay for multi-hop // work it doesn't need. ActionSynthesizeDirectly = "synthesize_directly" // ActionRetighten re-runs nl_classify with refined topic/hints when // the initial classification produced noisy or empty hits. Bounded // at MaxIterations=2 on R2's retighten branch. ActionRetighten = "retighten" // ActionWalkSeeds dispatches execute_subqueries with seed // REFERENCES (names, partial IDs, or candidate-list indices) the // classifier surfaced. Per ADR-045 Amendment 2026-05-23 // (intent-not-structure), the model emits references; the backend // resolves to full 6-part entity IDs via the entity index. Multi- // hop expansion from concrete seeds — structurally different from // topic-wide decompose. See WalkSeedsArgs. ActionWalkSeeds = "walk_seeds" // ActionDecompose dispatches execute_subqueries with the // decomposition INTENT (axes, focus, scope). Per ADR-045 // Amendment 2026-05-23 (intent-not-structure), the model emits // intent; the backend constructs the typed sub-queries from // templates. The v1 default-path now used only when the // classifier shows it's necessary. See DecomposeArgs. ActionDecompose = "decompose" )
RouteAction values for RouteDecision.Action. Closed enum per ADR-045 section "Why this is not just-a-better-classifier" — the four actions are the load-bearing taxonomy. Validate rejects values outside this set so structured-emit retries can trigger per ADR-035.
const ( // PredicateResearchRequested marks a research-pipeline loop entity // as the target of a research_graph tool invocation. Triple // subject = research-loop entity ID; object = the topic string. // R0 of the rule chain watches for this triple to kick off the // chain. PredicateResearchRequested = "research.requested" // PredicateResearchTopic carries the topic verbatim. Distinct from // PredicateResearchRequested because the latter doubles as the // chain-kickoff trigger; this predicate is the durable record of // what the parent actually asked for. PredicateResearchTopic = "research.topic" // PredicateResearchHint stamps a single hint key/value pair. One // triple per hint, with the key encoded in the Object field as // "<key>=<value>". Multi-triple is preferred over JSON-stringified // hint maps so graph queries can filter on specific hint keys. PredicateResearchHint = "research.hint" // PredicateResearchBudgetTokens carries the resolved per-call token // budget (after defaulting). Stored as string per Triple.Object // shape; consumers parse to int. PredicateResearchBudgetTokens = "research.budget_tokens" // PredicateResearchMaxIterations carries the resolved refine-loop // cap (after defaulting). PredicateResearchMaxIterations = "research.max_iterations" // PredicateResearchParentLoop links the research-pipeline loop // back to its parent loop entity ID. Stamped by the research_graph // tool from call.LoopID so the continuation rule can route the // SearchResult back to the right caller. PredicateResearchParentLoop = "research.parent_loop" // PredicateLoopRole stamps the loop entity's role. Same convention // as other agentic loops; lets ops dashboards filter // research-pipeline loops without parsing intent payloads. PredicateLoopRole = "loop.role" )
Predicate constants for triples emitted by the research_graph chain. Every predicate lives under the research.* namespace so graph queries can filter chain triples cleanly without colliding with other agentic systems.
Triple emission discipline (ADR-028):
- Bulky payloads (synthesis text, evidence body) live in ObjectStore via ContentStorable; triples carry the ref.
- LLM-authored predicates (eventual route_rationale and synthesis predicates introduced in PR 3 and PR 5) MUST default to WithRuleOpaque(true) per feedback_llm_authored_predicates_rule_opaque — rules branch on typed fields, not free-form text. PR 1 reserves the namespace here; the named constants land with the PRs that emit them so the contract is reviewable alongside the emission.
const ( SeedRefTypeName = "name" SeedRefTypePartialID = "partial_id" SeedRefTypeCandidateIndex = "candidate_index" )
SeedRefType constants for WalkSeedsArgs.Seeds[*].RefType. Closed enum enforced by ParseWalkSeedsArgs. The backend (execute_subqueries) interprets the ref string differently per type: a "name" matches the entity's display name, a "partial_id" matches any suffix of the 6-part federated ID, a "candidate_index" indexes into the upstream ClassifierOutput candidate list.
const ( DecomposeScopeNarrow = "narrow" DecomposeScopeMedium = "medium" DecomposeScopeBroad = "broad" )
DecomposeScope constants for DecomposeArgs.Scope. Closed enum; empty resolves to ScopeMedium at the backend so a model that omits the field still routes deterministically.
const DefaultBudgetTokens = 4000
DefaultBudgetTokens is ResearchIntent.BudgetTokens default per docs/operations/22-adr045-phase1-plan.md PR 1 spec.
const DefaultMaxIterations = 5
DefaultMaxIterations is ResearchIntent.MaxIterations default. The refine loop cap on R4 ships with the same default — see ADR-045 rule chain R4.
const PipelineRole = "research_pipeline"
PipelineRole is the role string carried on the research-pipeline LoopEntity in AGENT_LOOPS. Lets downstream tooling (trajectory viewers, ops dashboards) filter research loops from coordinator and task loops without parsing intent payloads.
const TripleSource = "agent-research-graph"
TripleSource is the Source field on triples emitted by the research_graph tool. Mirrors the decide / write_todos convention so operators can distinguish chain-kickoff triples from later component emissions in graph queries.
Variables ¶
This section is empty.
Functions ¶
func IsValidDecomposeScope ¶
IsValidDecomposeScope reports whether s is one of the closed decompose-scope values. Empty string is treated as valid (it resolves to "medium" at the backend); callers that want to reject empty should test before calling.
func IsValidRouteAction ¶
IsValidRouteAction reports whether s is one of the four canonical router actions. Exported so route_search's structured-emit validator can reuse the closed set.
func IsValidSeedRefType ¶
IsValidSeedRefType reports whether s is one of the closed seed reference types. Exported so component validators can reuse the set.
func RegisterPayloads ¶
func RegisterPayloads(reg *payloadregistry.Registry) error
RegisterPayloads registers the three research payload types with the supplied registry. Called from payloadbuiltins.Register at process bootstrap so every production binary picks up the types without extra wiring.
Mirrors the agentic.RegisterPayloads + agentic/operating-model shape so the bootstrap aggregator can call this uniformly.
Types ¶
type Candidate ¶
type Candidate struct {
// EntityID is the matching graph entity. Required.
EntityID string `json:"entity_id"`
// Label is a human-readable name for the entity. Optional; empty
// when the candidate source doesn't carry a label (some graph
// queries return only IDs).
Label string `json:"label,omitempty"`
// Type is the entity's domain type when known (e.g. "drone",
// "sensor"). Optional.
Type string `json:"type,omitempty"`
// Relevance is the within-tier ranking score (higher = better).
// Per ADR-045 Phase 1, cross-tier comparison isn't meaningful —
// PR 4 keeps per-tier ordering + recency tie-break; Phase 2 may
// add a learned ranker.
Relevance float64 `json:"relevance,omitempty"`
// SnippetText is a short inline preview useful for prompt
// injection. Omit when the candidate has no readable preview.
SnippetText string `json:"snippet_text,omitempty"`
// Tier is the retrieval tier that surfaced this hit. Same
// taxonomy as Evidence.Tier in result.go so the evidence schema
// stays consistent across PR 2 (this file) and PR 4.
Tier string `json:"tier"`
// Source is the retrieval source within the tier — e.g.
// "classifier_chain", "search_graph". Required so PR 4 can
// classify candidates by retrieval pathway.
Source string `json:"source"`
}
Candidate is one entity hit the classifier surfaced for the topic. Mirrors the search_graph EntityDigest shape (entity_id + label + type + relevance score + optional snippet) so PR 4 (execute_subqueries) can consume nl_classify's output and search- graph's output through one evidence schema.
Provenance is mandatory: every Candidate carries the Tier ("0" keyword / "1" embedding/BM25 / "2" neural — Phase 2) and Source (the retrieval method, e.g. "classifier_chain", "search_graph") so downstream rules can filter by retrieval pathway.
type ClassifierOutput ¶
type ClassifierOutput struct {
// Topic is the topic the classifier ran on. Echoed so the
// downstream consumer (route_search) doesn't have to read the
// intent payload separately.
Topic string `json:"topic"`
// Tier is which classifier tier produced the hint set:
// "0" (keyword), "1" (embedding/BM25), or "3" (LLM). The
// numbering follows ClassifierChain conventions (T0/T1/T2/T3 — T2
// neural is Phase 2, not currently emitted). Empty/"0" is the
// default fall-through when no tier matched.
//
// IMPORTANT: this is the *classifier* tier and shares no enum
// with [Candidate.Tier] / [Evidence.Tier], which are *retrieval*
// tiers ("0"/"1"/"2"). A LLM-tier classifier may surface
// Tier="3" here while its surfaced candidates carry Tier="0" or
// "1". Don't copy this string into a Candidate without
// re-mapping — Candidate.Validate rejects "3".
Tier string `json:"tier,omitempty"`
// Intent is the LLM-or-embedding-detected intent classification.
// Empty when the keyword tier matched (KeywordClassifier doesn't
// set Intent — its semantics live in the Options map).
Intent string `json:"intent,omitempty"`
// Confidence is the classifier's confidence in the hint set.
// 1.0 for keyword matches, <=1.0 from embedding similarity.
Confidence float64 `json:"confidence,omitempty"`
// Hints is the operator-facing flattened classifier output: time
// range, geo bounds, path intent, aggregation type, etc. Wire
// shape matches ClassificationResult.Options so consumers can
// reconstruct a SearchOptions when they need to re-execute.
Hints map[string]any `json:"hints,omitempty"`
// Candidates is the initial candidate set: entities the
// classifier surfaced, with scores + snippets where available.
// Empty when the topic produced no hits — PR 3's route_search
// will choose "retighten" or "decompose" in that case.
Candidates []Candidate `json:"candidates,omitempty"`
// Degraded is set when the candidate retrieval fell back to a
// less-rich path (e.g. server-side semantic fallback). Carried
// through so PR 3's router can treat the candidates as
// advisory.
Degraded bool `json:"degraded,omitempty"`
// DegradedReason is a short operator-readable label describing
// the fallback path when Degraded is true. Empty otherwise.
DegradedReason string `json:"degraded_reason,omitempty"`
}
ClassifierOutput is the nl_classify component's emit: the classifier hints (extracted from ClassificationResult.Options) plus the initial candidate set surfaced by executing the resulting SearchOptions against the graph. R1 fires on this payload (via the classify.complete.<loop_id> trigger key) and route_search (PR 3) consumes it to choose one of the four routing actions.
func (*ClassifierOutput) MarshalJSON ¶
func (p *ClassifierOutput) MarshalJSON() ([]byte, error)
MarshalJSON implements json.Marshaler with the alias-recursion guard.
func (*ClassifierOutput) Schema ¶
func (p *ClassifierOutput) Schema() message.Type
Schema implements message.Payload.
func (*ClassifierOutput) UnmarshalJSON ¶
func (p *ClassifierOutput) UnmarshalJSON(data []byte) error
UnmarshalJSON implements json.Unmarshaler.
func (*ClassifierOutput) Validate ¶
func (p *ClassifierOutput) Validate() error
Validate implements message.Payload. Topic is required; Candidates are individually validated when present.
type DecompTrace ¶
type DecompTrace struct {
// RouterAction is the value emitted by route_search at the chain's
// first LLM judgment. One of the four ActionXxx constants.
RouterAction string `json:"router_action,omitempty"`
// SubQueries records the decomposed sub-queries the router
// generated when RouterAction == ActionDecompose. Free-form per
// Phase 1; typed sub-query schemas land with PR 3's router action
// arg schemas.
SubQueries []map[string]any `json:"sub_queries,omitempty"`
// SeedEntities records the walk_seeds entity set when
// RouterAction == ActionWalkSeeds.
SeedEntities []string `json:"seed_entities,omitempty"`
// RetightenRounds records how many times R2's retighten branch
// fired before terminating (success, cap, or downstream branch).
RetightenRounds int `json:"retighten_rounds,omitempty"`
}
DecompTrace records what decomposition the chain actually performed, for operator review of router quality. The shape is deliberately loose in Phase 1 — operator trajectories will dictate whether stricter typing is worth the churn (see plan doc PR 5 open-questions).
type DecomposeArgs ¶
type DecomposeArgs struct {
// Axes lists the dimensions of decomposition (e.g.
// "entity_type", "time", "spatial", "relationship"). Free-form
// strings; backend matches against its known decomposition
// templates.
Axes []string `json:"axes"`
// Focus is the central concept to decompose around. Anchors the
// backend's template selection — e.g. "sensor maintenance
// events" or "drone telemetry anomalies".
Focus string `json:"focus"`
// Scope is the breadth of decomposition. One of the
// DecomposeScope* constants. Empty resolves to "medium" at the
// backend.
Scope string `json:"scope,omitempty"`
}
DecomposeArgs is the typed view of RouteDecision.Args when Action == ActionDecompose. Intent-shaped per ADR-045 Amendment 2026-05-23: the model emits decomposition INTENT (what to decompose along, the central concept, the breadth); the backend (execute_subqueries) constructs the typed sub-queries from templates keyed on Axes + Focus. The model isn't asked to spell sub-query types it can't reliably produce.
type Evidence ¶
type Evidence struct {
// EntityID is the graph entity this evidence refers to. Required.
EntityID string `json:"entity_id"`
// Tier is the retrieval tier that surfaced this hit: "0" (rules /
// predicate queries), "1" (BM25), or "2" (neural — deferred to
// Phase 2). Operators may use it to filter evidence by retrieval
// method.
Tier string `json:"tier"`
// Source is the retrieval source within the tier — e.g.
// "classifier", "predicate_walk", "bm25_index". Required.
Source string `json:"source"`
// Score is the within-tier ranking score (higher = better).
// Cross-tier comparison is not meaningful in Phase 1 (per-tier
// ordering + recency tie-break); Phase 2 may add a learned ranker.
Score float64 `json:"score,omitempty"`
// SnippetText is a short inline preview (prompt-injection
// friendly). Omit when the evidence has no readable preview.
SnippetText string `json:"snippet_text,omitempty"`
// ObjectStoreRef is the ObjectStore key for the full body when
// the evidence has bulk content. Empty when the evidence is fully
// expressed in the EntityID + triples on the graph.
ObjectStoreRef string `json:"objectstore_ref,omitempty"`
}
Evidence is one item in SearchResult.Evidence — a single entity hit or evidence snippet the chain surfaced. Provenance is mandatory: every evidence item carries enough metadata (EntityID + Tier + Source) for the caller to verify it back against the graph and for synthesize_answer's quote-back validation to reject fabricated refs.
ObjectStoreRef is set when the body of the evidence (long text, document, etc.) lives in ObjectStore; consumers read it via that ref. SnippetText carries a short inline preview for prompt-injection readability without triggering ObjectStore round-trips on every hit.
type Intent ¶
type Intent struct {
// Topic is the natural-language research question or subject.
// Required.
Topic string `json:"topic"`
// Hints is an optional, free-form map of classifier hints. Phase 1
// keeps the shape flexible; Phase 2 may add typed subtypes once
// operator trajectories show what hints get passed.
Hints map[string]string `json:"hints,omitempty"`
// BudgetTokens caps total LLM token spend across the chain. Zero
// falls back to DefaultBudgetTokens at consumption time so callers
// can omit it.
BudgetTokens int `json:"budget_tokens,omitempty"`
// MaxIterations caps the refine loop in R4. Zero falls back to
// DefaultMaxIterations.
MaxIterations int `json:"max_iterations,omitempty"`
}
Intent is the caller's request: a free-form topic plus optional hints, plus per-call budgets. Per ADR-045's v2 amendment, the intent carries only what the caller actually knows — no entity IDs, no predicate names, no tier mix; those are outputs of the chain.
Hints is intentionally a map[string]string in Phase 1: trajectory data from operator use will inform whether typed hint subtypes are worth adding in Phase 2 (see plan doc Risks for PR 1). Keys are classifier-parseable strings ("entity_kind", "domain", "recency", etc.); unknown keys are passed through and ignored downstream rather than rejected, so operators can experiment without schema churn.
func (*Intent) MarshalJSON ¶
MarshalJSON implements json.Marshaler. Uses the alias pattern to avoid recursion through the Payload interface (the registry decodes via this method).
func (*Intent) ResolvedBudgetTokens ¶
ResolvedBudgetTokens returns BudgetTokens or DefaultBudgetTokens when zero. Use this at consumption time so callers can omit the field and the chain still has a budget cap.
func (*Intent) ResolvedMaxIterations ¶
ResolvedMaxIterations returns MaxIterations or DefaultMaxIterations when zero.
func (*Intent) UnmarshalJSON ¶
UnmarshalJSON implements json.Unmarshaler. Mirrors MarshalJSON's alias pattern for the round-trip through the payload registry.
type RetightenArgs ¶
type RetightenArgs struct {
// Topic is the refined topic string. Required.
Topic string `json:"topic"`
// Hints is the refined hint set. Optional; empty map and absent
// field both treated as no hints.
Hints map[string]string `json:"hints,omitempty"`
}
RetightenArgs is the typed view of RouteDecision.Args when Action == ActionRetighten. The refined topic + hints feed back into a second nl_classify run; R2's retighten branch caps the loop at MaxIterations=2.
type RouteDecision ¶
type RouteDecision struct {
// Action is the routing decision. Must be one of ActionXxx
// constants. UnmarshalJSON and Validate both enforce the enum.
Action string `json:"action"`
// Args carries the action-specific argument map. Per-action shapes
// (intent-not-structure per ADR-045 Amendment 2026-05-23 — model
// emits intent, backend constructs typed structures):
// - synthesize_directly: empty (uses classifier output as-is)
// - retighten: RetightenArgs {topic, hints}
// - walk_seeds: WalkSeedsArgs {seeds: [{ref, ref_type}]}
// ref_type ∈ {"name","partial_id","candidate_index"};
// backend resolves to full 6-part entity IDs
// - decompose: DecomposeArgs {axes, focus, scope}
// backend constructs typed sub-queries
// The map is intentionally loose at the wire level so the payload
// stays uniform; strict per-action shape checks live in the
// Parse*Args helpers below, called by the consuming component
// after a successful Validate() of the action enum.
Args map[string]any `json:"args,omitempty"`
// Rationale is the LLM's natural-language justification for the
// chosen action. Captured for operator trajectory review;
// rule-opaque per discipline memory.
Rationale string `json:"rationale,omitempty"`
}
RouteDecision is route_search's structured-emit output: one of four routing actions, action-specific args, and a free-form rationale for trajectory review. Phase 1's PR 3 specializes the LLM prompt around these four actions; ADR-045 commits to the closed enum at the schema level (Phase 2 may add actions, but Phase 1 ships strict).
Per feedback_llm_authored_predicates_rule_opaque, when this payload is stamped onto loop entity triples, the Rationale field's triple-predicate is published with WithRuleOpaque(true). Rules should not pattern-match on rationale prose — they branch on the typed Action field only.
func (*RouteDecision) MarshalJSON ¶
func (p *RouteDecision) MarshalJSON() ([]byte, error)
MarshalJSON implements json.Marshaler with the alias-recursion guard.
func (*RouteDecision) ParseDecomposeArgs ¶
func (p *RouteDecision) ParseDecomposeArgs() (DecomposeArgs, error)
ParseDecomposeArgs decodes p.Args into DecomposeArgs and validates shape. Returns an error if p.Action != ActionDecompose, if required fields are missing, or if Scope is non-empty and outside the closed enum.
func (*RouteDecision) ParseRetightenArgs ¶
func (p *RouteDecision) ParseRetightenArgs() (RetightenArgs, error)
ParseRetightenArgs decodes p.Args into RetightenArgs and validates shape. Returns an error if p.Action != ActionRetighten, if Topic is empty, or if any Hints value is empty.
func (*RouteDecision) ParseWalkSeedsArgs ¶
func (p *RouteDecision) ParseWalkSeedsArgs() (WalkSeedsArgs, error)
ParseWalkSeedsArgs decodes p.Args into WalkSeedsArgs and validates shape. Returns an error if p.Action != ActionWalkSeeds, if Seeds is empty, or if any seed has an empty Ref or an out-of-enum RefType.
func (*RouteDecision) Schema ¶
func (p *RouteDecision) Schema() message.Type
Schema implements message.Payload.
func (*RouteDecision) UnmarshalJSON ¶
func (p *RouteDecision) UnmarshalJSON(data []byte) error
UnmarshalJSON implements json.Unmarshaler with strict action enum enforcement. Schema-violating values fail at decode rather than surfacing as nil-action downstream.
func (*RouteDecision) Validate ¶
func (p *RouteDecision) Validate() error
Validate implements message.Payload. Enforces the closed action enum. Args content is not validated here — per-action arg shapes are checked by the consuming component (PR 3+).
type SearchResult ¶
type SearchResult struct {
// Evidence is the set of hits that informed Synthesis. Every
// evidence item synthesize_answer references must appear here;
// quote-back validation enforces this in PR 5.
Evidence []Evidence `json:"evidence,omitempty"`
// Synthesis is the natural-language answer produced by
// synthesize_answer. Refs quoted in the prose must resolve to
// Evidence items above.
Synthesis string `json:"synthesis"`
// DecompTrace is the per-call audit of the chain's routing
// choices and decompositions. Optional — present when the chain
// took a non-trivial path (anything beyond synthesize_directly).
DecompTrace *DecompTrace `json:"decomp_trace,omitempty"`
// TokensUsed is the total LLM-token spend across the chain's
// LLM calls (route_search + assess_sufficiency + synthesize_answer
// + any retighten/refine repetitions).
TokensUsed int `json:"tokens_used,omitempty"`
// Iterations is the total refine-loop iterations the chain
// executed before terminating. Distinct from RetightenRounds in
// DecompTrace — Iterations counts R4's refine loop (capped by
// ResearchIntent.MaxIterations); RetightenRounds counts R2's
// retighten branch (capped at 2).
Iterations int `json:"iterations,omitempty"`
}
SearchResult is the chain's terminal output, returned to the parent loop via the continuation rule. Carries synthesis text plus the evidence refs synthesize_answer quoted back, plus the decomp trace for trajectory review.
func (*SearchResult) MarshalJSON ¶
func (p *SearchResult) MarshalJSON() ([]byte, error)
MarshalJSON implements json.Marshaler with the alias-recursion guard.
func (*SearchResult) Schema ¶
func (p *SearchResult) Schema() message.Type
Schema implements message.Payload.
func (*SearchResult) UnmarshalJSON ¶
func (p *SearchResult) UnmarshalJSON(data []byte) error
UnmarshalJSON implements json.Unmarshaler.
func (*SearchResult) Validate ¶
func (p *SearchResult) Validate() error
Validate implements message.Payload. Synthesis is required (the chain's terminal emit always carries at least the synthesized answer; failure paths emit a degraded result with an error string rather than an empty Synthesis). Evidence items are individually validated when present.
type SeedRef ¶
type SeedRef struct {
// Ref is the reference itself — a display name, a partial
// federated ID, or a stringified index into the upstream
// classifier candidate list. Interpretation depends on RefType.
Ref string `json:"ref"`
// RefType disambiguates how the backend resolves Ref. Closed
// enum.
RefType string `json:"ref_type"`
}
SeedRef carries one seed reference for WalkSeedsArgs. RefType is one of SeedRefType*.
type WalkSeedsArgs ¶
type WalkSeedsArgs struct {
Seeds []SeedRef `json:"seeds"`
}
WalkSeedsArgs is the typed view of RouteDecision.Args when Action == ActionWalkSeeds. Intent-shaped per ADR-045 Amendment 2026-05-23: the model emits SEED REFERENCES (names, partial IDs, or indices into the classifier candidate list); the backend resolves to full 6-part entity IDs via the entity index. The model isn't asked to spell full IDs it can't reliably produce.