Repository navigation
Release/1.26.0 - #1424
Merged
Merged
Release/1.26.0#1424
Conversation
Adds backend/scripts/compaction_quality_harness.py and the compaction_quality package: a seeded corpus of long editing sessions with planted, exactly-scorable facts (constraint, decision, reference, superseded), replayed through the production TurnBasedSessionManager under each arm (full, model_relative, legacy, floor_50) and pace (restore, cold, warm). Only I/O is stubbed; the cut, the deferred apply, the restore slice, anchor truncation and bound_summary are the real code. Steps: cut (free, writes probe-time histories plus a plant-availability table), records and ask (Bedrock spend, estimate-first, --yes to run, a cachePoint after the shared history), score (exact match, paired per family against the full-history control, exact McNemar, n on every row). The clock stub stamps real time on save and inserts the pace gap only between turns; backdating every save hid the restore-path missed free apply, which a tripwire test now pins until it is fixed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Spec §5 gains a status note above the waiver: the harness exists, the control is full history rather than the kill switch, and the first full run waits for the missed-free-apply fix. The scoping doc marks slice 1 built, answers its open questions, and records the aggregate prod readout. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every _save_compaction_state stamps updated_at. On a restore, _apply_compaction advanced the truncation anchor (a save) before the coordinator called apply_pending_compaction, which then read a ~0s gap and left a parked cut waiting on a cache that was already cold; it landed only at the paid hard-ceiling apply, after the session had also paid the anchor re-write. apply_document_offload had the same defect after any apply that itself saved. The restore slice now captures the previous turn's stamp before the anchor advance (_restore_turn_stamp); apply_pending_compaction consumes it every turn into _turn_start_stamp, and _document_offload_reason reads that first. Both stamps are cleared at turn end so an old stamp can never make a warm turn look cold. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The quality harness's restore pace pinned the missed free apply; with the turn-start gap fix it applies the parked cut on cache_expired. Spec §5 and the scoping doc record the fix. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n actions - kb_migration teardown passes its own 780 s poll window to the delete saga. Managed KBs on dev took ~10 minutes to delete, so at the shared 480 s every teardown needed two worker runs (~30 min). The shared default stays 480 s for the reconciler, which deletes several per run. - Agents page Chat/Edit/Share/Delete icon buttons (grid and list) get aria-labels naming the agent; the tooltip only set aria-describedby. - Kaizen queue: the production orphaned-agent cleanup chore, blocked on #1293 and #1301 reaching main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…skills-from-tool-result-offload fix(compaction): never offload skill instructions from the skills tool
…t-model-text fix: keep streamed model text out of runtime logs and EMF lines
…ll-and-agent-button-labels fix: one-run KB teardown, named Agents page actions; kaizen chore for the prod orphan cleanup
…ion-quality-harness feat(compaction): offline quality-veto harness (slice 1)
…ion-apply-turn-gap fix(compaction): read the head-of-turn cache gap from before any save
…d Projects 2.3a) Pure library for the canonical memory-file format (spec §4.2) and the manifest-aware save checks (§4.3 steps 1-4), plus a bounded CountTokens counter in apis.shared for save-time token accounting. No callers yet; 2.3b wires these into the save path. - format.py: system-rendered frontmatter (YAML subset, no new dependency), items with ULID anchors, links, aliases, slug rules, reserved MEMORY.md. - validation.py: locked-field echo rule, name/alias collisions, anchor known/unique/minted, link resolution incl. archived targets; only new dead links fail. - tokens.py: one-attempt, 2 s CountTokens with a labelled chars/4 fallback; MEMORY_TOKEN_COUNT_MODEL_ID="" turns it off (tests do). - Hypothesis properties for round trips, anchor stability and link resolution. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Wires the 2.3a format and validation into every Memory Space write path
and adds per-file version history.
- MemorySpaceService.save_entry: validate, count tokens once
(CountTokens), write the object, conditional manifest swap, then the
FILEVER row. The swap is the commit point, so version numbers cannot
collide; replaced objects are kept because history references them.
- fileFormat freeform|canonical on the space, set at create. Existing
spaces stay freeform (stored as written; warnings only). Canonical
spaces enforce items, anchors, links, collisions and the 8,000-token
hard cap; a concurrent save of the same file is a 409.
- Pre-history entries get a baseline version on first replacement.
delete_entry purges its history (dedup-aware); delete_space pages its
rows and purges every object; consolidate never collects history.
- MEMORY.md is reserved in the service on every path.
- API: PUT entries returns version/tokens/warnings/anchors; new
GET /memory/spaces/{id}/history?slug= and /history/{n}?slug=.
- memory_write records reason "save" and no longer sends an empty
description (tool spec text unchanged, so toolConfig is unchanged).
- app-api task role gains bedrock:CountTokens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Under AgentCore, ADOT writes every prompt and reply into the runtime log group's otel-rt-logs stream from two scopes: - strands.telemetry.tracer: Strands puts messages on span events and ADOT's LLOHandler re-emits them as log records. Setting OTEL_SEMCONV_STABILITY_OPT_IN=gen_ai_unredacted_attributes= turns on Strands' redaction with nothing exempt. - botocore bedrock-runtime: ADOT defaults OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT to true with setdefault, so the image sets it to false first. Both are image ENV rather than Runtime variables, because the Runtime's 50-variable cap is nearly spent. A Runtime variable still overrides them. The new test drives a Strands turn through the real tracer and LLOHandler, and the botocore event builders, against the Dockerfile's values. Each case has a capture-on control. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…#1314) Adds the CDK plumbing so the platform self-service flag can be turned on per deployed environment (it was code-only before, readable from os.environ but never set on the runtime). - config.ts: PlatformSelfServiceConfig + opt-in parse (CDK_PLATFORM_SELF_SERVICE_ENABLED, only literal 'true' enables; mirrors projects). - inference-agentcore-construct.ts: sets PLATFORM_SELF_SERVICE_ENABLED on the AgentCore Runtime (read only there). 47->48 of 50 env slots; guard test reports 2 free. - platform.yml: forwards the CDK var from the GitHub environment. - mock-config.ts: default off. Default OFF everywhere; prod stays dark unless its own env var is set. Not wired into app-api (it never reads the flag). Verified: tsc clean, env-var-limit + config tests pass (198).
…format-validation feat(memory): canonical memory-file format and save validation (Shared Projects 2.3a)
…t-capture fix(observability): keep conversation content out of runtime OTEL logs
…ce type Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- raw_summary arm: the production cut with the summary budget lifted, so comparing it with model_relative isolates what bound_summary's Nova Micro compression costs, separately from the cut. - records: --record-words sets the per-record word cap, and --workers runs transcripts in parallel. One record per turn at 600 words reproduces prod's ~20k-token LTM records at cut time, which is what makes the compression run. - ask: --workers parallelizes the questions on one history after its first call has written the cache. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…window (#1319) The alb-unhealthy-hosts alarm paged on any single App API target failing one health check for 10 min (evaluationPeriods:2). ECS self-heals such a target in ~5 min while the other targets keep serving, so this fired ~daily on benign, zero-impact blips. Require 4 consecutive 5-min periods (20 min) instead. Threshold stays 0 and treatMissingData stays BREACHING, so a stuck target and a total outage both still page. Window-not-threshold per observability.md section 11.
…documents
Agent icons live in the RAG documents bucket at
assistants/{id}/icons/{digest}.{ext}. The legacy ingestion parser read that
4-part key as document_id="icons" and left a failed DOC#icons row on the
agent. Both ingestion paths now parse strictly and skip (info log, success,
no row written) anything that is not assistants/{id}/documents/{docId}/{file}.
The managed-KB EventBridge rule is narrowed to assistants/*/documents/*.
Adds backend/scripts/cleanup_stray_doc_rows.py (report-only by default) to
remove the stray rows already written on live agents.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The ~1,300 retrieval failure lines in the production runtime log are 745 events (each logged twice) and none is a throttle. 476 turns lost retrieval to dead pooled connections on cached agents after ~6 min idle, masked by a handler bug that discards every namespace's results; 84 more sent a query over the 10,000-character limit. Documents the evidence and a recommended fix (not implemented). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The daily reconciler snapshots every KB_Record and then walks AWS. A teardown that finished in between left the run holding a record whose knowledge base was gone, and mark_vector_state_missing's unconditional UpdateItem recreated it as a ghost KB# item holding only vectorState/updatedAt. That's the orphaned row the teardown exists to remove. refresh_stored_bytes had the same upsert hazard, and both run in report-only mode too. - records.update_if_present: an update_item guarded on attribute_exists(PK) that returns False (logged at info) instead of creating a removed record. - reconciler: both record-side writes go through it and are only counted when they land. Records in migrationState=teardown are neither marked missing nor refreshed, and are listed in ReconcileReport.tearing_down (tearingDown in the serialized report). - Same guard on the other unconditional KB# writes that could outlive a teardown: set_resource_policy_state (both branches; REMOVE-only upserts too), byte_cap commit/release/reserve (reserve tells "record gone" apart from "over the cap" via ReturnValuesOnConditionCheckFailure), and the worker's _set_retain_until and _record_progress. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
appTooltip only sets aria-describedby, so the icon-only buttons on the
connector list and form were announced as just "button" (axe button-name,
critical). Add aria-labels following the Agents page pattern: row actions
name their connector ("Delete Example Drive"), the secret toggle names its
current action, and the callback-URL copy buttons say what they copy. Also
names the row Edit link (same defect, axe link-name) and adds type="button"
where missing. Specs assert every icon-only control has an accessible name.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ion fails Spec §5's waiver is replaced by the first full harness result (Haiku 4.5, 12 transcripts x 9 planted facts, k=3, prod-sized LTM-style records). The model-relative cut with an uncompressed summary matches the full history; the same cut with bound_summary's Nova Micro compression (~15k -> ~730 tokens, reproducing prod's ~20k -> ~760) loses standing instructions (1.00 -> 0.72, p=0.002) and exact identifiers (0.96 -> 0.58, p=0.004). The tuning rule's FLOOR_RATIO lever is scoped to retained-turn loss. The scoping doc's §8 records method, spend (~$16) and the candidate levers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…save-pipeline feat(memory): save pipeline and FILEVER history (Shared Projects 2.3b)
…ingestion-skip fix(ingestion): skip agent icon uploads instead of ingesting them as documents
…-ups Adds a "Dev-validated 2026-09-25" entry to the 2.3b as-built notes with the observed numbers (counted tokens, versions, rejections, purge), and a "Carried from 2.3" list under 2.4: canonical project spaces, the anchor-stripping decision for injected memory, the counter cold start, and the deferred items. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1325) The platform self-service set_default_model tool calls UserSettingsRepository.update_settings (PutItem + conditional UpdateItem), but the inference/AgentCore runtime IAM role granted only GetItem on the user-settings table. The write AccessDenied'd and the tool swallowed it into a generic error. - inference-api-iam-roles.ts: broaden the user-settings grant to GetItem + PutItem + UpdateItem (renamed sid to UserSettingsTableReadWriteAccess), scoped to the one table ARN, no DeleteItem, no wildcard. - security-policy.test.ts: update the regression guard to assert read+write (no delete, no wildcard). - account_tools.py: set_default_model now surfaces the real error type (e.g. AccessDenied) instead of a generic 'try again', so a hidden infra gap is diagnosable from the tool result.
Arm gains an optional summarizer with bound_summary's signature, patched into turn_based_session_manager for that arm only and restored after, so a candidate runs behind the production cut. summarizers.py adds two prototypes: - compress_only: production's prompt, temperature only. Production's compress_with_model also sends topP, which Claude 4.5+ rejects. - extract_then_compress: a verbatim pinned block of instructions, decisions, identifiers and changed values, then the narrative compressed into the rest of the budget. New arms: nova2lite_compress (production code, other model id), haiku_compress, extract_nova_micro, extract_nova2lite and extract_haiku. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The agent-binding rules ask whether a thread already has messages, on an agent conversation's first turn and on every @-mention, before the stream opens. `_session_has_messages` answered with get_messages(limit=1): a full history read plus metadata, interrupts, UI resources and tool summaries, to keep one message — ~540ms on a new conversation on dev, growing with history — and constructing the Memory session manager behind it wrote a SESSION event into sessions that had none. `session_has_messages` is now one ListEvents: maxResults=1, no payloads, filtered server-side to events without the SDK's reserved `stateType` key (its SESSION/AGENT records carry it; messages never do). 91ms median on dev, same answer as the old semantics on 42 sampled conversations and on all 14 that hold only a SESSION record. Fails open on a failed read, as the old path effectively did. Also records the #1411 (KB search ahead of the build) dev readout in the turn-path spec. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-turn-smoke ci(nightly): run the turn-path smoke matrix after the E2E step
The nightly stack never runs the bootstrap data seeding, so its default role grants no tools and the turn-smoke rows for calculator and ask_user_question could only fail. seed-e2e-users.sh now puts the regular E2E user in a Cognito group (e2e-users) and seeds an e2e_user app role mapped to it through the shared AppRoleRepository (backend/scripts/seed_e2e_role.py), so the DEFINITION, JWT_MAPPING, TOOL_GRANT and MODEL_GRANT rows the resolver reads land together and effective_permissions mirrors the grants. Idempotent: create on first run, update after. The uv setup moves ahead of the E2E step so the seed can run inside e2e-test.sh. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…-messages-existence-check perf(agents): make the binding history check an existence check
…-e2e-user-grants
#1411 overlapped an agent turn's knowledge-base search with the agent build, but two DynamoDB reads still ran on the event loop after the search returned — the document-status filter and resolve_context_cap's record read — so they waited for a cold build to release the loop: ~120ms of residual rag_wait on every agent first turn on dev, classic and managed alike. The facade now runs the backend search and the status filter together on one worker thread with a private loop (_search_then_filter); everything that schedules onto the caller's loop (dual-read observational read, observation, activity touch) stays on it, and legacy_ms still times the search alone. The context cap is read on a worker started alongside the search. What reaches the model is unchanged. test_kb_dual_read's fail-closed case asserted the managed read never *started*, which held only because the stub legacy backend never yielded; it now asserts the read is cancelled and never finishes (mutation-checked). Also records the #1413 and managed-KB dev readouts in the turn-path spec. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…arch-off-loop perf(rag): keep the KB search's follow-up reads off the event loop
A managed agent's follow-up turn spent ~896ms in the knowledge base search (rag_wait 786ms of a 1254ms turn_prelude on dev). The Runtime's botocore spans split it as Retrieve 660ms plus ~230ms of our own DynamoDB: the KB_Record was read three times (load_record, ManagedKbBackend._locate, resolve_context_cap) and the document statuses once more, each on a fresh boto3.resource paying ~45-55ms for its first request against ~4ms reused. - The facade reads the record once and passes it across the seam (search(..., record=)); the managed backend takes awsKbId from it and falls back to its own lookup when the record has none. - search_assistant_knowledgebase_with_cap returns the engine's context cap with the results, decided from the same record; the inference route uses it instead of reading the record again. The augmented message is unchanged. - records._table() and the status lookup use the process-cached aws_clients table on one shared connection pool. The kb-migration, kb-sync and rag-ingestion images now ship aws_clients.py (and hash it). - A root conftest fixture resets the aws_clients cache around every test; Hypothesis tests that patch boto3.resource per example isolate the cache per example. Expected: ~215ms off a managed follow-up's rag_wait (~150ms on classic), and first-turn rag_wait ~120ms -> ~10ms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1415) #1415 moved the search and the status filter onto a worker thread and read the context cap on a second worker. This branch reads the KB_Record once and returns the cap with the results, so the cap worker is removed and the record is passed through _search_then_filter to backend.search. The off-loop test's two cap-worker cases are replaced by the new contract: the route takes the returned cap and never reads it again, and a failed search still fails open. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
reviews/2026-09-25.md and reviews/2026-10-02.md both recommended these and drafted them verbatim; decisions.md had not changed since 09-21. - Declined as kaizen items: docling #405 and Guardrails #480. Both issues stay open as product backlog; the docling bump alone remains welcome as a PR-sized slice. - Declined: the standing "do derived execution contexts re-evaluate RBAC" audit. Re-open on a named context with the check it skips. - Accepted: bedrock-agentcore #629 (TracerProvider not flushed before microVM freeze, last-turn telemetry under-reports). - Declined as scoped: reversing the Astra 272K cap. The cap is load-bearing pricing until CuratedModel carries a per-tier rate. review-queue.md: the Astra, docling and two Guardrails entries move from Open to Resolved. The RBAC audit and #629 had no entry of their own, so nothing else moves. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sions-2026-10-03 chore(kaizen): log the four decisions carried since the 09-25 review
…single-record-read perf(kb): read the KB record once per search, on a cached table
One KB_Record read per search at ~4ms, the status filter at ~4ms per document, no cap read: ~180ms of DynamoDB around Retrieve is now ~20ms. Normalised by Retrieve, a managed follow-up's rag_wait lost ~100ms against #1415; it is now the managed Retrieve itself. Stored augmented messages are byte-identical to the pre-change conversations. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…read-readout docs(specs): record the KB single-read readout on dev (#1416)
Spec for "Rewind to here" and "Fork from here" on user messages, written around the prompt-cache question: when a cut reuses cached history, why a warm rewind is cache-safe while a fork (a cold rebuild) is best-effort, and the storage, compaction and observability rules that follow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Default-on flags come in two kinds and only one is permanent. A feature switch turns off a capability a deployment may not want; a rollout switch guards a change meant to be invisible and exists only for a fast undo. Rollout switches now carry a retirement condition (default: one prod release plus two weeks clean) and are removed in a per-release cleanup with a CHANGELOG Removed entry. The flag reference gains a Kind column that classifies every runtime-only flag, plus the two that were missing from it (INLINE_ATTACHMENT_PERSIST_ENABLED, COMPACTION_SUMMARY_EXTRACT_ENABLED). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…h-retirement docs(flags): feature switches stay, rollout switches retire
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ation-rewind-fork-spec docs(specs): conversation rewind and fork
The first token arrives sooner: the turn path is mapped and timed end to end, and the largest stages we own are off the critical path (cold agent build 869 -> 372ms, the per-turn history count 388 -> 0ms on long conversations, concurrent MCP pre-flight, the knowledge-base search overlapped with the build, the long-term-memory lookup overlapped with the message writes). Voice is priced per modality and users can choose the Nova 2 Sonic voice and microphone. Claude Opus 5.5, GPT-6 Sol, GPT-6 Luna and GPT-5.5 join the catalog. Managed KBs cap imports and re-ingest changed synced files. A turn-path smoke and regression pass runs nightly. Strands 1.57.2 with bedrock-agentcore 1.24.0. - CDK deploy required (turn-latency dashboard widgets only); no GSI changes, no backfill - Operator step: add the Nova 2 Sonic catalog row per environment, or voice stays unmetered - Optional: repair_managed_kb_byte_counters.py --settle-unexplained for a KB the 1.25.0 repair left for review - Six new default-on rollout kill switches, tracked for retirement in #1422 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| finished.append(True) | ||
| return result | ||
|
|
||
| managed = managed_registered(_Tracked([_chunk("doc-managed-1")], delay=0.2)) |
| import inspect | ||
| import pathlib | ||
|
|
||
| import agents.main_agent.voice_agent as va |
| """The usage-detail subclass only exists with the bidi extra installed.""" | ||
|
|
||
| def test_subclass_overrides_the_converter_the_provider_still_has(self): | ||
| import agents.main_agent.voice_agent as va |
| """ | ||
| import inspect | ||
|
|
||
| import agents.main_agent.voice_agent as va |
| assert ours == theirs | ||
|
|
||
| def test_converter_keeps_the_split_on_the_usage_event(self): | ||
| import agents.main_agent.voice_agent as va |
| assert d["modality_details"][0]["input_tokens"] == 1_000 | ||
|
|
||
| def test_converter_leaves_other_events_to_the_provider(self): | ||
| import agents.main_agent.voice_agent as va |
| reaches the model. | ||
| """ | ||
|
|
||
| import asyncio |
| path. | ||
| """ | ||
|
|
||
| import asyncio |
| import threading | ||
| from concurrent.futures import ThreadPoolExecutor | ||
| from typing import Any, List | ||
| from unittest.mock import MagicMock, patch |
1 of 2 tasks
CodeQL py/log-injection on the release PR: send_audio logged the sample_rate the voice WebSocket passes through unvalidated, so a crafted value could forge a log line. Log only the configured rate. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Release 1.26.0
The first token arrives sooner. The turn path is now mapped and timed end to end, and the largest stages we own are off the critical path. Dev figures:
Also in this release:
Full write-up:
RELEASE_NOTES.md(v1.26.0) andCHANGELOG.md.Pre-merge checks
check-gsi-update-limit.mjsPASSED. 29 tables compared, and no existing table needs more than one GSI operation. There are no index changes at all.check-pending-backfills.mjsfound nobackfill_*.pyadded in this range.sync-version.sh --checkPASS. The lockfile diffs change only the version.Operator steps (also in the Deployment notes)
platform.yml→backend.yml→frontend-deploy.yml). The only infrastructure change is new widgets on the turn-latency dashboard.repair_managed_kb_byte_counters.py --settle-unexplained --agent <id>, after the deploy.CodeQL
sample_rate, a log-injection risk. Fixed ondevelopby fix(voice): don't log the client-supplied sample rate #1425 and merged into this branch.requires_oauth_provider), 869 (json.dumpsescapes CR/LF), 905–912 (Protocolstub bodies) and 891 (import used in quoted annotations).Behaviour change
After merge
main→developas a PR, using a merge commit.🤖 Generated with Claude Code