feat(sessions): deliver durable Stop directly to the runner - #6503
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. 🗂️ Base branches to auto review (1)
Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository YAML (base), Organization UI (inherited) Review profile: CHILL Plan: Team Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository YAML (base), Organization UI (inherited) Review profile: CHILL Plan: Team Run ID: ⛔ Files ignored due to path filters (13)
📒 Files selected for processing (172)
🚧 Files skipped from review as they are similar to previous changes (58)
Included review availability: Your plan provides up to 8 included reviews per hour; 1 remains after this review. 📝 SummarySummary by CodeRabbit
WalkthroughThe change adds durable Stop commands, runner-side cancellation and warm parking, single-turn admission, cancellation records, commit-aware record redelivery, adaptive liveness polling, client execution targeting, persisted build-kit state, and release-gate validation. ChangesSession control and runner lifecycle
Estimated code review effort: 5 (Critical) | ~120 minutes Merge Risk: 🟠 High · up to Merging can cancel the wrong active turn, lose deferred transcript records or rejected-message state, and expose command control across insufficiently scoped runner boundaries. These issues should be fixed before merge. Sequence Diagram(s)sequenceDiagram
participant Client
participant SessionControlRouter
participant PostgreSQL
participant Runner
participant SandboxAgent
Client->>SessionControlRouter: POST /sessions/{session_id}/cancel
SessionControlRouter->>PostgreSQL: Store pending command
SessionControlRouter->>Runner: POST /cancel
Runner->>SandboxAgent: Send session/cancel
SandboxAgent-->>Runner: Confirm or timeout
Runner->>SessionControlRouter: Report outcome
SessionControlRouter->>PostgreSQL: Settle command
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 27.15% which is insufficient. The required threshold is 60.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 372 functions across 50 files. (117 skipped: 38 unsupported, 79 over the file limit.) ✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…on id PR #6503 already uses oss000000022 on the same parent. This file becomes oss000000025 so both branches can merge; whichever lands second re-points down_revision to the new head. Claude-Session: https://claude.ai/code/session_01GAqSs7fw6QRi2n1ZJ2tmAV
mmabrouk
left a comment
There was a problem hiding this comment.
AGENT-GENERATED review, low weight. A read guide and the points a human should check; not an approval.
Read in this order
api/oss/src/core/sessions/commands/service.py— admission, the three late-Stop guards, delivery, settlement.api/oss/databases/.../oss000000022_add_session_commands.py— the table, the partial unique index, the twosession_streamscolumns.api/oss/src/dbs/postgres/sessions/commands/dao.py— every transition is one guarded compare-and-set.api/oss/src/apis/fastapi/sessions/router.py+middlewares/auth.py— the public Stop route and the runner-token outcome route.web/oss/.../useAgentChatSession.ts— the Stop button, now on the durable endpoint.
Top findings
- One coherent change; the DAO compare-and-set tests run against real Postgres and are strong.
- The rollback flag
AGENTA_SESSIONS_DURABLE_STOPis NOT in this PR — the frontend switches to the new endpoint unconditionally and the route is always mounted. The gate arrives in #6501, so #6503 is safe only landed together with it. Confirm against the design's rollback contract. - Auth exemption
/sessions/control/collides with a session literally namedcontrol(see inline). - Migration is additive with a downgrade and chains cleanly on
oss000000021; the outcome route fails closed with a constant-time compare.
📘 Docs preview
This comment updates in place on every push. |
mmabrouk
left a comment
There was a problem hiding this comment.
Review summary
- Risky:
services/runner/src/server.ts:753- parked-session ownership ignores the project half of the pool key, so the same session ID in another project can make this runner accept a command it does not hold. - Risky:
api/oss/src/core/sessions/commands/service.py:235- an idempotency-key collision can return an old terminal command while the response names the newly running turn. - Missing tests: cross-project parked sessions with identical session IDs; reuse of one idempotency key after a later turn starts.
Reviewed at e4cbd64ac91a81f172c999667b9d6a3131b4467e. Advisory COMMENT review only.
Railway Preview Environment
Updated at 2026-09-04T19:34:34.737Z |
There was a problem hiding this comment.
Actionable comments posted: 17
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
services/runner/patches/sandbox-agent@0.4.2.patch (1)
207-207: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winRestore child termination on Windows.
When
process.platform === "win32", usechild.kill(signal)instead ofprocess.kill(-child.pid, signal). The detached child can remain running when the process-group call throws and the emptycatchdiscards the error.
🧹 Nitpick comments (3)
docs/design/session-control-and-live-events/slice-durable-cancel.md (1)
122-124: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse the Fern-generated client for Stop.
Add the cancellation route to Fern, regenerate the sessions client, and replace the raw
axios.postincancelSessionExecution. Preserve the request fields and409handling. Update this paragraph to state that the Fern client is used.api/oss/tests/pytest/unit/sessions/test_session_cancel_admission.py (1)
879-880: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAssert that the late claim is attempted.
_ReportsBeforeTheClaimCommits.deliversettles the row before returningaccepted, after whichSessionCommandsService._delivercalls_dao.claim_for_delivery. Line 880 repeats line 877, so the test can pass if that late claim call is removed. Assert thatdao.claimscontains this command and"runner-1"; keep the existing outcome assertion.api/oss/tests/pytest/unit/sessions/test_session_cancel_feature_flag.py (1)
94-94: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winPin the durable response body, not only the status code.
Assert the JSON returned by
SessionCancelResponsewith the command ID andpendingstate, plus execution ID andstoppingstate. This test should detect future wire-contract changes.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Organization UI (inherited)
Review profile: CHILL
Plan: Team
Run ID: 9f7a6b40-593b-4f4a-8d51-dc8df0bfeef9
⛔ Files ignored due to path filters (1)
services/runner/pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (81)
api/entrypoints/routers.pyapi/oss/databases/postgres/migrations/core_oss/versions/oss000000022_add_session_commands.pyapi/oss/src/apis/fastapi/sessions/models.pyapi/oss/src/apis/fastapi/sessions/router.pyapi/oss/src/core/sessions/commands/__init__.pyapi/oss/src/core/sessions/commands/dtos.pyapi/oss/src/core/sessions/commands/interfaces.pyapi/oss/src/core/sessions/commands/service.pyapi/oss/src/core/sessions/commands/types.pyapi/oss/src/core/sessions/interactions/interfaces.pyapi/oss/src/core/sessions/interactions/service.pyapi/oss/src/core/sessions/streams/dtos.pyapi/oss/src/core/sessions/streams/runner_client.pyapi/oss/src/core/sessions/streams/service.pyapi/oss/src/core/sessions/streams/types.pyapi/oss/src/dbs/http/__init__.pyapi/oss/src/dbs/http/sessions/__init__.pyapi/oss/src/dbs/http/sessions/control_delivery_direct.pyapi/oss/src/dbs/postgres/sessions/commands/__init__.pyapi/oss/src/dbs/postgres/sessions/commands/dao.pyapi/oss/src/dbs/postgres/sessions/commands/dbas.pyapi/oss/src/dbs/postgres/sessions/commands/dbes.pyapi/oss/src/dbs/postgres/sessions/commands/mappings.pyapi/oss/src/dbs/postgres/sessions/interactions/dao.pyapi/oss/src/dbs/postgres/sessions/streams/dbes.pyapi/oss/src/dbs/postgres/sessions/streams/mappings.pyapi/oss/src/middlewares/auth.pyapi/oss/src/utils/env.pyapi/oss/tests/pytest/unit/middlewares/test_auth_public_endpoints.pyapi/oss/tests/pytest/unit/sessions/test_command_matrix_inputs_data.pyapi/oss/tests/pytest/unit/sessions/test_interaction_cancel_records.pyapi/oss/tests/pytest/unit/sessions/test_session_cancel_admission.pyapi/oss/tests/pytest/unit/sessions/test_session_cancel_feature_flag.pyapi/oss/tests/pytest/unit/sessions/test_session_commands_dao.pyapi/oss/tests/pytest/unit/sessions/test_watch_interactions_publish.pyapi/oss/tests/pytest/unit/sessions/test_wp5_dao_fanout.pydocs/design/session-control-and-live-events/README.mddocs/design/session-control-and-live-events/api-design.mddocs/design/session-control-and-live-events/context.mddocs/design/session-control-and-live-events/decisions.mddocs/design/session-control-and-live-events/plan.mddocs/design/session-control-and-live-events/records-invariants.mddocs/design/session-control-and-live-events/requirements.mddocs/design/session-control-and-live-events/research.mddocs/design/session-control-and-live-events/rfc.mddocs/design/session-control-and-live-events/slice-durable-cancel.mddocs/design/session-control-and-live-events/spike-a-sandbox-cancel.mddocs/design/session-control-and-live-events/spike-b-durable-commands-design.mddocs/design/session-control-and-live-events/status.mddocs/design/session-control-and-live-events/tonight-handoff.mdhosting/docker-compose/ee/env.ee.dev.examplehosting/docker-compose/oss/env.oss.dev.exampleservices/runner/patches/sandbox-agent@0.4.2.patchservices/runner/src/engines/sandbox_agent/cancel-turn.tsservices/runner/src/engines/sandbox_agent/engine.tsservices/runner/src/engines/sandbox_agent/run-turn.tsservices/runner/src/engines/sandbox_agent/session-identity.tsservices/runner/src/engines/sandbox_agent/teardown.tsservices/runner/src/lifecycle/session-coordinator.tsservices/runner/src/protocol.tsservices/runner/src/server.tsservices/runner/src/sessions/applied-commands.tsservices/runner/src/sessions/control-channel.tsservices/runner/src/sessions/execution-registry.tsservices/runner/src/sessions/stop-signal.tsservices/runner/src/tracing/otel.tsservices/runner/tests/unit/control-command-apply.test.tsservices/runner/tests/unit/harness-cancel-park.test.tsservices/runner/tests/unit/session-pool.test.tsservices/runner/tests/unit/teardown.test.tsweb/mobile/src/features/sessions/useActionableInteractions.tsweb/mobile/src/features/sessions/useLivenessPoll.tsweb/oss/src/components/AgentChatSlice/hooks/useAgentChatSession.tsweb/oss/src/components/AgentChatSlice/state/liveness.tsweb/packages/agenta-chat/tests/unit/assets/transcriptToMessages.test.tsweb/packages/agenta-entities/src/session/api/api.tsweb/packages/agenta-entities/src/session/core/liveness.tsweb/packages/agenta-entities/src/session/index.tsweb/packages/agenta-entities/tests/unit/session-liveness.test.tsweb/packages/agenta-navigation/src/dynamic/sessionsSource.tsweb/packages/agenta-navigation/tests/unit/sidebarChildren.test.ts
Included review availability: Your plan provides up to 8 included reviews per hour; 5 remain after this review.
A user Stop reached the runner only as the absence of a Redis lock, noticed on the next heartbeat up to 30 seconds later. Nothing recorded that a Stop had been asked for, so a Stop against an unreachable runner was simply lost and no execution ever reached a terminal outcome anyone could read. Add session_commands: one row per durable request to change an execution. Two columns that are never merged carry the two questions a caller actually asks. state says where the COMMAND is (pending, claimed, applied, obsolete). outcome says what happened to the EXECUTION (stopped, not_running, superseded_by_newer_turn, failed, lost). A client drawing a Stop button reads the execution; a client retrying safely reads the command id. Every transition is one UPDATE ... WHERE <expected state> RETURNING *, decided by scalar_one_or_none, the same compare-and-set transition_interaction already uses. That is what stops two API replicas both winning a claim or both writing a terminal outcome. Idempotency has two layers: the caller's Idempotency-Key on (project_id, session_id, idempotency_key), and a collapse onto any open command for the same target execution, which is what makes two Stops in a row correct without asking the browser to send a key. session_streams gains two columns. stopping_turn_id names the execution an accepted Stop is waiting on, written in the same transaction as the command insert. turn_started_at records when the row's current turn_id started, because the stale-Stop guard has to compare a Stop's arrival time with the running execution's start time and there was nowhere to read that: updated_at is the heartbeat timestamp and moves every 30 seconds, runner-minted turn ids are uuid4 and carry no time, the Redis lock value is a bare turn id a Lua compare reads whole, and the session_turns append is fire-and-forget so a running turn may have no row. It is stamped only when the id actually changes, so the repeated heartbeats that restamp the same id never move it. Both columns backfill to NULL. A row written before this migration yields no comparison and the guard does not fire, deliberately: a guard that refused every Stop it could not verify would break the common case to protect a rare one. Claude-Session: https://claude.ai/code/session_01GAqSs7fw6QRi2n1ZJ2tmAV
Keep the execution-versus-warm invariants at the affected call sites while moving each changed code comment back to one concise line. Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk
Align the API design, decisions, status, and handoff with the implemented direct control adapter. Mark runner-initiated long polling as deferred behind the same delivery port. Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk
2251b4d to
cd21450
Compare
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
There was a problem hiding this comment.
Actionable comments posted: 15
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Organization UI (inherited)
Review profile: CHILL
Plan: Team
Run ID: 7d9ba61a-653e-4a63-b809-75d469ee8856
⛔ Files ignored due to path filters (13)
api/uv.lockis excluded by!**/*.lockclients/python/uv.lockis excluded by!**/*.locksdks/python/uv.lockis excluded by!**/*.lockservices/runner/pnpm-lock.yamlis excluded by!**/pnpm-lock.yamlservices/uv.lockis excluded by!**/*.lockweb/packages/agenta-api-client/src/generated/api/resources/sessions/client/Client.tsis excluded by!**/generated/**web/packages/agenta-api-client/src/generated/api/resources/sessions/client/requests/CancelSessionExecutionRequest.tsis excluded by!**/generated/**web/packages/agenta-api-client/src/generated/api/resources/sessions/client/requests/SessionStreamCommandRequest.tsis excluded by!**/generated/**web/packages/agenta-api-client/src/generated/api/resources/sessions/client/requests/index.tsis excluded by!**/generated/**web/packages/agenta-api-client/src/generated/api/types/SessionCancelRequest.tsis excluded by!**/generated/**web/packages/agenta-api-client/src/generated/api/types/SessionStream.tsis excluded by!**/generated/**web/packages/agenta-api-client/src/generated/api/types/SessionStreamCommandResponse.tsis excluded by!**/generated/**web/packages/agenta-api-client/src/generated/api/types/index.tsis excluded by!**/generated/**
📒 Files selected for processing (172)
.agents/skills/agent-release-gate/SKILL.md.agents/skills/agent-release-gate/resources/path_triggers.py.agents/skills/agent-release-gate/resources/qa_product.py.agents/skills/agent-release-gate/resources/session_control.py.agents/skills/agent-release-gate/resources/test_qa_product_concurrency.py.agents/skills/agent-release-gate/resources/test_session_control.py.all-contributorsrcREADME.mdapi/ee/src/dbs/postgres/sessions/records/dao.pyapi/entrypoints/routers.pyapi/entrypoints/worker_streams.pyapi/oss/databases/postgres/migrations/core_oss/versions/oss000000022_add_session_commands.pyapi/oss/src/apis/fastapi/evaluations/router.pyapi/oss/src/apis/fastapi/sessions/models.pyapi/oss/src/apis/fastapi/sessions/router.pyapi/oss/src/core/sessions/commands/__init__.pyapi/oss/src/core/sessions/commands/dtos.pyapi/oss/src/core/sessions/commands/interfaces.pyapi/oss/src/core/sessions/commands/service.pyapi/oss/src/core/sessions/commands/types.pyapi/oss/src/core/sessions/interactions/interfaces.pyapi/oss/src/core/sessions/interactions/service.pyapi/oss/src/core/sessions/streams/dtos.pyapi/oss/src/core/sessions/streams/runner_client.pyapi/oss/src/core/sessions/streams/service.pyapi/oss/src/core/sessions/streams/types.pyapi/oss/src/dbs/http/__init__.pyapi/oss/src/dbs/http/sessions/__init__.pyapi/oss/src/dbs/http/sessions/control_delivery_direct.pyapi/oss/src/dbs/postgres/sessions/commands/__init__.pyapi/oss/src/dbs/postgres/sessions/commands/dao.pyapi/oss/src/dbs/postgres/sessions/commands/dbas.pyapi/oss/src/dbs/postgres/sessions/commands/dbes.pyapi/oss/src/dbs/postgres/sessions/commands/mappings.pyapi/oss/src/dbs/postgres/sessions/interactions/dao.pyapi/oss/src/dbs/postgres/sessions/streams/dbes.pyapi/oss/src/dbs/postgres/sessions/streams/mappings.pyapi/oss/src/middlewares/auth.pyapi/oss/src/routers/user_profile.pyapi/oss/src/services/db_manager.pyapi/oss/src/tasks/asyncio/sessions/records_worker.pyapi/oss/src/tasks/asyncio/shared/consumer.pyapi/oss/src/tasks/taskiq/triggers/worker.pyapi/oss/src/utils/caching.pyapi/oss/src/utils/crypting.pyapi/oss/src/utils/env.pyapi/oss/src/utils/exceptions.pyapi/oss/src/utils/helpers.pyapi/oss/tests/pytest/unit/middlewares/test_auth_public_endpoints.pyapi/oss/tests/pytest/unit/sessions/test_command_matrix_inputs_data.pyapi/oss/tests/pytest/unit/sessions/test_heartbeat_is_current_turn.pyapi/oss/tests/pytest/unit/sessions/test_heartbeat_release_owner.pyapi/oss/tests/pytest/unit/sessions/test_interaction_cancel_records.pyapi/oss/tests/pytest/unit/sessions/test_records_worker_durability.pyapi/oss/tests/pytest/unit/sessions/test_runner_client_kill.pyapi/oss/tests/pytest/unit/sessions/test_session_cancel_admission.pyapi/oss/tests/pytest/unit/sessions/test_session_cancel_feature_flag.pyapi/oss/tests/pytest/unit/sessions/test_session_commands_dao.pyapi/oss/tests/pytest/unit/sessions/test_watch_interactions_publish.pyapi/oss/tests/pytest/unit/sessions/test_watch_publish.pyapi/oss/tests/pytest/unit/sessions/test_wp5_dao_fanout.pyapi/oss/tests/pytest/unit/triggers/test_triggers_worker_lifecycle.pyapi/oss/tests/pytest/unit/utils/test_caching.pyapi/pyproject.tomlclients/python/pyproject.tomldocs/design/agent-workflows/documentation/adapters/agenta.mddocs/design/agent-workflows/documentation/tools.mddocs/design/agent-workflows/interfaces/README.mddocs/design/agent-workflows/interfaces/in-service/harness-adapters.mddocs/design/agent-workflows/interfaces/public-edge/agent-config-schema.mddocs/design/agent-workflows/projects/default-agent-builtins/addendum-always-active.mddocs/design/session-control-and-live-events/README.mddocs/design/session-control-and-live-events/api-design.mddocs/design/session-control-and-live-events/context.mddocs/design/session-control-and-live-events/decisions.mddocs/design/session-control-and-live-events/plan.mddocs/design/session-control-and-live-events/records-invariants.mddocs/design/session-control-and-live-events/requirements.mddocs/design/session-control-and-live-events/research.mddocs/design/session-control-and-live-events/rfc.mddocs/design/session-control-and-live-events/slice-admission.mddocs/design/session-control-and-live-events/slice-durable-cancel.mddocs/design/session-control-and-live-events/slice-records-ack.mddocs/design/session-control-and-live-events/spike-a-sandbox-cancel.mddocs/design/session-control-and-live-events/spike-b-durable-commands-design.mddocs/design/session-control-and-live-events/status.mddocs/design/session-control-and-live-events/tonight-handoff.mdhosting/docker-compose/ee/env.ee.dev.examplehosting/docker-compose/oss/env.oss.dev.examplehosting/kubernetes/helm/Chart.yamlsdks/python/agenta/sdk/agents/adapters/vercel/stream.pysdks/python/oss/tests/pytest/unit/agents/adapters/test_vercel_stream_conformance.pysdks/python/pyproject.tomlservices/pyproject.tomlservices/runner/patches/sandbox-agent@0.4.2.patchservices/runner/src/engines/sandbox_agent/agent-mount.tsservices/runner/src/engines/sandbox_agent/cancel-turn.tsservices/runner/src/engines/sandbox_agent/credential-preflight.tsservices/runner/src/engines/sandbox_agent/engine.tsservices/runner/src/engines/sandbox_agent/environment-setup.tsservices/runner/src/engines/sandbox_agent/environment.tsservices/runner/src/engines/sandbox_agent/errors.tsservices/runner/src/engines/sandbox_agent/mount.tsservices/runner/src/engines/sandbox_agent/provider.tsservices/runner/src/engines/sandbox_agent/reap-exec.tsservices/runner/src/engines/sandbox_agent/reconstruct-history.tsservices/runner/src/engines/sandbox_agent/run-turn.tsservices/runner/src/engines/sandbox_agent/runtime-contracts.tsservices/runner/src/engines/sandbox_agent/session-identity.tsservices/runner/src/engines/sandbox_agent/teardown.tsservices/runner/src/environment/abortable-sandbox-provider.tsservices/runner/src/environment/acquire-abort.tsservices/runner/src/environment/harness-session-lifecycle.tsservices/runner/src/environment/mount-lifecycle.tsservices/runner/src/lifecycle/session-coordinator.tsservices/runner/src/protocol.tsservices/runner/src/server.tsservices/runner/src/sessions/admission.tsservices/runner/src/sessions/alive.tsservices/runner/src/sessions/applied-commands.tsservices/runner/src/sessions/control-channel.tsservices/runner/src/sessions/execution-registry.tsservices/runner/src/sessions/stop-signal.tsservices/runner/src/tracing/otel.tsservices/runner/tests/unit/acquire-abort.test.tsservices/runner/tests/unit/cancel-continuity.test.tsservices/runner/tests/unit/continuation.test.tsservices/runner/tests/unit/control-command-apply.test.tsservices/runner/tests/unit/credential-preflight.test.tsservices/runner/tests/unit/environment-units.test.tsservices/runner/tests/unit/harness-cancel-park.test.tsservices/runner/tests/unit/reap-exec.test.tsservices/runner/tests/unit/sandbox-agent-mount.test.tsservices/runner/tests/unit/sandbox-agent-orchestration.test.tsservices/runner/tests/unit/sandbox-lifecycle.test.tsservices/runner/tests/unit/server.test.tsservices/runner/tests/unit/session-admission.test.tsservices/runner/tests/unit/session-alive-interrupt.test.tsservices/runner/tests/unit/session-keepalive-dispatch.test.tsservices/runner/tests/unit/session-ownership-release.test.tsservices/runner/tests/unit/session-pool.test.tsservices/runner/tests/unit/session-reconstruct-history.test.tsservices/runner/tests/unit/session-steer-mount-loss.test.tsservices/runner/tests/unit/teardown.test.tsweb/ee/package.jsonweb/mobile/package.jsonweb/mobile/src/features/chat/Composer.tsxweb/mobile/src/features/chat/LiveConversation.tsxweb/mobile/src/features/sessions/useActionableInteractions.tsweb/mobile/src/features/sessions/useLivenessPoll.tsweb/oss/package.jsonweb/oss/src/components/AgentChatSlice/AgentConversation.tsxweb/oss/src/components/AgentChatSlice/components/AgentMessage.tsxweb/oss/src/components/AgentChatSlice/hooks/useAgentChatSession.tsweb/oss/src/components/AgentChatSlice/state/liveness.tsweb/package.jsonweb/packages/agenta-api-client/package.jsonweb/packages/agenta-chat/src/hooks/useAgentChatQueue.tsweb/packages/agenta-chat/src/model/error.tsweb/packages/agenta-chat/tests/unit/assets/transcriptToMessages.test.tsweb/packages/agenta-chat/tests/unit/hooks/useAgentChatQueue.test.tsweb/packages/agenta-chat/tests/unit/model/error.test.tsweb/packages/agenta-entities/src/session/api/api.tsweb/packages/agenta-entities/src/session/core/liveness.tsweb/packages/agenta-entities/src/session/core/schema.tsweb/packages/agenta-entities/src/session/index.tsweb/packages/agenta-entities/src/workflow/state/store.tsweb/packages/agenta-entities/tests/unit/agent-build-kit-ui-state-atom.test.tsweb/packages/agenta-entities/tests/unit/session-cancel-api.test.tsweb/packages/agenta-entities/tests/unit/session-liveness.test.tsweb/packages/agenta-navigation/src/dynamic/sessionsSource.tsweb/packages/agenta-navigation/tests/unit/sidebarChildren.test.ts
🚧 Files skipped from review as they are similar to previous changes (58)
- docs/design/session-control-and-live-events/README.md
- web/packages/agenta-entities/src/session/core/schema.ts
- api/oss/src/middlewares/auth.py
- api/oss/tests/pytest/unit/middlewares/test_auth_public_endpoints.py
- services/runner/patches/sandbox-agent@0.4.2.patch
- hosting/docker-compose/oss/env.oss.dev.example
- web/packages/agenta-entities/src/session/api/api.ts
- web/oss/src/components/AgentChatSlice/state/liveness.ts
- api/oss/src/dbs/postgres/sessions/commands/dbas.py
- web/packages/agenta-entities/tests/unit/session-liveness.test.ts
- hosting/docker-compose/ee/env.ee.dev.example
- api/oss/src/dbs/http/sessions/control_delivery_direct.py
- api/oss/src/core/sessions/streams/runner_client.py
- web/packages/agenta-entities/src/session/index.ts
- api/oss/src/core/sessions/commands/types.py
- api/oss/tests/pytest/unit/sessions/test_command_matrix_inputs_data.py
- web/mobile/src/features/sessions/useLivenessPoll.ts
- api/oss/src/core/sessions/interactions/interfaces.py
- services/runner/src/sessions/stop-signal.ts
- api/oss/tests/pytest/unit/sessions/test_watch_interactions_publish.py
- services/runner/src/engines/sandbox_agent/engine.ts
- web/oss/src/components/AgentChatSlice/hooks/useAgentChatSession.ts
- api/oss/src/core/sessions/interactions/service.py
- api/oss/src/dbs/postgres/sessions/commands/mappings.py
- api/oss/src/dbs/postgres/sessions/interactions/dao.py
- web/packages/agenta-entities/tests/unit/session-cancel-api.test.ts
- services/runner/src/tracing/otel.ts
- api/oss/src/core/sessions/streams/dtos.py
- services/runner/tests/unit/session-pool.test.ts
- api/oss/src/apis/fastapi/sessions/models.py
- services/runner/src/sessions/execution-registry.ts
- api/oss/tests/pytest/unit/sessions/test_wp5_dao_fanout.py
- api/oss/src/dbs/postgres/sessions/streams/dbes.py
- services/runner/src/sessions/control-channel.ts
- api/oss/tests/pytest/unit/sessions/test_interaction_cancel_records.py
- services/runner/src/server.ts
- web/packages/agenta-entities/src/session/core/liveness.ts
- services/runner/tests/unit/teardown.test.ts
- services/runner/src/sessions/applied-commands.ts
- api/oss/src/core/sessions/commands/interfaces.py
- services/runner/tests/unit/control-command-apply.test.ts
- api/oss/src/core/sessions/commands/dtos.py
- api/oss/src/dbs/postgres/sessions/streams/mappings.py
- api/oss/src/dbs/postgres/sessions/commands/dbes.py
- web/packages/agenta-navigation/src/dynamic/sessionsSource.ts
- api/oss/src/core/sessions/commands/service.py
- api/oss/tests/pytest/unit/sessions/test_session_commands_dao.py
- web/packages/agenta-navigation/tests/unit/sidebarChildren.test.ts
- services/runner/tests/unit/harness-cancel-park.test.ts
- services/runner/src/engines/sandbox_agent/teardown.ts
- api/oss/src/core/sessions/streams/types.py
- api/oss/databases/postgres/migrations/core_oss/versions/oss000000022_add_session_commands.py
- api/oss/src/dbs/postgres/sessions/commands/dao.py
- api/oss/tests/pytest/unit/sessions/test_session_cancel_admission.py
- web/packages/agenta-chat/tests/unit/assets/transcriptToMessages.test.ts
- web/mobile/src/features/sessions/useActionableInteractions.ts
- services/runner/src/engines/sandbox_agent/session-identity.ts
- api/entrypoints/routers.py
Included review availability: Your plan provides up to 8 included reviews per hour; 1 remains after this review.
There was a problem hiding this comment.
Caution
Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.
Actionable comments posted: 15
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Organization UI (inherited)
Review profile: CHILL
Plan: Team
Run ID: 7d9ba61a-653e-4a63-b809-75d469ee8856
⛔ Files ignored due to path filters (13)
api/uv.lockis excluded by!**/*.lockclients/python/uv.lockis excluded by!**/*.locksdks/python/uv.lockis excluded by!**/*.lockservices/runner/pnpm-lock.yamlis excluded by!**/pnpm-lock.yamlservices/uv.lockis excluded by!**/*.lockweb/packages/agenta-api-client/src/generated/api/resources/sessions/client/Client.tsis excluded by!**/generated/**web/packages/agenta-api-client/src/generated/api/resources/sessions/client/requests/CancelSessionExecutionRequest.tsis excluded by!**/generated/**web/packages/agenta-api-client/src/generated/api/resources/sessions/client/requests/SessionStreamCommandRequest.tsis excluded by!**/generated/**web/packages/agenta-api-client/src/generated/api/resources/sessions/client/requests/index.tsis excluded by!**/generated/**web/packages/agenta-api-client/src/generated/api/types/SessionCancelRequest.tsis excluded by!**/generated/**web/packages/agenta-api-client/src/generated/api/types/SessionStream.tsis excluded by!**/generated/**web/packages/agenta-api-client/src/generated/api/types/SessionStreamCommandResponse.tsis excluded by!**/generated/**web/packages/agenta-api-client/src/generated/api/types/index.tsis excluded by!**/generated/**
📒 Files selected for processing (172)
.agents/skills/agent-release-gate/SKILL.md.agents/skills/agent-release-gate/resources/path_triggers.py.agents/skills/agent-release-gate/resources/qa_product.py.agents/skills/agent-release-gate/resources/session_control.py.agents/skills/agent-release-gate/resources/test_qa_product_concurrency.py.agents/skills/agent-release-gate/resources/test_session_control.py.all-contributorsrcREADME.mdapi/ee/src/dbs/postgres/sessions/records/dao.pyapi/entrypoints/routers.pyapi/entrypoints/worker_streams.pyapi/oss/databases/postgres/migrations/core_oss/versions/oss000000022_add_session_commands.pyapi/oss/src/apis/fastapi/evaluations/router.pyapi/oss/src/apis/fastapi/sessions/models.pyapi/oss/src/apis/fastapi/sessions/router.pyapi/oss/src/core/sessions/commands/__init__.pyapi/oss/src/core/sessions/commands/dtos.pyapi/oss/src/core/sessions/commands/interfaces.pyapi/oss/src/core/sessions/commands/service.pyapi/oss/src/core/sessions/commands/types.pyapi/oss/src/core/sessions/interactions/interfaces.pyapi/oss/src/core/sessions/interactions/service.pyapi/oss/src/core/sessions/streams/dtos.pyapi/oss/src/core/sessions/streams/runner_client.pyapi/oss/src/core/sessions/streams/service.pyapi/oss/src/core/sessions/streams/types.pyapi/oss/src/dbs/http/__init__.pyapi/oss/src/dbs/http/sessions/__init__.pyapi/oss/src/dbs/http/sessions/control_delivery_direct.pyapi/oss/src/dbs/postgres/sessions/commands/__init__.pyapi/oss/src/dbs/postgres/sessions/commands/dao.pyapi/oss/src/dbs/postgres/sessions/commands/dbas.pyapi/oss/src/dbs/postgres/sessions/commands/dbes.pyapi/oss/src/dbs/postgres/sessions/commands/mappings.pyapi/oss/src/dbs/postgres/sessions/interactions/dao.pyapi/oss/src/dbs/postgres/sessions/streams/dbes.pyapi/oss/src/dbs/postgres/sessions/streams/mappings.pyapi/oss/src/middlewares/auth.pyapi/oss/src/routers/user_profile.pyapi/oss/src/services/db_manager.pyapi/oss/src/tasks/asyncio/sessions/records_worker.pyapi/oss/src/tasks/asyncio/shared/consumer.pyapi/oss/src/tasks/taskiq/triggers/worker.pyapi/oss/src/utils/caching.pyapi/oss/src/utils/crypting.pyapi/oss/src/utils/env.pyapi/oss/src/utils/exceptions.pyapi/oss/src/utils/helpers.pyapi/oss/tests/pytest/unit/middlewares/test_auth_public_endpoints.pyapi/oss/tests/pytest/unit/sessions/test_command_matrix_inputs_data.pyapi/oss/tests/pytest/unit/sessions/test_heartbeat_is_current_turn.pyapi/oss/tests/pytest/unit/sessions/test_heartbeat_release_owner.pyapi/oss/tests/pytest/unit/sessions/test_interaction_cancel_records.pyapi/oss/tests/pytest/unit/sessions/test_records_worker_durability.pyapi/oss/tests/pytest/unit/sessions/test_runner_client_kill.pyapi/oss/tests/pytest/unit/sessions/test_session_cancel_admission.pyapi/oss/tests/pytest/unit/sessions/test_session_cancel_feature_flag.pyapi/oss/tests/pytest/unit/sessions/test_session_commands_dao.pyapi/oss/tests/pytest/unit/sessions/test_watch_interactions_publish.pyapi/oss/tests/pytest/unit/sessions/test_watch_publish.pyapi/oss/tests/pytest/unit/sessions/test_wp5_dao_fanout.pyapi/oss/tests/pytest/unit/triggers/test_triggers_worker_lifecycle.pyapi/oss/tests/pytest/unit/utils/test_caching.pyapi/pyproject.tomlclients/python/pyproject.tomldocs/design/agent-workflows/documentation/adapters/agenta.mddocs/design/agent-workflows/documentation/tools.mddocs/design/agent-workflows/interfaces/README.mddocs/design/agent-workflows/interfaces/in-service/harness-adapters.mddocs/design/agent-workflows/interfaces/public-edge/agent-config-schema.mddocs/design/agent-workflows/projects/default-agent-builtins/addendum-always-active.mddocs/design/session-control-and-live-events/README.mddocs/design/session-control-and-live-events/api-design.mddocs/design/session-control-and-live-events/context.mddocs/design/session-control-and-live-events/decisions.mddocs/design/session-control-and-live-events/plan.mddocs/design/session-control-and-live-events/records-invariants.mddocs/design/session-control-and-live-events/requirements.mddocs/design/session-control-and-live-events/research.mddocs/design/session-control-and-live-events/rfc.mddocs/design/session-control-and-live-events/slice-admission.mddocs/design/session-control-and-live-events/slice-durable-cancel.mddocs/design/session-control-and-live-events/slice-records-ack.mddocs/design/session-control-and-live-events/spike-a-sandbox-cancel.mddocs/design/session-control-and-live-events/spike-b-durable-commands-design.mddocs/design/session-control-and-live-events/status.mddocs/design/session-control-and-live-events/tonight-handoff.mdhosting/docker-compose/ee/env.ee.dev.examplehosting/docker-compose/oss/env.oss.dev.examplehosting/kubernetes/helm/Chart.yamlsdks/python/agenta/sdk/agents/adapters/vercel/stream.pysdks/python/oss/tests/pytest/unit/agents/adapters/test_vercel_stream_conformance.pysdks/python/pyproject.tomlservices/pyproject.tomlservices/runner/patches/sandbox-agent@0.4.2.patchservices/runner/src/engines/sandbox_agent/agent-mount.tsservices/runner/src/engines/sandbox_agent/cancel-turn.tsservices/runner/src/engines/sandbox_agent/credential-preflight.tsservices/runner/src/engines/sandbox_agent/engine.tsservices/runner/src/engines/sandbox_agent/environment-setup.tsservices/runner/src/engines/sandbox_agent/environment.tsservices/runner/src/engines/sandbox_agent/errors.tsservices/runner/src/engines/sandbox_agent/mount.tsservices/runner/src/engines/sandbox_agent/provider.tsservices/runner/src/engines/sandbox_agent/reap-exec.tsservices/runner/src/engines/sandbox_agent/reconstruct-history.tsservices/runner/src/engines/sandbox_agent/run-turn.tsservices/runner/src/engines/sandbox_agent/runtime-contracts.tsservices/runner/src/engines/sandbox_agent/session-identity.tsservices/runner/src/engines/sandbox_agent/teardown.tsservices/runner/src/environment/abortable-sandbox-provider.tsservices/runner/src/environment/acquire-abort.tsservices/runner/src/environment/harness-session-lifecycle.tsservices/runner/src/environment/mount-lifecycle.tsservices/runner/src/lifecycle/session-coordinator.tsservices/runner/src/protocol.tsservices/runner/src/server.tsservices/runner/src/sessions/admission.tsservices/runner/src/sessions/alive.tsservices/runner/src/sessions/applied-commands.tsservices/runner/src/sessions/control-channel.tsservices/runner/src/sessions/execution-registry.tsservices/runner/src/sessions/stop-signal.tsservices/runner/src/tracing/otel.tsservices/runner/tests/unit/acquire-abort.test.tsservices/runner/tests/unit/cancel-continuity.test.tsservices/runner/tests/unit/continuation.test.tsservices/runner/tests/unit/control-command-apply.test.tsservices/runner/tests/unit/credential-preflight.test.tsservices/runner/tests/unit/environment-units.test.tsservices/runner/tests/unit/harness-cancel-park.test.tsservices/runner/tests/unit/reap-exec.test.tsservices/runner/tests/unit/sandbox-agent-mount.test.tsservices/runner/tests/unit/sandbox-agent-orchestration.test.tsservices/runner/tests/unit/sandbox-lifecycle.test.tsservices/runner/tests/unit/server.test.tsservices/runner/tests/unit/session-admission.test.tsservices/runner/tests/unit/session-alive-interrupt.test.tsservices/runner/tests/unit/session-keepalive-dispatch.test.tsservices/runner/tests/unit/session-ownership-release.test.tsservices/runner/tests/unit/session-pool.test.tsservices/runner/tests/unit/session-reconstruct-history.test.tsservices/runner/tests/unit/session-steer-mount-loss.test.tsservices/runner/tests/unit/teardown.test.tsweb/ee/package.jsonweb/mobile/package.jsonweb/mobile/src/features/chat/Composer.tsxweb/mobile/src/features/chat/LiveConversation.tsxweb/mobile/src/features/sessions/useActionableInteractions.tsweb/mobile/src/features/sessions/useLivenessPoll.tsweb/oss/package.jsonweb/oss/src/components/AgentChatSlice/AgentConversation.tsxweb/oss/src/components/AgentChatSlice/components/AgentMessage.tsxweb/oss/src/components/AgentChatSlice/hooks/useAgentChatSession.tsweb/oss/src/components/AgentChatSlice/state/liveness.tsweb/package.jsonweb/packages/agenta-api-client/package.jsonweb/packages/agenta-chat/src/hooks/useAgentChatQueue.tsweb/packages/agenta-chat/src/model/error.tsweb/packages/agenta-chat/tests/unit/assets/transcriptToMessages.test.tsweb/packages/agenta-chat/tests/unit/hooks/useAgentChatQueue.test.tsweb/packages/agenta-chat/tests/unit/model/error.test.tsweb/packages/agenta-entities/src/session/api/api.tsweb/packages/agenta-entities/src/session/core/liveness.tsweb/packages/agenta-entities/src/session/core/schema.tsweb/packages/agenta-entities/src/session/index.tsweb/packages/agenta-entities/src/workflow/state/store.tsweb/packages/agenta-entities/tests/unit/agent-build-kit-ui-state-atom.test.tsweb/packages/agenta-entities/tests/unit/session-cancel-api.test.tsweb/packages/agenta-entities/tests/unit/session-liveness.test.tsweb/packages/agenta-navigation/src/dynamic/sessionsSource.tsweb/packages/agenta-navigation/tests/unit/sidebarChildren.test.ts
🚧 Files skipped from review as they are similar to previous changes (58)
- docs/design/session-control-and-live-events/README.md
- web/packages/agenta-entities/src/session/core/schema.ts
- api/oss/src/middlewares/auth.py
- api/oss/tests/pytest/unit/middlewares/test_auth_public_endpoints.py
- services/runner/patches/sandbox-agent@0.4.2.patch
- hosting/docker-compose/oss/env.oss.dev.example
- web/packages/agenta-entities/src/session/api/api.ts
- web/oss/src/components/AgentChatSlice/state/liveness.ts
- api/oss/src/dbs/postgres/sessions/commands/dbas.py
- web/packages/agenta-entities/tests/unit/session-liveness.test.ts
- hosting/docker-compose/ee/env.ee.dev.example
- api/oss/src/dbs/http/sessions/control_delivery_direct.py
- api/oss/src/core/sessions/streams/runner_client.py
- web/packages/agenta-entities/src/session/index.ts
- api/oss/src/core/sessions/commands/types.py
- api/oss/tests/pytest/unit/sessions/test_command_matrix_inputs_data.py
- web/mobile/src/features/sessions/useLivenessPoll.ts
- api/oss/src/core/sessions/interactions/interfaces.py
- services/runner/src/sessions/stop-signal.ts
- api/oss/tests/pytest/unit/sessions/test_watch_interactions_publish.py
- services/runner/src/engines/sandbox_agent/engine.ts
- web/oss/src/components/AgentChatSlice/hooks/useAgentChatSession.ts
- api/oss/src/core/sessions/interactions/service.py
- api/oss/src/dbs/postgres/sessions/commands/mappings.py
- api/oss/src/dbs/postgres/sessions/interactions/dao.py
- web/packages/agenta-entities/tests/unit/session-cancel-api.test.ts
- services/runner/src/tracing/otel.ts
- api/oss/src/core/sessions/streams/dtos.py
- services/runner/tests/unit/session-pool.test.ts
- api/oss/src/apis/fastapi/sessions/models.py
- services/runner/src/sessions/execution-registry.ts
- api/oss/tests/pytest/unit/sessions/test_wp5_dao_fanout.py
- api/oss/src/dbs/postgres/sessions/streams/dbes.py
- services/runner/src/sessions/control-channel.ts
- api/oss/tests/pytest/unit/sessions/test_interaction_cancel_records.py
- services/runner/src/server.ts
- web/packages/agenta-entities/src/session/core/liveness.ts
- services/runner/tests/unit/teardown.test.ts
- services/runner/src/sessions/applied-commands.ts
- api/oss/src/core/sessions/commands/interfaces.py
- services/runner/tests/unit/control-command-apply.test.ts
- api/oss/src/core/sessions/commands/dtos.py
- api/oss/src/dbs/postgres/sessions/streams/mappings.py
- api/oss/src/dbs/postgres/sessions/commands/dbes.py
- web/packages/agenta-navigation/src/dynamic/sessionsSource.ts
- api/oss/src/core/sessions/commands/service.py
- api/oss/tests/pytest/unit/sessions/test_session_commands_dao.py
- web/packages/agenta-navigation/tests/unit/sidebarChildren.test.ts
- services/runner/tests/unit/harness-cancel-park.test.ts
- services/runner/src/engines/sandbox_agent/teardown.ts
- api/oss/src/core/sessions/streams/types.py
- api/oss/databases/postgres/migrations/core_oss/versions/oss000000022_add_session_commands.py
- api/oss/src/dbs/postgres/sessions/commands/dao.py
- api/oss/tests/pytest/unit/sessions/test_session_cancel_admission.py
- web/packages/agenta-chat/tests/unit/assets/transcriptToMessages.test.ts
- web/mobile/src/features/sessions/useActionableInteractions.ts
- services/runner/src/engines/sandbox_agent/session-identity.ts
- api/entrypoints/routers.py
Included review availability: Your plan provides up to 8 included reviews per hour; 1 remains after this review.
🛑 Comments failed to post (15)
.agents/skills/agent-release-gate/resources/path_triggers.py (1)
88-91: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Trigger session-control evidence for persistence and composition-root changes.
A release limited to
api/oss/src/dbs/postgres/sessions/**, the related session-command migration, or session-control wiring inapi/entrypoints/**does not requiresession_control.py. Those paths can break durable Stop persistence or delivery while the release gate remains green.
.agents/skills/agent-release-gate/resources/path_triggers.py#L88-L91: Add path rules for session persistence, relevant migrations, and session-control composition roots..agents/skills/agent-release-gate/SKILL.md#L195-L199: Document the same expanded mandatory-path scope.📍 Affects 2 files
.agents/skills/agent-release-gate/resources/path_triggers.py#L88-L91(this comment).agents/skills/agent-release-gate/SKILL.md#L195-L199api/oss/src/apis/fastapi/sessions/router.py (1)
1983-1983: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Reject oversized idempotency keys.
cancel_session_executiontruncates the normalized key beforerequest_cancel. The DAO uses that value for the unique command lookup, so two distinct keys with the same first 255 characters can replay the first command after a later execution starts. If the normalized key exceeds 255 characters, return HTTP 422 instead of truncating it.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.idempotency_key = request.headers.get("Idempotency-Key") if idempotency_key is not None: idempotency_key = idempotency_key.strip() if len(idempotency_key) > _MAX_IDEMPOTENCY_KEY_CHARACTERS: raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="Idempotency-Key is too long.", ) idempotency_key = idempotency_key or Noneapi/oss/src/core/sessions/streams/service.py (1)
212-215: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
Make expected-turn cancellation atomic.
A new turn can replace the locks after the earlier owner check. This path then tombstones and clears that new turn. Use an atomic compare-and-delete or fencing operation that verifies
expected_turn_idat the lock mutation point.api/oss/src/tasks/asyncio/sessions/records_worker.py (1)
243-248: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
🔎 Supported by static analysis
🤖 get_repo_knowledge executed:
get_repo_knowledge Agenta-AI/agenta /tmp/coderabbit-repo-knowledge/agenta-ai-agenta-4b53879a/learnings /tmp/coderabbit-repo-knowledge/agenta-ai-agenta-4b53879a/conventionsLength of output: 12646
🏁 Script executed:
#!/bin/bash set -eu printf '%s\n' '--- worker outline ---' ast-grep outline api/oss/src/tasks/asyncio/sessions/records_worker.py printf '%s\n' '--- worker focused source ---' sed -n '1,125p' api/oss/src/tasks/asyncio/sessions/records_worker.py sed -n '150,390p' api/oss/src/tasks/asyncio/sessions/records_worker.py printf '%s\n' '--- directly bound reclaim symbols and callers ---' rg -n -C 5 'reclaim_batch|drop_expired|max_deliveries|_last_commit_at|process_batch|quota|entitlement|org_deferred' api/oss/src/tasks/asyncio api/oss/src -g '*.py'Repository: Agenta-AI/agenta
Length of output: 50373
🏁 Script executed:
#!/bin/bash set -eu printf '%s\n' '--- consumer size and outline ---' wc -l api/oss/src/tasks/asyncio/shared/consumer.py ast-grep outline api/oss/src/tasks/asyncio/shared/consumer.py printf '%s\n' '--- consumer implementation ---' sed -n '1,420p' api/oss/src/tasks/asyncio/shared/consumer.py printf '%s\n' '--- focused tests and configuration ---' rg -n -C 6 'reclaim_batch|drop_expired|mark_committed|last_commit|max_deliveries|reclaim_pending' api/oss/tests api/oss/src/tasks/asyncio/shared api/oss/src/utils/env.py -g '*.py'Repository: Agenta-AI/agenta
Length of output: 44429
Exclude quota-deferred entries from
StreamConsumer’s delivery budget.When
check_entitlementsfails,RecordsWorker.process_batchleaves the entry pending. An unrelated successful append callsmark_committed(), soreclaim_batchcan treat the path as healthy anddrop_expiredcan acknowledge and delete the deferred entry aftermax_deliveries. Track deferred entries or retry the quota check before expiration.Add a regression with one successful project and one project with repeated entitlement failures. The deferred entry must remain pending beyond
max_deliveries.api/oss/src/utils/env.py (1)
528-532: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Reject non-positive record recovery settings.
AGENTA_RECORDS_RECLAIM_IDLE_MS=0makesStreamConsumerclaim pending entries on every loop. Negative idle values make Redis reclaim calls invalid.AGENTA_RECORDS_MAX_DELIVERIES<=0can make a healthy consumer ACK/delete a pending record before its next retry. Use_parse_optional_positive_int_envfor both settings.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.reclaim_idle_ms: int = ( _parse_optional_positive_int_env("AGENTA_RECORDS_RECLAIM_IDLE_MS") or 30_000 ) # Deliveries after which a record message is dropped instead of retried forever. A message # Postgres never accepts would otherwise hold every later message in the group. max_deliveries: int = ( _parse_optional_positive_int_env("AGENTA_RECORDS_MAX_DELIVERIES") or 5 )docs/design/session-control-and-live-events/spike-b-durable-commands-design.md (1)
519-521: 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash set -eu file='docs/design/session-control-and-live-events/spike-b-durable-commands-design.md' printf '%s\n' '--- relevant identifiers and authorization terms ---' rg -n -C 4 'AGENTA_RUNNER_TOKEN|replica_id|sessions|lease|owner|claim|settle|consume|authorization|authenticate|project scope' "$file" printf '%s\n' '--- target context ---' sed -n '470,545p' "$file"Repository: Agenta-AI/agenta
Length of output: 50374
🤖 get_repo_knowledge executed:
get_repo_knowledge Agenta-AI/agenta /tmp/coderabbit-repo-knowledge/agenta-ai-agenta-4b53879a/learnings /tmp/coderabbit-repo-knowledge/agenta-ai-agenta-4b53879a/conventionsLength of output: 23964
🏁 Script executed:
#!/bin/bash set -eu file='docs/design/session-control-and-live-events/spike-b-durable-commands-design.md' printf '%s\n' '--- authentication and claim contract ---' sed -n '494,545p' "$file" printf '%s\n' '--- runner contract and ownership-related terms ---' sed -n '637,670p' "$file" sed -n '918,1015p' "$file" printf '%s\n' '--- bounded ownership checks ---' rg -n -C 3 'not_held|held_sessions|owns|owner|lease|replica_id|runner token|AGENTA_RUNNER_TOKEN|server-side|authorize|authorization' "$file" | tail -n 180Repository: Agenta-AI/agenta
Length of output: 21726
Authorization Bypass (CWE-639): Authorization Bypass Through User-Controlled Key (IDOR)
Reachability: Internal · Exploitability: Difficult
Bind declared sessions to the authenticated runner.
The shared
AGENTA_RUNNER_TOKENdoes not bind a caller to itssessionsorreplica_id. The claim query and settlement guard trust both caller-supplied values, so a token holder can claim commands for another project and settle them. Require a server-side runner lease or owner check for every declared session, or use credentials scoped to the allowed sessions.services/runner/src/environment/mount-lifecycle.ts (1)
211-211: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
#!/bin/bash set -e file="services/runner/src/environment/mount-lifecycle.ts" wc -l "$file" sed -n '1,290p' "$file" printf '\n--- directly bound symbols ---\n' rg -n --glob '*.ts' 'function (throwIfAcquireAborted|rethrowIfInvariant)|const (throwIfAcquireAborted|rethrowIfInvariant)|throwIfAcquireAborted|rethrowIfInvariant|commitLocalMount|mountStorage|AcquireAbortedError' services/runner/srcRepository: Agenta-AI/agenta
Length of output: 22542
🏁 Script executed:
#!/bin/bash set -e printf '%s\n' '--- acquire-abort.ts ---' cat -n services/runner/src/environment/acquire-abort.ts printf '%s\n' '--- acquire-context.ts: commit and mount state ---' sed -n '90,155p' services/runner/src/environment/acquire-context.ts sed -n '235,305p' services/runner/src/environment/acquire-context.ts printf '%s\n' '--- context implementation: commit and cleanup-related methods ---' sed -n '90,175p' services/runner/src/environment/acquire-context-impl.ts printf '%s\n' '--- mountStorage success/abort path ---' sed -n '300,455p' services/runner/src/engines/sandbox_agent/mount.ts printf '%s\n' '--- callers and teardown references ---' rg -n -C 8 --glob '*.ts' 'mountLocalDurableCwd|mountLocalAgentCwd|mountedCwd|agentMountedPath|unmountStorage\\(' services/runner/src/environment services/runner/src/engines/sandbox_agent/environment.tsRepository: Agenta-AI/agenta
Length of output: 19676
🏁 Script executed:
#!/bin/bash set -e printf '%s\n' '--- mount lifecycle callers and remaining functions ---' sed -n '290,391p' services/runner/src/environment/mount-lifecycle.ts printf '%s\n' '--- environment acquisition and teardown around local mounts ---' sed -n '410,490p' services/runner/src/engines/sandbox_agent/environment.ts sed -n '520,590p' services/runner/src/engines/sandbox_agent/environment.ts sed -n '740,785p' services/runner/src/engines/sandbox_agent/environment.ts printf '%s\n' '--- all direct call sites ---' rg -n -C 6 --glob '*.ts' 'mountLocalDurableCwd|mountLocalAgentCwd' services/runner/srcRepository: Agenta-AI/agenta
Length of output: 34364
Commit each successful local mount before propagating acquisition aborts. Both helpers can receive
truefrommountStorage, then throw beforectx.commitLocalMount; teardown therefore has nomountedCwdoragentMountedPathto unmount. Commit immediately after success, then propagateAcquireAbortedError. InmountLocalAgentCwd,rethrowIfInvariantdoes not rethrowAcquireAbortedError, so the catch logs it as a normal mount failure and returnsfalse.📍 Affects 1 file
services/runner/src/environment/mount-lifecycle.ts#L211-L211(this comment)services/runner/src/environment/mount-lifecycle.ts#L247-L255services/runner/tests/unit/sandbox-agent-orchestration.test.ts (1)
2637-2640: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
Assert that the paused execution remains registered
findExecution(... )?.settledisundefinedboth when the entry is present and unsettled and when the entry is absent. The test can therefore pass after the registry entry is removed, leaving the live-turn lookup and cancellation invariant untested.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.const execution = findExecution( "11111111-1111-4111-8111-111111111111", sessionId, ); assert.ok(execution, "the paused turn stays registered"); assert.equal(execution.settled, undefined);services/runner/tests/unit/session-alive-interrupt.test.ts (1)
178-187: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Exercise the interval heartbeat and assert
onInterrupted.startAliveWatchdogstarts its 30-secondsetIntervalonly after the first beat.flushMicrotasks()waits 0 ms, and this test does not provide anonInterruptedcallback. Therefore it never observes the later interruption, so a broken production path can still pass. Use fake timers to advance one interval, then assertonInterruptedfires whilewatchdog.admittedremainstrue.services/runner/tests/unit/session-steer-mount-loss.test.ts (1)
457-459: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Match the fake teardown markers in the assertion.
makeHost().teardownemits onlyunmountandrmSync, soincludes("teardown")is always false. This test otherwise allows a refusal regression that acquires and tears down a rival environment to pass because it checks only the original environment'sdestroyedcount.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.assert.ok( !host.trace.some( (line) => line.includes("unmount") || line.includes("rmSync"), ), `a teardown ran during the refusal: ${host.trace.join(" | ")}`,web/mobile/src/features/chat/Composer.tsx (1)
52-53: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Reduce the new in-code comments.
Each listed block exceeds one short line. Replace it with one brief reason-only comment, or remove it when the code is self-explanatory.
web/mobile/src/features/chat/Composer.tsx#L52-L53: reduce the placeholder comment to one short line.web/mobile/src/features/chat/LiveConversation.tsx#L150-L151: reduce the pending-task comment to one short line.web/mobile/src/features/chat/LiveConversation.tsx#L307-L311: remove or reduce the JSX comment to one short line.web/oss/src/components/AgentChatSlice/AgentConversation.tsx#L430-L437: reduce the refusal-recovery comment to one short line.web/oss/src/components/AgentChatSlice/components/AgentMessage.tsx#L162-L167: reduce the error-state comment to one short line.As per coding guidelines, “Hard rule. At most ONE short line per comment.”
📍 Affects 4 files
web/mobile/src/features/chat/Composer.tsx#L52-L53(this comment)web/mobile/src/features/chat/LiveConversation.tsx#L150-L151web/mobile/src/features/chat/LiveConversation.tsx#L307-L311web/oss/src/components/AgentChatSlice/AgentConversation.tsx#L430-L437web/oss/src/components/AgentChatSlice/components/AgentMessage.tsx#L162-L167Source: Coding guidelines
web/oss/src/components/AgentChatSlice/AgentConversation.tsx (1)
440-442: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Restore consumed attachments on session-busy refusal.
finishSubmitcallsattachments.clearAttachments(consumedUids)immediately aftersubmit. The queue retainsfileParts, but this effect restores onlysent.text, so the staged files are removed and no other path restores them. Retain the consumed staged entries and pass them toattachments.restoreAttachmentsbefore the retry.web/packages/agenta-chat/src/hooks/useAgentChatQueue.ts (2)
86-97: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Reduce new code comments to one short line each.
The web guideline has a hard one-short-line limit per comment. Move detailed rationale to documentation or encode it in focused tests.
web/packages/agenta-chat/src/hooks/useAgentChatQueue.ts#L86-L97: replace the long recovery rationale with one short comment.web/packages/agenta-chat/src/hooks/useAgentChatQueue.ts#L212-L213: replace the two-line recovery comment with one short comment.web/packages/agenta-chat/src/model/error.ts#L49-L59: replace the admission-contract block with one short comment.web/packages/agenta-chat/src/model/error.ts#L65-L72: replace the matching rationale block with one short comment.web/packages/agenta-chat/src/model/error.ts#L76-L78: remove the parity-history comment or reduce it to one short line.web/packages/agenta-chat/tests/unit/hooks/useAgentChatQueue.test.ts#L367-L371: reduce the scenario explanation to one short line.web/packages/agenta-chat/tests/unit/hooks/useAgentChatQueue.test.ts#L390-L391: reduce the queue-state explanation to one short line.web/packages/agenta-chat/tests/unit/model/error.test.ts#L87-L90: reduce the runner-path explanation to one short line.📍 Affects 4 files
web/packages/agenta-chat/src/hooks/useAgentChatQueue.ts#L86-L97(this comment)web/packages/agenta-chat/src/hooks/useAgentChatQueue.ts#L212-L213web/packages/agenta-chat/src/model/error.ts#L49-L59web/packages/agenta-chat/src/model/error.ts#L65-L72web/packages/agenta-chat/src/model/error.ts#L76-L78web/packages/agenta-chat/tests/unit/hooks/useAgentChatQueue.test.ts#L367-L371web/packages/agenta-chat/tests/unit/hooks/useAgentChatQueue.test.ts#L390-L391web/packages/agenta-chat/tests/unit/model/error.test.ts#L87-L90Source: Coding guidelines
214-214: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
#!/bin/bash set -eu file='web/packages/agenta-chat/src/hooks/useAgentChatQueue.ts' printf '%s\n' '--- focused source ---' sed -n '1,260p' "$file" printf '%s\n' '--- bound symbols and callers ---' rg -n -C 3 'lastSentRef|takeLastSent|refus|release|send' "$file" web/packages/agenta-chat/src printf '%s\n' '--- focused diff ---' git diff --unified=30 -- "$file"Repository: Agenta-AI/agenta
Length of output: 50373
🤖 get_repo_knowledge executed:
get_repo_knowledge Agenta-AI/agenta /tmp/coderabbit-repo-knowledge/agenta-ai-agenta-4b53879a/learningsLength of output: 4671
🏁 Script executed:
#!/bin/bash set -eu printf '%s\n' '--- hook consumers ---' rg -l 'useAgentChatQueue|takeLastSent' web --glob '*.ts' --glob '*.tsx' | sort printf '%s\n' '--- exact consumer context ---' rg -n -C 12 'useAgentChatQueue|takeLastSent' web/packages web/apps --glob '*.ts' --glob '*.tsx' printf '%s\n' '--- refusal classification and handlers ---' rg -n -C 12 'SESSION_TURN_IN_USE|isSessionTurnInUse|takeLastSent|sendQueued|onError' web/packages/agenta-chat web/apps --glob '*.ts' --glob '*.tsx'Repository: Agenta-AI/agenta
Length of output: 31616
🏁 Script executed:
#!/bin/bash set -eu file='web/packages/agenta-chat/src/hooks/useAgentConversation.ts' printf '%s\n' '--- hook outline ---' ast-grep outline "$file" printf '%s\n' '--- queue integration ---' sed -n '100,190p' "$file" sed -n '450,570p' "$file" printf '%s\n' '--- all host reclaim/error references ---' rg -n -C 10 'takeLastSent|sendQueued|isSessionTurnInUse|SESSION_TURN_IN_USE|onError|sendMessage' web/packages/agenta-chat/src/hooks/useAgentConversation.ts web/oss/src/components/AgentChatSlice/AgentConversation.tsxRepository: Agenta-AI/agenta
Length of output: 38981
🏁 Script executed:
#!/bin/bash set -eu printf '%s\n' '--- OSS refusal effect and surrounding hook order ---' sed -n '330,455p' web/oss/src/components/AgentChatSlice/AgentConversation.tsx printf '%s\n' '--- release predicate binding ---' rg -n -l 'canReleaseQueuedMessage' web/packages web/oss rg -n -C 18 'canReleaseQueuedMessage' web/packages/agenta-playground web/packages/agenta-* web/oss 2>/dev/null | head -240 printf '%s\n' '--- package test setup and relevant release tests ---' sed -n '360,425p' web/packages/agenta-chat/tests/unit/hooks/useAgentChatQueue.test.tsRepository: Agenta-AI/agenta
Length of output: 34540
Protect refusal recovery before releasing the queue head.
When a refusal exposes
status === "error"with no pending HITL interaction, the queue can release B. Its effect stores B in the single-slotlastSentRefbefore the OSS refusal effect callstakeLastSent, so A can be lost. Preserve recovery by send identity or reclaim A before releasing B.web/packages/agenta-entities/tests/unit/agent-build-kit-ui-state-atom.test.ts (1)
1-6: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Reduce these comments to one short line each.
web/packages/agenta-entities/tests/unit/agent-build-kit-ui-state-atom.test.ts#L1-L6: replace the reload explanation with one short comment.web/packages/agenta-entities/tests/unit/agent-build-kit-ui-state-atom.test.ts#L10-L12: replace the storage-stub explanation with one short comment.web/packages/agenta-entities/src/workflow/state/store.ts#L1469-L1473: replace the persistence overview with one short comment.web/packages/agenta-entities/src/workflow/state/store.ts#L1481-L1486: replace the normalization overview with one short comment.As per coding guidelines: “Hard rule. At most ONE short line per comment.”
📍 Affects 2 files
web/packages/agenta-entities/tests/unit/agent-build-kit-ui-state-atom.test.ts#L1-L6(this comment)web/packages/agenta-entities/tests/unit/agent-build-kit-ui-state-atom.test.ts#L10-L12web/packages/agenta-entities/src/workflow/state/store.ts#L1469-L1473web/packages/agenta-entities/src/workflow/state/store.ts#L1481-L1486Source: Coding guidelines
mmabrouk
left a comment
There was a problem hiding this comment.
Agent-generated Codex review, low weight.
Verdict: CHANGES NEEDED
The rebase keeps the #6496 warm-Stop/Codex-reap work and the #6500 admission checks, but their integration with durable Stop leaves two release-blocking races. I also found one retry-contract defect and confirmed the flag-off desktop response still fails its client schema.
The flag-off transport itself is preserved: desktop reaches the legacy API service through /cancel, mobile continues to use the legacy stream command, and the runner receives the heartbeat-carried interrupt. The remaining client-side issue is that desktop waits for fetchSessionStream before aborting its local stream, so a slow read delays both local feedback and either Stop request.
No focused test was run; the failures follow from the current control flow, and the existing suites are green but do not compose registry replacement with asynchronous admission or abort a turn after PAUSED has won the prompt race.
Register session executions only after asynchronous admission succeeds. Cover a refused second turn while Stop cancels the admitted turn. Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk
Re-check labelled Stop after paused teardown and close the registry race before terminal reporting. Clear parked approval state when the turn converts to cancellation. Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk
Resolve exact retry keys before consulting current execution state and reject keys reused with a different expected execution. Cover replay after a later turn starts. Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk
Accept the flag-off session command payload alongside the durable cancel response and normalize it without logging a schema error. Cover the API's exact legacy body. Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk
Start the session-stream snapshot and local abort in the same tick, then cancel the captured execution when the read completes. Cover the unresolved-read ordering. Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk
Keep the fake cancel method behind a narrow structural view while returning the original SandboxAgent type. This lets the pause teardown regression participate in strict typechecking. Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk
mmabrouk
left a comment
There was a problem hiding this comment.
Agent-generated Codex review, low weight.
CHANGES NEEDED
The post-admission registry fix, pause-teardown cancellation, pre-guard idempotency replay, and legacy flag-off parsing all satisfy the requested scenarios and have the right regression coverage. The P3 change also aborts the local stream in the same tick. However, that timing change reopens the wrong-execution race that the preceding target-capture fix closed: local Stop now unlocks queued/new sends before the asynchronous stream read has actually pinned turn A. I left one P1 inline.
Focused validation passed: nice -n 19 pnpm exec vitest run tests/unit/sandbox-agent-orchestration.test.ts -t "converts a Stop during pause teardown" --maxWorkers=3 (1 passed, 75 skipped). git diff --check cd21450f7e..HEAD also passed. No other regression was found in the six-commit fix range.
Abort the local stream immediately while holding queued sends and regenerations until the session snapshot has pinned the stopped execution. Release new work as soon as the snapshot resolves so the durable cancel keeps targeting the turn the user saw. Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk
mmabrouk
left a comment
There was a problem hiding this comment.
Agent-generated Codex review, low weight.
CHANGES NEEDED
Within one mount, Stop still aborts locally in the same tick; queued/new sends and regenerations wait for the snapshot, and the regression proves an attempted B admission stays held until cancellation is pinned to A. However, the gate is mount-local while the desktop session Chat and queue survive remounts, so the original wrong-turn race remains across a route/pane remount. I left one P1 inline.
Focused validation passed: nice -n 19 pnpm exec vitest run src/components/AgentChatSlice/assets/stopWhileResolvingExecution.test.ts --maxWorkers=3 (2 tests). No other regression was found in git diff 00fae4b739..HEAD.
Remember the admitted turn id outside the hook mount and copy it into expected_execution_id before aborting the local stream. New sends can proceed immediately without changing an in-flight Stop's target, including across remounts. Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk
mmabrouk
left a comment
There was a problem hiding this comment.
Agent-generated Codex review, low weight.
CHANGES NEEDED
The new target is mount-independent once captured, and Stop still aborts locally in the same tick. The B-admission and remount regressions both pass and prove a captured request still names A. One earlier startup race remains, and it also affects the flag-off legacy fence; I left one P1 inline.
Focused validation passed: nice -n 19 pnpm exec vitest run src/components/AgentChatSlice/assets/stopWhileResolvingExecution.test.ts --maxWorkers=3 (3 tests).
Stop turn-id recovery at the latest user-message boundary so starting a new turn cannot restore the previous execution id before the new metadata arrives. Cover pre-metadata Stop with session-scoped cancellation and no stale expected id. Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk
mmabrouk
left a comment
There was a problem hiding this comment.
Agent-generated Codex review, low weight.
CHANGES NEEDED
The four focused Stop regressions pass, and manual send, queued send, regenerate, and assistant rewind all enter the empty-id window correctly. The remaining P1 is that an unfenced cancel is not running-only in either server mode; the flag-off route is therefore behaviorally affected by this change.
An unfenced Stop could fall back to a parked or recently finished alive owner while a newer turn was still entering admission. That could tombstone the stale turn without stopping the new one. Resolve durable unfenced cancels only from the running owner. The legacy path now uses owner-checked releases for that same running-only target and performs no row or lifecycle writes when no turn is running. Named cancels retain the parked alive fallback. Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk
mmabrouk
left a comment
There was a problem hiding this comment.
Agent-generated Codex review, low weight.
SHIP
No findings in d4c4e43..3935e0c. Both durable and flag-off legacy paths now make no-ID Stop running-only: before B owns running they leave parked A untouched and return an idle/no-op shape; once B runs they target B. Named Stops retain the alive-owner fallback, so stopping a parked approval remains reachable. The client normalizes both no-op forms to idle, not stopping. The earlier five fixes remain intact.
Focused legacy matrix: 10 passed.
Agent-generated, low weight. Draft. Do not merge before Mahmoud reviews it.
Implements the Spike B design with the direct adapter: the
session_commandstable,POST /sessions/{id}/cancelwith an optionalexpected_execution_id, the control-delivery port, the runnerPOST /cancelbeside/kill, the outcome route, and settlement that releasesrunningunder the owner check and leavesaliveto its TTL. Long polling is designed (PR #6497) and parked in Linear AGE-4253. Heartbeats stay a health and ownership signal. Stacked on Spike A (PR #6496).Evidence
58bec4d382applied/stopped,park-cancelled, next message recalled the codeword58bec4d382expected_execution_id; two Stops; Stop on a parked approvalobsolete200; 409 nothing written; one command; interaction cancelled, environment kept, resume warm58bec4d382is_running: true(the runner's final beat landed 0.88 s after settlement and the tombstone check refused it before the mirror write); the collection query every liveness poll drives is served from Postgres alone76e4b1368bmirror_livenessat settlementis_running: falseat Stop plus 0.15 s,is_alive: true; warm resume 2.01 s inside the park window against a 14.16 s cold rebuild after it9110c08000(integration)runningonly; a parked approval holds the turn inalive); the same Stop without an id cancelled the gate89d7c7c90dpendingtocancelled; a stale id still 409, naming the turn that would have been stopped9110c08000claimeduntil the watchdog settled itlost(one case exactly 120 s)89d7c7c90dpendingorclaimed; the duplicate gets 40989d7c7c90dno-park:end_turn), cold rebuild 7.21 s38cbc92201obsolete/not_running, parked, warmhit-continue1.91 s76e4b1368bdocker restartthe runner, then continueownerlease (120 s) expired: the runner never releases its owner claim at shutdown and a restarted runner has a new replica id; deleting only the owner key admitted the next message at onceTrade-off taken on
38cbc92201: past teardown, the API now discriminateslostfromnot_runningon therunningkey instead of the heartbeat, so in a multi-runner deployment a Stop misrouted to a replica that holds a parked approval settlesnot_runninginstead oflost. Multi-replica direct delivery is not version one; open question 8 in the report.Tests
38cbc92201).cd services/runner && pnpm test: 2671 pass, 160 files (runpnpm run build:extensiononce in a fresh worktree first).@agenta/entities1468,@agenta/navigation46,@agenta/mobile144.uvx ruff@0.15.12 formatandcheck: clean.pnpm lint-fix: clean.Branch head
38cbc92201onfeat/session-durable-cancel, basespike/session-cancel-warm. Reports:slice-durable-cancel.mdon this branch;evidence-2026-09-03/post-stop-mirror.md,post-stop-liveness.md, anddaytona-pi-claude.mdon PR #6505.https://claude.ai/code/session_01GAqSs7fw6QRi2n1ZJ2tmAV
Restack of 2026-09-04
This PR now carries the rollback switch itself, so it is safe on its own:
AGENTA_SESSIONS_DURABLE_STOP(default off) inenv.py; with it off the cancel route returns the exact legacy response and takes the heartbeat-carried Stop, and nosession_commandsrow is written. Commits50bffdc8efande9858e64acmoved down from PR #6501. Also5a66bf0e12narrows the runner-control auth exemption from/sessions/control/to/sessions/control/commands/, so a session whose id is literallycontrolstill needs project auth (a whole-PR review finding). Head5a66bf0e12; API sessions suite 568 passed. Agent-generated, low weight.Cancelled interaction record (2026-09-04, afternoon)
The browser pass showed an approval card that stayed pending after a Stop, because the cancel updated the interaction row and published a notice but wrote no transcript record. Commit
e4cbd64ac9writes one cancelled interaction record per cancelled interaction (deterministic id, identifiers only, published after commit); a later answer gets 409 "Interaction is no longer pending". Reviewed: passes. Known item, non-blocking: the desktop clears the card from the interaction row state, and its record handler ignores the new record'soutcome: cancelledshape; a records-only consumer would need the client handler aligned (client side, PR #6504). The record append is a separate transaction from the cancel commit, as on the normal-answer path.