From 73d7ffd012cf90b0e91ad10888692a7e961f152c Mon Sep 17 00:00:00 2001 From: Mikhail Date: Mon, 24 Aug 2026 02:56:19 +0300 Subject: [PATCH 1/2] feat: execute generic local work graph candidates --- cmd/agent-execution-bridge/main.go | 40 ++++++++++++++++++- cmd/agent-execution-bridge/main_test.go | 31 ++++++++++++++ cmd/agent-worker/status.go | 1 + contracts/work/lease.go | 8 ++++ ...-000280-local-generic-agent-execution.json | 26 ++++++++++++ 5 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 dev/tasks/DEV-000280-local-generic-agent-execution.json diff --git a/cmd/agent-execution-bridge/main.go b/cmd/agent-execution-bridge/main.go index 2b06611..a1aba34 100644 --- a/cmd/agent-execution-bridge/main.go +++ b/cmd/agent-execution-bridge/main.go @@ -12,6 +12,7 @@ import ( "git.cin.su/m/Assistant/contracts/artifact" "git.cin.su/m/Assistant/contracts/capability" + "git.cin.su/m/Assistant/contracts/cognitive" "git.cin.su/m/Assistant/contracts/work" capabilitydomain "git.cin.su/m/Assistant/modules/capability/domain" ) @@ -63,6 +64,9 @@ func (b bridge) executeHandler(w http.ResponseWriter, r *http.Request) { func (b bridge) execute(ctx context.Context, request work.AgentExecutionRequest) (work.AgentExecutionResult, error) { now := time.Now().UnixMilli() intent := request.Intent + if intent.TaskEvaluationExecution != nil && intent.TaskEvaluationExecution.GenericMessage != "" { + return b.executeGenericCognitiveWork(ctx, request, *intent.TaskEvaluationExecution) + } if intent.TaskEvaluationExecution == nil || intent.TaskEvaluationProfile == nil || len(intent.TaskEvaluationCases) == 0 { return work.AgentExecutionResult{State: work.AgentExecutionWaiting, Summary: "локальный bridge не поддерживает это намерение; работа сохранена и ожидает подходящего исполнителя", ObservedAtUnixMillis: now}, nil } @@ -108,6 +112,40 @@ func (b bridge) execute(ctx context.Context, request work.AgentExecutionRequest) return work.AgentExecutionResult{State: work.AgentExecutionCompleted, Summary: "local bridge executed task evaluation and persisted verifier callback", OutcomeRef: string(evaluation.ID), ArtifactRefs: []string{string(evaluation.ArtifactID)}, ObservedAtUnixMillis: time.Now().UnixMilli()}, nil } +func (b bridge) executeGenericCognitiveWork(ctx context.Context, request work.AgentExecutionRequest, execution work.TaskEvaluationDispatchContext) (work.AgentExecutionResult, error) { + if execution.GraphID == "" || execution.NodeID == "" || execution.ActorID == "" || execution.PrincipalID == "" || execution.ScopeID == "" { + return work.AgentExecutionResult{State: work.AgentExecutionWaiting, Summary: "generic cognitive work context is incomplete; durable work remains waiting for rerouting", ObservedAtUnixMillis: time.Now().UnixMilli()}, nil + } + maxOutputBytes := execution.GenericMaxOutputBytes + if maxOutputBytes < 1 { + maxOutputBytes = 4096 + } + dataPolicy := capability.DataPolicy(execution.GenericDataPolicy) + if dataPolicy == "" { + dataPolicy = capability.DataPolicyLocalOnly + } + response, err := postJSON[cognitive.GatewayAdvisoryResponse](ctx, b.client, b.cognitiveURL+"/v1/advisories", cognitive.GatewayAdvisoryRequest{ + Version: cognitive.CreatorAdvisoryV1, RequestID: cognitive.RequestID("agent-work:" + request.Intent.DispatchID), ActorID: execution.ActorID, + PrincipalID: execution.PrincipalID, ScopeID: execution.ScopeID, Message: execution.GenericMessage, Locale: execution.GenericLocale, + MaxOutputBytes: maxOutputBytes, DataPolicy: dataPolicy, Meaning: cognitive.TaskMeaning{SpeechAct: "DIRECTIVE", RequestedOutcome: execution.GenericPurpose}, + }) + if err != nil { + return work.AgentExecutionResult{}, err + } + if response.FinishReason != cognitive.FinishCompleted || strings.TrimSpace(response.AdvisoryText) == "" { + return work.AgentExecutionResult{}, fmt.Errorf("generic cognitive work did not complete: %s", response.FinishReason) + } + candidate := work.GraphResultCallback{GraphID: execution.GraphID, Revision: execution.GraphRevision, Candidate: work.ResultCandidate{ + ID: "candidate:" + request.Intent.DispatchID, GraphID: execution.GraphID, GraphRevision: execution.GraphRevision, NodeID: execution.NodeID, + AuthorRunID: request.Intent.RunID, ArtifactRefs: []work.EvidenceRef{work.EvidenceRef("agent-output:" + request.Intent.DispatchID)}, EvidenceSummary: "cognitive gateway output recorded as an unverified candidate", + Summary: response.AdvisoryText, + }} + if _, err := postJSON[json.RawMessage](ctx, b.client, b.callbackURL+"/v1/work-graphs/result-callback", candidate); err != nil { + return work.AgentExecutionResult{}, err + } + return work.AgentExecutionResult{State: work.AgentExecutionCompleted, Summary: "local cognitive specialist produced a candidate for independent verification", OutcomeRef: "candidate:" + request.Intent.DispatchID, ArtifactRefs: []string{"agent-output:" + request.Intent.DispatchID}, ObservedAtUnixMillis: time.Now().UnixMilli()}, nil +} + func hasCriticalFailure(observations []capability.TaskCaseObservation) bool { for _, observation := range observations { if observation.CriticalFailure { @@ -151,7 +189,7 @@ func healthHandler(w http.ResponseWriter, r *http.Request) { return } w.Header().Set("Content-Type", "application/json") - _, _ = w.Write([]byte(`{"runtime":"agent-execution-bridge","status":"ok","capabilities":["task-evaluation"]}`)) + _, _ = w.Write([]byte(`{"runtime":"agent-execution-bridge","status":"ok","capabilities":["task-evaluation","generic-cognitive-work"]}`)) } func envOr(name, fallback string) string { diff --git a/cmd/agent-execution-bridge/main_test.go b/cmd/agent-execution-bridge/main_test.go index 34bef5e..7f8c3b8 100644 --- a/cmd/agent-execution-bridge/main_test.go +++ b/cmd/agent-execution-bridge/main_test.go @@ -8,6 +8,7 @@ import ( "testing" "git.cin.su/m/Assistant/contracts/capability" + "git.cin.su/m/Assistant/contracts/cognitive" "git.cin.su/m/Assistant/contracts/work" ) @@ -53,3 +54,33 @@ func TestBridgeRunsTaskEvaluationThroughVerifierAndCallback(t *testing.T) { t.Fatalf("result=%+v err=%v", result, err) } } + +func TestBridgeRunsGenericCognitiveWorkAsUnverifiedGraphCandidate(t *testing.T) { + var callback work.GraphResultCallback + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case "/v1/advisories": + var request cognitive.GatewayAdvisoryRequest + if err := json.NewDecoder(r.Body).Decode(&request); err != nil || request.Message != "find the bounded solution" { + t.Fatalf("unexpected advisory request: %+v err=%v", request, err) + } + _ = json.NewEncoder(w).Encode(cognitive.GatewayAdvisoryResponse{Version: cognitive.CreatorAdvisoryV1, RequestID: request.RequestID, AdvisoryText: "candidate answer", FinishReason: cognitive.FinishCompleted}) + case "/v1/work-graphs/result-callback": + if err := json.NewDecoder(r.Body).Decode(&callback); err != nil { + t.Fatal(err) + } + w.WriteHeader(http.StatusNoContent) + default: + http.NotFound(w, r) + } + })) + defer server.Close() + b := bridge{cognitiveURL: server.URL, callbackURL: server.URL, client: server.Client()} + result, err := b.execute(context.Background(), work.AgentExecutionRequest{Intent: work.AgentDispatchIntent{ + DispatchID: "dispatch-generic", RunID: "run-generic", + TaskEvaluationExecution: &work.TaskEvaluationDispatchContext{GraphID: "graph-1", GraphRevision: 2, NodeID: "node-1", ActorID: "actor-1", PrincipalID: "principal-1", ScopeID: "scope-1", GenericMessage: "find the bounded solution", GenericPurpose: "investigate", GenericDataPolicy: string(capability.DataPolicyLocalOnly)}, + }}) + if err != nil || result.State != work.AgentExecutionCompleted || callback.Candidate.Summary != "candidate answer" || callback.Candidate.AuthorRunID != "run-generic" { + t.Fatalf("result=%+v callback=%+v err=%v", result, callback, err) + } +} diff --git a/cmd/agent-worker/status.go b/cmd/agent-worker/status.go index 223cb7d..e16e0b9 100644 --- a/cmd/agent-worker/status.go +++ b/cmd/agent-worker/status.go @@ -102,6 +102,7 @@ func workerStatusHandler(executorConfigured bool, localFallback ...interface{}) baseStatus.Mode = "execution" baseStatus.Ready = true baseStatus.Reason = "typed agent execution bridge configured" + baseStatus.Capabilities = []string{"task-evaluation", "generic-cognitive-work"} } return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet || (r.URL.Path != "/healthz" && r.URL.Path != "/readyz" && r.URL.Path != "/status") { diff --git a/contracts/work/lease.go b/contracts/work/lease.go index 825280e..c1beced 100644 --- a/contracts/work/lease.go +++ b/contracts/work/lease.go @@ -83,4 +83,12 @@ type TaskEvaluationDispatchContext struct { ActorID identity.ActorID `json:"actor_id"` PrincipalID identity.PrincipalID `json:"principal_id"` ScopeID authority.ScopeID `json:"scope_id"` + // GenericMessage turns the existing durable dispatch context into a + // discriminated local cognitive-work payload without adding a second + // persistence column. It is ignored by task-evaluation executors. + GenericMessage string `json:"generic_message,omitempty"` + GenericPurpose string `json:"generic_purpose,omitempty"` + GenericLocale string `json:"generic_locale,omitempty"` + GenericDataPolicy string `json:"generic_data_policy,omitempty"` + GenericMaxOutputBytes int `json:"generic_max_output_bytes,omitempty"` } diff --git a/dev/tasks/DEV-000280-local-generic-agent-execution.json b/dev/tasks/DEV-000280-local-generic-agent-execution.json new file mode 100644 index 0000000..8f36562 --- /dev/null +++ b/dev/tasks/DEV-000280-local-generic-agent-execution.json @@ -0,0 +1,26 @@ +{ + "task_id": "DEV-000280", + "task_revision": 1, + "architecture_revision": "ADR-0018", + "base_repo_revision": "51db637", + "risk_tier": "C", + "intent": "Add a typed local generic cognitive specialist execution path for Work Graph nodes, preserving independent verification and durable recovery semantics.", + "requirement_ids": ["generic local agent execution", "candidate output is not verified fact", "work graph callback remains independently verifiable"], + "allowed_change_scope": { + "modules": ["work"], + "paths": ["contracts/work/bridge.go", "contracts/work/lease.go", "adapters/postgres/work_repository.go", "cmd/agent-execution-bridge/**", "cmd/agent-worker/**", "tests/agent_execution/**", "dev/tasks/DEV-000280-local-generic-agent-execution.json"] + }, + "forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "modules/identity/**", "modules/authority/**", "LLM-local/**", "deploy/**"]}, + "allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0}, + "contracts": {"consume": ["contracts/work@v1", "cognitive gateway advisory endpoint", "work graph result callback"], "may_change": ["contracts/work@v1 bridge extension"]}, + "semantic_owner": "work", + "canonical_state": ["work dispatch intent and graph candidate only; no canonical fact or external mutation"], + "state_transitions": ["queued -> leased -> executing -> candidate recorded -> verifying", "unsupported or unavailable -> WAITING with durable retry"], + "side_effects_and_privileges": ["local cognitive gateway call", "work graph candidate callback", "no external mutation"], + "persistence_and_migrations": ["reuse existing work.agent_dispatch_intents and graph candidate persistence; no migration"], + "failure_retry_restart": ["bridge timeout or gateway failure returns error for durable retry", "unsupported context returns WAITING", "lease heartbeat and stale claim recovery remain unchanged"], + "assumptions": ["Cognitive Gateway is the only model boundary", "generic output is an unverified candidate until independent synthesis", "local CPU/Lifeboat fallback remains the default capability"], + "acceptance_scenarios": ["typed generic request reaches gateway and callback", "callback candidate leaves graph eligible for verification rather than success", "missing generic context returns WAITING", "gateway failure is retryable and never reported completed"], + "required_tests": ["go test ./...", "go vet ./...", "preflight", "check-diff", "check-architecture", "independent", "git diff --check", "CI"], + "terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_ARCHITECTURE_GAP"] +} -- 2.54.0 From 318a68c3dd7f30e726dceba9f1c2aebddeadfdd1 Mon Sep 17 00:00:00 2001 From: Mikhail Date: Mon, 24 Aug 2026 02:58:40 +0300 Subject: [PATCH 2/2] chore: activate generic agent execution task --- dev/ACTIVE_TASK.json | 4 ++-- dev/tasks/DEV-000280-local-generic-agent-execution.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dev/ACTIVE_TASK.json b/dev/ACTIVE_TASK.json index ffe2c86..a384281 100644 --- a/dev/ACTIVE_TASK.json +++ b/dev/ACTIVE_TASK.json @@ -1,5 +1,5 @@ { - "task_file": "dev/tasks/DEV-000279-live-audit-matrix-reconciliation.json", - "task_id": "DEV-000279", + "task_file": "dev/tasks/DEV-000280-local-generic-agent-execution.json", + "task_id": "DEV-000280", "task_revision": 1 } diff --git a/dev/tasks/DEV-000280-local-generic-agent-execution.json b/dev/tasks/DEV-000280-local-generic-agent-execution.json index 8f36562..c6ef4c8 100644 --- a/dev/tasks/DEV-000280-local-generic-agent-execution.json +++ b/dev/tasks/DEV-000280-local-generic-agent-execution.json @@ -3,12 +3,12 @@ "task_revision": 1, "architecture_revision": "ADR-0018", "base_repo_revision": "51db637", - "risk_tier": "C", + "risk_tier": "E", "intent": "Add a typed local generic cognitive specialist execution path for Work Graph nodes, preserving independent verification and durable recovery semantics.", "requirement_ids": ["generic local agent execution", "candidate output is not verified fact", "work graph callback remains independently verifiable"], "allowed_change_scope": { "modules": ["work"], - "paths": ["contracts/work/bridge.go", "contracts/work/lease.go", "adapters/postgres/work_repository.go", "cmd/agent-execution-bridge/**", "cmd/agent-worker/**", "tests/agent_execution/**", "dev/tasks/DEV-000280-local-generic-agent-execution.json"] + "paths": ["contracts/work/bridge.go", "contracts/work/lease.go", "adapters/postgres/work_repository.go", "cmd/agent-execution-bridge/**", "cmd/agent-worker/**", "tests/agent_execution/**", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000280-local-generic-agent-execution.json"] }, "forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "modules/identity/**", "modules/authority/**", "LLM-local/**", "deploy/**"]}, "allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0}, -- 2.54.0