Files

95 lines
4.2 KiB
Go

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"
}