Skip to content

Mc/golden agent tuneagent#309

Open
michael-chou359 wants to merge 3 commits into
mainfrom
mc/golden-agent-tuneagent
Open

Mc/golden agent tuneagent#309
michael-chou359 wants to merge 3 commits into
mainfrom
mc/golden-agent-tuneagent

Conversation

@michael-chou359

@michael-chou359 michael-chou359 commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Signal Endpoint

  • created a signal endpoint to send task configs to agentex on clicking the "save" button in tune agent which will trigger workflow signal in agent worker. Full E2E flow was tested and proof included in PR but only code changes were the POST request endpoint.

Before state:

image

After triggering post request, params successfully updated:

Screenshot 2026-06-11 at 4 44 43 PM

On agentex side, DB updated:

Screenshot 2026-06-11 at 4 46 11 PM

config updated signal also triggered:

image

worker updated:

image

Greptile Summary

This PR adds a POST /tasks/{task_id}/signal endpoint that dispatches a named Temporal signal to a running task's workflow, with an optional shallow-merge of the task's params JSONB column so the DB stays in sync with any live config change.

  • New route + schema: SignalTaskRequest carries signal_name, an optional payload forwarded as-is to the Temporal signal handler, and an optional merge_params dict for the DB patch.
  • Atomic DB merge: TaskRepository.merge_params uses Postgres' || JSONB operator inside a single UPDATE … RETURNING to avoid a read-modify-write race; COALESCE guards against a NULL existing column.
  • Use-case orchestration: TasksUseCase.signal_task validates the task is RUNNING, dispatches the Temporal signal, then conditionally applies the DB patch — but silently returns a stale entity when the DB update finds no matching row (see inline comments).

Confidence Score: 3/5

The core JSONB merge and route wiring are solid, but two defects in the signaling path need fixing before merging: the use case silently swallows a failed DB write and returns stale data, and the Temporal adapter unconditionally forwards None as a signal argument which will break handlers with no parameters.

The merge_params silent-fallback means a caller can get a 200 response showing the old params even though the DB update never landed — the Temporal workflow proceeds with new config but the stored task row does not. Separately, adapter_temporal.py always calls handle.signal(signal, arg) even when arg=None, sending a JSON-null positional argument to any signal handler that declares no parameters; this PR is the first consumer of signal_workflow, so this path is now live.

agentex/src/domain/use_cases/tasks_use_case.py (silent stale-entity return) and agentex/src/adapters/temporal/adapter_temporal.py (unconditional None arg in handle.signal)

Important Files Changed

Filename Overview
agentex/src/domain/use_cases/tasks_use_case.py Adds signal_task use case; has a silent-fallback bug when merge_params DB write returns None after a successful signal dispatch
agentex/src/domain/repositories/task_repository.py Adds atomic JSONB shallow-merge via Postgres
agentex/src/domain/services/task_service.py Thin pass-through to repository merge_params; straightforward addition
agentex/src/api/routes/tasks.py Adds POST /{task_id}/signal route with correct authorization annotation; routes to the use case cleanly
agentex/src/api/schemas/tasks.py Adds SignalTaskRequest schema with signal_name, payload, and merge_params fields; well-documented
agentex/openapi.yaml OpenAPI spec updated with new /tasks/{task_id}/signal endpoint and SignalTaskRequest schema; consistent with implementation

Sequence Diagram

sequenceDiagram
    participant Client
    participant Router as POST /tasks/{id}/signal
    participant UseCase as TasksUseCase
    participant TaskSvc as AgentTaskService
    participant TemporalAdapter
    participant Temporal
    participant Repo as TaskRepository
    participant DB as PostgreSQL

    Client->>Router: signal_name, payload?, merge_params?
    Router->>UseCase: signal_task(id, signal_name, payload, merge_params)
    UseCase->>TaskSvc: get_task(id)
    TaskSvc->>DB: "SELECT task WHERE id=?"
    DB-->>TaskSvc: TaskEntity
    TaskSvc-->>UseCase: "task (status=RUNNING guard)"
    UseCase->>TemporalAdapter: "signal_workflow(workflow_id=id, signal=signal_name, arg=payload)"
    TemporalAdapter->>Temporal: handle.signal(signal_name, arg)
    Temporal-->>TemporalAdapter: OK
    TemporalAdapter-->>UseCase: void
    alt merge_params provided
        UseCase->>TaskSvc: merge_task_params(id, patch)
        TaskSvc->>Repo: merge_params(task_id, patch)
        Repo->>DB: "UPDATE tasks SET params = COALESCE(params, {})::jsonb || patch::jsonb WHERE id=? RETURNING *"
        DB-->>Repo: updated row or None
        Repo-->>TaskSvc: TaskEntity or None
        TaskSvc-->>UseCase: merged entity or silent fallback to stale task
    end
    UseCase-->>Router: TaskEntity
    Router-->>Client: Task 200 OK
Loading

Fix All in Cursor Fix All in Claude Code Fix All in Codex

Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
agentex/src/domain/use_cases/tasks_use_case.py:235-239
**Silent stale-entity return when DB merge is skipped**

When `merge_params` is provided, `merge_task_params` returns `None` only if the task row wasn't found by the `UPDATE ... WHERE id = ?` — meaning the task was deleted between the RUNNING guard above and the DB write. In that case the signal was already dispatched to Temporal (the workflow proceeds with the new config), but the code silently falls through to `return task`, handing the caller a 200 with the *pre-signal* entity and never surfacing that the DB write failed. The caller has no way to distinguish "merge succeeded" from "merge was silently dropped".

Reviews (1): Last reviewed commit: "Merge branch 'main' of github.com:scalea..." | Re-trigger Greptile

Greptile also left 1 inline comment on this PR.

@michael-chou359 michael-chou359 requested a review from a team as a code owner June 15, 2026 21:33
@github-actions

github-actions Bot commented Jun 15, 2026

Copy link
Copy Markdown

✱ Stainless preview builds

This PR will update the agentex-sdk SDKs with the following commit messages.

openapi

feat(api): add signal method to tasks

python

chore(internal): regenerate SDK with no functional changes

typescript

chore(internal): regenerate SDK with no functional changes

Edit this comment to update them. They will appear in their respective SDK's changelogs.

agentex-sdk-python studio · code · diff

Your SDK build had at least one new note diagnostic, which is a regression from the base state.
generate ⚠️build ⏭️ (prev: build ✅) → lint ⏭️ (prev: lint ✅) → test ✅

New diagnostics (1 note)
💡 Endpoint/NotConfigured: Skipped endpoint because it's not in your Stainless config: `post /tasks/{task_id}/signal`
agentex-sdk-typescript studio · code · diff

Your SDK build had at least one new note diagnostic, which is a regression from the base state.
generate ⚠️build ⏭️lint ⏭️test ✅

New diagnostics (1 note)
💡 Endpoint/NotConfigured: Skipped endpoint because it's not in your Stainless config: `post /tasks/{task_id}/signal`
agentex-sdk-openapi studio · code · diff

Your SDK build had at least one new note diagnostic, which is a regression from the base state.
generate ✅

New diagnostics (1 note)
💡 Endpoint/NotConfigured: Skipped endpoint because it's not in your Stainless config: `post /tasks/{task_id}/signal`

This comment is auto-generated by GitHub Actions and is automatically kept up to date as you push.
If you push custom code to the preview branch, re-run this workflow to update the comment.
Last updated: 2026-06-15 21:37:30 UTC

Comment on lines +235 to +239
if merge_params:
merged = await self.task_service.merge_task_params(id, merge_params)
if merged is not None:
return merged
return task

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Silent stale-entity return when DB merge is skipped

When merge_params is provided, merge_task_params returns None only if the task row wasn't found by the UPDATE ... WHERE id = ? — meaning the task was deleted between the RUNNING guard above and the DB write. In that case the signal was already dispatched to Temporal (the workflow proceeds with the new config), but the code silently falls through to return task, handing the caller a 200 with the pre-signal entity and never surfacing that the DB write failed. The caller has no way to distinguish "merge succeeded" from "merge was silently dropped".

Prompt To Fix With AI
This is a comment left during a code review.
Path: agentex/src/domain/use_cases/tasks_use_case.py
Line: 235-239

Comment:
**Silent stale-entity return when DB merge is skipped**

When `merge_params` is provided, `merge_task_params` returns `None` only if the task row wasn't found by the `UPDATE ... WHERE id = ?` — meaning the task was deleted between the RUNNING guard above and the DB write. In that case the signal was already dispatched to Temporal (the workflow proceeds with the new config), but the code silently falls through to `return task`, handing the caller a 200 with the *pre-signal* entity and never surfacing that the DB write failed. The caller has no way to distinguish "merge succeeded" from "merge was silently dropped".

How can I resolve this? If you propose a fix, please make it concise.

Fix in Cursor Fix in Claude Code Fix in Codex

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant