merge Local MEDIUM readiness remediation
CI / test (push) Failing after 29s

This commit is contained in:
m
2026-08-30 01:52:02 +03:00
8 changed files with 157 additions and 7 deletions
+6 -1
View File
@@ -20,6 +20,7 @@ type fleetEndpoint struct {
URL string
Headers map[string]string
Client *http.Client
Timeout time.Duration
}
type fleetStatusResponse struct {
@@ -63,7 +64,11 @@ func probeFleetEndpoint(parent context.Context, fallbackClient *http.Client, end
if strings.TrimSpace(endpoint.URL) == "" {
return member
}
ctx, cancel := context.WithTimeout(parent, 2*time.Second)
timeout := endpoint.Timeout
if timeout <= 0 {
timeout = 2 * time.Second
}
ctx, cancel := context.WithTimeout(parent, timeout)
defer cancel()
request, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.URL, nil)
if err != nil {
+18
View File
@@ -68,3 +68,21 @@ func TestFleetReadinessSendsConfiguredProviderHeaders(t *testing.T) {
t.Fatalf("status=%d body=%s", recorder.Code, recorder.Body.String())
}
}
func TestFleetReadinessUsesProviderSpecificTimeout(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
time.Sleep(40 * time.Millisecond)
w.WriteHeader(http.StatusOK)
}))
defer server.Close()
handler := fleetReadinessHandler(server.Client(), capability.FleetTierMedium, []fleetEndpoint{{
ProviderID: "medium", ModelID: "medium-model", Tier: capability.FleetTierMedium,
URL: server.URL, Client: server.Client(), Timeout: 100 * time.Millisecond,
}})
recorder := httptest.NewRecorder()
handler.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/v1/fleet/readiness", nil))
if recorder.Code != http.StatusOK || !strings.Contains(recorder.Body.String(), `"selected_tier":"MEDIUM"`) {
t.Fatalf("provider-specific timeout was not honored: status=%d body=%s", recorder.Code, recorder.Body.String())
}
}
+2 -2
View File
@@ -160,8 +160,8 @@ func main() {
}
fleetEndpoints := []fleetEndpoint{
{ProviderID: "local-cpu-primary", ModelID: "local-cpu-model", Tier: capability.FleetTierFast, URL: cpuEndpoint, Headers: cpuFleetHeaders, Client: &http.Client{Timeout: 10 * time.Second}},
{ProviderID: "local-medium-primary", ModelID: "local-medium-model", Tier: capability.FleetTierMedium, URL: mediumEndpoint, Headers: mediumHeaders, Client: mediumReadinessClient},
{ProviderID: "local-cpu-primary", ModelID: "local-cpu-model", Tier: capability.FleetTierFast, URL: cpuEndpoint, Headers: cpuFleetHeaders, Client: &http.Client{Timeout: 10 * time.Second}, Timeout: 2 * time.Second},
{ProviderID: "local-medium-primary", ModelID: "local-medium-model", Tier: capability.FleetTierMedium, URL: mediumEndpoint, Headers: mediumHeaders, Client: mediumReadinessClient, Timeout: 15 * time.Second},
{ProviderID: "cognitive-lifeboat", ModelID: "qwen3-8b-lifeboat", Tier: capability.FleetTierLifeboat, URL: lifeboatURL},
}
if gpuConfigured {
+3 -1
View File
@@ -155,13 +155,15 @@ func main() {
mux.Handle("/healthz", health.Handler{Runtime: "core-runtime", SemanticGeneration: 1, ArchitectureGeneration: 1})
readinessClient := &http.Client{Timeout: 2 * time.Second}
cognitiveReadinessClient := &http.Client{Timeout: 20 * time.Second}
cognitiveReadinessProbe := health.HTTPProbe(cognitiveReadinessClient, strings.TrimRight(envOr("COGNITIVE_RUNTIME_URL", "http://cognitive-worker:8083"), "/")+"/readyz")
cognitiveReadinessProbe.Timeout = 20 * time.Second
mux.Handle("/readyz", health.ReadinessHandler{Runtime: "core-runtime", Probes: []health.Probe{
health.TCPProbe("postgres", envOr("POSTGRES_HOST", "postgres"), envOr("POSTGRES_PORT", "5432")),
health.HTTPProbe(readinessClient, strings.TrimRight(envOr("CONTINUITY_RUNTIME_URL", "http://continuity-runtime:8081"), "/")+"/healthz"),
// A running worker is not necessarily ready to serve cognitive traffic.
// Keep deterministic paths alive, but do not advertise overall readiness
// while the cognitive dependency is still warming.
health.HTTPProbe(cognitiveReadinessClient, strings.TrimRight(envOr("COGNITIVE_RUNTIME_URL", "http://cognitive-worker:8083"), "/")+"/readyz"),
cognitiveReadinessProbe,
}})
admissionPolicy := capability.AdmissionPolicy{ID: envOr("COGNITIVE_ADMISSION_POLICY_ID", "creator-advisory-local-v1"), MaxConcurrent: envInt("COGNITIVE_ADMISSION_MAX_CONCURRENT", 1), MaxAcceptedPerHour: envInt("COGNITIVE_ADMISSION_MAX_ACCEPTED_PER_HOUR", 60), MaxInputBytes: envInt("COGNITIVE_ADMISSION_MAX_INPUT_BYTES", 80<<10), MaxOutputBytes: envInt("COGNITIVE_ADMISSION_MAX_OUTPUT_BYTES", 16<<10)}
mux.HandleFunc("POST /v1/internal/cognitive/admission", func(w http.ResponseWriter, r *http.Request) {
+1 -1
View File
@@ -1 +1 @@
{"task_file":"dev/tasks/GEN2-COGNITIVE-001-qualify-and-promote-local-medium.json","task_id":"GEN2-COGNITIVE-001","task_revision":1}
{"task_file":"dev/tasks/GEN2-REM-CORE-M5-LOCAL-MEDIUM-001.json","task_id":"GEN2-REM-CORE-M5-LOCAL-MEDIUM-001","task_revision":1}
@@ -0,0 +1,97 @@
{
"task_id": "GEN2-REM-CORE-M5-LOCAL-MEDIUM-001",
"task_revision": 1,
"architecture_revision": "ARCH-GEN2-000",
"base_repo_revision": "gen2-integration",
"risk_tier": "E",
"intent": "Restore the existing Local MEDIUM cognitive path and make discovery, invocation, model availability and readiness evidence bounded and distinct for Core M5 acceptance.",
"requirement_ids": [
"REQ-GEN2-027-GOAL",
"REQ-GEN2-027-BOUNDARY",
"REQ-GEN2-027-GATE"
],
"allowed_change_scope": {
"modules": ["configuration", "capability", "operations/supervisor"],
"paths": [
"cmd/cognitive-worker/**",
"cmd/core-runtime/**",
"runtime/health/**",
"runtime/supervisor/**",
"deploy/**",
"modules/configuration/**",
"contracts/configuration/**",
"tests/**",
"docs/evidence/**",
"dev/ACTIVE_TASK.json",
"dev/tasks/GEN2-REM-CORE-M5-LOCAL-MEDIUM-001.json"
]
},
"forbidden_change_scope": {
"paths": [
"security/**",
"architecture/**",
"database/**",
"adapters/models/**",
"go.mod",
"go.sum",
".gitea/workflows/**"
]
},
"allowed_architecture_delta": {
"new_cross_module_dependencies": 0,
"new_canonical_owners": 0,
"new_privileges": 0,
"new_database_owner_roles": 0,
"new_third_party_dependencies": 0
},
"contracts": {
"consume": ["contracts.capability@gen2", "contracts.configuration@gen2"],
"may_change": ["Only bounded readiness and provider-routing behavior in the declared paths."]
},
"semantic_owner": "Capability and Operations/Supervisor; no new semantic owner.",
"canonical_state": [
"Existing Capability registry remains authoritative for binding qualification and activation.",
"Lifeboat remains fallback only; it does not replace Local MEDIUM."
],
"state_transitions": [
"provider discovery -> qualified binding -> active route -> unavailable/degraded -> recovered active route"
],
"side_effects_and_privileges": [
"No external mutation; no credential value is read into evidence; no new privilege or database change."
],
"persistence_and_migrations": [
"No database schema or migration change. Existing provider and capability state remain authoritative."
],
"assumptions": [
"The existing llm-local gateway and protected provider references are the approved Local MEDIUM deployment path.",
"Production data and secret values are not copied or exposed."
],
"failure_retry_restart": [
"Provider probes have bounded per-provider timeouts and do not block unrelated healthy paths.",
"Core readiness is ready when required infrastructure and cognitive worker readiness are available; optional provider loss is represented as degraded fleet state.",
"Restart must re-run discovery and restore the same routing without changing canonical state."
],
"acceptance_scenarios": [
"SQLite/gateway contention is identified without reading credentials.",
"Local MEDIUM discovery health, invocation health and model availability are separately observable.",
"Local MEDIUM authenticated model probe and a real cognitive invocation succeed.",
"CPU/GPU loss does not prevent MEDIUM selection; Lifeboat is fallback.",
"MEDIUM loss selects Lifeboat and recovery returns selection to MEDIUM.",
"Core and cognitive-worker restart restore readiness and routing."
],
"required_tests": [
"FAST: go test ./cmd/cognitive-worker/... ./cmd/core-runtime/... ./runtime/health/... ./runtime/supervisor/...",
"FAST: go vet ./cmd/cognitive-worker/... ./cmd/core-runtime/... ./runtime/health/... ./runtime/supervisor/...",
"FAST governance: go run ./cmd/devctl preflight -task dev/tasks/GEN2-REM-CORE-M5-LOCAL-MEDIUM-001.json",
"FAST scope: go run ./cmd/devctl check-diff -task dev/tasks/GEN2-REM-CORE-M5-LOCAL-MEDIUM-001.json",
"FAST independent: go run ./cmd/devctl/independent",
"FAST whitespace: git diff --check gen2-integration --",
"Targeted production provider/restart/recovery smoke"
],
"terminal_gap_states": [
"BLOCKED_BY_ARCHITECTURE_GAP",
"BLOCKED_BY_SPEC_GAP",
"BLOCKED_BY_MISSING_CAPABILITY",
"REQUIRES_MIGRATION_DECISION"
]
}
@@ -0,0 +1,28 @@
{
"evidence_id": "gen2-027-core-acceptance-amendment-2026-08-30",
"task_id": "GEN2-027",
"acceptance_amendment": {
"approved_by_project_owner": true,
"ui_browser_acceptance": "DEFERRED",
"counts_as_pass": false,
"counts_as_blocker": false,
"note": "UI/browser acceptance is consciously excluded from the current core-only M5 stage."
},
"core_status": "BLOCKED_BY_MISSING_CAPABILITY",
"findings": {
"core_health": "200",
"core_ready": "503",
"cognitive_health": "200",
"cognitive_fleet": {
"local_medium": "UNAVAILABLE: authenticated /v1/models probe timed out",
"cpu_fast": "UNAVAILABLE",
"gpu": "UNAVAILABLE",
"lifeboat": "AVAILABLE"
},
"configured_secret_files": "present and readable; values not read",
"interpretation": "The configured Local MEDIUM path cannot currently be proven ACTIVE/VALIDATED/AVAILABLE; Lifeboat remains fallback and is not counted as primary MEDIUM acceptance."
},
"full_m5_gate": "NOT_RUN",
"production_promotion": "NOT_RUN",
"gen2_028": "NOT_STARTED"
}
+2 -2
View File
@@ -10,7 +10,7 @@ function Invoke-JsonPost([string]$container, [string]$url, [object]$body, [bool]
[IO.File]::WriteAllText($local, $json, [Text.UTF8Encoding]::new($false))
docker --context $DockerContext cp $local "${container}:$remote" | Out-Null
if ($internal) {
$response = docker --context $DockerContext exec $container sh -c 'wget -qO- --header="Content-Type: application/json" --header="Authorization: Bearer $COMMIT_EXECUTOR_TOKEN" --post-file "$1" "$2"' sh $remote $url
$response = docker --context $DockerContext exec -e "POST_FILE=$remote" -e "POST_URL=$url" $container sh -c 'wget -qO- --header="Content-Type: application/json" --header="Authorization: Bearer $COMMIT_EXECUTOR_TOKEN" --post-file "$POST_FILE" "$POST_URL"'
} else {
$response = docker --context $DockerContext exec $container wget -qO- --header "Content-Type: application/json" --post-file $remote $url
}
@@ -31,7 +31,7 @@ function Expect-InternalPostStatus([string]$container, [string]$url, [object]$bo
docker --context $DockerContext cp $local "${container}:$remote" | Out-Null
$previous = $ErrorActionPreference
$ErrorActionPreference = "Continue"
$response = (docker --context $DockerContext exec $container sh -c 'wget -S -O /dev/null --header="Content-Type: application/json" --header="Authorization: Bearer $COMMIT_EXECUTOR_TOKEN" --post-file "$1" "$2"' sh $remote $url 2>&1) -join "`n"
$response = (docker --context $DockerContext exec -e "POST_FILE=$remote" -e "POST_URL=$url" $container sh -c 'wget -S -O /dev/null --header="Content-Type: application/json" --header="Authorization: Bearer $COMMIT_EXECUTOR_TOKEN" --post-file "$POST_FILE" "$POST_URL"' 2>&1) -join "`n"
$ErrorActionPreference = $previous
if ($response -notmatch "HTTP/1\.[01] $status") { throw "POST $url did not return $status" }
} finally {