{"record":{"id":"bd0651f0b1e85435","repo":"JuliusBrussee/caveman","slug":"session-value-artifact-version-identity-invalid","errorCode":null,"errorMessage":"session-value artifact version identity invalid","messagePattern":"session-value artifact version identity invalid","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/routing/session_value.go","lineNumber":172,"sourceCode":"func SealSessionValueArtifact(artifact SessionValuePolicyArtifact) (SessionValuePolicyArtifact, error) {\n\thash, err := ComputeSessionValueArtifactHash(artifact)\n\tif err != nil {\n\t\treturn SessionValuePolicyArtifact{}, err\n\t}\n\tartifact.ArtifactHash = hash\n\treturn artifact, nil\n}\n\nfunc ValidateSessionValueArtifact(artifact SessionValuePolicyArtifact, organizationID, projectID, candidatePoolHash string, now time.Time) error {\n\tif artifact.Schema != SessionValueArtifactSchema || artifact.StateSchemaVersion != SessionValueStateSchema {\n\t\treturn errors.New(\"session-value artifact schema mismatch\")\n\t}\n\tif strings.TrimSpace(artifact.OrganizationID) == \"\" || artifact.OrganizationID != strings.TrimSpace(organizationID) ||\n\t\tstrings.TrimSpace(artifact.ProjectID) == \"\" || artifact.ProjectID != strings.TrimSpace(projectID) {\n\t\treturn errors.New(\"session-value artifact tenant scope mismatch\")\n\t}\n\tif artifact.PolicyVersion <= 0 || artifact.RouterVersion != SessionValueRouterVersion || strings.TrimSpace(artifact.EstimatorVersion) == \"\" {\n\t\treturn errors.New(\"session-value artifact version identity invalid\")\n\t}\n\tif !validCompactPoolHash(artifact.CandidatePoolHash) || artifact.CandidatePoolHash != candidatePoolHash {\n\t\treturn errors.New(\"session-value artifact candidate pool mismatch\")\n\t}\n\tif !validSHA256Ref(artifact.TrainingManifestHash) || strings.TrimSpace(artifact.TrainingExtractor) == \"\" || strings.TrimSpace(artifact.OutcomeContractVersion) == \"\" {\n\t\treturn errors.New(\"session-value artifact training lineage invalid\")\n\t}\n\tif artifact.ValidFrom.IsZero() || artifact.ValidUntil.IsZero() || !artifact.ValidUntil.After(artifact.ValidFrom) || now.Before(artifact.ValidFrom) || !now.Before(artifact.ValidUntil) {\n\t\treturn errors.New(\"session-value artifact outside validity window\")\n\t}\n\tif artifact.RollbackParentHash != \"\" && (!validSHA256Ref(artifact.RollbackParentHash) || artifact.RollbackParentHash == artifact.ArtifactHash) {\n\t\treturn errors.New(\"session-value artifact rollback lineage invalid\")\n\t}\n\tif !finite(artifact.QualityUncertaintyZ) || artifact.QualityUncertaintyZ <= 0 || artifact.QualityUncertaintyZ > 5 ||\n\t\t!finite(artifact.MaxInversePropensity) || artifact.MaxInversePropensity < 1 || artifact.MaxInversePropensity > 100 {\n\t\treturn errors.New(\"session-value artifact confidence policy invalid\")\n\t}\n\tfeatureNames := SessionValueFeatureNames()","sourceCodeStart":154,"sourceCodeEnd":190,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/766dce6b1394ebb56a3090748d5a0240a5aefb36/proxy/routing/session_value.go#L154-L190","documentation":"The artifact must identify how it was built and which evaluator can run it: PolicyVersion must be >= 1, RouterVersion must be exactly \"session-value-v1\", and EstimatorVersion must be non-empty. A mismatch means the coefficients were trained for a different router or estimator generation and cannot be interpreted by this evaluator.","triggerScenarios":"PolicyVersion left at the Go zero value 0 by the artifact generator; RouterVersion carried from a newer evaluator (for example a hypothetical session-value-v2); the estimator_version field dropped or renamed during JSON refactoring so it unmarshals empty.","commonSituations":"Router upgraded to a new evaluation format while artifacts still carry the old router version string; training pipeline forgetting to stamp versions; manual artifact editing that clears fields.","solutions":["Regenerate the artifact with the current trainer so router_version and estimator_version are stamped correctly.","Upgrade the router to the version the artifact targets, or keep artifacts grouped per router generation.","Set policy_version to a monotonically increasing positive integer during training instead of relying on defaults."],"exampleFix":"// before\n{\n  \"policy_version\": 0,\n  \"router_version\": \"session-value-v0\",\n  \"estimator_version\": \"\"\n}\n\n// after\n{\n  \"policy_version\": 1,\n  \"router_version\": \"session-value-v1\",\n  \"estimator_version\": \"lin-est-2026-05\"\n}","handlingStrategy":"validation","validationCode":"// Before validation: check version identity fields.\nif artifact.PolicyVersion < 1 ||\n\tartifact.RouterVersion != routing.SessionValueRouterVersion ||\n\tstrings.TrimSpace(artifact.EstimatorVersion) == \"\" {\n\treturn fmt.Errorf(\"artifact built for router %q policy %d estimator %q; this router runs %q\",\n\t\tartifact.RouterVersion, artifact.PolicyVersion, artifact.EstimatorVersion,\n\t\trouting.SessionValueRouterVersion)\n}","typeGuard":"func artifactVersionCompatible(a routing.SessionValuePolicyArtifact) bool {\n\treturn a.PolicyVersion >= 1 &&\n\t\ta.RouterVersion == routing.SessionValueRouterVersion &&\n\t\tstrings.TrimSpace(a.EstimatorVersion) != \"\"\n}","tryCatchPattern":null,"preventionTips":["Stamp router_version and estimator_version automatically during training; fail the build when either is empty.","Keep a registry of policy_version increments so zero never ships.","Treat router-version mismatches as deploy-blockers in CI, not runtime surprises."],"tags":["routing","versioning","estimator","artifact"],"backgroundTag":"policy-version-mismatch","analyzedSha":"766dce6b1394ebb56a3090748d5a0240a5aefb36","analyzedAt":"2026-08-18T03:14:35.516Z","contentChangedAt":"2026-08-18T03:14:35.516Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}