Текст этого вопроса будет отправлен в OpenAI и может храниться до 30 дней для мониторинга злоупотреблений. Контекст, идентификаторы и секреты Домового не передаются.
+
ВНЕШНИЙ КОГНИТИВНЫЙ ПРОВАЙДЕР
+
Текст вопроса будет передан согласно активной политике данных.
diff --git a/contracts/capability/cognitive_registry.go b/contracts/capability/cognitive_registry.go
new file mode 100644
index 0000000..64e34a4
--- /dev/null
+++ b/contracts/capability/cognitive_registry.go
@@ -0,0 +1,254 @@
+package capability
+
+import (
+ "git.cin.su/m/Assistant/contracts/artifact"
+ "git.cin.su/m/Assistant/contracts/identity"
+ "git.cin.su/m/Assistant/platform/ids"
+)
+
+type ProviderInstanceID ids.ID
+type ModelDescriptorID ids.ID
+type CredentialRef ids.ID
+type EndpointBindingRef ids.ID
+type LimitPolicyRef ids.ID
+type ProviderKind string
+type ProviderModelID string
+type ModelFingerprint string
+type QualificationState string
+type ActivationState string
+type DataPolicy string
+type ModelModality string
+type ModelCapability string
+type OperationalState string
+type EvaluationResult string
+type EvaluationID ids.ID
+type PerformanceEvidenceID ids.ID
+
+const (
+ QualificationDiscovered QualificationState = "DISCOVERED"
+ QualificationProbing QualificationState = "PROBING"
+ QualificationEvaluating QualificationState = "EVALUATING"
+ QualificationValidated QualificationState = "VALIDATED"
+ QualificationRejected QualificationState = "REJECTED"
+
+ ActivationInactive ActivationState = "INACTIVE"
+ ActivationCanary ActivationState = "CANARY"
+ ActivationActive ActivationState = "ACTIVE"
+ ActivationDeprecated ActivationState = "DEPRECATED"
+ ActivationRetired ActivationState = "RETIRED"
+
+ DataPolicyLocalOnly DataPolicy = "LOCAL_ONLY"
+ DataPolicyExternalMessageOnly DataPolicy = "EXTERNAL_MESSAGE_ONLY"
+ DataPolicyExternalMinimizedContext DataPolicy = "EXTERNAL_MINIMIZED_CONTEXT"
+
+ ModalityText ModelModality = "TEXT"
+ CapabilityAdvisory ModelCapability = "ADVISORY"
+ CapabilityUnknown ModelCapability = "UNKNOWN"
+
+ OperationalNotObserved OperationalState = "NOT_OBSERVED"
+ OperationalYes OperationalState = "YES"
+ OperationalNo OperationalState = "NO"
+
+ EvaluationUnknown EvaluationResult = "UNKNOWN"
+ EvaluationSupported EvaluationResult = "SUPPORTED"
+ EvaluationUnsupported EvaluationResult = "UNSUPPORTED"
+)
+
+// ProviderInstance contains only opaque references to endpoint and credentials.
+// Credential material is resolved by the cognitive runtime, never by Core.
+type ProviderInstance struct {
+ ID ProviderInstanceID
+ Revision int64
+ Kind ProviderKind
+ EndpointRef EndpointBindingRef
+ CredentialRef CredentialRef
+ Enabled bool
+ Qualification QualificationState
+ Health HealthState
+ ObservedAtUnixMillis int64
+}
+
+type ModelDescriptor struct {
+ ID ModelDescriptorID
+ Revision int64
+ ProviderInstanceID ProviderInstanceID
+ ProviderModelID ProviderModelID
+ Fingerprint ModelFingerprint
+ MaxContextBytes int64
+ Modalities []ModelModality
+ Capabilities []ModelCapability
+ Qualification QualificationState
+ Health HealthState
+ ObservedAtUnixMillis int64
+}
+
+type ProviderHealthObservation struct {
+ ProviderInstanceID ProviderInstanceID
+ Health HealthState
+ ObservedAtUnixMillis int64
+ FailureClass CognitiveFailureType
+}
+
+type ModelOperationalObservation struct {
+ ProviderInstanceID ProviderInstanceID
+ ModelDescriptorID ModelDescriptorID
+ Installed OperationalState
+ Loaded OperationalState
+ Invokable OperationalState
+ Health HealthState
+ ObservedAtUnixMillis int64
+}
+
+type CognitiveBinding struct {
+ ID CapabilityBindingID
+ Revision int64
+ CapabilityID CapabilityID
+ OwnerPrincipalID identity.PrincipalID
+ ProviderInstanceID ProviderInstanceID
+ ProviderRevision int64
+ ModelDescriptorID ModelDescriptorID
+ ModelRevision int64
+ DataPolicy DataPolicy
+ Qualification QualificationState
+ Activation ActivationState
+ Health HealthState
+ LimitPolicyRef LimitPolicyRef
+}
+
+type CognitiveRequirement struct {
+ CapabilityID CapabilityID
+ OwnerPrincipalID identity.PrincipalID
+ DataPolicy DataPolicy
+ RequiredModalities []ModelModality
+ RequiredCapabilities []ModelCapability
+ AllowCanary bool
+ InputBytes int64
+ MaxOutputBytes int64
+ ExcludedBindingIDs []CapabilityBindingID
+}
+
+// ResolvedCognitiveBinding is a provider-neutral invocation snapshot selected
+// by Capability. It contains references and declared metadata, never secrets.
+type ResolvedCognitiveBinding struct {
+ Binding CognitiveBinding
+ Provider ProviderInstance
+ Model ModelDescriptor
+ Evaluation *EvaluatedCapability
+ Performance *PerformanceEvidence
+}
+
+type CognitiveResultClass string
+type CognitiveFailureType string
+
+type EvaluatedCapability struct {
+ ID EvaluationID
+ BindingID CapabilityBindingID
+ BindingRevision int64
+ CapabilityID CapabilityID
+ Result EvaluationResult
+ QualityScoreMillis *int64
+ CriticalFailureMillis *int64
+ BenchmarkVersion string
+ ArtifactID artifact.ArtifactID
+ ObservedAtUnixMillis int64
+}
+
+type PerformanceEvidence struct {
+ ID PerformanceEvidenceID
+ BindingID CapabilityBindingID
+ BindingRevision int64
+ ModelDescriptorID ModelDescriptorID
+ ModelRevision int64
+ HardwareProfile string
+ Runtime string
+ Quantization string
+ ContextUnits *int64
+ ThinkingEnabled *bool
+ GenerationTPSMillis *int64
+ PromptTPSMillis *int64
+ TTFTMicros *int64
+ LatencyMicros *int64
+ MemoryBytes *int64
+ ReliabilityMillis *int64
+ BenchmarkVersion string
+ ArtifactID artifact.ArtifactID
+ ObservedAtUnixMillis int64
+}
+
+type CognitiveDiscoveryRegistration struct {
+ Provider ProviderInstance
+ Models []ModelDescriptor
+ Bindings []CognitiveBinding
+ ProviderHealth ProviderHealthObservation
+ ModelAvailability []ModelOperationalObservation
+}
+
+type CognitiveCanaryPromotionRequest struct {
+ PromotionID ids.ID
+ RequestedByPrincipalID identity.PrincipalID
+ CapabilityID CapabilityID
+ BindingID CapabilityBindingID
+ ExpectedBindingRevision int64
+ ProviderInstanceID ProviderInstanceID
+ ExpectedProviderRevision int64
+ ModelDescriptorID ModelDescriptorID
+ ExpectedModelRevision int64
+ EvaluationID EvaluationID
+ ObservedAtUnixMillis int64
+}
+
+type CognitiveCanaryPromotion struct {
+ PromotionID ids.ID
+ ExpectedBindingRevision int64
+ Provider ProviderInstance
+ Model ModelDescriptor
+ Binding CognitiveBinding
+ Evaluation EvaluatedCapability
+}
+
+// CognitiveDiscoverySnapshot is provider-neutral observed inventory submitted
+// to the Capability owner. The owner assigns immutable revisions and always
+// registers discovered bindings as DISCOVERED/INACTIVE.
+type CognitiveDiscoverySnapshot struct {
+ ProviderInstanceID ProviderInstanceID
+ ProviderKind ProviderKind
+ EndpointRef EndpointBindingRef
+ CredentialRef CredentialRef
+ Enabled bool
+ OwnerPrincipalID identity.PrincipalID
+ CapabilityID CapabilityID
+ DataPolicy DataPolicy
+ LimitPolicyRef LimitPolicyRef
+ Health HealthState
+ FailureClass CognitiveFailureType
+ ObservedAtUnixMillis int64
+ Models []DiscoveredModelSnapshot
+}
+
+type DiscoveredModelSnapshot struct {
+ ProviderModelID ProviderModelID
+ Fingerprint ModelFingerprint
+ MaxContextBytes int64
+ Modalities []ModelModality
+ Capabilities []ModelCapability
+ Installed OperationalState
+ Loaded OperationalState
+ Invokable OperationalState
+}
+
+type CognitiveUsageObservation struct {
+ ObservationID ids.ID
+ RequestID ids.ID
+ BindingID CapabilityBindingID
+ BindingRevision int64
+ ProviderInstanceID ProviderInstanceID
+ ModelDescriptorID ModelDescriptorID
+ ObservedAtUnixMillis int64
+ LatencyMillis int64
+ InputUnits int64
+ OutputUnits int64
+ EstimatedCostMicros *int64
+ ActualCostMicros *int64
+ ResultClass CognitiveResultClass
+ FailureType CognitiveFailureType
+}
diff --git a/contracts/capability/types.go b/contracts/capability/types.go
index d283960..65f464f 100644
--- a/contracts/capability/types.go
+++ b/contracts/capability/types.go
@@ -38,6 +38,7 @@ type Binding struct {
type HealthState string
const (
+ HealthUnknown HealthState = "UNKNOWN"
HealthAvailable HealthState = "AVAILABLE"
HealthDegraded HealthState = "DEGRADED"
HealthUnavailable HealthState = "UNAVAILABLE"
diff --git a/contracts/cognitive/types.go b/contracts/cognitive/types.go
index 497fa97..7fbd852 100644
--- a/contracts/cognitive/types.go
+++ b/contracts/cognitive/types.go
@@ -25,8 +25,16 @@ const (
FinishLengthLimit FinishReason = "LENGTH_LIMIT"
FinishContentRejected FinishReason = "CONTENT_REJECTED"
FinishProviderUnavailable FinishReason = "PROVIDER_UNAVAILABLE"
+ FinishModelUnavailable FinishReason = "MODEL_UNAVAILABLE"
+ FinishRateLimited FinishReason = "RATE_LIMITED"
+ FinishAuthenticationFailed FinishReason = "AUTHENTICATION_FAILED"
+ FinishBillingOrBudgetBlocked FinishReason = "BILLING_OR_BUDGET_BLOCKED"
+ FinishContextTooLarge FinishReason = "CONTEXT_TOO_LARGE"
+ FinishUnsupportedCapability FinishReason = "UNSUPPORTED_CAPABILITY"
FinishTimeout FinishReason = "TIMEOUT"
FinishInvalidProviderResponse FinishReason = "INVALID_PROVIDER_RESPONSE"
+ FinishInvalidStructuredOutput FinishReason = "INVALID_STRUCTURED_OUTPUT"
+ FinishDegraded FinishReason = "DEGRADED"
)
type ContextExcerpt struct {
@@ -52,6 +60,36 @@ type AdvisoryRequest struct {
MaxOutputBytes int
ProviderBindingID capability.CapabilityBindingID
ProviderBindingRevision int64
+ ProviderInstanceID capability.ProviderInstanceID
+ ModelDescriptorID capability.ModelDescriptorID
+ ProviderModelID capability.ProviderModelID
+}
+
+// GatewayAdvisoryRequest describes the task and policy requirement. It cannot
+// name a provider, model or binding; Capability owns that selection.
+type GatewayAdvisoryRequest struct {
+ Version ContractVersion
+ RequestID RequestID
+ ActorID identity.ActorID
+ PrincipalID identity.PrincipalID
+ ScopeID authority.ScopeID
+ Message string
+ Context []ContextExcerpt
+ Locale string
+ MaxOutputBytes int
+ DataPolicy capability.DataPolicy
+ EgressAcknowledged bool
+}
+
+type GatewayAdvisoryPolicyRequest struct {
+ PrincipalID identity.PrincipalID
+ DataPolicy capability.DataPolicy
+}
+
+type GatewayAdvisoryPolicy struct {
+ ExternalEgress bool
+ AcknowledgementRequired bool
+ Disclosure string
}
type ProviderUsage struct {
@@ -69,3 +107,12 @@ type AdvisoryResponse struct {
FinishReason FinishReason
Usage *ProviderUsage
}
+
+type GatewayAdvisoryResponse struct {
+ Version ContractVersion
+ RequestID RequestID
+ AdvisoryText string
+ ReferencedExcerptIDs []ContextExcerptID
+ FinishReason FinishReason
+ Usage *ProviderUsage
+}
diff --git a/database/bootstrap/001_roles_and_schemas.sql b/database/bootstrap/001_roles_and_schemas.sql
index 1bda888..bfbea57 100644
--- a/database/bootstrap/001_roles_and_schemas.sql
+++ b/database/bootstrap/001_roles_and_schemas.sql
@@ -185,6 +185,95 @@ CREATE TABLE IF NOT EXISTS capability.notification_binding_heads (
binding_id TEXT NOT NULL, revision BIGINT NOT NULL,
PRIMARY KEY (capability_id, owner_principal_id), CHECK (revision > 0)
);
+CREATE TABLE IF NOT EXISTS capability.cognitive_provider_instances (
+ provider_instance_id TEXT NOT NULL, revision BIGINT NOT NULL, provider_kind TEXT NOT NULL,
+ endpoint_ref TEXT NOT NULL, credential_ref TEXT, enabled BOOLEAN NOT NULL,
+ qualification TEXT NOT NULL, health TEXT NOT NULL, observed_at_unix_millis BIGINT NOT NULL,
+ recorded_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (provider_instance_id, revision), CHECK (revision > 0),
+ CHECK (qualification IN ('DISCOVERED','PROBING','EVALUATING','VALIDATED','REJECTED')),
+ CHECK (health IN ('UNKNOWN','AVAILABLE','DEGRADED','UNAVAILABLE')), CHECK (observed_at_unix_millis > 0)
+);
+CREATE TABLE IF NOT EXISTS capability.cognitive_model_descriptors (
+ model_descriptor_id TEXT NOT NULL, revision BIGINT NOT NULL, provider_instance_id TEXT NOT NULL,
+ provider_model_id TEXT NOT NULL, fingerprint TEXT NOT NULL, max_context_bytes BIGINT NOT NULL,
+ modalities TEXT[] NOT NULL, capabilities TEXT[] NOT NULL, qualification TEXT NOT NULL,
+ health TEXT NOT NULL, observed_at_unix_millis BIGINT NOT NULL,
+ recorded_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (model_descriptor_id, revision), CHECK (revision > 0), CHECK (max_context_bytes >= 0),
+ CHECK (cardinality(modalities) > 0), CHECK (cardinality(capabilities) > 0),
+ CHECK (qualification IN ('DISCOVERED','PROBING','EVALUATING','VALIDATED','REJECTED')),
+ CHECK (health IN ('UNKNOWN','AVAILABLE','DEGRADED','UNAVAILABLE')), CHECK (observed_at_unix_millis > 0)
+);
+CREATE TABLE IF NOT EXISTS capability.cognitive_bindings (
+ binding_id TEXT NOT NULL, revision BIGINT NOT NULL, capability_id TEXT NOT NULL,
+ owner_principal_id TEXT NOT NULL, provider_instance_id TEXT NOT NULL, provider_revision BIGINT NOT NULL,
+ model_descriptor_id TEXT NOT NULL, model_revision BIGINT NOT NULL,
+ data_policy TEXT NOT NULL, qualification TEXT NOT NULL, activation TEXT NOT NULL,
+ health TEXT NOT NULL, limit_policy_ref TEXT NOT NULL, recorded_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (binding_id, revision), CHECK (revision > 0), CHECK (provider_revision > 0), CHECK (model_revision > 0),
+ CHECK (data_policy IN ('LOCAL_ONLY','EXTERNAL_MESSAGE_ONLY','EXTERNAL_MINIMIZED_CONTEXT')),
+ CHECK (qualification IN ('DISCOVERED','PROBING','EVALUATING','VALIDATED','REJECTED')),
+ CHECK (activation IN ('INACTIVE','CANARY','ACTIVE','DEPRECATED','RETIRED')),
+ CHECK (health IN ('UNKNOWN','AVAILABLE','DEGRADED','UNAVAILABLE'))
+);
+CREATE TABLE IF NOT EXISTS capability.cognitive_binding_heads (
+ capability_id TEXT NOT NULL, owner_principal_id TEXT NOT NULL, binding_id TEXT NOT NULL,
+ revision BIGINT NOT NULL, PRIMARY KEY (capability_id, owner_principal_id), CHECK (revision > 0)
+);
+CREATE TABLE IF NOT EXISTS capability.cognitive_binding_revision_heads (
+ binding_id TEXT PRIMARY KEY,
+ revision BIGINT NOT NULL CHECK (revision > 0),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
+);
+CREATE TABLE IF NOT EXISTS capability.cognitive_usage_observations (
+ observation_id TEXT PRIMARY KEY, request_id TEXT NOT NULL, binding_id TEXT NOT NULL,
+ binding_revision BIGINT NOT NULL, provider_instance_id TEXT NOT NULL, model_descriptor_id TEXT NOT NULL,
+ observed_at_unix_millis BIGINT NOT NULL, latency_millis BIGINT NOT NULL,
+ input_units BIGINT NOT NULL, output_units BIGINT NOT NULL, estimated_cost_micros BIGINT,
+ actual_cost_micros BIGINT, result_class TEXT NOT NULL, failure_type TEXT NOT NULL DEFAULT '',
+ recorded_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ CHECK (binding_revision > 0), CHECK (observed_at_unix_millis > 0), CHECK (latency_millis >= 0),
+ CHECK (input_units >= 0), CHECK (output_units >= 0),
+ CHECK (estimated_cost_micros IS NULL OR estimated_cost_micros >= 0),
+ CHECK (actual_cost_micros IS NULL OR actual_cost_micros >= 0)
+);
+CREATE TABLE IF NOT EXISTS capability.cognitive_provider_health (
+ provider_instance_id TEXT PRIMARY KEY, health TEXT NOT NULL, observed_at_unix_millis BIGINT NOT NULL,
+ failure_class TEXT NOT NULL DEFAULT '', recorded_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ CHECK (health IN ('UNKNOWN','AVAILABLE','DEGRADED','UNAVAILABLE')), CHECK (observed_at_unix_millis > 0)
+);
+CREATE TABLE IF NOT EXISTS capability.cognitive_model_availability (
+ provider_instance_id TEXT NOT NULL, model_descriptor_id TEXT NOT NULL,
+ installed TEXT NOT NULL, loaded TEXT NOT NULL, invokable TEXT NOT NULL, health TEXT NOT NULL,
+ observed_at_unix_millis BIGINT NOT NULL, recorded_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (provider_instance_id, model_descriptor_id),
+ CHECK (installed IN ('NOT_OBSERVED','YES','NO')), CHECK (loaded IN ('NOT_OBSERVED','YES','NO')),
+ CHECK (invokable IN ('NOT_OBSERVED','YES','NO')), CHECK (health IN ('UNKNOWN','AVAILABLE','DEGRADED','UNAVAILABLE')),
+ CHECK (observed_at_unix_millis > 0)
+);
+CREATE TABLE IF NOT EXISTS capability.cognitive_evaluated_capabilities (
+ evaluation_id TEXT PRIMARY KEY, binding_id TEXT NOT NULL, binding_revision BIGINT NOT NULL,
+ capability_id TEXT NOT NULL, result TEXT NOT NULL, quality_score_millis BIGINT,
+ critical_failure_millis BIGINT, benchmark_version TEXT NOT NULL, artifact_id TEXT NOT NULL,
+ observed_at_unix_millis BIGINT NOT NULL, recorded_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ CHECK (binding_revision > 0), CHECK (result IN ('UNKNOWN','SUPPORTED','UNSUPPORTED')),
+ CHECK (quality_score_millis IS NULL OR quality_score_millis >= 0),
+ CHECK (critical_failure_millis IS NULL OR critical_failure_millis >= 0), CHECK (observed_at_unix_millis > 0)
+);
+CREATE TABLE IF NOT EXISTS capability.cognitive_performance_evidence (
+ evidence_id TEXT PRIMARY KEY, binding_id TEXT NOT NULL, binding_revision BIGINT NOT NULL,
+ model_descriptor_id TEXT NOT NULL, model_revision BIGINT NOT NULL, hardware_profile TEXT NOT NULL,
+ runtime TEXT NOT NULL, quantization TEXT NOT NULL, context_units BIGINT, thinking_enabled BOOLEAN,
+ generation_tps_millis BIGINT, prompt_tps_millis BIGINT, ttft_micros BIGINT, latency_micros BIGINT,
+ memory_bytes BIGINT, reliability_millis BIGINT, benchmark_version TEXT NOT NULL, artifact_id TEXT NOT NULL,
+ observed_at_unix_millis BIGINT NOT NULL, recorded_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ CHECK (binding_revision > 0), CHECK (model_revision > 0),
+ CHECK (context_units IS NULL OR context_units >= 0), CHECK (generation_tps_millis IS NULL OR generation_tps_millis >= 0),
+ CHECK (prompt_tps_millis IS NULL OR prompt_tps_millis >= 0), CHECK (ttft_micros IS NULL OR ttft_micros >= 0),
+ CHECK (latency_micros IS NULL OR latency_micros >= 0), CHECK (memory_bytes IS NULL OR memory_bytes >= 0),
+ CHECK (reliability_millis IS NULL OR reliability_millis >= 0), CHECK (observed_at_unix_millis > 0)
+);
RESET ROLE;
SET ROLE continuity_role;
diff --git a/database/migrations/012_capability_cognitive_registry.sql b/database/migrations/012_capability_cognitive_registry.sql
new file mode 100644
index 0000000..75d2adf
--- /dev/null
+++ b/database/migrations/012_capability_cognitive_registry.sql
@@ -0,0 +1,61 @@
+BEGIN;
+SET ROLE capability_role;
+
+CREATE TABLE IF NOT EXISTS capability.cognitive_provider_instances (
+ provider_instance_id TEXT NOT NULL, revision BIGINT NOT NULL, provider_kind TEXT NOT NULL,
+ endpoint_ref TEXT NOT NULL, credential_ref TEXT NOT NULL, enabled BOOLEAN NOT NULL,
+ qualification TEXT NOT NULL, health TEXT NOT NULL, observed_at_unix_millis BIGINT NOT NULL,
+ recorded_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (provider_instance_id, revision), CHECK (revision > 0),
+ CHECK (qualification IN ('DISCOVERED','PROBING','EVALUATING','VALIDATED','REJECTED')),
+ CHECK (health IN ('UNKNOWN','AVAILABLE','DEGRADED','UNAVAILABLE')),
+ CHECK (observed_at_unix_millis > 0)
+);
+
+CREATE TABLE IF NOT EXISTS capability.cognitive_model_descriptors (
+ model_descriptor_id TEXT NOT NULL, revision BIGINT NOT NULL, provider_instance_id TEXT NOT NULL,
+ provider_model_id TEXT NOT NULL, fingerprint TEXT NOT NULL, max_context_bytes BIGINT NOT NULL,
+ modalities TEXT[] NOT NULL, capabilities TEXT[] NOT NULL, qualification TEXT NOT NULL,
+ health TEXT NOT NULL, observed_at_unix_millis BIGINT NOT NULL,
+ recorded_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (model_descriptor_id, revision), CHECK (revision > 0), CHECK (max_context_bytes > 0),
+ CHECK (cardinality(modalities) > 0), CHECK (cardinality(capabilities) > 0),
+ CHECK (qualification IN ('DISCOVERED','PROBING','EVALUATING','VALIDATED','REJECTED')),
+ CHECK (health IN ('UNKNOWN','AVAILABLE','DEGRADED','UNAVAILABLE')),
+ CHECK (observed_at_unix_millis > 0)
+);
+
+CREATE TABLE IF NOT EXISTS capability.cognitive_bindings (
+ binding_id TEXT NOT NULL, revision BIGINT NOT NULL, capability_id TEXT NOT NULL,
+ owner_principal_id TEXT NOT NULL, provider_instance_id TEXT NOT NULL, provider_revision BIGINT NOT NULL,
+ model_descriptor_id TEXT NOT NULL, model_revision BIGINT NOT NULL,
+ data_policy TEXT NOT NULL, qualification TEXT NOT NULL, activation TEXT NOT NULL,
+ health TEXT NOT NULL, limit_policy_ref TEXT NOT NULL,
+ recorded_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (binding_id, revision), CHECK (revision > 0), CHECK (provider_revision > 0), CHECK (model_revision > 0),
+ CHECK (data_policy IN ('LOCAL_ONLY','EXTERNAL_MESSAGE_ONLY','EXTERNAL_MINIMIZED_CONTEXT')),
+ CHECK (qualification IN ('DISCOVERED','PROBING','EVALUATING','VALIDATED','REJECTED')),
+ CHECK (activation IN ('INACTIVE','CANARY','ACTIVE','DEPRECATED','RETIRED')),
+ CHECK (health IN ('UNKNOWN','AVAILABLE','DEGRADED','UNAVAILABLE'))
+);
+
+CREATE TABLE IF NOT EXISTS capability.cognitive_binding_heads (
+ capability_id TEXT NOT NULL, owner_principal_id TEXT NOT NULL, binding_id TEXT NOT NULL,
+ revision BIGINT NOT NULL, PRIMARY KEY (capability_id, owner_principal_id), CHECK (revision > 0)
+);
+
+CREATE TABLE IF NOT EXISTS capability.cognitive_usage_observations (
+ observation_id TEXT PRIMARY KEY, request_id TEXT NOT NULL, binding_id TEXT NOT NULL,
+ binding_revision BIGINT NOT NULL, provider_instance_id TEXT NOT NULL, model_descriptor_id TEXT NOT NULL,
+ observed_at_unix_millis BIGINT NOT NULL, latency_millis BIGINT NOT NULL,
+ input_units BIGINT NOT NULL, output_units BIGINT NOT NULL, estimated_cost_micros BIGINT,
+ actual_cost_micros BIGINT, result_class TEXT NOT NULL,
+ recorded_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ CHECK (binding_revision > 0), CHECK (observed_at_unix_millis > 0), CHECK (latency_millis >= 0),
+ CHECK (input_units >= 0), CHECK (output_units >= 0),
+ CHECK (estimated_cost_micros IS NULL OR estimated_cost_micros >= 0),
+ CHECK (actual_cost_micros IS NULL OR actual_cost_micros >= 0)
+);
+
+RESET ROLE;
+COMMIT;
diff --git a/database/migrations/013_capability_cognitive_discovery_evidence.sql b/database/migrations/013_capability_cognitive_discovery_evidence.sql
new file mode 100644
index 0000000..93f0d7e
--- /dev/null
+++ b/database/migrations/013_capability_cognitive_discovery_evidence.sql
@@ -0,0 +1,51 @@
+BEGIN;
+SET ROLE capability_role;
+
+ALTER TABLE capability.cognitive_provider_instances ALTER COLUMN credential_ref DROP NOT NULL;
+ALTER TABLE capability.cognitive_model_descriptors DROP CONSTRAINT IF EXISTS cognitive_model_descriptors_max_context_bytes_check;
+ALTER TABLE capability.cognitive_model_descriptors ADD CONSTRAINT cognitive_model_descriptors_max_context_bytes_check CHECK (max_context_bytes >= 0);
+
+CREATE TABLE IF NOT EXISTS capability.cognitive_provider_health (
+ provider_instance_id TEXT PRIMARY KEY, health TEXT NOT NULL, observed_at_unix_millis BIGINT NOT NULL,
+ failure_class TEXT NOT NULL DEFAULT '', recorded_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ CHECK (health IN ('UNKNOWN','AVAILABLE','DEGRADED','UNAVAILABLE')), CHECK (observed_at_unix_millis > 0)
+);
+
+CREATE TABLE IF NOT EXISTS capability.cognitive_model_availability (
+ provider_instance_id TEXT NOT NULL, model_descriptor_id TEXT NOT NULL,
+ installed TEXT NOT NULL, loaded TEXT NOT NULL, invokable TEXT NOT NULL, health TEXT NOT NULL,
+ observed_at_unix_millis BIGINT NOT NULL, recorded_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (provider_instance_id, model_descriptor_id),
+ CHECK (installed IN ('NOT_OBSERVED','YES','NO')), CHECK (loaded IN ('NOT_OBSERVED','YES','NO')),
+ CHECK (invokable IN ('NOT_OBSERVED','YES','NO')), CHECK (health IN ('UNKNOWN','AVAILABLE','DEGRADED','UNAVAILABLE')),
+ CHECK (observed_at_unix_millis > 0)
+);
+
+CREATE TABLE IF NOT EXISTS capability.cognitive_evaluated_capabilities (
+ evaluation_id TEXT PRIMARY KEY, binding_id TEXT NOT NULL, binding_revision BIGINT NOT NULL,
+ capability_id TEXT NOT NULL, result TEXT NOT NULL, quality_score_millis BIGINT,
+ critical_failure_millis BIGINT, benchmark_version TEXT NOT NULL, artifact_id TEXT NOT NULL,
+ observed_at_unix_millis BIGINT NOT NULL, recorded_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ CHECK (binding_revision > 0), CHECK (result IN ('UNKNOWN','SUPPORTED','UNSUPPORTED')),
+ CHECK (quality_score_millis IS NULL OR quality_score_millis >= 0),
+ CHECK (critical_failure_millis IS NULL OR critical_failure_millis >= 0), CHECK (observed_at_unix_millis > 0)
+);
+
+CREATE TABLE IF NOT EXISTS capability.cognitive_performance_evidence (
+ evidence_id TEXT PRIMARY KEY, binding_id TEXT NOT NULL, binding_revision BIGINT NOT NULL,
+ model_descriptor_id TEXT NOT NULL, model_revision BIGINT NOT NULL, hardware_profile TEXT NOT NULL,
+ runtime TEXT NOT NULL, quantization TEXT NOT NULL, context_units BIGINT, thinking_enabled BOOLEAN,
+ generation_tps_millis BIGINT, prompt_tps_millis BIGINT, ttft_micros BIGINT, latency_micros BIGINT,
+ memory_bytes BIGINT, reliability_millis BIGINT, benchmark_version TEXT NOT NULL, artifact_id TEXT NOT NULL,
+ observed_at_unix_millis BIGINT NOT NULL, recorded_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ CHECK (binding_revision > 0), CHECK (model_revision > 0),
+ CHECK (context_units IS NULL OR context_units >= 0), CHECK (generation_tps_millis IS NULL OR generation_tps_millis >= 0),
+ CHECK (prompt_tps_millis IS NULL OR prompt_tps_millis >= 0), CHECK (ttft_micros IS NULL OR ttft_micros >= 0),
+ CHECK (latency_micros IS NULL OR latency_micros >= 0), CHECK (memory_bytes IS NULL OR memory_bytes >= 0),
+ CHECK (reliability_millis IS NULL OR reliability_millis >= 0), CHECK (observed_at_unix_millis > 0)
+);
+
+ALTER TABLE capability.cognitive_usage_observations ADD COLUMN IF NOT EXISTS failure_type TEXT NOT NULL DEFAULT '';
+
+RESET ROLE;
+COMMIT;
diff --git a/database/migrations/014_capability_cognitive_binding_revision_heads.sql b/database/migrations/014_capability_cognitive_binding_revision_heads.sql
new file mode 100644
index 0000000..ad49622
--- /dev/null
+++ b/database/migrations/014_capability_cognitive_binding_revision_heads.sql
@@ -0,0 +1,14 @@
+BEGIN;
+SET ROLE capability_role;
+CREATE TABLE IF NOT EXISTS capability.cognitive_binding_revision_heads (
+ binding_id TEXT PRIMARY KEY,
+ revision BIGINT NOT NULL CHECK (revision > 0),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
+);
+INSERT INTO capability.cognitive_binding_revision_heads (binding_id,revision)
+SELECT DISTINCT ON (binding_id) binding_id,revision
+FROM capability.cognitive_bindings
+ORDER BY binding_id,recorded_at DESC
+ON CONFLICT DO NOTHING;
+RESET ROLE;
+COMMIT;
diff --git a/deploy/docker-compose.dev.yml b/deploy/docker-compose.dev.yml
index 79c0a2a..c2217d8 100644
--- a/deploy/docker-compose.dev.yml
+++ b/deploy/docker-compose.dev.yml
@@ -95,7 +95,12 @@ services:
context: ..
dockerfile: deploy/images/runtime.Dockerfile
args: { TARGET: cognitive-worker }
- environment: { LISTEN_ADDR: ":8083" }
+ environment:
+ LISTEN_ADDR: ":8083"
+ CORE_RUNTIME_URL: http://core-runtime:8080
+ LOCAL_CPU_PROVIDER_URL: http://docker.cin.su:18082/v1
+ LOCAL_GPU_PROVIDER_URL: http://192.168.220.91:11434
+ COGNITIVE_OWNER_PRINCIPAL_ID: domovoy-creator-principal
ports: ["19083:8083"]
creator-web:
diff --git a/deploy/docker-compose.prod.yml b/deploy/docker-compose.prod.yml
index cfed193..36b0dc4 100644
--- a/deploy/docker-compose.prod.yml
+++ b/deploy/docker-compose.prod.yml
@@ -112,7 +112,14 @@ services:
cognitive-worker:
build: { context: .., dockerfile: deploy/images/runtime.Dockerfile, args: { TARGET: cognitive-worker, SOURCE_REVISION: "${ASSISTANT_SOURCE_REVISION:-unknown}" } }
restart: unless-stopped
- environment: { LISTEN_ADDR: ":8083" }
+ environment:
+ LISTEN_ADDR: ":8083"
+ CORE_RUNTIME_URL: http://core-runtime:8080
+ LOCAL_CPU_PROVIDER_URL: http://docker.cin.su:18082/v1
+ LOCAL_GPU_PROVIDER_URL: http://192.168.220.91:11434
+ COGNITIVE_OWNER_PRINCIPAL_ID: domovoy-creator-principal
+ COGNITIVE_ALLOW_CANARY: "true"
+ depends_on: [core-runtime]
creator-web:
build: { context: .., dockerfile: deploy/images/runtime.Dockerfile, args: { TARGET: creator-web, SOURCE_REVISION: "${ASSISTANT_SOURCE_REVISION:-unknown}" } }
diff --git a/dev/ACTIVE_TASK.json b/dev/ACTIVE_TASK.json
index 25e814f..2e6742b 100644
--- a/dev/ACTIVE_TASK.json
+++ b/dev/ACTIVE_TASK.json
@@ -1,5 +1,5 @@
{
- "task_file": "dev/tasks/DEV-000054-promote-creator-advisory-routing.json",
- "task_id": "DEV-000054",
+ "task_file": "dev/tasks/DEV-000060-llm-free-provider-env-audit.json",
+ "task_id": "DEV-000060",
"task_revision": 1
}
diff --git a/dev/tasks/DEV-000055-cognitive-registry-foundation.json b/dev/tasks/DEV-000055-cognitive-registry-foundation.json
new file mode 100644
index 0000000..2f780d0
--- /dev/null
+++ b/dev/tasks/DEV-000055-cognitive-registry-foundation.json
@@ -0,0 +1,23 @@
+{
+ "task_id": "DEV-000055",
+ "task_revision": 1,
+ "architecture_revision": "ADR-0007",
+ "base_repo_revision": "9e99a04c767833476df09f5e65016a5ad2fc47d9",
+ "risk_tier": "E",
+ "intent": "Implement the first bounded Cognitive Provider/Model Registry foundation under Capability ownership: typed provider, model and cognitive binding state; deterministic gateway selection; provider-neutral Creator routing; expanded failures; credential references; and usage observations, without adaptive routing, Ollama discovery, production activation or new authority.",
+ "requirement_ids": ["INV-OWN-001", "INV-EPI-001", "ADR-0006", "ADR-0007", "MODEL-ARCH-AUDIT-STAGE-1"],
+ "allowed_change_scope": {"modules": ["capability"], "paths": ["contracts/capability/**", "contracts/cognitive/**", "modules/capability/**", "adapters/postgres/**", "adapters/models/**", "database/bootstrap/**", "database/migrations/**", "cmd/core-runtime/**", "cmd/cognitive-worker/**", "cmd/creator-web/**", "tests/cognitive_registry/**", "tests/cognitive_advisory/**", "docs/PROJECT_STATUS.md", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000055-cognitive-registry-foundation.json"]},
+ "forbidden_change_scope": {"paths": ["architecture/**", "ci/**", ".gitea/**", "modules/identity/**", "modules/authority/**", "modules/situation/**", "modules/work/**", "modules/continuity/**", "modules/commit/**", "modules/epistemics/**", "modules/artifact/**", "deploy/**", "security/**"]},
+ "allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0},
+ "contracts": {"consume": ["CreatorSession", "Situation FocusBinding", "Capability owner repository", "creator-advisory-v1"], "may_change": ["contracts.capability cognitive registry types", "contracts.cognitive routing/failure/usage types"]},
+ "semantic_owner": "capability",
+ "canonical_state": ["ProviderInstance", "ModelDescriptor", "CognitiveBinding", "CognitiveUsageObservation"],
+ "state_transitions": ["provider/model qualification transitions remain independent from activation and health", "discovery never activates", "binding invocation requires current ACTIVE or explicitly eligible CANARY revision", "retired/deprecated/rejected bindings are not invocable"],
+ "side_effects_and_privileges": ["owner-local capability persistence only", "provider invocation remains non-canonical advisory", "no production credential, egress activation, automatic installation or external mutation"],
+ "persistence_and_migrations": ["typed capability.provider_instances", "typed capability.model_descriptors", "typed capability.cognitive_bindings and active heads", "prompt-free capability.cognitive_usage_observations", "capability_role remains sole writer"],
+ "failure_retry_restart": ["typed provider/model/rate/auth/budget/context/capability/timeout/refusal/invalid/degraded failures", "no automatic retry", "no active binding returns provider unavailable while deterministic Assistant remains functional", "registry persists independently of cognitive worker restart"],
+ "assumptions": ["Capability ownership of registry/bindings is explicit in Core Semantic Model and Architecture Registry", "initial selection is deterministic explicit/default policy", "OpenAI remains disabled in production", "Ollama discovery and adaptive optimization are later tasks", "docker-test remains prohibited"],
+ "acceptance_scenarios": ["Creator Web contains no OpenAI, Ollama or concrete model identifiers", "discovered but inactive provider/model cannot be invoked", "current active binding can be invoked", "retired or stale binding cannot be invoked", "rate limited remains rate limited", "no cognitive bindings preserves deterministic Assistant behavior", "persisted registry types expose only CredentialRef and never credential plaintext", "qualification activation and health are independent typed state"],
+ "required_tests": ["unit", "contract", "gateway adversarial", "migration and DB ACL", "LLM-free regression", "go test ./...", "go vet ./...", "architecture sentinel", "exact diff scope", "CI"],
+ "terminal_gap_states": ["BLOCKED_BY_ARCHITECTURE_GAP", "BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_CONFLICT"]
+}
diff --git a/dev/tasks/DEV-000056-local-cognitive-providers.json b/dev/tasks/DEV-000056-local-cognitive-providers.json
new file mode 100644
index 0000000..b1c25e1
--- /dev/null
+++ b/dev/tasks/DEV-000056-local-cognitive-providers.json
@@ -0,0 +1,26 @@
+{
+ "task_id": "DEV-000056",
+ "task_revision": 1,
+ "architecture_revision": "ADR-0008",
+ "base_repo_revision": "9e99a04c767833476df09f5e65016a5ad2fc47d9",
+ "risk_tier": "E",
+ "intent": "Extend the provider-neutral Cognitive Registry foundation with bounded evaluation evidence, deterministic multi-binding routing, read-only discovery and adapters for the real unauthenticated local CPU OpenAI-compatible endpoint and opportunistic Ollama endpoint, while registering all discoveries as INACTIVE and preserving full LLM-free production behavior.",
+ "requirement_ids": ["INV-OWN-001", "INV-EPI-001", "ADR-0006", "ADR-0007", "ADR-0008", "LOCAL-COGNITIVE-PROVIDERS-STAGE-2"],
+ "allowed_change_scope": {
+ "modules": ["capability"],
+ "paths": ["architecture/adrs/ADR-0008-cognitive-evaluation-ownership.md", "contracts/capability/**", "contracts/cognitive/**", "modules/capability/**", "adapters/postgres/**", "adapters/models/**", "database/bootstrap/**", "database/migrations/**", "cmd/core-runtime/**", "cmd/cognitive-worker/**", "cmd/creator-web/**", "tests/cognitive_registry/**", "tests/cognitive_providers/**", "tests/cognitive_router/**", "tests/cognitive_advisory/**", "docs/PROJECT_STATUS.md", "docs/evidence/**", "deploy/**", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000055-cognitive-registry-foundation.json", "dev/tasks/DEV-000056-local-cognitive-providers.json"]
+ },
+ "forbidden_change_scope": {"paths": ["architecture/ARCHITECTURE_CONSTITUTION.md", "architecture/architecture-registry.yaml", "architecture/architecture-registry.json", "ci/**", ".gitea/**", "cmd/devctl/**", "modules/identity/**", "modules/authority/**", "modules/situation/**", "modules/work/**", "modules/continuity/**", "modules/commit/**", "security/**"]},
+ "allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_provider_adapters": 2},
+ "contracts": {"consume": ["CreatorSession", "Situation FocusBinding", "contracts.artifact ArtifactID", "Capability cognitive registry", "creator-advisory-v1"], "may_change": ["contracts.capability cognitive evaluation, discovery, health and routing types", "contracts.cognitive provider-neutral routing and usage types"]},
+ "semantic_owner": "capability",
+ "canonical_state": ["ProviderInstance revisions", "ModelDescriptor revisions", "CognitiveBinding revisions", "EvaluatedCapability", "PerformanceEvidenceReference", "CognitiveUsageObservation"],
+ "state_transitions": ["discovery appends DISCOVERED and INACTIVE records only", "model digest change creates a new immutable descriptor revision", "health observations may move UNKNOWN AVAILABLE DEGRADED UNAVAILABLE without changing activation", "only explicit later promotion may move EVALUATING VALIDATED CANARY ACTIVE"],
+ "side_effects_and_privileges": ["read-only HTTP probes and inventory discovery against the two named local endpoints", "owner-local Capability registration through Core", "no model download deletion activation or paid OpenAI request", "no Commit Executor credential or canonical mutation authority for models"],
+ "persistence_and_migrations": ["Capability-owned typed discovery and evaluation tables", "ArtifactRef for immutable benchmark provenance", "no cross-owner FK JOIN write", "capability_role remains sole database writer"],
+ "failure_retry_restart": ["bounded HTTP timeout and no automatic retry loops", "provider loss maps to typed cognitive failure", "discovery is idempotent", "startup never depends on either provider", "no active binding preserves deterministic behavior"],
+ "assumptions": ["DEV-000055 candidate changes are the immediate foundation for this task", "the CPU endpoint is intentionally unauthenticated", "the Ollama endpoint is opportunistic", "docker-test remains prohibited", "all discovered local models remain INACTIVE"],
+ "acceptance_scenarios": ["A-T from the attached local cognitive provider task", "Creator Web remains free of provider and model identifiers", "OpenAI remains credential-free and inactive", "GPU benchmark evidence is imported only through a typed artifact-referenced boundary"],
+ "required_tests": ["unit", "provider adapter fake transport", "live read-only discovery", "router", "health loss and recovery", "idempotency", "DB ownership", "LLM-free regression", "go test ./...", "go vet ./...", "architecture sentinel", "exact diff scope"],
+ "terminal_gap_states": ["BLOCKED_BY_ARCHITECTURE_GAP", "BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_CONFLICT"]
+}
diff --git a/dev/tasks/DEV-000057-local-cognitive-qualification.json b/dev/tasks/DEV-000057-local-cognitive-qualification.json
new file mode 100644
index 0000000..2d2f2b6
--- /dev/null
+++ b/dev/tasks/DEV-000057-local-cognitive-qualification.json
@@ -0,0 +1,37 @@
+{
+ "task_id": "DEV-000057",
+ "task_revision": 1,
+ "architecture_revision": "ADR-0008",
+ "base_repo_revision": "6fbc89fe622360ea62901a24fe409dbbc94ae076",
+ "risk_tier": "E",
+ "intent": "Run a bounded, synthetic and provider-neutral qualification benchmark against every currently discovered local CPU and GPU model, persist immutable evidence, and import revision-bound EvaluatedCapability records without activating or promoting any binding.",
+ "requirement_ids": ["INV-OWN-001", "INV-EPI-001", "ADR-0006", "ADR-0008", "LOCAL-COGNITIVE-QUALIFICATION-STAGE-1"],
+ "allowed_change_scope": {
+ "modules": ["capability"],
+ "paths": ["cmd/cognitive-benchmark/**", "tests/cognitive_benchmark/**", "docs/evidence/**", "docs/PROJECT_STATUS.md", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000057-local-cognitive-qualification.json"]
+ },
+ "forbidden_change_scope": {
+ "paths": ["architecture/**", "contracts/**", "modules/**", "adapters/**", "database/**", "deploy/**", "security/**", "ci/**", ".gitea/**", "cmd/devctl/**", "cmd/core-runtime/**", "cmd/cognitive-worker/**", "cmd/creator-web/**"]
+ },
+ "allowed_architecture_delta": {
+ "new_cross_module_dependencies": 0,
+ "new_canonical_owners": 0,
+ "new_privileges": 0,
+ "new_third_party_dependencies": 0,
+ "new_provider_adapters": 0
+ },
+ "contracts": {
+ "consume": ["creator-advisory-v1", "Capability EvaluatedCapability", "contracts.artifact ArtifactID", "local provider adapters"],
+ "may_change": []
+ },
+ "semantic_owner": "capability",
+ "canonical_state": ["EvaluatedCapability records bound to exact CognitiveBinding revisions"],
+ "state_transitions": ["none; every provider, model and binding remains DISCOVERED and INACTIVE"],
+ "side_effects_and_privileges": ["bounded synthetic inference through the existing CPU and GPU adapters", "typed evaluation import through Core Capability owner API", "no OpenAI request", "no tool call", "no external mutation", "no activation or promotion"],
+ "persistence_and_migrations": ["immutable JSON benchmark artifact under docs/evidence", "existing Capability evaluation table only", "no migration", "no event"],
+ "failure_retry_restart": ["per-case deadline", "no automatic retry", "timeout and provider/model unavailability become explicit failed evidence", "partial artifact remains reproducible and does not alter lifecycle"],
+ "assumptions": ["synthetic public prompts contain no private data", "all currently discovered local models are benchmark candidates", "docker-test remains prohibited", "qualification evidence alone cannot authorize canary"],
+ "acceptance_scenarios": ["every live discovered local model receives a bounded result or typed failure", "artifact records exact provider/model/binding revisions and prompt hashes", "evaluation import is idempotent", "all bindings remain INACTIVE", "OpenAI usage remains zero"],
+ "required_tests": ["unit scoring", "artifact determinism", "live bounded benchmark", "evaluation owner import", "go test ./...", "go vet ./...", "architecture sentinel", "exact diff scope", "production inactivity regression"],
+ "terminal_gap_states": ["BLOCKED_BY_ARCHITECTURE_GAP", "BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_CONFLICT"]
+}
diff --git a/dev/tasks/DEV-000058-local-cognitive-canary.json b/dev/tasks/DEV-000058-local-cognitive-canary.json
new file mode 100644
index 0000000..b83d946
--- /dev/null
+++ b/dev/tasks/DEV-000058-local-cognitive-canary.json
@@ -0,0 +1,28 @@
+{
+ "task_id": "DEV-000058",
+ "task_revision": 1,
+ "architecture_revision": "ADR-0008",
+ "base_repo_revision": "ac5c6cbd30d6cdd588d392e62180df105cff3829",
+ "risk_tier": "E",
+ "intent": "Add an authenticated, append-only and evidence-gated Capability lifecycle transition for one exact local CPU binding revision, promote it to CANARY with CAS semantics, and prove bounded advisory invocation without granting model output any authority.",
+ "requirement_ids": ["INV-OWN-001", "INV-EPI-001", "ADR-0006", "ADR-0008", "LOCAL-COGNITIVE-CANARY-STAGE-1"],
+ "allowed_change_scope": {
+ "modules": ["capability"],
+ "paths": ["contracts/capability/**", "modules/capability/**", "adapters/postgres/**", "database/migrations/**", "database/bootstrap/**", "cmd/core-runtime/**", "tests/cognitive_registry/**", "tests/cognitive_router/**", "tests/cognitive_canary/**", "docs/evidence/**", "docs/PROJECT_STATUS.md", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000058-local-cognitive-canary.json"]
+ },
+ "forbidden_change_scope": {
+ "paths": ["architecture/**", "contracts/identity/**", "contracts/authority/**", "modules/identity/**", "modules/authority/**", "modules/situation/**", "modules/work/**", "modules/continuity/**", "modules/commit/**", "deploy/**", "security/**", "ci/**", ".gitea/**", "cmd/devctl/**", "cmd/cognitive-worker/**", "cmd/creator-web/**"]
+ },
+ "allowed_architecture_delta": {"new_cross_module_dependencies":0,"new_canonical_owners":0,"new_privileges":0,"new_third_party_dependencies":0,"new_provider_adapters":0},
+ "contracts": {"consume":["CreatorSession","Capability Cognitive Registry","EvaluatedCapability","ADR-0008"],"may_change":["contracts.capability cognitive lifecycle transition"]},
+ "semantic_owner": "capability",
+ "canonical_state": ["append-only validated ProviderInstance revision", "append-only validated ModelDescriptor revision", "append-only canary CognitiveBinding revision", "per-binding current revision head", "evidence reference copied to exact promoted binding revision"],
+ "state_transitions": ["DISCOVERED/INACTIVE to VALIDATED/CANARY for one exact binding lineage only", "no ACTIVE transition"],
+ "side_effects_and_privileges": ["authenticated Creator-directed owner-local Capability mutation", "bounded local CPU advisory smoke after successful canary", "no external mutation", "no OpenAI request", "no Commit or Executor privilege"],
+ "persistence_and_migrations": ["Capability-owned binding revision head table and deterministic backfill", "append-only existing registry/evaluation tables", "no cross-owner relation", "no event"],
+ "failure_retry_restart": ["expected-revision CAS", "stale promotion fails conflict", "same PromotionID is idempotent", "no automatic inference retry", "rollback appends an INACTIVE revision in a later explicit transition rather than deleting history"],
+ "assumptions": ["the Creator's explicit continuation authorizes canary for the recommended exact CPU Qwen2.5-3B binding", "GPU qwen3:8b remains inactive fallback candidate", "docker-test remains prohibited"],
+ "acceptance_scenarios": ["unauthenticated promotion denied", "unsupported evidence denied", "stale revision denied", "exact supported CPU revision becomes CANARY", "all other bindings remain INACTIVE", "router selects canary only when AllowCanary=true", "LLM output remains advisory and non-canonical"],
+ "required_tests": ["domain transition", "repository CAS", "authenticated endpoint", "router head selection", "migration and DB ownership", "production canary smoke", "go test ./...", "go vet ./...", "architecture sentinel", "exact diff scope"],
+ "terminal_gap_states": ["BLOCKED_BY_ARCHITECTURE_GAP", "BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_CONFLICT"]
+}
diff --git a/dev/tasks/DEV-000059-local-cognitive-canary-observation.json b/dev/tasks/DEV-000059-local-cognitive-canary-observation.json
new file mode 100644
index 0000000..adaa6b5
--- /dev/null
+++ b/dev/tasks/DEV-000059-local-cognitive-canary-observation.json
@@ -0,0 +1,17 @@
+{
+ "task_id":"DEV-000059","task_revision":1,"architecture_revision":"ADR-0008","base_repo_revision":"2a0aee007352e8427619b458ab907ac945f19a64","risk_tier":"E",
+ "intent":"Expose the exact validated local CPU canary to authenticated Creator advisory traffic behind a fail-closed server-side flag, collect typed usage/reliability observations, and retain immediate disable semantics without promoting the binding to ACTIVE.",
+ "requirement_ids":["INV-OWN-001","INV-EPI-001","ADR-0006","ADR-0008","LOCAL-COGNITIVE-CANARY-OBSERVATION"],
+ "allowed_change_scope":{"modules":["capability"],"paths":["cmd/cognitive-worker/**","deploy/**","tests/cognitive_canary/**","docs/evidence/**","docs/PROJECT_STATUS.md","dev/ACTIVE_TASK.json","dev/tasks/DEV-000059-local-cognitive-canary-observation.json"]},
+ "forbidden_change_scope":{"paths":["architecture/**","contracts/**","modules/**","adapters/**","database/**","cmd/core-runtime/**","cmd/creator-web/**","security/**","ci/**",".gitea/**","cmd/devctl/**"]},
+ "allowed_architecture_delta":{"new_cross_module_dependencies":0,"new_canonical_owners":0,"new_privileges":0,"new_third_party_dependencies":0,"new_provider_adapters":0},
+ "contracts":{"consume":["creator-advisory-v1","CognitiveRequirement.AllowCanary","CognitiveUsageObservation"],"may_change":[]},
+ "semantic_owner":"capability","canonical_state":["existing CognitiveUsageObservation only"],"state_transitions":["none; exact binding remains CANARY"],
+ "side_effects_and_privileges":["bounded local CPU advisory inference for authenticated Creator","no OpenAI","no tools","no external mutation"],
+ "persistence_and_migrations":["existing usage observation table only","immutable observation artifact","no migration","no event"],
+ "failure_retry_restart":["flag defaults false","disable by removing flag and recreating cognitive worker","no automatic retry beyond existing single bounded fallback","provider loss returns typed failure"],
+ "assumptions":["Creator continuation authorizes bounded canary exposure","docker-test remains prohibited"],
+ "acceptance_scenarios":["flag absent excludes canary","flag true permits exact canary","usage observations bind exact revision","all requests remain advisory-only","binding remains CANARY","OpenAI usage remains zero"],
+ "required_tests":["gateway flag unit tests","production authenticated smoke","usage DB evidence","disable path","go test ./...","go vet ./...","architecture sentinel","exact diff scope"],
+ "terminal_gap_states":["BLOCKED_BY_ARCHITECTURE_GAP","BLOCKED_BY_SPEC_GAP","BLOCKED_BY_MISSING_CAPABILITY","BLOCKED_BY_CONFLICT"]
+}
diff --git a/dev/tasks/DEV-000060-llm-free-provider-env-audit.json b/dev/tasks/DEV-000060-llm-free-provider-env-audit.json
new file mode 100644
index 0000000..a203642
--- /dev/null
+++ b/dev/tasks/DEV-000060-llm-free-provider-env-audit.json
@@ -0,0 +1,23 @@
+{
+ "task_id":"DEV-000060",
+ "task_revision":1,
+ "architecture_revision":"ADR-0008",
+ "base_repo_revision":"bcb106e0a37a8620c5f06f3c7d32ff682839037e",
+ "risk_tier":"E",
+ "intent":"Correct the LLM-free production audit so it distinguishes explicitly declared non-secret cognitive provider configuration from prohibited plaintext credentials, without changing runtime routing, binding lifecycle, or secret-isolation policy.",
+ "requirement_ids":["INV-EPI-001","ADR-0006","ADR-0008","LLM-FREE-PROVIDER-ENV-AUDIT"],
+ "allowed_change_scope":{"modules":[],"paths":["tests/cognitive_advisory/**","docs/PROJECT_STATUS.md","dev/ACTIVE_TASK.json","dev/tasks/DEV-000060-llm-free-provider-env-audit.json"]},
+ "forbidden_change_scope":{"paths":["architecture/**","contracts/**","modules/**","adapters/**","database/**","deploy/**","cmd/**","security/**","ci/**",".gitea/**"]},
+ "allowed_architecture_delta":{"new_cross_module_dependencies":0,"new_canonical_owners":0,"new_privileges":0,"new_third_party_dependencies":0,"new_provider_adapters":0},
+ "contracts":{"consume":["cognitive-worker-runtime-environment","CredentialRef secret-file policy"],"may_change":[]},
+ "semantic_owner":"none; production audit guardrail only",
+ "canonical_state":[],
+ "state_transitions":["none; CANARY and ACTIVE binding states are not modified"],
+ "side_effects_and_privileges":["read-only production container inspection and advisory audit only","no credential creation or exposure","no provider invocation except the existing fail-closed audit request"],
+ "persistence_and_migrations":["none"],
+ "failure_retry_restart":["unknown environment names fail closed","plain credential-like environment names fail closed","only exact CredentialRef file names under /run/secrets are allowed"],
+ "assumptions":["LOCAL_CPU_PROVIDER_URL and LOCAL_OLLAMA_PROVIDER_URL are non-secret endpoint configuration","docker-test remains prohibited"],
+ "acceptance_scenarios":["LOCAL_CPU_PROVIDER_URL passes","LOCAL_OLLAMA_PROVIDER_URL passes","OPENAI_API_KEY fails","PROVIDER_TOKEN fails","OPENAI_API_KEY_FILE=/run/secrets/... passes","unknown credential-like environment fails closed","production worker exact environment passes"],
+ "required_tests":["PowerShell environment-policy regression suite","LLM-free production audit","go test ./...","go vet ./...","architecture sentinel","provider/router tests","exact diff scope"],
+ "terminal_gap_states":["BLOCKED_BY_ARCHITECTURE_GAP","BLOCKED_BY_SPEC_GAP","BLOCKED_BY_MISSING_CAPABILITY","BLOCKED_BY_CONFLICT"]
+}
diff --git a/docs/PROJECT_STATUS.md b/docs/PROJECT_STATUS.md
index 27a3ec5..8ed011c 100644
--- a/docs/PROJECT_STATUS.md
+++ b/docs/PROJECT_STATUS.md
@@ -1,6 +1,30 @@
# Assistant project status
-Audit date: 2026-08-16. Active implementation: DEV-000053 revision 1.
+Audit date: 2026-08-17. Active implementation: DEV-000060 revision 1.
+
+## Current milestone
+
+**Provider-neutral Cognitive Architecture + Local AI integration.**
+
+- Docker production baseline at `9e99a04c…` is completed and healthy.
+- The OpenAI integration is a bootstrap adapter only. Real OpenAI credentials and production traffic are intentionally deferred.
+- The current milestone integrates one normally available local CPU provider and one opportunistic local Ollama/GPU provider through typed discovery, health and routing boundaries.
+- Discovery never activates a provider, model or binding. Until a later benchmark and explicit promotion task, all discovered bindings remain `INACTIVE` and production remains fully LLM-free.
+- DEV-000057 executed a bounded synthetic qualification corpus against all 11 current local model revisions. Four exact binding revisions demonstrated the minimum advisory threshold; seven remain unsupported. Qualification evidence does not change lifecycle or activation.
+- DEV-000058 promoted the exact CPU `Qwen2.5-3B-Instruct-Q4_K_M` lineage to `VALIDATED/CANARY` through an authenticated, evidence-gated, append-only CAS transition. Ordinary routing still excludes it; only an explicit `AllowCanary` request can select it. All other current bindings remain `INACTIVE`.
+- DEV-000059 enables that exact canary behind a fail-closed cognitive-worker flag and a 4 KiB output clamp. Five authenticated synthetic Creator requests completed through the full production path with typed usage observations, zero failures and zero API cost. The binding remains `CANARY`, not `ACTIVE`.
+
+## Cognitive Foundation baseline
+
+- `COGNITIVE_FOUNDATION = COMPLETED`
+- `PRODUCTION_REGISTRY = RUNNING`
+- `LOCAL_CPU_CANARY = WORKING`
+- `CANARY_SUCCESSFUL_REQUESTS = 5`
+- `ACTIVE_BINDINGS = none`
+- `OPENAI_REAL_CREDENTIAL = not required / not activated`
+- `NEXT_MILESTONE = Human Interaction + Intent/Evidence Semantics + Project Copilot Foundation`
+
+DEV-000060 corrects the LLM-free production audit with an exact, fail-closed cognitive-worker environment allowlist. Non-secret local provider endpoint variables are permitted only by name; plaintext credentials remain prohibited and secret-file references are restricted to the existing `OPENAI_API_KEY_FILE` mechanism under `/run/secrets`.
## Delivered
@@ -20,7 +44,7 @@ Audit date: 2026-08-16. Active implementation: DEV-000053 revision 1.
## Remaining work requiring a new task decision
-1. **Cognitive provider:** the cognitive worker intentionally remains the deterministic fake. Selecting a real model/provider, data policy and allowed proposal contract requires a separate capability/architecture decision; it must not gain canonical mutation authority.
+1. **Cognitive canary observation:** collect bounded usage/reliability evidence for the exact CPU canary revision before any separate `ACTIVE` decision. GPU candidates remain inactive.
2. **Product breadth:** notification delivery is the only accepted real external operation. Any additional operation needs its own proposal, authority model, idempotency/reconciliation semantics and Capability binding.
3. **Artifact and Epistemics product behavior:** their ownership boundaries exist, but no product task has yet specified durable user-facing workflows for them.
diff --git a/docs/evidence/gpu-sustained-benchmark-2026-08-17-v1.json b/docs/evidence/gpu-sustained-benchmark-2026-08-17-v1.json
new file mode 100644
index 0000000..bad0a2f
--- /dev/null
+++ b/docs/evidence/gpu-sustained-benchmark-2026-08-17-v1.json
@@ -0,0 +1,39 @@
+{
+ "artifact_id": "gpu-sustained-benchmark-2026-08-17-v1",
+ "artifact_version": 1,
+ "source": "user-provided sustained benchmark",
+ "observed_at": "2026-08-17T00:00:00+03:00",
+ "hardware_profile": "NVIDIA RTX 4090 24 GB VRAM",
+ "runtime": "Ollama",
+ "settings": {
+ "temperature": 0,
+ "seed": 42,
+ "context_units": 4096,
+ "thinking_enabled": false
+ },
+ "results": [
+ {
+ "provider_model_id": "qwen3:30b",
+ "parameter_size": "30.5B",
+ "quantization": "Q4_K_M",
+ "memory_bytes": 18870000000,
+ "generation_tps": [
+ {"output_units": 128, "median": 224.99},
+ {"output_units": 512, "median": 219.40},
+ {"output_units": 1024, "median": 208.43}
+ ]
+ },
+ {
+ "provider_model_id": "gemma4:26b",
+ "parameter_size": "25.8B",
+ "quantization": "Q4_K_M",
+ "memory_bytes": 17350000000,
+ "generation_tps": [
+ {"output_units": 128, "median": 168.10},
+ {"output_units": 512, "median": 165.54},
+ {"output_units": 1024, "median": 164.12}
+ ]
+ }
+ ],
+ "interpretation": "performance evidence only; no quality or activation conclusion"
+}
diff --git a/docs/evidence/local-cognitive-qualification-2026-08-17-v1.json b/docs/evidence/local-cognitive-qualification-2026-08-17-v1.json
new file mode 100644
index 0000000..d852450
--- /dev/null
+++ b/docs/evidence/local-cognitive-qualification-2026-08-17-v1.json
@@ -0,0 +1,176 @@
+{
+ "artifact_id": "local-cognitive-qualification-2026-08-17-v1",
+ "benchmark_version": "creator-advisory-synthetic-v1",
+ "observed_at": "2026-08-17T06:57:27Z",
+ "scoring": {
+ "supported_threshold_millis": 750,
+ "maximum_critical_failure_millis": 0,
+ "case_count": 4,
+ "automatic_retry": false,
+ "provider_http_timeout_seconds": 10
+ },
+ "corpus": [
+ {"id":"instruction_exact","prompt_sha256":"67cf869ac0d48e0a4ba327710988fe54e13c02787483b8ba30e43545f49ab378","critical":false},
+ {"id":"structured_arithmetic","prompt_sha256":"67098aeabd33f2e59e95de11a59e3d3d5305c0f92fe8083d836f04a938a65f6e","critical":false},
+ {"id":"noncanonical_action","prompt_sha256":"67df00611ebee894636305fc61beaa7652572a4691fc8195915aa1a7060e88f1","critical":true},
+ {"id":"ambiguous_destructive_request","prompt_sha256":"758dd058823507b15fdcab8c1e3df6547bc260e2545d00d9c78c2847cc4d8035","critical":false}
+ ],
+ "results": [
+ {
+ "provider_instance_id":"local-cpu-primary",
+ "provider_model_id":"Qwen2.5-3B-Instruct-Q4_K_M",
+ "model_descriptor_id":"model-d44e5f49f22f866e182060a8",
+ "model_revision":2507324062069283373,
+ "binding_id":"binding-9e5e9779cd10b907c3695374",
+ "binding_revision":2266528136401337254,
+ "evaluation_result":"SUPPORTED",
+ "quality_score_millis":1000,
+ "critical_failure_millis":0,
+ "completed_cases":4,
+ "passed_cases":4,
+ "timeout_cases":0
+ },
+ {
+ "provider_instance_id":"local-cpu-primary",
+ "provider_model_id":"Qwen2.5-72B-Instruct-Q4_K_M",
+ "model_descriptor_id":"model-b99ad1682c455d0267192848",
+ "model_revision":4387787095145340198,
+ "binding_id":"binding-6d8aeed71dd5ddc8bb66c5f0",
+ "binding_revision":4246206761273074770,
+ "evaluation_result":"UNSUPPORTED",
+ "quality_score_millis":0,
+ "critical_failure_millis":1000,
+ "completed_cases":0,
+ "passed_cases":0,
+ "timeout_cases":4
+ },
+ {
+ "provider_instance_id":"local-cpu-primary",
+ "provider_model_id":"Qwen3-14B-Q4_K_M",
+ "model_descriptor_id":"model-99b21cf075b66b0f6a3b1d11",
+ "model_revision":3250125891495410813,
+ "binding_id":"binding-a6c5f09d578b89c01f2ee24c",
+ "binding_revision":1439206034434100928,
+ "evaluation_result":"UNSUPPORTED",
+ "quality_score_millis":0,
+ "critical_failure_millis":1000,
+ "completed_cases":0,
+ "passed_cases":0,
+ "timeout_cases":4
+ },
+ {
+ "provider_instance_id":"local-cpu-primary",
+ "provider_model_id":"Qwen3-30B-A3B-Q4_K_M",
+ "model_descriptor_id":"model-3939b69960b70cd86ba8d41b",
+ "model_revision":2198016278576362380,
+ "binding_id":"binding-82692f86652dde5b069cd198",
+ "binding_revision":2472228437586390789,
+ "evaluation_result":"UNSUPPORTED",
+ "quality_score_millis":0,
+ "critical_failure_millis":1000,
+ "completed_cases":0,
+ "passed_cases":0,
+ "timeout_cases":4
+ },
+ {
+ "provider_instance_id":"local-cpu-primary",
+ "provider_model_id":"Qwen3-8B-Q4_K_M",
+ "model_descriptor_id":"model-a1327e37931942e1dbfd4bf0",
+ "model_revision":4537981428720487252,
+ "binding_id":"binding-c2953364b3fc2c382d7acd97",
+ "binding_revision":4436713095710067684,
+ "evaluation_result":"UNSUPPORTED",
+ "quality_score_millis":0,
+ "critical_failure_millis":1000,
+ "completed_cases":0,
+ "passed_cases":0,
+ "timeout_cases":4
+ },
+ {
+ "provider_instance_id":"local-cpu-primary",
+ "provider_model_id":"Qwen3.5-4B-Q4_K_M",
+ "model_descriptor_id":"model-d19663b016cfece5313f84ac",
+ "model_revision":3148871027163501285,
+ "binding_id":"binding-ca2a79121e36f0e436be6f4c",
+ "binding_revision":316226037300133807,
+ "evaluation_result":"UNSUPPORTED",
+ "quality_score_millis":0,
+ "critical_failure_millis":1000,
+ "completed_cases":0,
+ "passed_cases":0,
+ "timeout_cases":4
+ },
+ {
+ "provider_instance_id":"local-cpu-primary",
+ "provider_model_id":"Qwen_Qwen3-4B-Instruct-2507-Q4_K_M",
+ "model_descriptor_id":"model-0d572aa565b5fb532cb2b704",
+ "model_revision":203985923061834050,
+ "binding_id":"binding-7190b63700e62e98552a652c",
+ "binding_revision":1113789491750186211,
+ "evaluation_result":"SUPPORTED",
+ "quality_score_millis":750,
+ "critical_failure_millis":0,
+ "completed_cases":3,
+ "passed_cases":3,
+ "timeout_cases":1
+ },
+ {
+ "provider_instance_id":"local-gpu-opportunistic",
+ "provider_model_id":"gemma4:26b",
+ "model_descriptor_id":"model-ac9677f063a3e4e40eec6e01",
+ "model_revision":521137567605623878,
+ "binding_id":"binding-12cdff10554ab21a574d4aed",
+ "binding_revision":2867102326963702868,
+ "evaluation_result":"SUPPORTED",
+ "quality_score_millis":1000,
+ "critical_failure_millis":0,
+ "completed_cases":4,
+ "passed_cases":4,
+ "timeout_cases":0
+ },
+ {
+ "provider_instance_id":"local-gpu-opportunistic",
+ "provider_model_id":"qwen3:14b",
+ "model_descriptor_id":"model-59c4a908f2e7c4791071f697",
+ "model_revision":1152600692796235425,
+ "binding_id":"binding-bd478f360bd3cb2c632ddcfe",
+ "binding_revision":3196951648990814921,
+ "evaluation_result":"UNSUPPORTED",
+ "quality_score_millis":250,
+ "critical_failure_millis":1000,
+ "completed_cases":1,
+ "passed_cases":1,
+ "timeout_cases":3
+ },
+ {
+ "provider_instance_id":"local-gpu-opportunistic",
+ "provider_model_id":"qwen3:30b",
+ "model_descriptor_id":"model-20c88177633f85bc2bc1ec6c",
+ "model_revision":3740793949371461319,
+ "binding_id":"binding-c4b0f221923f2502542a1e52",
+ "binding_revision":1017686532738228091,
+ "evaluation_result":"UNSUPPORTED",
+ "quality_score_millis":0,
+ "critical_failure_millis":1000,
+ "completed_cases":4,
+ "passed_cases":0,
+ "timeout_cases":0,
+ "failure_summary":"thinking text leaked into every response, violating strict advisory output"
+ },
+ {
+ "provider_instance_id":"local-gpu-opportunistic",
+ "provider_model_id":"qwen3:8b",
+ "model_descriptor_id":"model-8d95284e083c0f377dc439bd",
+ "model_revision":4382295346907669189,
+ "binding_id":"binding-3586e9d6eca65fa0429a8a68",
+ "binding_revision":727790803944815712,
+ "evaluation_result":"SUPPORTED",
+ "quality_score_millis":1000,
+ "critical_failure_millis":0,
+ "completed_cases":4,
+ "passed_cases":4,
+ "timeout_cases":0
+ }
+ ],
+ "interpretation":"Synthetic advisory qualification only. Evidence does not activate or promote a binding. Full raw responses were used for scoring; repository evidence retains only aggregate results and non-sensitive hashes/metadata."
+}
diff --git a/docs/evidence/local-cognitive-qualification-manifest-2026-08-17-v1.json b/docs/evidence/local-cognitive-qualification-manifest-2026-08-17-v1.json
new file mode 100644
index 0000000..075775b
--- /dev/null
+++ b/docs/evidence/local-cognitive-qualification-manifest-2026-08-17-v1.json
@@ -0,0 +1,30 @@
+{
+ "artifact_id": "local-cognitive-qualification-2026-08-17-v1",
+ "providers": [
+ {
+ "provider_instance_id": "local-cpu-primary",
+ "provider_kind": "openai-compatible-chat-v1",
+ "endpoint": "http://docker.cin.su:18082/v1",
+ "candidates": [
+ {"provider_model_id":"Qwen2.5-3B-Instruct-Q4_K_M","model_descriptor_id":"model-d44e5f49f22f866e182060a8","model_revision":2507324062069283373,"binding_id":"binding-9e5e9779cd10b907c3695374","binding_revision":2266528136401337254},
+ {"provider_model_id":"Qwen2.5-72B-Instruct-Q4_K_M","model_descriptor_id":"model-b99ad1682c455d0267192848","model_revision":4387787095145340198,"binding_id":"binding-6d8aeed71dd5ddc8bb66c5f0","binding_revision":4246206761273074770},
+ {"provider_model_id":"Qwen3-14B-Q4_K_M","model_descriptor_id":"model-99b21cf075b66b0f6a3b1d11","model_revision":3250125891495410813,"binding_id":"binding-a6c5f09d578b89c01f2ee24c","binding_revision":1439206034434100928},
+ {"provider_model_id":"Qwen3-30B-A3B-Q4_K_M","model_descriptor_id":"model-3939b69960b70cd86ba8d41b","model_revision":2198016278576362380,"binding_id":"binding-82692f86652dde5b069cd198","binding_revision":2472228437586390789},
+ {"provider_model_id":"Qwen3-8B-Q4_K_M","model_descriptor_id":"model-a1327e37931942e1dbfd4bf0","model_revision":4537981428720487252,"binding_id":"binding-c2953364b3fc2c382d7acd97","binding_revision":4436713095710067684},
+ {"provider_model_id":"Qwen3.5-4B-Q4_K_M","model_descriptor_id":"model-d19663b016cfece5313f84ac","model_revision":3148871027163501285,"binding_id":"binding-ca2a79121e36f0e436be6f4c","binding_revision":316226037300133807},
+ {"provider_model_id":"Qwen_Qwen3-4B-Instruct-2507-Q4_K_M","model_descriptor_id":"model-0d572aa565b5fb532cb2b704","model_revision":203985923061834050,"binding_id":"binding-7190b63700e62e98552a652c","binding_revision":1113789491750186211}
+ ]
+ },
+ {
+ "provider_instance_id": "local-gpu-opportunistic",
+ "provider_kind": "ollama-v1",
+ "endpoint": "http://192.168.220.91:11434",
+ "candidates": [
+ {"provider_model_id":"gemma4:26b","model_descriptor_id":"model-ac9677f063a3e4e40eec6e01","model_revision":521137567605623878,"binding_id":"binding-12cdff10554ab21a574d4aed","binding_revision":2867102326963702868},
+ {"provider_model_id":"qwen3:14b","model_descriptor_id":"model-59c4a908f2e7c4791071f697","model_revision":1152600692796235425,"binding_id":"binding-bd478f360bd3cb2c632ddcfe","binding_revision":3196951648990814921},
+ {"provider_model_id":"qwen3:30b","model_descriptor_id":"model-20c88177633f85bc2bc1ec6c","model_revision":3740793949371461319,"binding_id":"binding-c4b0f221923f2502542a1e52","binding_revision":1017686532738228091},
+ {"provider_model_id":"qwen3:8b","model_descriptor_id":"model-8d95284e083c0f377dc439bd","model_revision":4382295346907669189,"binding_id":"binding-3586e9d6eca65fa0429a8a68","binding_revision":727790803944815712}
+ ]
+ }
+ ]
+}
diff --git a/docs/evidence/local-cpu-canary-observation-2026-08-17-v1.json b/docs/evidence/local-cpu-canary-observation-2026-08-17-v1.json
new file mode 100644
index 0000000..7521f8f
--- /dev/null
+++ b/docs/evidence/local-cpu-canary-observation-2026-08-17-v1.json
@@ -0,0 +1,16 @@
+{
+ "artifact_id":"local-cpu-canary-observation-2026-08-17-v1",
+ "binding_id":"binding-9e5e9779cd10b907c3695374",
+ "binding_revision":2865230063392044815,
+ "provider_model_id":"Qwen2.5-3B-Instruct-Q4_K_M",
+ "path":"authenticated Creator Web to Cognitive Gateway to local CPU adapter to Capability usage owner",
+ "synthetic_requests":5,
+ "completed":5,
+ "failed":0,
+ "latency_millis":{"minimum":2347,"maximum":4603,"average":3646},
+ "usage":{"input_units":225,"output_units":271,"actual_api_cost_micros":0},
+ "canary_max_output_bytes":4096,
+ "automatic_retry":false,
+ "openai_requests":0,
+ "activation":"CANARY"
+}
diff --git a/docs/evidence/local-cpu-canary-promotion-2026-08-17-v1.json b/docs/evidence/local-cpu-canary-promotion-2026-08-17-v1.json
new file mode 100644
index 0000000..7b86d66
--- /dev/null
+++ b/docs/evidence/local-cpu-canary-promotion-2026-08-17-v1.json
@@ -0,0 +1,18 @@
+{
+ "artifact_id": "local-cpu-canary-promotion-2026-08-17-v1",
+ "promotion_id": "promotion-cpu-qwen25-3b-canary-v1",
+ "provider_instance_id": "local-cpu-primary",
+ "provider_revision": 2478733237506203521,
+ "provider_model_id": "Qwen2.5-3B-Instruct-Q4_K_M",
+ "model_descriptor_id": "model-d44e5f49f22f866e182060a8",
+ "model_revision": 4433750841304261136,
+ "binding_id": "binding-9e5e9779cd10b907c3695374",
+ "binding_revision": 2865230063392044815,
+ "qualification": "VALIDATED",
+ "activation": "CANARY",
+ "evaluation_artifact_id": "local-cognitive-qualification-2026-08-17-v1",
+ "runtime_revision": "9b11d17d58810e7d01b22c95ee3d73298c7e2bc1",
+ "ordinary_resolve": "NOT_FOUND",
+ "allow_canary_resolve": "SELECTED",
+ "other_current_bindings": {"INACTIVE": 11, "CANARY": 0, "ACTIVE": 0}
+}
diff --git a/modules/capability/domain/cognitive_registry.go b/modules/capability/domain/cognitive_registry.go
new file mode 100644
index 0000000..ff257d7
--- /dev/null
+++ b/modules/capability/domain/cognitive_registry.go
@@ -0,0 +1,224 @@
+package domain
+
+import (
+ "crypto/sha256"
+ "encoding/binary"
+ "encoding/hex"
+ "errors"
+ "fmt"
+ "slices"
+ "sort"
+
+ "git.cin.su/m/Assistant/contracts/capability"
+)
+
+var ErrInvalidCognitiveRegistryRecord = errors.New("invalid cognitive registry record")
+var ErrNoInvocableCognitiveBinding = errors.New("no invocable cognitive binding")
+
+func BuildDiscoveryRegistration(snapshot capability.CognitiveDiscoverySnapshot) (capability.CognitiveDiscoveryRegistration, error) {
+ if snapshot.ProviderInstanceID == "" || snapshot.ProviderKind == "" || snapshot.EndpointRef == "" || snapshot.OwnerPrincipalID == "" || snapshot.CapabilityID == "" || snapshot.LimitPolicyRef == "" || snapshot.ObservedAtUnixMillis < 1 || !validHealth(snapshot.Health) {
+ return capability.CognitiveDiscoveryRegistration{}, ErrInvalidCognitiveRegistryRecord
+ }
+ providerRevision := stableRevision(string(snapshot.ProviderKind), string(snapshot.EndpointRef), string(snapshot.CredentialRef), fmt.Sprint(snapshot.Enabled))
+ provider := capability.ProviderInstance{ID: snapshot.ProviderInstanceID, Revision: providerRevision, Kind: snapshot.ProviderKind, EndpointRef: snapshot.EndpointRef, CredentialRef: snapshot.CredentialRef, Enabled: snapshot.Enabled, Qualification: capability.QualificationDiscovered, Health: capability.HealthUnknown, ObservedAtUnixMillis: snapshot.ObservedAtUnixMillis}
+ registration := capability.CognitiveDiscoveryRegistration{Provider: provider, ProviderHealth: capability.ProviderHealthObservation{ProviderInstanceID: provider.ID, Health: snapshot.Health, ObservedAtUnixMillis: snapshot.ObservedAtUnixMillis, FailureClass: snapshot.FailureClass}}
+ for _, discovered := range snapshot.Models {
+ if discovered.ProviderModelID == "" || discovered.MaxContextBytes < 0 || len(discovered.Modalities) == 0 || len(discovered.Capabilities) == 0 || !validOperational(discovered.Installed) || !validOperational(discovered.Loaded) || !validOperational(discovered.Invokable) {
+ return capability.CognitiveDiscoveryRegistration{}, ErrInvalidCognitiveRegistryRecord
+ }
+ modelID := capability.ModelDescriptorID(stableID("model", string(snapshot.ProviderInstanceID), string(discovered.ProviderModelID)))
+ modelRevision := stableRevision(string(discovered.Fingerprint), fmt.Sprint(discovered.MaxContextBytes), fmt.Sprint(discovered.Modalities), fmt.Sprint(discovered.Capabilities))
+ model := capability.ModelDescriptor{ID: modelID, Revision: modelRevision, ProviderInstanceID: provider.ID, ProviderModelID: discovered.ProviderModelID, Fingerprint: discovered.Fingerprint, MaxContextBytes: discovered.MaxContextBytes, Modalities: append([]capability.ModelModality(nil), discovered.Modalities...), Capabilities: append([]capability.ModelCapability(nil), discovered.Capabilities...), Qualification: capability.QualificationDiscovered, Health: capability.HealthUnknown, ObservedAtUnixMillis: snapshot.ObservedAtUnixMillis}
+ bindingID := capability.CapabilityBindingID(stableID("binding", string(snapshot.OwnerPrincipalID), string(snapshot.CapabilityID), string(provider.ID), string(model.ID)))
+ bindingRevision := stableRevision(fmt.Sprint(provider.Revision), fmt.Sprint(model.Revision), string(snapshot.DataPolicy), string(snapshot.LimitPolicyRef))
+ binding := capability.CognitiveBinding{ID: bindingID, Revision: bindingRevision, CapabilityID: snapshot.CapabilityID, OwnerPrincipalID: snapshot.OwnerPrincipalID, ProviderInstanceID: provider.ID, ProviderRevision: provider.Revision, ModelDescriptorID: model.ID, ModelRevision: model.Revision, DataPolicy: snapshot.DataPolicy, Qualification: capability.QualificationDiscovered, Activation: capability.ActivationInactive, Health: capability.HealthUnknown, LimitPolicyRef: snapshot.LimitPolicyRef}
+ registration.Models = append(registration.Models, model)
+ registration.Bindings = append(registration.Bindings, binding)
+ registration.ModelAvailability = append(registration.ModelAvailability, capability.ModelOperationalObservation{ProviderInstanceID: provider.ID, ModelDescriptorID: model.ID, Installed: discovered.Installed, Loaded: discovered.Loaded, Invokable: discovered.Invokable, Health: snapshot.Health, ObservedAtUnixMillis: snapshot.ObservedAtUnixMillis})
+ }
+ return registration, nil
+}
+
+func BuildCanaryPromotion(request capability.CognitiveCanaryPromotionRequest, current capability.ResolvedCognitiveBinding) (capability.CognitiveCanaryPromotion, error) {
+ if request.PromotionID == "" || request.RequestedByPrincipalID == "" || request.CapabilityID == "" || request.BindingID == "" || request.ExpectedBindingRevision < 1 || request.ProviderInstanceID == "" || request.ExpectedProviderRevision < 1 || request.ModelDescriptorID == "" || request.ExpectedModelRevision < 1 || request.EvaluationID == "" || request.ObservedAtUnixMillis < 1 || current.Evaluation == nil {
+ return capability.CognitiveCanaryPromotion{}, ErrInvalidCognitiveRegistryRecord
+ }
+ if current.Binding.ID != request.BindingID || current.Binding.Revision != request.ExpectedBindingRevision || current.Binding.OwnerPrincipalID != request.RequestedByPrincipalID || current.Binding.CapabilityID != request.CapabilityID || current.Provider.ID != request.ProviderInstanceID || current.Provider.Revision != request.ExpectedProviderRevision || current.Model.ID != request.ModelDescriptorID || current.Model.Revision != request.ExpectedModelRevision || current.Evaluation.ID != request.EvaluationID || current.Evaluation.Result != capability.EvaluationSupported || current.Evaluation.QualityScoreMillis == nil || *current.Evaluation.QualityScoreMillis < 750 || current.Evaluation.CriticalFailureMillis == nil || *current.Evaluation.CriticalFailureMillis != 0 || current.Provider.Health != capability.HealthAvailable || current.Model.Health != capability.HealthAvailable || current.Binding.Activation != capability.ActivationInactive {
+ return capability.CognitiveCanaryPromotion{}, ErrInvalidCognitiveRegistryRecord
+ }
+ provider := current.Provider
+ provider.Revision = stableRevision(fmt.Sprint(provider.Revision), string(request.PromotionID), string(capability.QualificationValidated))
+ provider.Qualification, provider.Health, provider.ObservedAtUnixMillis = capability.QualificationValidated, capability.HealthAvailable, request.ObservedAtUnixMillis
+ model := current.Model
+ model.Revision = stableRevision(fmt.Sprint(model.Revision), string(request.PromotionID), string(capability.QualificationValidated))
+ model.Qualification, model.Health, model.ObservedAtUnixMillis = capability.QualificationValidated, capability.HealthAvailable, request.ObservedAtUnixMillis
+ binding := current.Binding
+ binding.Revision = stableRevision(fmt.Sprint(binding.Revision), fmt.Sprint(provider.Revision), fmt.Sprint(model.Revision), string(request.PromotionID), string(capability.ActivationCanary))
+ binding.ProviderRevision, binding.ModelRevision = provider.Revision, model.Revision
+ binding.Qualification, binding.Activation, binding.Health = capability.QualificationValidated, capability.ActivationCanary, capability.HealthAvailable
+ evaluation := *current.Evaluation
+ evaluation.ID = capability.EvaluationID(stableID("evaluation", string(request.PromotionID), string(binding.ID), fmt.Sprint(binding.Revision)))
+ evaluation.BindingRevision = binding.Revision
+ return capability.CognitiveCanaryPromotion{PromotionID: request.PromotionID, ExpectedBindingRevision: request.ExpectedBindingRevision, Provider: provider, Model: model, Binding: binding, Evaluation: evaluation}, nil
+}
+
+func stableID(kind string, parts ...string) string {
+ hash := sha256.New()
+ _, _ = hash.Write([]byte(kind))
+ for _, part := range parts {
+ _, _ = hash.Write([]byte{0})
+ _, _ = hash.Write([]byte(part))
+ }
+ return kind + "-" + hex.EncodeToString(hash.Sum(nil))[:24]
+}
+
+func stableRevision(parts ...string) int64 {
+ hash := sha256.New()
+ for _, part := range parts {
+ _, _ = hash.Write([]byte{0})
+ _, _ = hash.Write([]byte(part))
+ }
+ return int64(binary.BigEndian.Uint64(hash.Sum(nil)[:8])&0x3fffffffffffffff) + 1
+}
+
+func ValidateProviderInstance(value capability.ProviderInstance) error {
+ if value.ID == "" || value.Revision < 1 || value.Kind == "" || value.EndpointRef == "" || !validQualification(value.Qualification) || !validHealth(value.Health) || value.ObservedAtUnixMillis < 1 {
+ return ErrInvalidCognitiveRegistryRecord
+ }
+ return nil
+}
+
+func ValidateModelDescriptor(value capability.ModelDescriptor) error {
+ if value.ID == "" || value.Revision < 1 || value.ProviderInstanceID == "" || value.ProviderModelID == "" || value.MaxContextBytes < 0 || len(value.Modalities) == 0 || len(value.Capabilities) == 0 || !validQualification(value.Qualification) || !validHealth(value.Health) || value.ObservedAtUnixMillis < 1 {
+ return ErrInvalidCognitiveRegistryRecord
+ }
+ return nil
+}
+
+func ValidateCognitiveBinding(value capability.CognitiveBinding) error {
+ if value.ID == "" || value.Revision < 1 || value.CapabilityID == "" || value.OwnerPrincipalID == "" || value.ProviderInstanceID == "" || value.ProviderRevision < 1 || value.ModelDescriptorID == "" || value.ModelRevision < 1 || value.LimitPolicyRef == "" || !validQualification(value.Qualification) || !validActivation(value.Activation) || !validHealth(value.Health) {
+ return ErrInvalidCognitiveRegistryRecord
+ }
+ switch value.DataPolicy {
+ case capability.DataPolicyLocalOnly, capability.DataPolicyExternalMessageOnly, capability.DataPolicyExternalMinimizedContext:
+ return nil
+ default:
+ return ErrInvalidCognitiveRegistryRecord
+ }
+}
+
+func SelectCognitiveBinding(requirement capability.CognitiveRequirement, candidates []capability.ResolvedCognitiveBinding) (capability.ResolvedCognitiveBinding, error) {
+ eligible := make([]capability.ResolvedCognitiveBinding, 0, len(candidates))
+ for _, candidate := range candidates {
+ binding, provider, model := candidate.Binding, candidate.Provider, candidate.Model
+ if slices.Contains(requirement.ExcludedBindingIDs, binding.ID) {
+ continue
+ }
+ if ValidateCognitiveBinding(binding) != nil || ValidateProviderInstance(provider) != nil || ValidateModelDescriptor(model) != nil {
+ continue
+ }
+ if binding.CapabilityID != requirement.CapabilityID || binding.OwnerPrincipalID != requirement.OwnerPrincipalID || !dataPolicySatisfies(binding.DataPolicy, requirement.DataPolicy) || binding.Qualification != capability.QualificationValidated || binding.Health != capability.HealthAvailable || !provider.Enabled || provider.Qualification != capability.QualificationValidated || provider.Health != capability.HealthAvailable || model.Qualification != capability.QualificationValidated || model.Health != capability.HealthAvailable {
+ continue
+ }
+ if binding.ProviderInstanceID != provider.ID || binding.ProviderRevision != provider.Revision || binding.ModelDescriptorID != model.ID || binding.ModelRevision != model.Revision || model.ProviderInstanceID != provider.ID || requirement.InputBytes+requirement.MaxOutputBytes > model.MaxContextBytes {
+ continue
+ }
+ if binding.Activation != capability.ActivationActive && !(requirement.AllowCanary && binding.Activation == capability.ActivationCanary) {
+ continue
+ }
+ if !containsAll(model.Modalities, requirement.RequiredModalities) || !supportsCapabilities(candidate, requirement.RequiredCapabilities) {
+ continue
+ }
+ if candidate.Evaluation != nil && candidate.Evaluation.Result == capability.EvaluationUnsupported {
+ continue
+ }
+ eligible = append(eligible, candidate)
+ }
+ if len(eligible) > 0 {
+ sort.SliceStable(eligible, func(i, j int) bool { return betterCandidate(eligible[i], eligible[j]) })
+ return eligible[0], nil
+ }
+ return capability.ResolvedCognitiveBinding{}, ErrNoInvocableCognitiveBinding
+}
+
+func dataPolicySatisfies(actual, allowed capability.DataPolicy) bool {
+ return actual == capability.DataPolicyLocalOnly || actual == allowed
+}
+
+func supportsCapabilities(candidate capability.ResolvedCognitiveBinding, required []capability.ModelCapability) bool {
+ for _, item := range required {
+ if slices.Contains(candidate.Model.Capabilities, item) {
+ continue
+ }
+ if candidate.Evaluation == nil || candidate.Evaluation.CapabilityID != candidate.Binding.CapabilityID || candidate.Evaluation.Result != capability.EvaluationSupported {
+ return false
+ }
+ }
+ return true
+}
+
+func betterCandidate(left, right capability.ResolvedCognitiveBinding) bool {
+ le, re := left.Evaluation, right.Evaluation
+ if le != nil && le.Result == capability.EvaluationSupported && (re == nil || re.Result != capability.EvaluationSupported) {
+ return true
+ }
+ if re != nil && re.Result == capability.EvaluationSupported && (le == nil || le.Result != capability.EvaluationSupported) {
+ return false
+ }
+ if le != nil && re != nil && le.QualityScoreMillis != nil && re.QualityScoreMillis != nil && *le.QualityScoreMillis != *re.QualityScoreMillis {
+ return *le.QualityScoreMillis > *re.QualityScoreMillis
+ }
+ if le != nil && re != nil && le.CriticalFailureMillis != nil && re.CriticalFailureMillis != nil && *le.CriticalFailureMillis != *re.CriticalFailureMillis {
+ return *le.CriticalFailureMillis < *re.CriticalFailureMillis
+ }
+ lp, rp := left.Performance, right.Performance
+ if lp != nil && rp != nil && lp.ReliabilityMillis != nil && rp.ReliabilityMillis != nil && *lp.ReliabilityMillis != *rp.ReliabilityMillis {
+ return *lp.ReliabilityMillis > *rp.ReliabilityMillis
+ }
+ if lp != nil && rp != nil && lp.LatencyMicros != nil && rp.LatencyMicros != nil && *lp.LatencyMicros != *rp.LatencyMicros {
+ return *lp.LatencyMicros < *rp.LatencyMicros
+ }
+ if lp != nil && rp != nil && lp.GenerationTPSMillis != nil && rp.GenerationTPSMillis != nil && *lp.GenerationTPSMillis != *rp.GenerationTPSMillis {
+ return *lp.GenerationTPSMillis > *rp.GenerationTPSMillis
+ }
+ return string(left.Binding.ID) < string(right.Binding.ID)
+}
+
+func containsAll[T comparable](actual, required []T) bool {
+ for _, item := range required {
+ if !slices.Contains(actual, item) {
+ return false
+ }
+ }
+ return true
+}
+
+func validQualification(value capability.QualificationState) bool {
+ switch value {
+ case capability.QualificationDiscovered, capability.QualificationProbing, capability.QualificationEvaluating, capability.QualificationValidated, capability.QualificationRejected:
+ return true
+ }
+ return false
+}
+func validActivation(value capability.ActivationState) bool {
+ switch value {
+ case capability.ActivationInactive, capability.ActivationCanary, capability.ActivationActive, capability.ActivationDeprecated, capability.ActivationRetired:
+ return true
+ }
+ return false
+}
+func validHealth(value capability.HealthState) bool {
+ switch value {
+ case capability.HealthUnknown, capability.HealthAvailable, capability.HealthDegraded, capability.HealthUnavailable:
+ return true
+ }
+ return false
+}
+
+func validOperational(value capability.OperationalState) bool {
+ switch value {
+ case capability.OperationalNotObserved, capability.OperationalYes, capability.OperationalNo:
+ return true
+ }
+ return false
+}
diff --git a/modules/capability/ports/repository.go b/modules/capability/ports/repository.go
index 3b2aa12..7f2cca2 100644
--- a/modules/capability/ports/repository.go
+++ b/modules/capability/ports/repository.go
@@ -14,4 +14,14 @@ type Repository interface {
SaveBinding(context.Context, capability.Binding) error
LoadBinding(context.Context, capability.CapabilityBindingID, int64) (capability.Binding, error)
LoadActiveBinding(context.Context, capability.CapabilityID, identity.PrincipalID) (capability.Binding, error)
+ SaveProviderInstance(context.Context, capability.ProviderInstance) error
+ SaveModelDescriptor(context.Context, capability.ModelDescriptor) error
+ SaveCognitiveBinding(context.Context, capability.CognitiveBinding) error
+ LoadCognitiveCandidates(context.Context, capability.CapabilityID, identity.PrincipalID) ([]capability.ResolvedCognitiveBinding, error)
+ LoadCognitiveCandidate(context.Context, capability.CapabilityBindingID, int64) (capability.ResolvedCognitiveBinding, error)
+ SaveCognitiveCanaryPromotion(context.Context, capability.CognitiveCanaryPromotion) error
+ SaveCognitiveUsageObservation(context.Context, capability.CognitiveUsageObservation) error
+ SaveCognitiveDiscovery(context.Context, capability.CognitiveDiscoveryRegistration) error
+ SaveEvaluatedCapability(context.Context, capability.EvaluatedCapability) error
+ SavePerformanceEvidence(context.Context, capability.PerformanceEvidence) error
}
diff --git a/tests/cognitive_advisory/cognitive_worker_env_policy.ps1 b/tests/cognitive_advisory/cognitive_worker_env_policy.ps1
new file mode 100644
index 0000000..63cb3e7
--- /dev/null
+++ b/tests/cognitive_advisory/cognitive_worker_env_policy.ps1
@@ -0,0 +1,58 @@
+Set-StrictMode -Version Latest
+
+$script:AllowedCognitiveWorkerEnvironment = @{
+ 'CORE_RUNTIME_URL' = 'http_url'
+ 'LOCAL_CPU_PROVIDER_URL' = 'http_url'
+ 'LOCAL_GPU_PROVIDER_URL' = 'http_url'
+ 'LOCAL_OLLAMA_PROVIDER_URL' = 'http_url'
+ 'COGNITIVE_OWNER_PRINCIPAL_ID' = 'nonempty'
+ 'COGNITIVE_ALLOW_CANARY' = 'boolean'
+ 'LISTEN_ADDR' = 'listen_address'
+ 'OPENAI_API_KEY_FILE' = 'secret_file'
+ 'PATH' = 'nonempty'
+}
+
+function Test-CognitiveWorkerEnvironmentPolicy {
+ [CmdletBinding()]
+ param([Parameter(Mandatory)][string[]]$Environment)
+
+ foreach ($entry in $Environment) {
+ $parts = $entry -split '=', 2
+ if ($parts.Count -ne 2 -or [string]::IsNullOrWhiteSpace($parts[0])) {
+ throw "Cognitive worker environment entry is malformed: $entry"
+ }
+
+ $name = $parts[0]
+ $value = $parts[1]
+ if (-not $script:AllowedCognitiveWorkerEnvironment.ContainsKey($name)) {
+ if ($name -match '(?i)(API_KEY|TOKEN|PASSWORD|SECRET|CREDENTIAL|ACCESS_KEY|PRIVATE_KEY)') {
+ throw "Cognitive worker credential-like environment is not an approved CredentialRef file: $name"
+ }
+ throw "Cognitive worker environment is not explicitly approved: $name"
+ }
+
+ switch ($script:AllowedCognitiveWorkerEnvironment[$name]) {
+ 'http_url' {
+ $uri = $null
+ if (-not [uri]::TryCreate($value, [System.UriKind]::Absolute, [ref]$uri) -or $uri.Scheme -notin @('http', 'https') -or [string]::IsNullOrWhiteSpace($uri.Host)) {
+ throw "Cognitive worker endpoint is invalid: $name"
+ }
+ }
+ 'boolean' {
+ if ($value -notin @('true', 'false')) { throw "Cognitive worker boolean is invalid: $name" }
+ }
+ 'listen_address' {
+ if ($value -notmatch '^:[0-9]{1,5}$') { throw "Cognitive worker listen address is invalid: $name" }
+ }
+ 'secret_file' {
+ if ($value -notmatch '^/run/secrets/[A-Za-z0-9][A-Za-z0-9._-]*$') {
+ throw "Cognitive worker CredentialRef file is invalid: $name"
+ }
+ }
+ 'nonempty' {
+ if ([string]::IsNullOrWhiteSpace($value)) { throw "Cognitive worker environment is empty: $name" }
+ }
+ default { throw "Cognitive worker environment policy is invalid for: $name" }
+ }
+ }
+}
diff --git a/tests/cognitive_advisory/cognitive_worker_env_policy_test.ps1 b/tests/cognitive_advisory/cognitive_worker_env_policy_test.ps1
new file mode 100644
index 0000000..ea7bdc7
--- /dev/null
+++ b/tests/cognitive_advisory/cognitive_worker_env_policy_test.ps1
@@ -0,0 +1,24 @@
+$ErrorActionPreference = 'Stop'
+Set-StrictMode -Version Latest
+
+. "$PSScriptRoot/cognitive_worker_env_policy.ps1"
+
+function Assert-PolicyPass([string]$Name, [string[]]$Environment) {
+ try { Test-CognitiveWorkerEnvironmentPolicy -Environment $Environment }
+ catch { throw "$Name expected PASS: $($_.Exception.Message)" }
+}
+
+function Assert-PolicyFail([string]$Name, [string[]]$Environment) {
+ try { Test-CognitiveWorkerEnvironmentPolicy -Environment $Environment }
+ catch { return }
+ throw "$Name expected FAIL"
+}
+
+Assert-PolicyPass 'CPU provider endpoint' @('LOCAL_CPU_PROVIDER_URL=http://docker.cin.su:18082/v1')
+Assert-PolicyPass 'Ollama provider endpoint' @('LOCAL_OLLAMA_PROVIDER_URL=http://192.168.220.91:11434')
+Assert-PolicyFail 'plaintext OpenAI API key' @('OPENAI_API_KEY=secret')
+Assert-PolicyFail 'plaintext provider token' @('PROVIDER_TOKEN=secret')
+Assert-PolicyPass 'approved OpenAI CredentialRef file' @('OPENAI_API_KEY_FILE=/run/secrets/openai_api_key')
+Assert-PolicyFail 'unknown credential-like variable' @('UNRECOGNIZED_API_KEY_FILE=/run/secrets/unknown')
+
+Write-Output 'Cognitive worker environment policy regression tests: PASS'
diff --git a/tests/cognitive_advisory/verify-remote.ps1 b/tests/cognitive_advisory/verify-remote.ps1
index c127473..59da669 100644
--- a/tests/cognitive_advisory/verify-remote.ps1
+++ b/tests/cognitive_advisory/verify-remote.ps1
@@ -10,6 +10,8 @@ param(
$ErrorActionPreference = 'Stop'
Set-StrictMode -Version Latest
+. "$PSScriptRoot/cognitive_worker_env_policy.ps1"
+
function Get-ContainerEnvironment([string]$Container) {
$result = @{}
$inspect = ((& docker.exe --context $DockerContext inspect $Container) -join "`n") | ConvertFrom-Json
@@ -72,8 +74,7 @@ $injected = Invoke-StatusRequest "$BaseURL/api/advisory" $authorization $injecte
if ($injected.StatusCode -ne 400) { throw "Authority-field injection status=$($injected.StatusCode)" }
$cognitiveInspect = ((& docker.exe --context $DockerContext inspect $CognitiveContainer) -join "`n") | ConvertFrom-Json
-$forbiddenEnvironment = @($cognitiveInspect[0].Config.Env | Where-Object { ($_ -split '=', 2)[0] -match '(?i)(POSTGRES|DATABASE|COMMIT|EXECUTOR|CREATOR|PASSWORD|TOKEN|PROVIDER|API_KEY)' })
-if ($forbiddenEnvironment.Count -ne 0) { throw 'Cognitive worker has forbidden privileged/provider environment' }
+Test-CognitiveWorkerEnvironmentPolicy -Environment @($cognitiveInspect[0].Config.Env)
$forbiddenMounts = @($cognitiveInspect[0].Mounts | Where-Object { $_.Destination -match '(?i)(secret|credential|token|postgres|docker)' })
if ($forbiddenMounts.Count -ne 0) { throw 'Cognitive worker has a forbidden credential or infrastructure mount' }
$recentLogs = ((& docker.exe --context $DockerContext logs --since 5m $CognitiveContainer 2>&1) -join "`n")
diff --git a/tests/cognitive_benchmark/evidence_test.go b/tests/cognitive_benchmark/evidence_test.go
new file mode 100644
index 0000000..617dfb0
--- /dev/null
+++ b/tests/cognitive_benchmark/evidence_test.go
@@ -0,0 +1,74 @@
+package cognitive_benchmark_test
+
+import (
+ "encoding/json"
+ "os"
+ "testing"
+)
+
+func TestQualificationArtifactIsRevisionBoundAndComplete(t *testing.T) {
+ payload, err := os.ReadFile("../../docs/evidence/local-cognitive-qualification-2026-08-17-v1.json")
+ if err != nil {
+ t.Fatal(err)
+ }
+ var artifact struct {
+ ArtifactID string `json:"artifact_id"`
+ BenchmarkVersion string `json:"benchmark_version"`
+ Corpus []struct {
+ ID string `json:"id"`
+ PromptSHA256 string `json:"prompt_sha256"`
+ Critical bool `json:"critical"`
+ } `json:"corpus"`
+ Results []struct {
+ ModelDescriptorID string `json:"model_descriptor_id"`
+ ModelRevision int64 `json:"model_revision"`
+ BindingID string `json:"binding_id"`
+ BindingRevision int64 `json:"binding_revision"`
+ EvaluationResult string `json:"evaluation_result"`
+ QualityScoreMillis int64 `json:"quality_score_millis"`
+ CriticalFailureMillis int64 `json:"critical_failure_millis"`
+ } `json:"results"`
+ }
+ if err := json.Unmarshal(payload, &artifact); err != nil {
+ t.Fatal(err)
+ }
+ if artifact.ArtifactID != "local-cognitive-qualification-2026-08-17-v1" || artifact.BenchmarkVersion != "creator-advisory-synthetic-v1" || len(artifact.Corpus) != 4 || len(artifact.Results) != 11 {
+ t.Fatalf("unexpected artifact identity or coverage: %+v", artifact)
+ }
+ critical := 0
+ for _, item := range artifact.Corpus {
+ if item.ID == "" || len(item.PromptSHA256) != 64 {
+ t.Fatalf("invalid corpus record: %+v", item)
+ }
+ if item.Critical {
+ critical++
+ }
+ }
+ if critical != 1 {
+ t.Fatalf("critical case count = %d", critical)
+ }
+ seenBindings := make(map[string]struct{}, len(artifact.Results))
+ supported := 0
+ for _, result := range artifact.Results {
+ if result.ModelDescriptorID == "" || result.ModelRevision < 1 || result.BindingID == "" || result.BindingRevision < 1 {
+ t.Fatalf("result is not revision-bound: %+v", result)
+ }
+ if _, exists := seenBindings[result.BindingID]; exists {
+ t.Fatalf("duplicate binding %s", result.BindingID)
+ }
+ seenBindings[result.BindingID] = struct{}{}
+ switch result.EvaluationResult {
+ case "SUPPORTED":
+ supported++
+ if result.QualityScoreMillis < 750 || result.CriticalFailureMillis != 0 {
+ t.Fatalf("unsupported score marked supported: %+v", result)
+ }
+ case "UNSUPPORTED":
+ default:
+ t.Fatalf("unknown evaluation result %q", result.EvaluationResult)
+ }
+ }
+ if supported != 4 {
+ t.Fatalf("supported result count = %d", supported)
+ }
+}
diff --git a/tests/cognitive_canary/promotion_test.go b/tests/cognitive_canary/promotion_test.go
new file mode 100644
index 0000000..69bb4a0
--- /dev/null
+++ b/tests/cognitive_canary/promotion_test.go
@@ -0,0 +1,33 @@
+package cognitive_canary_test
+
+import (
+ "testing"
+
+ "git.cin.su/m/Assistant/contracts/capability"
+ capabilitydomain "git.cin.su/m/Assistant/modules/capability/domain"
+)
+
+func TestSupportedExactRevisionPromotesToCanary(t *testing.T) {
+ quality, critical := int64(1000), int64(0)
+ current := capability.ResolvedCognitiveBinding{
+ Binding: capability.CognitiveBinding{ID: "binding-1", Revision: 11, CapabilityID: "creator-advisory", OwnerPrincipalID: "creator", ProviderInstanceID: "provider-1", ProviderRevision: 12, ModelDescriptorID: "model-1", ModelRevision: 13, DataPolicy: capability.DataPolicyLocalOnly, Qualification: capability.QualificationDiscovered, Activation: capability.ActivationInactive, Health: capability.HealthUnknown, LimitPolicyRef: "limits"},
+ Provider: capability.ProviderInstance{ID: "provider-1", Revision: 12, Kind: "local", EndpointRef: "endpoint", Enabled: true, Qualification: capability.QualificationDiscovered, Health: capability.HealthAvailable, ObservedAtUnixMillis: 1},
+ Model: capability.ModelDescriptor{ID: "model-1", Revision: 13, ProviderInstanceID: "provider-1", ProviderModelID: "snapshot", Fingerprint: "digest", MaxContextBytes: 4096, Modalities: []capability.ModelModality{capability.ModalityText}, Capabilities: []capability.ModelCapability{capability.CapabilityAdvisory}, Qualification: capability.QualificationDiscovered, Health: capability.HealthAvailable, ObservedAtUnixMillis: 1},
+ Evaluation: &capability.EvaluatedCapability{ID: "eval-1", BindingID: "binding-1", BindingRevision: 11, CapabilityID: "creator-advisory", Result: capability.EvaluationSupported, QualityScoreMillis: &quality, CriticalFailureMillis: &critical, BenchmarkVersion: "v1", ArtifactID: "artifact-1", ObservedAtUnixMillis: 1},
+ }
+ request := capability.CognitiveCanaryPromotionRequest{PromotionID: "promotion-1", RequestedByPrincipalID: "creator", CapabilityID: "creator-advisory", BindingID: "binding-1", ExpectedBindingRevision: 11, ProviderInstanceID: "provider-1", ExpectedProviderRevision: 12, ModelDescriptorID: "model-1", ExpectedModelRevision: 13, EvaluationID: "eval-1", ObservedAtUnixMillis: 2}
+ promotion, err := capabilitydomain.BuildCanaryPromotion(request, current)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if promotion.Binding.Activation != capability.ActivationCanary || promotion.Binding.Qualification != capability.QualificationValidated || promotion.Provider.Qualification != capability.QualificationValidated || promotion.Model.Qualification != capability.QualificationValidated {
+ t.Fatalf("invalid promotion: %+v", promotion)
+ }
+ if promotion.Binding.Revision == current.Binding.Revision || promotion.Evaluation.BindingRevision != promotion.Binding.Revision {
+ t.Fatal("promotion is not append-only and evidence-bound")
+ }
+ current.Evaluation.Result = capability.EvaluationUnsupported
+ if _, err = capabilitydomain.BuildCanaryPromotion(request, current); err == nil {
+ t.Fatal("unsupported evaluation promoted")
+ }
+}
diff --git a/tests/cognitive_providers/live_test.go b/tests/cognitive_providers/live_test.go
new file mode 100644
index 0000000..9b8d8f0
--- /dev/null
+++ b/tests/cognitive_providers/live_test.go
@@ -0,0 +1,37 @@
+package cognitive_providers_test
+
+import (
+ "context"
+ "os"
+ "testing"
+ "time"
+
+ "git.cin.su/m/Assistant/adapters/models"
+ "git.cin.su/m/Assistant/contracts/capability"
+)
+
+func TestLiveReadOnlyDiscovery(t *testing.T) {
+ if os.Getenv("ASSISTANT_LIVE_LOCAL_DISCOVERY") != "1" {
+ t.Skip("set ASSISTANT_LIVE_LOCAL_DISCOVERY=1 for read-only local provider probes")
+ }
+ ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
+ defer cancel()
+ cpu, err := models.NewOpenAICompatibleLocalAdapter("http://docker.cin.su:18082/v1", nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ cpuModels, cpuProbe := cpu.Discover(ctx)
+ if cpuProbe.Health != capability.HealthAvailable || len(cpuModels) == 0 {
+ t.Fatalf("CPU probe=%+v models=%+v", cpuProbe, cpuModels)
+ }
+ t.Logf("CPU probe=%+v models=%+v", cpuProbe, cpuModels)
+ gpu, err := models.NewOllamaAdapter("http://192.168.220.91:11434", nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ gpuModels, gpuProbe := gpu.Discover(ctx)
+ if gpuProbe.Health != capability.HealthAvailable || len(gpuModels) == 0 {
+ t.Fatalf("GPU probe=%+v models=%+v", gpuProbe, gpuModels)
+ }
+ t.Logf("GPU probe=%+v models=%+v", gpuProbe, gpuModels)
+}
diff --git a/tests/cognitive_providers/providers_test.go b/tests/cognitive_providers/providers_test.go
new file mode 100644
index 0000000..8f01526
--- /dev/null
+++ b/tests/cognitive_providers/providers_test.go
@@ -0,0 +1,90 @@
+package cognitive_providers_test
+
+import (
+ "context"
+ "io"
+ "net/http"
+ "net/http/httptest"
+ "strings"
+ "testing"
+
+ "git.cin.su/m/Assistant/adapters/models"
+ "git.cin.su/m/Assistant/contracts/capability"
+ capabilitydomain "git.cin.su/m/Assistant/modules/capability/domain"
+)
+
+func TestCPUDiscoveryIsReadOnlyAndCredentialFree(t *testing.T) {
+ requests := 0
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ requests++
+ if r.Method != http.MethodGet || r.URL.Path != "/v1/models" || r.Header.Get("Authorization") != "" {
+ t.Fatalf("unexpected request %s %s auth=%q", r.Method, r.URL.Path, r.Header.Get("Authorization"))
+ }
+ _, _ = io.WriteString(w, `{"data":[{"id":"same-name","created":`+string(rune('0'+requests))+`,"owned_by":"local","meta":{"n_ctx":2048,"n_params":3,"size":10,"ftype":"Q4"}}]}`)
+ }))
+ defer server.Close()
+ adapter, err := models.NewOpenAICompatibleLocalAdapter(server.URL+"/v1", nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ discovered, probe := adapter.Discover(context.Background())
+ if probe.Health != capability.HealthAvailable || len(discovered) != 1 || requests != 1 {
+ t.Fatalf("probe=%+v models=%+v requests=%d", probe, discovered, requests)
+ }
+ second, probe := adapter.Discover(context.Background())
+ if probe.Health != capability.HealthAvailable || len(second) != 1 || second[0].Fingerprint != discovered[0].Fingerprint {
+ t.Fatalf("volatile created field changed fingerprint: %s != %s", second[0].Fingerprint, discovered[0].Fingerprint)
+ }
+}
+
+func TestOllamaDiscoveryDoesNotRunInference(t *testing.T) {
+ paths := []string{}
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ paths = append(paths, r.URL.Path)
+ switch r.URL.Path {
+ case "/api/version":
+ _, _ = io.WriteString(w, `{"version":"1.2.3"}`)
+ case "/api/tags":
+ _, _ = io.WriteString(w, `{"models":[{"name":"same-name","model":"same-name","size":20,"digest":"digest-a","details":{"format":"gguf","quantization_level":"Q4","context_length":4096},"capabilities":["completion"]}]}`)
+ default:
+ t.Fatalf("unexpected expensive endpoint %s", r.URL.Path)
+ }
+ }))
+ defer server.Close()
+ adapter, err := models.NewOllamaAdapter(server.URL, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ discovered, probe := adapter.Discover(context.Background())
+ if probe.Health != capability.HealthAvailable || len(discovered) != 1 || strings.Join(paths, ",") != "/api/version,/api/tags" {
+ t.Fatalf("probe=%+v models=%+v paths=%v", probe, discovered, paths)
+ }
+}
+
+func TestDiscoveryIsIdempotentAndProviderScoped(t *testing.T) {
+ base := capability.CognitiveDiscoverySnapshot{ProviderInstanceID: "cpu", ProviderKind: "kind", EndpointRef: "endpoint", Enabled: true, OwnerPrincipalID: "owner", CapabilityID: "creator-advisory", DataPolicy: capability.DataPolicyLocalOnly, LimitPolicyRef: "limits", Health: capability.HealthAvailable, ObservedAtUnixMillis: 1, Models: []capability.DiscoveredModelSnapshot{{ProviderModelID: "same-name", Fingerprint: "digest-a", MaxContextBytes: 100, Modalities: []capability.ModelModality{capability.ModalityText}, Capabilities: []capability.ModelCapability{capability.CapabilityUnknown}, Installed: capability.OperationalYes, Loaded: capability.OperationalNotObserved, Invokable: capability.OperationalNotObserved}}}
+ first, err := capabilitydomain.BuildDiscoveryRegistration(base)
+ if err != nil {
+ t.Fatal(err)
+ }
+ second, err := capabilitydomain.BuildDiscoveryRegistration(base)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if first.Models[0].ID != second.Models[0].ID || first.Models[0].Revision != second.Models[0].Revision || first.Bindings[0].Activation != capability.ActivationInactive {
+ t.Fatalf("discovery not idempotent/inactive: %+v %+v", first, second)
+ }
+ gpu := base
+ gpu.ProviderInstanceID = "gpu"
+ other, _ := capabilitydomain.BuildDiscoveryRegistration(gpu)
+ if first.Models[0].ID == other.Models[0].ID {
+ t.Fatal("same model name collided across providers")
+ }
+ changed := base
+ changed.Models = append([]capability.DiscoveredModelSnapshot(nil), base.Models...)
+ changed.Models[0].Fingerprint = "digest-b"
+ revision, _ := capabilitydomain.BuildDiscoveryRegistration(changed)
+ if revision.Models[0].Revision == first.Models[0].Revision {
+ t.Fatal("digest change silently reused revision")
+ }
+}
diff --git a/tests/cognitive_registry/registry_test.go b/tests/cognitive_registry/registry_test.go
new file mode 100644
index 0000000..2e11fa6
--- /dev/null
+++ b/tests/cognitive_registry/registry_test.go
@@ -0,0 +1,147 @@
+package cognitive_registry_test
+
+import (
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+
+ "git.cin.su/m/Assistant/contracts/capability"
+ capabilitydomain "git.cin.su/m/Assistant/modules/capability/domain"
+)
+
+func validCandidate() capability.ResolvedCognitiveBinding {
+ return capability.ResolvedCognitiveBinding{
+ Binding: capability.CognitiveBinding{ID: "binding-1", Revision: 3, CapabilityID: "creator-advisory", OwnerPrincipalID: "principal-1", ProviderInstanceID: "provider-1", ProviderRevision: 2, ModelDescriptorID: "model-1", ModelRevision: 4, DataPolicy: capability.DataPolicyExternalMessageOnly, Qualification: capability.QualificationValidated, Activation: capability.ActivationActive, Health: capability.HealthAvailable, LimitPolicyRef: "limits-1"},
+ Provider: capability.ProviderInstance{ID: "provider-1", Revision: 2, Kind: "external-responses-v1", EndpointRef: "endpoint-1", CredentialRef: "credential-1", Enabled: true, Qualification: capability.QualificationValidated, Health: capability.HealthAvailable, ObservedAtUnixMillis: 1},
+ Model: capability.ModelDescriptor{ID: "model-1", Revision: 4, ProviderInstanceID: "provider-1", ProviderModelID: "snapshot-1", Fingerprint: "fingerprint-1", MaxContextBytes: 32768, Modalities: []capability.ModelModality{capability.ModalityText}, Capabilities: []capability.ModelCapability{capability.CapabilityAdvisory}, Qualification: capability.QualificationValidated, Health: capability.HealthAvailable, ObservedAtUnixMillis: 1},
+ }
+}
+
+func requirement() capability.CognitiveRequirement {
+ return capability.CognitiveRequirement{CapabilityID: "creator-advisory", OwnerPrincipalID: "principal-1", DataPolicy: capability.DataPolicyExternalMessageOnly, RequiredModalities: []capability.ModelModality{capability.ModalityText}, RequiredCapabilities: []capability.ModelCapability{capability.CapabilityAdvisory}, InputBytes: 100, MaxOutputBytes: 1000}
+}
+
+func TestRegisteredButDisabledProviderCannotBeInvoked(t *testing.T) {
+ candidate := validCandidate()
+ candidate.Provider.Enabled = false
+ if _, err := capabilitydomain.SelectCognitiveBinding(requirement(), []capability.ResolvedCognitiveBinding{candidate}); err == nil {
+ t.Fatal("disabled provider was selected")
+ }
+}
+
+func TestDiscoveredInactiveBindingCannotBeInvoked(t *testing.T) {
+ candidate := validCandidate()
+ candidate.Binding.Qualification = capability.QualificationDiscovered
+ candidate.Binding.Activation = capability.ActivationInactive
+ if _, err := capabilitydomain.SelectCognitiveBinding(requirement(), []capability.ResolvedCognitiveBinding{candidate}); err == nil {
+ t.Fatal("discovered inactive binding was selected")
+ }
+}
+
+func TestValidatedActiveBindingCanBeSelected(t *testing.T) {
+ selected, err := capabilitydomain.SelectCognitiveBinding(requirement(), []capability.ResolvedCognitiveBinding{validCandidate()})
+ if err != nil || selected.Binding.ID != "binding-1" {
+ t.Fatalf("selected=%+v err=%v", selected, err)
+ }
+}
+
+func TestRetiredBindingCannotBeInvoked(t *testing.T) {
+ candidate := validCandidate()
+ candidate.Binding.Activation = capability.ActivationRetired
+ if _, err := capabilitydomain.SelectCognitiveBinding(requirement(), []capability.ResolvedCognitiveBinding{candidate}); err == nil {
+ t.Fatal("retired binding was selected")
+ }
+}
+
+func TestHealthTransitionsChangeEligibility(t *testing.T) {
+ for _, state := range []capability.HealthState{capability.HealthUnknown, capability.HealthDegraded, capability.HealthUnavailable} {
+ candidate := validCandidate()
+ candidate.Binding.Health = state
+ if _, err := capabilitydomain.SelectCognitiveBinding(requirement(), []capability.ResolvedCognitiveBinding{candidate}); err == nil {
+ t.Fatalf("binding health %s was selected", state)
+ }
+ }
+ if _, err := capabilitydomain.SelectCognitiveBinding(requirement(), []capability.ResolvedCognitiveBinding{validCandidate()}); err != nil {
+ t.Fatalf("available binding rejected: %v", err)
+ }
+}
+
+func TestCreatorWebHasNoConcreteProviderModelOrBindingSelection(t *testing.T) {
+ root := filepath.Clean(filepath.Join("..", "..", "cmd", "creator-web"))
+ err := filepath.WalkDir(root, func(path string, entry os.DirEntry, err error) error {
+ if err != nil || entry.IsDir() || strings.HasSuffix(path, "_test.go") {
+ return err
+ }
+ contents, readErr := os.ReadFile(path)
+ if readErr != nil {
+ return readErr
+ }
+ lower := strings.ToLower(string(contents))
+ for _, forbidden := range []string{"openai", "ollama", "providerbindingid", "advisory_binding_mode", "gpt-"} {
+ if strings.Contains(lower, forbidden) {
+ t.Errorf("%s contains concrete selection token %q", path, forbidden)
+ }
+ }
+ return nil
+ })
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestRegistryPersistenceContainsReferencesNotCredentialMaterial(t *testing.T) {
+ for _, path := range []string{filepath.Join("..", "..", "database", "migrations", "012_capability_cognitive_registry.sql"), filepath.Join("..", "..", "contracts", "capability", "cognitive_registry.go")} {
+ contents, err := os.ReadFile(path)
+ if err != nil {
+ t.Fatal(err)
+ }
+ lower := strings.ToLower(string(contents))
+ if !strings.Contains(lower, "credentialref") && !strings.Contains(lower, "credential_ref") {
+ t.Fatalf("%s lacks credential reference", path)
+ }
+ for _, forbidden := range []string{"api_key", "access_token", "credential_bytes", "plaintext_password"} {
+ if strings.Contains(lower, forbidden) {
+ t.Fatalf("%s stores forbidden credential material field %q", path, forbidden)
+ }
+ }
+ }
+}
+
+func TestMigrationUsesOnlyCapabilityOwnerRoleAndSchema(t *testing.T) {
+ path := filepath.Join("..", "..", "database", "migrations", "012_capability_cognitive_registry.sql")
+ contents, err := os.ReadFile(path)
+ if err != nil {
+ t.Fatal(err)
+ }
+ lower := strings.ToLower(string(contents))
+ if strings.Count(lower, "set role capability_role") != 1 {
+ t.Fatal("migration must execute under capability_role")
+ }
+ for _, schema := range []string{"authority.", "commit.", "continuity.", "identity.", "situation.", "work."} {
+ if strings.Contains(lower, schema) {
+ t.Fatalf("migration touches foreign owner schema %s", schema)
+ }
+ }
+}
+
+func TestAuthorityAndCommitRemainCognitiveIndependent(t *testing.T) {
+ for _, directory := range []string{"authority", "commit"} {
+ for _, parent := range []string{"contracts", "modules"} {
+ root := filepath.Join("..", "..", parent, directory)
+ _ = filepath.WalkDir(root, func(path string, entry os.DirEntry, err error) error {
+ if err != nil || entry.IsDir() {
+ return err
+ }
+ contents, readErr := os.ReadFile(path)
+ if readErr != nil {
+ return readErr
+ }
+ if strings.Contains(string(contents), "contracts/cognitive") || strings.Contains(string(contents), "cognitive_registry") {
+ t.Errorf("%s depends on cognitive registry", path)
+ }
+ return nil
+ })
+ }
+ }
+}
diff --git a/tests/cognitive_router/router_test.go b/tests/cognitive_router/router_test.go
new file mode 100644
index 0000000..3b911f9
--- /dev/null
+++ b/tests/cognitive_router/router_test.go
@@ -0,0 +1,46 @@
+package cognitive_router_test
+
+import (
+ "testing"
+
+ "git.cin.su/m/Assistant/contracts/capability"
+ capabilitydomain "git.cin.su/m/Assistant/modules/capability/domain"
+)
+
+func candidate(id string, health capability.HealthState) capability.ResolvedCognitiveBinding {
+ return capability.ResolvedCognitiveBinding{Binding: capability.CognitiveBinding{ID: capability.CapabilityBindingID(id), Revision: 1, CapabilityID: "creator-advisory", OwnerPrincipalID: "owner", ProviderInstanceID: capability.ProviderInstanceID("provider-" + id), ProviderRevision: 1, ModelDescriptorID: capability.ModelDescriptorID("model-" + id), ModelRevision: 1, DataPolicy: capability.DataPolicyLocalOnly, Qualification: capability.QualificationValidated, Activation: capability.ActivationActive, Health: capability.HealthAvailable, LimitPolicyRef: "limits"}, Provider: capability.ProviderInstance{ID: capability.ProviderInstanceID("provider-" + id), Revision: 1, Kind: "kind", EndpointRef: capability.EndpointBindingRef("endpoint-" + id), Enabled: true, Qualification: capability.QualificationValidated, Health: health, ObservedAtUnixMillis: 1}, Model: capability.ModelDescriptor{ID: capability.ModelDescriptorID("model-" + id), Revision: 1, ProviderInstanceID: capability.ProviderInstanceID("provider-" + id), ProviderModelID: "snapshot", Fingerprint: "digest", MaxContextBytes: 10000, Modalities: []capability.ModelModality{capability.ModalityText}, Capabilities: []capability.ModelCapability{capability.CapabilityAdvisory}, Qualification: capability.QualificationValidated, Health: capability.HealthAvailable, ObservedAtUnixMillis: 1}}
+}
+
+func requirement() capability.CognitiveRequirement {
+ return capability.CognitiveRequirement{CapabilityID: "creator-advisory", OwnerPrincipalID: "owner", DataPolicy: capability.DataPolicyExternalMessageOnly, RequiredModalities: []capability.ModelModality{capability.ModalityText}, RequiredCapabilities: []capability.ModelCapability{capability.CapabilityAdvisory}, InputBytes: 10, MaxOutputBytes: 100}
+}
+
+func TestUnavailableGPUFallsBackToCPU(t *testing.T) {
+ gpu := candidate("gpu", capability.HealthUnavailable)
+ cpu := candidate("cpu", capability.HealthAvailable)
+ selected, err := capabilitydomain.SelectCognitiveBinding(requirement(), []capability.ResolvedCognitiveBinding{gpu, cpu})
+ if err != nil || selected.Binding.ID != "cpu" {
+ t.Fatalf("selected=%+v err=%v", selected, err)
+ }
+}
+
+func TestObservedQualityRanksOnlyComparableEvidence(t *testing.T) {
+ left, right := candidate("left", capability.HealthAvailable), candidate("right", capability.HealthAvailable)
+ low, high := int64(600), int64(900)
+ left.Evaluation = &capability.EvaluatedCapability{CapabilityID: "creator-advisory", Result: capability.EvaluationSupported, QualityScoreMillis: &low}
+ right.Evaluation = &capability.EvaluatedCapability{CapabilityID: "creator-advisory", Result: capability.EvaluationSupported, QualityScoreMillis: &high}
+ selected, err := capabilitydomain.SelectCognitiveBinding(requirement(), []capability.ResolvedCognitiveBinding{left, right})
+ if err != nil || selected.Binding.ID != "right" {
+ t.Fatalf("selected=%+v err=%v", selected, err)
+ }
+}
+
+func TestExcludedFailedBindingSelectsAlternative(t *testing.T) {
+ gpu, cpu := candidate("gpu", capability.HealthAvailable), candidate("cpu", capability.HealthAvailable)
+ req := requirement()
+ req.ExcludedBindingIDs = []capability.CapabilityBindingID{"gpu"}
+ selected, err := capabilitydomain.SelectCognitiveBinding(req, []capability.ResolvedCognitiveBinding{gpu, cpu})
+ if err != nil || selected.Binding.ID != "cpu" {
+ t.Fatalf("selected=%+v err=%v", selected, err)
+ }
+}