Skip to content

[FRD 0008 U3] Complete ACA sandbox GA qualification - #160

Draft
Laveesh Rohra (larohra) wants to merge 36 commits into
feature/aca-sandboxesfrom
larohra-u3-ga-gate
Draft

[FRD 0008 U3] Complete ACA sandbox GA qualification#160
Laveesh Rohra (larohra) wants to merge 36 commits into
feature/aca-sandboxesfrom
larohra-u3-ga-gate

Conversation

@larohra

@larohra Laveesh Rohra (larohra) commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Summary

Completes U3, the final GA qualification unit for the ACA Sandbox Session Runtime from FRD 0008.

  • Enables the opt-in ACA backend on supported Linux x86_64 Python 3.13/3.14 hosts while preserving the default in-language-worker path.
  • Proves real Azure create, content delivery, bootstrap, model execution, event/result flow, suspend/resume/reclaim, and backing-loss recovery through deployed Easy Auth-protected Function Apps.
  • Hardens provider-state projection, lost-backing reconciliation, bounded provisioning retries, transient file-plane recovery, timer reconciliation deadlines, and deterministic Sandbox Group authorization failures based on live-provider findings.
  • Extends setup/create allowance from 30 to 90 seconds, uses one flat sliding 120-second lease for every durable controller operation, returns Retry-After: 120, and keeps the 180-second synchronous wall cap.
  • Emits typed, redacted setup-timeout telemetry with allowlisted phase/reason/timing fields across HTTP and built-in MCP paths; no prompt, owner/session/backing identifiers, token, provider body, or result is recorded.
  • Emits structured successful timer-pass summaries and per-session reclaim/tombstone audit events without changing lifecycle, cadence, retention, pruning, or Table schema.
  • Adds cleanup-safe N=5 orchestration diagnostics, customer-path cold-start measurement, and a Manual/Scheduled nonblocking Python 3.13/3.14 matrix.
  • Binds test doubles to real provider/store contracts so unit tests cannot silently drift from SDK or lease semantics.
  • Updates FRD decisions, architecture, observability, operator guidance, CI bounds, and live-test runbooks with GA evidence and remaining human-only gates.

Real Azure evidence

  • ADO 297517: deployed public agent turn.
  • ADO 297555: auto-suspend, same-sandbox resume, timer reclaim/tombstone, zero leaks.
  • ADO 297702: Python 3.13 N=5 common-active/replay/conflict/cleanup and three-sample cold-start qualification.
  • ADO 297727: Python 3.14 automatic public-disk pairing, model turn, lifecycle, backing loss, and cold-start qualification.
  • ADO 297752 and 297839: both Python 3.13/3.14 cold-start legs passed; the parallel full deployed legs exposed shared Sandbox Group contention and remain nonblocking reliability evidence rather than a claimed pass.
  • Production diagnosis on func-agent-func-twm2hp52kchdm: controller Contributor produced repeatable data-plane 403/setup-504 loops; granting Container Apps SandboxGroup Data Owner changed list to 200 and the public model-backed turn completed with E2E_OK. The runtime now fails that misconfiguration fast, settles durable state, and maps submit/cancel/resume to the same redacted outcome.
  • Real file-plane probe: digest-verified single writes through 80 MiB, covering the current approximately 76 MiB dependency closure without chunking.

Observed fresh-session customer-path timing before the timeout expansion:

Runtime Acceptance First event Terminal
Python 3.13 p50 14.1s; p95/max 23.6s p50 15.6s; p95/max 25.1s p50 18.4s; p95/max 29.3s
Python 3.14 24.9s 26.7s 30.3s

Safety boundaries

  • AgentExecutionBackend remains exactly four methods.
  • ACA remains opt-in; the default execution path is unchanged.
  • The runtime attaches no identity and forwards no controller token, storage credential, or state-writing capability to the sandbox.
  • transport/aca_sdk.py remains the only production preview-SDK import boundary.
  • /var/lib/azurefunctions-agents-runtime remains the sole sandbox root.
  • N=5 is the only automated/agent-run load diagnostic. N=100 remains human-only, single-runtime, and explicitly guarded.
  • Live jobs remain Manual/Scheduled, nonblocking, and excluded from PR-required checks.

Validation status

  • Python 3.14 CI-equivalent gate at 7aa4c424: Ruff clean, strict mypy clean for 97 files, 2038 passed / 64 skipped / 82 deselected, coverage XML generated.
  • Setup-timeout coverage includes all typed phases/mappings, full and short authored budgets, initial/conflict reconciliation anchoring, provider poll-budget recomputation, flat lease renewal/takeover semantics, HTTP/MCP exactly-once observation/redaction, and live watchdog arithmetic.
  • Azurite lease tests are committed but were skipped locally because the service was unavailable. Paid live Azure tests and N=100 were not run for this change.

Remaining before ready for merge

  • Run the updated deployed N=5/cold-start qualification with the 90/120 policy and confirm timeout telemetry plus zero provider/durable leaks.
  • Human-run N=100 formal acceptance on exactly one selected runtime; this PR intentionally does not claim it has passed.
  • Accumulate the scheduled-smoke reliability evidence required before any future promotion to a blocking check.

Target: feature/aca-sandboxes. The eventual feature-to-main merge remains a separate human decision.

Laveesh Rohra (larohra) and others added 30 commits August 12, 2026 09:59
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a6242d7f-3c99-4dd6-b29d-3f43731e2245
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a6242d7f-3c99-4dd6-b29d-3f43731e2245
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a6242d7f-3c99-4dd6-b29d-3f43731e2245
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a6242d7f-3c99-4dd6-b29d-3f43731e2245
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a6242d7f-3c99-4dd6-b29d-3f43731e2245
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a6242d7f-3c99-4dd6-b29d-3f43731e2245
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a6242d7f-3c99-4dd6-b29d-3f43731e2245
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a6242d7f-3c99-4dd6-b29d-3f43731e2245
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a6242d7f-3c99-4dd6-b29d-3f43731e2245
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a6242d7f-3c99-4dd6-b29d-3f43731e2245
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a6242d7f-3c99-4dd6-b29d-3f43731e2245
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a6242d7f-3c99-4dd6-b29d-3f43731e2245
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a6242d7f-3c99-4dd6-b29d-3f43731e2245
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a6242d7f-3c99-4dd6-b29d-3f43731e2245
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a6242d7f-3c99-4dd6-b29d-3f43731e2245
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a6242d7f-3c99-4dd6-b29d-3f43731e2245
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a6242d7f-3c99-4dd6-b29d-3f43731e2245
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a6242d7f-3c99-4dd6-b29d-3f43731e2245
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a6242d7f-3c99-4dd6-b29d-3f43731e2245
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a6242d7f-3c99-4dd6-b29d-3f43731e2245
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a6242d7f-3c99-4dd6-b29d-3f43731e2245
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a6242d7f-3c99-4dd6-b29d-3f43731e2245
Run Python 3.13 and 3.14 cold-start qualification before the deployed N=5 diagnostics, while retaining manual/scheduled nonblocking policy and human-only single-runtime N=100 safeguards.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a6242d7f-3c99-4dd6-b29d-3f43731e2245
Reduce shared-group matrix contention, classify live SSE failures without reading bodies, preserve admission deadline evidence, and close review gaps in lost-backing snapshot cleanup, docs, and boundary coverage.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a6242d7f-3c99-4dd6-b29d-3f43731e2245
Translate Sandbox Group 401/403 responses into a redacted runtime error, atomically settle reserved provision state, preserve deterministic idempotent replay, and document the controller Data Owner requirement.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a6242d7f-3c99-4dd6-b29d-3f43731e2245
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a6242d7f-3c99-4dd6-b29d-3f43731e2245
Emit structured timer-pass summaries and per-session reclaim audit events after successful durable settlement, with focused coverage and operator KQL.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a6242d7f-3c99-4dd6-b29d-3f43731e2245
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a6242d7f-3c99-4dd6-b29d-3f43731e2245
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a6242d7f-3c99-4dd6-b29d-3f43731e2245
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a6242d7f-3c99-4dd6-b29d-3f43731e2245
Use a 90-second setup budget with renewable 120-second provision leases and retry guidance, add typed redacted timeout observability across HTTP and MCP paths, and align live qualification watchdogs.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a6242d7f-3c99-4dd6-b29d-3f43731e2245

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Completes ACA Sandbox GA qualification by enabling the opt-in backend, hardening lifecycle behavior, and adding deployed qualification coverage.

Changes:

  • Enables supported Linux x86_64 Python 3.13/3.14 execution.
  • Adds timeout, authorization, reconciliation, and audit handling.
  • Expands unit, Azurite, live Azure, documentation, and pipeline coverage.

Reviewed changes

Copilot reviewed 80 out of 80 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/test_transport_ports.py Updates SDK client contract coverage.
tests/test_transport_models.py Tests sandbox-state validation.
tests/test_transport_aca_sdk.py Covers reconciliation and authorization handling.
tests/test_session_runtime_validation.py Tests supported-host startup.
tests/test_registration_handlers.py Tests timeout telemetry emission.
tests/test_registration_endpoints.py Covers MCP ACA timeout behavior.
tests/test_observability.py Tests runtime event spans.
tests/test_harness_bootstrap.py Updates delivered import-path coverage.
tests/test_fake_session_runtime.py Tests operation lease selection.
tests/test_execution_setup_budget.py Tests the 90-second setup budget.
tests/test_convention_guards.py Enforces typed setup deadlines.
tests/test_controller_readiness.py Covers provisioning reconciliation and authorization.
tests/test_controller_http.py Tests timeout and authorization responses.
tests/test_controller_budget.py Tests setup deadline origins.
tests/test_app.py Covers timer deadlines and summaries.
tests/test_aca_smoke_support.py Tests live-package and model configuration.
tests/test_aca_deployed_runtime_matrix.py Validates deployed runtime matrices.
tests/live/test_aca_real_agent_turn.py Adds lower-level live model qualification.
tests/live/test_aca_deployed_loss.py Adds deployed backing-loss qualification.
tests/live/test_aca_deployed_lifecycle.py Adds deployed lifecycle qualification.
tests/live/test_aca_deployed_agent_turn.py Adds authenticated public-turn qualification.
tests/live/aca_deployed_loss_support.py Defines backing-loss assertions.
tests/live/aca_deployed_load_support.py Adds load metrics and safeguards.
tests/live/aca_deployed_lifecycle_support.py Adds lifecycle observation helpers.
tests/live/aca_deployed_cold_start_support.py Adds cold-start metrics and bounds.
tests/fixtures/live_aca_model_turn/model_turn.agent.md Defines the live model agent.
tests/fixtures/live_aca_model_turn/agents.config.yaml Configures the live model fixture.
tests/fixtures/live_aca_deployed_agent_turn/tools/qualification_hold.py Adds a load-test hold tool.
tests/fixtures/live_aca_deployed_agent_turn/host.json Configures deployed function timeout.
tests/fixtures/live_aca_deployed_agent_turn/function_app.py Defines the deployed fixture app.
tests/fixtures/live_aca_deployed_agent_turn/deployed_turn.agent.md Defines the public qualification agent.
tests/fixtures/live_aca_deployed_agent_turn/deployed_load.agent.md Defines the load qualification agent.
tests/fixtures/live_aca_deployed_agent_turn/agents.config.yaml Configures deployed ACA sessions.
tests/fixtures/live_aca_deployed_agent_turn/.gitignore Excludes packaged dependencies.
tests/fixtures/live_aca_deployed_agent_turn/.funcignore Excludes local deployment artifacts.
tests/fixtures/config_scenarios/29_aca_sandbox_valid/main.agent.md Adds a valid ACA scenario.
tests/fixtures/config_scenarios/29_aca_sandbox_valid/agents.config.yaml Marks the scenario as valid.
tests/fixtures/config_scenarios/29_aca_sandbox_valid_but_unavailable/main.agent.md Removes obsolete gated fixture.
tests/endtoend/test_session_state_store_azurite.py Verifies persisted lease durations.
tests/doubles/fake_session_runtime.py Aligns fake lease semantics.
tests/conftest.py Adds live qualification options.
src/azure_functions_agents/transport/transport_models.py Adds provider state and reconciliation models.
src/azure_functions_agents/transport/aca_sdk.py Hardens ACA SDK reconciliation and authorization.
src/azure_functions_agents/session_state/store.py Applies kind-specific operation leases.
src/azure_functions_agents/session_state/session_models.py Adds provisioning reconciliation state.
src/azure_functions_agents/registration/endpoints.py Routes MCP calls through ACA execution.
src/azure_functions_agents/registration/_handlers.py Emits setup-timeout observations.
src/azure_functions_agents/execution/setup_budget.py Adds typed 90-second setup budgets.
src/azure_functions_agents/execution/aca_sandbox.py Applies setup deadlines across execution.
src/azure_functions_agents/execution/__init__.py Lazily imports ACA execution components.
src/azure_functions_agents/controller/reconciler.py Handles backing loss, suspension, and audits.
src/azure_functions_agents/controller/http.py Maps timeout and authorization responses.
src/azure_functions_agents/controller/budget.py Preserves setup timing metadata.
src/azure_functions_agents/config/validation.py Opens the supported ACA capability gate.
src/azure_functions_agents/app.py Adds dependency validation and timer deadlines.
src/azure_functions_agents/_observability.py Adds customer-queryable runtime events.
README.md Documents ACA backend availability.
eng/templates/official/jobs/e2e-tests.yml Adds deployed qualification jobs.
eng/ci/variables/aca-deployed-runtime-targets.yml Defines runtime deployment targets.
eng/ci/official-build.yml Forwards ACA qualification parameters.
eng/ci/e2e-tests.yml Configures the ACA runtime matrix.
docs/observability.md Documents timeout and reconciliation telemetry.
docs/front-matter-spec.md Updates ACA availability guidance.
docs/aca-sandbox-session-runtime.md Expands ACA deployment and operations guidance.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +368 to +371
except SessionActivationSetupTimeoutError as exc:
raise RunSubmissionIndeterminateError(
"Run launch may have started but journal acceptance was not confirmed."
) from exc
"The resumed public session did not become ready within the bounded "
"setup-deadline retry window."
)
await asyncio.sleep(_SETUP_RETRY_DELAY_SECONDS)
Comment on lines +1523 to +1528
_log_session_reclaimed(
session_id=latest.record.session_id,
sandbox_id=latest.record.sandbox_id,
backing_deleted=backing_deleted,
deleted_snapshot_count=deleted_snapshot_count,
)

@hallvictoria hallvictoria left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking findings

  1. [P1] Accepted create can be terminalized after a recovery 403 - src/azure_functions_agents/transport/aca_sdk.py:337. If _await_create_result() first raises a timeout/runtime error after begin_create was accepted, the generic recovery calls _find_failed_create_sandboxes() directly. A 401/403 from that lookup replaces the original error with SandboxGroupAuthorizationError; readiness treats it as deterministic, fails the run, and marks the session deleting even though the labeled sandbox may exist. This violates FRD Decision 160. Route every create_accepted recovery through _recover_stable_accepted_create() and add timeout-then-403 coverage.

  2. [P1] Setup cancellation can retry an accepted create as a second create - src/azure_functions_agents/controller/readiness.py:1618. _within_setup_budget() can cancel provider.create() after ACA accepts the request while the durable phase remains provision_create. After lease expiry, takeover retries with reconcile_only=False; if label-list visibility is delayed, the adapter issues another create with the same operation label. Persist provision_reconcile for this ambiguous timeout, or make stable-label retries reconcile-only until absence is authoritative, and test delayed list visibility.

  3. [P1] Status/result authorization failures escape as 500 - src/azure_functions_agents/execution/aca_sandbox.py:468. get_run() invokes targeted reconciliation here and on nonterminal reads. The reconciler performs initial list_sandboxes()/list_snapshots() calls outside its per-session handling, so provider 401/403 escapes as SandboxGroupAuthorizationError. read_status() and read_result() do not map it (or SessionActivationAuthorizationError) like submit/cancel do. Normalize this to the documented redacted sandbox_group_authorization_failed 503 and add both management-read tests.

  4. [P1] The explicit <=2-second p95 visibility gate is not enforced - tests/live/test_aca_deployed_load.py:341. The test computes submission-to-first-SSE-event percentiles and only renders them. It never asserts a threshold, and this end-to-end latency is not state-change-to-observation visibility at <=1 poll/s. Add timestamped state transitions plus bounded status/event polling and fail when p95 exceeds two seconds.

  5. [P1] The Python 3.13/3.14 matrix does not attest the remote runtime or artifact - eng/templates/official/jobs/e2e-tests.yml:230. The matrix changes the test client interpreter and static app URL, but never deploys the build or verifies the remote Python minor/commit/package digest. A stale app, two apps on one Python minor, or an app without this PR can satisfy both labeled legs. Deploy the built artifact per leg or require remote runtime and package-digest attestation before qualification.

I did not repeat the three existing Copilot findings. I also did not flag the Manual/Scheduled nonblocking policy itself because FRD Decisions 156-157 explicitly retain it.

Use one flat sliding 120-second lease for every durable operation kind while preserving fencing, takeover, and journal recovery semantics.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a6242d7f-3c99-4dd6-b29d-3f43731e2245
Comment thread eng/ci/e2e-tests.yml
- '5'
- '100'
- name: acaProvisionConcurrency
displayName: 'Deployed ACA provisioning concurrency'

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one does not work with "both". Add that to the name or somewhere so its evident.

Comment thread eng/ci/official-build.yml
- name: acaServiceConnection
displayName: 'ACA smoke: Azure service connection'
type: string
default: 'larohra-sandboxgroup-test'

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revert default to the original value

- name: acaServiceConnection
type: string
default: 'saf-foundry-connection'
default: 'larohra-sandboxgroup-test'

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revert default

- job: "ACADeployedAgentTurn"
displayName: 'Run deployed ACA agent-turn, lifecycle, and backing-loss qualification'
dependsOn: ACADeployedColdStart
condition: >-

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this condition, it can run in PRs too now

# The job default is the sole automated N=5 diagnostic. Both runtime
# legs share one Sandbox Group, so each may provision only one session.
# A human single-runtime N=100 request may use all four known safe slots.
case "${ACA_DEPLOYED_CONFIGURED_LOAD_CONCURRENCY:-}" in

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why only 5 or 100? Make this configurable to any value <=100

fi
export AZURE_FUNCTIONS_AGENTS_ACA_LOAD_CONCURRENCY="${ACA_DEPLOYED_CONFIGURED_LOAD_CONCURRENCY}"
export AZURE_FUNCTIONS_AGENTS_ACA_PROVISION_CONCURRENCY="${ACA_DEPLOYED_CONFIGURED_PROVISION_CONCURRENCY}"
python - <<'PY'

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make this a script under eng/scripts

image: 1es-ubuntu-22.04
os: linux

strategy:

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This block is being repeated, can we make it a common module somehow?

scriptType: bash
scriptLocation: inlineScript
inlineScript: |
set -euo pipefail

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These validations are repeated, make it a common script, avoid duplication

esac
export AZURE_FUNCTIONS_AGENTS_ACA_COLD_START_SAMPLES="${ACA_DEPLOYED_COLD_START_SAMPLES}"
fi
python - <<'PY'

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move to a dedicated script

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