feat(epistemics): compile truth evidence bundle
CI / test (push) Successful in 24s

This commit is contained in:
m
2026-08-22 13:37:22 +03:00
parent e9bb42cd07
commit 32e33e3050
5 changed files with 121 additions and 2 deletions
+38
View File
@@ -0,0 +1,38 @@
package epistemics
type BeliefState string
const (
BeliefProvisional BeliefState = "PROVISIONAL"
BeliefConflicted BeliefState = "CONFLICTED"
BeliefVerified BeliefState = "VERIFIED"
)
type Observation struct {
ID ObservationID
SourceSnapshot SourceSnapshotID
Excerpt SourceExcerptID
Text string
ObservedAt int64
}
type Claim struct {
ID ClaimID
Statement string
ObservationID ObservationID
CitationID CitationID
Verification VerificationState
}
type Belief struct {
ID BeliefID
ClaimID ClaimID
State BeliefState
}
type TruthBundle struct {
Observation Observation
Claim Claim
Belief Belief
Citation Citation
}
+2 -2
View File
@@ -1,5 +1,5 @@
{
"task_file": "dev/tasks/DEV-000133-work-graph-persistence.json",
"task_id": "DEV-000133",
"task_file": "dev/tasks/DEV-000134-truth-bundle-compiler.json",
"task_id": "DEV-000134",
"task_revision": 1
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000134",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "e9bb42c",
"risk_tier": "E",
"intent": "Implement the first Truth runtime compiler that produces typed Observation, Claim, Belief and Citation values from an exact source chain.",
"requirement_ids": ["WP-100", "Observation/Claim/Belief", "Truth citation provenance"],
"allowed_change_scope": {"modules": ["epistemics"], "paths": ["contracts/epistemics/truth.go", "modules/epistemics/domain/truth.go", "tests/research_contracts/truth_bundle_test.go", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000134-truth-bundle-compiler.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "deploy/**", "database/**", "adapters/**", "cmd/**", "runtime/**", ".gitea/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["DEV-000109 research contracts", "DEV-000110 citation resolver"], "may_change": ["Typed Truth bundle contracts and pure compiler"]},
"semantic_owner": "epistemics",
"canonical_state": ["Typed TruthBundle value only; persistence follows"],
"state_transitions": ["source chain -> observed observation -> unverified claim -> provisional/conflicted belief"],
"side_effects_and_privileges": ["Pure compiler; no fetching, external mutation or authority grant"],
"persistence_and_migrations": ["Truth persistence is a later task"],
"failure_retry_restart": ["Broken source chain rejects; conflict remains visible"],
"assumptions": ["Research executor will supply exact source snapshots/excerpts"],
"acceptance_scenarios": ["Observation/claim/citation refs align", "Conflict produces CONFLICTED belief", "Broken source chain is rejected"],
"required_tests": ["go test ./...", "go vet ./...", "preflight", "check-diff", "check-architecture", "independent", "git diff --check"],
"terminal_gap_states": ["BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_CONFLICT"]
}
+31
View File
@@ -0,0 +1,31 @@
package domain
import (
"errors"
"strings"
"git.cin.su/m/Assistant/contracts/epistemics"
)
var ErrInvalidTruthBundle = errors.New("invalid truth bundle")
func CompileObservedClaim(source epistemics.Source, snapshot epistemics.SourceSnapshot, excerpt epistemics.SourceExcerpt, statement string, observedAt int64, conflict epistemics.ConflictState) (epistemics.TruthBundle, error) {
if strings.TrimSpace(statement) == "" || observedAt < 1 {
return epistemics.TruthBundle{}, ErrInvalidTruthBundle
}
citation, err := epistemics.ResolveCitation(source, snapshot, excerpt, epistemics.EvidenceObserved, epistemics.CoverageComplete, conflict, observedAt)
if err != nil {
return epistemics.TruthBundle{}, ErrInvalidTruthBundle
}
observation := epistemics.Observation{ID: epistemics.ObservationID("observation:" + string(excerpt.ID)), SourceSnapshot: snapshot.ID, Excerpt: excerpt.ID, Text: excerpt.Text, ObservedAt: observedAt}
claim := epistemics.Claim{ID: epistemics.ClaimID("claim:" + string(excerpt.ID)), Statement: statement, ObservationID: observation.ID, CitationID: citation.ID, Verification: epistemics.VerificationUnverified}
state := epistemics.BeliefProvisional
if conflict == epistemics.ConflictPresent || conflict == epistemics.ConflictUnknown {
state = epistemics.BeliefConflicted
}
bundle := epistemics.TruthBundle{Observation: observation, Claim: claim, Belief: epistemics.Belief{ID: epistemics.BeliefID("belief:" + string(excerpt.ID)), ClaimID: claim.ID, State: state}, Citation: citation}
if bundle.Observation.SourceSnapshot != snapshot.ID || bundle.Claim.ObservationID != bundle.Observation.ID || bundle.Claim.CitationID != bundle.Citation.ID || bundle.Belief.ClaimID != bundle.Claim.ID {
return epistemics.TruthBundle{}, ErrInvalidTruthBundle
}
return bundle, nil
}
@@ -0,0 +1,27 @@
package research_contracts_test
import (
"testing"
"git.cin.su/m/Assistant/contracts/epistemics"
episdomain "git.cin.su/m/Assistant/modules/epistemics/domain"
)
func TestCompileObservedClaimPreservesEvidenceChainAndConflict(t *testing.T) {
source := epistemics.Source{ID: "source-1", Locator: "https://example.test/source"}
snapshot := epistemics.SourceSnapshot{ID: "snapshot-1", SourceID: source.ID, ContentDigest: "sha256:test", CapturedAtUnixMillis: 100, Freshness: epistemics.FreshnessCurrent}
excerpt := epistemics.SourceExcerpt{ID: "excerpt-1", SnapshotID: snapshot.ID, Text: "exact observation", Start: 0, End: 17}
bundle, err := episdomain.CompileObservedClaim(source, snapshot, excerpt, "The observed fact is true", 200, epistemics.ConflictPresent)
if err != nil || bundle.Claim.ObservationID != bundle.Observation.ID || bundle.Belief.State != epistemics.BeliefConflicted || bundle.Citation.Evidence != epistemics.EvidenceObserved {
t.Fatalf("invalid truth bundle: %#v %v", bundle, err)
}
}
func TestCompileObservedClaimRejectsBrokenSourceChain(t *testing.T) {
source := epistemics.Source{ID: "source-1", Locator: "https://example.test/source"}
snapshot := epistemics.SourceSnapshot{ID: "snapshot-1", SourceID: "other-source", ContentDigest: "sha256:test", CapturedAtUnixMillis: 100, Freshness: epistemics.FreshnessCurrent}
excerpt := epistemics.SourceExcerpt{ID: "excerpt-1", SnapshotID: snapshot.ID, Text: "exact observation", Start: 0, End: 17}
if _, err := episdomain.CompileObservedClaim(source, snapshot, excerpt, "statement", 200, epistemics.ConflictNone); err != episdomain.ErrInvalidTruthBundle {
t.Fatalf("broken chain was accepted: %v", err)
}
}