From 711a734fee7976aa873c4d4a0cfa6edf9bd067a6 Mon Sep 17 00:00:00 2001 From: Mikhail Date: Sun, 30 Aug 2026 01:09:02 +0300 Subject: [PATCH 1/3] fix continuity recovery harness request forwarding --- tests/continuity_execution/verify-remote.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/continuity_execution/verify-remote.ps1 b/tests/continuity_execution/verify-remote.ps1 index 00c8429..f3b8495 100644 --- a/tests/continuity_execution/verify-remote.ps1 +++ b/tests/continuity_execution/verify-remote.ps1 @@ -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 { From 39f6fdee7b99354a67b3c216e8ad9df1816bcfc9 Mon Sep 17 00:00:00 2001 From: Mikhail Date: Sun, 30 Aug 2026 01:45:21 +0300 Subject: [PATCH 2/3] record core M5 acceptance amendment --- ...-core-acceptance-amendment-2026-08-30.json | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 docs/evidence/gen2-027-core-acceptance-amendment-2026-08-30.json diff --git a/docs/evidence/gen2-027-core-acceptance-amendment-2026-08-30.json b/docs/evidence/gen2-027-core-acceptance-amendment-2026-08-30.json new file mode 100644 index 0000000..b1b96d3 --- /dev/null +++ b/docs/evidence/gen2-027-core-acceptance-amendment-2026-08-30.json @@ -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" +} From e8b4c4fdabeb3400b9fa2349e641425972b80eb6 Mon Sep 17 00:00:00 2001 From: Mikhail Date: Sun, 30 Aug 2026 01:51:18 +0300 Subject: [PATCH 3/3] bound cognitive provider readiness and fleet probes --- cmd/cognitive-worker/fleet_status.go | 7 +- cmd/cognitive-worker/fleet_status_test.go | 18 ++++ cmd/cognitive-worker/main.go | 4 +- cmd/core-runtime/main.go | 4 +- dev/ACTIVE_TASK.json | 2 +- .../GEN2-REM-CORE-M5-LOCAL-MEDIUM-001.json | 97 +++++++++++++++++++ 6 files changed, 127 insertions(+), 5 deletions(-) create mode 100644 dev/tasks/GEN2-REM-CORE-M5-LOCAL-MEDIUM-001.json diff --git a/cmd/cognitive-worker/fleet_status.go b/cmd/cognitive-worker/fleet_status.go index 51f4073..b8292b1 100644 --- a/cmd/cognitive-worker/fleet_status.go +++ b/cmd/cognitive-worker/fleet_status.go @@ -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 { diff --git a/cmd/cognitive-worker/fleet_status_test.go b/cmd/cognitive-worker/fleet_status_test.go index 0f223e8..4b830cd 100644 --- a/cmd/cognitive-worker/fleet_status_test.go +++ b/cmd/cognitive-worker/fleet_status_test.go @@ -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()) + } +} diff --git a/cmd/cognitive-worker/main.go b/cmd/cognitive-worker/main.go index b9fa467..2979f46 100644 --- a/cmd/cognitive-worker/main.go +++ b/cmd/cognitive-worker/main.go @@ -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 { diff --git a/cmd/core-runtime/main.go b/cmd/core-runtime/main.go index dc41b00..ddb0d27 100644 --- a/cmd/core-runtime/main.go +++ b/cmd/core-runtime/main.go @@ -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) { diff --git a/dev/ACTIVE_TASK.json b/dev/ACTIVE_TASK.json index 32edf20..82ce70e 100644 --- a/dev/ACTIVE_TASK.json +++ b/dev/ACTIVE_TASK.json @@ -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} diff --git a/dev/tasks/GEN2-REM-CORE-M5-LOCAL-MEDIUM-001.json b/dev/tasks/GEN2-REM-CORE-M5-LOCAL-MEDIUM-001.json new file mode 100644 index 0000000..a9b303c --- /dev/null +++ b/dev/tasks/GEN2-REM-CORE-M5-LOCAL-MEDIUM-001.json @@ -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" + ] +}