package epistemics import ( "errors" "strings" ) var ErrCitationEvidenceMismatch = errors.New("citation evidence does not match source chain") type ResearchPlanID string type SourceID string type SourceSnapshotID string type SourceExcerptID string type CitationID string type FreshnessState string type CoverageState string type ConflictState string type EvidenceKind string type ResearchFindingState string const ( FreshnessCurrent FreshnessState = "CURRENT" FreshnessStale FreshnessState = "STALE" FreshnessUnknown FreshnessState = "UNKNOWN" CoverageComplete CoverageState = "COMPLETE" CoveragePartial CoverageState = "PARTIAL" CoverageUnknown CoverageState = "UNKNOWN" ConflictNone ConflictState = "NONE" ConflictPresent ConflictState = "PRESENT" ConflictUnknown ConflictState = "UNKNOWN" EvidenceObserved EvidenceKind = "OBSERVED" EvidenceAssumed EvidenceKind = "ASSUMED" FindingSupported ResearchFindingState = "SUPPORTED" FindingStale ResearchFindingState = "STALE" FindingConflicted ResearchFindingState = "CONFLICTED" FindingUnknown ResearchFindingState = "UNKNOWN" ) type ResearchPlan struct { ID ResearchPlanID Question string SourceClasses []string FreshnessPolicy string CreatedAtUnixMillis int64 } type Source struct { ID SourceID Kind string Locator string Title string Publisher string } type SourceSnapshot struct { ID SourceSnapshotID SourceID SourceID ContentDigest string CapturedAtUnixMillis int64 Freshness FreshnessState } type SourceExcerpt struct { ID SourceExcerptID SnapshotID SourceSnapshotID Text string Start int64 End int64 } type Citation struct { ID CitationID ExcerptID SourceExcerptID Evidence EvidenceKind Coverage CoverageState Conflict ConflictState ResolvedAtUnixMillis int64 } // ResearchCandidate is read-only evidence returned by a connector. The // connector supplies the immutable snapshot and exact excerpt; the evaluator // never fetches, rewrites or invents either one. type ResearchCandidate struct { PropositionKey string Statement string Source Source Snapshot SourceSnapshot Excerpt SourceExcerpt Evidence EvidenceKind Coverage CoverageState Conflict ConflictState } type ResearchFinding struct { PropositionKey string Statement string Claim Claim Citations []Citation State ResearchFindingState Reason string } type ResearchResult struct { PlanID ResearchPlanID Findings []ResearchFinding EvaluatedAtUnixMillis int64 } // ResearchEvaluationRequest carries immutable evidence from a read-only // connector to the evaluator. The runtime never fetches or mutates a source // from this contract. type ResearchEvaluationRequest struct { Plan ResearchPlan Candidates []ResearchCandidate } type RepositoryResearchRequest struct { Plan ResearchPlan PropositionKey string SourceID SourceID Path string Statement string } type WebResearchRequest struct { Plan ResearchPlan PropositionKey string SourceID SourceID URL string Title string Statement string } // ResolveCitation creates evidence only when all identifiers form one exact // source chain. It deliberately does not fetch, infer or rewrite source text. func ResolveCitation(source Source, snapshot SourceSnapshot, excerpt SourceExcerpt, evidence EvidenceKind, coverage CoverageState, conflict ConflictState, resolvedAtUnixMillis int64) (Citation, error) { if source.ID == "" || strings.TrimSpace(source.Locator) == "" || snapshot.SourceID != source.ID || excerpt.SnapshotID != snapshot.ID || excerpt.Validate() != nil || resolvedAtUnixMillis < 1 || evidence == "" || coverage == "" || conflict == "" { return Citation{}, ErrCitationEvidenceMismatch } citation := Citation{ID: CitationID("citation:" + string(excerpt.ID)), ExcerptID: excerpt.ID, Evidence: evidence, Coverage: coverage, Conflict: conflict, ResolvedAtUnixMillis: resolvedAtUnixMillis} return citation, citation.Validate() } func (p ResearchPlan) Validate() error { if p.ID == "" || strings.TrimSpace(p.Question) == "" || len(p.SourceClasses) == 0 || strings.TrimSpace(p.FreshnessPolicy) == "" { return errors.New("research plan requires question, source classes and freshness policy") } return nil } func (s SourceSnapshot) Validate() error { if s.ID == "" || s.SourceID == "" || strings.TrimSpace(s.ContentDigest) == "" || s.CapturedAtUnixMillis < 1 || s.Freshness == "" { return errors.New("source snapshot requires source, digest, capture time and freshness") } return nil } func (e SourceExcerpt) Validate() error { if e.ID == "" || e.SnapshotID == "" || strings.TrimSpace(e.Text) == "" || e.Start < 0 || e.End < e.Start { return errors.New("source excerpt requires snapshot, text and valid offsets") } return nil } func (c Citation) Validate() error { if c.ID == "" || c.ExcerptID == "" || c.Evidence == "" || c.Coverage == "" || c.Conflict == "" || c.ResolvedAtUnixMillis < 1 { return errors.New("citation requires exact excerpt, evidence, coverage, conflict and resolution time") } return nil }