Skip to content

[release] v0.115.0 - #6571

Merged
mmabrouk merged 249 commits into
mainfrom
release/v0.115.0
Sep 5, 2026
Merged

[release] v0.115.0#6571
mmabrouk merged 249 commits into
mainfrom
release/v0.115.0

Conversation

@github-actions

@github-actions github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

New version v0.115.0 in

  • web
    • web/oss
    • web/ee
    • web/mobile
  • services
  • api
  • sdks
    • sdks/python
  • clients
    • clients/python
    • clients/typescript
  • kubernetes
    • kubernetes/helm

mmabrouk and others added 30 commits September 2, 2026 15:05
The records stream worker added every decoded Redis message id to its
acknowledged list during deserialization, before it attempted the Postgres
write. A failed `append_many` logged an error and continued, and the shared
consumer loop then acknowledged and deleted those messages from the stream.
Every Postgres failure was therefore permanent, silent record loss, and the
worker reported success while doing it (#5496).

`append_many` is one statement in one transaction, so one record Postgres
rejected also took its whole batch with it, losing up to fifty unrelated
records per rejection (#5594).

Three changes:

- `process_batch` returns a message id only once its rows are committed, or
  once the worker has decided to drop it on purpose (undecodable, or over
  quota). A failed entitlements check now defers instead of dropping, because
  an unreachable meter is transient.
- A failed group is rewritten one record at a time, so a rejected record no
  longer discards the rest of its batch.
- `StreamConsumer` gains an opt-in reclaim pass. `read_batch` only ever asks
  for `>`, so without it an unacknowledged entry is invisible to every later
  read and "leave it pending" would still lose the record. The pass claims the
  group's pending entries, and drops one after `max_deliveries` failures with
  an error log naming the lost record.

The drop applies only while other records are committing. The delivery counter
cannot tell a rejected record apart from a database that is down, so dropping
on the count alone would delete every record in flight once an outage outlasts
the budget. A live run against a real Redis found that hole; the guard closes
it.

The reclaim pass is off for the tracing and events workers, so their behaviour
is unchanged.

Verified against a real Redis 8: five records published during a twenty second
write outage stayed pending, then all landed on recovery with no duplicates and
an empty stream; a permanently rejected record let its batch mates through and
was dropped loudly once traffic resumed.

Claude-Session: https://claude.ai/code/session_01GAqSs7fw6QRi2n1ZJ2tmAV
`RecordsRetentionDAO.delete_records_before_cutoff` selected and deleted on
`RecordDBE.id`. That attribute does not exist. The records key is
`(project_id, record_id)`, so every call to the retention flush raised before
it deleted anything and records have never been aged out.

Scope added on purpose: this defect is clear, obvious and one line, it sits in
the records durability area this branch already touches, and Spike D found it
while auditing the same pipeline. It is kept in its own commit so it can be
reverted or landed alone.

Verified: `hasattr(RecordDBE, "id")` is False, the primary key constraint at
`dbes.py:18` is `(project_id, record_id)`, and the corrected statement compiles
against the Postgres dialect.

Claude-Session: https://claude.ai/code/session_01GAqSs7fw6QRi2n1ZJ2tmAV
Port the durable-cancel spike's 13-cell driver (refresh_live.py) into the
release-gate skill as resources/session_control.py, per qa-audit-2026-09-03.md
section 4, so the standing regression check survives outside one evidence
folder. Matches the gate's env contract (AGENTA_BASE, AGENTA_ADMIN_KEY,
QA_OPENAI_API_KEY, no file fallback), moves the Docker/Postgres-only helpers
behind an OperatorHooks interface so six cells run over HTTP against any
deployment and the rest SKIP by name without --project, emits the gate's
PASS/FAIL/SKIP result shape into a timestamped ~/agenta-qa-evidence/ run
folder, and adds --resume so a lost agent costs one cell, not the run. Adds
two new cells (repeat-stop, stop-during-completion) from qa-audit section 3,
a path_triggers.py rule that makes the suite mandatory for session-code
changes, a SKILL.md section naming the command and the model-key locations,
and a pytest-and-standalone-runnable unit test for the pure parts (cell
registry, hooks skip path, resume, verdict shape, env resolution).

Claude-Session: https://claude.ai/code/session_01GAqSs7fw6QRi2n1ZJ2tmAV
The live smoke run against the integration stack showed /sessions/{id}/cancel
returns 202 Accepted (a pending command plus a stopping execution), the
correct async-acceptance status. The verdict checks in stop-warm,
stop-approval, repeat-stop, and stop-during-completion hardcoded 200 and
FAILed every real Stop. Accept 200 or 202 in each.

Claude-Session: https://claude.ai/code/session_01GAqSs7fw6QRi2n1ZJ2tmAV
…ontrol.py

Add HARNESSES["claude"] (kind claude, model sonnet, provider anthropic, vault
connection) so the session-control cells can drive the Claude Code harness,
and stock an Anthropic provider key into the bootstrapped account's vault
the same way the OpenAI key is stocked, gated on --harness claude so a
pi_core/codex-only run does not need ANTHROPIC_API_KEY set. Also widen
wait_for_turn/wait_for_tool by a configurable SANDBOX_STARTUP_SLACK_S (25s)
when --sandbox daytona is selected, since a Daytona sandbox takes 10 to 20s
to start on top of local timings. Record the session's distinct sandbox ids
(via /sessions/turns/query, HTTP-only) in every HTTP-only cell's evidence as
sandbox_ids / warm_same_sandbox, so a resume that silently rebuilt the
sandbox is visible in the result instead of only in the recalled codeword.

Claude-Session: https://claude.ai/code/session_01GAqSs7fw6QRi2n1ZJ2tmAV
The Daytona smoke run FAILed stop-approval with only
"resume did not recall the codeword" and no reply text to check why, so a
driver replay bug (the reconstructed output-denied tool part) could not be
told apart from a genuine product miss. Add resume_text, resume_frames, and
resume_errors to the cell's evidence.

Claude-Session: https://claude.ai/code/session_01GAqSs7fw6QRi2n1ZJ2tmAV
Two invocations started in the same second (e.g. Claude Code and Daytona
smoke runs fired in parallel tonight) shared a run folder, since the
timestamp alone has 1-second resolution -- the second writer silently
overwrote the first one's results.json mid-run and one run's evidence was
lost until recovered from its redirected stdout log. Add the PID to the
folder name so concurrent invocations never collide.

Claude-Session: https://claude.ai/code/session_01GAqSs7fw6QRi2n1ZJ2tmAV
…imeout

assistant_message() indexed turn["segments"] unconditionally. When the
driver's own wait for a turn times out (handle["out"] stays None,
observed when the runner is unhealthy after a restart), the cell passed
an empty {} dict in and the KeyError masked the real signal, which is a
driver-side timeout rather than a cell result.

Claude-Session: https://claude.ai/code/session_01GAqSs7fw6QRi2n1ZJ2tmAV
Add cell_runner_gone, ported from cell_runner_gone in refresh_live.py, as
cell "runner-gone" in the session-control driver's registry. It restarts
the runner right after a Stop is claimed, then checks that the sweep
settles the command as lost (not claimed) in session_commands, the
session_streams row reads is_running: false, and a Send sent after that
runs. Register it in CELLS and in the registry's stable-names unit test,
and update SKILL.md's cell count and Docker-needing cell list.

Claude-Session: https://claude.ai/code/session_01GAqSs7fw6QRi2n1ZJ2tmAV
…ncurrent-stops

Add OperatorHooks.ensure_runner_healthy(), implemented in DockerComposeHooks, that
unpauses, restarts, and health-checks the runner as needed. Extract the per-cell
execution in main() into run_cell(), which calls it in a finally block after every
needs_hooks cell, so a cell that raises before its own restore code runs (as
cell_stale_tail did tonight, leaving the runner paused) cannot strand the runner
for the next cell. Also wrap cell_stale_tail's pause/unpause and
cell_records_outage's stop/start Postgres in their own try/finally, so each cell
restores what it touched even on an exception in between.

Add cell "concurrent-stops": five sessions started at once with a long turn, Stop
sent to all five within about a second, each expected to return HTTP 202, settle
exactly one terminal record, and recall its own codeword on a warm resume.
HTTP-only, no hooks needed.

Add unit tests for run_cell's finally path (NullHooks skips the recovery call
without crashing; a stub hooks object confirms the recovery call fires when a
cell raises, and is skipped for a cell that does not need hooks) and add
"ensure_runner_healthy" to the NullHooks-raises coverage. Update SKILL.md's cell
count and lists.

Claude-Session: https://claude.ai/code/session_01GAqSs7fw6QRi2n1ZJ2tmAV
… both races

Split cell_runner_gone into two: the new cell_runner_gone pauses the runner
BEFORE sending the Stop, so the command can never be claimed or reported and
must settle lost off a deterministic sweep, with an explicit check for the
watchdog's execution_lost ending. cell_runner_gone_late keeps the old
restart-after-stop timing, which mostly loses that hard race because the
runner often reports the Stop's outcome before it actually dies.

Both races satisfy the same invariant: exactly one effective terminal
outcome, no command left pending or claimed, is_running false, and the next
Send succeeds. Factor that shared PASS rule into _judge_runner_gone(), used
by cell_runner_gone_late (cell_runner_gone keeps its own stricter assertion
since pausing first is meant to force the lost/execution_lost shape every
time). Both record which race landed on evidence["race"].

Register runner-gone-late in CELLS and the registry's stable-names test.
Update SKILL.md's cell count and Docker-needing cell list.

Claude-Session: https://claude.ai/code/session_01GAqSs7fw6QRi2n1ZJ2tmAV
mmabrouk and others added 11 commits September 5, 2026 01:29
Document owner routing limits, transport requirements, fail-closed admission, cleanup continuity, and sanitized test evidence.

Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk
Treat post-cancel process reaping as best effort without changing the harness-confirmed cancellation outcome. Log cleanup misses for QA and cover failed and unknown reap paths through parking and continuity.

Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk
Keep the recovery slot when a newer composer draft blocks restoration. Consume it only after refused text and staged attachments are placed safely, with regressions for the occupied-composer case.

Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk
Document that Codex cleanup misses are QA evidence rather than a teardown signal. Keep settled Stop parking and continuity under the 600-second stopped-session window.

Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk
Apply the repository Prettier style to the inherited test file so the TypeScript format check passes.

Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk
Move a refused send into a conversation-local holding slot before a newer submission can replace the queue recovery value. Restore its text and staged attachments once the composer becomes empty, without submitting it automatically.

Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk
Capture the refused send into the conversation-local holding slot before scheduling editor placement. Cover the interleaving where a newer submission replaces the queue recovery value before the frame runs.

Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk
fix: address CodeRabbit review for session control
Expose the fixture-generated database name to the concurrency test. Connect the raw sweep and observer clients to that isolated database.

Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk
[fix] Point the watchdog collapse test at its own database
@vercel

vercel Bot commented Sep 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
agenta-documentation Ready Ready Preview Sep 5, 2026 5:03pm UTC

Request Review

[feat] Session control milestone 1: warm Stop, durable Stop, recovery
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

📘 Docs preview

Status ✅ Ready
Preview https://pr-6571-agenta-docs-preview.mahmoud-637.workers.dev/docs
Inspect Actions run
Commit 362d1a71e6dbc4032ad206b044dcc4d704d3e1fa

This comment updates in place on every push.

@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

Railway Preview Environment

Status Destroyed (PR closed)

Updated at 2026-09-05T17:11:29.194Z

…6577)

* fix(api): preserve flag-off session defaults

Keep the pre-milestone watchdog threshold and visible late output when durable Stop is disabled. Retain watchdog terminal settlement and the stricter durable Stop behavior.

Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk

* fix(frontend): settle acknowledged legacy stops

Abort the local stream as soon as the flag-off legacy API confirms Redis displacement. Keep durable Stops waiting for terminal evidence and prevent the legacy path from entering the 30-second retry state.

Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk
@mmabrouk
mmabrouk merged commit a4af13c into main Sep 5, 2026
73 of 74 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant