Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR introduces the Wave 0 shared execution event contract, establishing the foundational type system, validation logic, redaction/truncation rules, store interfaces, API DTOs, and worker-side recorder abstractions for task execution timeline events.
Changes:
- Added event taxonomy (types, severities, stream type), normalization, validation, and redaction/truncation contract in
internal/events. - Added store-facing
ExecutionEvent/filter/store interfaces with an in-memory fake implementation, API DTOs with store-to-DTO conversion, and workerEventRecorderwith no-op/fake implementations. - Added reusable fixture timelines for downstream testing and Wave 0/Wave 1 development documentation.
Show a summary per file
| File | Description |
|---|---|
internal/events/execution_event.go |
Defines Wave 0 event type constants, severity constants, stream type, and validation/normalization helpers |
internal/events/redaction.go |
Implements redaction (secrets), truncation (size limits), and sanitization for event payloads |
internal/events/execution_event_test.go |
Tests for event taxonomy, severity normalization, and redaction/truncation logic |
internal/store/execution_event.go |
Store-facing ExecutionEvent struct, ExecutionEventFilter, ExecutionEventStore interface, and in-memory fake |
internal/store/execution_event_test.go |
Tests for store filter normalization, validation, append/list/delete operations |
internal/api/execution_event_dto.go |
API DTOs for event submission, response, and list response; store-to-DTO conversion |
internal/api/execution_event_dto_test.go |
Tests for DTO serialization, conversion, sanitization, and fixture validation |
workers/common/event_recorder.go |
Worker-side EventRecorder interface, no-op and fake implementations, event option helpers |
workers/common/event_recorder_test.go |
Tests for fake and no-op event recorders |
internal/testdata/execution-events/*.json |
Fixture timelines for basic, tool-using, and failed task scenarios |
docs/development/execution-events-wave-0.md |
Documents dependency graph and Wave 1 issue slicing |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 13/13 changed files
- Comments generated: 2
Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5138ba7ffa
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ca397cf119
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 89fbd4aaa9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 79b066cc9e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4137624387
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
There was a problem hiding this comment.
Copilot's findings
Files not reviewed (1)
- api/v1alpha1/zz_generated.deepcopy.go: Generated file
Comments suppressed due to low confidence (4)
workers/ai/main.go:1
firstNonEmptyis duplicated identically inworkers/common/event_recorder_http.go(line 230). Since theworkers/aipackage already importsworkers/common, consider removing this local copy and usingcommon.firstNonEmpty(after exporting it), or extracting it to a shared utility. The same applies toeventContent(line 1010) which mirrorsagentRuntimeEventContentinworkers/common/agent_runtime.go(line 818). Having two copies means bug fixes or behavioral changes must be applied in both places.
workers/ai/main_test.go:1- The
_ = reqassignment is unnecessary—Go does not require suppressing unused-variable warnings for function parameters. Ifreqis not used in the function body, rename it to_to match the first parameter's style. If it is intended for future use, simply remove the_ = reqline.
internal/store/sqlite/sqlite.go:1 - The
rowidfallback on line 638 (used whensession_seq = 0) is from a different numeric namespace than logical session sequences. If a database has existing events withsession_seq = 0and largerowidvalues, the backfill will seedlatest_seqwith thoserowidvalues, causing future session sequences to start at unexpectedly high numbers. Consider usingCOUNT(*)over the session's events or0as the fallback instead ofrowid, so the post-migration sequence allocator starts from a predictable baseline.
workers/ai/main_test.go:1 assertEventTypesPresentis functionally identical toassertRecordedEventTypesinworkers/common/agent_runtime_test.go(line 60). While test helpers in different_testpackages can't be shared directly, consider extracting this into a shared test helper package (e.g.,workers/common/testutil) to avoid maintaining two copies of the same assertion logic.
- Files reviewed: 78/79 changed files
- Comments generated: 1
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: eb419bc35f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
| return nil | ||
| } | ||
|
|
||
| func MapFrameToExecutionEvent(frame HarnessEventFrame, ctx EventMapContext) (*store.ExecutionEvent, error) { |
| func firstNonEmpty(values ...string) string { | ||
| for _, value := range values { | ||
| if strings.TrimSpace(value) != "" { | ||
| return value | ||
| } | ||
| } | ||
| return "" | ||
| } |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7009a8386e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| spec.Prompt = strings.TrimSpace(req.Prompt) | ||
| if spec.AI != nil { | ||
| spec.AI.Prompt = strings.TrimSpace(req.Prompt) | ||
| } |
There was a problem hiding this comment.
Pass fork context into the created task
When POST /fork is used with an afterSeq (including the CLI flow with orka task fork --after ... --prompt ...), this only replaces the task prompt with the caller text; the computed forkCtx is returned after the Task is already created and only summarized in annotations, and no worker path reads those fork annotations. The forked task therefore has neither the selected checkpoint events nor a workspace snapshot, so it cannot actually continue from the requested event sequence; include the bounded fork context in the created Task's worker-visible context before creation.
Useful? React with 👍 / 👎.
| content, err := json.Marshal(map[string]string{ | ||
| "approvalID": approvalID, | ||
| "decision": decision, | ||
| "reason": strings.TrimSpace(req.Reason), | ||
| "actor": actor, | ||
| }) |
There was a problem hiding this comment.
Keep approval IDs outside truncatable reason payloads
When an approval decision includes a large reason (large enough to push this JSON over MaxExecutionEventContentJSONBytes), the store sanitization replaces the whole content object with a truncated marker before persistence. For approval requests that only carry approvalID and no ToolCallID (which the API/tests allow), approvals.Derive can no longer associate the terminal decision with the pending approval, so the decision response can be empty and the approval remains pending. Bound the reason separately or store the decision identity in fields that survive content truncation.
Useful? React with 👍 / 👎.
| newName = generatedForkTaskName(sourceName) | ||
| } | ||
|
|
||
| spec := *source.Spec.DeepCopy() |
There was a problem hiding this comment.
Clear schedules when creating fork tasks
When the source task is a scheduled task, this full spec copy preserves spec.schedule (and related scheduling fields) on the fork. The controller checks task.Spec.Schedule in the pending phase before creating a Job, so a checkpoint fork of a scheduled source is moved back onto the cron schedule instead of running the requested fork once, and it may repeat later as a scheduled task. Clear scheduling fields for forked tasks unless the request explicitly asks for a scheduled fork.
Useful? React with 👍 / 👎.
| if err != nil { | ||
| logf.FromContext(ctx).Error( | ||
| err, | ||
| "failed to record task lifecycle execution event", |
There was a problem hiding this comment.
Requeue when terminal event recording fails
When the event store transiently rejects the terminal lifecycle append, this helper only logs the failure. completeTask gets one later retry via handleCompleted, but handleCompleted also swallows the append error and can return without requeueing, leaving a terminal Task with no TaskSucceeded/TaskFailed/TaskCancelled event. Since task SSE completion is driven by those terminal events rather than task status, followers can remain open indefinitely after the store recovers; surface the failure from the terminal path or requeue until the terminal event is persisted.
Useful? React with 👍 / 👎.
Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d274079395
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| - `ApprovalRequested`, | ||
| - `ApprovalApproved`, | ||
| - `ApprovalDeclined`, | ||
| - `ApprovalExpired`, | ||
| - `ApprovalCancelled`. |
There was a problem hiding this comment.
Don't document terminal approvals as worker events
This list tells worker authors they can submit terminal approval events, but SubmitExecutionEvent rejects ApprovalApproved, ApprovalDeclined, ApprovalExpired, and ApprovalCancelled as worker terminal events with 403; only ApprovalRequested is worker-emittable, while decisions go through the approval decision API. Following this reference will make worker integrations fail as soon as they try to report a decision or expiry.
Useful? React with 👍 / 👎.
| if ctx.Err() != nil { | ||
| fmt.Fprintf(cmd.ErrOrStderr(), "Resume with --after %d\n", lastSeq) //nolint:errcheck | ||
| } | ||
| return reader.Err() |
There was a problem hiding this comment.
Treat interrupted follows as a clean exit
When a user stops orka task follow or orka session follow with Ctrl-C/SIGTERM, ctx.Err() is set and the SSE scanner commonly reports context canceled; this branch prints the resume cursor but then returns reader.Err(), so Cobra still exits with an error for the normal stop path. The existing chat/log streaming paths suppress stream errors once the signal context is canceled, and this follow command should do the same after printing the resume hint.
Useful? React with 👍 / 👎.
| if strings.TrimSpace(event.TaskName) == "" { | ||
| event.TaskName = streamID | ||
| } |
There was a problem hiding this comment.
Force worker events to use the routed task name
When a worker includes a non-empty taskName that differs from the streamID in this internal route, this only fills the field when it is missing and never overwrites or rejects the spoofed value. The writer check proves the caller owns streamID, but session timelines and event DTOs display event.TaskName, so a buggy worker for task A can persist rows attributed to task B; derive TaskName from the route just like SessionName is derived from the Task.
Useful? React with 👍 / 👎.
| if after >= 0 { | ||
| bodyMap["afterSeq"] = after |
There was a problem hiding this comment.
Reject explicit negative fork checkpoints
When a user runs orka task fork --after -5, Cobra records the negative value but this sentinel check treats every negative after as if the flag were omitted, so the request body leaves out afterSeq and the server forks from the latest event instead of returning its non-negative afterSeq validation error. Distinguish the default sentinel from an explicitly changed flag, otherwise a mistyped checkpoint can create a fork from the wrong point in the timeline.
Useful? React with 👍 / 👎.
Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
| EnvOrkaControllerURL = "ORKA_CONTROLLER_URL" | ||
| EnvOrkaTaskNamespace = "ORKA_TASK_NAMESPACE" | ||
| EnvOrkaTaskName = "ORKA_TASK_NAME" | ||
| EnvOrkaSessionName = "ORKA_SESSION_NAME" |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1b4ceb4c74
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if err := submitResult(workDir, output); err != nil { | ||
| return err | ||
| } | ||
| recordGeneralResultSubmitted(eventRecorder, taskName, len(output)) |
There was a problem hiding this comment.
Emit artifact events from general workers
When a general worker writes files under /tmp/artifacts, submitResult calls UploadArtifacts() before this ResultSubmitted event is recorded, but this path never emits ArtifactUploadCompleted or ArtifactUploadFailed. As a result, successful uploads from general/container tasks are missing from task trace (which only populates artifacts from those event types), and if artifact upload fails after the result was submitted, the timeline shows only a worker failure with no result or artifact-upload event explaining what happened.
Useful? React with 👍 / 👎.
| encoded, err := json.Marshal(forkCtx) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| contextBlock := "Fork context through execution event checkpoint:\n" + string(encoded) |
There was a problem hiding this comment.
Bound fork context before embedding in Task specs
When forking a long task whose recent events include large contentText or JSON payloads, this marshals the whole bounded-by-count fork context directly into the new Task prompt. Since each event can still carry tens of KiB after redaction/truncation and up to 200 events are included, the generated Task object can become multi-MiB and be rejected by the Kubernetes API, making POST /fork fail for exactly the long traces this checkpoint path targets; apply a byte budget or summarize before writing the context into the spec.
Useful? React with 👍 / 👎.
| `CREATE INDEX IF NOT EXISTS idx_execution_events_session | ||
| ON execution_events(namespace, session_name, seq)`, |
There was a problem hiding this comment.
Index session streams by their session cursor
This index is keyed by the per-task seq, but the session APIs page and order by the derived session_seq/effective_session_seq cursor in ListSessionExecutionEvents. For long-lived sessions, every orka session follow poll with a high after cursor still has to scan the session's historical rows to compute/list later session events instead of seeking to the cursor, so idle followers become O(total session events); add an index on the session cursor (and type if needed) or use the maintained cursor table for latest-seq reads.
Useful? React with 👍 / 👎.
Summary
afterresume, heartbeats, reconnect safety, and terminal task completion frames.events,follow,trace,fork,approvals,approve, anddecline.Notable behavior
(namespace, streamType=task, streamID=<task>, seq); session timelines are derived read models with separate stable session cursors.stream_completeframe on terminal task events, while session SSE streams remain open because sessions can receive future task events.Validation
make lint-fixmake testgo test ./internal/events ./internal/store ./internal/store/sqlite -run ExecutionEvent -vgo test ./internal/api -run 'ExecutionEvent|SubmitExecutionEvent|Task.*Events|Session.*Events|Stream|Reconnect|Trace|Fork|Approval' -vgo test ./internal/controller -run 'Task.*Event|ExecutionEvent' -vgo test ./workers/common -run 'Event|RunAgent.*Event|AgentRuntime.*Event' -vgo test ./workers/ai -run Event -v$autoreview/.agents/skills/autoreview/scripts/autoreview --mode localcompleted cleanly on the stacked eventing slices.