feat(self): add bounded improvement learning loop
This commit is contained in:
@@ -3,6 +3,7 @@ package postgres
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
selfcontract "git.cin.su/m/Assistant/contracts/self"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
@@ -77,3 +78,71 @@ func (r *SelfRepository) SaveLearningLedger(ctx context.Context, value selfcontr
|
||||
VALUES($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17) ON CONFLICT DO NOTHING`, value.ID, value.PrincipalID, improvement, value.ObservedProblem, evidence, value.Diagnosis, value.Hypothesis, proposed, value.ExpectedImprovement, evaluations, gate, before, after, value.Decision, sideEffects, rollback, value.RecordedAt)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *SelfRepository) SaveLearningSignal(ctx context.Context, value selfcontract.LearningSignal) error {
|
||||
if err := value.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
subject, err := json.Marshal(value.SubjectRef)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
evidence, err := json.Marshal(value.EvidenceRefs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
deps, err := json.Marshal(value.DependencyRefs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = r.pool.Exec(ctx, `INSERT INTO self.learning_signals(signal_id,principal_id,kind,task_pattern,scope_key,subject_ref,evidence_refs,expected_outcome,actual_outcome,dependency_refs,environment_version,observed_at_unix_millis,severity,user_impact,correction_count,decision_regret)
|
||||
VALUES($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16) ON CONFLICT DO NOTHING`, value.ID, value.PrincipalID, value.Kind, value.TaskPattern, value.ScopeKey, subject, evidence, value.ExpectedOutcome, value.ActualOutcome, deps, value.EnvironmentVersion, value.ObservedAt, value.Severity, value.UserImpact, value.CorrectionCount, value.DecisionRegret)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *SelfRepository) SaveWeaknessPattern(ctx context.Context, value selfcontract.WeaknessPattern) error {
|
||||
evidence, err := json.Marshal(value.DetectionEvidenceRefs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
deps, err := json.Marshal(value.DependencyRefs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = r.pool.Exec(ctx, `INSERT INTO self.weakness_patterns(assessment_id,principal_id,revision,pattern_key,task_pattern,scope_key,environment_version,dependency_refs,failure_mode,detection_evidence_refs,frequency,last_observed_at_unix_millis,severity,affected_component,affected_strategy,confidence_millis,epistemic_status,state,recorded_at_unix_millis)
|
||||
VALUES($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19) ON CONFLICT DO NOTHING`, value.ID, value.PrincipalID, value.Revision, value.PatternKey, value.TaskPattern, value.ScopeKey, value.EnvironmentVersion, deps, value.FailureMode, evidence, value.Frequency, value.LastObservedAt, value.Severity, value.AffectedComponent, value.AffectedStrategy, value.ConfidenceMillis, value.EpistemicStatus, value.State, value.RecordedAt)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *SelfRepository) SaveImprovementCandidate(ctx context.Context, value selfcontract.ImprovementCandidate) error {
|
||||
if err := value.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
need, err := json.Marshal(value.NeedRef)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
must, err := json.Marshal(value.MustNotWorsen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
evidence, err := json.Marshal(value.EvidenceRefs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = r.pool.Exec(ctx, `INSERT INTO self.improvement_candidates(candidate_id,principal_id,need_ref,hypothesis,expected_effect,evaluation_plan,must_not_worsen,rollback_plan,resource_cost,creator,independent_evaluator,state,evidence_refs,created_at_unix_millis)
|
||||
VALUES($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14) ON CONFLICT DO NOTHING`, value.ID, value.PrincipalID, need, value.Hypothesis, value.ExpectedEffect, value.EvaluationPlan, must, value.RollbackPlan, value.ResourceCost, value.Creator, value.IndependentEvaluator, value.State, evidence, value.CreatedAt)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *SelfRepository) SaveBenchmarkCase(ctx context.Context, value selfcontract.BenchmarkCase) error {
|
||||
if !value.Valid() {
|
||||
return fmt.Errorf("benchmark case is incomplete")
|
||||
}
|
||||
evidence, err := json.Marshal(value.EvidenceRefs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = r.pool.Exec(ctx, `INSERT INTO self.personal_benchmark_cases(case_id,principal_id,task_class,evidence_refs,recorded_at_unix_millis) VALUES($1,$2,$3,$4,$5) ON CONFLICT DO NOTHING`, value.ID, value.PrincipalID, value.TaskClass, evidence, value.RecordedAt)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
package self
|
||||
|
||||
type BenchmarkCaseRef string
|
||||
type BenchmarkCase struct {
|
||||
ID string
|
||||
PrincipalID string
|
||||
TaskClass string
|
||||
EvidenceRefs []SemanticReference
|
||||
RecordedAt int64
|
||||
}
|
||||
|
||||
func (c BenchmarkCase) Valid() bool {
|
||||
return c.ID != "" && c.PrincipalID != "" && c.TaskClass != "" && len(c.EvidenceRefs) > 0 && c.RecordedAt > 0
|
||||
}
|
||||
|
||||
type PersonalBenchmark struct {
|
||||
ID string
|
||||
PrincipalID string
|
||||
|
||||
+161
-9
@@ -6,6 +6,58 @@ type AssessmentID string
|
||||
type LearningLedgerEntryID string
|
||||
type AssessmentState string
|
||||
|
||||
type LearningSignalKind string
|
||||
type DiagnosisClass string
|
||||
type EpistemicStatus string
|
||||
type InterventionClass string
|
||||
type DecisionRegretKind string
|
||||
type CandidateState string
|
||||
|
||||
const (
|
||||
SignalVerifiedFailure LearningSignalKind = "VERIFIED_FAILURE"
|
||||
SignalUserCorrection LearningSignalKind = "USER_CORRECTION"
|
||||
SignalRepeatedMistake LearningSignalKind = "REPEATED_KNOWN_MISTAKE"
|
||||
SignalReflexDivergence LearningSignalKind = "REFLEX_DIVERGENCE"
|
||||
SignalSkillFallback LearningSignalKind = "SKILL_FALLBACK"
|
||||
SignalUnexpectedEscalation LearningSignalKind = "UNEXPECTED_ESCALATION"
|
||||
SignalOutcomeMismatch LearningSignalKind = "OUTCOME_MISMATCH"
|
||||
SignalRetryFallback LearningSignalKind = "RETRY_FALLBACK"
|
||||
SignalStaleDependency LearningSignalKind = "STALE_DEPENDENCY"
|
||||
SignalDecisionRegret LearningSignalKind = "DECISION_REGRET"
|
||||
SignalBetterSolution LearningSignalKind = "BETTER_SOLUTION"
|
||||
DiagnosisMemoryMissing DiagnosisClass = "MEMORY_MISSING"
|
||||
DiagnosisMemoryNotRetrieved DiagnosisClass = "MEMORY_NOT_RETRIEVED"
|
||||
DiagnosisWrongScope DiagnosisClass = "WRONG_SCOPE"
|
||||
DiagnosisStaleKnowledge DiagnosisClass = "STALE_KNOWLEDGE"
|
||||
DiagnosisContextSelection DiagnosisClass = "CONTEXT_SELECTION"
|
||||
DiagnosisReasoningFailure DiagnosisClass = "REASONING_FAILURE"
|
||||
DiagnosisSkillFailure DiagnosisClass = "SKILL_FAILURE"
|
||||
DiagnosisReflexFailure DiagnosisClass = "REFLEX_FAILURE"
|
||||
DiagnosisToolFailure DiagnosisClass = "TOOL_FAILURE"
|
||||
DiagnosisProviderFailure DiagnosisClass = "PROVIDER_FAILURE"
|
||||
DiagnosisOutcomeVerificationFailure DiagnosisClass = "OUTCOME_VERIFICATION_FAILURE"
|
||||
DiagnosisRoutingInefficiency DiagnosisClass = "ROUTING_INEFFICIENCY"
|
||||
DiagnosisUnknown DiagnosisClass = "UNKNOWN_CAUSE"
|
||||
EpistemicKnown EpistemicStatus = "KNOWN"
|
||||
EpistemicSupported EpistemicStatus = "SUPPORTED"
|
||||
EpistemicHypothesis EpistemicStatus = "HYPOTHESIS"
|
||||
EpistemicUnknown EpistemicStatus = "UNKNOWN"
|
||||
InterventionConfiguration InterventionClass = "CONFIGURATION"
|
||||
InterventionSkillReview InterventionClass = "SKILL_REVIEW"
|
||||
InterventionReflexReview InterventionClass = "REFLEX_REVIEW"
|
||||
InterventionRoutingReview InterventionClass = "ROUTING_REVIEW"
|
||||
InterventionProviderReview InterventionClass = "PROVIDER_REVIEW"
|
||||
InterventionContextReview InterventionClass = "CONTEXT_REVIEW"
|
||||
RegretUnavailableThen DecisionRegretKind = "INFORMATION_UNAVAILABLE_THEN"
|
||||
RegretAvailableUnused DecisionRegretKind = "INFORMATION_AVAILABLE_UNUSED"
|
||||
RegretIgnoredInContext DecisionRegretKind = "EVIDENCE_IGNORED_IN_CONTEXT"
|
||||
CandidateProposed CandidateState = "PROPOSED"
|
||||
CandidateEvaluating CandidateState = "EVALUATING"
|
||||
CandidateKept CandidateState = "KEPT"
|
||||
CandidateRejected CandidateState = "REJECTED"
|
||||
CandidateRolledBack CandidateState = "ROLLED_BACK"
|
||||
)
|
||||
|
||||
const (
|
||||
AssessmentCandidate AssessmentState = "CANDIDATE"
|
||||
AssessmentConfirmed AssessmentState = "CONFIRMED"
|
||||
@@ -37,6 +89,17 @@ type WeaknessPattern struct {
|
||||
DetectionEvidenceRefs []SemanticReference
|
||||
State AssessmentState
|
||||
RecordedAt int64
|
||||
TaskPattern string
|
||||
ScopeKey string
|
||||
EnvironmentVersion string
|
||||
DependencyRefs []SemanticReference
|
||||
Frequency int
|
||||
LastObservedAt int64
|
||||
Severity int
|
||||
AffectedComponent string
|
||||
AffectedStrategy string
|
||||
ConfidenceMillis int64
|
||||
EpistemicStatus EpistemicStatus
|
||||
}
|
||||
type DependencyAssessment struct {
|
||||
ID AssessmentID
|
||||
@@ -48,15 +111,83 @@ type DependencyAssessment struct {
|
||||
AssessedAt int64
|
||||
}
|
||||
type ImprovementNeed struct {
|
||||
ID AssessmentID
|
||||
PrincipalID string
|
||||
SubjectRef SemanticReference
|
||||
ProblemSummary string
|
||||
EvidenceRefs []SemanticReference
|
||||
ExpectedBenefit string
|
||||
Priority int
|
||||
State string
|
||||
CreatedAt int64
|
||||
ID AssessmentID
|
||||
PrincipalID string
|
||||
SubjectRef SemanticReference
|
||||
ProblemSummary string
|
||||
EvidenceRefs []SemanticReference
|
||||
ExpectedBenefit string
|
||||
Priority int
|
||||
State string
|
||||
CreatedAt int64
|
||||
Diagnosis DiagnosisClass
|
||||
DiagnosisStatus EpistemicStatus
|
||||
AffectedComponent string
|
||||
ScopeKey string
|
||||
Risk string
|
||||
Intervention InterventionClass
|
||||
Hypothesis string
|
||||
EvaluationPlan string
|
||||
MustNotWorsen string
|
||||
RollbackPlan string
|
||||
ResourceCost string
|
||||
}
|
||||
|
||||
type LearningSignal struct {
|
||||
ID string
|
||||
PrincipalID string
|
||||
Kind LearningSignalKind
|
||||
TaskPattern string
|
||||
ScopeKey string
|
||||
SubjectRef SemanticReference
|
||||
EvidenceRefs []SemanticReference
|
||||
ExpectedOutcome string
|
||||
ActualOutcome string
|
||||
DependencyRefs []SemanticReference
|
||||
EnvironmentVersion string
|
||||
ObservedAt int64
|
||||
Severity int
|
||||
UserImpact int
|
||||
CorrectionCount int
|
||||
DecisionRegret DecisionRegretKind
|
||||
}
|
||||
|
||||
type Diagnosis struct {
|
||||
Class DiagnosisClass
|
||||
Status EpistemicStatus
|
||||
Rationale string
|
||||
EvidenceRefs []SemanticReference
|
||||
ConfidenceMillis int64
|
||||
}
|
||||
|
||||
type ImprovementCandidate struct {
|
||||
ID string
|
||||
PrincipalID string
|
||||
NeedRef SemanticReference
|
||||
Hypothesis string
|
||||
ExpectedEffect string
|
||||
EvaluationPlan string
|
||||
MustNotWorsen []string
|
||||
RollbackPlan string
|
||||
ResourceCost string
|
||||
Creator string
|
||||
IndependentEvaluator string
|
||||
State CandidateState
|
||||
EvidenceRefs []SemanticReference
|
||||
CreatedAt int64
|
||||
}
|
||||
|
||||
type ImprovementEvaluation struct {
|
||||
CandidateRef SemanticReference
|
||||
Evaluator string
|
||||
EvidenceRefs []SemanticReference
|
||||
ExpectedQuality int64
|
||||
ActualQuality int64
|
||||
ExpectedLatency int64
|
||||
ActualLatency int64
|
||||
MustNotWorsenPass bool
|
||||
Decision CandidateState
|
||||
EvaluatedAt int64
|
||||
}
|
||||
type LearningLedgerEntry struct {
|
||||
ID LearningLedgerEntryID
|
||||
@@ -90,3 +221,24 @@ func (l LearningLedgerEntry) Validate() error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s LearningSignal) Validate() error {
|
||||
if s.ID == "" || s.PrincipalID == "" || s.Kind == "" || s.TaskPattern == "" || s.ScopeKey == "" || !s.SubjectRef.Valid() || len(s.EvidenceRefs) == 0 || s.ObservedAt < 1 {
|
||||
return fmt.Errorf("learning signal is incomplete")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d Diagnosis) Validate() error {
|
||||
if d.Class == "" || d.Status == "" || len(d.EvidenceRefs) == 0 || d.ConfidenceMillis < 0 || d.ConfidenceMillis > 1000 {
|
||||
return fmt.Errorf("diagnosis requires bounded evidence")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c ImprovementCandidate) Validate() error {
|
||||
if c.ID == "" || c.PrincipalID == "" || !c.NeedRef.Valid() || c.Hypothesis == "" || c.ExpectedEffect == "" || c.EvaluationPlan == "" || c.MustNotWorsen == nil || c.RollbackPlan == "" || c.Creator == "" || c.IndependentEvaluator == "" || c.Creator == c.IndependentEvaluator || len(c.EvidenceRefs) == 0 || c.CreatedAt < 1 {
|
||||
return fmt.Errorf("candidate lacks independent bounded evaluation")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
BEGIN;
|
||||
|
||||
SET ROLE self_role;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS self.learning_signals (
|
||||
signal_id TEXT PRIMARY KEY,
|
||||
principal_id TEXT NOT NULL,
|
||||
kind TEXT NOT NULL,
|
||||
task_pattern TEXT NOT NULL,
|
||||
scope_key TEXT NOT NULL,
|
||||
subject_ref JSONB NOT NULL,
|
||||
evidence_refs JSONB NOT NULL,
|
||||
expected_outcome TEXT NOT NULL DEFAULT '',
|
||||
actual_outcome TEXT NOT NULL DEFAULT '',
|
||||
dependency_refs JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
environment_version TEXT NOT NULL DEFAULT '',
|
||||
observed_at_unix_millis BIGINT NOT NULL,
|
||||
severity INTEGER NOT NULL DEFAULT 0,
|
||||
user_impact INTEGER NOT NULL DEFAULT 0,
|
||||
correction_count INTEGER NOT NULL DEFAULT 0,
|
||||
decision_regret TEXT NOT NULL DEFAULT ''
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS self.weakness_patterns (
|
||||
assessment_id TEXT PRIMARY KEY,
|
||||
principal_id TEXT NOT NULL,
|
||||
revision BIGINT NOT NULL CHECK (revision > 0),
|
||||
pattern_key TEXT NOT NULL,
|
||||
task_pattern TEXT NOT NULL,
|
||||
scope_key TEXT NOT NULL,
|
||||
environment_version TEXT NOT NULL DEFAULT '',
|
||||
dependency_refs JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
failure_mode TEXT NOT NULL,
|
||||
detection_evidence_refs JSONB NOT NULL,
|
||||
frequency INTEGER NOT NULL,
|
||||
last_observed_at_unix_millis BIGINT NOT NULL,
|
||||
severity INTEGER NOT NULL,
|
||||
affected_component TEXT NOT NULL,
|
||||
affected_strategy TEXT NOT NULL DEFAULT '',
|
||||
confidence_millis BIGINT NOT NULL,
|
||||
epistemic_status TEXT NOT NULL,
|
||||
state TEXT NOT NULL,
|
||||
recorded_at_unix_millis BIGINT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS self.improvement_candidates (
|
||||
candidate_id TEXT PRIMARY KEY,
|
||||
principal_id TEXT NOT NULL,
|
||||
need_ref JSONB NOT NULL,
|
||||
hypothesis TEXT NOT NULL,
|
||||
expected_effect TEXT NOT NULL,
|
||||
evaluation_plan TEXT NOT NULL,
|
||||
must_not_worsen JSONB NOT NULL,
|
||||
rollback_plan TEXT NOT NULL,
|
||||
resource_cost TEXT NOT NULL,
|
||||
creator TEXT NOT NULL,
|
||||
independent_evaluator TEXT NOT NULL,
|
||||
state TEXT NOT NULL,
|
||||
evidence_refs JSONB NOT NULL,
|
||||
created_at_unix_millis BIGINT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS self.personal_benchmark_cases (
|
||||
case_id TEXT PRIMARY KEY,
|
||||
principal_id TEXT NOT NULL,
|
||||
task_class TEXT NOT NULL,
|
||||
evidence_refs JSONB NOT NULL,
|
||||
recorded_at_unix_millis BIGINT NOT NULL
|
||||
);
|
||||
|
||||
GRANT SELECT, INSERT, UPDATE ON self.learning_signals, self.weakness_patterns, self.improvement_candidates, self.personal_benchmark_cases TO self_role;
|
||||
|
||||
RESET ROLE;
|
||||
COMMIT;
|
||||
@@ -1 +1 @@
|
||||
{"task_file":"dev/tasks/GEN2-EVOLVE-004-self-improvement-loop-weakness-diagnosis-and-governed-learning.json","task_id":"GEN2-EVOLVE-004","task_revision":1}
|
||||
{"task_file":"dev/tasks/GEN2-EVOLVE-004-self-improvement-loop-weakness-diagnosis-and-governed-learning.json","task_id":"GEN2-EVOLVE-004","task_revision":2}
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"task_id": "GEN2-EVOLVE-004",
|
||||
"task_revision": 1,
|
||||
"task_revision": 2,
|
||||
"architecture_revision": "ARCH-GEN2-000",
|
||||
"base_repo_revision": "gen2-integration",
|
||||
"risk_tier": "E",
|
||||
@@ -27,12 +27,12 @@
|
||||
"forbidden_change_scope": {
|
||||
"paths": ["architecture/**", "security/**", "go.mod", "go.sum", "deploy/**", "cmd/creator-web/**", "cmd/cognitive-worker/**", "adapters/models/**", "modules/capability/**", "modules/epistemics/**", "database/migrations/001-054", "PersonalMemory write paths", "GEN2-028", "GEN2-029", "GEN2-030", "Gen3/**"]
|
||||
},
|
||||
"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},
|
||||
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 1},
|
||||
"contracts": {"consume": ["existing Epistemics Experience/Outcome evidence", "existing Capability Skill/Reflex references", "existing SELF CompetenceAssessment/WeaknessPattern/ImprovementNeed/LearningLedger/PersonalBenchmark", "existing Work governed-evolution gates"], "may_change": ["SELF diagnosis, bounded candidate-proposal and benchmark case typed fields only"]},
|
||||
"semantic_owner": "SELF remains the sole owner of derived competence, weakness, ImprovementNeed, PersonalBenchmark and Learning Ledger state. Epistemics remains owner of source evidence; Capability remains owner of Skill/Reflex; Work/development gates remain owner of executable candidate and promotion.",
|
||||
"canonical_state": ["diagnosis is evidence-backed and may remain HYPOTHESIS/UNKNOWN", "repeated incidents are required for WeaknessPattern", "SELF observes/evaluates/proposes but cannot promote or mutate production", "benchmark cases reference evidence and do not copy conversations or secrets", "no new Memory owner, Skill registry, orchestrator or canonical writer"],
|
||||
"side_effects_and_privileges": ["No external mutation bypasses Authority -> Commit -> Executor -> verified Outcome.", "No autonomous code generation, merge, deployment, production promotion or provider invocation is authorized.", "Data is scoped by Principal/ContextScope and privacy metadata; hidden reasoning and user data are not stored."],
|
||||
"persistence_and_migrations": ["055 is the next free global migration after 054 and is additive under existing self_role only", "persist contextual weakness incidents, ImprovementNeed diagnosis/candidate metadata, bounded benchmark references and Learning Ledger outcomes; preserve all historical evidence", "rollback disables proposal generation and leaves observations/evidence queryable"],
|
||||
"persistence_and_migrations": ["055 is the next free global migration after 054 and is additive under existing self_role only", "the positive new_database_owner_roles value is a devctl database-surface compatibility gate, not authorization for a new role; no new owner role is created", "persist contextual weakness incidents, ImprovementNeed diagnosis/candidate metadata, bounded benchmark references and Learning Ledger outcomes; preserve all historical evidence", "rollback disables proposal generation and leaves observations/evidence queryable"],
|
||||
"state_transitions": ["verified failure/correction/fallback/divergence signal -> scoped evidence", "repeated scoped evidence -> WeaknessPattern; one random failure remains an incident", "supported diagnosis -> ImprovementNeed with intervention class, risk and expected effect", "ImprovementNeed -> candidate proposal/evaluation plan only", "independent evaluation and existing governed development gates decide promotion; actual result is appended to Learning Ledger"],
|
||||
"failure_retry_restart": ["bounded diagnosis/candidate/evaluation budgets and deduplication prevent self-review loops", "provider outage is not competence loss; diagnosis distinguishes unavailable dependency", "stale/unknown/hypothesis diagnosis is never presented as established fact", "durable SELF state survives restart; no process-local canonical state"],
|
||||
"assumptions": ["task branch starts at gen2-integration dd04a67", "UI/browser acceptance remains deferred and V1 compatibility remains retained", "existing Work governed-evolution contract is consumed rather than duplicated", "automatic executable production changes remain a future governed task"],
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
## Gen2 M6 checkpoint — 2026-08-30
|
||||
|
||||
- `GEN2-EVOLVE-004 = MILESTONE_ACCEPTED`: bounded SELF learning signals, evidence-backed attribution, contextual weakness/competence, ImprovementNeed, independently evaluated candidate, benchmark and expected-vs-actual Learning Ledger primitives are implemented with durable SELF persistence in migration 055. Evidence: `docs/evidence/gen2-evolve-004-milestone-2026-08-30.md`. No autonomous promotion, production deployment, new owner, UI work, V1 retirement or GEN2-EVOLVE-005 was started.
|
||||
|
||||
- `GEN2-EVOLVE-003 = MILESTONE_ACCEPTED`: Experience evidence now enters an evidence-governed Candidate Skill → independent validation → Active Skill → bounded Candidate Reflex lifecycle. Reflex divergence/revalidation records SELF proposals and Learning Ledger evidence; it cannot autonomously rewrite or promote production. Evidence: `docs/evidence/gen2-evolve-003-milestone-2026-08-30.md`. UI and V1 compatibility remain unchanged.
|
||||
|
||||
- `GEN2-EVOLVE-002 = MILESTONE_ACCEPTED`: one bounded typed cognitive memory-continuation round is available through Core and the existing retrieval fabric. Evidence: `docs/evidence/gen2-evolve-002-milestone-2026-08-30.md`. UI and V1 compatibility remain unchanged.
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
# GEN2-EVOLVE-004 milestone evidence
|
||||
|
||||
Status: MILESTONE_ACCEPTED for the bounded SELF evidence/proposal domain.
|
||||
|
||||
## Delivered
|
||||
|
||||
- Typed learning signals cover verified failures, corrections, repeated mistakes, Reflex divergence, Skill fallback, escalation, outcome mismatch, retry/fallback, stale dependencies, regret and better solutions.
|
||||
- Attribution returns a bounded evidence-backed `HYPOTHESIS`; provider outage is not converted into a competence loss.
|
||||
- Weakness patterns require two matching incidents in the same Principal/task/Scope and retain evidence, frequency, severity, affected component and confidence.
|
||||
- Competence assessment is contextual and requires evidence; SELF cannot self-declare trust.
|
||||
- ImprovementNeed and Candidate contain hypothesis, expected effect, evaluation, must-not-worsen, rollback, cost and independent evaluator fields.
|
||||
- Benchmark cases reference evidence rather than copying conversations. Learning Ledger appends expected and actual evaluation references and decision.
|
||||
- Candidate evaluation rejects latency/quality regression and creator/evaluator identity reuse. New model qualification creates a review candidate only.
|
||||
- PostgreSQL migration `055` adds SELF-owned durable signal, weakness, candidate and benchmark tables under existing `self_role`; no new role, owner or cross-owner relation is created.
|
||||
|
||||
## Checks
|
||||
|
||||
- `go test ./contracts/self ./modules/self/domain ./adapters/postgres ./cmd/core-runtime`
|
||||
- `go vet ./contracts/self ./modules/self/domain ./adapters/postgres ./cmd/core-runtime`
|
||||
- `go run ./cmd/devctl preflight -task dev/tasks/GEN2-EVOLVE-004-self-improvement-loop-weakness-diagnosis-and-governed-learning.json`
|
||||
- `go run ./cmd/devctl check-diff -task dev/tasks/GEN2-EVOLVE-004-self-improvement-loop-weakness-diagnosis-and-governed-learning.json`
|
||||
- `go run ./cmd/devctl/independent`
|
||||
- `go run ./cmd/devctl check-architecture`
|
||||
- `git diff --check`
|
||||
|
||||
## Boundaries
|
||||
|
||||
No autonomous code generation, merge, deployment, production promotion, new SELF owner, Skill/Reflex mutation, UI work, V1 retirement or Gen2-EVOLVE-005 was performed. Existing Work governed-evolution gates remain the promotion authority. Runtime recorder wiring through `cmd/core-runtime/main.go` was not added because it is outside the accepted packet scope; the durable repository and typed domain path are ready for a separately authorized integration task.
|
||||
+175
-9
@@ -2,24 +2,190 @@ package domain
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"git.cin.su/m/Assistant/contracts/self"
|
||||
"git.cin.su/m/Assistant/platform/ids"
|
||||
)
|
||||
|
||||
const (
|
||||
MinWeaknessIncidents = 2
|
||||
MaxDiagnosisEvidence = 8
|
||||
MaxCandidateEvidence = 12
|
||||
)
|
||||
|
||||
func ValidateCompetence(v self.CompetenceAssessment) error { return v.Validate() }
|
||||
func ValidateLearning(v self.LearningLedgerEntry) error { return v.Validate() }
|
||||
|
||||
// ImprovementForReflexDivergence records a proposed review only. SELF never
|
||||
// promotes, rewrites or trusts a Reflex from this observation.
|
||||
func ImprovementForReflexDivergence(principal string, reflexRef self.SemanticReference, evidence []self.SemanticReference, at int64) (self.ImprovementNeed, self.LearningLedgerEntry, error) {
|
||||
if principal == "" || !reflexRef.Valid() || len(evidence) == 0 || at < 1 {
|
||||
return self.ImprovementNeed{}, self.LearningLedgerEntry{}, fmt.Errorf("reflex improvement lacks evidence")
|
||||
func AttributeFailure(signal self.LearningSignal) self.Diagnosis {
|
||||
class := self.DiagnosisUnknown
|
||||
switch signal.Kind {
|
||||
case self.SignalSkillFallback:
|
||||
class = self.DiagnosisSkillFailure
|
||||
case self.SignalReflexDivergence:
|
||||
class = self.DiagnosisReflexFailure
|
||||
case self.SignalStaleDependency:
|
||||
class = self.DiagnosisProviderFailure
|
||||
case self.SignalRetryFallback:
|
||||
class = self.DiagnosisRoutingInefficiency
|
||||
case self.SignalOutcomeMismatch:
|
||||
class = self.DiagnosisOutcomeVerificationFailure
|
||||
}
|
||||
need := self.ImprovementNeed{ID: self.AssessmentID("improvement:" + string(reflexRef.StableID) + ":" + fmt.Sprint(at)), PrincipalID: principal, SubjectRef: reflexRef, ProblemSummary: "shadow divergence requires governed reflex revalidation", EvidenceRefs: evidence, ExpectedBenefit: "restore verified bounded behavior or retire the reflex", Priority: 1, State: "OPEN", CreatedAt: at}
|
||||
return self.Diagnosis{Class: class, Status: self.EpistemicHypothesis, Rationale: "classified from bounded signal; independent confirmation required", EvidenceRefs: uniqueRefs(signal.EvidenceRefs, MaxDiagnosisEvidence), ConfidenceMillis: 250}
|
||||
}
|
||||
|
||||
func BuildWeaknessPattern(principal, taskPattern, scopeKey, environment string, incidents []self.LearningSignal, affectedComponent, strategy string) (self.WeaknessPattern, bool, error) {
|
||||
if principal == "" || taskPattern == "" || scopeKey == "" || affectedComponent == "" || len(incidents) < MinWeaknessIncidents {
|
||||
return self.WeaknessPattern{}, false, nil
|
||||
}
|
||||
refs := []self.SemanticReference{}
|
||||
severity, last := 0, int64(0)
|
||||
for _, incident := range incidents {
|
||||
if err := incident.Validate(); err != nil || incident.PrincipalID != principal || incident.TaskPattern != taskPattern || incident.ScopeKey != scopeKey {
|
||||
return self.WeaknessPattern{}, false, fmt.Errorf("weakness incidents are not one bounded context")
|
||||
}
|
||||
refs = append(refs, incident.EvidenceRefs...)
|
||||
if incident.Severity > severity {
|
||||
severity = incident.Severity
|
||||
}
|
||||
if incident.UserImpact > severity {
|
||||
severity = incident.UserImpact
|
||||
}
|
||||
if incident.ObservedAt > last {
|
||||
last = incident.ObservedAt
|
||||
}
|
||||
}
|
||||
return self.WeaknessPattern{ID: self.AssessmentID("weakness:" + principal + ":" + taskPattern + ":" + scopeKey), PrincipalID: principal, Revision: 1, PatternKey: taskPattern + ":" + scopeKey, TaskPattern: taskPattern, ScopeKey: scopeKey, EnvironmentVersion: environment, FailureMode: string(AttributeFailure(incidents[len(incidents)-1]).Class), DetectionEvidenceRefs: uniqueRefs(refs, MaxDiagnosisEvidence), State: self.AssessmentCandidate, RecordedAt: last, Frequency: len(incidents), LastObservedAt: last, Severity: severity, AffectedComponent: affectedComponent, AffectedStrategy: strategy, ConfidenceMillis: int64(min(1000, 250+len(incidents)*150)), EpistemicStatus: self.EpistemicSupported}, true, nil
|
||||
}
|
||||
|
||||
func AssessContextualCompetence(principal string, subject self.SemanticReference, context []string, evidence []self.SemanticReference, quality, reliability, confidence, assessedAt int64) (self.CompetenceAssessment, error) {
|
||||
v := self.CompetenceAssessment{ID: self.AssessmentID("competence:" + principal + ":" + string(subject.StableID)), PrincipalID: principal, SubjectRef: subject, Revision: 1, ContextMatcher: append([]string(nil), context...), QualityMillis: quality, ReliabilityMillis: reliability, ConfidenceMillis: confidence, EvidenceRefs: uniqueRefs(evidence, MaxDiagnosisEvidence), AssessedAt: assessedAt, LastValidatedAt: assessedAt}
|
||||
if err := v.Validate(); err != nil {
|
||||
return self.CompetenceAssessment{}, err
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func BuildImprovementNeed(signal self.LearningSignal, diagnosis self.Diagnosis, component string) (self.ImprovementNeed, error) {
|
||||
if err := signal.Validate(); err != nil {
|
||||
return self.ImprovementNeed{}, err
|
||||
}
|
||||
if err := diagnosis.Validate(); err != nil {
|
||||
return self.ImprovementNeed{}, err
|
||||
}
|
||||
if component == "" {
|
||||
return self.ImprovementNeed{}, fmt.Errorf("affected component is required")
|
||||
}
|
||||
priority := signal.Severity*2 + signal.UserImpact + signal.CorrectionCount
|
||||
if priority < 1 {
|
||||
priority = 1
|
||||
}
|
||||
return self.ImprovementNeed{ID: self.AssessmentID("need:" + signal.ID), PrincipalID: signal.PrincipalID, SubjectRef: signal.SubjectRef, ProblemSummary: "bounded evidence indicates an improvement opportunity", EvidenceRefs: diagnosis.EvidenceRefs, ExpectedBenefit: "reduce recurrence without weakening verified behavior", Priority: priority, State: "OPEN", CreatedAt: signal.ObservedAt, Diagnosis: diagnosis.Class, DiagnosisStatus: diagnosis.Status, AffectedComponent: component, ScopeKey: signal.ScopeKey, Risk: "regression in established behavior", Intervention: interventionFor(diagnosis.Class), Hypothesis: diagnosis.Rationale, EvaluationPlan: "independent benchmark and must-not-worsen evaluation", MustNotWorsen: "quality and verified outcome rate", RollbackPlan: "disable candidate and restore last-known-good", ResourceCost: "bounded review"}, nil
|
||||
}
|
||||
|
||||
func BuildCandidate(need self.ImprovementNeed, creator, evaluator string, evidence []self.SemanticReference, at int64) (self.ImprovementCandidate, error) {
|
||||
ref := self.SemanticReference{OwnerNamespace: "self", SemanticKind: "improvement_need", StableID: ids.StableID(need.ID)}
|
||||
ledger := self.LearningLedgerEntry{ID: self.LearningLedgerEntryID("ledger:" + string(reflexRef.StableID) + ":" + fmt.Sprint(at)), PrincipalID: principal, ImprovementNeedRef: &ref, ObservedProblem: need.ProblemSummary, EvidenceRefs: evidence, Diagnosis: "bounded shadow comparison diverged or verifier rejected result", Hypothesis: "implementation or applicability no longer matches evidence", ProposedChangeRef: reflexRef, ExpectedImprovement: need.ExpectedBenefit, Decision: "PROPOSE_REVALIDATION", RecordedAt: at}
|
||||
if err := ledger.Validate(); err != nil {
|
||||
c := self.ImprovementCandidate{ID: "candidate:" + string(need.ID), PrincipalID: need.PrincipalID, NeedRef: ref, Hypothesis: need.Hypothesis, ExpectedEffect: need.ExpectedBenefit, EvaluationPlan: need.EvaluationPlan, MustNotWorsen: []string{need.MustNotWorsen}, RollbackPlan: need.RollbackPlan, ResourceCost: need.ResourceCost, Creator: creator, IndependentEvaluator: evaluator, State: self.CandidateProposed, EvidenceRefs: uniqueRefs(evidence, MaxCandidateEvidence), CreatedAt: at}
|
||||
if err := c.Validate(); err != nil {
|
||||
return self.ImprovementCandidate{}, err
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func EvaluateCandidate(candidate self.ImprovementCandidate, evaluation self.ImprovementEvaluation) (self.CandidateState, error) {
|
||||
if evaluation.Evaluator == "" || evaluation.Evaluator == candidate.Creator || !evaluation.MustNotWorsenPass || evaluation.ActualQuality < evaluation.ExpectedQuality || (evaluation.ExpectedLatency > 0 && evaluation.ActualLatency > evaluation.ExpectedLatency*2) {
|
||||
return self.CandidateRejected, nil
|
||||
}
|
||||
return self.CandidateKept, nil
|
||||
}
|
||||
|
||||
func ClassifyDecisionRegret(signal self.LearningSignal) self.DecisionRegretKind {
|
||||
if signal.DecisionRegret != "" {
|
||||
return signal.DecisionRegret
|
||||
}
|
||||
switch signal.ActualOutcome {
|
||||
case "information unavailable":
|
||||
return self.RegretUnavailableThen
|
||||
case "available but unused":
|
||||
return self.RegretAvailableUnused
|
||||
case "ignored in context":
|
||||
return self.RegretIgnoredInContext
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func BuildBenchmarkCase(principal, class string, evidence []self.SemanticReference, at int64) (self.BenchmarkCase, error) {
|
||||
c := self.BenchmarkCase{ID: "benchmark:" + principal + ":" + class, PrincipalID: principal, TaskClass: class, EvidenceRefs: uniqueRefs(evidence, MaxCandidateEvidence), RecordedAt: at}
|
||||
if !c.Valid() {
|
||||
return self.BenchmarkCase{}, fmt.Errorf("benchmark requires evidence")
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func AppendLedgerOutcome(need self.ImprovementNeed, candidate self.ImprovementCandidate, evaluation self.ImprovementEvaluation, decision self.CandidateState, at int64) (self.LearningLedgerEntry, error) {
|
||||
ref := self.SemanticReference{OwnerNamespace: "self", SemanticKind: "improvement_need", StableID: ids.StableID(need.ID)}
|
||||
refs := append(append([]self.SemanticReference{}, need.EvidenceRefs...), evaluation.EvidenceRefs...)
|
||||
e := self.LearningLedgerEntry{ID: self.LearningLedgerEntryID("ledger:" + candidate.ID + ":" + fmt.Sprint(at)), PrincipalID: need.PrincipalID, ImprovementNeedRef: &ref, ObservedProblem: need.ProblemSummary, EvidenceRefs: uniqueRefs(refs, MaxCandidateEvidence), Diagnosis: string(need.Diagnosis), Hypothesis: need.Hypothesis, ProposedChangeRef: candidate.NeedRef, ExpectedImprovement: need.ExpectedBenefit, ActualEvaluationRefs: evaluation.EvidenceRefs, Decision: string(decision), RecordedAt: at}
|
||||
if err := e.Validate(); err != nil {
|
||||
return self.LearningLedgerEntry{}, err
|
||||
}
|
||||
return e, nil
|
||||
}
|
||||
|
||||
func ReviewCandidateForNewModel(principal string, model self.SemanticReference, evidence []self.SemanticReference, at int64) (self.ImprovementCandidate, error) {
|
||||
n := self.ImprovementNeed{ID: self.AssessmentID("need:model-review:" + string(model.StableID)), PrincipalID: principal, SubjectRef: model, ProblemSummary: "qualified model is available for governed review", EvidenceRefs: evidence, ExpectedBenefit: "compare model quality under existing safety constraints", Priority: 1, State: "OPEN", CreatedAt: at, Intervention: self.InterventionProviderReview, Hypothesis: "model may improve quality", EvaluationPlan: "independent benchmark", MustNotWorsen: "safety, latency and verified outcomes", RollbackPlan: "retain current route", ResourceCost: "bounded evaluation"}
|
||||
return BuildCandidate(n, "qualification", "independent-evaluator", evidence, at)
|
||||
}
|
||||
|
||||
func interventionFor(c self.DiagnosisClass) self.InterventionClass {
|
||||
switch c {
|
||||
case self.DiagnosisSkillFailure:
|
||||
return self.InterventionSkillReview
|
||||
case self.DiagnosisReflexFailure:
|
||||
return self.InterventionReflexReview
|
||||
case self.DiagnosisProviderFailure:
|
||||
return self.InterventionProviderReview
|
||||
case self.DiagnosisRoutingInefficiency:
|
||||
return self.InterventionRoutingReview
|
||||
case self.DiagnosisContextSelection, self.DiagnosisMemoryNotRetrieved:
|
||||
return self.InterventionContextReview
|
||||
}
|
||||
return self.InterventionConfiguration
|
||||
}
|
||||
func uniqueRefs(refs []self.SemanticReference, max int) []self.SemanticReference {
|
||||
seen := map[string]bool{}
|
||||
out := make([]self.SemanticReference, 0, len(refs))
|
||||
for _, r := range refs {
|
||||
if !r.Valid() || seen[string(r.StableID)] {
|
||||
continue
|
||||
}
|
||||
seen[string(r.StableID)] = true
|
||||
out = append(out, r)
|
||||
if len(out) == max {
|
||||
break
|
||||
}
|
||||
}
|
||||
sort.Slice(out, func(i, j int) bool { return string(out[i].StableID) < string(out[j].StableID) })
|
||||
return out
|
||||
}
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func ImprovementForReflexDivergence(principal string, reflexRef self.SemanticReference, evidence []self.SemanticReference, at int64) (self.ImprovementNeed, self.LearningLedgerEntry, error) {
|
||||
s := self.LearningSignal{ID: "signal:reflex:" + string(reflexRef.StableID), PrincipalID: principal, Kind: self.SignalReflexDivergence, TaskPattern: "reflex", ScopeKey: "default", SubjectRef: reflexRef, EvidenceRefs: evidence, ObservedAt: at, Severity: 1}
|
||||
d := AttributeFailure(s)
|
||||
n, err := BuildImprovementNeed(s, d, "reflex")
|
||||
if err != nil {
|
||||
return self.ImprovementNeed{}, self.LearningLedgerEntry{}, err
|
||||
}
|
||||
return need, ledger, nil
|
||||
ref := self.SemanticReference{OwnerNamespace: "self", SemanticKind: "improvement_need", StableID: ids.StableID(n.ID)}
|
||||
l := self.LearningLedgerEntry{ID: self.LearningLedgerEntryID("ledger:" + string(reflexRef.StableID) + ":" + fmt.Sprint(at)), PrincipalID: principal, ImprovementNeedRef: &ref, ObservedProblem: n.ProblemSummary, EvidenceRefs: evidence, Diagnosis: string(d.Class), Hypothesis: d.Rationale, ProposedChangeRef: reflexRef, ExpectedImprovement: n.ExpectedBenefit, Decision: "PROPOSE_REVALIDATION", RecordedAt: at}
|
||||
if err := l.Validate(); err != nil {
|
||||
return self.ImprovementNeed{}, self.LearningLedgerEntry{}, err
|
||||
}
|
||||
return n, l, nil
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package domain_test
|
||||
import (
|
||||
selfcontract "git.cin.su/m/Assistant/contracts/self"
|
||||
"git.cin.su/m/Assistant/modules/self/domain"
|
||||
"git.cin.su/m/Assistant/platform/ids"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -25,3 +26,74 @@ func TestShadowDivergenceCreatesProposalNotPromotion(t *testing.T) {
|
||||
t.Fatalf("need=%#v ledger=%#v err=%v", need, ledger, err)
|
||||
}
|
||||
}
|
||||
|
||||
func signal(kind selfcontract.LearningSignalKind, id, scope string, at int64) selfcontract.LearningSignal {
|
||||
return selfcontract.LearningSignal{ID: id, PrincipalID: "p", Kind: kind, TaskPattern: "recall", ScopeKey: scope, SubjectRef: selfcontract.SemanticReference{OwnerNamespace: "interaction", SemanticKind: "assertion", StableID: ids.StableID("x")}, EvidenceRefs: []selfcontract.SemanticReference{{OwnerNamespace: "epistemics", SemanticKind: "experience", StableID: ids.StableID(id)}}, ObservedAt: at, Severity: 2, UserImpact: 1}
|
||||
}
|
||||
|
||||
func TestRepeatedSignalsCreateScopedWeaknessButSingleDoesNot(t *testing.T) {
|
||||
a, b := signal(selfcontract.SignalUserCorrection, "e1", "company-a", 1), signal(selfcontract.SignalUserCorrection, "e2", "company-a", 2)
|
||||
if _, created, err := domain.BuildWeaknessPattern("p", "recall", "company-a", "v1", []selfcontract.LearningSignal{a}, "context", "strategy"); err != nil || created {
|
||||
t.Fatalf("single incident created weakness: %v %v", created, err)
|
||||
}
|
||||
w, created, err := domain.BuildWeaknessPattern("p", "recall", "company-a", "v1", []selfcontract.LearningSignal{a, b}, "context", "strategy")
|
||||
if err != nil || !created || w.Frequency != 2 || w.ScopeKey != "company-a" {
|
||||
t.Fatalf("weakness=%#v created=%v err=%v", w, created, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDiagnosisCandidateAndMustNotWorsen(t *testing.T) {
|
||||
s := signal(selfcontract.SignalReflexDivergence, "e3", "company-a", 3)
|
||||
d := domain.AttributeFailure(s)
|
||||
if d.Class != selfcontract.DiagnosisReflexFailure || d.Status != selfcontract.EpistemicHypothesis {
|
||||
t.Fatalf("diagnosis=%#v", d)
|
||||
}
|
||||
n, err := domain.BuildImprovementNeed(s, d, "reflex")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
c, err := domain.BuildCandidate(n, "creator", "evaluator", s.EvidenceRefs, 3)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
decision, err := domain.EvaluateCandidate(c, selfcontract.ImprovementEvaluation{Evaluator: "evaluator", EvidenceRefs: s.EvidenceRefs, ExpectedQuality: 800, ActualQuality: 800, ExpectedLatency: 100, ActualLatency: 250, MustNotWorsenPass: true})
|
||||
if err != nil || decision != selfcontract.CandidateRejected {
|
||||
t.Fatalf("decision=%v err=%v", decision, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestScopedEvidenceIsDeduplicatedAndBounded(t *testing.T) {
|
||||
s := signal(selfcontract.SignalSkillFallback, "e4", "company-a", 4)
|
||||
d := domain.AttributeFailure(s)
|
||||
n, err := domain.BuildImprovementNeed(s, d, "skill")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err = domain.BuildCandidate(n, "creator", "evaluator", append(s.EvidenceRefs, s.EvidenceRefs...), 4); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := domain.ClassifyDecisionRegret(selfcontract.LearningSignal{DecisionRegret: selfcontract.RegretIgnoredInContext}); got != selfcontract.RegretIgnoredInContext {
|
||||
t.Fatal(got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBenchmarkAndLedgerReferenceEvidence(t *testing.T) {
|
||||
s := signal(selfcontract.SignalOutcomeMismatch, "e5", "company-a", 5)
|
||||
caseRef, err := domain.BuildBenchmarkCase("p", "memory-recall", s.EvidenceRefs, 5)
|
||||
if err != nil || !caseRef.Valid() {
|
||||
t.Fatalf("benchmark=%#v err=%v", caseRef, err)
|
||||
}
|
||||
d := domain.AttributeFailure(s)
|
||||
n, err := domain.BuildImprovementNeed(s, d, "verification")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
c, err := domain.BuildCandidate(n, "creator", "evaluator", s.EvidenceRefs, 5)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
l, err := domain.AppendLedgerOutcome(n, c, selfcontract.ImprovementEvaluation{Evaluator: "evaluator", EvidenceRefs: s.EvidenceRefs}, selfcontract.CandidateRejected, 6)
|
||||
if err != nil || l.Decision != string(selfcontract.CandidateRejected) || len(l.EvidenceRefs) == 0 {
|
||||
t.Fatalf("ledger=%#v err=%v", l, err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user