package domain import ( "errors" "fmt" "git.cin.su/m/Assistant/contracts/capability" ) var ErrInsufficientReflexEvidence = errors.New("insufficient reflex evidence") func ValidateReflexRevision(v capability.ReflexRevision) error { if v.ReflexID == "" || v.SourceSkillRevision < 1 || v.Kind == "" || v.ImplementationRef == "" || v.Lifecycle == "" || v.RecordedAt < 1 { return fmt.Errorf("invalid reflex revision") } if (v.Lifecycle == capability.ReflexActive || v.Lifecycle == capability.ReflexValidating) && len(v.ValidationEvidenceRefs) == 0 { return fmt.Errorf("active reflex requires evidence") } return nil } // CandidateReflex admits only a bounded, deterministic implementation from an // already active Skill with multiple verified results. It never activates it. func CandidateReflex(skill capability.SkillProfile, value capability.ReflexRevision, verifierEvidence capability.SkillEvidenceRef, recordedAt int64) (capability.ReflexRevision, error) { if err := ValidateSkillProfile(skill); err != nil || skill.Status != capability.SkillActive || skill.VerifiedOutcomeCount < 3 || verifierEvidence == "" || recordedAt < 1 { return capability.ReflexRevision{}, ErrInsufficientReflexEvidence } if value.Kind != capability.ReflexDeterministicCode && value.Kind != capability.ReflexRule && value.Kind != capability.ReflexWorkflow && value.Kind != capability.ReflexBoundedAdapter { return capability.ReflexRevision{}, ErrInsufficientReflexEvidence } value.SourceSkillRevision = skill.Revision value.Lifecycle = capability.ReflexCandidate value.ValidationEvidenceRefs = appendEvidence(value.ValidationEvidenceRefs, verifierEvidence) value.LastValidatedAt = 0 value.RecordedAt = recordedAt if err := ValidateReflexRevision(value); err != nil { return capability.ReflexRevision{}, err } return value, nil } func ValidateAndActivateReflex(value capability.ReflexRevision, independentEvidence capability.SkillEvidenceRef, mustNotWorsen bool, recordedAt int64) (capability.ReflexRevision, error) { if err := ValidateReflexRevision(value); err != nil || value.Lifecycle != capability.ReflexCandidate || independentEvidence == "" || !mustNotWorsen || recordedAt < 1 { return capability.ReflexRevision{}, ErrInsufficientReflexEvidence } next := value next.Revision++ next.Lifecycle = capability.ReflexActive next.ValidationEvidenceRefs = appendEvidence(next.ValidationEvidenceRefs, independentEvidence) next.LastValidatedAt = recordedAt next.RecordedAt = recordedAt return next, nil } // ObserveShadow keeps normal execution non-blocking. Divergence marks the // revision stale for governed revalidation; it cannot replace implementation. func ObserveShadow(value capability.ReflexRevision, comparison capability.ShadowComparison) (capability.ReflexRevision, bool, error) { if err := ValidateReflexRevision(value); err != nil || comparison.ReflexID != value.ReflexID || comparison.ReflexRevision != value.Revision || comparison.ObservedAt < 1 { return capability.ReflexRevision{}, false, ErrInsufficientReflexEvidence } next := value next.ExecutionCount++ if comparison.Diverged || !comparison.VerifierPassed { next.DivergenceCount++ next.Lifecycle = capability.ReflexStale next.RecordedAt = comparison.ObservedAt return next, true, nil } return next, false, nil } func RevalidationRequired(value capability.ReflexRevision, dependencyChanged bool, correctionCount int64, now int64) bool { if value.Lifecycle == capability.ReflexRetired || value.Lifecycle == capability.ReflexStale || value.Lifecycle == capability.ReflexDegraded { return true } if dependencyChanged || (value.Revalidation.RevalidateAfterCorrectionCount != nil && correctionCount >= *value.Revalidation.RevalidateAfterCorrectionCount) { return true } return value.Revalidation.MaxAgeMillis != nil && value.LastValidatedAt > 0 && now-value.LastValidatedAt >= *value.Revalidation.MaxAgeMillis } func appendEvidence(values []capability.SkillEvidenceRef, value capability.SkillEvidenceRef) []capability.SkillEvidenceRef { for _, existing := range values { if existing == value { return values } } return append(values, value) } func ReflexFallback(d capability.FastPathDecision) string { if d.Decision == capability.FastPathApplied { return "reflex" } return "skill->reasoning" }