Skip to content

Release/1.27.0 - #1520

Merged
philmerrell merged 3512 commits into
mainfrom
release/1.27.0
Oct 9, 2026
Merged

philmerrell merged 3512 commits into
mainfrom
release/1.27.0

Conversation

@philmerrell

@philmerrell philmerrell commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Release 1.27.0. It carries everything merged into develop since 1.26.0 (#1423 through #1519). The full narrative and the operator steps are in RELEASE_NOTES.md; the log is in CHANGELOG.md.

What ships

Release checks

  • GSI update limit: PASSED. 29 tables compared; no existing table gains more than one index. No new tables or GSIs.
  • Pending backfills: both new scripts are named in the release notes, with commands (backfill_session_search_attributes.py, backfill_conversation_archive.py).
  • Version: sync-version.sh --check PASS. The lockfile diffs are version lines only.

Before merging

  • Voice on dev. Confirmed working by Phil on 2026-10-09 (fix(voice): take the voice identity from the upgrade token, never a claimed user_id #1495).
  • /smoke-regression against dev (2026-10-09). L0: backend 12,359 passed, frontend 4,489 passed, infra 1,072 passed. L1 (Runtime-direct): 10 PASS / 0 FAIL / 4 SKIP, plus GLM 5 (text-only attachments) and GPT-6 Luna (new retry path), 5/5 each. L2: first and second turn, restore, search (lexical, full text about a minute after the turn, jump-to-message), Stop, the 409, the question picker, and dark mode at phone width all OK. Two non-blocking findings filed as follow-ups: a resumed turn that ends with no reply archives under the wrong key, and the SPA silently drops a queued follow-up that gets a 403.
  • production GitHub environment: CDK_CONVERSATION_INDEX_ENABLED=true set 2026-10-09; CDK_PROJECTS_ENABLED=false unchanged.

After merging

  1. The Platform and Backend deploys race for one slot. If deploy-conversation-index-code or deploy-memory-maintenance-code skipped, re-run it after Platform succeeds. Confirm the conversation-index Lambda runs the real image before the archive backfill; the bootstrap stub fails every queue message.
  2. Run backfill_session_search_attributes.py (dry run, then --apply).
  3. Start backfill_conversation_archive.py the same day. It is a race against the 90-day expiry of events written before this release.
  4. Check voice in prod.
  5. Backmerge main → develop (merge commit, not squash).

🤖 Generated with Claude Code

philmerrell and others added 30 commits October 5, 2026 10:16
…ser-guide

docs(projects): end-user guide + team-simulation test report
"Continue in my own task" copied the share snapshot's display shape into
the fork's Converse history. Four defects followed:

- Document/image blocks went in with no `source`, so Bedrock rejected every
  turn once the agent was rebuilt. They now become one line naming the
  attachments that were not copied; images/documents inside tool results
  become text placeholders.
- No AGENT record was written, so Strands' initialize() took the new-agent
  branch and the fork's first turns ran on an empty history. Export now
  writes the record (conversation-manager state the runtime's manager
  restores) before the messages.
- A user message carried the model-facing prompt (RAG context, notes, the
  attachments marker). It now carries the author's displayText.
- Session files are not copied (they are the owner's uploads); the note
  says so, for the forker and the model alike.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Forks created before the export fix hold document/image blocks with no
`source`, and Bedrock fails every turn on them. Restore now replaces such
blocks (top level and inside tool results) with a text placeholder. Healthy
blocks pass through as the same objects, and the repair is a pure function
of the stored block, so restored history stays byte-stable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ning-throughput

fix(kb-migration): release the worker lease per step; hand born-managed backlog to the consumer
…xing

Neither ingestion engine understands w:ins / w:del. Managed knowledge bases
parse the raw .docx with Bedrock's parser, which flattens a redline (B9 saw
deleted and inserted wording concatenated; an inline parse keeps only the
insertions). Legacy ingestion goes through Docling and python-docx, whose
Paragraph.text only reads direct-child runs, so both sides of every change
vanish.

kb_backend/docx_revisions.annotate_tracked_changes rewrites the package so each
revised span is ordinary text — [deleted: ...] / [inserted: ...] — adds a note
paragraph naming the authors, and inlines comments at their anchor. Each
engine's own parser still handles the rest of the document. Stdlib only, so it
fits both Lambda images unchanged.

Managed: a .docx with tracked changes is sent as inline BYTE content (capped at
3,932,160 raw bytes by ByteContentDoc's base64 limit); anything else, and any
failure, ingests from S3 exactly as before. Legacy: Docling converts the
annotated copy. Documents without tracked changes keep their original bytes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…red-conversation-export

fix(shares): make a shared-conversation fork usable from its first turn
Only a space's MEMORY.md is injected into a project task, and indexing was
a separate save the model usually forgot: in the 2026-10 team simulation
two of three team files and a personal preference were orphaned (G2/G17).

The harness memory_save now appends "- [[slug]] — description" to the
scope's index when it creates a file (version 1), through a new
MemorySpaceService.add_index_link: skipped when the index already links
the file or its alias, or when the line would push the index past the
scope's injection budget (2k project, 1k mine), with a nudge instead. The
META write is conditional on the index hash, so concurrent creates keep
both lines. Updates never touch the index. Ordinary Agents' memory tools
are unchanged (spec hashes still pinned).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A project's harness had no idea who was talking to it and told the
export-control specialist to "send this to" her own name (G18 of the
2026-10 team simulation).

Each harness turn now prepends a short <project_member> note (name, email,
project role) to the user message, the same position as the interruption
note: persisted in append-only history, hidden from the UI by the
displayText split, and never in the system prompt or toolConfig, which
every member shares. The role is the one the access check already
resolved, so the turn makes no extra read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…mory-index-and-speaker

fix(projects): auto-index new memory files and tell the harness who is speaking
…-tracked-changes

fix(kb-ingestion): write Word tracked changes out as text before indexing
A "just for me" preference saved in one project task wasn't applied in the
next (G17 of the 2026-10 team simulation). After #1432 indexed new files,
two causes remained, both reproduced on dev with Haiku 4.5:

- Asked to "remember this just for me", the harness never called
  memory_save: it kept the preference "in my context for this session
  only". memory_save now says to save such a request to "mine" right away.
- A saved preference whose index line only named it ("My status-check
  reply format") was not read in the next task. memory_save now asks for a
  preference's description to be the rule itself, so the auto-indexed line
  carries it, and the "mine" block's intro says to follow its preferences.
  The intro also names the member instead of "you", which the model read
  as its own memory (the confusion G18 hit).

Local A/B on dev data, Haiku 4.5, 4 samples each with a unique reply token
per sample: develop saved 0/4 and applied 0/4; this change saved 4/4 and
applied 4/4. Changes the harness memory_save spec and the mine intro once;
ordinary Agents' memory tools and the agent block stay byte-identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The runnability preview diffed an Agent's skill bindings against the
viewer's own skill palette (role grants + skills they wrote). The run-time
resolver also admits skills the Agent's owner wrote (invoke-through), so
every owner-authored skill read as "missing" for anyone the Agent was
shared with. The detail page then disabled Launch and the chat showed
"isn't available to your account" for an Agent that would have run.

Skills now diff against resolve_invocable_skill_ids, the same predicate
agent_binding_resolver uses, so the preview and the turn agree.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Invoke-through (Skills v2 §6/D7) resolves an Agent owner's own bound skills
for anyone the Agent is shared with, and those people can get the model to
show the skill instructions. Only the marketplace submit dialog said so; the
share dialog, where the author adds people or sees the Agent is PUBLIC, did not.

- listing_service._exposed_skills becomes exposed_skills, the one rule both
  disclosures read (owner_id == agent.owner_id, same as the turn path).
- New owner-only GET /agents/{id}/shares/exposed-skills (session-cookie auth,
  gated on AGENTS_API_ENABLED only, not the marketplace flag). It applies that
  rule to what a share recipient runs: the published snapshot when there is
  one, falling back to the live record if the snapshot cannot be read.
- The share dialog lists those skills once the Agent reaches anyone (PUBLIC,
  or someone on the working list, so it appears before Save), in a live region.

Read-only: neither resolve_accessible_skill_ids (the viewer's picker) nor
resolve_invocable_skill_ids (the turn path) is touched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t .docx

Dev validation of #1431 showed that a managed knowledge base provisioned with
image extraction enabled (every one this platform creates) parses an uploaded
.docx through a model that treats the [deleted: ...] markers as instructions:
"[deleted: Idaho]" was indexed as "[deleted: ]", "[deleted: Ada County, Idaho]"
as "[deleted: Ada County, o]", and the note's "...]" placeholders vanished.
The markers survived; the deleted wording did not.

The managed path now sends docx_revisions.annotated_text — the rewritten
document as plain text, tables one line per row, footnotes after the body — as
inline TEXT content, which Bedrock chunks as given. Re-ingested on dev, all
three redlines came back verbatim. Docling (legacy) still converts the
rewritten .docx, which it reads deterministically.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…mory-preference-recall

fix(projects): make a member's personal preference persist and apply (G17)
…inline-text

fix(kb-ingestion): send annotated redlines to managed KBs as text, not .docx
The dev team simulation (docs/testing/projects-team-simulation-2026-10.md,
gaps G5, G6 and G15) showed team knowledge stranded in shared tasks: the
assistant could not read them and nobody was told they existed, so
counsel's escalation summary listed 2 of 7 items.

Split 2.5 into 2.5a (memory governance, unchanged scope), 2.5b (opt-in
notifications and a note when sharing a task to the project) and 2.5c
(harness-only tools that list and read shared-task snapshots, bounded,
with constant specs so the cached prefix and time to first token are
untouched). Data model, API, agent-tool and configuration tables
updated to match.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The shared view passed snapshot messages straight to the message list,
skipping the toolResult->toolUse fold the session page runs on load, so
every call kept its default pending status and the rail read "Running
memory read". Move the fold into a pure helper and run it (plus the
steering normalization) on the snapshot too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A project share is listed once per task, newest wins, so "Creating a new
share will add another snapshot" was wrong for Project members when the
task is already shared with the project. Public and limited shares keep
the existing copy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The gate also refuses continuing an existing task, so "can't start new
conversations" misdescribed it. Say the project is read-only instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-5-collaboration

docs(projects): plan share notifications and shared-task reading in 2.5
The Shared Projects user guide (#1428) described several bugs as limits.
They have since been fixed: fork history and attachments (#1430), KB
throughput for a new project (#1429), tracked changes in Files (#1431,
#1436), memory auto-indexing and member identity (#1432), and personal
preferences (#1433). The guide now describes the current behavior. The
contract-negotiation walkthrough keeps the test run as it happened and
marks each lesson that has been fixed since.

The simulation report gains a Status section mapping findings to the PRs
that fixed them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Picking a different model in an open conversation starts a new one, and it
navigated to a bare `/`, dropping the conversation's `assistantId`. In a
Shared Projects task that left the project silently: the new chat had no
project assistant, instructions, files or memory (B8 in the 2026-10 projects
team simulation). An agent session lost its agent the same way.

The new conversation now keeps the URL's `assistantId`, so a project task
starts a new task in the same project and an agent session a new session with
the same agent. The menu says so before the choice ("Choosing another model
starts a new task in this project."), and each row's pill reads New task /
New session / New chat to match. A project or agent that pins a model already
locks the picker, so that case is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lish-b7-g20-g21

fix(projects): snapshot tool status, re-share and archived copy (B7, G21, G20)
…ors, drop user ids from memory

G8: Projects responses carry a display name beside every email (members,
project owner, shared tasks, files, settings history, the audit page's
`people` map, and the inbox's `actorName`), resolved from the directory's
per-process users-table snapshot rather than per-row reads. Email stays the
key; the name is null when the directory has none, and the SPA falls back to
the email (`personLabel`), with the email secondary or on hover. A directory
failure shows emails and never fails the request.

G9: `hasSignedIn` now means signed in to the platform (the member row is
bound, or the directory knows the email), not "has opened this project".
Transfer binds an unbound editor to the directory's account for their email
(the most recent sign-in when the users table holds duplicates), so "Make
owner" works before they open the project; an editor who has never signed in
is labelled "Can become owner once they've signed in". The access-time
back-fill is unchanged and still never overwrites.

G12: memory-spaces `updatedBy` is the editor's email, plus `updatedByName`,
for entries and file history; a project's space returns no `ownerId`. No SPA
code read either field.

The directory snapshot now loads off the event loop (a sync users-table page
read run in a thread, single-flight under a lock) and keeps the held snapshot
when a refresh reads empty.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…uide-fixed-limits

docs(projects): stop listing fixed bugs as current limits
…h-keeps-project

fix(spa): keep a project task's project when switching models
…someone leaves

Archiving or restoring a project now notifies every member (pending invitees
included) through a new batched NotificationService.notify_many; leaving
notifies the owner, and the bell opens the Members tab. Pulled forward from
2.5b's deferred list (G20): both events are rare, so they need no per-member
preference. A failed member read or write is logged and never fails the
archive.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
create_share already fetched the session's tool summaries from get_messages
and dropped them, so a snapshot's rails fell back to the client-side wording
("Ran memory read"). Store them in the snapshot body, return them as
toolSummaries, and seed them in the shared view under a share:<id> key that
the assistant message reads in place of the viewed session. The view now
reads the S3 body once instead of twice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-archive-leave-notifications

feat(projects): notify on archive/restore and when a member leaves (G20)
philmerrell and others added 24 commits October 8, 2026 17:12
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nment

CDK_AGENTCORE_RUNTIME_PLATFORM_VERSION (inferenceApi.runtimePlatformVersion)
selects V1 (default) or V2 for the inference-api Runtime. Precedence is
env var > flat context > nested context > V1, and an unset GitHub
variable ('') falls through to V1. Any other value fails synth, since the
CloudFormation schema accepts any non-blank string.

PlatformVersion is written on every synth, V1 included, so an existing
stack is unchanged and a V2 -> V1 rollback is an in-place update rather
than a property removal. aws-cdk-lib 2.265.0 has no typed property, so
it goes through addPropertyOverride.

platform.yml forwards the variable; ACTIONS-REFERENCE, the environments
docs page, the V2 spec (plan step 2) and CHANGELOG are updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-deploy-preserve-platform-version

fix(deploy): keep Runtime platformVersion through image deploys
…runtime-v2-flag

# Conflicts:
#	docs/specs/agentcore-runtime-v2.md
AgentCore Runtime V2 starts a session by restoring a snapshot of a booted
container, which restores the idle origin runtime_health stamped before
the snapshot. /ping would then report a session that has served nothing
as idle for as long as the snapshot is old, and the reaper could shut it
down at once.

The platform polls /ping every ~2s, so a gap of more than 60s since the
last poll (or since import, before any poll) means the process was not
running. The first poll after such a gap restarts the idle clock and logs
it, at most once per process, so irregular polling on V1 can cost at
most one extra idle period and can never make a microVM immortal.

Tests that skipped an hour of idle time in one jump now poll every 2s,
since a single jump is exactly what a restore looks like.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-health-restore-safe-idle-clock

fix(inference-api): restart the idle clock after a snapshot restore
…runtime-v2-flag

# Conflicts:
#	docs/specs/agentcore-runtime-v2.md
…re-runtime-v2-flag

feat(infra): choose the AgentCore Runtime platform version per environment
…-6c-dev-validation

docs(spec): record Shared Projects 2.6c's dev validation
… plan

The first dev V2 attempt failed: V2 caps a runtime's environment
variables at 2,560 bytes, and dev's are 3,007 by AWS's count (prod is
over too). The CFN rollback also failed, because omitting PlatformVersion
keeps the runtime on V2, which answers an open question in the spec.

- B4: the limit, both environments' numbers, and the recovery runbook
  used on dev (restore V1 directly, continue-update-rollback skipping
  the runtime, re-run the platform deploy).
- §7: derive the ~30 prefix-plus-suffix names in apis.shared behind an
  env-var override, kept in step with CDK by a manifest test and an
  app-api drift warning, plus a 2,000-byte payload guard in CI. Dev
  would go from 49 variables / 2,938 bytes to ~19 / ~755.
- Plan: the refactor becomes step 4 (two PRs), before the V2 retry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…re-ssrf-hardening

fix(security): connected-peer check for fetch_url_content, harden browse_web URL check
…runtime-v2-env-payload

docs(specs): V2 env-var payload limit, recovery runbook, and refactor plan
… mode

apply_runtime_session_header now takes (headers, session_id, user_id)
because runtime affinity is pinned per (user, session). RuntimeTransport
still called it with two arguments, so every --auth headless-grant run
failed on its first turn with a TypeError.

Adds a hermetic test that drives RuntimeTransport.stream_turn through a
spy that calls the real helper, so a future signature change fails CI
rather than a dev validation pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…urns-affinity-user

fix(smoke): pass user id to runtime affinity header in headless-grant mode
… Projects 2.7)

A deterministic pattern check (spec §4.3 step 6, §9.3) over text entering a
project's memory: instruction-like phrasing, prompt and tool-call markup,
credential-shaped strings, and the deployment's MEMORY_SENSITIVE_PATTERNS.
MEMORY_LINT_MODE is warn by default, block refuses a new finding
(lint_blocked), off skips it. It applies to a project's spaces only, so it
rides PROJECTS_ENABLED.

It runs in _prepare_save, so every write path gets it: edits, memory_save
and memory_propose, approvals, archive and history restores, and
maintenance. Only text a save adds or changes is read, items a split moves
are skipped, and a finding the item already had only warns.

Findings travel on the save's response and the tool result, and are
recomputed on read for the file view and a proposal's detail. Nothing is
written into memory, so the injected index, memory_read and the tool specs
are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d Projects 2.7)

GET /projects/{id}/memory/export?scope=project|mine (viewer+; mine is the
caller's own) is the space export plus provenance.json: per file its path
in the zip, version and pinned anchors, and each item by anchor with its
provenance (added, updated, source task, proposal, approver, restore, move).
People are emails, never user ids.

The ordinary space export carries the same file for any item-format space.
The zip code moves to memory_spaces/export_zip.py so both routes share it,
and metadata.json gains scope and projectId for a project space.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…them into MEMORY_ARN's slot

CDK_MEMORY_LINT_MODE (off | warn | block, default warn) and
CDK_MEMORY_SENSITIVE_PATTERNS are parsed and checked at synth (an unknown
mode or a malformed list fails the deploy; the list is capped at 3,000
characters), forwarded in platform.yml, and set on app-api and the
maintenance worker as MEMORY_LINT_MODE / MEMORY_SENSITIVE_PATTERNS.

The AgentCore Runtime was at 49 of its 50 environment variables. MEMORY_ARN
was only logged at startup (AGENTCORE_MEMORY_ID is what code reads), so it is
retired and its slot carries both settings as one JSON value, MEMORY_LINT.
The Runtime stays at 49. No new resources and no IAM change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t it (Shared Projects 2.7)

The file view puts a "Content check" line under a flagged item and under a
flagged description. A proposal's review shows a banner for what it adds
and marks the flagged item; a tidy-up names the item by its first words.
A flagged save, index save or proposal toasts a warning instead of a
success, and a block refusal is the editor's existing alert.

Export beside Archive downloads the open scope's memory as a zip with
provenance.json.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The spec gains 2.7's as-built entry: scope, rules, what is read, why
restores and maintenance are re-read, what "annotate" means, how the
Runtime gets its settings, why the per-project override waits for 4.2, the
measured cost and the dev validation to run. §3.1, §4.3, §5, §8, §9.3 and
§9.9 record what the build settled. The admin Projects page documents the
two variables and the export; the work-together guide explains the check
and Export to members; CHANGELOG entries for both and for MEMORY_ARN.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e's environment

V2 caps a Runtime's environment at 2,560 bytes (agentcore-runtime-v2.md B4),
and a deployment's patterns can run to 3,000 characters, so they can't ride
in MEMORY_LINT. CDK now puts them in /{prefix}/memory/sensitive-patterns,
created only when patterns are set (no resource otherwise; the stack stays
at 460), and MEMORY_LINT carries the mode plus "patterns": "ssm:<hash>". The
hash rolls the Runtime when the patterns change.

The Runtime reads the parameter once per process, on its first project-memory
save, through the /{prefix}/* SSM read its role already has. A failed read
is logged and retried a minute later, with the built-in rules still applying.
By default MEMORY_LINT is 26 bytes, against roughly 90 for the MEMORY_ARN it
replaced.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s-memory-lint-provenance

feat(projects): memory content lint and provenance export (Shared Projects 2.7)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-7-dev-validation

docs(spec): record Shared Projects 2.7's dev validation
Conversation search ships default-on (lexical; full text with the opt-in
conversation index), conversation retention rises from 90 to 365 days with
a per-turn archive and messages-route fallback, two security fixes
(cross-user exposure on a shared session id, voice identity), transient
retries on every model provider, metering for failed and interrupted turns,
and Shared Projects (still off by default) completes its memory phase and
gains Outputs.

- Bump VERSION to 1.27.0 and sync manifests and lockfiles
- CHANGELOG: fold #1506's default-on change into the conversation-search
  entries, drop the sidebar title-filter removal (added and removed within
  this release), and add entries for the PRs that shipped without one
- RELEASE_NOTES: 1.27.0 with deployment notes for the two backfills
  (backfill_session_search_attributes.py, backfill_conversation_archive.py),
  the new Lambdas' code-deploy re-run, and the voice check

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@philmerrell
philmerrell requested a review from a team October 9, 2026 02:41
Comment on lines +249 to +253
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ inputs.ref }}
persist-credentials: false
- name: Toolchain
replaced = files.replaced(project_id, user, scope, slug)
except Exception:
# The markers are a courtesy: the file still reads without them.
logger.warning("Could not read what the items of %s replaced", slug, exc_info=True)
except _ERRORS as e:
raise _translate(e)
except MemorySpaceStoreError:
logger.error("projects: memory export failed to read the store for project=%s", project_id)
self._memory = memory
self._projects = projects
self._notifications = notifications
self._planner_factory = planner_factory or (lambda model_id: Planner(model_id))
logger.error("Could not read the memory sensitive patterns from SSM (%s); retrying in a minute", type(exc).__name__)
_ssm_failed_at = time.monotonic()
return None
_ssm_failed_at = None
response = boto3.client("ssm", region_name=region).get_parameter(Name=f"/{prefix}/{PATTERNS_SSM_SUFFIX}")
except Exception as exc: # noqa: BLE001 - a lint setting never fails a save
logger.error("Could not read the memory sensitive patterns from SSM (%s); retrying in a minute", type(exc).__name__)
_ssm_failed_at = time.monotonic()

def artifact_session_id(owner_id: str, artifact_id: str) -> str:
"""The task an artifact was made in, from its HEAD row in the owner's partition ("" if unknown)."""
from .service import _table
owner_id: str, artifact_id: str, project_id: str, repository: Optional[ProjectRepository] = None
) -> None:
"""Point ``OUTPUT#{artifact_id}`` at the artifact's newest share with ``project_id``, or remove it."""
from .service import ArtifactShareService
# Same metering as the in-loop error arm, unless the success
# block (or that arm) already took this turn's usage.
if not turn_usage_recorded:
turn_usage_recorded = True
# A disconnect can land while the success block or a failure arm
# is already writing this turn's cost rows; never meter twice.
meter_usage = not turn_usage_recorded
turn_usage_recorded = True
# re-raising, so a still-connected client sees a proper close and
# the route's finally releases the lease.
meter_usage = not turn_usage_recorded
turn_usage_recorded = True
@philmerrell
philmerrell merged commit ab585b0 into main Oct 9, 2026
19 checks passed
@philmerrell
philmerrell deleted the release/1.27.0 branch October 9, 2026 03:37
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.

3 participants